/* * Copyright (c) 2020, Alibaba Group Holding Limited * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "xengine_api.h" #include #include #include #include "table.h" #include "./xdb_cf_manager.h" #include "./xdb_datadic.h" #include "./ha_xengine.h" // Export utilities functions from myx namespace myx { extern int xdb_normalize_tablename(const std::string &tablename, std::string *const strbuf); extern Xdb_dict_manager dict_manager; extern Xdb_cf_manager cf_manager; extern Xdb_ddl_manager ddl_manager; extern xengine::util::TransactionDB *xdb; } // namespace myx // Static function for internal use. namespace { static int xengine_strcasecmp(const char *a, const char *b) { if (!a) { if (!b) { return(0); } else { return(-1); } } else if (!b) { return(1); } return my_strcasecmp(system_charset_info, a, b); } static xengine_col_type_t get_col_type(Field *field) { static std::unordered_map mysql_engine_map = { {MYSQL_TYPE_DECIMAL, XENGINE_DECIMAL}, {MYSQL_TYPE_NEWDECIMAL, XENGINE_NEWDECIMAL}, {MYSQL_TYPE_TINY, XENGINE_INT}, {MYSQL_TYPE_SHORT, XENGINE_INT}, {MYSQL_TYPE_LONG, XENGINE_INT}, {MYSQL_TYPE_FLOAT, XENGINE_FLOAT}, {MYSQL_TYPE_DOUBLE, XENGINE_DOUBLE}, {MYSQL_TYPE_LONGLONG, XENGINE_INT}, {MYSQL_TYPE_INT24, XENGINE_INT}, {MYSQL_TYPE_VARCHAR, XENGINE_VARCHAR_ANYCHARSET}, {MYSQL_TYPE_TINY_BLOB, XENGINE_BLOB}, {MYSQL_TYPE_MEDIUM_BLOB, XENGINE_BLOB}, {MYSQL_TYPE_LONG_BLOB, XENGINE_BLOB}, {MYSQL_TYPE_BLOB, XENGINE_BLOB}, {MYSQL_TYPE_VAR_STRING, XENGINE_VARCHAR_ANYCHARSET}, {MYSQL_TYPE_DATETIME, XENGINE_DATETIME}, {MYSQL_TYPE_DATETIME2, XENGINE_DATETIME2}, }; xengine_col_type_t result = XENGINE_VARCHAR; enum_field_types field_type = field->real_type(); auto it = mysql_engine_map.find(field_type); if (it != mysql_engine_map.end()) { result = it->second; } if (MYSQL_TYPE_STRING == field_type) { if (field->flags & BINARY_FLAG) { result = XENGINE_BINARY; } else { result = XENGINE_CHAR; } } return result; } static uint32_t get_pk_index(const TABLE *const table, const myx::Xdb_tbl_def *const table_def) { return table->s->primary_key == MAX_INDEXES ? table_def->m_key_count - 1 : table->s->primary_key; } static xengine_err_t get_unpack_info( const char *db_value, const char **output, uint32_t *data_len) { *output = db_value; *data_len = myx::xdb_netbuf_to_uint16( reinterpret_cast(db_value + 1)); return DB_SUCCESS; } //unused function comment by beilou #if 0 static xengine_err_t skip_unpack_info( bool has_unpack_info, const char *db_value, int64_t *pos) { if (has_unpack_info) { *pos += myx::xdb_netbuf_to_uint16( reinterpret_cast(db_value + 1)); } return DB_SUCCESS; } static xengine_err_t get_varchar_col( const Field_varstring *field, char *db_value, const char **output) { if (nullptr == db_value) { return DB_INVALID_NULL; } /* field->length_bytes is 1 or 2 */ *output = db_value + field->length_bytes; return DB_SUCCESS; } #endif static xengine_err_t copy_varchar_col( const Field_varstring *field, const char *db_value, char *output) { if (nullptr == db_value || nullptr == output) { return DB_INVALID_NULL; } uint32_t data_len; /* field->length_bytes is 1 or 2 */ if (field->length_bytes == 1) { data_len = (uchar)db_value[0]; } else { DBUG_ASSERT(field->length_bytes == 2); data_len = uint2korr(db_value); } memcpy(output, db_value, field->length_bytes); if (data_len > field->field_length) { /* The data on disk is longer than table DDL allows? */ return DB_DATA_MISMATCH; } memcpy(output + field->length_bytes, db_value + field->length_bytes, data_len); return DB_SUCCESS; } static xengine_err_t skip_varchar_col( const Field_varstring *field, const char *db_value, int64_t *pos) { if (nullptr == db_value) { return DB_INVALID_NULL; } /* field->length_bytes is 1 or 2 */ *pos += field->length_bytes; if (field->length_bytes == 1) { *pos += (uchar)db_value[0]; } else { DBUG_ASSERT(field->length_bytes == 2); *pos += uint2korr(db_value); } return DB_SUCCESS; } static xengine_err_t get_varchar_col_length(const Field_varstring *field, const char *db_value, uint64_t *res) { if (nullptr == db_value) { return DB_INVALID_NULL; } if (field->length_bytes == 1) { *res = (uchar)db_value[0]; } else { *res = uint2korr(db_value); } return DB_SUCCESS; } static int64_t get_length_bytes(const Field_varstring *field) { return field->length_bytes; } #define PORTABLE_SIZEOF_CHAR_PTR 8 //unused function, comment by beilou #if 0 static xengine_err_t get_blob_col(const Field_blob *field, char *db_value, bool db_low_byte_first, const char **output) { if (nullptr == db_value) { return DB_INVALID_NULL; } *output = db_value + field->pack_length_no_ptr(); return DB_SUCCESS; } #endif static xengine_err_t copy_blob_col(const Field_blob *field, const char *db_value, bool db_low_byte_first, char *output) { if (nullptr == db_value || nullptr == output) { return DB_INVALID_NULL; } uint32_t length_bytes = field->pack_length_no_ptr(); memcpy(output, db_value, length_bytes); memset(output + length_bytes, 0, PORTABLE_SIZEOF_CHAR_PTR); const char *blob_ptr = db_value + length_bytes; memcpy(output + length_bytes, &blob_ptr, sizeof(char**)); return DB_SUCCESS; } static xengine_err_t skip_blob_col(const Field_blob *field, const char *db_value, bool db_low_byte_first, int64_t *pos) { if (nullptr == db_value) { return DB_INVALID_NULL; } uint32_t length_bytes = field->pack_length_no_ptr(); uint64_t data_len = 0; *pos += length_bytes; memcpy(&data_len, db_value, length_bytes); *pos += data_len; return DB_SUCCESS; } static xengine_err_t get_blob_col_length(const Field_blob *field, const char *db_value, bool db_low_byte_first, uint64_t *res) { if (nullptr == db_value || nullptr == field) { return DB_INVALID_NULL; } memcpy(res, db_value, field->pack_length_no_ptr()); return DB_SUCCESS; } static int64_t get_length_bytes(const Field_blob *field) { return field->pack_length_no_ptr(); } //unused function comment by beilou #if 0 static xengine_err_t get_normal_col(const Field *field, const char *db_value, const char **output) { if (nullptr == db_value || nullptr == output) { return DB_INVALID_NULL; } *output = db_value; return DB_SUCCESS; } #endif static xengine_err_t copy_normal_col(const Field *field, const char *db_value, char *output) { if (nullptr == db_value || nullptr == output) { return DB_INVALID_NULL; } memcpy(output, db_value, field->pack_length_in_rec()); return DB_SUCCESS; } static xengine_err_t skip_normal_col(const Field *field, const char *db_value, int64_t *pos) { if (nullptr == db_value) { return DB_INVALID_NULL; } *pos += field->pack_length_in_rec(); return DB_SUCCESS; } static xengine_err_t get_normal_col_length(const Field *field, const char *db_value, uint64_t *res) { if (nullptr == db_value || nullptr == res) { return DB_INVALID_NULL; } *res = field->pack_length_in_rec(); return DB_SUCCESS; } //unused function comment by beilou #if 0 static int64_t get_length_bytes(const Field *field) { return 0; } #endif static xengine_err_t xengine_tuple_copy_col(int64_t src_idx, xengine_tuple_t *src, int64_t idx, xengine_tuple_t *tpl) { uint64_t len = tpl->length_array_[idx] = src->length_array_[src_idx]; tpl->offset_array_[idx] = src->offset_array_[src_idx]; tpl->data_array_[idx] = src->data_array_[src_idx] - src->data_ + tpl->data_; memcpy(tpl->data_array_[idx], src->data_array_[src_idx], len); return DB_SUCCESS; } static xengine_err_t xengine_convert_value(std::set &field_offset_set, const char *row_value, xengine_table_def_t table_def, myx::Xdb_key_def *key_def, xengine_tuple_t *result) { const char *unpack_info = nullptr; uint32_t unpack_info_len = 0; auto table = static_cast(table_def->table_); int64_t pos = table_def->null_bytes_; const char *null_bytes = row_value; if (key_def->table_has_unpack_info(table)) { get_unpack_info(row_value + pos, &unpack_info, &unpack_info_len); pos += unpack_info_len; } // Parse and copy column one by one. Field **field_array = reinterpret_cast(table_def->field_array_); for (int64_t k = 0; k < table_def->col_array_len_; ++k) { // For unpacked key, don't call the skip...col function bscause this // field is not in the value. if (field_offset_set.find(static_cast(k)) != field_offset_set.end()) { continue; } const Field *field = field_array[k]; int64_t field_offset = field->ptr - table->record[0]; char *tpl_cursor = result->data_ + field_offset; enum_field_types field_type = field->real_type(); result->offset_array_[k] = field_offset; // null columns, fill default value. int64_t null_byte_offset = table_def->null_byte_offset_array_[k]; unsigned char null_mask = table_def->null_mask_array_[k]; if (null_mask & (*(null_bytes + null_byte_offset))) { const char *default_value = reinterpret_cast( table->s->default_values + field_offset); memcpy(tpl_cursor, default_value, field->pack_length()); result->length_array_[k] = XENGINE_SQL_NULL; result->data_array_[k] = nullptr; continue; } // normal fill. if (MYSQL_TYPE_BLOB == field_type) { const Field_blob *blob_field = static_cast(field); copy_blob_col(blob_field, row_value + pos, true, tpl_cursor); skip_blob_col(blob_field, row_value + pos, true, &pos); get_blob_col_length(blob_field, tpl_cursor, true, result->length_array_ + k); result->data_array_[k] = *(reinterpret_cast( tpl_cursor + get_length_bytes(blob_field))); } else if (MYSQL_TYPE_VARCHAR == field_type) { const Field_varstring *varchar_field = static_cast(field); copy_varchar_col(varchar_field, row_value + pos, tpl_cursor); skip_varchar_col(varchar_field, row_value + pos, &pos); get_varchar_col_length(varchar_field, tpl_cursor, result->length_array_ + k); result->data_array_[k] = tpl_cursor + get_length_bytes(varchar_field); } else { copy_normal_col(field, row_value + pos, tpl_cursor); skip_normal_col(field, row_value + pos, &pos); get_normal_col_length(field, tpl_cursor, result->length_array_ + k); result->data_array_[k] = tpl_cursor; } } return DB_SUCCESS; } // Malloc data for the member of xengine_tuple_t. Need a tuple instance created. // This function will not set the content in offset_array_, length_array_ and // data_array_. // This function will only allocate once for all memoery requirements. // The buffer can be divided into 4 continuous parts: // [data_: col_buffer_size] // [offset_array_: sizeof(int64_t) * col_array_len] // [length_array_: sizeof(uint64_t) * col_array_len] // [data_array_: sizeof(char*) * col_array_len] static xengine_err_t xengine_create_tuple(int64_t col_array_len, int64_t col_buffer_size, xengine_tuple_t *tuple) { // tuple will be sent to C code in X-KV and then free, so use malloc. tuple->size_ = col_buffer_size; int64_t offset_data = 0; int64_t offset_offset = offset_data + sizeof(tuple->data_[0]) * col_buffer_size; int64_t offset_length = offset_offset + sizeof(tuple->offset_array_[0]) * col_array_len; int64_t offset_data_array = offset_length + sizeof(tuple->length_array_[0]) * col_array_len; int64_t offset_null_array = offset_data_array + sizeof(tuple->data_array_[0]) * col_array_len; int64_t offset_mask_array = offset_null_array + sizeof(tuple->null_offset_array_[0]) * col_array_len; int64_t total_mem_size = offset_mask_array + sizeof(tuple->null_mask_array_[0]) * col_array_len; void *mem = malloc(total_mem_size); if (NULL == mem) { return DB_OUT_OF_MEMORY; } memset(mem, 0x00, sizeof(char) * total_mem_size); tuple->data_ = (char *) mem + offset_data; tuple->offset_array_ = (int64_t *) ((char *) mem + offset_offset); tuple->length_array_ = (uint64_t *) ((char *) mem + offset_length); tuple->data_array_ = (char**) ((char *) mem + offset_data_array); tuple->null_offset_array_ = (int64_t *) ((char *) mem + offset_null_array); tuple->null_mask_array_ = (uchar *) ((char *) mem + offset_mask_array); tuple->key_parts_ = 0; tuple->buffer_ = nullptr; return DB_SUCCESS; } static void xengine_free_tuple(xengine_tuple_t *tuple) { if (NULL != tuple) { free(tuple->data_); delete static_cast(tuple->buffer_); } free(tuple); } static void xengine_free_iter(xengine_iter_t *iter) { if (NULL != iter) { free(iter->key_tuple_.data_); delete static_cast(iter->key_tuple_.buffer_); free(iter->value_tuple_.data_); delete static_cast(iter->value_tuple_.buffer_); free(iter->upper_bound_); free(iter->lower_bound_); delete static_cast(iter->iter_); } free(iter); } // We construct the key tuple in the following rule: // 1. Get the total buffer size including all columns in this table. // 2. Create a buffer with the buffer size. // 3. For each part of the key, find its column offset in the buffer. // 4. Index these offset using increamental index from 0 in // data_array_ and offset_array_ // The key tuple is different with value tuple. In a value tuple, the // data_array_[i] and offset_array_[i] allway point to the i th column // in the buffer. But in a key tuple, the data_array_[i] and offset_array_[i] // will point to the real column of ths i th part of the key static xengine_err_t xengine_bless_key_tuple(xengine_table_def_t table_def, xengine_key_def_t key_def, xengine_tuple_t *tuple, int64_t key_parts) { xengine_err_t result = DB_SUCCESS; auto table = static_cast
(table_def->table_); auto db_key_def = static_cast(key_def); std::unique_ptr field_array; field_array.reset(new Field*[key_parts]); for (int64_t i = 0; i < key_parts; ++i) { Field *field = db_key_def->get_table_field_for_part_no(table, i); field_array[i] = field; } int64_t col_num = table_def->col_array_len_; int64_t buffer_size = table_def->col_buffer_size_; result = xengine_create_tuple(col_num, buffer_size, tuple); if (DB_SUCCESS != result) { return result; } int64_t first_offset = field_array[0]->ptr - table->record[0]; memset(tuple->data_, 0x00, sizeof(tuple->data_[0]) * first_offset); for (int64_t i = 0; i < key_parts; ++i) { Field *field = field_array[i]; int64_t offset = field->ptr - table->record[0]; tuple->offset_array_[i] = offset; int64_t length_bytes = 0; enum_field_types field_type = field->real_type(); if (MYSQL_TYPE_BLOB == field_type) { length_bytes = get_length_bytes(static_cast(field)); } else if (MYSQL_TYPE_VARCHAR == field_type) { length_bytes = get_length_bytes(static_cast(field)); } tuple->data_array_[i] = tuple->data_ + offset + length_bytes; if (MYSQL_TYPE_STRING == field_type) { memset(tuple->data_array_[i], 0x20, field->pack_length_in_rec()); } // Here, field->m_null_ptr and field->null_bit is nullptr and so useless. uint16_t index = field->field_index; tuple->null_offset_array_[i] = table_def->null_byte_offset_array_[index]; tuple->null_mask_array_[i] = table_def->null_mask_array_[index]; } return result; } static xengine_err_t xengine_create_iter(xengine_table_def_t table_def, xengine_key_def_t key_def, xengine_iter_t **iter) { auto db_key_def = static_cast(key_def); auto db_table_def = static_cast(table_def->db_obj_); auto table = static_cast
(table_def->table_); int64_t value_col_num = table_def->col_array_len_; int64_t value_buffer_size = table_def->col_buffer_size_; uint32_t pk_parts = static_cast(table_def->pk_def_)->get_key_parts(); xengine_iter_t *new_iter = (xengine_iter_t *) malloc(sizeof(xengine_iter_t)); if (NULL == new_iter) { goto end; } new_iter->key_tuple_.data_ = NULL; new_iter->value_tuple_.data_ = NULL; if (myx::Xdb_key_def::INDEX_TYPE_PRIMARY == db_key_def->m_index_type) { new_iter->seek_key_parts_ = pk_parts; } else if (!db_key_def->table_has_hidden_pk(table)) { new_iter->seek_key_parts_ = db_key_def->get_key_parts() - pk_parts; } else { new_iter->seek_key_parts_ = db_key_def->get_key_parts() - 1 /* hidden pk */; } if (DB_SUCCESS != xengine_bless_key_tuple(table_def, key_def, &new_iter->key_tuple_, new_iter->seek_key_parts_)) { goto free_iter; } if (DB_SUCCESS != xengine_create_tuple(value_col_num, value_buffer_size, &new_iter->value_tuple_)) { goto free_key_tuple; } new_iter->upper_bound_ = (char *) malloc( sizeof(char) * db_key_def->max_storage_fmt_length()); new_iter->upper_bound_size_ = 0; if (NULL == new_iter->upper_bound_) { goto free_value_tuple; } new_iter->lower_bound_ = (char *) malloc( sizeof(char) * db_key_def->max_storage_fmt_length()); new_iter->lower_bound_size_ = 0; if (NULL == new_iter->lower_bound_) { goto free_upper_bound; } *iter = new_iter; return DB_SUCCESS; free_upper_bound: free(new_iter->upper_bound_); free_value_tuple: xengine_free_tuple(&new_iter->value_tuple_); free_key_tuple: xengine_free_tuple(&new_iter->key_tuple_); free_iter: xengine_free_iter(new_iter); end: return DB_OUT_OF_MEMORY; } }; // namespace using namespace xengine; using namespace common; xengine_err_t xengine_trx_start(xengine_trx_t *trx, xengine_trx_level_t trx_level, xengine_bool_t read_write, xengine_bool_t auto_commit, void *thd) { return DB_ERROR; } xengine_trx_t *xengine_trx_begin(xengine_trx_level_t trx_level, xengine_bool_t read_write, xengine_bool_t auto_commit) { xengine_trx_t *new_trx = (xengine_trx_t *) malloc(sizeof(xengine_trx_t)); new_trx->db_snapshot_ = const_cast(static_cast( myx::xdb->GetSnapshot())); return new_trx; } uint32_t xengine_trx_read_only(xengine_trx_t *trx) { //TODO comment by beilou 5.7->8.0 //i don't know why there is a empty function, so just abort it abort(); return 0; } xengine_err_t xengine_trx_release(xengine_trx_t *trx) { if (trx != nullptr) { auto snapshot = static_cast(trx->db_snapshot_); myx::xdb->ReleaseSnapshot(snapshot); free(trx); } return DB_SUCCESS; } xengine_err_t xengine_trx_commit(xengine_trx_t *xengine_trx) { return DB_ERROR; } xengine_err_t xengine_trx_rollback(xengine_trx_t *xengine_trx) { return DB_ERROR; } xengine_err_t xengine_open_table_using_id(xengine_id_u64_t table_id, xengine_trx_t *xengine_trx, xengine_request_t *xengine_crsr) { return DB_ERROR; } xengine_err_t xengine_open_index_using_name(xengine_request_t req, const char *index_name, xengine_iter_t **iter, int *idx_type, xengine_id_u64_t *idx_id) { xengine_err_t result = DB_SUCCESS; auto db_table_def = static_cast(req->table_def_->db_obj_); auto table = static_cast
(req->table_def_->table_); myx::Xdb_key_def *db_key_def = nullptr; for (size_t i = 0; i != db_table_def->m_key_count; ++i) { db_key_def = db_table_def->m_key_descr_arr[i].get(); if (xengine_strcasecmp(db_key_def->get_name().data(), index_name) == 0) { *idx_id = db_key_def->get_index_number(); *idx_type = db_key_def->m_index_type == myx::Xdb_key_def::INDEX_TYPE_PRIMARY ? XENGINE_CLUSTERED : table->key_info[i].flags & HA_NOSAME ? XENGINE_UNIQUE: XENGINE_SECONDARY; break; } } if (nullptr == db_key_def) { return DB_NOT_FOUND; } xengine_iter_t *new_iter; xengine_create_iter(req->table_def_, db_key_def, &new_iter); // target index is found, create xengine iterator. new_iter->key_def_ = static_cast(db_key_def); myx::GL_INDEX_ID gl_index_id = db_key_def->get_gl_index_id(); new_iter->cf_id_ = gl_index_id.cf_id; new_iter->index_number_ = db_key_def->get_index_number(); xengine::common::ReadOptions ro; ro.total_order_seek = true; xengine::db::ColumnFamilyHandle *cfh = db_key_def->get_cf(); new_iter->iter_ = static_cast(myx::xdb->NewIterator(ro, cfh)); new_iter->is_primary_ = db_key_def->m_index_type == myx::Xdb_key_def::INDEX_TYPE_PRIMARY; *iter = new_iter; return result; } xengine_err_t xengine_open_table(const char *db_name, const char *table_name, void *thd, void *mysql_table, int64_t lock_type, xengine_request_t *req) { int64_t db_name_len = strlen(db_name); int64_t table_name_len = strlen(table_name); int64_t name_len = db_name_len + table_name_len + 1; char *name_buffer = new char[name_len + 1]; std::unique_ptr normalized_name {name_buffer}; memcpy(name_buffer, db_name, db_name_len); name_buffer[db_name_len] = '.'; memcpy(name_buffer + db_name_len + 1, table_name, table_name_len); bool from_dict = false; auto tbl_def = myx::ddl_manager.find(name_buffer, name_len, &from_dict, false); myx::Xdb_tbl_def *xdb_table_def = tbl_def.get(); if (nullptr == xdb_table_def) { return DB_TABLE_NOT_FOUND; } myx::Xdb_key_def *xdb_key_def = xdb_table_def->m_key_descr_arr->get(); if (nullptr == xdb_key_def) { return DB_NOT_FOUND; } // malloc and init xegnine_request *req = (xengine_request_t) malloc(sizeof(xengine_request)); xengine_table_def_t table_def = (xengine_table_def_t) malloc(sizeof(*((*req)->table_def_))); table_def->db_name_ = db_name; table_def->table_name_ = table_name; table_def->db_name_len_ = strlen(db_name); table_def->table_name_len_ = strlen(table_name); table_def->db_obj_ = static_cast(xdb_table_def); TABLE *table = static_cast
(mysql_table); int64_t number_fields = table->s->fields; table_def->col_array_len_ = number_fields; table_def->col_array_len_ = number_fields; table_def->col_name_array_ = (const char **) malloc( sizeof(char *) * number_fields); table_def->col_length_array_ = (int64_t *) malloc( sizeof(int64_t) * number_fields); table_def->field_array_ = (void **) malloc( sizeof(void *) * number_fields); table_def->null_byte_offset_array_ = (int64_t *) malloc( sizeof(int64_t) * number_fields); table_def->null_mask_array_ = (unsigned char *) malloc( sizeof(unsigned char) * number_fields); unsigned char current_null_mask = 0x1; int64_t current_null_byte = 0; table_def->col_buffer_size_ = 1; for (uint32_t i = 0; i < number_fields; ++i) { Field *const field = table->field[i]; table_def->col_buffer_size_ += field->pack_length(); table_def->col_name_array_[i] = field->field_name; table_def->col_length_array_[i] = field->pack_length(); table_def->field_array_[i] = static_cast(field); if (field->real_maybe_null()) { table_def->null_mask_array_[i] = current_null_mask; table_def->null_byte_offset_array_[i] = current_null_byte; if (0x80 == current_null_mask) { current_null_mask = 0x1; current_null_byte += 1; } else { current_null_mask <<= 1; } } else { table_def->null_mask_array_[i] = 0; table_def->null_byte_offset_array_[i] = 0; } } table_def->null_bytes_ = current_null_byte + (~current_null_mask & 1); table_def->table_ = table; uint32_t pk_index = get_pk_index(table, xdb_table_def); myx::Xdb_key_def *key_def = xdb_table_def->m_key_descr_arr[pk_index].get(); table_def->pk_def_ = key_def; table_def->pk_parts_ = key_def->get_key_parts(); (*req)->lock_type_ = lock_type; (*req)->table_def_ = table_def; (*req)->read_trx_ = nullptr; (*req)->thd_ = thd; return DB_SUCCESS; } void xengine_delete_tuple(xengine_tuple_t *tuple) { xengine_free_tuple(tuple); } xengine_err_t xengine_close(xengine_request_t req) { return DB_ERROR; } xengine_err_t xengine_close_table(xengine_request_t req) { if (NULL != req) { free(req->table_def_->col_name_array_); free(req->table_def_->col_length_array_); free(req->table_def_->field_array_); free(req->table_def_->null_byte_offset_array_); free(req->table_def_->null_mask_array_); free(req->table_def_); free(req); } return DB_SUCCESS; } xengine_err_t xengine_new_trx(xengine_request_t req, xengine_trx_t *xengine_trx) { return DB_ERROR; } xengine_err_t xengine_commit_trx(xengine_request_t req, xengine_trx_t *xengine_trx) { return DB_ERROR; } xengine_err_t xengine_insert_row(xengine_request_t req, const xengine_tpl_t xengine_tpl) { return DB_ERROR; } xengine_err_t xengine_update_row(xengine_request_t xengine_crsr, const xengine_tpl_t xengine_old_tpl, const xengine_tpl_t xengine_new_tpl) { return DB_ERROR; } xengine_err_t xengine_delete_row(xengine_request_t xengine_crsr) { return DB_ERROR; } // Read using PK. xengine_err_t xengine_pk_search(xengine_request_t req, xengine_tuple_t *key_tuple, xengine_tuple_t *value_tuple) { xengine_table_def_t table_def = req->table_def_; auto pk_def = static_cast(table_def->pk_def_); auto table = static_cast
(table_def->table_); uint32_t pack_buffer_size = pk_def->max_storage_fmt_length(); std::unique_ptr pack_buf {new uchar[pack_buffer_size]}; std::unique_ptr pack_res {new char[pack_buffer_size]}; char *packed_key_data = pack_res.get(); int64_t key_parts = pk_def->get_key_parts(); uint32_t packed_key_size = pk_def->pack_record(table, pack_buf.get(), reinterpret_cast(key_tuple->data_), reinterpret_cast(packed_key_data), nullptr, false, 0, key_parts, nullptr); assert(packed_key_size <= pack_buffer_size); Slice key(packed_key_data, packed_key_size); ReadOptions ro; std::string *row_value = value_tuple->buffer_ != nullptr ? static_cast(value_tuple->buffer_) : new std::string(); xengine_trx_t *trx = req->read_trx_; if (trx != nullptr) { ro.snapshot = static_cast(trx->db_snapshot_); } Status s = myx::xdb->Get(ro, pk_def->get_cf(), key, row_value); if (!s.ok()) { return DB_GET_ERROR; } std::set field_id_set; if (!pk_def->table_has_hidden_pk(table)) { for (int64_t k = 0; k != pk_def->get_key_parts(); ++k) { Field *field = pk_def->get_table_field_for_part_no(table, k); uint16_t field_index = field->field_index; xengine_tuple_copy_col(k, key_tuple, field_index, value_tuple); field_id_set.insert(field_index); } } value_tuple->buffer_ = static_cast(row_value); // Parse and copy column one by one. return xengine_convert_value(field_id_set, row_value->data(), table_def, pk_def, value_tuple); } // Read from iter. // When seach using SK, automatically switch to PK. xengine_err_t xengine_read_row(xengine_request_t req, xengine_iter_t *iter, xengine_tpl_t cmp_tpl, int mode, void **row_buf, xengine_ulint_t *row_len, xengine_ulint_t *used_len) { int64_t pos = 0; auto db_iter = static_cast(iter->iter_); xengine_table_def_t table_def = req->table_def_; if (nullptr == db_iter) { assert(false); return DB_INVALID_NULL; } xengine_tuple_t *tpl = &(iter->value_tuple_); xengine::common::Slice value = db_iter->value(); xengine::common::Slice key = db_iter->key(); const char *db_value = value.data(); const char *null_bytes = db_value; // Secondary index store null info just in field data position. int64_t null_bytes_size = iter->is_primary_ ? table_def->null_bytes_ : 0; const char *unpack_info = nullptr; uint32_t unpack_info_len = 0; TABLE *table = static_cast
(table_def->table_); myx::Xdb_key_def *key_def = static_cast(iter->key_def_); pos += null_bytes_size; if (key_def->table_has_unpack_info(table)) { get_unpack_info(db_value + pos, &unpack_info, &unpack_info_len); pos += unpack_info_len; xengine::common::Slice unpack_slice(unpack_info, unpack_info_len); if (key_def->unpack_record(table, reinterpret_cast(tpl->data_), &key, unpack_info ? &unpack_slice : nullptr, false /* verify_checksum */)) { return DB_UNPACK_ERROR; } } else if (key_def->unpack_record_1(table, reinterpret_cast(tpl->data_), &key, nullptr, false /* verify_checksum */)) { return DB_UNPACK_ERROR; } // Read from secondary key to get PK and then call db->Get using PK. std::string *row_value = tpl->buffer_ != nullptr ? static_cast(tpl->buffer_) : new std::string(); if (!(iter->is_primary_)) { myx::Xdb_key_def *pk_def = static_cast (table_def->pk_def_); uint32_t pk_len = key_def->get_primary_key_tuple(table, *pk_def, &key, reinterpret_cast(iter->key_tuple_.data_)); xengine::common::Slice row_key(iter->key_tuple_.data_, pk_len); xengine::common::ReadOptions ro; xengine_trx_t *trx = req->read_trx_; // Must access the referenced PK row in the same snapshot. if (nullptr == trx) { return DB_SNAPSHOT_ERROR; } ro.snapshot = static_cast(trx->db_snapshot_); xengine::common::Status status = myx::xdb->Get(ro, pk_def->get_cf(), row_key, row_value); if (!status.ok()) { return DB_GET_ERROR; } // For hidden PK, value contains all fields. // So fill field_d_set with only PK. key_def = pk_def; db_value = row_value->data(); } std::set field_id_set; if (!key_def->table_has_hidden_pk(table)) { for (int64_t k = 0; k != key_def->get_key_parts(); ++k) { Field *field = key_def->get_table_field_for_part_no(table, k); char *tpl_cursor = field->ptr - table->record[0] + tpl->data_; uint16_t field_index = field->field_index; field_id_set.insert(field_index); enum_field_types field_type = field->real_type(); if (MYSQL_TYPE_BLOB == field_type) { auto blob_field = static_cast(field); get_blob_col_length(blob_field, tpl_cursor, true, tpl->length_array_ + field_index); tpl->data_array_[field_index] = tpl_cursor + get_length_bytes(blob_field); } else if (MYSQL_TYPE_VARCHAR == field_type) { auto varchar_field = static_cast(field); get_varchar_col_length(varchar_field, tpl_cursor, tpl->length_array_ + field_index); tpl->data_array_[field_index] = tpl_cursor + get_length_bytes(varchar_field); } else { get_normal_col_length(field, tpl_cursor, tpl->length_array_ + field_index); tpl->data_array_[field_index] = tpl_cursor; } } } // Parse and copy column one by one. tpl->buffer_ = static_cast(row_value); return xengine_convert_value(field_id_set, db_value, table_def, key_def, tpl); } xengine_err_t xengine_iter_valid(xengine_iter_t *iter) { auto db_key_def = static_cast(iter->key_def_); auto db_iter = static_cast(iter->iter_); xengine_err_t result = DB_SUCCESS; if (!db_iter->Valid() || !db_key_def->covers_key(db_iter->key())) { result = DB_END_OF_INDEX; } else { xengine::common::Slice current_key = db_iter->key(); // If iter->upper_bound_size_ is 0, memcmp return 0. uint64_t cmp_size = std::min(current_key.size(), iter->upper_bound_size_); if (memcmp(current_key.data(), iter->upper_bound_, cmp_size) > 0) { result = DB_END_OF_INDEX; } } return result; } xengine_err_t xengine_iter_first(xengine_iter_t *iter) { xengine_err_t result = DB_SUCCESS; assert(iter != nullptr); auto db_iter = static_cast(iter->iter_); if (db_iter != nullptr && iter->key_def_ != nullptr) { uchar first_key[myx::Xdb_key_def::INDEX_NUMBER_SIZE]; uint first_key_size = 0; auto db_key_def = static_cast(iter->key_def_); db_key_def->get_infimum_key(first_key, &first_key_size); uchar* key_ptr = first_key; db_iter->Seek(xengine::common::Slice( reinterpret_cast(key_ptr), first_key_size)); result = xengine_iter_valid(iter); } else { assert(false); result = DB_INVALID_NULL; } return result; } xengine_err_t xengine_iter_next(xengine_iter_t *iter) { xengine_err_t result = DB_SUCCESS; auto db_key_def = static_cast(iter->key_def_); auto db_iter = static_cast(iter->iter_); if (db_iter != nullptr) { db_iter->Next(); result = xengine_iter_valid(iter); } else { assert(false); result = DB_INVALID_NULL; } return result; } xengine_err_t xengine_iter_seek(xengine_request_t req, xengine_tpl_t tpl, xengine_iter_t *iter, xengine_srch_mode_t xengine_srch_mode, xengine_ulint_t direction) { xengine_err_t result = DB_SUCCESS; auto db_key_def = static_cast(iter->key_def_); auto table = static_cast
(req->table_def_->table_); uint32_t pack_buffer_size = db_key_def->max_storage_fmt_length(); std::unique_ptr pack_buf {new uchar[pack_buffer_size]}; std::unique_ptr pack_res {new char[pack_buffer_size]}; char *packed_key_data = pack_res.get(); int64_t key_parts = tpl->key_parts_; if (key_parts == 0) { key_parts = iter->seek_key_parts_; } assert(key_parts <= iter->seek_key_parts_); uint32_t packed_key_size = db_key_def->pack_record(table, pack_buf.get(), reinterpret_cast(tpl->data_), reinterpret_cast(packed_key_data), nullptr, false, 0, key_parts, nullptr); assert(packed_key_size <= pack_buffer_size); memcpy(iter->upper_bound_, packed_key_data, packed_key_size); iter->upper_bound_size_ = packed_key_size; auto db_iter = static_cast(iter->iter_); if (db_iter != nullptr) { xengine::common::Slice key(packed_key_data, packed_key_size); db_iter->Seek(key); } else { assert(false); result = DB_INVALID_NULL; } if (DB_SUCCESS == result) { result = xengine_iter_valid(iter); } return result; } void xengine_set_match_mode(xengine_request_t req, xengine_match_mode_t match_mode) { if (req != nullptr) { req->match_mode_ = match_mode; } } xengine_err_t xengine_col_set_value(xengine_tuple_t *tpl, xengine_ulint_t col, const void *src, xengine_ulint_t len, xengine_bool_t need_cpy) { // TODO @zhencheng: process need_cpy if (len != XENGINE_SQL_NULL) { assert(src != nullptr); memcpy(tpl->data_array_[col], src, len); // Not equal means this field has length parts. int64_t field_offset = tpl->offset_array_[col]; int64_t len_len = tpl->data_array_[col] - tpl->data_ - field_offset; // Assume little endian. memcpy(tpl->data_ + field_offset, &len, len_len); } else { tpl->data_[tpl->null_offset_array_[col]] |= tpl->null_mask_array_[col]; } // For null fields, only set the len to XENGINE_SQL_NULL tpl->length_array_[col] = len; return DB_SUCCESS; } xengine_ulint_t xengine_col_get_len(xengine_tpl_t xengine_tpl, xengine_ulint_t i) { abort(); //TODO add by beilou 5.7->8.0 return 0; } xengine_ulint_t xengine_col_copy_value(xengine_tpl_t xengine_tpl, xengine_ulint_t i, void *dst, xengine_ulint_t len) { abort(); //TODO add by beilou 5.7->8.0 return 0; } xengine_err_t xengine_tuple_read_i8(xengine_tpl_t tpl, xengine_ulint_t i, xengine_i8_t *ival) { *ival = *(reinterpret_cast(tpl->data_array_[i])); return DB_ERROR; } xengine_err_t xengine_tuple_read_u8(xengine_tpl_t tpl, xengine_ulint_t i, xengine_u8_t *ival) { *ival = *(reinterpret_cast(tpl->data_array_[i])); return DB_SUCCESS; } xengine_err_t xengine_tuple_read_i16(xengine_tpl_t tpl, xengine_ulint_t i, xengine_i16_t *ival) { *ival = *(reinterpret_cast(tpl->data_array_[i])); return DB_SUCCESS; } xengine_err_t xengine_tuple_read_u16(xengine_tpl_t tpl, xengine_ulint_t i, xengine_u16_t *ival) { *ival = *(reinterpret_cast(tpl->data_array_[i])); return DB_SUCCESS; } xengine_err_t xengine_tuple_read_i24(xengine_tpl_t tpl, xengine_ulint_t i, xengine_i32_t *ival) { *ival = *(reinterpret_cast(tpl->data_array_[i])); return DB_SUCCESS; } xengine_err_t xengine_tuple_read_u24(xengine_tpl_t tpl, xengine_ulint_t i, xengine_u32_t *ival) { *ival = *(reinterpret_cast(tpl->data_array_[i])); return DB_SUCCESS; } xengine_err_t xengine_tuple_read_i32(xengine_tpl_t tpl, xengine_ulint_t i, xengine_i32_t *ival) { *ival = *(reinterpret_cast(tpl->data_array_[i])); return DB_SUCCESS; } xengine_err_t xengine_tuple_read_u32(xengine_tpl_t tpl, xengine_ulint_t i, xengine_u32_t *ival) { *ival = *(reinterpret_cast(tpl->data_array_[i])); return DB_SUCCESS; } xengine_err_t xengine_tuple_read_i64(xengine_tpl_t tpl, xengine_ulint_t i, xengine_i64_t *ival) { *ival = *(reinterpret_cast(tpl->data_array_[i])); return DB_SUCCESS; } xengine_err_t xengine_tuple_read_u64(xengine_tpl_t tpl, xengine_ulint_t i, xengine_u64_t *ival) { *ival = *(reinterpret_cast(tpl->data_array_[i])); return DB_SUCCESS; } const void *xengine_col_get_value(xengine_tuple_t *tpl, xengine_ulint_t i) { return static_cast(tpl->data_array_[i]); } xengine_ulint_t xengine_col_get_meta(xengine_request_t req, xengine_tuple_t *tpl, xengine_ulint_t i, xengine_col_meta_t *col_meta) { Field *field = reinterpret_cast(req->table_def_->field_array_)[i]; col_meta->type = get_col_type(field); uint16_t type = col_meta->client_type = col_meta->prtype = field->real_type(); col_meta->type_len = field->pack_length(); col_meta->attr = XENGINE_COL_NONE; if (field->flags & UNSIGNED_FLAG) { col_meta->attr |= XENGINE_COL_UNSIGNED; } if (nullptr == tpl) { return 0; } const char *null_bytes = tpl->data_; int64_t null_byte_offset = req->table_def_->null_byte_offset_array_[i]; unsigned char null_mask = req->table_def_->null_mask_array_[i]; if (null_mask & (*(null_bytes + null_byte_offset))) { return XENGINE_SQL_NULL; } return static_cast(tpl->length_array_[i]); } void xengine_tuple_clear(xengine_iter_t **iter, xengine_tuple_t **tpl) { xengine_free_iter(*iter); xengine_free_tuple(*tpl); } xengine_err_t xengine_tuple_get_cluster_key(xengine_request_t xengine_crsr, xengine_tpl_t *xengine_dst_tpl, const xengine_tpl_t xengine_src_tpl) { return DB_UNSUPPORTED; } xengine_err_t xengine_key_tuple_create(xengine_request_t req, xengine_key_def_t key_def, int64_t key_part, xengine_tuple_t **tuple) { xengine_err_t result = DB_SUCCESS; xengine_tuple_t *new_tuple = (xengine_tuple_t *) malloc(sizeof(xengine_tuple_t)); result = xengine_bless_key_tuple(req->table_def_, key_def, new_tuple, key_part); if (DB_SUCCESS != result) { return result; } *tuple = new_tuple; return DB_SUCCESS; } xengine_err_t xengine_value_tuple_create(xengine_table_def_t table_def, xengine_tuple_t **tuple) { auto new_tuple = (xengine_tuple_t *) malloc(sizeof(xengine_tuple_t)); int64_t col_num = table_def->col_array_len_; int64_t buffer_size = table_def->col_buffer_size_; xengine_create_tuple(col_num, buffer_size, new_tuple); *tuple = new_tuple; return DB_SUCCESS; } xengine_err_t xengine_sec_iter_create(xengine_request_t req, const char *index_name, xengine_iter_t **iter) { if (NULL == iter || NULL == req->table_def_) { assert(false); return DB_INVALID_NULL; } xengine_iter_t *new_iter; auto db_table_def = static_cast(req->table_def_->db_obj_); myx::Xdb_key_def *db_key_def = nullptr; for (size_t i = 0; i != db_table_def->m_key_count; ++i) { db_key_def = db_table_def->m_key_descr_arr[i].get(); if (0 == xengine_strcasecmp(db_key_def->get_name().data(), index_name)) { break; } } if (nullptr == db_key_def) { return DB_NOT_FOUND; } xengine_create_iter(req->table_def_, db_key_def, &new_iter); new_iter->key_def_ = static_cast(db_key_def); myx::GL_INDEX_ID gl_index_id = db_key_def->get_gl_index_id(); new_iter->cf_id_ = gl_index_id.cf_id; new_iter->index_number_ = gl_index_id.index_id; xengine::common::ReadOptions ro; ro.total_order_seek = true; xengine_trx_t *trx = req->read_trx_; if (trx != nullptr) { ro.snapshot = static_cast(trx->db_snapshot_); } xengine::db::ColumnFamilyHandle *cfh = db_key_def->get_cf(); new_iter->iter_ = static_cast(myx::xdb->NewIterator(ro, cfh)); new_iter->is_primary_ = false; *iter = new_iter; return DB_SUCCESS; } xengine_err_t xengine_clust_iter_create(xengine_request_t req, xengine_iter_t **iter) { if (NULL == iter || NULL == req->table_def_) { assert(false); return DB_INVALID_NULL; } xengine_iter_t *new_iter; auto db_table_def = static_cast(req->table_def_->db_obj_); auto db_key_def = static_cast(req->table_def_->pk_def_); xengine_create_iter(req->table_def_, db_key_def, &new_iter); new_iter->key_def_ = static_cast(db_key_def); myx::GL_INDEX_ID gl_index_id = db_key_def->get_gl_index_id(); new_iter->cf_id_ = gl_index_id.cf_id; new_iter->index_number_ = gl_index_id.index_id; xengine::common::ReadOptions ro; ro.total_order_seek = true; xengine_trx_t *trx = req->read_trx_; if (trx != nullptr) { ro.snapshot = static_cast(trx->db_snapshot_); } xengine::db::ColumnFamilyHandle *cfh = db_key_def->get_cf(); new_iter->iter_ = static_cast(myx::xdb->NewIterator(ro, cfh)); new_iter->is_primary_ = true; *iter = new_iter; return DB_SUCCESS; } // The primary key is one part of the key tuple. Don't include primary key // part for the range scan condition in X-KV. xengine_ulint_t xengine_iter_get_n_user_cols(const xengine_iter_t *iter) { return iter->seek_key_parts_; } xengine_ulint_t xengine_iter_get_n_cols(const xengine_iter_t *iter) { auto db_key_def = static_cast(iter->key_def_); return db_key_def->get_key_parts(); } void xengine_tuple_set_n_fields_cmp(const xengine_tpl_t tpl, uint32_t n_fields_cmp) { tpl->key_parts_ = static_cast(n_fields_cmp); } void xengine_delete_iter(xengine_iter_t *iter) { xengine_free_iter(iter); } xengine_err_t xengine_clust_search_tuple_create(xengine_request_t req, xengine_iter_t *iter) { return DB_UNSUPPORTED; } xengine_err_t xengine_truncate(xengine_request_t *xengine_crsr, xengine_id_u64_t *table_id) { return DB_ERROR; } xengine_err_t xengine_table_get_id(const char *table_name, xengine_id_u64_t *table_id) { return DB_ERROR; } xengine_bool_t xengine_is_positioned(const xengine_request_t xengine_crsr) { abort();//TODO add by beilou return false; } xengine_bool_t xengine_schema_lock_is_exclusive( const xengine_trx_t *xengine_trx) { abort();//TODO add by beilou return false; } xengine_err_t xengine_lock(xengine_request_t xengine_crsr, xengine_lck_mode_t xengine_lck_mode) { return DB_ERROR; } xengine_err_t xengine_table_lock(xengine_trx_t *xengine_trx, xengine_id_u64_t table_id, xengine_lck_mode_t xengine_lck_mode) { return DB_ERROR; } xengine_err_t xengine_set_lock_mode( xengine_request_t xengine_crsr, xengine_lck_mode_t xengine_lck_mode) { abort(); //TODO add by beilou return DB_SUCCESS; } void xengine_set_cluster_access(xengine_request_t xengine_crsr) { } void xengine_stmt_begin(xengine_request_t xengine_crsr) { } xengine_err_t xengine_tuple_write_double(xengine_tpl_t xengine_tpl, int col_no, double val) { return DB_ERROR; } xengine_err_t xengine_tuple_read_double(xengine_tpl_t tpl, xengine_ulint_t col_no, double *dval) { *dval = *(reinterpret_cast(tpl->data_array_[col_no])); return DB_SUCCESS; } xengine_err_t xengine_tuple_write_float(xengine_tpl_t xengine_tpl, int col_no, float val) { return DB_ERROR; } xengine_err_t xengine_tuple_read_float(xengine_tpl_t tpl, xengine_ulint_t col_no, float *fval) { *fval = *(reinterpret_cast(tpl->data_array_[col_no])); return DB_SUCCESS; } const char *xengine_col_get_name(xengine_request_t req, xengine_ulint_t i) { return req->table_def_->col_name_array_[i]; } const char *xengine_get_idx_field_name(xengine_request_t req, xengine_iter_t *iter, xengine_ulint_t i) { auto db_key_def = static_cast(iter->key_def_); Field *field = db_key_def->get_table_field_for_part_no( static_cast
(req->table_def_->table_), i); return field->field_name; } xengine_err_t xengine_table_truncate(const char *table_name, xengine_id_u64_t *table_id) { return DB_ERROR; } int xengine_cfg_get_cfg() { return 0; } xengine_err_t xengine_set_memcached_sync(xengine_request_t xengine_crsr, xengine_bool_t flag) { return DB_ERROR; } xengine_trx_level_t xengine_cfg_trx_level() { abort();//TODO add by beilou return XENGINE_TRX_READ_COMMITTED; } xengine_ulint_t xengine_cfg_bk_commit_interval() { return 0; } xengine_u64_t xengine_trx_get_start_time(xengine_trx_t *xengine_trx) { return 0; } const char *xengine_ut_strerr(xengine_err_t num) { switch (num) { case DB_SUCCESS: return "Success"; case DB_SUCCESS_LOCKED_REC: return "Success, record lock created"; case DB_ERROR: return "Generic error"; case DB_READ_ONLY: return "Read only transaction"; case DB_INTERRUPTED: return "Operation interrupted"; 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_TOO_BIG_INDEX_COL: return "Index columns size 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_CANNOT_DROP_CONSTRAINT: return "Cannot drop constraint"; case DB_NO_SAVEPOINT: return "No such savepoint"; case DB_TABLESPACE_EXISTS: return "Tablespace already exists"; case DB_TABLESPACE_DELETED: return "Tablespace deleted or being deleted"; case DB_TABLESPACE_TRUNCATED: return "Tablespace was truncated"; case DB_TABLESPACE_NOT_FOUND: return "Tablespace not found"; 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_FOREIGN_EXCEED_MAX_CASCADE: return "Foreign key cascade delete/update exceeds max depth"; case DB_TOO_MANY_CONCURRENT_TRXS: return "Too many concurrent transactions"; case DB_UNSUPPORTED: return "Unsupported"; case DB_INVALID_NULL: return "NULL value encountered in NOT NULL column"; case DB_STATS_DO_NOT_EXIST: return "Persistent statistics do not exist"; 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_CHILD_NO_INDEX: return "No index on referencing keys in referencing table"; case DB_PARENT_NO_INDEX: return "No index on referenced keys in referenced table"; case DB_FTS_INVALID_DOCID: return "FTS Doc ID cannot be zero"; case DB_INDEX_CORRUPT: return "Index corrupted"; case DB_UNDO_RECORD_TOO_BIG: return "Undo record too big"; case DB_END_OF_INDEX: return "End of index"; case DB_IO_ERROR: return "I/O error"; case DB_TABLE_IN_FK_CHECK: return "Table is being used in foreign key check"; case DB_DATA_MISMATCH: return "data mismatch"; case DB_SCHEMA_NOT_LOCKED: return "schema not locked"; case DB_SNAPSHOT_ERROR: return "snapshot error"; case DB_UNPACK_ERROR: return "unpack error"; case DB_GET_ERROR: return "get error"; case DB_NOT_FOUND: return "not found"; case DB_ONLINE_LOG_TOO_BIG: return "Log size exceeded during online index creation"; case DB_IDENTIFIER_TOO_LONG: return "Identifier name is too long"; case DB_FTS_EXCEED_RESULT_CACHE_LIMIT: return "FTS query exceeds result cache limit"; case DB_TEMP_FILE_WRITE_FAIL: return "Temp file write failure"; case DB_CANT_CREATE_GEOMETRY_OBJECT: return "Can't create specificed geometry data object"; case DB_CANNOT_OPEN_FILE: return "Cannot open a file"; case DB_TABLE_CORRUPT: return "Table is corrupted"; case DB_FTS_TOO_MANY_WORDS_IN_PHRASE: return "Too many words in a FTS phrase or proximity search"; case DB_IO_DECOMPRESS_FAIL: return "Page decompress failed after reading from disk"; case DB_IO_NO_PUNCH_HOLE: return "No punch hole support"; case DB_IO_NO_PUNCH_HOLE_FS: return "Punch hole not supported by the file system"; case DB_IO_NO_PUNCH_HOLE_TABLESPACE: return "Punch hole not supported by the tablespace"; case DB_IO_NO_ENCRYPT_TABLESPACE: return "Page encryption not supported by the tablespace"; case DB_IO_DECRYPT_FAIL: return "Page decryption failed after reading from disk"; case DB_IO_PARTIAL_FAILED: return "Partial IO failed"; case DB_FORCED_ABORT: return "Transaction aborted by another higher priority" "transaction"; case DB_WRONG_FILE_NAME: return "Invalid Filename"; case DB_NO_FK_ON_S_BASE_COL: return "Cannot add foreign key on the base column" "of stored column"; case DB_COMPUTE_VALUE_FAILED: return "Compute generated column failed"; case DB_X_PROTOCOL_INVISIBLE_CONFIG: return "X PROTOCOL: invisible config"; case DB_X_PROTOCOL_WRONG_VERSION: return "X PROTOCOL: wrong version"; case DB_X_PROTOCOL_EINVAL: return "X PROTOCL: invalid argument"; case DB_META_INVAL: return "meta information in containers table is invalid"; case DB_RES_OVERFLOW: return "result is overflow"; /* 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"; } xengine_bool_t xengine_is_virtual_table(xengine_request_t crsr) { return false; } void xengine_set_tuple_key(xengine_tpl_t xengine_tpl) { }