From 86f9b7714791f45f386e9aaa6f469ada626cd69d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Wed, 9 Aug 2017 12:47:12 +0300 Subject: [PATCH 01/13] MDEV-13037: wsrep_sst_mysqldump checking wrong version of mysql client This happens because on line 59 of /usr/bin/wsrep_sst_mysqldump we have a check : "if ! $MYSQL_CLIENT --version | grep 'Distrib 10.1' >/dev/null" but the client is 10.2 so it doesnt match the grep expression. Fixed check to be: "if ! $MYSQL_CLIENT --version | grep 'Distrib 10.' >/dev/null" so that every 10. version is accepted. --- scripts/wsrep_sst_mysqldump.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/wsrep_sst_mysqldump.sh b/scripts/wsrep_sst_mysqldump.sh index 5f25c2c9d13..aaa82e1ed5c 100644 --- a/scripts/wsrep_sst_mysqldump.sh +++ b/scripts/wsrep_sst_mysqldump.sh @@ -56,7 +56,7 @@ then fi # Check client version -if ! $MYSQL_CLIENT --version | grep 'Distrib 10.1' >/dev/null +if ! $MYSQL_CLIENT --version | grep 'Distrib 10.' >/dev/null then $MYSQL_CLIENT --version >&2 wsrep_log_error "this operation requires MySQL client version 10 or newer" @@ -133,7 +133,7 @@ SET_GTID_BINLOG_STATE="" SQL_LOG_BIN_OFF="" # Safety check -if echo $SERVER_VERSION | grep '^10.1' > /dev/null +if echo $SERVER_VERSION | grep '^10.' > /dev/null then # If binary logging is enabled on the joiner node, we need to copy donor's # gtid_binlog_state to joiner. In order to do that, a RESET MASTER must be From 067ee84d67247209f83805366f59791a9ac074b2 Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Fri, 4 Aug 2017 11:02:13 +0200 Subject: [PATCH 02/13] MDEV-13300 Query cache doesn't take in account CLIENT_DEPRECATE_EOF capability take into account new capabilty flag which has influence on result format. --- sql/sql_cache.cc | 10 ++++++++-- sql/sql_cache.h | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 18a75dd9024..ba4e4de937f 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1413,6 +1413,8 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) flags.client_long_flag= MY_TEST(thd->client_capabilities & CLIENT_LONG_FLAG); flags.client_protocol_41= MY_TEST(thd->client_capabilities & CLIENT_PROTOCOL_41); + flags.client_depr_eof= MY_TEST(thd->client_capabilities & + CLIENT_DEPRECATE_EOF); /* Protocol influences result format, so statement results in the binary protocol (COM_EXECUTE) cannot be served to statements asking for results @@ -1443,12 +1445,13 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) flags.div_precision_increment= thd->variables.div_precincrement; flags.default_week_format= thd->variables.default_week_format; DBUG_PRINT("qcache", ("\ -long %d, 4.1: %d, bin_proto: %d, more results %d, pkt_nr: %d, \ +long %d, 4.1: %d, eof: %d, bin_proto: %d, more results %d, pkt_nr: %d, \ CS client: %u, CS result: %u, CS conn: %u, limit: %lu, TZ: 0x%lx, \ sql mode: 0x%llx, sort len: %lu, conncat len: %lu, div_precision: %lu, \ def_week_frmt: %lu, in_trans: %d, autocommit: %d", (int)flags.client_long_flag, (int)flags.client_protocol_41, + (int)flags.client_depr_eof, (int)flags.protocol_type, (int)flags.more_results_exists, flags.pkt_nr, @@ -1917,6 +1920,8 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length) flags.client_long_flag= MY_TEST(thd->client_capabilities & CLIENT_LONG_FLAG); flags.client_protocol_41= MY_TEST(thd->client_capabilities & CLIENT_PROTOCOL_41); + flags.client_depr_eof= MY_TEST(thd->client_capabilities & + CLIENT_DEPRECATE_EOF); flags.protocol_type= (unsigned int) thd->protocol->type(); flags.more_results_exists= MY_TEST(thd->server_status & SERVER_MORE_RESULTS_EXISTS); @@ -1938,12 +1943,13 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length) flags.default_week_format= thd->variables.default_week_format; flags.lc_time_names= thd->variables.lc_time_names; DBUG_PRINT("qcache", ("\ -long %d, 4.1: %d, bin_proto: %d, more results %d, pkt_nr: %d, \ +long %d, 4.1: %d, eof: %d, bin_proto: %d, more results %d, pkt_nr: %d, \ CS client: %u, CS result: %u, CS conn: %u, limit: %lu, TZ: 0x%lx, \ sql mode: 0x%llx, sort len: %lu, conncat len: %lu, div_precision: %lu, \ def_week_frmt: %lu, in_trans: %d, autocommit: %d", (int)flags.client_long_flag, (int)flags.client_protocol_41, + (int)flags.client_depr_eof, (int)flags.protocol_type, (int)flags.more_results_exists, flags.pkt_nr, diff --git a/sql/sql_cache.h b/sql/sql_cache.h index 945de307ffb..6b8bf0b2d05 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -545,6 +545,7 @@ struct Query_cache_query_flags { unsigned int client_long_flag:1; unsigned int client_protocol_41:1; + unsigned int client_depr_eof:1; unsigned int protocol_type:2; unsigned int more_results_exists:1; unsigned int in_trans:1; From a72f34c0a240c2dca920041c0c0c1e8a9119a0c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 9 Aug 2017 15:21:42 +0300 Subject: [PATCH 03/13] MDEV-12868 MySQL bug #84038 also affects MariaDB 10.2 Cherry-pick the commit from MySQL 5.7.19, and adapt the test case: commit 45c933ac19c73a3e9c756a87ee1ba18ba1ac564c Author: Aakanksha Verma Date: Tue Mar 21 10:31:43 2017 +0530 Bug #25189192 ERRORS WHEN RESTARTING MYSQL AFTER RENAME TABLE. PROBLEM While renaming table innodb doesn't update the InnoDB Dictionary table INNODB_SYS_DATAFILES incase there is change in database while doing rename table. Hence on a restart the server log shows error that it couldnt find table with old path before rename which has actually been renamed. So the errors would only vanish if we update the system tablespace FIX Update the innodb dictionary table with new path in the case there is not a change in the table but the database holding the table as well. Reviewed-by: Jimmy Yang RB: 15751 --- mysql-test/suite/innodb/r/rename_table.result | 20 ++++++++++++ mysql-test/suite/innodb/t/rename_table.opt | 2 ++ mysql-test/suite/innodb/t/rename_table.test | 31 +++++++++++++++++++ storage/innobase/row/row0mysql.cc | 10 ++++++ 4 files changed, 63 insertions(+) create mode 100644 mysql-test/suite/innodb/r/rename_table.result create mode 100644 mysql-test/suite/innodb/t/rename_table.opt create mode 100644 mysql-test/suite/innodb/t/rename_table.test diff --git a/mysql-test/suite/innodb/r/rename_table.result b/mysql-test/suite/innodb/r/rename_table.result new file mode 100644 index 00000000000..3d7c3ff1b0e --- /dev/null +++ b/mysql-test/suite/innodb/r/rename_table.result @@ -0,0 +1,20 @@ +CREATE DATABASE test_jfg; +CREATE DATABASE test_jfg2; +CREATE TABLE test_jfg.test (a int unsigned PRIMARY KEY) ENGINE=InnoDB; +RENAME TABLE test_jfg.test TO test_jfg2.test; +SELECT REPLACE(path,'\\','/') path +FROM INFORMATION_SCHEMA.INNODB_SYS_DATAFILES WHERE PATH LIKE '%test%'; +path +./test_jfg2/test.ibd +DROP DATABASE test_jfg; +DROP DATABASE test_jfg2; +CREATE DATABASE abc_def; +CREATE DATABASE abc_def2; +CREATE TABLE abc_def.test (a int unsigned PRIMARY KEY) ENGINE=InnoDB; +RENAME TABLE abc_def.test TO abc_def2.test1; +SELECT REPLACE(path,'\\','/') path +FROM INFORMATION_SCHEMA.INNODB_SYS_DATAFILES WHERE PATH LIKE '%test%'; +path +./abc_def2/test1.ibd +DROP DATABASE abc_def; +DROP DATABASE abc_def2; diff --git a/mysql-test/suite/innodb/t/rename_table.opt b/mysql-test/suite/innodb/t/rename_table.opt new file mode 100644 index 00000000000..a4c52ea7d79 --- /dev/null +++ b/mysql-test/suite/innodb/t/rename_table.opt @@ -0,0 +1,2 @@ +--innodb +--innodb-sys-datafiles diff --git a/mysql-test/suite/innodb/t/rename_table.test b/mysql-test/suite/innodb/t/rename_table.test new file mode 100644 index 00000000000..ea9f70bacb0 --- /dev/null +++ b/mysql-test/suite/innodb/t/rename_table.test @@ -0,0 +1,31 @@ +--source include/have_innodb.inc +--source include/not_embedded.inc + +CREATE DATABASE test_jfg; +CREATE DATABASE test_jfg2; +CREATE TABLE test_jfg.test (a int unsigned PRIMARY KEY) ENGINE=InnoDB; +RENAME TABLE test_jfg.test TO test_jfg2.test; + +SELECT REPLACE(path,'\\','/') path +FROM INFORMATION_SCHEMA.INNODB_SYS_DATAFILES WHERE PATH LIKE '%test%'; + +DROP DATABASE test_jfg; + +--source include/restart_mysqld.inc + +DROP DATABASE test_jfg2; + +CREATE DATABASE abc_def; +CREATE DATABASE abc_def2; + +CREATE TABLE abc_def.test (a int unsigned PRIMARY KEY) ENGINE=InnoDB; +RENAME TABLE abc_def.test TO abc_def2.test1; + +SELECT REPLACE(path,'\\','/') path +FROM INFORMATION_SCHEMA.INNODB_SYS_DATAFILES WHERE PATH LIKE '%test%'; + +DROP DATABASE abc_def; + +--source include/restart_mysqld.inc + +DROP DATABASE abc_def2; diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index e7ecca307cc..720140e572e 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -4595,6 +4595,15 @@ row_rename_table_for_mysql( && dict_table_is_file_per_table(table)) { /* Make a new pathname to update SYS_DATAFILES. */ char* new_path = row_make_new_pathname(table, new_name); + char* old_path = fil_space_get_first_path(table->space); + + /* If old path and new path are the same means tablename + has not changed and only the database name holding the table + has changed so we need to make the complete filepath again. */ + if (!dict_tables_have_same_db(old_name, new_name)) { + ut_free(new_path); + new_path = fil_make_filepath(NULL, new_name, IBD, false); + } info = pars_info_create(); @@ -4614,6 +4623,7 @@ row_rename_table_for_mysql( "END;\n" , FALSE, trx); + ut_free(old_path); ut_free(new_path); } if (err != DB_SUCCESS) { From 88c391ad6db8490797200d95a2b85fa05b9fdd6b Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Wed, 22 Mar 2017 16:39:35 +0530 Subject: [PATCH 04/13] Bug #25573565 TABLE REBUILD USES EXCESSIVE MEMORY Problem: ======= Offsets allocates memory from row_heap even for deleted row traversal during table rebuild. Solution: ========= Empty the row_heap even for deleted record. So that offsets don't allocate memory everytime. Reviewed-by: Jimmy Yang RB: 15694 --- storage/innobase/row/row0merge.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index 73cb3fcea6f..de1f35a876e 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2005, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2005, 2017, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2014, 2017, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under @@ -1997,6 +1997,8 @@ row_merge_read_clustered_index( goto func_exit; } + mem_heap_empty(row_heap); + page_cur_move_to_next(cur); stage->n_pk_recs_inc(); @@ -2676,7 +2678,6 @@ write_buffers: goto func_exit; } - mem_heap_empty(row_heap); if (v_heap) { mem_heap_empty(v_heap); } From bf8054b0e89afdf613761e434140c3bcced3fb47 Mon Sep 17 00:00:00 2001 From: Darshan M N Date: Fri, 24 Mar 2017 12:18:52 +0530 Subject: [PATCH 05/13] BUG#25365223 ERRORLOG UPDATED WITH NOTES INFORMATION WHEN A REF FKEY ISN'T FOUND IN GRSETUP Issue ==== The issue is that the info message that InnoDB prints when a table is created with a reference which doesn't exist fills up the log as it's printed for every insert when foreign_key_checks is disabled. Fix === The fix is to display the message only if foreign_key_checks is enabled. Reviewed-by: Jimmy Yang --- storage/innobase/handler/ha_innodb.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index facfe349dd1..d1af3812f41 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -15637,10 +15637,14 @@ get_foreign_key_info( if (ref_table == NULL) { - ib::info() << "Foreign Key referenced table " - << foreign->referenced_table_name - << " not found for foreign table " - << foreign->foreign_table_name; + if (!thd_test_options( + thd, OPTION_NO_FOREIGN_KEY_CHECKS)) { + ib::info() + << "Foreign Key referenced table " + << foreign->referenced_table_name + << " not found for foreign table " + << foreign->foreign_table_name; + } } else { dict_table_close(ref_table, TRUE, FALSE); From 9d57468dde8c84f589a3d763166a9b77d614b2ef Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Thu, 18 May 2017 14:09:23 +0530 Subject: [PATCH 06/13] Bug #25357789 INNODB: LATCH ORDER VIOLATION DURING TRUNCATE TABLE IF INNODB_SYNC_DEBUG ENABLED Analysis: ======== (1) During TRUNCATE of file_per_table tablespace, dict_operation_lock is released before eviction of dirty pages of a tablespace from the buffer pool. After eviction, we try to re-acquire dict_operation_lock (higher level latch) but we already hold lower level latch (index->lock). This causes latch order violation (2) Deadlock issue is present if child table is being truncated and it holds index lock. At the same time, cascade dml happens and it took dict_operation_lock and waiting for index lock. Fix: ==== 1) Release the indexes lock before releasing the dict operation lock. 2) Ignore the cascading dml operation on the parent table, for the cascading foreign key, if the child table is truncated or if it is in the process of being truncated. Reviewed-by: Jimmy Yang Reviewed-by: Kevin Lewis RB: 16122 --- storage/innobase/fil/fil0fil.cc | 14 ++++++++++--- storage/innobase/include/fil0fil.h | 6 +++--- storage/innobase/row/row0trunc.cc | 2 +- storage/innobase/row/row0upd.cc | 33 +++++++++++++++++++++++++++++- 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index fec7405cb6d..cb3f5bb6f3f 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -3292,15 +3292,17 @@ fil_prepare_for_truncate( /** Reinitialize the original tablespace header with the same space id for single tablespace -@param[in] id space id of the tablespace +@param[in] table table belongs to tablespace @param[in] size size in blocks @param[in] trx Transaction covering truncate */ void -fil_reinit_space_header( - ulint id, +fil_reinit_space_header_for_table( + dict_table_t* table, ulint size, trx_t* trx) { + ulint id = table->space; + ut_a(!is_system_tablespace(id)); /* Invalidate in the buffer pool all pages belonging @@ -3309,6 +3311,9 @@ fil_reinit_space_header( and the dict operation lock during the scan and aquire it again after the buffer pool scan.*/ + /* Release the lock on the indexes too. So that + they won't violate the latch ordering. */ + dict_table_x_unlock_indexes(table); row_mysql_unlock_data_dictionary(trx); /* Lock the search latch in shared mode to prevent user @@ -3317,8 +3322,11 @@ fil_reinit_space_header( DEBUG_SYNC_C("buffer_pool_scan"); buf_LRU_flush_or_remove_pages(id, BUF_REMOVE_ALL_NO_WRITE, 0); btr_search_s_unlock_all(); + row_mysql_lock_data_dictionary(trx); + dict_table_x_lock_indexes(table); + /* Remove all insert buffer entries for the tablespace */ ibuf_delete_for_discarded_space(id); diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h index 097be6a5f96..204f031bce2 100644 --- a/storage/innobase/include/fil0fil.h +++ b/storage/innobase/include/fil0fil.h @@ -963,12 +963,12 @@ fil_prepare_for_truncate( /** Reinitialize the original tablespace header with the same space id for single tablespace -@param[in] id space id of the tablespace +@param[in] table table belongs to the tablespace @param[in] size size in blocks @param[in] trx Transaction covering truncate */ void -fil_reinit_space_header( - ulint id, +fil_reinit_space_header_for_table( + dict_table_t* table, ulint size, trx_t* trx); diff --git a/storage/innobase/row/row0trunc.cc b/storage/innobase/row/row0trunc.cc index 5724fad801f..afa49082136 100644 --- a/storage/innobase/row/row0trunc.cc +++ b/storage/innobase/row/row0trunc.cc @@ -2018,7 +2018,7 @@ row_truncate_table_for_mysql( space_size -= ib_vector_size(table->fts->indexes); } - fil_reinit_space_header(table->space, space_size, trx); + fil_reinit_space_header_for_table(table, space_size, trx); } DBUG_EXECUTE_IF("ib_trunc_crash_with_intermediate_log_checkpoint", diff --git a/storage/innobase/row/row0upd.cc b/storage/innobase/row/row0upd.cc index 4f57136db0b..05580ca0e31 100644 --- a/storage/innobase/row/row0upd.cc +++ b/storage/innobase/row/row0upd.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2015, 2016, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under @@ -256,6 +256,9 @@ row_upd_check_references_constraints( row_mysql_freeze_data_dictionary(trx); } + DEBUG_SYNC_C_IF_THD(thr_get_trx(thr)->mysql_thd, + "foreign_constraint_check_for_insert"); + for (dict_foreign_set::iterator it = table->referenced_set.begin(); it != table->referenced_set.end(); ++it) { @@ -283,6 +286,34 @@ row_upd_check_references_constraints( FALSE, FALSE, DICT_ERR_IGNORE_NONE); } + /* dict_operation_lock is held both here + (UPDATE or DELETE with FOREIGN KEY) and by TRUNCATE + TABLE operations. + If a TRUNCATE TABLE operation is in progress, + there can be 2 possible conditions: + 1) row_truncate_table_for_mysql() is not yet called. + 2) Truncate releases dict_operation_lock + during eviction of pages from buffer pool + for a file-per-table tablespace. + + In case of (1), truncate will wait for FK operation + to complete. + In case of (2), truncate will be rolled forward even + if it is interrupted. So if the foreign table is + undergoing a truncate, ignore the FK check. */ + + if (foreign_table) { + mutex_enter(&fil_system->mutex); + const fil_space_t* space = fil_space_get_by_id( + foreign_table->space); + const bool being_truncated = space + && space->is_being_truncated; + mutex_exit(&fil_system->mutex); + if (being_truncated) { + continue; + } + } + /* NOTE that if the thread ends up waiting for a lock we will release dict_operation_lock temporarily! But the counter on the table protects 'foreign' from From f2eaac5d928afd8fde0851f102e94753a1eab18a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 9 Aug 2017 16:34:57 +0300 Subject: [PATCH 07/13] Remove dead references to clust_templ_for_sec MariaDB 10.2 never contained the Oracle change Bug#23481444 OPTIMISER CALL ROW_SEARCH_MVCC() AND READ THE INDEX APPLIED BY UNCOMMITTED ROWS because it was considered risky for a GA release and incomplete. Remove the references that were added when merging MySQL 5.6.36 to MariaDB 10.0.31, 10.1.24, and 10.2.7. --- storage/innobase/row/row0sel.cc | 6 ------ 1 file changed, 6 deletions(-) diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 55278dd9406..d71fa902e3b 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -2981,8 +2981,6 @@ row_sel_field_store_in_mysql_format_func( @param[in] field_no templ->rec_field_no or templ->clust_rec_field_no or templ->icp_rec_field_no - or sec field no if clust_templ_for_sec - is TRUE @param[in] templ row template */ static MY_ATTRIBUTE((warn_unused_result)) @@ -3141,10 +3139,6 @@ be needed in the query. @param[in] rec_clust whether the rec in the clustered index @param[in] index index of rec @param[in] offsets array returned by rec_get_offsets(rec) -@param[in] clust_templ_for_sec TRUE if rec belongs to secondary index - but the prebuilt->template is in - clustered index format and it is - used only for end range comparison @return TRUE on success, FALSE if not all columns could be retrieved */ static MY_ATTRIBUTE((warn_unused_result)) ibool From ab2c31850df173375cc8920f9895362897ec4def Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 9 Aug 2017 16:47:09 +0300 Subject: [PATCH 08/13] Import a test case from MySQL 5.7.19 The test is for a bug that was introduced in MySQL 5.7.18 but not MariaDB 10.2, because MariaDB did not merge the change that was considered incomplete and too risky for a GA release: Bug#23481444 OPTIMISER CALL ROW_SEARCH_MVCC() AND READ THE INDEX APPLIED BY UNCOMMITTED ROWS So, we are only merging the test changes from the bug fix in MySQL 5.7.19, not any code changes: commit 4f86aca37d551cc756d9187ec901f8c4a68a0543 Author: Thirunarayanan Balathandayuthapani Date: Wed Apr 26 11:10:41 2017 +0530 Bug #25793677 INNODB: FAILING ASSERTION: CLUST_TEMPL_FOR_SEC || LEN --- mysql-test/suite/gcol/r/gcol_bugfixes.result | 13 +++++++++++++ mysql-test/suite/gcol/t/gcol_bugfixes.test | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/mysql-test/suite/gcol/r/gcol_bugfixes.result b/mysql-test/suite/gcol/r/gcol_bugfixes.result index fd40515225e..9aff30aabc9 100644 --- a/mysql-test/suite/gcol/r/gcol_bugfixes.result +++ b/mysql-test/suite/gcol/r/gcol_bugfixes.result @@ -576,6 +576,19 @@ SELECT 1 FROM t WHERE c GROUP BY b; COMMIT; DROP TABLE t; # +# Bug #25793677 INNODB: FAILING ASSERTION: CLUST_TEMPL_FOR_SEC || LEN .... +# +CREATE TABLE v ( +a INT, +c INT, +b CHAR(2) GENERATED ALWAYS AS (a IN (1)) VIRTUAL, +KEY(c,b(1))) charset utf8mb4; +INSERT INTO v (a,c) VALUES (1,1); +SELECT (SELECT MAX(c) FROM v); +(SELECT MAX(c) FROM v) +1 +DROP TABLE v; +# # MDEV-9255 Add generation_expression to information_schema.columns. # CREATE TABLE gcol_t1 ( diff --git a/mysql-test/suite/gcol/t/gcol_bugfixes.test b/mysql-test/suite/gcol/t/gcol_bugfixes.test index 4acae77e830..5563347a02a 100644 --- a/mysql-test/suite/gcol/t/gcol_bugfixes.test +++ b/mysql-test/suite/gcol/t/gcol_bugfixes.test @@ -537,6 +537,19 @@ SELECT 1 FROM t WHERE c GROUP BY b; COMMIT; DROP TABLE t; +--echo # +--echo # Bug #25793677 INNODB: FAILING ASSERTION: CLUST_TEMPL_FOR_SEC || LEN .... +--echo # + +CREATE TABLE v ( +a INT, +c INT, +b CHAR(2) GENERATED ALWAYS AS (a IN (1)) VIRTUAL, +KEY(c,b(1))) charset utf8mb4; +INSERT INTO v (a,c) VALUES (1,1); +SELECT (SELECT MAX(c) FROM v); +DROP TABLE v; + --echo # --echo # MDEV-9255 Add generation_expression to information_schema.columns. --echo # From 5721d5ba125fa22ee24d0a0dee1a3ac8672c92b0 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Mon, 8 May 2017 10:28:58 +0530 Subject: [PATCH 09/13] Bug #24960450 CONCURRENT DELETE DOESN'T APPLY DURING TABLE REBUILD Analysis: ======== During alter table rebuild, InnoDB fails to apply concurrent delete log. Parsing and validation of merge record happens while applying the log operation on a table. Validation goes wrong for the virtual column. Validation assumes that virtual column information can't be the end of the merge record end. Fix: ==== Virtual column information in the merge record can be end of the merge record. Virtual column information is written at the end for row_log_table_delete(). Reviewed-by: Satya Bodapati RB: 16155 --- storage/innobase/row/row0log.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/innobase/row/row0log.cc b/storage/innobase/row/row0log.cc index 6df0d745e46..2d99609d935 100644 --- a/storage/innobase/row/row0log.cc +++ b/storage/innobase/row/row0log.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2011, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2011, 2017, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2017, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under @@ -2457,7 +2457,7 @@ row_log_table_apply_op( rec_init_offsets_temp(mrec, new_index, offsets); next_mrec = mrec + rec_offs_data_size(offsets) + ext_size; if (log->table->n_v_cols) { - if (next_mrec + 2 >= mrec_end) { + if (next_mrec + 2 > mrec_end) { return(NULL); } From 38be0beb5da1ee72697242504a5e21766063bc41 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Thu, 18 May 2017 13:53:27 +0530 Subject: [PATCH 10/13] Bug #24961167 CONCURRENT INSERT FAILS IF TABLE DOES REBUILD Analysis: ========= During alter table rebuild, InnoDB fails to apply concurrent insert log. If the insert log record is present across the blocks then apply phase trying to access the next block without fetching it. Fix: ==== During virtual column parsing, check whether the record is present across the blocks before accessing the virtual column information. Reviewed-by: Jimmy Yang RB: 16243 --- storage/innobase/row/row0log.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/storage/innobase/row/row0log.cc b/storage/innobase/row/row0log.cc index 2d99609d935..31a5402dc15 100644 --- a/storage/innobase/row/row0log.cc +++ b/storage/innobase/row/row0log.cc @@ -2419,6 +2419,9 @@ row_log_table_apply_op( next_mrec = mrec + rec_offs_data_size(offsets); if (log->table->n_v_cols) { + if (next_mrec + 2 > mrec_end) { + return(NULL); + } next_mrec += mach_read_from_2(next_mrec); } From cb2a57c2036ba09ba28d413aa5f171cb25cb36f5 Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Mon, 7 Aug 2017 13:42:35 +0200 Subject: [PATCH 11/13] MDEV-13439: Database permissions are not enough to run a subquery with GROUP BY within a view The bug is result adding ability to have derived tables inside views. Fixed checks should be a switch between view/derived or select derived and information schema. --- mysql-test/r/view.result | 19 +++++++++++++++++++ mysql-test/t/view.test | 25 +++++++++++++++++++++++++ sql/sql_acl.cc | 5 ++++- sql/sql_derived.cc | 7 ++++--- 4 files changed, 52 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 1fae4e6ec9f..e0bc7b3d984 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -6559,5 +6559,24 @@ Warnings: Warning 1356 View 'test.v' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them DROP VIEW v; # +# MDEV-13439: Database permissions are not enough to run a subquery +# with GROUP BY within a view +# +create database test_db; +use test_db; +create table t (i int); +create user foo@localhost; +grant all on test_db.* to foo@localhost; +connect con1,localhost,foo,,; +use test_db; +create view v as select * from (select i from t group by i) sq; +select * from v; +i +disconnect con1; +connection default; +use test; +drop database test_db; +drop user foo@localhost; +# # End of 10.2 tests # diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 4430b65e5f4..47dc62c1b96 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -6268,6 +6268,31 @@ DROP TABLE IF EXISTS t; SHOW CREATE VIEW v; DROP VIEW v; +--echo # +--echo # MDEV-13439: Database permissions are not enough to run a subquery +--echo # with GROUP BY within a view +--echo # + +create database test_db; +use test_db; +create table t (i int); + +create user foo@localhost; +grant all on test_db.* to foo@localhost; + +--connect (con1,localhost,foo,,) + +use test_db; +create view v as select * from (select i from t group by i) sq; +select * from v; + +# Cleanup +--disconnect con1 +--connection default +use test; +drop database test_db; +drop user foo@localhost; + --echo # --echo # End of 10.2 tests --echo # diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 77f502d3a7b..6032c7a9742 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -7594,8 +7594,11 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, /* It is subquery in the FROM clause. VIEW set t_ref->derived after table opening, but this function always called before table opening. + + NOTE: is_derived() can't be used here because subquery in this case + the FROM clase (derived tables) can be not be marked yet. */ - if (!t_ref->referencing_view) + if (t_ref->is_anonymous_derived_table() || t_ref->schema_table) { /* If it's a temporary table created for a subquery in the FROM diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 649f745fdc4..f788d35a297 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -822,13 +822,14 @@ exit: table->derived_select_number= first_select->select_number; table->s->tmp_table= INTERNAL_TMP_TABLE; #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (derived->referencing_view) + if (derived->is_view()) table->grant= derived->grant; else { + DBUG_ASSERT(derived->is_derived()); + DBUG_ASSERT(derived->is_anonymous_derived_table()); table->grant.privilege= SELECT_ACL; - if (derived->is_derived()) - derived->grant.privilege= SELECT_ACL; + derived->grant.privilege= SELECT_ACL; } #endif /* Add new temporary table to list of open derived tables */ From bfffe571accb93c80066b070688e6712d4cb5643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 10 Aug 2017 14:00:51 +0300 Subject: [PATCH 12/13] Fix some GCC 7 warnings for InnoDB buf_page_io_complete(): Do not test bpage for NULL, because it is declared (and always passed) as nonnull. buf_flush_batch(): Remove the constant local variable count=0. fil_ibd_load(): Use magic comment to suppress -Wimplicit-fallthrough. ut_stage_alter_t::inc(ulint): Disable references to an unused parameter. lock_queue_validate(), sync_array_find_thread(), rbt_check_ordering(): Define only in debug builds. --- storage/innobase/buf/buf0buf.cc | 2 +- storage/innobase/buf/buf0flu.cc | 5 +---- storage/innobase/fil/fil0fil.cc | 2 +- storage/innobase/include/ut0stage.h | 8 ++++---- storage/innobase/lock/lock0lock.cc | 2 ++ storage/innobase/sync/sync0arr.cc | 2 +- storage/innobase/ut/ut0rbt.cc | 2 ++ 7 files changed, 12 insertions(+), 11 deletions(-) diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index d1476cca67f..b57fba75869 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -6038,7 +6038,7 @@ database_corrupted: && fil_page_get_type(frame) == FIL_PAGE_INDEX && page_is_leaf(frame)) { - if (bpage && bpage->encrypted) { + if (bpage->encrypted) { ib::warn() << "Table in tablespace " << bpage->id.space() diff --git a/storage/innobase/buf/buf0flu.cc b/storage/innobase/buf/buf0flu.cc index 24971cfebd4..8575de8bfa3 100644 --- a/storage/innobase/buf/buf0flu.cc +++ b/storage/innobase/buf/buf0flu.cc @@ -1884,8 +1884,6 @@ buf_flush_batch( buf_pool_mutex_enter(buf_pool); - ulint count = 0; - /* Note: The buffer pool mutex is released and reacquired within the flush functions. */ switch (flush_type) { @@ -1902,8 +1900,7 @@ buf_flush_batch( buf_pool_mutex_exit(buf_pool); - DBUG_PRINT("ib_buf", ("flush %u completed, %u pages", - unsigned(flush_type), unsigned(count))); + DBUG_LOG("ib_buf", "flush " << flush_type << " completed"); } /******************************************************************//** diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index cb3f5bb6f3f..1cafb253630 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -4644,7 +4644,7 @@ fil_ibd_load( break; } - /* Fall through to error handling */ + /* fall through */ case DB_TABLESPACE_EXISTS: return(FIL_LOAD_INVALID); diff --git a/storage/innobase/include/ut0stage.h b/storage/innobase/include/ut0stage.h index baad5cd77b4..1d5457a3ab0 100644 --- a/storage/innobase/include/ut0stage.h +++ b/storage/innobase/include/ut0stage.h @@ -267,12 +267,10 @@ ut_stage_alter_t::n_pk_recs_inc() } /** Flag either one record or one page processed, depending on the -current phase. -@param[in] inc_val flag this many units processed at once */ +current phase. */ inline void -ut_stage_alter_t::inc( - ulint inc_val /* = 1 */) +ut_stage_alter_t::inc(ulint) { if (m_progress == NULL) { return; @@ -286,12 +284,14 @@ ut_stage_alter_t::inc( ut_error; case READ_PK: m_n_pk_pages++; +#if 0 /* TODO: MySQL 5.7 PSI */ ut_ad(inc_val == 1); /* Overall the read pk phase will read all the pages from the PK and will do work, proportional to the number of added indexes, thus when this is called once per read page we increment with 1 + m_n_sort_indexes */ inc_val = 1 + m_n_sort_indexes; +#endif break; case SORT: multi_factor = m_sort_multi_factor; diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc index a6caa009ef7..d6e8be7fac0 100644 --- a/storage/innobase/lock/lock0lock.cc +++ b/storage/innobase/lock/lock0lock.cc @@ -1853,6 +1853,7 @@ lock_rec_insert_by_trx_age( return DB_SUCCESS; } +#ifdef UNIV_DEBUG static bool lock_queue_validate( @@ -1888,6 +1889,7 @@ lock_queue_validate( } return true; } +#endif /* UNIV_DEBUG */ static void diff --git a/storage/innobase/sync/sync0arr.cc b/storage/innobase/sync/sync0arr.cc index 75919043731..f1589e1f3a7 100644 --- a/storage/innobase/sync/sync0arr.cc +++ b/storage/innobase/sync/sync0arr.cc @@ -621,6 +621,7 @@ sync_array_cell_print( } } +#ifdef UNIV_DEBUG /******************************************************************//** Looks for a cell with the given thread id. @return pointer to cell or NULL if not found */ @@ -648,7 +649,6 @@ sync_array_find_thread( return(NULL); /* Not found */ } -#ifdef UNIV_DEBUG /******************************************************************//** Recursion step for deadlock detection. @return TRUE if deadlock detected */ diff --git a/storage/innobase/ut/ut0rbt.cc b/storage/innobase/ut/ut0rbt.cc index d3e4ceae97d..cb8e4f2df20 100644 --- a/storage/innobase/ut/ut0rbt.cc +++ b/storage/innobase/ut/ut0rbt.cc @@ -54,6 +54,7 @@ red-black properties: #define ROOT(t) (t->root->left) #define SIZEOF_NODE(t) ((sizeof(ib_rbt_node_t) + t->sizeof_value) - 1) +#if defined UNIV_DEBUG || defined IB_RBT_TESTING /**********************************************************************//** Verify that the keys are in order. @return TRUE of OK. FALSE if not ordered */ @@ -91,6 +92,7 @@ rbt_check_ordering( return(TRUE); } +#endif /* UNIV_DEBUG || IB_RBT_TESTING */ /**********************************************************************//** Check that every path from the root to the leaves has the same count. From 79d28533549d15e848b342cf518ae4b409ba3e64 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Fri, 11 Aug 2017 00:50:29 +0400 Subject: [PATCH 13/13] MDEV-12604 Comparison of JSON_EXTRACT result differs with Mysql. JSON_EXTRACT behaves specifically in the comparison, so we have to implement specific method for that in Arg_comparator. --- sql/item_cmpfunc.cc | 48 ++++++-- sql/item_cmpfunc.h | 6 + sql/item_func.h | 2 +- sql/item_jsonfunc.cc | 280 +++++++++++++++++++++++-------------------- sql/item_jsonfunc.h | 6 +- 5 files changed, 199 insertions(+), 143 deletions(-) diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index efab3da4ac1..16452a0de84 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -627,6 +627,21 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg, */ if (owner->agg_arg_charsets_for_comparison(&m_compare_collation, a, b)) return 1; + + if ((*a)->type() == Item::FUNC_ITEM && + ((Item_func *) (*a))->functype() == Item_func::JSON_EXTRACT_FUNC) + { + func= is_owner_equal_func() ? &Arg_comparator::compare_e_json_str: + &Arg_comparator::compare_json_str; + return 0; + } + else if ((*b)->type() == Item::FUNC_ITEM && + ((Item_func *) (*b))->functype() == Item_func::JSON_EXTRACT_FUNC) + { + func= is_owner_equal_func() ? &Arg_comparator::compare_e_json_str: + &Arg_comparator::compare_str_json; + return 0; + } } if (m_compare_type == TIME_RESULT) @@ -670,15 +685,6 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg, &Arg_comparator::compare_datetime; } - if ((*a)->is_json_type() ^ (*b)->is_json_type()) - { - Item **j_item= (*a)->is_json_type() ? a : b; - Item *uf= new(thd->mem_root) Item_func_json_unquote(thd, *j_item); - if (!uf || uf->fix_fields(thd, &uf)) - return 1; - *j_item= uf; - } - a= cache_converted_constant(thd, a, &a_cache, m_compare_type); b= cache_converted_constant(thd, b, &b_cache, m_compare_type); return set_compare_func(owner_arg, m_compare_type); @@ -1169,6 +1175,30 @@ int Arg_comparator::compare_e_row() } +int Arg_comparator::compare_json_str() +{ + return compare_json_str_basic(*a, *b); +} + + +int Arg_comparator::compare_str_json() +{ + return -compare_json_str_basic(*b, *a); +} + + +int Arg_comparator::compare_e_json_str() +{ + return compare_e_json_str_basic(*a, *b); +} + + +int Arg_comparator::compare_e_str_json() +{ + return compare_e_json_str_basic(*b, *a); +} + + void Item_func_truth::fix_length_and_dec() { maybe_null= 0; diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 8ff789d983a..131062dab36 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -106,6 +106,12 @@ public: int compare_e_datetime() { return compare_e_temporal(MYSQL_TYPE_DATETIME); } int compare_time() { return compare_temporal(MYSQL_TYPE_TIME); } int compare_e_time() { return compare_e_temporal(MYSQL_TYPE_TIME); } + int compare_json_str_basic(Item *j, Item *s); + int compare_json_str(); + int compare_str_json(); + int compare_e_json_str_basic(Item *j, Item *s); + int compare_e_json_str(); + int compare_e_str_json(); Item** cache_converted_constant(THD *thd, Item **value, Item **cache, Item_result type); diff --git a/sql/item_func.h b/sql/item_func.h index 585b981ba05..9aa9b09db1d 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -67,7 +67,7 @@ public: NOW_FUNC, NOW_UTC_FUNC, SYSDATE_FUNC, TRIG_COND_FUNC, SUSERVAR_FUNC, GUSERVAR_FUNC, COLLATE_FUNC, EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP, UDF_FUNC, - NEG_FUNC, GSYSVAR_FUNC, DYNCOL_FUNC }; + NEG_FUNC, GSYSVAR_FUNC, DYNCOL_FUNC, JSON_EXTRACT_FUNC }; enum Type type() const { return FUNC_ITEM; } virtual enum Functype functype() const { return UNKNOWN_FUNC; } Item_func(THD *thd): Item_func_or_sum(thd), allowed_arg_cols(1) diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index d871175a3ba..88165c09426 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -642,81 +642,6 @@ error: } -double Item_func_json_unquote::val_real() -{ - json_engine_t je; - double d= 0.0; - String *js; - - if ((js= read_json(&je)) != NULL) - { - switch (je.value_type) - { - case JSON_VALUE_NUMBER: - { - char *end; - int err; - d= my_strntod(je.s.cs, (char *) je.value, je.value_len, &end, &err); - break; - } - case JSON_VALUE_TRUE: - d= 1.0; - break; - case JSON_VALUE_STRING: - { - char *end; - int err; - d= my_strntod(js->charset(), (char *) js->ptr(), js->length(), - &end, &err); - break; - } - default: - break; - }; - } - - return d; -} - - -longlong Item_func_json_unquote::val_int() -{ - json_engine_t je; - longlong i= 0; - String *js; - - if ((js= read_json(&je)) != NULL) - { - switch (je.value_type) - { - case JSON_VALUE_NUMBER: - { - char *end; - int err; - i= my_strntoll(je.s.cs, (char *) je.value, je.value_len, 10, - &end, &err); - break; - } - case JSON_VALUE_TRUE: - i= 1; - break; - case JSON_VALUE_STRING: - { - char *end; - int err; - i= my_strntoll(js->charset(), (char *) js->ptr(), js->length(), 10, - &end, &err); - break; - } - default: - break; - }; - } - - return i; -} - - static int alloc_tmp_paths(THD *thd, uint n_paths, json_path_with_flags **paths,String **tmp_paths) { @@ -779,7 +704,7 @@ void Item_func_json_extract::fix_length_and_dec() static bool path_exact(const json_path_with_flags *paths_list, int n_paths, - const json_path_t *p, enum json_value_types vt) + const json_path_t *p, json_value_types vt) { for (; n_paths > 0; n_paths--, paths_list++) { @@ -791,7 +716,7 @@ static bool path_exact(const json_path_with_flags *paths_list, int n_paths, static bool path_ok(const json_path_with_flags *paths_list, int n_paths, - const json_path_t *p, enum json_value_types vt) + const json_path_t *p, json_value_types vt) { for (; n_paths > 0; n_paths--, paths_list++) { @@ -802,7 +727,9 @@ static bool path_ok(const json_path_with_flags *paths_list, int n_paths, } -String *Item_func_json_extract::val_str(String *str) +String *Item_func_json_extract::read_json(String *str, + json_value_types *type, + char **out_val, int *value_len) { String *js= args[0]->val_json(&tmp_js); json_engine_t je, sav_je; @@ -838,8 +765,13 @@ String *Item_func_json_extract::val_str(String *str) possible_multiple_values= arg_count > 2 || (paths[0].p.types_used & (JSON_PATH_WILD | JSON_PATH_DOUBLE_WILD)); - str->set_charset(js->charset()); - str->length(0); + *type= possible_multiple_values ? JSON_VALUE_ARRAY : JSON_VALUE_NULL; + + if (str) + { + str->set_charset(js->charset()); + str->length(0); + } if (possible_multiple_values && str->append("[", 1)) goto error; @@ -854,6 +786,18 @@ String *Item_func_json_extract::val_str(String *str) value= je.value_begin; + if (*type == JSON_VALUE_NULL) + { + *type= je.value_type; + *out_val= (char *) je.value; + *value_len= je.value_len; + } + if (!str) + { + /* If str is NULL, we only care about the first found value. */ + goto return_ok; + } + if (json_value_scalar(&je)) v_len= je.value_end - value; else @@ -897,6 +841,7 @@ String *Item_func_json_extract::val_str(String *str) if (json_nice(&je, &tmp_js, Item_func_json_format::LOOSE)) goto error; +return_ok: return &tmp_js; error: @@ -907,68 +852,74 @@ return_null: } +String *Item_func_json_extract::val_str(String *str) +{ + json_value_types type; + char *value; + int value_len; + return read_json(str, &type, &value, &value_len); +} + + longlong Item_func_json_extract::val_int() { - String *js= args[0]->val_json(&tmp_js); - json_engine_t je; - uint n_arg; - uint array_counters[JSON_DEPTH_LIMIT]; + json_value_types type; + char *value; + int value_len; + longlong i; - if ((null_value= args[0]->null_value)) - return 0; - - for (n_arg=1; n_arg < arg_count; n_arg++) + if (read_json(NULL, &type, &value, &value_len) != NULL) { - json_path_with_flags *c_path= paths + n_arg - 1; - if (!c_path->parsed) + switch (type) { - String *s_p= args[n_arg]->val_str(tmp_paths+(n_arg-1)); - if (s_p && - json_path_setup(&c_path->p,s_p->charset(),(const uchar *) s_p->ptr(), - (const uchar *) s_p->ptr() + s_p->length())) - goto error; - c_path->parsed= c_path->constant; - } + case JSON_VALUE_NUMBER: + case JSON_VALUE_STRING: + { + char *end; + int err; + i= my_strntoll(collation.collation, value, value_len, 10, &end, &err); + break; + } + case JSON_VALUE_TRUE: + i= 1; + break; + default: + i= 0; + break; + }; + } + return i; +} - if (args[n_arg]->null_value) - goto error; - json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), - (const uchar *) js->ptr() + js->length()); +double Item_func_json_extract::val_real() +{ + json_value_types type; + char *value; + int value_len; + double d= 0.0; - c_path->cur_step= c_path->p.steps; - - if (json_find_path(&je, &c_path->p, &c_path->cur_step, array_counters)) + if (read_json(NULL, &type, &value, &value_len) != NULL) + { + switch (type) { - /* Path wasn't found. */ - if (je.s.error) - goto error; - - continue; - } - - if (json_read_value(&je)) - goto error; - - if (json_value_scalar(&je)) - { - int err; - char *v_end= (char *) je.value_end; - return (je.s.cs->cset->strtoll10)(je.s.cs, (const char *) je.value_begin, - &v_end, &err); - } - else - break; + case JSON_VALUE_STRING: + case JSON_VALUE_NUMBER: + { + char *end; + int err; + d= my_strntod(collation.collation, value, value_len, &end, &err); + break; + } + case JSON_VALUE_TRUE: + d= 1.0; + break; + default: + break; + }; } - /* Nothing was found. */ - null_value= 1; - return 0; - -error: - /* TODO: launch error messages. */ - null_value= 1; - return 0; + return d; } @@ -3193,4 +3144,71 @@ String *Item_func_json_format::val_json(String *str) return js; } +int Arg_comparator::compare_json_str_basic(Item *j, Item *s) +{ + String *res1,*res2; + json_value_types type; + char *value; + int value_len, c_len; + Item_func_json_extract *e= (Item_func_json_extract *) j; + + if ((res1= e->read_json(&value1, &type, &value, &value_len))) + { + if ((res2= s->val_str(&value2))) + { + if (type == JSON_VALUE_STRING) + { + if (value1.realloc_with_extra_if_needed(value_len) || + (c_len= json_unescape(value1.charset(), (uchar *) value, + (uchar *) value+value_len, + &my_charset_utf8_general_ci, + (uchar *) value1.ptr(), + (uchar *) (value1.ptr() + value_len))) < 0) + goto error; + value1.length(c_len); + res1= &value1; + } + + if (set_null) + owner->null_value= 0; + return sortcmp(res1, res2, compare_collation()); + } + } +error: + if (set_null) + owner->null_value= 1; + return -1; +} + + +int Arg_comparator::compare_e_json_str_basic(Item *j, Item *s) +{ + String *res1,*res2; + json_value_types type; + char *value; + int value_len, c_len; + Item_func_json_extract *e= (Item_func_json_extract *) j; + + res1= e->read_json(&value1, &type, &value, &value_len); + res2= s->val_str(&value2); + + if (!res1 || !res2) + return MY_TEST(res1 == res2); + + if (type == JSON_VALUE_STRING) + { + if (value1.realloc_with_extra_if_needed(value_len) || + (c_len= json_unescape(value1.charset(), (uchar *) value, + (uchar *) value+value_len, + &my_charset_utf8_general_ci, + (uchar *) value1.ptr(), + (uchar *) (value1.ptr() + value_len))) < 0) + return 1; + value1.length(c_len); + res1= &value1; + } + + return MY_TEST(sortcmp(res1, res2, compare_collation()) == 0); +} + diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h index cc089129556..b5c35ff551f 100644 --- a/sql/item_jsonfunc.h +++ b/sql/item_jsonfunc.h @@ -133,8 +133,6 @@ public: const char *func_name() const { return "json_unquote"; } void fix_length_and_dec(); String *val_str(String *); - double val_real(); - longlong val_int(); Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy(thd, mem_root, this); } }; @@ -160,12 +158,16 @@ class Item_func_json_extract: public Item_json_str_multipath protected: String tmp_js; public: + String *read_json(String *str, json_value_types *type, + char **out_val, int *value_len); Item_func_json_extract(THD *thd, List &list): Item_json_str_multipath(thd, list) {} const char *func_name() const { return "json_extract"; } + enum Functype functype() const { return JSON_EXTRACT_FUNC; } void fix_length_and_dec(); String *val_str(String *); longlong val_int(); + double val_real(); uint get_n_paths() const { return arg_count - 1; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy(thd, mem_root, this); }