diff options
author | unknown <serg@serg.mylan> | 2005-03-14 14:47:51 +0100 |
---|---|---|
committer | unknown <serg@serg.mylan> | 2005-03-14 14:47:51 +0100 |
commit | ebbee9d974a37ce9fad0a2b67152cbc412a58067 (patch) | |
tree | f9212bc6a4dc14f47bfce79ca07320ec3d810728 | |
parent | 1c234737515106342180daa9671de15dedd48fcc (diff) | |
parent | 87b40f2013a108ed3caaf6912772354d0b0dc842 (diff) | |
download | mariadb-git-ebbee9d974a37ce9fad0a2b67152cbc412a58067.tar.gz |
Merge bk-internal:/home/bk/mysql-5.0
into serg.mylan:/usr/home/serg/Abk/mysql-5.0
mysql-test/r/join_outer.result:
Auto merged
mysql-test/t/join_outer.test:
Auto merged
sql/ha_innodb.cc:
Auto merged
sql/item_cmpfunc.h:
Auto merged
sql/mysql_priv.h:
Auto merged
sql/mysqld.cc:
Auto merged
-rw-r--r-- | BitKeeper/etc/gone | 9 | ||||
-rw-r--r-- | innobase/fil/fil0fil.c | 14 | ||||
-rw-r--r-- | innobase/include/trx0trx.h | 5 | ||||
-rw-r--r-- | innobase/row/row0mysql.c | 10 | ||||
-rw-r--r-- | innobase/trx/trx0roll.c | 14 | ||||
-rw-r--r-- | innobase/trx/trx0trx.c | 104 | ||||
-rw-r--r-- | innobase/trx/trx0undo.c | 31 | ||||
-rw-r--r-- | mysql-test/r/join_outer.result | 22 | ||||
-rw-r--r-- | mysql-test/t/join_outer.test | 19 | ||||
-rw-r--r-- | sql/field.cc | 18 | ||||
-rw-r--r-- | sql/field.h | 8 | ||||
-rw-r--r-- | sql/ha_innodb.cc | 15 | ||||
-rw-r--r-- | sql/item.cc | 125 | ||||
-rw-r--r-- | sql/item.h | 114 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 1 | ||||
-rw-r--r-- | sql/item_sum.cc | 352 | ||||
-rw-r--r-- | sql/item_sum.h | 82 | ||||
-rw-r--r-- | sql/mysql_priv.h | 2 | ||||
-rw-r--r-- | sql/mysqld.cc | 6 | ||||
-rw-r--r-- | sql/set_var.cc | 4 | ||||
-rw-r--r-- | sql/sql_base.cc | 3 | ||||
-rw-r--r-- | sql/sql_class.h | 1 | ||||
-rw-r--r-- | sql/sql_error.cc | 6 | ||||
-rw-r--r-- | sql/sql_select.cc | 111 | ||||
-rw-r--r-- | sql/sql_select.h | 1 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 2 |
26 files changed, 826 insertions, 253 deletions
diff --git a/BitKeeper/etc/gone b/BitKeeper/etc/gone index 126bd4e38d3..776d469ee41 100644 --- a/BitKeeper/etc/gone +++ b/BitKeeper/etc/gone @@ -457,6 +457,14 @@ ccarkner@nslinuxw10.bedford.progress.com|mysql-test/r/isolation.result|200103271 ccarkner@nslinuxw10.bedford.progress.com|mysql-test/t/isolation.test|20010327145543|39049|6a39e4138dd4a456 jani@hynda.mysql.fi|client/mysqlcheck|20010419221207|26716|363e3278166d84ec jcole@tetra.bedford.progress.com|BitKeeper/etc/logging_ok|20001004201211|30554 +jimw@mysql.com|mysql-test/t/ndb_alter_table.disabled|20050311230559|27526|411e026940e7a0aa +jimw@mysql.com|mysql-test/t/ndb_alter_table.disabled|20050311230559|27526|411e026940e7a0aajimw@mysql.com|mysql-test/t/ndb_autodiscover2.disabled|20050311230559|22363|afa8e5b6e46a3ea1jimw@mysql.com|mysql-test/t/ndb_autodiscover.disabled|20050311230559|58101|dda20d04dddbb06jimw@mysql.com|mysql-test/t/ndb_cache_multi2.disabled|20050311230600|47901|84fed1a78c0d3e6djimw@mysql.com|mysql-test/t/ndb_cache_multi.disabled|20050311230600|18039|9657b6eff7deb27ajimw@mysql.com|mysql-test/t/ndb_multi.disabled|20050311230600|12240|2599367ad06100f6jimw@mysql.com|mysql-test/t/ndb_restore.disabled|20050311230600|30718|3c2453d6164b1a30jimw@mysql.com|mysql-test/t/ndb_alter_table.disabled|20050311230559|27526|411e026940e7a0aajimw@mysql.com|mysql-test/t/ndb_autodiscover2.disabled|20050311230559|22363|afa8e5b6e46a3ea1jimw@mysql.com|mysql-test/t/ndb_autodiscover.disabled|20050311230559|58101|dda20d04dddbb06jimw@mysql.com|mysql-test/t/ndb_cache_multi2.disabled|20050311230600|47901|84fed1a78c0d3e6djimw@mysql.com|mysql-test/t/ndb_cache_multi.disabled|20050311230600|18039|9657b6eff7deb27ajimw@mysql.com|mysql-test/t/ndb_multi.disabled|20050311230600|12240|2599367ad06100f6jimw@mysql.com|mysql-test/t/ndb_restore.disabled|20050311230600|30718|3c2453d6164b1a30magnus@neptunus.(none)|ndb/src/client/Makefile|20040414084436|02010|6c2778d2bf4954a2 +jimw@mysql.com|mysql-test/t/ndb_autodiscover.disabled|20050311230559|58101|dda20d04dddbb06 +jimw@mysql.com|mysql-test/t/ndb_autodiscover2.disabled|20050311230559|22363|afa8e5b6e46a3ea1 +jimw@mysql.com|mysql-test/t/ndb_cache_multi.disabled|20050311230600|18039|9657b6eff7deb27a +jimw@mysql.com|mysql-test/t/ndb_cache_multi2.disabled|20050311230600|47901|84fed1a78c0d3e6d +jimw@mysql.com|mysql-test/t/ndb_multi.disabled|20050311230600|12240|2599367ad06100f6 +jimw@mysql.com|mysql-test/t/ndb_restore.disabled|20050311230600|30718|3c2453d6164b1a30 magnus@neptunus.(none)|ndb/src/client/Makefile|20040414084436|02010|6c2778d2bf4954a2 magnus@neptunus.(none)|ndb/src/client/odbc/Extra.mk|20040414082358|47442|eabbb28986ca817d magnus@neptunus.(none)|ndb/src/client/odbc/Makefile|20040414084435|33394|9bc928a18aa88d66 @@ -1174,6 +1182,7 @@ mwagner@evoq.home.mwagner.org|mysql-test/xml/xsl/README|20001013051514|26509|cd4 mwagner@evoq.home.mwagner.org|mysql-test/xml/xsl/mysqltest.xsl|20001013051514|27425|1b8f6ec4f1b5f634 mwagner@work.mysql.com|mysql-test/r/3.23/sel000001.result|20001010091454|28284|383913ae4505ec86 mwagner@work.mysql.com|mysql-test/r/3.23/sel000002.result|20001010091454|29230|d1787e6fd5dbc1cc +ndb/src/client/Makefile nick@nick.leippe.com|mysql-test/r/rpl_empty_master_crash.result|20020531235552|47718|615f521be2132141 nick@nick.leippe.com|mysql-test/t/rpl_empty_master_crash.test|20020531235552|52328|99464e737639ccc6 sasha@mysql.sashanet.com|BitKeeper/etc/logging_ok|20000801000905|12967|5b7d847a2158554 diff --git a/innobase/fil/fil0fil.c b/innobase/fil/fil0fil.c index 3ff70aece5e..74970648c1a 100644 --- a/innobase/fil/fil0fil.c +++ b/innobase/fil/fil0fil.c @@ -2621,12 +2621,12 @@ fil_open_single_table_tablespace( fputs("!\n" "InnoDB: Have you moved InnoDB .ibd files around without using the\n" "InnoDB: commands DISCARD TABLESPACE and IMPORT TABLESPACE?\n" -"InnoDB: It is also possible that this is a table created with\n" -"InnoDB: CREATE TEMPORARY TABLE, and MySQL removed the .ibd file for this.\n" +"InnoDB: It is also possible that this is a temporary table #sql...,\n" +"InnoDB: and MySQL removed the .ibd file for this.\n" "InnoDB: Please refer to\n" "InnoDB:" " http://dev.mysql.com/doc/mysql/en/InnoDB_troubleshooting_datadict.html\n" -"InnoDB: how to resolve the issue.\n", stderr); +"InnoDB: for how to resolve the issue.\n", stderr); mem_free(filepath); @@ -2666,7 +2666,7 @@ fil_open_single_table_tablespace( "InnoDB: Please refer to\n" "InnoDB:" " http://dev.mysql.com/doc/mysql/en/InnoDB_troubleshooting_datadict.html\n" -"InnoDB: how to resolve the issue.\n", (ulong) space_id, (ulong) id); +"InnoDB: for how to resolve the issue.\n", (ulong) space_id, (ulong) id); ret = FALSE; @@ -3261,7 +3261,7 @@ fil_space_for_table_exists_in_mem( ut_print_filename(stderr, name); fprintf(stderr, "\n" "InnoDB: in InnoDB data dictionary has tablespace id %lu,\n" -"InnoDB: but tablespace with that id does not exist. There is\n" +"InnoDB: but a tablespace with that id does not exist. There is\n" "InnoDB: a tablespace of name %s and id %lu, though. Have\n" "InnoDB: you deleted or moved .ibd files?\n", (ulong) id, namespace->name, @@ -3272,7 +3272,7 @@ fil_space_for_table_exists_in_mem( "InnoDB: Please refer to\n" "InnoDB:" " http://dev.mysql.com/doc/mysql/en/InnoDB_troubleshooting_datadict.html\n" -"InnoDB: how to resolve the issue.\n", stderr); +"InnoDB: for how to resolve the issue.\n", stderr); mem_free(path); mutex_exit(&(system->mutex)); @@ -3286,7 +3286,7 @@ fil_space_for_table_exists_in_mem( ut_print_filename(stderr, name); fprintf(stderr, "\n" "InnoDB: in InnoDB data dictionary has tablespace id %lu,\n" -"InnoDB: but tablespace with that id has name %s.\n" +"InnoDB: but the tablespace with that id has name %s.\n" "InnoDB: Have you deleted or moved .ibd files?\n", (ulong) id, space->name); if (namespace != NULL) { diff --git a/innobase/include/trx0trx.h b/innobase/include/trx0trx.h index 61d372a824a..9db69261468 100644 --- a/innobase/include/trx0trx.h +++ b/innobase/include/trx0trx.h @@ -369,6 +369,11 @@ struct trx_struct{ XID xid; /* X/Open XA transaction identification to identify a transaction branch */ + ibool support_xa; /* normally we do the XA two-phase + commit steps, but by setting this to + FALSE, one can save CPU time and about + 150 bytes in the undo log size as then + we skip XA steps */ dulint no; /* transaction serialization number == max trx id when the transaction is moved to COMMITTED_IN_MEMORY state */ diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index 5009f017053..2b2b2d83002 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -3382,8 +3382,9 @@ row_rename_table_for_mysql( "InnoDB: data dictionary though MySQL is trying to rename the table.\n" "InnoDB: Have you copied the .frm file of the table to the\n" "InnoDB: MySQL database directory from another database?\n" - "InnoDB: You can look for further help from section 15.1 of\n" - "InnoDB: http://www.innodb.com/ibman.php\n", stderr); + "InnoDB: You can look for further help from\n" + "InnoDB: http://dev.mysql.com/doc/mysql/en/" + "InnoDB_troubleshooting_datadict.html\n", stderr); goto funct_exit; } @@ -3395,8 +3396,9 @@ row_rename_table_for_mysql( ut_print_name(stderr, trx, old_name); fputs( " does not have an .ibd file in the database directory.\n" - "InnoDB: You can look for further help from section 15.1 of\n" - "InnoDB: http://www.innodb.com/ibman.php\n", stderr); + "InnoDB: You can look for further help from\n" + "InnoDB: http://dev.mysql.com/doc/mysql/en/" + "InnoDB_troubleshooting_datadict.html\n", stderr); goto funct_exit; } diff --git a/innobase/trx/trx0roll.c b/innobase/trx/trx0roll.c index 4c68e0a0dd3..69f7a99187f 100644 --- a/innobase/trx/trx0roll.c +++ b/innobase/trx/trx0roll.c @@ -441,16 +441,8 @@ loop: trx = UT_LIST_GET_NEXT(trx_list, trx); } else if (trx->conc_state == TRX_PREPARED) { - /* Roll back all prepared transactions if - innobase_force_recovery > 0 in my.cnf */ - - if (srv_force_recovery > 0) { - trx->conc_state = TRX_ACTIVE; - break; - } else { - trx->sess = trx_dummy_sess; - trx = UT_LIST_GET_NEXT(trx_list, trx); - } + trx->sess = trx_dummy_sess; + trx = UT_LIST_GET_NEXT(trx_list, trx); } else { break; } @@ -461,7 +453,7 @@ loop: if (trx == NULL) { ut_print_timestamp(stderr); fprintf(stderr, - " InnoDB: Rollback of uncommitted transactions completed\n"); + " InnoDB: Rollback of non-prepared transactions completed\n"); mem_heap_free(heap); diff --git a/innobase/trx/trx0trx.c b/innobase/trx/trx0trx.c index ad82560e26c..614058e6860 100644 --- a/innobase/trx/trx0trx.c +++ b/innobase/trx/trx0trx.c @@ -93,6 +93,8 @@ trx_create( trx->id = ut_dulint_zero; trx->no = ut_dulint_max; + trx->support_xa = TRUE; + trx->check_foreigns = TRUE; trx->check_unique_secondary = TRUE; @@ -453,9 +455,15 @@ trx_lists_init_at_db_start(void) ut_dulint_get_high(trx->id), ut_dulint_get_low(trx->id)); - trx->conc_state = TRX_ACTIVE; + if (srv_force_recovery == 0) { - /* trx->conc_state = TRX_PREPARED;*/ + trx->conc_state = TRX_PREPARED; + } else { + fprintf(stderr, +"InnoDB: Since innodb_force_recovery > 0, we will rollback it anyway.\n"); + + trx->conc_state = TRX_ACTIVE; + } } else { trx->conc_state = TRX_COMMITTED_IN_MEMORY; @@ -511,15 +519,20 @@ trx_lists_init_at_db_start(void) commit or abort decision from MySQL */ if (undo->state == TRX_UNDO_PREPARED) { - fprintf(stderr, + fprintf(stderr, "InnoDB: Transaction %lu %lu was in the XA prepared state.\n", - ut_dulint_get_high(trx->id), - ut_dulint_get_low(trx->id)); + ut_dulint_get_high(trx->id), + ut_dulint_get_low(trx->id)); - trx->conc_state = TRX_ACTIVE; + if (srv_force_recovery == 0) { + + trx->conc_state = TRX_PREPARED; + } else { + fprintf(stderr, +"InnoDB: Since innodb_force_recovery > 0, we will rollback it anyway.\n"); - /* trx->conc_state = - TRX_PREPARED; */ + trx->conc_state = TRX_ACTIVE; + } } else { trx->conc_state = TRX_COMMITTED_IN_MEMORY; @@ -823,9 +836,6 @@ trx_commit_off_kernel( trx->read_view = NULL; } -/* fprintf(stderr, "Trx %lu commit finished\n", - ut_dulint_get_low(trx->id)); */ - if (must_flush_log) { mutex_exit(&kernel_mutex); @@ -869,14 +879,15 @@ trx_commit_off_kernel( /* Do nothing */ } else if (srv_flush_log_at_trx_commit == 1) { if (srv_unix_file_flush_method == SRV_UNIX_NOSYNC) { - /* Write the log but do not flush it to disk */ + /* Write the log but do not flush it to disk */ - log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE); + log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, + FALSE); } else { - /* Write the log to the log files AND flush - them to disk */ + /* Write the log to the log files AND flush + them to disk */ - log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE); + log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE); } } else if (srv_flush_log_at_trx_commit == 2) { @@ -1747,12 +1758,11 @@ Prepares a transaction. */ void trx_prepare_off_kernel( -/*==================*/ +/*===================*/ trx_t* trx) /* in: transaction */ { page_t* update_hdr_page; trx_rseg_t* rseg; - trx_undo_t* undo; ibool must_flush_log = FALSE; dulint lsn; mtr_t mtr; @@ -1779,19 +1789,18 @@ trx_prepare_off_kernel( mutex_enter(&(rseg->mutex)); if (trx->insert_undo != NULL) { - trx_undo_set_state_at_prepare(trx, trx->insert_undo, - &mtr); - } - - undo = trx->update_undo; - if (undo) { /* It is not necessary to obtain trx->undo_mutex here because only a single OS thread is allowed to do the transaction prepare for this transaction. */ - + + trx_undo_set_state_at_prepare(trx, trx->insert_undo, + &mtr); + } + + if (trx->update_undo) { update_hdr_page = trx_undo_set_state_at_prepare(trx, - undo, &mtr); + trx->update_undo, &mtr); } mutex_exit(&(rseg->mutex)); @@ -1815,17 +1824,48 @@ trx_prepare_off_kernel( /*--------------------------------------*/ if (must_flush_log) { + /* Depending on the my.cnf options, we may now write the log + buffer to the log files, making the prepared state of the + transaction durable if the OS does not crash. We may also + flush the log files to disk, making the prepared state of the + transaction durable also at an OS crash or a power outage. + + The idea in InnoDB's group prepare is that a group of + transactions gather behind a trx doing a physical disk write + to log files, and when that physical write has been completed, + one of those transactions does a write which prepares the whole + group. Note that this group prepare will only bring benefit if + there are > 2 users in the database. Then at least 2 users can + gather behind one doing the physical log write to disk. + + TODO: find out if MySQL holds some mutex when calling this. + That would spoil our group prepare algorithm. */ mutex_exit(&kernel_mutex); - - /* Write the log to the log files AND flush them to disk */ - /*-------------------------------------*/ + if (srv_flush_log_at_trx_commit == 0) { + /* Do nothing */ + } else if (srv_flush_log_at_trx_commit == 1) { + if (srv_unix_file_flush_method == SRV_UNIX_NOSYNC) { + /* Write the log but do not flush it to disk */ - log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE); + log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, + FALSE); + } else { + /* Write the log to the log files AND flush + them to disk */ + + log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE); + } + } else if (srv_flush_log_at_trx_commit == 2) { + + /* Write the log but do not flush it to disk */ + + log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE); + } else { + ut_error; + } - /*-------------------------------------*/ - mutex_enter(&kernel_mutex); } } diff --git a/innobase/trx/trx0undo.c b/innobase/trx/trx0undo.c index 88185973dfc..bb314dd35e9 100644 --- a/innobase/trx/trx0undo.c +++ b/innobase/trx/trx0undo.c @@ -596,7 +596,7 @@ trx_undo_read_xid( } /******************************************************************* -Adds the XA XID after an undo log old-style header. */ +Adds space for the XA XID after an undo log old-style header. */ static void trx_undo_header_add_space_for_xid( @@ -1488,6 +1488,7 @@ trx_undo_create( /*============*/ /* out: undo log object, NULL if did not succeed: out of space */ + trx_t* trx, /* in: transaction */ trx_rseg_t* rseg, /* in: rollback segment memory copy */ ulint type, /* in: type of the log: TRX_UNDO_INSERT or TRX_UNDO_UPDATE */ @@ -1530,7 +1531,10 @@ trx_undo_create( offset = trx_undo_header_create(undo_page, trx_id, mtr); - trx_undo_header_add_space_for_xid(undo_page, undo_page + offset, mtr); + if (trx->support_xa) { + trx_undo_header_add_space_for_xid(undo_page, + undo_page + offset, mtr); + } undo = trx_undo_mem_create(rseg, id, type, trx_id, xid, page_no, offset); @@ -1547,6 +1551,7 @@ trx_undo_reuse_cached( /*==================*/ /* out: the undo log memory object, NULL if none cached */ + trx_t* trx, /* in: transaction */ trx_rseg_t* rseg, /* in: rollback segment memory object */ ulint type, /* in: type of the log: TRX_UNDO_INSERT or TRX_UNDO_UPDATE */ @@ -1597,16 +1602,22 @@ trx_undo_reuse_cached( if (type == TRX_UNDO_INSERT) { offset = trx_undo_insert_header_reuse(undo_page, trx_id, mtr); - trx_undo_header_add_space_for_xid(undo_page, undo_page + offset, - mtr); + + if (trx->support_xa) { + trx_undo_header_add_space_for_xid(undo_page, + undo_page + offset, mtr); + } } else { ut_a(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE) == TRX_UNDO_UPDATE); offset = trx_undo_header_create(undo_page, trx_id, mtr); - trx_undo_header_add_space_for_xid(undo_page, undo_page + offset, - mtr); + + if (trx->support_xa) { + trx_undo_header_add_space_for_xid(undo_page, + undo_page + offset, mtr); + } } trx_undo_mem_init_for_reuse(undo, trx_id, xid, offset); @@ -1674,11 +1685,11 @@ trx_undo_assign_undo( #endif /* UNIV_SYNC_DEBUG */ mutex_enter(&(rseg->mutex)); - undo = trx_undo_reuse_cached(rseg, type, trx->id, &trx->xid, &mtr); - + undo = trx_undo_reuse_cached(trx, rseg, type, trx->id, &trx->xid, + &mtr); if (undo == NULL) { - undo = trx_undo_create(rseg, type, trx->id, &trx->xid, &mtr); - + undo = trx_undo_create(trx, rseg, type, trx->id, &trx->xid, + &mtr); if (undo == NULL) { /* Did not succeed */ diff --git a/mysql-test/r/join_outer.result b/mysql-test/r/join_outer.result index 15131b90a7a..ddd05bbd2bc 100644 --- a/mysql-test/r/join_outer.result +++ b/mysql-test/r/join_outer.result @@ -896,3 +896,25 @@ a b a b 2 2 2 2 NULL NULL 3 3 DROP TABLE t0,t1,t2,t3; +CREATE TABLE t1 (a int PRIMARY KEY, b int); +CREATE TABLE t2 (a int PRIMARY KEY, b int); +INSERT INTO t1 VALUES (1,1), (2,1), (3,1), (4,2); +INSERT INTO t2 VALUES (1,2), (2,2); +SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.a; +a b a b +1 1 1 2 +2 1 2 2 +3 1 NULL NULL +4 2 NULL NULL +SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.a WHERE t1.b=1; +a b a b +1 1 1 2 +2 1 2 2 +3 1 NULL NULL +SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.a +WHERE t1.b=1 XOR (NOT ISNULL(t2.a) AND t2.b=1); +a b a b +1 1 1 2 +2 1 2 2 +3 1 NULL NULL +DROP TABLE t1,t2; diff --git a/mysql-test/t/join_outer.test b/mysql-test/t/join_outer.test index 00f7e5f00bf..6061ed5e59b 100644 --- a/mysql-test/t/join_outer.test +++ b/mysql-test/t/join_outer.test @@ -632,3 +632,22 @@ select * from t2 right join t1 on t2.a=t1.a; select straight_join * from t2 right join t1 on t2.a=t1.a; DROP TABLE t0,t1,t2,t3; + +# +# Test for bug #9017: left join mistakingly converted to inner join +# + +CREATE TABLE t1 (a int PRIMARY KEY, b int); +CREATE TABLE t2 (a int PRIMARY KEY, b int); + +INSERT INTO t1 VALUES (1,1), (2,1), (3,1), (4,2); +INSERT INTO t2 VALUES (1,2), (2,2); + +SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.a; +SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.a WHERE t1.b=1; +SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.a + WHERE t1.b=1 XOR (NOT ISNULL(t2.a) AND t2.b=1); + +DROP TABLE t1,t2; + + diff --git a/sql/field.cc b/sql/field.cc index a73c24d8f0d..26575d97f69 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -7162,6 +7162,24 @@ void create_field::create_length_to_internal_length(void) } +void create_field::init_for_tmp_table(enum_field_types sql_type_arg, + uint32 length_arg, uint32 decimals, + bool maybe_null, bool is_unsigned) +{ + field_name= ""; + sql_type= sql_type_arg; + length= length_arg;; + unireg_check= Field::NONE; + interval= 0; + charset= &my_charset_bin; + geom_type= Field::GEOM_GEOMETRY; + pack_flag= (FIELDFLAG_NUMBER | + ((decimals & FIELDFLAG_MAX_DEC) << FIELDFLAG_DEC_SHIFT) | + (maybe_null ? FIELDFLAG_MAYBE_NULL : 0) | + (is_unsigned ? 0 : FIELDFLAG_DECIMAL)); +} + + enum_field_types get_blob_type_from_length(ulong length) { enum_field_types type; diff --git a/sql/field.h b/sql/field.h index a92ef1db297..083af27d6d9 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1340,7 +1340,8 @@ public: Create field class for CREATE TABLE */ -class create_field :public Sql_alloc { +class create_field :public Sql_alloc +{ public: const char *field_name; const char *change; // If done with alter table @@ -1362,6 +1363,11 @@ public: create_field() :after(0) {} create_field(Field *field, Field *orig_field); void create_length_to_internal_length(void); + + /* Init for a tmp table field. To be extended if need be. */ + void init_for_tmp_table(enum_field_types sql_type_arg, + uint32 max_length, uint32 decimals, + bool maybe_null, bool is_unsigned); }; diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index a1ae5b7d8fb..73d5ac9e94e 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -692,6 +692,10 @@ check_trx_exists( trx->mysql_query_str = &(thd->query); trx->active_trans = 0; + /* Update the info whether we should skip XA steps that eat + CPU time */ + trx->support_xa = (ibool)(thd->variables.innodb_support_xa); + thd->ha_data[innobase_hton.slot] = trx; } else { if (trx->magic_n != TRX_MAGIC_N) { @@ -1436,6 +1440,9 @@ innobase_commit( trx = check_trx_exists(thd); + /* Update the info whether we should skip XA steps that eat CPU time */ + trx->support_xa = (ibool)(thd->variables.innodb_support_xa); + /* Release a possible FIFO ticket and search latch. Since we will reserve the kernel mutex, we have to release the search system latch first to obey the latching order. */ @@ -1622,6 +1629,9 @@ innobase_rollback( trx = check_trx_exists(thd); + /* Update the info whether we should skip XA steps that eat CPU time */ + trx->support_xa = (ibool)(thd->variables.innodb_support_xa); + /* Release a possible FIFO ticket and search latch. Since we will reserve the kernel mutex, we have to release the search system latch first to obey the latching order. */ @@ -6310,6 +6320,11 @@ innobase_xa_prepare( int error = 0; trx_t* trx; + if (!thd->variables.innodb_support_xa) { + + return(0); + } + trx = check_trx_exists(thd); trx->xid=thd->transaction.xid; diff --git a/sql/item.cc b/sql/item.cc index a09b8589bdc..e6be934e334 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -33,6 +33,131 @@ static void mark_as_dependent(THD *thd, const String my_null_string("NULL", 4, default_charset_info); +/****************************************************************************/ + +/* Hybrid_type_traits {_real} */ + +void Hybrid_type_traits::fix_length_and_dec(Item *item, Item *arg) const +{ + item->decimals= NOT_FIXED_DEC; + item->max_length= item->float_length(arg->decimals); +} + + +const Hybrid_type_traits *Hybrid_type_traits::instance() +{ + const static Hybrid_type_traits real_traits; + return &real_traits; +} + + +my_decimal * +Hybrid_type_traits::val_decimal(Hybrid_type *val, my_decimal *to) const +{ + double2my_decimal(E_DEC_FATAL_ERROR, val->real, val->dec_buf); + return val->dec_buf; +} + + +String * +Hybrid_type_traits::val_str(Hybrid_type *val, String *to, uint8 decimals) const +{ + to->set(val->real, decimals, &my_charset_bin); + return to; +} + +/* Hybrid_type_traits_decimal */ + +const Hybrid_type_traits_decimal *Hybrid_type_traits_decimal::instance() +{ + const static Hybrid_type_traits_decimal decimal_traits; + return &decimal_traits; +} + + +void +Hybrid_type_traits_decimal::fix_length_and_dec(Item *item, Item *arg) const +{ + item->decimals= arg->decimals; + item->max_length= min(arg->max_length + DECIMAL_LONGLONG_DIGITS, + DECIMAL_MAX_LENGTH); +} + + +void Hybrid_type_traits_decimal::set_zero(Hybrid_type *val) const +{ + my_decimal_set_zero(&val->dec_buf[0]); + val->used_dec_buf_no= 0; +} + + +void Hybrid_type_traits_decimal::add(Hybrid_type *val, Field *f) const +{ + my_decimal_add(E_DEC_FATAL_ERROR, + &val->dec_buf[val->used_dec_buf_no ^ 1], + &val->dec_buf[val->used_dec_buf_no], + f->val_decimal(&val->dec_buf[2])); + val->used_dec_buf_no^= 1; +} + + +void Hybrid_type_traits_decimal::div(Hybrid_type *val, ulonglong u) const +{ + int2my_decimal(E_DEC_FATAL_ERROR, u, TRUE, &val->dec_buf[2]); + /* XXX: what is '4' for scale? */ + my_decimal_div(E_DEC_FATAL_ERROR, + &val->dec_buf[val->used_dec_buf_no ^ 1], + &val->dec_buf[val->used_dec_buf_no], + &val->dec_buf[2], 4); + val->used_dec_buf_no^= 1; +} + + +longlong +Hybrid_type_traits_decimal::val_int(Hybrid_type *val, bool unsigned_flag) const +{ + longlong result; + my_decimal2int(E_DEC_FATAL_ERROR, &val->dec_buf[val->used_dec_buf_no], + unsigned_flag, &result); + return result; +} + + +double +Hybrid_type_traits_decimal::val_real(Hybrid_type *val) const +{ + my_decimal2double(E_DEC_FATAL_ERROR, &val->dec_buf[val->used_dec_buf_no], + &val->real); + return val->real; +} + + +String * +Hybrid_type_traits_decimal::val_str(Hybrid_type *val, String *to, + uint8 decimals) const +{ + my_decimal_round(E_DEC_FATAL_ERROR, &val->dec_buf[val->used_dec_buf_no], + decimals, FALSE, &val->dec_buf[2]); + my_decimal2string(E_DEC_FATAL_ERROR, &val->dec_buf[2], 0, 0, 0, to); + return to; +} + +/* Hybrid_type_traits_integer */ + +const Hybrid_type_traits_integer *Hybrid_type_traits_integer::instance() +{ + const static Hybrid_type_traits_integer integer_traits; + return &integer_traits; +} + +void +Hybrid_type_traits_integer::fix_length_and_dec(Item *item, Item *arg) const +{ + item->decimals= 0; + item->max_length= 21; + item->unsigned_flag= 0; +} + /***************************************************************************** ** Item functions *****************************************************************************/ diff --git a/sql/item.h b/sql/item.h index 97913e40916..a2bf33398cc 100644 --- a/sql/item.h +++ b/sql/item.h @@ -106,6 +106,120 @@ public: } }; + +/*************************************************************************/ +/* + A framework to easily handle different return types for hybrid items + (hybrid item is an item whose operand can be of any type, e.g. integer, + real, decimal). +*/ + +struct Hybrid_type_traits; + +struct Hybrid_type +{ + longlong integer; + + double real; + /* + Use two decimal buffers interchangeably to speed up += operation + which has no native support in decimal library. + Hybrid_type+= arg is implemented as dec_buf[1]= dec_buf[0] + arg. + The third decimal is used as a handy temporary storage. + */ + my_decimal dec_buf[3]; + int used_dec_buf_no; + + /* + Traits moved to a separate class to + a) be able to easily change object traits in runtime + b) they work as a differentiator for the union above + */ + const Hybrid_type_traits *traits; + + Hybrid_type() {} + /* XXX: add traits->copy() when needed */ + Hybrid_type(const Hybrid_type &rhs) :traits(rhs.traits) {} +}; + + +/* Hybryd_type_traits interface + default implementation for REAL_RESULT */ + +struct Hybrid_type_traits +{ + virtual Item_result type() const { return REAL_RESULT; } + + virtual void + fix_length_and_dec(Item *item, Item *arg) const; + + /* Hybrid_type operations. */ + virtual void set_zero(Hybrid_type *val) const { val->real= 0.0; } + virtual void add(Hybrid_type *val, Field *f) const + { val->real+= f->val_real(); } + virtual void div(Hybrid_type *val, ulonglong u) const + { val->real/= ulonglong2double(u); } + + virtual longlong val_int(Hybrid_type *val, bool unsigned_flag) const + { return (longlong) val->real; } + virtual double val_real(Hybrid_type *val) const { return val->real; } + virtual my_decimal *val_decimal(Hybrid_type *val, my_decimal *buf) const; + virtual String *val_str(Hybrid_type *val, String *buf, uint8 decimals) const; + static const Hybrid_type_traits *instance(); +}; + + +struct Hybrid_type_traits_decimal: public Hybrid_type_traits +{ + virtual Item_result type() const { return DECIMAL_RESULT; } + + virtual void + fix_length_and_dec(Item *arg, Item *item) const; + + /* Hybrid_type operations. */ + virtual void set_zero(Hybrid_type *val) const; + virtual void add(Hybrid_type *val, Field *f) const; + virtual void div(Hybrid_type *val, ulonglong u) const; + + virtual longlong val_int(Hybrid_type *val, bool unsigned_flag) const; + virtual double val_real(Hybrid_type *val) const; + virtual my_decimal *val_decimal(Hybrid_type *val, my_decimal *buf) const + { return &val->dec_buf[val->used_dec_buf_no]; } + virtual String *val_str(Hybrid_type *val, String *buf, uint8 decimals) const; + static const Hybrid_type_traits_decimal *instance(); +}; + + +struct Hybrid_type_traits_integer: public Hybrid_type_traits +{ + virtual Item_result type() const { return INT_RESULT; } + + virtual void + fix_length_and_dec(Item *arg, Item *item) const; + + /* Hybrid_type operations. */ + virtual void set_zero(Hybrid_type *val) const + { val->integer= 0; } + virtual void add(Hybrid_type *val, Field *f) const + { val->integer+= f->val_int(); } + virtual void div(Hybrid_type *val, ulonglong u) const + { val->integer/= (longlong) u; } + + virtual longlong val_int(Hybrid_type *val, bool unsigned_flag) const + { return val->integer; } + virtual double val_real(Hybrid_type *val) const + { return (double) val->integer; } + virtual my_decimal *val_decimal(Hybrid_type *val, my_decimal *buf) const + { + int2my_decimal(E_DEC_FATAL_ERROR, val->integer, 0, &val->dec_buf[2]); + return &val->dec_buf[2]; + } + virtual String *val_str(Hybrid_type *val, String *buf, uint8 decimals) const + { buf->set(val->integer, &my_charset_bin); return buf;} + static const Hybrid_type_traits_integer *instance(); +}; + +/*************************************************************************/ + typedef bool (Item::*Item_processor)(byte *arg); typedef Item* (Item::*Item_transformer) (byte *arg); diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 63e7a93d43a..02bbb00f293 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1237,6 +1237,7 @@ public: enum Type type() const { return FUNC_ITEM; } longlong val_int(); const char *func_name() const { return "xor"; } + table_map not_null_tables() const { return and_tables_cache; } }; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index dbba02c3cf7..b3a7332fe52 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -456,11 +456,30 @@ my_decimal *Item_sum_sum::val_decimal(my_decimal *val) return val_decimal_from_real(val); } +/***************************************************************************/ -/* Item_sum_sum_distinct */ +C_MODE_START + +/* Declarations for auxilary C-callbacks */ + +static int simple_raw_key_cmp(void* arg, const void* key1, const void* key2) +{ + return memcmp(key1, key2, *(uint *) arg); +} + + +static int item_sum_distinct_walk(void *element, element_count num_of_dups, + void *item) +{ + return ((Item_sum_distinct*) (item))->unique_walk_function(element); +} + +C_MODE_END -Item_sum_sum_distinct::Item_sum_sum_distinct(Item *item) - :Item_sum_sum(item), tree(0) +/* Item_sum_distinct */ + +Item_sum_distinct::Item_sum_distinct(Item *item_arg) + :Item_sum_num(item_arg), tree(0) { /* quick_group is an optimizer hint, which means that GROUP BY can be @@ -472,239 +491,242 @@ Item_sum_sum_distinct::Item_sum_sum_distinct(Item *item) } -Item_sum_sum_distinct::Item_sum_sum_distinct(THD *thd, - Item_sum_sum_distinct *original) - :Item_sum_sum(thd, original), tree(0), dec_bin_buff(original->dec_bin_buff) +Item_sum_distinct::Item_sum_distinct(THD *thd, Item_sum_distinct *original) + :Item_sum_num(thd, original), val(original->val), tree(0), + table_field_type(original->table_field_type) { quick_group= 0; } -void Item_sum_sum_distinct::fix_length_and_dec() +/* + Behaves like an Integer except to fix_length_and_dec(). + Additionally div() converts val with this traits to a val with true + decimal traits along with conversion of integer value to decimal value. + This is to speedup SUM/AVG(DISTINCT) evaluation for 8-32 bit integer + values. +*/ + +struct Hybrid_type_traits_fast_decimal: public + Hybrid_type_traits_integer { - Item_sum_sum::fix_length_and_dec(); - if (hybrid_type == DECIMAL_RESULT) + virtual Item_result type() const { return DECIMAL_RESULT; } + virtual void fix_length_and_dec(Item *item, Item *arg) const + { Hybrid_type_traits_decimal::instance()->fix_length_and_dec(item, arg); } + + virtual void div(Hybrid_type *val, ulonglong u) const { - dec_bin_buff= (byte *) - sql_alloc(my_decimal_get_binary_size(args[0]->max_length, - args[0]->decimals)); + int2my_decimal(E_DEC_FATAL_ERROR, val->integer, 0, val->dec_buf); + val->used_dec_buf_no= 0; + val->traits= Hybrid_type_traits_decimal::instance(); + val->traits->div(val, u); } -} + static const Hybrid_type_traits_fast_decimal *instance() + { + static const Hybrid_type_traits_fast_decimal fast_decimal_traits; + return &fast_decimal_traits; + } +}; -Item * -Item_sum_sum_distinct::copy_or_same(THD *thd) +void Item_sum_distinct::fix_length_and_dec() { - return new (thd->mem_root) Item_sum_sum_distinct(thd, this); -} + DBUG_ASSERT(args[0]->fixed); -C_MODE_START + table_field_type= args[0]->field_type(); -static int simple_raw_key_cmp(void* arg, const void* key1, const void* key2) -{ - return memcmp(key1, key2, *(uint *) arg); + /* Adjust tmp table type according to the chosen aggregation type */ + switch (args[0]->result_type()) { + case STRING_RESULT: + case REAL_RESULT: + val.traits= Hybrid_type_traits::instance(); + if (table_field_type != MYSQL_TYPE_FLOAT) + table_field_type= MYSQL_TYPE_DOUBLE; + break; + case INT_RESULT: + /* + Preserving int8, int16, int32 field types gives ~10% performance boost + as the size of result tree becomes significantly smaller. + Another speed up we gain by using longlong for intermediate + calculations. The range of int64 is enough to hold sum 2^32 distinct + integers each <= 2^32. + */ + if (table_field_type == MYSQL_TYPE_INT24 || + table_field_type >= MYSQL_TYPE_TINY && + table_field_type <= MYSQL_TYPE_LONG) + { + val.traits= Hybrid_type_traits_fast_decimal::instance(); + break; + } + table_field_type= MYSQL_TYPE_LONGLONG; + /* fallthrough */ + case DECIMAL_RESULT: + val.traits= Hybrid_type_traits_decimal::instance(); + if (table_field_type != MYSQL_TYPE_LONGLONG) + table_field_type= MYSQL_TYPE_NEWDECIMAL; + break; + case ROW_RESULT: + default: + DBUG_ASSERT(0); + } + val.traits->fix_length_and_dec(this, args[0]); } -C_MODE_END - -bool Item_sum_sum_distinct::setup(THD *thd) +bool Item_sum_distinct::setup(THD *thd) { - DBUG_ENTER("Item_sum_sum_distinct::setup"); - SELECT_LEX *select_lex= thd->lex->current_select; - /* what does it mean??? */ - if (select_lex->linkage == GLOBAL_OPTIONS_TYPE) - DBUG_RETURN(1); + List<create_field> field_list; + create_field field_def; /* field definition */ + + DBUG_ENTER("Item_sum_distinct::setup"); DBUG_ASSERT(tree == 0); /* setup can not be called twice */ /* - Uniques handles all unique elements in a tree until they can't fit in. - Then thee tree is dumped to the temporary file. - See class Unique for details. + Virtual table and the tree are created anew on each re-execution of + PS/SP. Hence all further allocations are performed in the runtime + mem_root. */ + if (field_list.push_back(&field_def)) + return TRUE; + null_value= maybe_null= 1; - /* - TODO: if underlying item result fits in 4 bytes we can take advantage - of it and have tree of long/ulong. It gives 10% performance boost - */ + quick_group= 0; + + DBUG_ASSERT(args[0]->fixed); + + field_def.init_for_tmp_table(table_field_type, args[0]->max_length, + args[0]->decimals, args[0]->maybe_null, + args[0]->unsigned_flag); + + if (! (table= create_virtual_tmp_table(thd, field_list))) + return TRUE; + + /* XXX: check that the case of CHAR(0) works OK */ + tree_key_length= table->s->reclength - table->s->null_bytes; /* - It's safe to use key_length here as even if we do copy_or_same() - the new item will just share the old items key_length, which will not - change or disappear during the life time of this item. + Unique handles all unique elements in a tree until they can't fit + in. Then the tree is dumped to the temporary file. We can use + simple_raw_key_cmp because the table contains numbers only; decimals + are converted to binary representation as well. */ - key_length= ((hybrid_type == DECIMAL_RESULT) ? - my_decimal_get_binary_size(args[0]->max_length, - args[0]->decimals) : - sizeof(double)); - tree= new Unique(simple_raw_key_cmp, &key_length, key_length, + tree= new Unique(simple_raw_key_cmp, &tree_key_length, tree_key_length, thd->variables.max_heap_table_size); - DBUG_PRINT("info", ("tree 0x%lx, key length %d", (ulong)tree, - key_length)); - DBUG_RETURN(tree == 0); -} - -void Item_sum_sum_distinct::clear() -{ - DBUG_ENTER("Item_sum_sum_distinct::clear"); - DBUG_ASSERT(tree != 0); /* we always have a tree */ - null_value= 1; - tree->reset(); - DBUG_VOID_RETURN; -} -void Item_sum_sum_distinct::cleanup() -{ - Item_sum_num::cleanup(); - delete tree; - tree= 0; + DBUG_RETURN(tree == 0); } -bool Item_sum_sum_distinct::add() +bool Item_sum_distinct::add() { - DBUG_ENTER("Item_sum_sum_distinct::add"); - if (hybrid_type == DECIMAL_RESULT) + args[0]->save_in_field(table->field[0], FALSE); + if (!table->field[0]->is_null()) { - my_decimal value, *val= args[0]->val_decimal(&value); - if (!args[0]->null_value) - { - DBUG_ASSERT(tree != 0); - null_value= 0; - my_decimal2binary(E_DEC_FATAL_ERROR, val, (char *) dec_bin_buff, - args[0]->max_length, args[0]->decimals); - DBUG_RETURN(tree->unique_add(dec_bin_buff)); - } - } - else - { - /* args[0]->val() may reset args[0]->null_value */ - double val= args[0]->val_real(); - if (!args[0]->null_value) - { - DBUG_ASSERT(tree != 0); - null_value= 0; - DBUG_PRINT("info", ("real: %lg, tree 0x%lx", val, (ulong)tree)); - if (val) - DBUG_RETURN(tree->unique_add(&val)); - } - else - DBUG_PRINT("info", ("real: NULL")); + DBUG_ASSERT(tree); + null_value= 0; + /* + '0' values are also stored in the tree. This doesn't matter + for SUM(DISTINCT), but is important for AVG(DISTINCT) + */ + return tree->unique_add(table->field[0]->ptr); } - DBUG_RETURN(0); + return 0; } -void Item_sum_sum_distinct::add_real(double val) +bool Item_sum_distinct::unique_walk_function(void *element) { - DBUG_ENTER("Item_sum_sum_distinct::add_real"); - sum+= val; - DBUG_PRINT("info", ("sum %lg, val %lg", sum, val)); - DBUG_VOID_RETURN; + memcpy(table->field[0]->ptr, element, tree_key_length); + ++count; + val.traits->add(&val, table->field[0]); + return 0; } -void Item_sum_sum_distinct::add_decimal(byte *val) +void Item_sum_distinct::clear() { - binary2my_decimal(E_DEC_FATAL_ERROR, (char *) val, &tmp_dec, - args[0]->max_length, args[0]->decimals); - my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff^1), - &tmp_dec, dec_buffs + curr_dec_buff); - curr_dec_buff^= 1; + DBUG_ENTER("Item_sum_distinct::clear"); + DBUG_ASSERT(tree != 0); /* we always have a tree */ + null_value= 1; + tree->reset(); + DBUG_VOID_RETURN; } -C_MODE_START - -static int sum_sum_distinct_real(void *element, element_count num_of_dups, - void *item_sum_sum_distinct) -{ - ((Item_sum_sum_distinct *) - (item_sum_sum_distinct))->add_real(* (double *) element); - return 0; +void Item_sum_distinct::cleanup() +{ + Item_sum_num::cleanup(); + delete tree; + tree= 0; + table= 0; } -static int sum_sum_distinct_decimal(void *element, element_count num_of_dups, - void *item_sum_sum_distinct) -{ - ((Item_sum_sum_distinct *) - (item_sum_sum_distinct))->add_decimal((byte *)element); - return 0; +Item_sum_distinct::~Item_sum_distinct() +{ + delete tree; + /* no need to free the table */ } -C_MODE_END - -double Item_sum_sum_distinct::val_real() +void Item_sum_distinct::calculate_val_and_count() { - DBUG_ENTER("Item_sum_sum_distinct::val"); + count= 0; + val.traits->set_zero(&val); /* We don't have a tree only if 'setup()' hasn't been called; this is the case of sql_select.cc:return_zero_rows. */ - if (hybrid_type == DECIMAL_RESULT) + if (tree) { - /* Item_sum_sum_distinct::val_decimal do not use argument */ - my_decimal *val= val_decimal(0); - if (!null_value) - my_decimal2double(E_DEC_FATAL_ERROR, val, &sum); - } - else - { - sum= 0.0; - DBUG_PRINT("info", ("tree 0x%lx", (ulong)tree)); - if (tree) - tree->walk(sum_sum_distinct_real, (void *) this); + table->field[0]->set_notnull(); + tree->walk(item_sum_distinct_walk, (void*) this); } - DBUG_RETURN(sum); } -my_decimal *Item_sum_sum_distinct::val_decimal(my_decimal *fake) +double Item_sum_distinct::val_real() { - if (hybrid_type == DECIMAL_RESULT) - { - my_decimal_set_zero(dec_buffs); - curr_dec_buff= 0; - if (tree) - tree->walk(sum_sum_distinct_decimal, (void *)this); - } - else - { - double real= val_real(); - curr_dec_buff= 0; - double2my_decimal(E_DEC_FATAL_ERROR, real, dec_buffs); - } - return(dec_buffs + curr_dec_buff); + calculate_val_and_count(); + return val.traits->val_real(&val); } -longlong Item_sum_sum_distinct::val_int() +my_decimal *Item_sum_distinct::val_decimal(my_decimal *to) { - longlong result; - if (hybrid_type == DECIMAL_RESULT) - { - /* Item_sum_sum_distinct::val_decimal do not use argument */ - my_decimal *val= val_decimal(0); - if (!null_value) - my_decimal2int(E_DEC_FATAL_ERROR, val, unsigned_flag, &result); - } - else - result= (longlong) val_real(); - return result; + calculate_val_and_count(); + if (null_value) + return 0; + return val.traits->val_decimal(&val, to); } -String *Item_sum_sum_distinct::val_str(String *str) +longlong Item_sum_distinct::val_int() { - DBUG_ASSERT(fixed == 1); - if (hybrid_type == DECIMAL_RESULT) - return val_string_from_decimal(str); - return val_string_from_real(str); + calculate_val_and_count(); + return val.traits->val_int(&val, unsigned_flag); } -/* end of Item_sum_sum_distinct */ +String *Item_sum_distinct::val_str(String *str) +{ + calculate_val_and_count(); + if (null_value) + return 0; + return val.traits->val_str(&val, str, decimals); +} + +/* end of Item_sum_distinct */ + +/* Item_sum_avg_distinct */ + +void +Item_sum_avg_distinct::calculate_val_and_count() +{ + Item_sum_distinct::calculate_val_and_count(); + if (count) + val.traits->div(&val, count); +} + Item *Item_sum_count::copy_or_same(THD* thd) { diff --git a/sql/item_sum.h b/sql/item_sum.h index af2710d7800..641acb0efd9 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -30,8 +30,8 @@ class Item_sum :public Item_result_field public: enum Sumfunctype { COUNT_FUNC, COUNT_DISTINCT_FUNC, SUM_FUNC, SUM_DISTINCT_FUNC, AVG_FUNC, - MIN_FUNC, MAX_FUNC, UNIQUE_USERS_FUNC, STD_FUNC, VARIANCE_FUNC, - SUM_BIT_FUNC, UDF_SUM_FUNC, GROUP_CONCAT_FUNC + AVG_DISTINCT_FUNC, MIN_FUNC, MAX_FUNC, UNIQUE_USERS_FUNC, STD_FUNC, + VARIANCE_FUNC, SUM_BIT_FUNC, UDF_SUM_FUNC, GROUP_CONCAT_FUNC }; Item **args, *tmp_args[2]; @@ -68,6 +68,9 @@ public: a temporary table. Similar to reset(), but must also store value in result_field. Like reset() it is supposed to reset start value to default. + This set of methods (reult_field(), reset_field, update_field()) of + Item_sum is used only if quick_group is not null. Otherwise + copy_or_same() is used to obtain a copy of this item. */ virtual void reset_field()=0; /* @@ -161,26 +164,28 @@ public: }; -/* - Item_sum_sum_distinct - SELECT SUM(DISTINCT expr) FROM ... - support. See also: MySQL manual, chapter 'Adding New Functions To MySQL' - and comments in item_sum.cc. -*/ + +/* Common class for SUM(DISTINCT), AVG(DISTINCT) */ class Unique; -class Item_sum_sum_distinct :public Item_sum_sum +class Item_sum_distinct :public Item_sum_num { +protected: + /* storage for the summation result */ + ulonglong count; + Hybrid_type val; + /* storage for unique elements */ Unique *tree; - byte *dec_bin_buff; - my_decimal tmp_dec; - uint key_length; -private: - Item_sum_sum_distinct(THD *thd, Item_sum_sum_distinct *item); + TABLE *table; + enum enum_field_types table_field_type; + uint tree_key_length; +protected: + Item_sum_distinct(THD *thd, Item_sum_distinct *item); public: - Item_sum_sum_distinct(Item *item_par); - ~Item_sum_sum_distinct() {} - + Item_sum_distinct(Item *item_par); + ~Item_sum_distinct(); + bool setup(THD *thd); void clear(); void cleanup(); @@ -190,15 +195,54 @@ public: longlong val_int(); String *val_str(String *str); - void add_real(double val); - void add_decimal(byte *val); + /* XXX: does it need make_unique? */ + enum Sumfunctype sum_func () const { return SUM_DISTINCT_FUNC; } void reset_field() {} // not used void update_field() {} // not used const char *func_name() const { return "sum_distinct"; } - Item *copy_or_same(THD* thd); virtual void no_rows_in_result() {} void fix_length_and_dec(); + enum Item_result result_type () const { return val.traits->type(); } + virtual void calculate_val_and_count(); + virtual bool unique_walk_function(void *elem); +}; + + +/* + Item_sum_sum_distinct - implementation of SUM(DISTINCT expr). + See also: MySQL manual, chapter 'Adding New Functions To MySQL' + and comments in item_sum.cc. +*/ + +class Item_sum_sum_distinct :public Item_sum_distinct +{ +private: + Item_sum_sum_distinct(THD *thd, Item_sum_sum_distinct *item) + :Item_sum_distinct(thd, item) {} +public: + Item_sum_sum_distinct(Item *item_arg) :Item_sum_distinct(item_arg) {} + + enum Sumfunctype sum_func () const { return SUM_DISTINCT_FUNC; } + const char *func_name() const { return "sum_distinct"; } + Item *copy_or_same(THD* thd) { return new Item_sum_sum_distinct(thd, this); } +}; + + +/* Item_sum_avg_distinct - SELECT AVG(DISTINCT expr) FROM ... */ + +class Item_sum_avg_distinct: public Item_sum_distinct +{ +private: + Item_sum_avg_distinct(THD *thd, Item_sum_avg_distinct *original) + :Item_sum_distinct(thd, original) {} +public: + Item_sum_avg_distinct(Item *item_arg) : Item_sum_distinct(item_arg) {} + + virtual void calculate_val_and_count(); + enum Sumfunctype sum_func () const { return AVG_DISTINCT_FUNC; } + const char *func_name() const { return "avg_distinct"; } + Item *copy_or_same(THD* thd) { return new Item_sum_avg_distinct(thd, this); } }; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index abc4dac4a8a..cd8587f0230 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -821,7 +821,7 @@ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, uint const char *msg); void push_warning_printf(THD *thd, MYSQL_ERROR::enum_warning_level level, uint code, const char *format, ...); -void mysql_reset_errors(THD *thd); +void mysql_reset_errors(THD *thd, bool force= false); bool mysqld_show_warnings(THD *thd, ulong levels_to_show); /* sql_handler.cc */ diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 5c96de1d73a..c55700b4495 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4216,6 +4216,7 @@ enum options_mysqld OPT_INNODB_STATUS_FILE, OPT_INNODB_MAX_DIRTY_PAGES_PCT, OPT_INNODB_TABLE_LOCKS, + OPT_INNODB_SUPPORT_XA, OPT_INNODB_OPEN_FILES, OPT_INNODB_AUTOEXTEND_INCREMENT, OPT_INNODB_SYNC_SPIN_LOOPS, @@ -4519,6 +4520,11 @@ Disable with --skip-innodb-doublewrite.", (gptr*) &innobase_use_doublewrite, (gptr*) &global_system_variables.innodb_table_locks, (gptr*) &global_system_variables.innodb_table_locks, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, + {"innodb_support_xa", OPT_INNODB_SUPPORT_XA, + "Enable InnoDB support for the XA two-phase commit", + (gptr*) &global_system_variables.innodb_support_xa, + (gptr*) &global_system_variables.innodb_support_xa, + 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, #endif /* End HAVE_INNOBASE_DB */ {"isam", OPT_ISAM, "Enable ISAM (if this version of MySQL supports it). \ Disable with --skip-isam.", diff --git a/sql/set_var.cc b/sql/set_var.cc index ad8eaee10f2..bb5d386934d 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -392,6 +392,8 @@ sys_var_long_ptr sys_innodb_max_purge_lag("innodb_max_purge_lag", &srv_max_purge_lag); sys_var_thd_bool sys_innodb_table_locks("innodb_table_locks", &SV::innodb_table_locks); +sys_var_thd_bool sys_innodb_support_xa("innodb_support_xa", + &SV::innodb_support_xa); sys_var_long_ptr sys_innodb_autoextend_increment("innodb_autoextend_increment", &srv_auto_extend_increment); sys_var_long_ptr sys_innodb_sync_spin_loops("innodb_sync_spin_loops", @@ -689,6 +691,7 @@ sys_var *sys_variables[]= &sys_innodb_max_dirty_pages_pct, &sys_innodb_max_purge_lag, &sys_innodb_table_locks, + &sys_innodb_support_xa, &sys_innodb_max_purge_lag, &sys_innodb_autoextend_increment, &sys_innodb_sync_spin_loops, @@ -810,6 +813,7 @@ struct show_var_st init_vars[]= { {"innodb_open_files", (char*) &innobase_open_files, SHOW_LONG }, {sys_innodb_sync_spin_loops.name, (char*) &sys_innodb_sync_spin_loops, SHOW_SYS}, {sys_innodb_table_locks.name, (char*) &sys_innodb_table_locks, SHOW_SYS}, + {sys_innodb_support_xa.name, (char*) &sys_innodb_support_xa, SHOW_SYS}, {sys_innodb_thread_concurrency.name, (char*) &sys_innodb_thread_concurrency, SHOW_SYS}, {sys_innodb_thread_sleep_delay.name, (char*) &sys_innodb_thread_sleep_delay, SHOW_SYS}, #endif diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 4fd943c08f4..09640eb3f57 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1606,7 +1606,8 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, if (ha_create_table_from_engine(thd, db, name, TRUE) != 0) goto err; - thd->clear_error(); // Clear error message + mysql_reset_errors(thd, true); // Clear warnings + thd->clear_error(); // Clear error message continue; } diff --git a/sql/sql_class.h b/sql/sql_class.h index e185631f5d6..2335ff54bfd 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -578,6 +578,7 @@ struct system_variables #endif /* HAVE_REPLICATION */ #ifdef HAVE_INNOBASE_DB my_bool innodb_table_locks; + my_bool innodb_support_xa; #endif /* HAVE_INNOBASE_DB */ #ifdef HAVE_NDBCLUSTER_DB ulong ndb_autoincrement_prefetch_sz; diff --git a/sql/sql_error.cc b/sql/sql_error.cc index 4d254b95514..4420f2d16ad 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -64,6 +64,7 @@ void MYSQL_ERROR::set_msg(THD *thd, const char *msg_arg) SYNOPSIS mysql_reset_errors() thd Thread handle + force Reset warnings even if it has been done before IMPLEMENTATION Don't reset warnings if this has already been called for this query. @@ -71,14 +72,15 @@ void MYSQL_ERROR::set_msg(THD *thd, const char *msg_arg) in which case push_warnings() has already called this function. */ -void mysql_reset_errors(THD *thd) +void mysql_reset_errors(THD *thd, bool force) { DBUG_ENTER("mysql_reset_errors"); - if (thd->query_id != thd->warn_id) + if (thd->query_id != thd->warn_id || force) { thd->warn_id= thd->query_id; free_root(&thd->warn_root,MYF(0)); bzero((char*) thd->warn_count, sizeof(thd->warn_count)); + if (force) thd->total_warn_count= 0; thd->warn_list.empty(); thd->row_count= 1; // by default point to row 1 } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 169e990e879..758d02faf67 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8359,6 +8359,117 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, } +/****************************************************************************/ + +/* + Create a reduced TABLE object with properly set up Field list from a + list of field definitions. + + SYNOPSIS + create_virtual_tmp_table() + thd connection handle + field_list list of column definitions + + DESCRIPTION + The created table doesn't have a table handler assotiated with + it, has no keys, no group/distinct, no copy_funcs array. + The sole purpose of this TABLE object is to use the power of Field + class to read/write data to/from table->record[0]. Then one can store + the record in any container (RB tree, hash, etc). + The table is created in THD mem_root, so are the table's fields. + Consequently, if you don't BLOB fields, you don't need to free it. + + RETURN + 0 if out of memory, TABLE object in case of success +*/ + +TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list) +{ + uint field_count= field_list.elements; + Field **field; + create_field *cdef; /* column definition */ + uint record_length= 0; + uint null_count= 0; /* number of columns which may be null */ + uint null_pack_length; /* NULL representation array length */ + TABLE_SHARE *s; + /* Create the table and list of all fields */ + TABLE *table= (TABLE*) thd->calloc(sizeof(*table)); + field= (Field**) thd->alloc((field_count + 1) * sizeof(Field*)); + if (!table || !field) + return 0; + + table->field= field; + table->s= s= &table->share_not_to_be_used; + s->fields= field_count; + + /* Create all fields and calculate the total length of record */ + List_iterator_fast<create_field> it(field_list); + while ((cdef= it++)) + { + *field= make_field(0, cdef->length, + (uchar*) (f_maybe_null(cdef->pack_flag) ? "" : 0), + f_maybe_null(cdef->pack_flag) ? 1 : 0, + cdef->pack_flag, cdef->sql_type, cdef->charset, + cdef->geom_type, cdef->unireg_check, + cdef->interval, cdef->field_name, table); + if (!*field) + goto error; + record_length+= (**field).pack_length(); + if (! ((**field).flags & NOT_NULL_FLAG)) + ++null_count; + ++field; + } + *field= NULL; /* mark the end of the list */ + + null_pack_length= (null_count + 7)/8; + s->reclength= record_length + null_pack_length; + s->rec_buff_length= ALIGN_SIZE(s->reclength + 1); + table->record[0]= (byte*) thd->alloc(s->rec_buff_length); + if (!table->record[0]) + goto error; + + if (null_pack_length) + { + table->null_flags= (uchar*) table->record[0]; + s->null_fields= null_count; + s->null_bytes= null_pack_length; + } + + table->in_use= thd; /* field->reset() may access table->in_use */ + { + /* Set up field pointers */ + byte *null_pos= table->record[0]; + byte *field_pos= null_pos + s->null_bytes; + uint null_bit= 1; + + for (field= table->field; *field; ++field) + { + Field *cur_field= *field; + if ((cur_field->flags & NOT_NULL_FLAG)) + cur_field->move_field((char*) field_pos); + else + { + cur_field->move_field((char*) field_pos, (uchar*) null_pos, null_bit); + null_bit<<= 1; + if (null_bit == (1 << 8)) + { + ++null_pos; + null_bit= 1; + } + } + cur_field->reset(); + + field_pos+= cur_field->pack_length(); + } + } + return table; +error: + for (field= table->field; *field; ++field) + delete *field; /* just invokes field destructor */ + return 0; +} + + static bool open_tmp_table(TABLE *table) { int error; diff --git a/sql/sql_select.h b/sql/sql_select.h index 1b7893dbc7c..f00fd476edd 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -387,6 +387,7 @@ TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, ORDER *group, bool distinct, bool save_sum_fields, ulong select_options, ha_rows rows_limit, char* alias); +TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list); void free_tmp_table(THD *thd, TABLE *entry); void count_field_types(TMP_TABLE_PARAM *param, List<Item> &fields, bool reset_with_sum_func); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 212f004e3bf..56dd6409eba 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -4754,6 +4754,8 @@ udf_expr: sum_expr: AVG_SYM '(' in_sum_expr ')' { $$=new Item_sum_avg($3); } + | AVG_SYM '(' DISTINCT in_sum_expr ')' + { $$=new Item_sum_avg_distinct($4); } | BIT_AND '(' in_sum_expr ')' { $$=new Item_sum_and($3); } | BIT_OR '(' in_sum_expr ')' |