Commit 6c43068d authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-15060 Assertion in row_log_table_apply_op after instant ADD when the...

MDEV-15060 Assertion in row_log_table_apply_op after instant ADD when the table is emptied during subsequent ALTER TABLE

During an online table rebuild, a table could be emptied and converted
from 'instant ADD' format to plain (pre-10.3) format. All online_log
records for rebuilding the table must be written and parsed in the
format of the table that existed at the start of the operation.

row_log_t::n_core_fields: A new field for recording index->n_core_fields
when online ALTER is initiated in row_log_allocate().

row_log_t::is_instant(): Determine if the log is in the instant format.
Only invoked by the row_log_table_ family of functions.

dict_index_t::get_n_nullable(): Remove is_instant() debug assertions.
Because a table can be converted to non-instant format during a
table-rebuilding ALTER TABLE, these assertions would be bogus when
executing row_log_table_apply().

rec_init_offsets_temp(): Add the parameter n_core for passing the
original index->n_core_fields.

rec_init_offsets_temp(): Add a 3-parameter variant.

rec_init_offsets_comp_ordinary(): Add the parameter n_core for
passing the index->n_core_fields.
parent 01843d19
...@@ -164,4 +164,28 @@ INSERT INTO t11 () VALUES (); ...@@ -164,4 +164,28 @@ INSERT INTO t11 () VALUES ();
UPDATE t11 SET c22 = 1; UPDATE t11 SET c22 = 1;
InnoDB 0 transactions not purged InnoDB 0 transactions not purged
DROP TABLE t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11; DROP TABLE t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11;
#
# MDEV-15060 Assertion in row_log_table_apply_op after instant ADD
# when the table is emptied during subsequent ALTER TABLE
#
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (NULL);
ALTER TABLE t1 ADD COLUMN b INT NOT NULL;
connect stop_purge,localhost,root;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connect ddl,localhost,root,,test;
DELETE FROM t1;
SET DEBUG_SYNC='row_log_table_apply1_before SIGNAL copied WAIT_FOR logged';
ALTER TABLE t1 FORCE;
connection default;
SET DEBUG_SYNC='now WAIT_FOR copied';
BEGIN;
INSERT INTO t1 SET b=1;
ROLLBACK;
disconnect stop_purge;
InnoDB 2 transactions not purged
SET DEBUG_SYNC='now SIGNAL logged';
disconnect ddl;
DROP TABLE t1;
SET DEBUG_SYNC='RESET';
SET GLOBAL innodb_purge_rseg_truncate_frequency = @save_frequency; SET GLOBAL innodb_purge_rseg_truncate_frequency = @save_frequency;
--source include/have_innodb.inc --source include/have_innodb.inc
--source include/have_debug.inc --source include/have_debug.inc
--source include/have_debug_sync.inc --source include/have_debug_sync.inc
--source include/have_innodb.inc
SET @save_frequency= @@GLOBAL.innodb_purge_rseg_truncate_frequency; SET @save_frequency= @@GLOBAL.innodb_purge_rseg_truncate_frequency;
SET GLOBAL innodb_purge_rseg_truncate_frequency=1; SET GLOBAL innodb_purge_rseg_truncate_frequency=1;
...@@ -178,4 +178,52 @@ UPDATE t11 SET c22 = 1; ...@@ -178,4 +178,52 @@ UPDATE t11 SET c22 = 1;
--source include/wait_all_purged.inc --source include/wait_all_purged.inc
DROP TABLE t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11; DROP TABLE t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11;
--echo #
--echo # MDEV-15060 Assertion in row_log_table_apply_op after instant ADD
--echo # when the table is emptied during subsequent ALTER TABLE
--echo #
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (NULL);
ALTER TABLE t1 ADD COLUMN b INT NOT NULL;
connect stop_purge,localhost,root;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connect ddl,localhost,root,,test;
DELETE FROM t1;
SET DEBUG_SYNC='row_log_table_apply1_before SIGNAL copied WAIT_FOR logged';
send ALTER TABLE t1 FORCE;
connection default;
SET DEBUG_SYNC='now WAIT_FOR copied';
BEGIN;
INSERT INTO t1 SET b=1;
ROLLBACK;
disconnect stop_purge;
# Wait for purge to empty the table.
# (This is based on wait_all_purged.inc, but there are 2 transactions
# from the pending ALTER TABLE t1 FORCE.)
let $wait_counter= 300;
while ($wait_counter)
{
--replace_regex /.*History list length ([0-9]+).*/\1/
let $remaining= `SHOW ENGINE INNODB STATUS`;
if ($remaining == 'InnoDB 2')
{
let $wait_counter= 0;
}
if ($wait_counter)
{
real_sleep 0.1;
dec $wait_counter;
}
}
echo $remaining transactions not purged;
SET DEBUG_SYNC='now SIGNAL logged';
disconnect ddl;
DROP TABLE t1;
SET DEBUG_SYNC='RESET';
SET GLOBAL innodb_purge_rseg_truncate_frequency = @save_frequency; SET GLOBAL innodb_purge_rseg_truncate_frequency = @save_frequency;
...@@ -1050,13 +1050,11 @@ struct dict_index_t{ ...@@ -1050,13 +1050,11 @@ struct dict_index_t{
@return number of fields 0..n_prefix-1 that can be set NULL */ @return number of fields 0..n_prefix-1 that can be set NULL */
unsigned get_n_nullable(ulint n_prefix) const unsigned get_n_nullable(ulint n_prefix) const
{ {
DBUG_ASSERT(is_instant());
DBUG_ASSERT(n_prefix > 0); DBUG_ASSERT(n_prefix > 0);
DBUG_ASSERT(n_prefix <= n_fields); DBUG_ASSERT(n_prefix <= n_fields);
unsigned n = n_nullable; unsigned n = n_nullable;
for (; n_prefix < n_fields; n_prefix++) { for (; n_prefix < n_fields; n_prefix++) {
const dict_col_t* col = fields[n_prefix].col; const dict_col_t* col = fields[n_prefix].col;
DBUG_ASSERT(is_dummy || col->is_instant());
DBUG_ASSERT(!col->is_virtual()); DBUG_ASSERT(!col->is_virtual());
n -= col->is_nullable(); n -= col->is_nullable();
} }
......
...@@ -961,15 +961,27 @@ rec_get_converted_size_temp( ...@@ -961,15 +961,27 @@ rec_get_converted_size_temp(
@param[in] rec temporary file record @param[in] rec temporary file record
@param[in] index index of that the record belongs to @param[in] index index of that the record belongs to
@param[in,out] offsets offsets to the fields; in: rec_offs_n_fields(offsets) @param[in,out] offsets offsets to the fields; in: rec_offs_n_fields(offsets)
@param[in] status REC_STATUS_ORDINARY or REC_STATUS_COLUMNS_ADDED @param[in] n_core number of core fields (index->n_core_fields)
*/ @param[in] status REC_STATUS_ORDINARY or REC_STATUS_COLUMNS_ADDED */
void void
rec_init_offsets_temp( rec_init_offsets_temp(
const rec_t* rec, const rec_t* rec,
const dict_index_t* index, const dict_index_t* index,
ulint* offsets, ulint* offsets,
ulint n_core,
rec_comp_status_t status = REC_STATUS_ORDINARY) rec_comp_status_t status = REC_STATUS_ORDINARY)
MY_ATTRIBUTE((nonnull)); MY_ATTRIBUTE((nonnull));
/** Determine the offset to each field in temporary file.
@param[in] rec temporary file record
@param[in] index index of that the record belongs to
@param[in,out] offsets offsets to the fields; in: rec_offs_n_fields(offsets)
*/
void
rec_init_offsets_temp(
const rec_t* rec,
const dict_index_t* index,
ulint* offsets)
MY_ATTRIBUTE((nonnull));
/** Convert a data tuple prefix to the temporary file format. /** Convert a data tuple prefix to the temporary file format.
@param[out] rec record in temporary file format @param[out] rec record in temporary file format
......
...@@ -298,6 +298,7 @@ in ROW_FORMAT=COMPACT,DYNAMIC,COMPRESSED. ...@@ -298,6 +298,7 @@ in ROW_FORMAT=COMPACT,DYNAMIC,COMPRESSED.
This is a special case of rec_init_offsets() and rec_get_offsets_func(). This is a special case of rec_init_offsets() and rec_get_offsets_func().
@param[in] rec leaf-page record @param[in] rec leaf-page record
@param[in] index the index that the record belongs in @param[in] index the index that the record belongs in
@param[in] n_core number of core fields (index->n_core_fields)
@param[in,out] offsets offsets, with valid rec_offs_n_fields(offsets) @param[in,out] offsets offsets, with valid rec_offs_n_fields(offsets)
@param[in] format record format */ @param[in] format record format */
static inline static inline
...@@ -306,17 +307,19 @@ rec_init_offsets_comp_ordinary( ...@@ -306,17 +307,19 @@ rec_init_offsets_comp_ordinary(
const rec_t* rec, const rec_t* rec,
const dict_index_t* index, const dict_index_t* index,
ulint* offsets, ulint* offsets,
ulint n_core,
rec_leaf_format format) rec_leaf_format format)
{ {
ulint offs = 0; ulint offs = 0;
ulint any = 0; ulint any = 0;
const byte* nulls = rec; const byte* nulls = rec;
const byte* lens = NULL; const byte* lens = NULL;
ulint n_fields = index->n_core_fields; ulint n_fields = n_core;
ulint null_mask = 1; ulint null_mask = 1;
ut_ad(index->n_core_fields > 0); ut_ad(index->n_core_fields >= n_core);
ut_ad(index->n_fields >= index->n_core_fields); ut_ad(n_core > 0);
ut_ad(index->n_fields >= n_core);
ut_ad(index->n_core_null_bytes <= UT_BITS_IN_BYTES(index->n_nullable)); ut_ad(index->n_core_null_bytes <= UT_BITS_IN_BYTES(index->n_nullable));
ut_ad(format == REC_LEAF_TEMP || format == REC_LEAF_TEMP_COLUMNS_ADDED ut_ad(format == REC_LEAF_TEMP || format == REC_LEAF_TEMP_COLUMNS_ADDED
|| dict_table_is_comp(index->table)); || dict_table_is_comp(index->table));
...@@ -344,17 +347,17 @@ rec_init_offsets_comp_ordinary( ...@@ -344,17 +347,17 @@ rec_init_offsets_comp_ordinary(
/* We would have !index->is_instant() when rolling back /* We would have !index->is_instant() when rolling back
an instant ADD COLUMN operation. */ an instant ADD COLUMN operation. */
nulls -= REC_N_NEW_EXTRA_BYTES; nulls -= REC_N_NEW_EXTRA_BYTES;
ut_ad(index->is_instant());
/* fall through */ /* fall through */
case REC_LEAF_TEMP_COLUMNS_ADDED: case REC_LEAF_TEMP_COLUMNS_ADDED:
ut_ad(index->is_instant()); n_fields = n_core + 1 + rec_get_n_add_field(nulls);
n_fields = unsigned(index->n_core_fields) + 1
+ rec_get_n_add_field(nulls);
ut_ad(n_fields <= index->n_fields); ut_ad(n_fields <= index->n_fields);
const ulint n_nullable = index->get_n_nullable(n_fields); const ulint n_nullable = index->get_n_nullable(n_fields);
const ulint n_null_bytes = UT_BITS_IN_BYTES(n_nullable); const ulint n_null_bytes = UT_BITS_IN_BYTES(n_nullable);
ut_d(n_null = n_nullable); ut_d(n_null = n_nullable);
ut_ad(n_null <= index->n_nullable); ut_ad(n_null <= index->n_nullable);
ut_ad(n_null_bytes >= index->n_core_null_bytes); ut_ad(n_null_bytes >= index->n_core_null_bytes
|| n_core < index->n_core_fields);
lens = --nulls - n_null_bytes; lens = --nulls - n_null_bytes;
} }
...@@ -614,11 +617,13 @@ rec_init_offsets( ...@@ -614,11 +617,13 @@ rec_init_offsets(
case REC_STATUS_COLUMNS_ADDED: case REC_STATUS_COLUMNS_ADDED:
ut_ad(leaf); ut_ad(leaf);
rec_init_offsets_comp_ordinary(rec, index, offsets, rec_init_offsets_comp_ordinary(rec, index, offsets,
index->n_core_fields,
REC_LEAF_COLUMNS_ADDED); REC_LEAF_COLUMNS_ADDED);
return; return;
case REC_STATUS_ORDINARY: case REC_STATUS_ORDINARY:
ut_ad(leaf); ut_ad(leaf);
rec_init_offsets_comp_ordinary(rec, index, offsets, rec_init_offsets_comp_ordinary(rec, index, offsets,
index->n_core_fields,
REC_LEAF_ORDINARY); REC_LEAF_ORDINARY);
return; return;
} }
...@@ -1689,25 +1694,44 @@ rec_get_converted_size_temp( ...@@ -1689,25 +1694,44 @@ rec_get_converted_size_temp(
@param[in] rec temporary file record @param[in] rec temporary file record
@param[in] index index of that the record belongs to @param[in] index index of that the record belongs to
@param[in,out] offsets offsets to the fields; in: rec_offs_n_fields(offsets) @param[in,out] offsets offsets to the fields; in: rec_offs_n_fields(offsets)
@param[in] status REC_STATUS_ORDINARY or REC_STATUS_COLUMNS_ADDED @param[in] n_core number of core fields (index->n_core_fields)
*/ @param[in] status REC_STATUS_ORDINARY or REC_STATUS_COLUMNS_ADDED */
void void
rec_init_offsets_temp( rec_init_offsets_temp(
const rec_t* rec, const rec_t* rec,
const dict_index_t* index, const dict_index_t* index,
ulint* offsets, ulint* offsets,
ulint n_core,
rec_comp_status_t status) rec_comp_status_t status)
{ {
ut_ad(status == REC_STATUS_ORDINARY ut_ad(status == REC_STATUS_ORDINARY
|| status == REC_STATUS_COLUMNS_ADDED); || status == REC_STATUS_COLUMNS_ADDED);
ut_ad(status == REC_STATUS_ORDINARY || index->is_instant()); /* The table may have been converted to plain format
if it was emptied during an ALTER TABLE operation. */
rec_init_offsets_comp_ordinary(rec, index, offsets, ut_ad(index->n_core_fields == n_core || !index->is_instant());
ut_ad(index->n_core_fields >= n_core);
rec_init_offsets_comp_ordinary(rec, index, offsets, n_core,
status == REC_STATUS_COLUMNS_ADDED status == REC_STATUS_COLUMNS_ADDED
? REC_LEAF_TEMP_COLUMNS_ADDED ? REC_LEAF_TEMP_COLUMNS_ADDED
: REC_LEAF_TEMP); : REC_LEAF_TEMP);
} }
/** Determine the offset to each field in temporary file.
@param[in] rec temporary file record
@param[in] index index of that the record belongs to
@param[in,out] offsets offsets to the fields; in: rec_offs_n_fields(offsets)
*/
void
rec_init_offsets_temp(
const rec_t* rec,
const dict_index_t* index,
ulint* offsets)
{
ut_ad(!index->is_instant());
rec_init_offsets_comp_ordinary(rec, index, offsets,
index->n_core_fields, REC_LEAF_TEMP);
}
/** Convert a data tuple prefix to the temporary file format. /** Convert a data tuple prefix to the temporary file format.
@param[out] rec record in temporary file format @param[out] rec record in temporary file format
@param[in] index clustered or secondary index @param[in] index clustered or secondary index
......
...@@ -221,9 +221,24 @@ struct row_log_t { ...@@ -221,9 +221,24 @@ struct row_log_t {
decryption or NULL */ decryption or NULL */
const char* path; /*!< where to create temporary file during const char* path; /*!< where to create temporary file during
log operation */ log operation */
/** the number of core fields in the clustered index of the
source table; before row_log_table_apply() completes, the
table could be emptied, so that table->is_instant() no longer holds,
but all log records must be in the "instant" format. */
unsigned n_core_fields;
bool ignore; /*!< Whether the alter ignore is being used; bool ignore; /*!< Whether the alter ignore is being used;
if not, NULL values will not be converted to if not, NULL values will not be converted to
defaults */ defaults */
/** Determine whether the log should be in the 'instant ADD' format
@param[in] index the clustered index of the source table
@return whether to use the 'instant ADD COLUMN' format */
bool is_instant(const dict_index_t* index) const
{
ut_ad(table);
ut_ad(n_core_fields <= index->n_fields);
return n_core_fields != index->n_fields;
}
}; };
/** Create the file or online log if it does not exist. /** Create the file or online log if it does not exist.
...@@ -872,12 +887,13 @@ row_log_table_low_redundant( ...@@ -872,12 +887,13 @@ row_log_table_low_redundant(
DATA_ROLL_PTR_LEN); DATA_ROLL_PTR_LEN);
} }
rec_comp_status_t status = index->is_instant() const bool is_instant = index->online_log->is_instant(index);
rec_comp_status_t status = is_instant
? REC_STATUS_COLUMNS_ADDED : REC_STATUS_ORDINARY; ? REC_STATUS_COLUMNS_ADDED : REC_STATUS_ORDINARY;
size = rec_get_converted_size_temp( size = rec_get_converted_size_temp(
index, tuple->fields, tuple->n_fields, &extra_size, status); index, tuple->fields, tuple->n_fields, &extra_size, status);
if (index->is_instant()) { if (is_instant) {
size++; size++;
extra_size++; extra_size++;
} }
...@@ -928,8 +944,8 @@ row_log_table_low_redundant( ...@@ -928,8 +944,8 @@ row_log_table_low_redundant(
} }
if (status == REC_STATUS_COLUMNS_ADDED) { if (status == REC_STATUS_COLUMNS_ADDED) {
ut_ad(index->is_instant()); ut_ad(is_instant);
if (n_fields <= index->n_core_fields) { if (n_fields <= index->online_log->n_core_fields) {
status = REC_STATUS_ORDINARY; status = REC_STATUS_ORDINARY;
} }
*b = status; *b = status;
...@@ -1019,11 +1035,12 @@ row_log_table_low( ...@@ -1019,11 +1035,12 @@ row_log_table_low(
const ulint omit_size = REC_N_NEW_EXTRA_BYTES; const ulint omit_size = REC_N_NEW_EXTRA_BYTES;
const ulint rec_extra_size = rec_offs_extra_size(offsets) - omit_size; const ulint rec_extra_size = rec_offs_extra_size(offsets) - omit_size;
extra_size = rec_extra_size + index->is_instant(); const bool is_instant = index->online_log->is_instant(index);
extra_size = rec_extra_size + is_instant;
mrec_size = ROW_LOG_HEADER_SIZE mrec_size = ROW_LOG_HEADER_SIZE
+ (extra_size >= 0x80) + rec_offs_size(offsets) - omit_size + (extra_size >= 0x80) + rec_offs_size(offsets) - omit_size
+ index->is_instant(); + is_instant;
if (insert || index->online_log->same_pk) { if (insert || index->online_log->same_pk) {
ut_ad(!old_pk); ut_ad(!old_pk);
...@@ -1068,7 +1085,7 @@ row_log_table_low( ...@@ -1068,7 +1085,7 @@ row_log_table_low(
*b++ = static_cast<byte>(extra_size); *b++ = static_cast<byte>(extra_size);
} }
if (index->is_instant()) { if (is_instant) {
*b++ = rec_get_status(rec); *b++ = rec_get_status(rec);
} else { } else {
ut_ad(rec_get_status(rec) == REC_STATUS_ORDINARY); ut_ad(rec_get_status(rec) == REC_STATUS_ORDINARY);
...@@ -2424,6 +2441,7 @@ row_log_table_apply_op( ...@@ -2424,6 +2441,7 @@ row_log_table_apply_op(
return(NULL); return(NULL);
} }
const bool is_instant = log->is_instant(dup->index);
const mrec_t* const mrec_start = mrec; const mrec_t* const mrec_start = mrec;
switch (*mrec++) { switch (*mrec++) {
...@@ -2443,7 +2461,7 @@ row_log_table_apply_op( ...@@ -2443,7 +2461,7 @@ row_log_table_apply_op(
mrec += extra_size; mrec += extra_size;
ut_ad(extra_size || !dup->index->is_instant()); ut_ad(extra_size || !is_instant);
if (mrec > mrec_end) { if (mrec > mrec_end) {
return(NULL); return(NULL);
...@@ -2451,7 +2469,8 @@ row_log_table_apply_op( ...@@ -2451,7 +2469,8 @@ row_log_table_apply_op(
rec_offs_set_n_fields(offsets, dup->index->n_fields); rec_offs_set_n_fields(offsets, dup->index->n_fields);
rec_init_offsets_temp(mrec, dup->index, offsets, rec_init_offsets_temp(mrec, dup->index, offsets,
dup->index->is_instant() log->n_core_fields,
is_instant
? static_cast<rec_comp_status_t>( ? static_cast<rec_comp_status_t>(
*(mrec - extra_size)) *(mrec - extra_size))
: REC_STATUS_ORDINARY); : REC_STATUS_ORDINARY);
...@@ -2535,7 +2554,7 @@ row_log_table_apply_op( ...@@ -2535,7 +2554,7 @@ row_log_table_apply_op(
is not changed, the log will only contain is not changed, the log will only contain
DB_TRX_ID,new_row. */ DB_TRX_ID,new_row. */
if (dup->index->online_log->same_pk) { if (log->same_pk) {
ut_ad(new_index->n_uniq == dup->index->n_uniq); ut_ad(new_index->n_uniq == dup->index->n_uniq);
extra_size = *mrec++; extra_size = *mrec++;
...@@ -2549,7 +2568,7 @@ row_log_table_apply_op( ...@@ -2549,7 +2568,7 @@ row_log_table_apply_op(
mrec += extra_size; mrec += extra_size;
ut_ad(extra_size || !dup->index->is_instant()); ut_ad(extra_size || !is_instant);
if (mrec > mrec_end) { if (mrec > mrec_end) {
return(NULL); return(NULL);
...@@ -2557,7 +2576,8 @@ row_log_table_apply_op( ...@@ -2557,7 +2576,8 @@ row_log_table_apply_op(
rec_offs_set_n_fields(offsets, dup->index->n_fields); rec_offs_set_n_fields(offsets, dup->index->n_fields);
rec_init_offsets_temp(mrec, dup->index, offsets, rec_init_offsets_temp(mrec, dup->index, offsets,
dup->index->is_instant() log->n_core_fields,
is_instant
? static_cast<rec_comp_status_t>( ? static_cast<rec_comp_status_t>(
*(mrec - extra_size)) *(mrec - extra_size))
: REC_STATUS_ORDINARY); : REC_STATUS_ORDINARY);
...@@ -2649,7 +2669,7 @@ row_log_table_apply_op( ...@@ -2649,7 +2669,7 @@ row_log_table_apply_op(
mrec += extra_size; mrec += extra_size;
ut_ad(extra_size || !dup->index->is_instant()); ut_ad(extra_size || !is_instant);
if (mrec > mrec_end) { if (mrec > mrec_end) {
return(NULL); return(NULL);
...@@ -2657,7 +2677,8 @@ row_log_table_apply_op( ...@@ -2657,7 +2677,8 @@ row_log_table_apply_op(
rec_offs_set_n_fields(offsets, dup->index->n_fields); rec_offs_set_n_fields(offsets, dup->index->n_fields);
rec_init_offsets_temp(mrec, dup->index, offsets, rec_init_offsets_temp(mrec, dup->index, offsets,
dup->index->is_instant() log->n_core_fields,
is_instant
? static_cast<rec_comp_status_t>( ? static_cast<rec_comp_status_t>(
*(mrec - extra_size)) *(mrec - extra_size))
: REC_STATUS_ORDINARY); : REC_STATUS_ORDINARY);
...@@ -3211,6 +3232,8 @@ row_log_allocate( ...@@ -3211,6 +3232,8 @@ row_log_allocate(
log->head.blocks = log->head.bytes = 0; log->head.blocks = log->head.bytes = 0;
log->head.total = 0; log->head.total = 0;
log->path = path; log->path = path;
log->n_core_fields = index->n_core_fields;
ut_ad(!table || log->is_instant(index) == index->is_instant());
log->ignore=ignore; log->ignore=ignore;
dict_index_set_online_status(index, ONLINE_INDEX_CREATION); dict_index_set_online_status(index, ONLINE_INDEX_CREATION);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment