From 6b5c0effe43d5cfd19d3aff0fdb5fb6f1b6d41d3 Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Thu, 7 Sep 2017 11:13:08 +0200 Subject: [PATCH 01/14] MDEV-13436 PREPARE doesn't work as expected & throws errors but MySQL is working fine Now we allow derived in the from clause of subqueries so set flag which prevent optimisation of subqueries during view creation before derived processing so subquery will be correctly printed in the view .frm. --- mysql-test/r/view.result | 23 +++++++++++++++++++++++ mysql-test/t/view.test | 21 +++++++++++++++++++++ sql/sql_prepare.cc | 2 +- 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index d0b2675ebb2..36b27871c95 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -6624,5 +6624,28 @@ drop table procViewTable; use test; drop database bugTest; # +# MDEV-13436: PREPARE doesn't work as expected & throws errors but +# MySQL is working fine +# +create table t1 (a int); +insert into t1 values (1),(2); +SET @sql_query = " + CREATE VIEW v1 AS + SELECT * FROM ( + SELECT CASE WHEN 1 IN (SELECT a from t1 where a < 2) THEN TRUE END AS testcase + ) testalias +"; +PREPARE stmt FROM @sql_query; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +show create view v1; +View Create View character_set_client collation_connection +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `testalias`.`testcase` AS `testcase` from (select case when 1 in (select `test`.`t1`.`a` from `test`.`t1` where `test`.`t1`.`a` < 2) then 1 end AS `testcase`) `testalias` latin1 latin1_swedish_ci +SELECT * FROM v1; +testcase +1 +drop view v1; +drop table t1; +# # End of 10.2 tests # diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index e2164e438dc..a9764bf0668 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -6344,6 +6344,27 @@ drop table procViewTable; use test; drop database bugTest; +--echo # +--echo # MDEV-13436: PREPARE doesn't work as expected & throws errors but +--echo # MySQL is working fine +--echo # + +create table t1 (a int); +insert into t1 values (1),(2); +SET @sql_query = " + CREATE VIEW v1 AS + SELECT * FROM ( + SELECT CASE WHEN 1 IN (SELECT a from t1 where a < 2) THEN TRUE END AS testcase + ) testalias +"; +PREPARE stmt FROM @sql_query; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +show create view v1; +SELECT * FROM v1; +drop view v1; +drop table t1; + --echo # --echo # End of 10.2 tests --echo # diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index f9da7e530cb..c8ebbb08f15 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2097,11 +2097,11 @@ static bool mysql_test_create_view(Prepared_statement *stmt) if (thd->open_temporary_tables(tables)) goto err; + lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW; if (open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL, DT_PREPARE)) goto err; - lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW; res= select_like_stmt_test(stmt, 0, 0); err: From 31774f0ede81d77d889061324930d3d0066c653a Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 11 Sep 2017 16:45:36 +0000 Subject: [PATCH 02/14] MDEV-13563 lock DDL for mariabackup in 10.2 Implement lock-ddl-per-table option that locks tables before it is copied to backup, and helds the lock until backup finished The "DDL-lock" itself is implemented as "SELECT * from LIMIT 0", inside a transaction, and "COMMIT" of this transaction is the DDL-unlock. --- extra/mariabackup/backup_mysql.cc | 94 +++++++++++++++++++ extra/mariabackup/xtrabackup.cc | 22 ++++- extra/mariabackup/xtrabackup.h | 4 + .../mariabackup/lock_ddl_per_table.result | 4 + .../suite/mariabackup/lock_ddl_per_table.test | 12 +++ mysql-test/suite/mariabackup/suite.opt | 2 +- 6 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 mysql-test/suite/mariabackup/lock_ddl_per_table.result create mode 100644 mysql-test/suite/mariabackup/lock_ddl_per_table.test diff --git a/extra/mariabackup/backup_mysql.cc b/extra/mariabackup/backup_mysql.cc index edd369e6ea9..60985dbb519 100644 --- a/extra/mariabackup/backup_mysql.cc +++ b/extra/mariabackup/backup_mysql.cc @@ -117,6 +117,7 @@ xb_mysql_connect() mysql_options(connection, MYSQL_PLUGIN_DIR, xb_plugin_dir); } mysql_options(connection, MYSQL_OPT_PROTOCOL, &opt_protocol); + mysql_options(connection,MYSQL_SET_CHARSET_NAME, "utf8"); msg_ts("Connecting to MySQL server host: %s, user: %s, password: %s, " "port: %s, socket: %s\n", opt_host ? opt_host : "localhost", @@ -1629,3 +1630,96 @@ backup_cleanup() mysql_close(mysql_connection); } } + + +static pthread_mutex_t mdl_lock_con_mutex; +static MYSQL *mdl_con = NULL; + +void +mdl_lock_init() +{ + pthread_mutex_init(&mdl_lock_con_mutex, NULL); + mdl_con = xb_mysql_connect(); + if (mdl_con) + { + xb_mysql_query(mdl_con, "BEGIN", false, true); + } +} + +#ifndef DBUF_OFF +/* Test that table is really locked, if lock_ddl_per_table is set. + The test is executed in DBUG_EXECUTE_IF block inside mdl_lock_table(). +*/ +static void check_mdl_lock_works(const char *table_name) +{ + MYSQL *test_con= xb_mysql_connect(); + char *query; + xb_a(asprintf(&query, + "SET STATEMENT max_statement_time=1 FOR ALTER TABLE %s" + " ADD COLUMN mdl_lock_column int", table_name)); + int err = mysql_query(test_con, query); + DBUG_ASSERT(err); + int err_no = mysql_errno(test_con); + DBUG_ASSERT(err_no == ER_STATEMENT_TIMEOUT); + mysql_close(test_con); +} +#endif + +extern void +dict_fs2utf8(const char*, char*, size_t, char*, size_t); + +void +mdl_lock_table(ulint space_id) +{ + char *query; + + pthread_mutex_lock(&mdl_lock_con_mutex); + + xb_a(asprintf(&query, + "SELECT NAME FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES " + "WHERE SPACE = %llu AND NAME LIKE '%%/%%'", (ulonglong)space_id)); + + xb_mysql_query(mdl_con, query, true, true); + + MYSQL_RES *mysql_result = xb_mysql_query(mdl_con, query, true); + + MYSQL_ROW row; + while ((row = mysql_fetch_row(mysql_result))) { + char full_table_name[2*FN_REFLEN +2]; + char db_utf8[FN_REFLEN]; + char table_utf8[FN_REFLEN]; + + dict_fs2utf8(row[0], db_utf8, sizeof(db_utf8),table_utf8,sizeof(table_utf8)); + snprintf(full_table_name,sizeof(full_table_name),"`%s`.`%s`",db_utf8,table_utf8); + + char *lock_query; + + msg_ts("Locking MDL for %s\n", full_table_name); + + xb_a(asprintf(&lock_query, + "SELECT * FROM %s LIMIT 0", + full_table_name)); + + xb_mysql_query(mdl_con, lock_query, false, false); + + free(lock_query); + + DBUG_EXECUTE_IF("check_mdl_lock_works", + check_mdl_lock_works(full_table_name);); + } + + mysql_free_result(mysql_result); + free(query); + pthread_mutex_unlock(&mdl_lock_con_mutex); +} + + +void +mdl_unlock_all() +{ + msg_ts("Unlocking MDL for all tables"); + xb_mysql_query(mdl_con, "COMMIT", false, true); + mysql_close(mdl_con); + pthread_mutex_destroy(&mdl_lock_con_mutex); +} + diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc index 65aa913cc29..5d8bc7eaaf3 100644 --- a/extra/mariabackup/xtrabackup.cc +++ b/extra/mariabackup/xtrabackup.cc @@ -296,6 +296,8 @@ my_bool opt_noversioncheck = FALSE; my_bool opt_no_backup_locks = FALSE; my_bool opt_decompress = FALSE; +my_bool opt_lock_ddl_per_table = FALSE; + static const char *binlog_info_values[] = {"off", "lockless", "on", "auto", NullS}; static TYPELIB binlog_info_typelib = {array_elements(binlog_info_values)-1, "", @@ -537,7 +539,8 @@ enum options_xtrabackup OPT_XTRA_TABLES_EXCLUDE, OPT_XTRA_DATABASES_EXCLUDE, - OPT_PROTOCOL + OPT_PROTOCOL, + OPT_LOCK_DDL_PER_TABLE }; struct my_option xb_client_options[] = @@ -1072,6 +1075,11 @@ struct my_option xb_server_options[] = (G_PTR*) &xb_open_files_limit, (G_PTR*) &xb_open_files_limit, 0, GET_ULONG, REQUIRED_ARG, 0, 0, UINT_MAX, 0, 1, 0}, + {"lock-ddl-per-table", OPT_LOCK_DDL_PER_TABLE, "Lock DDL for each table " + "before xtrabackup starts to copy it and until the backup is completed.", + (uchar*) &opt_lock_ddl_per_table, (uchar*) &opt_lock_ddl_per_table, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; @@ -2205,6 +2213,10 @@ xtrabackup_copy_datafile(fil_node_t* node, uint thread_n) return(FALSE); } + if (opt_lock_ddl_per_table) { + mdl_lock_table(node->space->id); + } + if (!changed_page_bitmap) { read_filter = &rf_pass_through; } @@ -3552,6 +3564,10 @@ xtrabackup_backup_func() "or RENAME TABLE during the backup, inconsistent backup will be " "produced.\n"); + if (opt_lock_ddl_per_table) { + mdl_lock_init(); + } + /* initialize components */ if(innodb_init_param()) { fail: @@ -3930,6 +3946,10 @@ reread_log_header: goto fail; } + if (opt_lock_ddl_per_table) { + mdl_unlock_all(); + } + xtrabackup_destroy_datasinks(); msg("xtrabackup: Redo log (from LSN " LSN_PF " to " LSN_PF diff --git a/extra/mariabackup/xtrabackup.h b/extra/mariabackup/xtrabackup.h index b226a9d1d83..a24626c48bc 100644 --- a/extra/mariabackup/xtrabackup.h +++ b/extra/mariabackup/xtrabackup.h @@ -193,4 +193,8 @@ xb_get_one_option(int optid, const char* xb_get_copy_action(const char *dflt = "Copying"); +void mdl_lock_init(); +void mdl_lock_table(ulint space_id); +void mdl_unlock_all(); + #endif /* XB_XTRABACKUP_H */ diff --git a/mysql-test/suite/mariabackup/lock_ddl_per_table.result b/mysql-test/suite/mariabackup/lock_ddl_per_table.result new file mode 100644 index 00000000000..d0137a1e072 --- /dev/null +++ b/mysql-test/suite/mariabackup/lock_ddl_per_table.result @@ -0,0 +1,4 @@ +CREATE TABLE t(i INT) ENGINE INNODB; +INSERT INTO t VALUES(1); +# xtrabackup backup +DROP TABLE t; diff --git a/mysql-test/suite/mariabackup/lock_ddl_per_table.test b/mysql-test/suite/mariabackup/lock_ddl_per_table.test new file mode 100644 index 00000000000..5b45aa7c61b --- /dev/null +++ b/mysql-test/suite/mariabackup/lock_ddl_per_table.test @@ -0,0 +1,12 @@ +--source include/have_debug.inc + +CREATE TABLE t(i INT) ENGINE INNODB; +INSERT INTO t VALUES(1); +echo # xtrabackup backup; +let $targetdir=$MYSQLTEST_VARDIR/tmp/backup; + +--disable_result_log +exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$targetdir --lock-ddl-per-table=1 --dbug=+d,check_mdl_lock_works; +--enable_result_log +DROP TABLE t; +rmdir $targetdir; \ No newline at end of file diff --git a/mysql-test/suite/mariabackup/suite.opt b/mysql-test/suite/mariabackup/suite.opt index ba78b2d8957..defba074293 100644 --- a/mysql-test/suite/mariabackup/suite.opt +++ b/mysql-test/suite/mariabackup/suite.opt @@ -1 +1 @@ ---innodb --loose-changed_page_bitmaps --innodb-file-format=Barracuda \ No newline at end of file +--innodb --loose-changed_page_bitmaps --innodb-file-format=Barracuda --innodb-sys-tables \ No newline at end of file From 6352ec2184dc8e5744aed3f718fc635edb4b511f Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 12 Sep 2017 11:20:30 +0400 Subject: [PATCH 03/14] MDEV-12982 JSON_EXTRACT returns data for invalid JSON. Let's check the validity to the end of the JSON. --- mysql-test/r/func_json.result | 5 +++++ mysql-test/t/func_json.test | 5 +++++ sql/item_jsonfunc.cc | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/mysql-test/r/func_json.result b/mysql-test/r/func_json.result index aa8a2850a58..f89c5f1c71b 100644 --- a/mysql-test/r/func_json.result +++ b/mysql-test/r/func_json.result @@ -669,3 +669,8 @@ JSON_EXTRACT('{\"asdf\":true}', "$.\"asdf\"") = 1 select JSON_EXTRACT('{\"input1\":\"\\u00f6\"}', '$.\"input1\"'); JSON_EXTRACT('{\"input1\":\"\\u00f6\"}', '$.\"input1\"') "\u00f6" +select JSON_EXTRACT('{"foo": "bar" foobar foo invalid ', '$.foo'); +JSON_EXTRACT('{"foo": "bar" foobar foo invalid ', '$.foo') +NULL +Warnings: +Warning 4038 Syntax error in JSON text in argument 1 to function 'json_extract' at position 15 diff --git a/mysql-test/t/func_json.test b/mysql-test/t/func_json.test index 3bbda0de409..eba492b72e8 100644 --- a/mysql-test/t/func_json.test +++ b/mysql-test/t/func_json.test @@ -317,3 +317,8 @@ select JSON_EXTRACT('{\"asdf\":true}', "$.\"asdf\"") = false; select JSON_EXTRACT('{\"asdf\":true}', "$.\"asdf\"") = 1; select JSON_EXTRACT('{\"input1\":\"\\u00f6\"}', '$.\"input1\"'); +# +# MDEV-129892 JSON_EXTRACT returns data for invalid JSON +# +select JSON_EXTRACT('{"foo": "bar" foobar foo invalid ', '$.foo'); + diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index c2a9ca28c99..c30ce45bfb2 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -821,7 +821,11 @@ String *Item_func_json_extract::read_json(String *str, not_first_value= 1; if (!possible_multiple_values) + { + /* Loop to the end of the JSON just to make sure it's valid. */ + while (json_get_path_next(&je, &p) == 0) {} break; + } } if (je.s.error) From ea876b39f33af6b7d54f2a0a5def0fbf40b30e27 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 12 Sep 2017 11:58:49 +0400 Subject: [PATCH 04/14] MDEV-13716 point_big test fails in 10.2. Implementation for the reverse spatial predicates was lost somehow. --- mysql-test/suite/innodb_gis/r/rtree.result | 6 ++++-- sql/item_geofunc.h | 13 ++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/innodb_gis/r/rtree.result b/mysql-test/suite/innodb_gis/r/rtree.result index 285a499b16c..d6604314909 100644 --- a/mysql-test/suite/innodb_gis/r/rtree.result +++ b/mysql-test/suite/innodb_gis/r/rtree.result @@ -56,6 +56,7 @@ INSERT INTO t1 VALUES("left3", ST_GeomFromText('POLYGON (( -3 0, -3 2, -1 2, -1 SET @p = ST_GeomFromText('POLYGON (( 0 0, 0 2, 2 2, 2 0, 0 0))'); SELECT name, ST_AsText(square) from t1 where MBRContains(@p, square); name ST_AsText(square) +small POLYGON((0 0,0 1,1 1,1 0,0 0)) SELECT name, ST_AsText(square) from t1 where MBRDisjoint(@p, square); name ST_AsText(square) up3 POLYGON((0 3,0 5,2 5,2 3,0 3)) @@ -90,6 +91,7 @@ down2 POLYGON((0 -2,0 0,2 0,2 -2,0 -2)) left2 POLYGON((-2 0,-2 2,0 2,0 0,-2 0)) SELECT name, ST_AsText(square) from t1 where MBRWithin(@p, square); name ST_AsText(square) +big POLYGON((0 0,0 3,3 3,3 0,0 0)) SET @vert1 = ST_GeomFromText('POLYGON ((0 -2, 0 2, 0 -2))'); SET @horiz1 = ST_GeomFromText('POLYGON ((-2 0, 2 0, -2 0))'); SET @horiz2 = ST_GeomFromText('POLYGON ((-1 0, 3 0, -1 0))'); @@ -217,7 +219,7 @@ SELECT COUNT(*) FROM t1 WHERE ST_CONTAINS(ST_GeomFromText('POLYGON((2 2,4 2, 4 4, 2 4, 2 2))'),way); COUNT(*) -0 +9 OPTIMIZE TABLE t1; Table Op Msg_type Msg_text test.t1 optimize note Table does not support optimize, doing recreate + analyze instead @@ -226,7 +228,7 @@ SELECT COUNT(*) FROM t1 WHERE ST_CONTAINS(ST_GeomFromText('POLYGON((2 2,4 2, 4 4, 2 4, 2 2))'),way); COUNT(*) -0 +9 DROP TABLE t1; CREATE TABLE t1( i INT, g GEOMETRY NOT NULL, SPATIAL INDEX (g)) ENGINE=InnoDB; INSERT INTO t1 VALUES(1, LINESTRING(POINT(1,1), POINT(4, 4))); diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h index abe189395ec..199bc1f47de 100644 --- a/sql/item_geofunc.h +++ b/sql/item_geofunc.h @@ -349,7 +349,18 @@ public: maybe_null= true; } enum Functype functype() const { return spatial_rel; } - enum Functype rev_functype() const { return spatial_rel; } + enum Functype rev_functype() const + { + switch (spatial_rel) + { + case SP_CONTAINS_FUNC: + return SP_WITHIN_FUNC; + case SP_WITHIN_FUNC: + return SP_CONTAINS_FUNC; + default: + return spatial_rel; + } + } bool is_null() { (void) val_int(); return null_value; } void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, table_map usable_tables, From 8ee4b414ae071b1c666b97bac1808bc67a260e78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 12 Sep 2017 09:22:11 +0300 Subject: [PATCH 05/14] Relax a too strict debug assertion for Mariabackup --prepare --- storage/innobase/trx/trx0trx.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index ac23234179c..2408e4bdaf4 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -619,6 +619,7 @@ trx_free_prepared( || (trx_state_eq(trx, TRX_STATE_ACTIVE) && trx->is_recovered && (!srv_was_started + || srv_operation == SRV_OPERATION_RESTORE || srv_read_only_mode || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO))); ut_a(trx->magic_n == TRX_MAGIC_N); From 66a09bd6aba0f7b74492d198146bb8d47cdd9dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 11 Sep 2017 16:47:23 +0300 Subject: [PATCH 06/14] MDEV-13318 Crash recovery failure after the server is killed during innodb_encrypt_log startup This fixes several InnoDB bugs related to innodb_encrypt_log and two Mariabackup --backup bugs. log_crypt(): Properly derive the initialization vector from the start LSN of each block. Add a debug assertion. log_crypt_init(): Note that the function should only be used when creating redo log files and that the information is persisted in the checkpoint pages. xtrabackup_copy_log(): Validate data_len. xtrabackup_backup_func(): Always use the chosen checkpoint buffer. log_group_write_buf(), log_write_up_to(): Only log_crypt() the redo log payload, not the padding bytes. innobase_start_or_create_for_mysql(): Do not invoke log_crypt_init() or initiate a redo log checkpoint. recv_find_max_checkpoint(): Return the contents of LOG_CHECKPOINT_NO to xtrabackup_backup_func() in log_sys->next_checkpoint_no. --- extra/mariabackup/xtrabackup.cc | 27 +++++++++++++++++---------- storage/innobase/include/log0crypt.h | 9 +++++++-- storage/innobase/log/log0crypt.cc | 21 +++++++++++++-------- storage/innobase/log/log0log.cc | 10 ++++++---- storage/innobase/log/log0recv.cc | 4 +++- storage/innobase/srv/srv0start.cc | 15 --------------- 6 files changed, 46 insertions(+), 40 deletions(-) diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc index 5d8bc7eaaf3..0027de960c1 100644 --- a/extra/mariabackup/xtrabackup.cc +++ b/extra/mariabackup/xtrabackup.cc @@ -2359,10 +2359,18 @@ xtrabackup_copy_log(copy_logfile copy, lsn_t start_lsn, lsn_t end_lsn) scanned_checkpoint = checkpoint; ulint data_len = log_block_get_data_len(log_block); - scanned_lsn += data_len; - if (data_len != OS_FILE_LOG_BLOCK_SIZE) { - /* The current end of the log was reached. */ + if (data_len == OS_FILE_LOG_BLOCK_SIZE) { + /* We got a full log block. */ + scanned_lsn += data_len; + } else if (data_len + >= OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE + || data_len <= LOG_BLOCK_HDR_SIZE) { + /* We got a garbage block (abrupt end of the log). */ + break; + } else { + /* We got a partial block (abrupt end of the log). */ + scanned_lsn += data_len; break; } } @@ -2375,7 +2383,7 @@ xtrabackup_copy_log(copy_logfile copy, lsn_t start_lsn, lsn_t end_lsn) if (ulint write_size = ulint(end_lsn - start_lsn)) { if (srv_encrypt_log) { - log_crypt(log_sys->buf, write_size); + log_crypt(log_sys->buf, start_lsn, write_size); } if (ds_write(dst_log_file, log_sys->buf, write_size)) { @@ -3757,10 +3765,10 @@ old_format: const byte* buf = log_sys->checkpoint_buf; - checkpoint_lsn_start = mach_read_from_8(buf + LOG_CHECKPOINT_LSN); - checkpoint_no_start = mach_read_from_8(buf + LOG_CHECKPOINT_NO); - reread_log_header: + checkpoint_lsn_start = log_sys->log.lsn; + checkpoint_no_start = log_sys->next_checkpoint_no; + err = recv_find_max_checkpoint(&max_cp_field); if (err != DB_SUCCESS) { @@ -3774,10 +3782,9 @@ reread_log_header: ut_ad(!((log_sys->log.format ^ LOG_HEADER_FORMAT_CURRENT) & ~LOG_HEADER_FORMAT_ENCRYPTED)); - if (checkpoint_no_start != mach_read_from_8(buf + LOG_CHECKPOINT_NO)) { + log_group_header_read(&log_sys->log, max_cp_field); - checkpoint_lsn_start = mach_read_from_8(buf + LOG_CHECKPOINT_LSN); - checkpoint_no_start = mach_read_from_8(buf + LOG_CHECKPOINT_NO); + if (checkpoint_no_start != mach_read_from_8(buf + LOG_CHECKPOINT_NO)) { goto reread_log_header; } diff --git a/storage/innobase/include/log0crypt.h b/storage/innobase/include/log0crypt.h index d1282043665..a5f7c56cc3f 100644 --- a/storage/innobase/include/log0crypt.h +++ b/storage/innobase/include/log0crypt.h @@ -32,7 +32,11 @@ MDEV-11782: Rewritten for MariaDB 10.2 by Marko Mäkelä, MariaDB Corporation. /** innodb_encrypt_log: whether to encrypt the redo log */ extern my_bool srv_encrypt_log; -/** Initialize the redo log encryption key. +/** Initialize the redo log encryption key and random parameters +when creating a new redo log. +The random parameters will be persisted in the log checkpoint pages. +@see log_crypt_write_checkpoint_buf() +@see log_crypt_read_checkpoint_buf() @return whether the operation succeeded */ UNIV_INTERN bool @@ -71,10 +75,11 @@ log_crypt_read_checkpoint_buf(const byte* buf); /** Encrypt or decrypt log blocks. @param[in,out] buf log blocks to encrypt or decrypt +@param[in] lsn log sequence number of the start of the buffer @param[in] size size of the buffer, in bytes @param[in] decrypt whether to decrypt instead of encrypting */ UNIV_INTERN void -log_crypt(byte* buf, ulint size, bool decrypt = false); +log_crypt(byte* buf, lsn_t lsn, ulint size, bool decrypt = false); #endif // log0crypt.h diff --git a/storage/innobase/log/log0crypt.cc b/storage/innobase/log/log0crypt.cc index 69cfec10fed..c0a10c74073 100644 --- a/storage/innobase/log/log0crypt.cc +++ b/storage/innobase/log/log0crypt.cc @@ -103,11 +103,12 @@ get_crypt_info(ulint checkpoint_no) /** Encrypt or decrypt log blocks. @param[in,out] buf log blocks to encrypt or decrypt +@param[in] lsn log sequence number of the start of the buffer @param[in] size size of the buffer, in bytes @param[in] decrypt whether to decrypt instead of encrypting */ UNIV_INTERN void -log_crypt(byte* buf, ulint size, bool decrypt) +log_crypt(byte* buf, lsn_t lsn, ulint size, bool decrypt) { ut_ad(size % OS_FILE_LOG_BLOCK_SIZE == 0); ut_a(info.key_version); @@ -117,12 +118,12 @@ log_crypt(byte* buf, ulint size, bool decrypt) compile_time_assert(sizeof(uint32_t) == 4); #define LOG_CRYPT_HDR_SIZE 4 + lsn &= ~lsn_t(OS_FILE_LOG_BLOCK_SIZE - 1); for (const byte* const end = buf + size; buf != end; - buf += OS_FILE_LOG_BLOCK_SIZE) { + buf += OS_FILE_LOG_BLOCK_SIZE, lsn += OS_FILE_LOG_BLOCK_SIZE) { uint32_t dst[(OS_FILE_LOG_BLOCK_SIZE - LOG_CRYPT_HDR_SIZE) / sizeof(uint32_t)]; - const ulint log_block_no = log_block_get_hdr_no(buf); /* The log block number is not encrypted. */ *aes_ctr_iv = @@ -137,10 +138,10 @@ log_crypt(byte* buf, ulint size, bool decrypt) # error "LOG_BLOCK_HDR_NO has been moved; redo log format affected!" #endif aes_ctr_iv[1] = info.crypt_nonce.word; - mach_write_to_8(reinterpret_cast(aes_ctr_iv + 2), - log_block_get_start_lsn( - decrypt ? srv_start_lsn : log_sys->lsn, - log_block_no)); + mach_write_to_8(reinterpret_cast(aes_ctr_iv + 2), lsn); + ut_ad(log_block_get_start_lsn(lsn, + log_block_get_hdr_no(buf)) + == lsn); int rc = encryption_crypt( buf + LOG_CRYPT_HDR_SIZE, sizeof dst, @@ -206,7 +207,11 @@ init_crypt_key(crypt_info_t* info, bool upgrade = false) return true; } -/** Initialize the redo log encryption key. +/** Initialize the redo log encryption key and random parameters +when creating a new redo log. +The random parameters will be persisted in the log checkpoint pages. +@see log_crypt_write_checkpoint_buf() +@see log_crypt_read_checkpoint_buf() @return whether the operation succeeded */ UNIV_INTERN bool diff --git a/storage/innobase/log/log0log.cc b/storage/innobase/log/log0log.cc index 463914982cf..f7974d243d8 100644 --- a/storage/innobase/log/log0log.cc +++ b/storage/innobase/log/log0log.cc @@ -997,10 +997,6 @@ loop: || log_block_get_hdr_no(buf) == log_block_convert_lsn_to_no(start_lsn)); - if (log_sys->is_encrypted()) { - log_crypt(buf, write_len); - } - /* Calculate the checksums for each log block and write them to the trailer fields of the log blocks */ @@ -1264,6 +1260,12 @@ loop: ::memset(write_buf + area_end, 0, pad_size); } } + + if (log_sys->is_encrypted()) { + log_crypt(write_buf + area_start, log_sys->write_lsn, + area_end - area_start); + } + /* Do the write to the log files */ log_group_write_buf( &log_sys->log, write_buf + area_start, diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index 8bc8df750e4..fd0940b08df 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -715,7 +715,8 @@ loop: } if (group->is_encrypted()) { - log_crypt(buf, OS_FILE_LOG_BLOCK_SIZE, true); + log_crypt(buf, start_lsn, + OS_FILE_LOG_BLOCK_SIZE, true); } } } @@ -1016,6 +1017,7 @@ recv_find_max_checkpoint(ulint* max_field) buf + LOG_CHECKPOINT_LSN); group->lsn_offset = mach_read_from_8( buf + LOG_CHECKPOINT_OFFSET); + log_sys->next_checkpoint_no = checkpoint_no; } } diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc index 21c1343f5b3..66866c98e62 100644 --- a/storage/innobase/srv/srv0start.cc +++ b/storage/innobase/srv/srv0start.cc @@ -2223,14 +2223,6 @@ files_checked: recv_sys->dblwr.pages.clear(); - if (err == DB_SUCCESS && !srv_read_only_mode) { - log_mutex_enter(); - if (log_sys->is_encrypted() && !log_crypt_init()) { - err = DB_ERROR; - } - log_mutex_exit(); - } - if (err == DB_SUCCESS) { /* Initialize the change buffer. */ err = dict_boot(); @@ -2733,13 +2725,6 @@ files_checked: fil_crypt_threads_init(); fil_system_exit(); - /* - Create a checkpoint before logging anything new, so that - the current encryption key in use is definitely logged - before any log blocks encrypted with that key. - */ - log_make_checkpoint_at(LSN_MAX, TRUE); - /* Initialize online defragmentation. */ btr_defragment_init(); btr_defragment_thread_active = true; From 594c6b37f3dacf537e804c7a9264eeb4d84b5671 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 12 Sep 2017 13:26:03 +0400 Subject: [PATCH 07/14] MDEV-13138 JSON_OBJECT returns null with strings containing backticks. Condition was fixed. --- mysql-test/r/func_json.result | 6 ++++++ mysql-test/t/func_json.test | 6 ++++++ strings/json_lib.c | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/func_json.result b/mysql-test/r/func_json.result index f89c5f1c71b..d11c0273024 100644 --- a/mysql-test/r/func_json.result +++ b/mysql-test/r/func_json.result @@ -674,3 +674,9 @@ JSON_EXTRACT('{"foo": "bar" foobar foo invalid ', '$.foo') NULL Warnings: Warning 4038 Syntax error in JSON text in argument 1 to function 'json_extract' at position 15 +SELECT JSON_OBJECT('foo', '`'); +JSON_OBJECT('foo', '`') +{"foo": "`"} +SELECT JSON_OBJECT("foo", "bar`bar"); +JSON_OBJECT("foo", "bar`bar") +{"foo": "bar`bar"} diff --git a/mysql-test/t/func_json.test b/mysql-test/t/func_json.test index eba492b72e8..3b978c1a1cf 100644 --- a/mysql-test/t/func_json.test +++ b/mysql-test/t/func_json.test @@ -322,3 +322,9 @@ select JSON_EXTRACT('{\"input1\":\"\\u00f6\"}', '$.\"input1\"'); # select JSON_EXTRACT('{"foo": "bar" foobar foo invalid ', '$.foo'); +# +# MDEV-13138 JSON_OBJECT returns null with strings containing backticks. +# +SELECT JSON_OBJECT('foo', '`'); +SELECT JSON_OBJECT("foo", "bar`bar"); + diff --git a/strings/json_lib.c b/strings/json_lib.c index b0c843caec1..25e5e81e6f9 100644 --- a/strings/json_lib.c +++ b/strings/json_lib.c @@ -1586,7 +1586,7 @@ int json_escape(CHARSET_INFO *str_cs, enum json_esc_char_classes c_class; str+= c_len; - if (c_chr > 0x60 || (c_class= json_escape_chr_map[c_chr]) == ESC_) + if (c_chr >= 0x60 || (c_class= json_escape_chr_map[c_chr]) == ESC_) { if ((c_len= json_cs->cset->wc_mb(json_cs, c_chr, json, json_end)) > 0) { From 49878be33107893452155bf1099cd600cc23fb91 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Tue, 12 Sep 2017 13:32:09 +0300 Subject: [PATCH 08/14] MDEV-13602: rocksdb.index_merge_rocksdb2 failed in buildbot Part#2: stabilize the test output --- .../rocksdb/mysql-test/rocksdb/include/index_merge1.inc | 8 ++++++++ .../mysql-test/rocksdb/r/index_merge_rocksdb2.result | 8 ++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/storage/rocksdb/mysql-test/rocksdb/include/index_merge1.inc b/storage/rocksdb/mysql-test/rocksdb/include/index_merge1.inc index d18faea61a8..91bcc68adb4 100644 --- a/storage/rocksdb/mysql-test/rocksdb/include/index_merge1.inc +++ b/storage/rocksdb/mysql-test/rocksdb/include/index_merge1.inc @@ -402,12 +402,20 @@ analyze table t1; -- enable_query_log # index_merge on first table in join +if ($index_merge_random_rows_in_EXPLAIN) +{ + --replace_column 9 # +} explain select * from t0 left join t1 on (t0.key1=t1.key1) where t0.key1=3 or t0.key2=4; select * from t0 left join t1 on (t0.key1=t1.key1) where t0.key1=3 or t0.key2=4; +if ($index_merge_random_rows_in_EXPLAIN) +{ + --replace_column 9 # +} explain select * from t0,t1 where (t0.key1=t1.key1) and ( t0.key1=3 or t0.key2=4); diff --git a/storage/rocksdb/mysql-test/rocksdb/r/index_merge_rocksdb2.result b/storage/rocksdb/mysql-test/rocksdb/r/index_merge_rocksdb2.result index bd9983d68a4..ff33e6061b3 100644 --- a/storage/rocksdb/mysql-test/rocksdb/r/index_merge_rocksdb2.result +++ b/storage/rocksdb/mysql-test/rocksdb/r/index_merge_rocksdb2.result @@ -250,8 +250,8 @@ insert into t1 select * from t0; explain select * from t0 left join t1 on (t0.key1=t1.key1) where t0.key1=3 or t0.key2=4; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 2 Using union(i1,i2); Using where -1 SIMPLE t1 ref i1 i1 4 test.t0.key1 2 +1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL # Using union(i1,i2); Using where +1 SIMPLE t1 ref i1 i1 4 test.t0.key1 # select * from t0 left join t1 on (t0.key1=t1.key1) where t0.key1=3 or t0.key2=4; key1 key2 key3 key4 key5 key6 key7 key8 key1 key2 key3 key4 key5 key6 key7 key8 @@ -260,8 +260,8 @@ key1 key2 key3 key4 key5 key6 key7 key8 key1 key2 key3 key4 key5 key6 key7 key8 explain select * from t0,t1 where (t0.key1=t1.key1) and ( t0.key1=3 or t0.key2=4); id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 2 Using union(i1,i2); Using where -1 SIMPLE t1 ref i1 i1 4 test.t0.key1 2 +1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL # Using union(i1,i2); Using where +1 SIMPLE t1 ref i1 i1 4 test.t0.key1 # explain select * from t0,t1 where (t0.key1=t1.key1) and (t0.key1=3 or t0.key2<4) and t1.key1=2; From 467acc2119e9408b640915f767515b9242a4d6a3 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 12 Sep 2017 14:40:18 +0400 Subject: [PATCH 09/14] MDEV-13324 JSON_SET returns NULL instead of object. Superfluous ',' was added to the JSON_SET result so it became invalid. --- mysql-test/r/func_json.result | 3 +++ mysql-test/suite/json/r/json_no_table.result | 3 +-- mysql-test/t/func_json.test | 5 +++++ sql/item_jsonfunc.cc | 6 +++++- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/func_json.result b/mysql-test/r/func_json.result index d11c0273024..80de41ccb07 100644 --- a/mysql-test/r/func_json.result +++ b/mysql-test/r/func_json.result @@ -680,3 +680,6 @@ JSON_OBJECT('foo', '`') SELECT JSON_OBJECT("foo", "bar`bar"); JSON_OBJECT("foo", "bar`bar") {"foo": "bar`bar"} +SELECT JSON_SET('{}', '$.age', 87); +JSON_SET('{}', '$.age', 87) +{"age": 87} diff --git a/mysql-test/suite/json/r/json_no_table.result b/mysql-test/suite/json/r/json_no_table.result index cf4cd5b8af8..034a4e6c0a6 100644 --- a/mysql-test/suite/json/r/json_no_table.result +++ b/mysql-test/suite/json/r/json_no_table.result @@ -2109,10 +2109,9 @@ ERROR 42000: Incorrect parameter count in the call to native function 'json_set' error ER_INVALID_JSON_TEXT_IN_PARAM SELECT JSON_SET('{}', '$.name', JSON_EXTRACT('', '$')); JSON_SET('{}', '$.name', JSON_EXTRACT('', '$')) -NULL +{"name": null} Warnings: Warning 4037 Unexpected end of JSON text in argument 1 to function 'json_extract' -Warning 4038 Syntax error in JSON text in argument 1 to function 'json_set' at position 2 select json_set('[1,2,3]', '$[2]', 4); json_set('[1,2,3]', '$[2]', 4) [1, 2, 4] diff --git a/mysql-test/t/func_json.test b/mysql-test/t/func_json.test index 3b978c1a1cf..6d640f42003 100644 --- a/mysql-test/t/func_json.test +++ b/mysql-test/t/func_json.test @@ -328,3 +328,8 @@ select JSON_EXTRACT('{"foo": "bar" foobar foo invalid ', '$.foo'); SELECT JSON_OBJECT('foo', '`'); SELECT JSON_OBJECT("foo", "bar`bar"); +# +# MDEV-13324 JSON_SET returns NULL instead of object. +# +SELECT JSON_SET('{}', '$.age', 87); + diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index c30ce45bfb2..e5544c6265d 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -2450,6 +2450,8 @@ String *Item_func_json_insert::val_str(String *str) } else /*JSON_PATH_KEY*/ { + uint n_key= 0; + if (je.value_type != JSON_VALUE_OBJECT) continue; @@ -2461,6 +2463,7 @@ String *Item_func_json_insert::val_str(String *str) json_string_set_str(&key_name, lp->key, lp->key_end); if (json_key_matches(&je, &key_name)) goto v_found; + n_key++; if (json_skip_key(&je)) goto js_error; break; @@ -2478,7 +2481,8 @@ String *Item_func_json_insert::val_str(String *str) v_to= (const char *) (je.s.c_str - je.sav_c_len); str->length(0); if (append_simple(str, js->ptr(), v_to - js->ptr()) || - str->append(", \"", 3) || + (n_key > 0 && str->append(", ", 2)) || + str->append("\"", 1) || append_simple(str, lp->key, lp->key_end - lp->key) || str->append("\":", 2) || append_json_value(str, args[n_arg+1], &tmp_val) || From 0cd731864e6e4d88b75bfea4f02454871aedb89e Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 12 Sep 2017 15:21:53 +0400 Subject: [PATCH 10/14] MDEV-13104 Json functions. An extra ',' added to the JSON_MERGE result making it invalid. --- mysql-test/r/func_json.result | 3 +++ mysql-test/t/func_json.test | 5 +++++ sql/item_jsonfunc.cc | 17 ++++++++++++++--- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/func_json.result b/mysql-test/r/func_json.result index 80de41ccb07..fc4eb16a067 100644 --- a/mysql-test/r/func_json.result +++ b/mysql-test/r/func_json.result @@ -683,3 +683,6 @@ JSON_OBJECT("foo", "bar`bar") SELECT JSON_SET('{}', '$.age', 87); JSON_SET('{}', '$.age', 87) {"age": 87} +SELECT JSON_MERGE('[]', '{"c":"d"}'); +JSON_MERGE('[]', '{"c":"d"}') +[{"c": "d"}] diff --git a/mysql-test/t/func_json.test b/mysql-test/t/func_json.test index 6d640f42003..8fc1f321928 100644 --- a/mysql-test/t/func_json.test +++ b/mysql-test/t/func_json.test @@ -333,3 +333,8 @@ SELECT JSON_OBJECT("foo", "bar`bar"); # SELECT JSON_SET('{}', '$.age', 87); +# +# MDEV-13104 Json functions. +# +SELECT JSON_MERGE('[]', '{"c":"d"}'); + diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index e5544c6265d..52b3f570844 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -1971,14 +1971,25 @@ continue_j2: else { const uchar *end1, *beg1, *end2, *beg2; + int empty_array= 0; beg1= je1->value_begin; /* Merge as a single array. */ if (je1->value_type == JSON_VALUE_ARRAY) { - if (json_skip_level(je1)) + int cur_level= je1->stack_p; + empty_array= 1; + while (json_scan_next(je1) == 0) + { + if (je1->stack_p < cur_level) + break; + empty_array= 0; + } + + if (je1->s.error) return 1; + end1= je1->s.c_str - je1->sav_c_len; } else @@ -1995,8 +2006,8 @@ continue_j2: end1= je1->value_end; } - if (str->append((const char*) beg1, end1 - beg1), - str->append(", ", 2)) + if (str->append((const char*) beg1, end1 - beg1) || + (!empty_array && str->append(", ", 2))) return 3; if (json_value_scalar(je2)) From 30db4e1fc7e83c17191925d5a2633c7afecf0fc8 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 12 Sep 2017 15:33:30 +0400 Subject: [PATCH 11/14] MDEV-13786 compiler complains about uninitialized variable. '=0' added to meke the compiler happy. --- sql/item_jsonfunc.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 52b3f570844..9295c34aba9 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -873,7 +873,7 @@ longlong Item_func_json_extract::val_int() json_value_types type; char *value; int value_len; - longlong i; + longlong i= 0; if (read_json(NULL, &type, &value, &value_len) != NULL) { From 825c8d793d6828bdb4d38ae6dea53ba1363852cf Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 12 Sep 2017 17:30:46 +0400 Subject: [PATCH 12/14] MDEV-12774 JSON_EXTRACT fails with some escaped unicode as key. Tests added. --- mysql-test/r/func_json.result | 10 ++++++++++ mysql-test/t/func_json.test | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/mysql-test/r/func_json.result b/mysql-test/r/func_json.result index fc4eb16a067..5aca3c21936 100644 --- a/mysql-test/r/func_json.result +++ b/mysql-test/r/func_json.result @@ -686,3 +686,13 @@ JSON_SET('{}', '$.age', 87) SELECT JSON_MERGE('[]', '{"c":"d"}'); JSON_MERGE('[]', '{"c":"d"}') [{"c": "d"}] +SET @str = "{\"\\u00e4\\u00f6\":\"yes\"}"; +SET @path = "$.\"\\u00e4\\u00f6\""; +select @str, @path, JSON_EXTRACT(@str, @path); +@str @path JSON_EXTRACT(@str, @path) +{"\u00e4\u00f6":"yes"} $."\u00e4\u00f6" "yes" +SET @str = "{\"\\u00e4\":\"yes\"}"; +SET @path = "$.\"\\u00e4\""; +select @str, @path, JSON_EXTRACT(@str, @path); +@str @path JSON_EXTRACT(@str, @path) +{"\u00e4":"yes"} $."\u00e4" "yes" diff --git a/mysql-test/t/func_json.test b/mysql-test/t/func_json.test index 8fc1f321928..f561a2cda8f 100644 --- a/mysql-test/t/func_json.test +++ b/mysql-test/t/func_json.test @@ -338,3 +338,14 @@ SELECT JSON_SET('{}', '$.age', 87); # SELECT JSON_MERGE('[]', '{"c":"d"}'); +# +# MDEV-12774 JSON_EXTRACT fails with some escaped unicode as key. +# + +SET @str = "{\"\\u00e4\\u00f6\":\"yes\"}"; +SET @path = "$.\"\\u00e4\\u00f6\""; +select @str, @path, JSON_EXTRACT(@str, @path); +SET @str = "{\"\\u00e4\":\"yes\"}"; +SET @path = "$.\"\\u00e4\""; +select @str, @path, JSON_EXTRACT(@str, @path); + From 80a38372832b391488a09c71a260334b23e5c98e Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 12 Sep 2017 19:15:31 +0400 Subject: [PATCH 13/14] MDEV-12877 Wrong result from JSON native function. JSON_QUOTE return type set to be JSON. --- mysql-test/r/func_json.result | 3 +++ mysql-test/t/func_json.test | 5 +++++ sql/item_jsonfunc.h | 1 + 3 files changed, 9 insertions(+) diff --git a/mysql-test/r/func_json.result b/mysql-test/r/func_json.result index 5aca3c21936..0c60c42ec77 100644 --- a/mysql-test/r/func_json.result +++ b/mysql-test/r/func_json.result @@ -696,3 +696,6 @@ SET @path = "$.\"\\u00e4\""; select @str, @path, JSON_EXTRACT(@str, @path); @str @path JSON_EXTRACT(@str, @path) {"\u00e4":"yes"} $."\u00e4" "yes" +select json_array(5,json_query('[1,2]','$')); +json_array(5,json_query('[1,2]','$')) +[5, [1,2]] diff --git a/mysql-test/t/func_json.test b/mysql-test/t/func_json.test index f561a2cda8f..39e31b8bb58 100644 --- a/mysql-test/t/func_json.test +++ b/mysql-test/t/func_json.test @@ -349,3 +349,8 @@ SET @str = "{\"\\u00e4\":\"yes\"}"; SET @path = "$.\"\\u00e4\""; select @str, @path, JSON_EXTRACT(@str, @path); +# +# MDEV-12877 Wrong result from JSON native function. +# +select json_array(5,json_query('[1,2]','$')); + diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h index b5c35ff551f..927b60015b8 100644 --- a/sql/item_jsonfunc.h +++ b/sql/item_jsonfunc.h @@ -101,6 +101,7 @@ class Item_func_json_query: public Item_func_json_value public: Item_func_json_query(THD *thd, Item *js, Item *i_path): Item_func_json_value(thd, js, i_path) {} + bool is_json_type() { return true; } const char *func_name() const { return "json_query"; } bool check_and_get_value(json_engine_t *je, String *res, int *error); Item *get_copy(THD *thd, MEM_ROOT *mem_root) From a237a920991f417e9a4567957f4fc7aa5b538270 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Wed, 13 Sep 2017 00:36:09 +0400 Subject: [PATCH 14/14] MDEV-12877 Wrong result from JSON native function. Set default charset for temporary paths so UDF call don't crash. --- sql/item_jsonfunc.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 9295c34aba9..a39b5f3f263 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -650,6 +650,7 @@ static int alloc_tmp_paths(THD *thd, uint n_paths, if (*tmp_paths == 0) { MEM_ROOT *root= thd->stmt_arena->mem_root; + *paths= (json_path_with_flags *) alloc_root(root, sizeof(json_path_with_flags) * n_paths); *tmp_paths= (String *) alloc_root(root, sizeof(String) * n_paths); @@ -657,6 +658,8 @@ static int alloc_tmp_paths(THD *thd, uint n_paths, return 1; bzero(*tmp_paths, sizeof(String) * n_paths); + for (uint c_path=0; c_path < n_paths; c_path++) + (*tmp_paths)[c_path].set_charset(&my_charset_utf8_general_ci); } return 0;