From: Date: September 16 2008 7:59pm Subject: bzr commit into mysql-6.0-falcon branch (hky:2685) List-Archive: http://lists.mysql.com/commits/54222 Message-Id: <20080916175919.9116B12376E@lu0011.efendi.com> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit #At file:///home/hakan/work/mysql/mysql-6.0-falcon-to-merge/ 2685 Hakan Kuecuekyilmaz 2008-09-16 [merge] Merge. added: mysql-test/r/test_wl4435.result sql/records.h sql/transaction.cc sql/transaction.h modified: .bzr-mysql/default.conf .bzrignore client/Makefile.am include/mysql.h include/mysql.h.pp include/mysql_com.h libmysql/client_settings.h libmysql/libmysql.c libmysql/libmysql.def libmysqld/CMakeLists.txt libmysqld/Makefile.am libmysqld/lib_sql.cc libmysqld/libmysqld.def mysql-test/r/events_bugs.result mysql-test/r/ps.result mysql-test/suite/falcon/r/falcon_bug_34164.result mysql-test/t/events_bugs.test mysql-test/t/ps.test sql/CMakeLists.txt sql/Makefile.am sql/backup/backup_test.cc sql/backup/be_snapshot.cc sql/backup/kernel.cc sql/events.cc sql/field.cc sql/field.h sql/handler.cc sql/handler.h sql/item.cc sql/item.h sql/lock.cc sql/log_event.cc sql/log_event_old.cc sql/mysql_priv.h sql/procedure.h sql/protocol.cc sql/protocol.h sql/records.cc sql/repl_failsafe.cc sql/rpl_injector.cc sql/rpl_injector.h sql/rpl_rli.cc sql/rpl_tblmap.cc sql/rpl_tblmap.h sql/set_var.cc sql/slave.cc sql/sp_head.cc sql/sql_acl.cc sql/sql_base.cc sql/sql_cache.h sql/sql_class.cc sql/sql_class.h sql/sql_cursor.cc sql/sql_do.cc sql/sql_error.cc sql/sql_handler.cc sql/sql_help.cc sql/sql_insert.cc sql/sql_lex.cc sql/sql_lex.h sql/sql_parse.cc sql/sql_partition.cc sql/sql_prepare.cc sql/sql_profile.cc sql/sql_repl.cc sql/sql_select.cc sql/sql_select.h sql/sql_show.cc sql/sql_table.cc sql/sql_yacc.yy sql/structs.h sql/table.cc sql/table.h storage/falcon/InfoTable.h storage/falcon/ha_falcon.cpp storage/falcon/ha_falcon.h storage/innobase/handler/ha_innodb.cc storage/maria/ha_maria.cc tests/mysql_client_test.c === modified file '.bzr-mysql/default.conf' --- a/.bzr-mysql/default.conf 2008-09-11 18:36:05 +0000 +++ b/.bzr-mysql/default.conf 2008-09-16 17:58:49 +0000 @@ -1,5 +1,5 @@ -[MYSQL] -tree_location = bzr+ssh://bk-internal.mysql.com/bzrroot/server/mysql-6.0-falcon -post_commit_to = commits@stripped -post_push_to = commits@stripped -tree_name = mysql-6.0-falcon +[MYSQL] +tree_location = bzr+ssh://bk-internal.mysql.com/bzrroot/server/mysql-6.0-falcon +post_commit_to = commits@stripped +post_push_to = commits@stripped +tree_name = mysql-6.0-falcon === modified file '.bzrignore' --- a/.bzrignore 2008-08-26 10:20:41 +0000 +++ b/.bzrignore 2008-09-12 09:22:46 +0000 @@ -1891,3 +1891,5 @@ zlib/*.vcproj extra/libevent/event-config.h libmysqld/ddl_blocker.cc libmysqld/mdl.cc +client/transaction.h +libmysqld/transaction.cc === modified file 'client/Makefile.am' --- a/client/Makefile.am 2008-08-20 16:21:14 +0000 +++ b/client/Makefile.am 2008-09-04 18:30:34 +0000 @@ -107,7 +107,8 @@ sql_src=log_event.h mysql_priv.h rpl_con rpl_utility.h rpl_tblmap.h rpl_tblmap.cc \ log_event.cc my_decimal.h my_decimal.cc \ log_event_old.h log_event_old.cc \ - rpl_record_old.h rpl_record_old.cc + rpl_record_old.h rpl_record_old.cc \ + transaction.h strings_src=decimal.c dtoa.c link_sources: === modified file 'include/mysql.h' --- a/include/mysql.h 2007-11-26 19:11:48 +0000 +++ b/include/mysql.h 2008-08-07 17:52:43 +0000 @@ -714,6 +714,7 @@ my_bool STDCALL mysql_rollback(MYSQL * m my_bool STDCALL mysql_autocommit(MYSQL * mysql, my_bool auto_mode); my_bool STDCALL mysql_more_results(MYSQL *mysql); int STDCALL mysql_next_result(MYSQL *mysql); +int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt); void STDCALL mysql_close(MYSQL *sock); === modified file 'include/mysql.h.pp' --- a/include/mysql.h.pp 2008-08-20 07:07:09 +0000 +++ b/include/mysql.h.pp 2008-09-12 08:38:48 +0000 @@ -612,4 +612,5 @@ my_bool mysql_rollback(MYSQL * mysql); my_bool mysql_autocommit(MYSQL * mysql, my_bool auto_mode); my_bool mysql_more_results(MYSQL *mysql); int mysql_next_result(MYSQL *mysql); +int mysql_stmt_next_result(MYSQL_STMT *stmt); void mysql_close(MYSQL *sock); === modified file 'include/mysql_com.h' --- a/include/mysql_com.h 2008-06-17 20:04:19 +0000 +++ b/include/mysql_com.h 2008-08-07 17:52:43 +0000 @@ -154,6 +154,7 @@ enum enum_server_command #define CLIENT_SECURE_CONNECTION 32768 /* New 4.1 authentication */ #define CLIENT_MULTI_STATEMENTS (1UL << 16) /* Enable/disable multi-stmt support */ #define CLIENT_MULTI_RESULTS (1UL << 17) /* Enable/disable multi-results */ +#define CLIENT_PS_MULTI_RESULTS (1UL << 18) /* Multi-results in PS-protocol */ #define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30) #define CLIENT_REMEMBER_OPTIONS (1UL << 31) @@ -177,6 +178,7 @@ enum enum_server_command CLIENT_SECURE_CONNECTION | \ CLIENT_MULTI_STATEMENTS | \ CLIENT_MULTI_RESULTS | \ + CLIENT_PS_MULTI_RESULTS | \ CLIENT_SSL_VERIFY_SERVER_CERT | \ CLIENT_REMEMBER_OPTIONS) @@ -221,6 +223,11 @@ enum enum_server_command #define SERVER_QUERY_WAS_SLOW 2048 /** + To mark ResultSet containing output parameter values. +*/ +#define SERVER_PS_OUT_PARAMS 4096 + +/** Server status flags that must be cleared when starting execution of a new SQL statement. Flags from this set are only added to the === modified file 'libmysql/client_settings.h' --- a/libmysql/client_settings.h 2007-09-29 19:31:08 +0000 +++ b/libmysql/client_settings.h 2008-08-07 17:52:43 +0000 @@ -16,9 +16,13 @@ extern uint mysql_port; extern char * mysql_unix_port; -#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | \ - CLIENT_TRANSACTIONS | \ - CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION) +#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | \ + CLIENT_LONG_FLAG | \ + CLIENT_TRANSACTIONS | \ + CLIENT_PROTOCOL_41 | \ + CLIENT_SECURE_CONNECTION | \ + CLIENT_MULTI_RESULTS | \ + CLIENT_PS_MULTI_RESULTS) sig_handler my_pipe_sig_handler(int sig); void read_user_name(char *name); === modified file 'libmysql/libmysql.c' --- a/libmysql/libmysql.c 2008-06-17 20:04:19 +0000 +++ b/libmysql/libmysql.c 2008-08-07 17:52:43 +0000 @@ -1725,6 +1725,8 @@ static void alloc_stmt_fields(MYSQL_STMT MEM_ROOT *alloc= &stmt->mem_root; MYSQL *mysql= stmt->mysql; + DBUG_ASSERT(mysql->field_count); + stmt->field_count= mysql->field_count; /* @@ -1746,6 +1748,7 @@ static void alloc_stmt_fields(MYSQL_STMT field= stmt->fields; field && fields < end; fields++, field++) { + field->catalog = strdup_root(alloc,fields->catalog); field->db = strdup_root(alloc,fields->db); field->table = strdup_root(alloc,fields->table); field->org_table= strdup_root(alloc,fields->org_table); @@ -2484,6 +2487,33 @@ static void reinit_result_set_metadata(M } +static void prepare_to_fetch_result(MYSQL_STMT *stmt) +{ + if (stmt->server_status & SERVER_STATUS_CURSOR_EXISTS) + { + stmt->mysql->status= MYSQL_STATUS_READY; + stmt->read_row_func= stmt_read_row_from_cursor; + } + else if (stmt->flags & CURSOR_TYPE_READ_ONLY) + { + /* + This is a single-row result set, a result set with no rows, EXPLAIN, + SHOW VARIABLES, or some other command which either a) bypasses the + cursors framework in the server and writes rows directly to the + network or b) is more efficient if all (few) result set rows are + precached on client and server's resources are freed. + */ + mysql_stmt_store_result(stmt); + } + else + { + stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled; + stmt->unbuffered_fetch_cancelled= FALSE; + stmt->read_row_func= stmt_read_row_unbuffered; + } +} + + /* Send placeholders data to server (if there are placeholders) and execute prepared statement. @@ -2551,28 +2581,7 @@ int STDCALL mysql_stmt_execute(MYSQL_STM if (mysql->field_count) { reinit_result_set_metadata(stmt); - if (stmt->server_status & SERVER_STATUS_CURSOR_EXISTS) - { - mysql->status= MYSQL_STATUS_READY; - stmt->read_row_func= stmt_read_row_from_cursor; - } - else if (stmt->flags & CURSOR_TYPE_READ_ONLY) - { - /* - This is a single-row result set, a result set with no rows, EXPLAIN, - SHOW VARIABLES, or some other command which either a) bypasses the - cursors framework in the server and writes rows directly to the - network or b) is more efficient if all (few) result set rows are - precached on client and server's resources are freed. - */ - mysql_stmt_store_result(stmt); - } - else - { - stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled; - stmt->unbuffered_fetch_cancelled= FALSE; - stmt->read_row_func= stmt_read_row_unbuffered; - } + prepare_to_fetch_result(stmt); } DBUG_RETURN(test(stmt->last_errno)); } @@ -4831,6 +4840,46 @@ int STDCALL mysql_next_result(MYSQL *mys } +int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt) +{ + MYSQL *mysql= stmt->mysql; + int rc; + DBUG_ENTER("mysql_stmt_next_result"); + + if (!mysql) + DBUG_RETURN(1); + + if (stmt->last_errno) + DBUG_RETURN(stmt->last_errno); + + if (mysql->server_status & SERVER_MORE_RESULTS_EXISTS) + { + if (reset_stmt_handle(stmt, RESET_STORE_RESULT)) + DBUG_RETURN(1); + } + + rc= mysql_next_result(mysql); + + if (rc) + DBUG_RETURN(rc); + + stmt->state= MYSQL_STMT_EXECUTE_DONE; + stmt->bind_result_done= FALSE; + + if (mysql->field_count) + { + alloc_stmt_fields(stmt); + prepare_to_fetch_result(stmt); + } + else + { + stmt->field_count= mysql->field_count; + } + + DBUG_RETURN(0); +} + + MYSQL_RES * STDCALL mysql_use_result(MYSQL *mysql) { return (*mysql->methods->use_result)(mysql); === modified file 'libmysql/libmysql.def' --- a/libmysql/libmysql.def 2007-12-04 15:27:44 +0000 +++ b/libmysql/libmysql.def 2008-08-11 10:03:45 +0000 @@ -142,3 +142,4 @@ EXPORTS mysql_get_character_set_info get_defaults_options modify_defaults_file + mysql_stmt_next_result === modified file 'libmysqld/CMakeLists.txt' --- a/libmysqld/CMakeLists.txt 2008-07-09 07:12:43 +0000 +++ b/libmysqld/CMakeLists.txt 2008-08-08 01:33:43 +0000 @@ -203,6 +203,7 @@ SET(LIBMYSQLD_SOURCES emb_qcache.cc libm ../sql/scheduler.cc ../sql/sql_audit.cc ../sql/ddl_blocker.cc ../sql/si_objects.cc ../sql/event_parse_data.cc ../sql/mdl.cc + ../sql/transaction.cc ${GEN_SOURCES} ${LIB_SOURCES}) === modified file 'libmysqld/Makefile.am' --- a/libmysqld/Makefile.am 2008-07-09 07:12:43 +0000 +++ b/libmysqld/Makefile.am 2008-08-08 01:33:43 +0000 @@ -78,8 +78,7 @@ sqlsources = derror.cc field.cc field_co sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc sp_rcontext.cc \ parse_file.cc sql_view.cc sql_trigger.cc my_decimal.cc \ rpl_filter.cc sql_partition.cc sql_builtin.cc sql_plugin.cc \ - debug_sync.cc \ - sql_tablespace.cc \ + debug_sync.cc sql_tablespace.cc transaction.cc \ rpl_injector.cc my_user.c partition_info.cc \ sql_servers.cc ddl_blocker.cc si_objects.cc sql_audit.cc \ event_parse_data.cc mdl.cc === modified file 'libmysqld/lib_sql.cc' --- a/libmysqld/lib_sql.cc 2008-08-13 19:17:28 +0000 +++ b/libmysqld/lib_sql.cc 2008-09-04 18:30:34 +0000 @@ -870,7 +870,7 @@ void Protocol_text::remove_last_row() } -bool Protocol::send_fields(List *list, uint flags) +bool Protocol::send_result_set_metadata(List *list, uint flags) { List_iterator_fast it(*list); Item *item; @@ -879,7 +879,7 @@ bool Protocol::send_fields(List *l CHARSET_INFO *thd_cs= thd->variables.character_set_results; CHARSET_INFO *cs= system_charset_info; MYSQL_DATA *data; - DBUG_ENTER("send_fields"); + DBUG_ENTER("send_result_set_metadata"); if (!thd->mysql) // bootstrap file handling DBUG_RETURN(0); @@ -972,7 +972,7 @@ bool Protocol::send_fields(List *l if (flags & SEND_EOF) write_eof_packet(thd, thd->server_status, thd->total_warn_count); - DBUG_RETURN(prepare_for_send(list)); + DBUG_RETURN(prepare_for_send(list->elements)); err: my_error(ER_OUT_OF_RESOURCES, MYF(0)); /* purecov: inspected */ DBUG_RETURN(1); /* purecov: inspected */ === modified file 'libmysqld/libmysqld.def' --- a/libmysqld/libmysqld.def 2007-12-04 15:27:44 +0000 +++ b/libmysqld/libmysqld.def 2008-08-11 10:03:45 +0000 @@ -170,3 +170,4 @@ EXPORTS my_charset_bin my_charset_same modify_defaults_file + mysql_stmt_next_result === modified file 'mysql-test/r/events_bugs.result' --- a/mysql-test/r/events_bugs.result 2008-02-19 14:09:52 +0000 +++ b/mysql-test/r/events_bugs.result 2008-09-12 19:34:30 +0000 @@ -736,3 +736,5 @@ select name from mysql.event where name name drop event e1; DROP DATABASE events_test; +# restore the defeault +SET GLOBAL event_scheduler = 'on'; === modified file 'mysql-test/r/ps.result' --- a/mysql-test/r/ps.result 2008-07-24 10:00:56 +0000 +++ b/mysql-test/r/ps.result 2008-08-07 17:52:43 +0000 @@ -2920,4 +2920,165 @@ execute stmt; Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator character_set_client collation_connection Database Collation drop table t1; deallocate prepare stmt; + End of 5.1 tests. + +# +# WL#4435: Support OUT-parameters in prepared statements. +# + +DROP PROCEDURE IF EXISTS p_string; +DROP PROCEDURE IF EXISTS p_double; +DROP PROCEDURE IF EXISTS p_int; +DROP PROCEDURE IF EXISTS p_decimal; + +CREATE PROCEDURE p_string( +IN v0 INT, +OUT v1 CHAR(32), +IN v2 CHAR(32), +INOUT v3 CHAR(32)) +BEGIN +SET v0 = -1; +SET v1 = 'test_v1'; +SET v2 = 'n/a'; +SET v3 = 'test_v3'; +END| + +CREATE PROCEDURE p_double( +IN v0 INT, +OUT v1 DOUBLE(4, 2), +IN v2 DOUBLE(4, 2), +INOUT v3 DOUBLE(4, 2)) +BEGIN +SET v0 = -1; +SET v1 = 12.34; +SET v2 = 98.67; +SET v3 = 56.78; +END| + +CREATE PROCEDURE p_int( +IN v0 CHAR(10), +OUT v1 INT, +IN v2 INT, +INOUT v3 INT) +BEGIN +SET v0 = 'n/a'; +SET v1 = 1234; +SET v2 = 9876; +SET v3 = 5678; +END| + +CREATE PROCEDURE p_decimal( +IN v0 INT, +OUT v1 DECIMAL(4, 2), +IN v2 DECIMAL(4, 2), +INOUT v3 DECIMAL(4, 2)) +BEGIN +SET v0 = -1; +SET v1 = 12.34; +SET v2 = 98.67; +SET v3 = 56.78; +END| + +PREPARE stmt_str FROM 'CALL p_string(?, ?, ?, ?)'; +PREPARE stmt_dbl FROM 'CALL p_double(?, ?, ?, ?)'; +PREPARE stmt_int FROM 'CALL p_int(?, ?, ?, ?)'; +PREPARE stmt_dec FROM 'CALL p_decimal(?, ?, ?, ?)'; + +SET @x_str_1 = NULL; +SET @x_str_2 = NULL; +SET @x_str_3 = NULL; +SET @x_dbl_1 = NULL; +SET @x_dbl_2 = NULL; +SET @x_dbl_3 = NULL; +SET @x_int_1 = NULL; +SET @x_int_2 = NULL; +SET @x_int_3 = NULL; +SET @x_dec_1 = NULL; +SET @x_dec_2 = NULL; +SET @x_dec_3 = NULL; + +-- Testing strings... + +EXECUTE stmt_str USING @x_int_1, @x_str_1, @x_str_2, @x_str_3; +SELECT @x_int_1, @x_str_1, @x_str_2, @x_str_3; +@x_int_1 @x_str_1 @x_str_2 @x_str_3 +NULL test_v1 NULL test_v3 + +EXECUTE stmt_str USING @x_int_1, @x_str_1, @x_str_2, @x_str_3; +SELECT @x_int_1, @x_str_1, @x_str_2, @x_str_3; +@x_int_1 @x_str_1 @x_str_2 @x_str_3 +NULL test_v1 NULL test_v3 + +-- Testing doubles... + +EXECUTE stmt_dbl USING @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3; +SELECT @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3; +@x_int_1 @x_dbl_1 @x_dbl_2 @x_dbl_3 +NULL 12.34 NULL 56.78 + +EXECUTE stmt_dbl USING @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3; +SELECT @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3; +@x_int_1 @x_dbl_1 @x_dbl_2 @x_dbl_3 +NULL 12.34 NULL 56.78 + +-- Testing ints... + +EXECUTE stmt_int USING @x_str_1, @x_int_1, @x_int_2, @x_int_3; +SELECT @x_str_1, @x_int_1, @x_int_2, @x_int_3; +@x_str_1 @x_int_1 @x_int_2 @x_int_3 +test_v1 1234 NULL 5678 + +EXECUTE stmt_int USING @x_str_1, @x_int_1, @x_int_2, @x_int_3; +SELECT @x_str_1, @x_int_1, @x_int_2, @x_int_3; +@x_str_1 @x_int_1 @x_int_2 @x_int_3 +test_v1 1234 NULL 5678 + +-- Testing decs... + +EXECUTE stmt_dec USING @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3; +SELECT @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3; +@x_int_1 @x_dec_1 @x_dec_2 @x_dec_3 +1234 12.34 NULL 56.78 + +EXECUTE stmt_dec USING @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3; +SELECT @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3; +@x_int_1 @x_dec_1 @x_dec_2 @x_dec_3 +1234 12.34 NULL 56.78 + +DEALLOCATE PREPARE stmt_str; +DEALLOCATE PREPARE stmt_dbl; +DEALLOCATE PREPARE stmt_int; +DEALLOCATE PREPARE stmt_dec; + +DROP PROCEDURE p_string; +DROP PROCEDURE p_double; +DROP PROCEDURE p_int; +DROP PROCEDURE p_decimal; + +DROP PROCEDURE IF EXISTS p1; +DROP PROCEDURE IF EXISTS p2; + +CREATE PROCEDURE p1(OUT v1 CHAR(10)) +SET v1 = 'test1'; + +CREATE PROCEDURE p2(OUT v2 CHAR(10)) +BEGIN +SET @query = 'CALL p1(?)'; +PREPARE stmt1 FROM @query; +EXECUTE stmt1 USING @u1; +DEALLOCATE PREPARE stmt1; +SET v2 = @u1; +END| + +CALL p2(@a); +SELECT @a; +@a +test1 + +DROP PROCEDURE p1; +DROP PROCEDURE p2; + +# End of WL#4435. + +End of 6.0 tests. === added file 'mysql-test/r/test_wl4435.result' --- a/mysql-test/r/test_wl4435.result 1970-01-01 00:00:00 +0000 +++ b/mysql-test/r/test_wl4435.result 2008-08-07 17:52:43 +0000 @@ -0,0 +1,114 @@ + +exec_counter: 0 +num_fields: 4 + - 0: name: 'a1'/'a1'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0 + - 1: name: 'a2'/'a2'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 32; max_length: 0; type: 254; decimals: 0 + - 2: name: 'a3'/'a3'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2 + - 3: name: 'a4'/'a4'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 5; max_length: 0; type: 246; decimals: 1 +Data: + int: 1; str: '11'; dbl: 12.340000; dec: '56.7'; + int: 2; str: '12'; dbl: 56.780000; dec: '90.1'; + int: 3; str: '13'; dbl: 23.450000; dec: '67.8'; +EOF +mysql_stmt_next_result(): 0; field_count: 5 +num_fields: 5 + - 0: name: 'b0'/'b0'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0 + - 1: name: 'b1'/'b1'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0 + - 2: name: 'b2'/'b2'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 32; max_length: 0; type: 254; decimals: 0 + - 3: name: 'b3'/'b3'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2 + - 4: name: 'b4'/'b4'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 5; max_length: 0; type: 246; decimals: 1 +Data: + int: 100; int: 10; str: '110'; dbl: 70.700000; dec: '10.1'; + int: 200; int: 20; str: '120'; dbl: 80.800000; dec: '20.2'; + int: 300; int: 30; str: '130'; dbl: 90.900000; dec: '30.3'; +EOF +mysql_stmt_next_result(): 0; field_count: 8 +num_fields: 8 + - 0: name: 'v_str_1'/'v_str_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 32; max_length: 0; type: 254; decimals: 0 + - 1: name: 'v_dbl_1'/'v_dbl_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2 + - 2: name: 'v_dec_1'/'v_dec_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 8; max_length: 0; type: 246; decimals: 3 + - 3: name: 'v_int_1'/'v_int_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0 + - 4: name: 'v_str_2'/'v_str_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 64; max_length: 0; type: 254; decimals: 0 + - 5: name: 'v_dbl_2'/'v_dbl_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 5; max_length: 0; type: 5; decimals: 3 + - 6: name: 'v_dec_2'/'v_dec_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 9; max_length: 0; type: 246; decimals: 4 + - 7: name: 'v_int_2'/'v_int_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0 +Data: + str: 'test_1'; dbl: 12.340000; dec: '567.891'; int: 2345; str: 'test_2'; dbl: 67.891000; dec: '234.6789'; int: 6789; +EOF +mysql_stmt_next_result(): 0; field_count: 0 + +exec_counter: 1 +num_fields: 4 + - 0: name: 'a1'/'a1'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0 + - 1: name: 'a2'/'a2'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 32; max_length: 0; type: 254; decimals: 0 + - 2: name: 'a3'/'a3'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2 + - 3: name: 'a4'/'a4'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 5; max_length: 0; type: 246; decimals: 1 +Data: + int: 1; str: '11'; dbl: 12.340000; dec: '56.7'; + int: 2; str: '12'; dbl: 56.780000; dec: '90.1'; + int: 3; str: '13'; dbl: 23.450000; dec: '67.8'; +EOF +mysql_stmt_next_result(): 0; field_count: 5 +num_fields: 5 + - 0: name: 'b0'/'b0'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0 + - 1: name: 'b1'/'b1'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0 + - 2: name: 'b2'/'b2'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 32; max_length: 0; type: 254; decimals: 0 + - 3: name: 'b3'/'b3'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2 + - 4: name: 'b4'/'b4'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 5; max_length: 0; type: 246; decimals: 1 +Data: + int: 100; int: 10; str: '110'; dbl: 70.700000; dec: '10.1'; + int: 200; int: 20; str: '120'; dbl: 80.800000; dec: '20.2'; + int: 300; int: 30; str: '130'; dbl: 90.900000; dec: '30.3'; +EOF +mysql_stmt_next_result(): 0; field_count: 8 +num_fields: 8 + - 0: name: 'v_str_1'/'v_str_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 32; max_length: 0; type: 254; decimals: 0 + - 1: name: 'v_dbl_1'/'v_dbl_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2 + - 2: name: 'v_dec_1'/'v_dec_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 8; max_length: 0; type: 246; decimals: 3 + - 3: name: 'v_int_1'/'v_int_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0 + - 4: name: 'v_str_2'/'v_str_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 64; max_length: 0; type: 254; decimals: 0 + - 5: name: 'v_dbl_2'/'v_dbl_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 5; max_length: 0; type: 5; decimals: 3 + - 6: name: 'v_dec_2'/'v_dec_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 9; max_length: 0; type: 246; decimals: 4 + - 7: name: 'v_int_2'/'v_int_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0 +Data: + str: 'test_1'; dbl: 12.340000; dec: '567.891'; int: 2345; str: 'test_2'; dbl: 67.891000; dec: '234.6789'; int: 6789; +EOF +mysql_stmt_next_result(): 0; field_count: 0 + +exec_counter: 2 +num_fields: 4 + - 0: name: 'a1'/'a1'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0 + - 1: name: 'a2'/'a2'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 32; max_length: 0; type: 254; decimals: 0 + - 2: name: 'a3'/'a3'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2 + - 3: name: 'a4'/'a4'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 5; max_length: 0; type: 246; decimals: 1 +Data: + int: 1; str: '11'; dbl: 12.340000; dec: '56.7'; + int: 2; str: '12'; dbl: 56.780000; dec: '90.1'; + int: 3; str: '13'; dbl: 23.450000; dec: '67.8'; +EOF +mysql_stmt_next_result(): 0; field_count: 5 +num_fields: 5 + - 0: name: 'b0'/'b0'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0 + - 1: name: 'b1'/'b1'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0 + - 2: name: 'b2'/'b2'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 32; max_length: 0; type: 254; decimals: 0 + - 3: name: 'b3'/'b3'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2 + - 4: name: 'b4'/'b4'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 5; max_length: 0; type: 246; decimals: 1 +Data: + int: 100; int: 10; str: '110'; dbl: 70.700000; dec: '10.1'; + int: 200; int: 20; str: '120'; dbl: 80.800000; dec: '20.2'; + int: 300; int: 30; str: '130'; dbl: 90.900000; dec: '30.3'; +EOF +mysql_stmt_next_result(): 0; field_count: 8 +num_fields: 8 + - 0: name: 'v_str_1'/'v_str_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 32; max_length: 0; type: 254; decimals: 0 + - 1: name: 'v_dbl_1'/'v_dbl_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2 + - 2: name: 'v_dec_1'/'v_dec_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 8; max_length: 0; type: 246; decimals: 3 + - 3: name: 'v_int_1'/'v_int_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0 + - 4: name: 'v_str_2'/'v_str_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 64; max_length: 0; type: 254; decimals: 0 + - 5: name: 'v_dbl_2'/'v_dbl_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 5; max_length: 0; type: 5; decimals: 3 + - 6: name: 'v_dec_2'/'v_dec_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 9; max_length: 0; type: 246; decimals: 4 + - 7: name: 'v_int_2'/'v_int_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0 +Data: + str: 'test_1'; dbl: 12.340000; dec: '567.891'; int: 2345; str: 'test_2'; dbl: 67.891000; dec: '234.6789'; int: 6789; +EOF +mysql_stmt_next_result(): 0; field_count: 0 === modified file 'mysql-test/suite/falcon/r/falcon_bug_34164.result' --- a/mysql-test/suite/falcon/r/falcon_bug_34164.result 2008-03-06 17:31:28 +0000 +++ b/mysql-test/suite/falcon/r/falcon_bug_34164.result 2008-09-12 08:52:36 +0000 @@ -29,6 +29,8 @@ a b Warnings: Warning 1568 Falcon does not support SERIALIZABLE ISOLATION, using REPEATABLE READ instead. UPDATE t1 SET b = 11 WHERE a = 1; +Warnings: +Warning 1568 Falcon does not support SERIALIZABLE ISOLATION, using REPEATABLE READ instead. # Establish connection conn2 (user = root) USE test2; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; @@ -48,12 +50,16 @@ UPDATE t1 SET b = 111 WHERE a = 1; # Switch to conn1 COMMIT; # Switch to conn2 +Warnings: +Warning 1568 Falcon does not support SERIALIZABLE ISOLATION, using REPEATABLE READ instead. SELECT * FROM t1; a b 1 111 2 2 3 3 4 4 +Warnings: +Warning 1568 Falcon does not support SERIALIZABLE ISOLATION, using REPEATABLE READ instead. COMMIT; # Switch to conn1 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; @@ -73,6 +79,8 @@ a b Warnings: Warning 1568 Falcon does not support READ UNCOMMITTED ISOLATION, using REPEATABLE READ instead. UPDATE t1 SET b = 22 WHERE a = 2; +Warnings: +Warning 1568 Falcon does not support READ UNCOMMITTED ISOLATION, using REPEATABLE READ instead. # Switch to conn2 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SHOW VARIABLES LIKE 'tx_isolation'; @@ -91,12 +99,16 @@ UPDATE t1 SET b = 222 WHERE a = 2; # Switch to conn1 COMMIT; # Switch to conn2 +Warnings: +Warning 1568 Falcon does not support READ UNCOMMITTED ISOLATION, using REPEATABLE READ instead. SELECT * FROM t1; a b 1 111 2 222 3 3 4 4 +Warnings: +Warning 1568 Falcon does not support READ UNCOMMITTED ISOLATION, using REPEATABLE READ instead. COMMIT; # Switch to default connection SET GLOBAL FALCON_CONSISTENT_READ=ON; @@ -119,6 +131,8 @@ a b Warnings: Warning 1568 Falcon does not support SERIALIZABLE ISOLATION, using REPEATABLE READ instead. UPDATE t1 SET b = 33 WHERE a = 3; +Warnings: +Warning 1568 Falcon does not support SERIALIZABLE ISOLATION, using REPEATABLE READ instead. # Establish connection conn4 (user = root) USE test2; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; @@ -145,6 +159,8 @@ a b 2 222 3 3 4 4 +Warnings: +Warning 1568 Falcon does not support SERIALIZABLE ISOLATION, using REPEATABLE READ instead. COMMIT; # Switch to conn3 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; @@ -164,6 +180,8 @@ a b Warnings: Warning 1568 Falcon does not support READ UNCOMMITTED ISOLATION, using REPEATABLE READ instead. UPDATE t1 SET b = 44 WHERE a = 4; +Warnings: +Warning 1568 Falcon does not support READ UNCOMMITTED ISOLATION, using REPEATABLE READ instead. # Switch to conn4 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SHOW VARIABLES LIKE 'tx_isolation'; @@ -189,6 +207,8 @@ a b 2 222 3 33 4 4 +Warnings: +Warning 1568 Falcon does not support READ UNCOMMITTED ISOLATION, using REPEATABLE READ instead. COMMIT; SELECT * FROM t1; a b === modified file 'mysql-test/t/events_bugs.test' --- a/mysql-test/t/events_bugs.test 2008-02-07 10:47:39 +0000 +++ b/mysql-test/t/events_bugs.test 2008-09-12 19:34:30 +0000 @@ -120,6 +120,14 @@ select event_schema, event_name, sql_mod --echo "Let's check whether we change the sql_mode on ALTER EVENT" set sql_mode='traditional'; alter event e_16407 do select 1; + +# wait for it to stop or we will get very spurious test failures +let $wait_condition= + select count(*) = 0 + from information_schema.processlist + where db = 'events_test' and info not like '%processlist%'; +--source include/wait_condition.inc + select event_schema, event_name, sql_mode from information_schema.events order by event_schema, event_name; drop event e_16407; @@ -970,5 +978,7 @@ let $wait_condition= --source include/wait_condition.inc DROP DATABASE events_test; +--echo # restore the defeault +SET GLOBAL event_scheduler = 'on'; # THIS MUST BE THE LAST LINE in this file. === modified file 'mysql-test/t/ps.test' --- a/mysql-test/t/ps.test 2008-05-21 10:17:29 +0000 +++ b/mysql-test/t/ps.test 2008-08-07 17:52:43 +0000 @@ -2998,5 +2998,209 @@ execute stmt; drop table t1; deallocate prepare stmt; +########################################################################### +--echo --echo End of 5.1 tests. + +########################################################################### + +--echo +--echo # +--echo # WL#4435: Support OUT-parameters in prepared statements. +--echo # +--echo + +# The idea of this test case is to check that +# - OUT-parameters of four allowed types (string, double, int, decimal) work +# properly; +# - INOUT and OUT parameters work properly; +# - A mix of IN and OUT parameters work properly; + +--disable_warnings +DROP PROCEDURE IF EXISTS p_string; +DROP PROCEDURE IF EXISTS p_double; +DROP PROCEDURE IF EXISTS p_int; +DROP PROCEDURE IF EXISTS p_decimal; +--enable_warnings + +delimiter |; + +--echo +CREATE PROCEDURE p_string( + IN v0 INT, + OUT v1 CHAR(32), + IN v2 CHAR(32), + INOUT v3 CHAR(32)) +BEGIN + SET v0 = -1; + SET v1 = 'test_v1'; + SET v2 = 'n/a'; + SET v3 = 'test_v3'; +END| + +--echo +CREATE PROCEDURE p_double( + IN v0 INT, + OUT v1 DOUBLE(4, 2), + IN v2 DOUBLE(4, 2), + INOUT v3 DOUBLE(4, 2)) +BEGIN + SET v0 = -1; + SET v1 = 12.34; + SET v2 = 98.67; + SET v3 = 56.78; +END| + +--echo +CREATE PROCEDURE p_int( + IN v0 CHAR(10), + OUT v1 INT, + IN v2 INT, + INOUT v3 INT) +BEGIN + SET v0 = 'n/a'; + SET v1 = 1234; + SET v2 = 9876; + SET v3 = 5678; +END| + +--echo +CREATE PROCEDURE p_decimal( + IN v0 INT, + OUT v1 DECIMAL(4, 2), + IN v2 DECIMAL(4, 2), + INOUT v3 DECIMAL(4, 2)) +BEGIN + SET v0 = -1; + SET v1 = 12.34; + SET v2 = 98.67; + SET v3 = 56.78; +END| + +delimiter ;| + +--echo +PREPARE stmt_str FROM 'CALL p_string(?, ?, ?, ?)'; +PREPARE stmt_dbl FROM 'CALL p_double(?, ?, ?, ?)'; +PREPARE stmt_int FROM 'CALL p_int(?, ?, ?, ?)'; +PREPARE stmt_dec FROM 'CALL p_decimal(?, ?, ?, ?)'; + +--echo +SET @x_str_1 = NULL; +SET @x_str_2 = NULL; +SET @x_str_3 = NULL; +SET @x_dbl_1 = NULL; +SET @x_dbl_2 = NULL; +SET @x_dbl_3 = NULL; +SET @x_int_1 = NULL; +SET @x_int_2 = NULL; +SET @x_int_3 = NULL; +SET @x_dec_1 = NULL; +SET @x_dec_2 = NULL; +SET @x_dec_3 = NULL; + +--echo +--echo -- Testing strings... + +--echo +EXECUTE stmt_str USING @x_int_1, @x_str_1, @x_str_2, @x_str_3; +SELECT @x_int_1, @x_str_1, @x_str_2, @x_str_3; + +--echo +EXECUTE stmt_str USING @x_int_1, @x_str_1, @x_str_2, @x_str_3; +SELECT @x_int_1, @x_str_1, @x_str_2, @x_str_3; + +--echo +--echo -- Testing doubles... + +--echo +EXECUTE stmt_dbl USING @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3; +SELECT @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3; + +--echo +EXECUTE stmt_dbl USING @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3; +SELECT @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3; + +--echo +--echo -- Testing ints... + +--echo +EXECUTE stmt_int USING @x_str_1, @x_int_1, @x_int_2, @x_int_3; +SELECT @x_str_1, @x_int_1, @x_int_2, @x_int_3; + +--echo +EXECUTE stmt_int USING @x_str_1, @x_int_1, @x_int_2, @x_int_3; +SELECT @x_str_1, @x_int_1, @x_int_2, @x_int_3; + +--echo +--echo -- Testing decs... + +--echo +EXECUTE stmt_dec USING @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3; +SELECT @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3; + +--echo +EXECUTE stmt_dec USING @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3; +SELECT @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3; + +--echo +DEALLOCATE PREPARE stmt_str; +DEALLOCATE PREPARE stmt_dbl; +DEALLOCATE PREPARE stmt_int; +DEALLOCATE PREPARE stmt_dec; + +--echo +DROP PROCEDURE p_string; +DROP PROCEDURE p_double; +DROP PROCEDURE p_int; +DROP PROCEDURE p_decimal; + +# +# Another test case for WL#4435: check out parameters in Dynamic SQL. +# + +--echo +--disable_warnings +DROP PROCEDURE IF EXISTS p1; +DROP PROCEDURE IF EXISTS p2; +--enable_warnings + +--echo + +CREATE PROCEDURE p1(OUT v1 CHAR(10)) + SET v1 = 'test1'; + +--echo + +delimiter |; +CREATE PROCEDURE p2(OUT v2 CHAR(10)) +BEGIN + SET @query = 'CALL p1(?)'; + PREPARE stmt1 FROM @query; + EXECUTE stmt1 USING @u1; + DEALLOCATE PREPARE stmt1; + + SET v2 = @u1; +END| +delimiter ;| + +--echo + +CALL p2(@a); +SELECT @a; + +--echo + +DROP PROCEDURE p1; +DROP PROCEDURE p2; + +--echo +--echo # End of WL#4435. + +########################################################################### + +--echo +--echo End of 6.0 tests. + +########################################################################### === modified file 'sql/CMakeLists.txt' --- a/sql/CMakeLists.txt 2008-07-09 07:12:43 +0000 +++ b/sql/CMakeLists.txt 2008-08-08 01:33:43 +0000 @@ -75,7 +75,7 @@ ADD_EXECUTABLE(mysqld sql_tablespace.cc events.cc ../sql-common/my_user.c partition_info.cc rpl_utility.cc rpl_injector.cc sql_locale.cc rpl_rli.cc rpl_mi.cc sql_servers.cc sql_audit.cc - sql_connect.cc scheduler.cc + sql_connect.cc scheduler.cc transaction.cc ddl_blocker.cc si_objects.cc sql_profile.cc event_parse_data.cc mdl.cc ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc === modified file 'sql/Makefile.am' --- a/sql/Makefile.am 2008-07-09 07:12:43 +0000 +++ b/sql/Makefile.am 2008-08-11 12:40:09 +0000 @@ -87,9 +87,9 @@ noinst_HEADERS = item.h item_func.h item sql_plugin.h authors.h event_parse_data.h \ event_data_objects.h event_scheduler.h \ sql_partition.h partition_info.h partition_element.h \ - probes.h sql_audit.h \ + probes.h sql_audit.h transaction.h \ contributors.h sql_servers.h ddl_blocker.h \ - si_objects.h sql_plist.h mdl.h + si_objects.h sql_plist.h mdl.h records.h mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ item.cc item_sum.cc item_buff.cc item_func.cc \ @@ -136,7 +136,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler. sql_builtin.cc sql_tablespace.cc partition_info.cc \ sql_servers.cc sql_audit.cc sha2.cc \ ddl_blocker.cc si_objects.cc event_parse_data.cc \ - mdl.cc + mdl.cc transaction.cc if HAVE_DTRACE mysqld_SOURCES += probes.d === modified file 'sql/backup/backup_test.cc' --- a/sql/backup/backup_test.cc 2008-08-20 13:23:10 +0000 +++ b/sql/backup/backup_test.cc 2008-09-05 14:16:07 +0000 @@ -42,7 +42,7 @@ int execute_backup_test_command(THD *thd field_list.push_back(new Item_empty_string("name", 5)); field_list.push_back(new Item_empty_string("type", 4)); field_list.push_back(new Item_empty_string("serialization", 13)); - protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); + protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); //obs::ObjIterator *it= obs::get_databases(thd); List_iterator it(*db_list); === modified file 'sql/backup/be_snapshot.cc' --- a/sql/backup/be_snapshot.cc 2008-07-07 12:51:56 +0000 +++ b/sql/backup/be_snapshot.cc 2008-08-08 19:58:37 +0000 @@ -44,6 +44,7 @@ #include "backup_engine.h" #include "be_snapshot.h" #include "backup_aux.h" +#include "transaction.h" namespace snapshot_backup { @@ -72,8 +73,8 @@ result_t Backup::cleanup() locking_thd->lock_state= LOCK_DONE; // set lock done so destructor won't wait if (m_trans_start) { - ha_autocommit_or_rollback(locking_thd->m_thd, 0); - end_active_trans(locking_thd->m_thd); + trans_commit_stmt(locking_thd->m_thd); + trans_commit_implicit(locking_thd->m_thd); m_trans_start= FALSE; } if (tables_open) @@ -102,9 +103,10 @@ result_t Backup::lock() state. */ locking_thd->m_thd->lex->sql_command= SQLCOM_SELECT; - locking_thd->m_thd->lex->start_transaction_opt|= + locking_thd->m_thd->lex->start_transaction_opt= MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT; - int res= begin_trans(locking_thd->m_thd); + int res= trans_begin(locking_thd->m_thd, + locking_thd->m_thd->lex->start_transaction_opt); if (res) DBUG_RETURN(ERROR); m_trans_start= TRUE; === modified file 'sql/backup/kernel.cc' --- a/sql/backup/kernel.cc 2008-09-11 16:28:29 +0000 +++ b/sql/backup/kernel.cc 2008-09-16 17:58:49 +0000 @@ -84,6 +84,7 @@ #include "be_nodata.h" #include "ddl_blocker.h" #include "backup_progress.h" +#include "transaction.h" /** @@ -299,9 +300,7 @@ int send_reply(Backup_restore_ctx &conte // FIXME: detect errors if reported. // FIXME: error logging. field_list.push_back(new Item_empty_string(STRING_WITH_LEN("backup_id"))); - // FIXME: detect errors if reported. - // FIXME: error logging. - protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); + protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); /* Send field data. @@ -804,8 +803,8 @@ int Backup_restore_ctx::close() */ if (m_thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) { - ha_autocommit_or_rollback(m_thd, 0); - end_active_trans(m_thd); + trans_commit_stmt(m_thd); + trans_commit_implicit(m_thd); } // unlock tables if they are still locked === modified file 'sql/events.cc' --- a/sql/events.cc 2008-07-26 16:38:20 +0000 +++ b/sql/events.cc 2008-08-07 17:52:43 +0000 @@ -682,7 +682,7 @@ send_show_create_event(THD *thd, Event_t field_list.push_back( new Item_empty_string("Database Collation", MY_CS_NAME_SIZE)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); === modified file 'sql/field.cc' --- a/sql/field.cc 2008-07-25 17:21:55 +0000 +++ b/sql/field.cc 2008-08-10 14:49:52 +0000 @@ -1775,7 +1775,7 @@ bool Field::optimize_range(uint idx, uin } -Field *Field::new_field(MEM_ROOT *root, struct st_table *new_table, +Field *Field::new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type __attribute__((unused))) { Field *tmp; @@ -1796,7 +1796,7 @@ Field *Field::new_field(MEM_ROOT *root, } -Field *Field::new_key_field(MEM_ROOT *root, struct st_table *new_table, +Field *Field::new_key_field(MEM_ROOT *root, TABLE *new_table, uchar *new_ptr, uchar *new_null_ptr, uint new_null_bit) { @@ -1813,7 +1813,7 @@ Field *Field::new_key_field(MEM_ROOT *ro /* This is used to generate a field in TABLE from TABLE_SHARE */ -Field *Field::clone(MEM_ROOT *root, struct st_table *new_table) +Field *Field::clone(MEM_ROOT *root, TABLE *new_table) { Field *tmp; if ((tmp= (Field*) memdup_root(root,(char*) this,size_of()))) @@ -6778,7 +6778,7 @@ uint Field_string::get_key_image(uchar * } -Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table, +Field *Field_string::new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type) { Field *field; @@ -7334,7 +7334,7 @@ int Field_varstring::cmp_binary(const uc } -Field *Field_varstring::new_field(MEM_ROOT *root, struct st_table *new_table, +Field *Field_varstring::new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type) { Field_varstring *res= (Field_varstring*) Field::new_field(root, new_table, @@ -7346,7 +7346,7 @@ Field *Field_varstring::new_field(MEM_RO Field *Field_varstring::new_key_field(MEM_ROOT *root, - struct st_table *new_table, + TABLE *new_table, uchar *new_ptr, uchar *new_null_ptr, uint new_null_bit) { @@ -8468,7 +8468,7 @@ void Field_enum::sql_type(String &res) c } -Field *Field_enum::new_field(MEM_ROOT *root, struct st_table *new_table, +Field *Field_enum::new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type) { Field_enum *res= (Field_enum*) Field::new_field(root, new_table, keep_type); @@ -8751,7 +8751,7 @@ Field_bit::do_last_null_byte() const Field *Field_bit::new_key_field(MEM_ROOT *root, - struct st_table *new_table, + TABLE *new_table, uchar *new_ptr, uchar *new_null_ptr, uint new_null_bit) { === modified file 'sql/field.h' --- a/sql/field.h 2008-08-28 11:17:29 +0000 +++ b/sql/field.h 2008-09-04 18:30:34 +0000 @@ -59,8 +59,8 @@ public: Note that you can use table->in_use as replacement for current_thd member only inside of val_*() and store() members (e.g. you can't use it in cons) */ - struct st_table *table; // Pointer for table - struct st_table *orig_table; // Pointer to original table + TABLE *table; // Pointer for table + TABLE *orig_table; // Pointer to original table const char **table_name, *field_name; LEX_STRING comment; /* Bitmap of indexes that start with this field */ @@ -321,12 +321,12 @@ public: */ virtual bool can_be_compared_as_longlong() const { return FALSE; } virtual void free() {} - virtual Field *new_field(MEM_ROOT *root, struct st_table *new_table, + virtual Field *new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type); - virtual Field *new_key_field(MEM_ROOT *root, struct st_table *new_table, + virtual Field *new_key_field(MEM_ROOT *root, TABLE *new_table, uchar *new_ptr, uchar *new_null_ptr, uint new_null_bit); - Field *clone(MEM_ROOT *mem_root, struct st_table *new_table); + Field *clone(MEM_ROOT *mem_root, TABLE *new_table); inline void move_field(uchar *ptr_arg,uchar *null_ptr_arg,uchar null_bit_arg) { ptr=ptr_arg; null_ptr=null_ptr_arg; null_bit=null_bit_arg; @@ -1547,7 +1547,7 @@ public: enum_field_types real_type() const { return MYSQL_TYPE_STRING; } bool has_charset(void) const { return charset() == &my_charset_bin ? FALSE : TRUE; } - Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type); + Field *new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type); virtual uint get_key_image(uchar *buff,uint length, imagetype type); private: int do_save_field_metadata(uchar *first_byte); @@ -1635,8 +1635,8 @@ public: enum_field_types real_type() const { return MYSQL_TYPE_VARCHAR; } bool has_charset(void) const { return charset() == &my_charset_bin ? FALSE : TRUE; } - Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type); - Field *new_key_field(MEM_ROOT *root, struct st_table *new_table, + Field *new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type); + Field *new_key_field(MEM_ROOT *root, TABLE *new_table, uchar *new_ptr, uchar *new_null_ptr, uint new_null_bit); uint is_equal(Create_field *new_field); @@ -1867,7 +1867,7 @@ public: { flags|=ENUM_FLAG; } - Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type); + Field *new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type); enum_field_types type() const { return MYSQL_TYPE_STRING; } enum Item_result cmp_type () const { return INT_RESULT; } enum Item_result cast_to_int_type () const { return INT_RESULT; } @@ -2000,7 +2000,7 @@ public: uint param_data, bool low_byte_first); virtual void set_default(); - Field *new_key_field(MEM_ROOT *root, struct st_table *new_table, + Field *new_key_field(MEM_ROOT *root, TABLE *new_table, uchar *new_ptr, uchar *new_null_ptr, uint new_null_bit); void set_bit_ptr(uchar *bit_ptr_arg, uchar bit_ofs_arg) @@ -2123,7 +2123,7 @@ public: A class for sending info to the client */ -class Send_field { +class Send_field :public Sql_alloc { public: const char *db_name; const char *table_name,*org_table_name; === modified file 'sql/handler.cc' --- a/sql/handler.cc 2008-09-03 14:40:19 +0000 +++ b/sql/handler.cc 2008-09-04 18:30:34 +0000 @@ -27,6 +27,7 @@ #include "rpl_filter.h" #include #include "myisam.h" +#include "transaction.h" #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" @@ -816,16 +817,16 @@ void ha_close_connection(THD* thd) a transaction in a given engine is read-write and will not involve the two-phase commit protocol! - At the end of a statement, server call - ha_autocommit_or_rollback() is invoked. This call in turn - invokes handlerton::prepare() for every involved engine. - Prepare is followed by a call to handlerton::commit_one_phase() - If a one-phase commit will suffice, handlerton::prepare() is not - invoked and the server only calls handlerton::commit_one_phase(). - At statement commit, the statement-related read-write engine - flag is propagated to the corresponding flag in the normal - transaction. When the commit is complete, the list of registered - engines is cleared. + At the end of a statement, server call trans_commit_stmt is + invoked. This call in turn invokes handlerton::prepare() + for every involved engine. Prepare is followed by a call + to handlerton::commit_one_phase() If a one-phase commit + will suffice, handlerton::prepare() is not invoked and + the server only calls handlerton::commit_one_phase(). + At statement commit, the statement-related read-write + engine flag is propagated to the corresponding flag in the + normal transaction. When the commit is complete, the list + of registered engines is cleared. Rollback is handled in a similar fashion. @@ -836,7 +837,7 @@ void ha_close_connection(THD* thd) do not "register" in thd->transaction lists, and thus do not modify the transaction state. Besides, each DDL in MySQL is prefixed with an implicit normal transaction commit - (a call to end_active_trans()), and thus leaves nothing + (a call to trans_commit_implicit()), and thus leaves nothing to modify. However, as it has been pointed out with CREATE TABLE .. SELECT, some DDL statements can start a *new* transaction. @@ -1277,42 +1278,6 @@ int ha_rollback_trans(THD *thd, bool all DBUG_RETURN(error); } -/** - This is used to commit or rollback a single statement depending on - the value of error. - - @note - Note that if the autocommit is on, then the following call inside - InnoDB will commit or rollback the whole transaction (= the statement). The - autocommit mechanism built into InnoDB is based on counting locks, but if - the user has used LOCK TABLES then that mechanism does not know to do the - commit. -*/ -int ha_autocommit_or_rollback(THD *thd, int error) -{ - DBUG_ENTER("ha_autocommit_or_rollback"); - - if (thd->transaction.stmt.ha_list) - { - if (!error) - { - if (ha_commit_trans(thd, 0)) - error=1; - } - else - { - (void) ha_rollback_trans(thd, 0); - if (thd->transaction_rollback_request && !thd->in_sub_stmt) - (void) ha_rollback(thd); - } - - thd->variables.tx_isolation=thd->session_tx_isolation; - } - - DBUG_RETURN(error); -} - - struct xahton_st { XID *xid; int result; @@ -1584,7 +1549,7 @@ bool mysql_xa_recover(THD *thd) field_list.push_back(new Item_int("bqual_length", 0, MY_INT32_NUM_DECIMAL_DIGITS)); field_list.push_back(new Item_empty_string("data",XIDDATASIZE)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); @@ -3317,7 +3282,7 @@ int ha_enable_transaction(THD *thd, bool So, let's commit an open transaction (if any) now. */ if (!(error= ha_commit_trans(thd, 0))) - error= end_trans(thd, COMMIT); + error= trans_commit_implicit(thd); } DBUG_RETURN(error); } @@ -5053,7 +5018,7 @@ bool ha_show_status(THD *thd, handlerton field_list.push_back(new Item_empty_string("Name",FN_REFLEN)); field_list.push_back(new Item_empty_string("Status",10)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) return TRUE; === modified file 'sql/handler.h' --- a/sql/handler.h 2008-09-11 18:36:05 +0000 +++ b/sql/handler.h 2008-09-16 17:58:49 +0000 @@ -572,7 +572,8 @@ class st_alter_tablespace : public Sql_a /* The handler for a table type. Will be included in the TABLE structure */ -struct st_table; +struct TABLE; +struct TABLE_SHARE; /* Make sure that the order of schema_tables and enum_schema_tables are the same. @@ -615,8 +616,6 @@ enum enum_schema_tables SCH_VIEWS }; -typedef struct st_table TABLE; -typedef struct st_table_share TABLE_SHARE; struct st_foreign_key_info; typedef struct st_foreign_key_info FOREIGN_KEY_INFO; typedef bool (stat_print_fn)(THD *thd, const char *type, uint type_len, @@ -691,6 +690,7 @@ struct handler_iterator { void *buffer; }; +class handler; /* handlerton is a singleton structure - one instance per storage engine - to provide access to storage engine functionality that works on the @@ -1386,8 +1386,8 @@ class handler :public Sql_alloc public: typedef ulonglong Table_flags; protected: - struct st_table_share *table_share; /* The table definition */ - struct st_table *table; /* The current open table */ + TABLE_SHARE *table_share; /* The table definition */ + TABLE *table; /* The current open table */ Table_flags cached_table_flags; /* Set on init() and open() */ ha_rows estimation_rows_to_insert; @@ -2457,10 +2457,6 @@ extern TYPELIB tx_isolation_typelib; extern TYPELIB myisam_stats_method_typelib; extern ulong total_ha, total_ha_2pc; - /* Wrapper functions */ -#define ha_commit(thd) (ha_commit_trans((thd), TRUE)) -#define ha_rollback(thd) (ha_rollback_trans((thd), TRUE)) - /* lookups */ handlerton *ha_default_handlerton(THD *thd); plugin_ref ha_resolve_by_name(THD *thd, const LEX_STRING *name); @@ -2537,13 +2533,12 @@ int ha_release_temporary_latches(THD *th int ha_start_consistent_snapshot(THD *thd); int ha_commit_or_rollback_by_xid(XID *xid, bool commit); int ha_commit_one_phase(THD *thd, bool all); +int ha_commit_trans(THD *thd, bool all); int ha_rollback_trans(THD *thd, bool all); int ha_prepare(THD *thd); int ha_recover(HASH *commit_list); /* transactions: these functions never call handlerton functions directly */ -int ha_commit_trans(THD *thd, bool all); -int ha_autocommit_or_rollback(THD *thd, int error); int ha_enable_transaction(THD *thd, bool on); /* savepoints */ === modified file 'sql/item.cc' --- a/sql/item.cc 2008-08-20 18:05:57 +0000 +++ b/sql/item.cc 2008-09-04 18:30:34 +0000 @@ -2515,7 +2515,8 @@ Item_param::Item_param(uint pos_in_query param_type(MYSQL_TYPE_VARCHAR), pos_in_query(pos_in_query_arg), set_param_func(default_set_param_func), - limit_clause_param(FALSE) + limit_clause_param(FALSE), + m_out_param_info(NULL) { name= (char*) "?"; /* @@ -2596,6 +2597,17 @@ void Item_param::set_decimal(const char DBUG_VOID_RETURN; } +void Item_param::set_decimal(const my_decimal *dv) +{ + state= DECIMAL_VALUE; + + my_decimal2decimal(dv, &decimal_value); + + decimals= (uint8) decimal_value.frac; + unsigned_flag= !decimal_value.sign(); + max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, + decimals, unsigned_flag); +} /** Set parameter value from MYSQL_TIME value. @@ -3217,6 +3229,158 @@ Item_param::set_param_type_and_swap_valu str_value_ptr.swap(src->str_value_ptr); } + +/** + This operation is intended to store some item value in Item_param to be + used later. + + @param thd thread context + @param ctx stored procedure runtime context + @param it a pointer to an item in the tree + + @return Error status + @retval TRUE on error + @retval FALSE on success +*/ + +bool +Item_param::set_value(THD *thd, sp_rcontext *ctx, Item **it) +{ + Item *value= *it; + + if (value->is_null()) + { + set_null(); + return FALSE; + } + + null_value= FALSE; + + switch (value->result_type()) { + case STRING_RESULT: + { + char str_buffer[STRING_BUFFER_USUAL_SIZE]; + String sv_buffer(str_buffer, sizeof(str_buffer), &my_charset_bin); + String *sv= value->val_str(&sv_buffer); + + if (!sv) + return TRUE; + + set_str(sv->c_ptr_safe(), sv->length()); + str_value_ptr.set(str_value.ptr(), + str_value.length(), + str_value.charset()); + collation.set(str_value.charset(), DERIVATION_COERCIBLE); + decimals= 0; + param_type= MYSQL_TYPE_STRING; + + break; + } + + case REAL_RESULT: + set_double(value->val_real()); + param_type= MYSQL_TYPE_DOUBLE; + break; + + case INT_RESULT: + set_int(value->val_int(), value->max_length); + param_type= MYSQL_TYPE_LONG; + break; + + case DECIMAL_RESULT: + { + my_decimal dv_buf; + my_decimal *dv= value->val_decimal(&dv_buf); + + if (!dv) + return TRUE; + + set_decimal(dv); + param_type= MYSQL_TYPE_NEWDECIMAL; + + break; + } + + default: + /* That can not happen. */ + + DBUG_ASSERT(TRUE); // Abort in debug mode. + + set_null(); // Set to NULL in release mode. + return FALSE; + } + + item_result_type= value->result_type(); + item_type= value->type(); + return FALSE; +} + + +/** + Setter of Item_param::m_out_param_info. + + m_out_param_info is used to store information about store routine + OUT-parameters, such as stored routine name, database, stored routine + variable name. It is supposed to be set in sp_head::execute() after + Item_param::set_value() is called. +*/ + +void +Item_param::set_out_param_info(Send_field *info) +{ + m_out_param_info= info; +} + + +/** + Getter of Item_param::m_out_param_info. + + m_out_param_info is used to store information about store routine + OUT-parameters, such as stored routine name, database, stored routine + variable name. It is supposed to be retrieved in + Protocol_binary::send_out_parameters() during creation of OUT-parameter + result set. +*/ + +const Send_field * +Item_param::get_out_param_info() const +{ + return m_out_param_info; +} + + +/** + Fill meta-data information for the corresponding column in a result set. + If this is an OUT-parameter of a stored procedure, preserve meta-data of + stored-routine variable. + + @param field container for meta-data to be filled +*/ + +void Item_param::make_field(Send_field *field) +{ + Item::make_field(field); + + if (!m_out_param_info) + return; + + /* + This is an OUT-parameter of stored procedure. We should use + OUT-parameter info to fill out the names. + */ + + field->db_name= m_out_param_info->db_name; + field->table_name= m_out_param_info->table_name; + field->org_table_name= m_out_param_info->org_table_name; + field->col_name= m_out_param_info->col_name; + field->org_col_name= m_out_param_info->org_col_name; + field->length= m_out_param_info->length; + field->charsetnr= m_out_param_info->charsetnr; + field->flags= m_out_param_info->flags; + field->decimals= m_out_param_info->decimals; + field->type= m_out_param_info->type; +} + /**************************************************************************** Item_copy_string ****************************************************************************/ @@ -3250,7 +3414,7 @@ my_decimal *Item_copy_string::val_decima /* - Functions to convert item to field (for send_fields) + Functions to convert item to field (for send_result_set_metadata) */ /* ARGSUSED */ === modified file 'sql/item.h' --- a/sql/item.h 2008-08-20 10:29:58 +0000 +++ b/sql/item.h 2008-09-04 18:30:34 +0000 @@ -443,6 +443,11 @@ public: TRUE if error has occured. */ virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it)= 0; + + virtual void set_out_param_info(Send_field *info) {} + + virtual const Send_field *get_out_param_info() const + { return NULL; } }; @@ -1609,7 +1614,8 @@ public: /* Item represents one placeholder ('?') of prepared statement */ -class Item_param :public Item +class Item_param :public Item, + private Settable_routine_parameter { char cnvbuf[MAX_FIELD_WIDTH]; String cnvstr; @@ -1697,6 +1703,7 @@ public: void set_int(longlong i, uint32 max_length_arg); void set_double(double i); void set_decimal(const char *str, ulong length); + void set_decimal(const my_decimal *dv); bool set_str(const char *str, ulong length); bool set_longdata(const char *str, ulong length); void set_time(MYSQL_TIME *tm, timestamp_type type, uint32 max_length_arg); @@ -1746,6 +1753,25 @@ public: /** Item is a argument to a limit clause. */ bool limit_clause_param; void set_param_type_and_swap_value(Item_param *from); + +private: + virtual inline Settable_routine_parameter * + get_settable_routine_parameter() + { + return this; + } + + virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it); + + virtual void set_out_param_info(Send_field *info); + +public: + virtual const Send_field *get_out_param_info() const; + + virtual void make_field(Send_field *field); + +private: + Send_field *m_out_param_info; }; @@ -2098,7 +2124,7 @@ public: /** Item_empty_string -- is a utility class to put an item into List - which is then used in protocol.send_fields() when sending SHOW output to + which is then used in protocol.send_result_set_metadata() when sending SHOW output to the client. */ === modified file 'sql/lock.cc' --- a/sql/lock.cc 2008-07-09 07:12:43 +0000 +++ b/sql/lock.cc 2008-08-08 01:33:43 +0000 @@ -74,6 +74,7 @@ */ #include "mysql_priv.h" +#include "transaction.h" #include #include @@ -1440,7 +1441,7 @@ int try_transactional_lock(THD *thd, TAB err: /* We need to explicitly commit if autocommit mode is active. */ - (void) ha_autocommit_or_rollback(thd, 0); + trans_commit_stmt(thd); /* Close the tables. The locks (if taken) persist in the storage engines. */ close_tables_for_reopen(thd, &table_list, FALSE); thd->in_lock_tables= FALSE; === modified file 'sql/log_event.cc' --- a/sql/log_event.cc 2008-08-28 09:59:54 +0000 +++ b/sql/log_event.cc 2008-09-04 18:30:34 +0000 @@ -31,6 +31,7 @@ #include "rpl_filter.h" #include "rpl_utility.h" #include "rpl_record.h" +#include "transaction.h" #include #endif /* MYSQL_CLIENT */ @@ -5093,7 +5094,7 @@ int Xid_log_event::do_apply_event(Relay_ /* For a slave Xid_log_event is COMMIT */ general_log_print(thd, COM_QUERY, "COMMIT /* implicit, from Xid_log_event */"); - return end_trans(thd, COMMIT); + return trans_commit(thd); } Log_event::enum_skip_reason @@ -7417,7 +7418,7 @@ Rows_log_event::do_update_pos(Relay_log_ are involved, commit the transaction and flush the pending event to the binlog. */ - error= ha_autocommit_or_rollback(thd, 0); + error= trans_commit_stmt(thd); /* Now what if this is not a transactional engine? we still need to === modified file 'sql/log_event_old.cc' --- a/sql/log_event_old.cc 2008-06-28 11:00:59 +0000 +++ b/sql/log_event_old.cc 2008-08-08 01:33:43 +0000 @@ -6,6 +6,7 @@ #endif #include "log_event_old.h" #include "rpl_record_old.h" +#include "transaction.h" #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) @@ -1827,7 +1828,7 @@ Old_rows_log_event::do_update_pos(Relay_ are involved, commit the transaction and flush the pending event to the binlog. */ - error= ha_autocommit_or_rollback(thd, 0); + error= trans_commit_stmt(thd); /* Now what if this is not a transactional engine? we still need to === modified file 'sql/mysql_priv.h' --- a/sql/mysql_priv.h 2008-09-11 16:28:29 +0000 +++ b/sql/mysql_priv.h 2008-09-16 17:58:49 +0000 @@ -688,8 +688,6 @@ enum enum_parsing_place IN_ON }; -struct st_table; - class THD; enum enum_check_fields @@ -890,15 +888,6 @@ bool parse_sql(THD *thd, Parser_state *parser_state, Object_creation_ctx *creation_ctx); -enum enum_mysql_completiontype { - ROLLBACK_RELEASE=-2, ROLLBACK=1, ROLLBACK_AND_CHAIN=7, - COMMIT_RELEASE=-1, COMMIT=0, COMMIT_AND_CHAIN=6 -}; - -bool begin_trans(THD *thd); -bool end_active_trans(THD *thd); -int end_trans(THD *thd, enum enum_mysql_completiontype completion); - Item *negate_expression(THD *thd, Item *expr); /* log.cc */ @@ -928,6 +917,7 @@ bool general_log_write(THD *thd, enum en #include "tztime.h" #ifdef MYSQL_SERVER #include "sql_servers.h" +#include "records.h" #include "opt_range.h" #ifdef HAVE_QUERY_CACHE === modified file 'sql/procedure.h' --- a/sql/procedure.h 2008-03-21 15:48:28 +0000 +++ b/sql/procedure.h 2008-08-07 17:52:43 +0000 @@ -23,7 +23,7 @@ #define PROC_NO_SORT 1 /**< Bits in flags */ #define PROC_GROUP 2 /**< proc must have group */ -/* Procedure items used by procedures to store values for send_fields */ +/* Procedure items used by procedures to store values for send_result_set_metadata */ class Item_proc :public Item { === modified file 'sql/protocol.cc' --- a/sql/protocol.cc 2008-08-11 19:37:53 +0000 +++ b/sql/protocol.cc 2008-09-04 18:30:34 +0000 @@ -68,7 +68,7 @@ bool Protocol_binary::net_store_data(con exactly one byte to store length. It allows not to use the "convert" member as a temporary buffer, conversion is done directly to the "packet" member. - The limit 251 is good enough to optimize send_fields() + The limit 251 is good enough to optimize send_result_set_metadata() because column, table, database names fit into this limit. */ @@ -246,7 +246,7 @@ static uchar eof_buff[1]= { (uchar) 254 @param thd Thread handler @param no_flush Set to 1 if there will be more data to the client, - like in send_fields(). + like in send_result_set_metadata(). */ void @@ -562,16 +562,16 @@ bool Protocol::flush() 1 Error (Note that in this case the error is not sent to the client) */ -bool Protocol::send_fields(List *list, uint flags) +bool Protocol::send_result_set_metadata(List *list, uint flags) { List_iterator_fast it(*list); Item *item; - uchar buff[80]; + uchar buff[MAX_FIELD_WIDTH]; String tmp((char*) buff,sizeof(buff),&my_charset_bin); Protocol_text prot(thd); String *local_packet= prot.storage_packet(); CHARSET_INFO *thd_charset= thd->variables.character_set_results; - DBUG_ENTER("send_fields"); + DBUG_ENTER("send_result_set_metadata"); if (flags & SEND_NUM_ROWS) { // Packet with number of elements @@ -704,7 +704,7 @@ bool Protocol::send_fields(List *l */ write_eof_packet(thd, &thd->net, thd->server_status, thd->total_warn_count); } - DBUG_RETURN(prepare_for_send(list)); + DBUG_RETURN(prepare_for_send(list->elements)); err: my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), @@ -723,6 +723,38 @@ bool Protocol::write() /** + Send one result set row. + + @param row_items a collection of column values for that row + + @return Error status. + @retval TRUE Error. + @retval FALSE Success. +*/ + +bool Protocol::send_result_set_row(List *row_items) +{ + char buffer[MAX_FIELD_WIDTH]; + String str_buffer(buffer, sizeof (buffer), &my_charset_bin); + List_iterator_fast it(*row_items); + + DBUG_ENTER("Protocol::send_result_set_row"); + + for (Item *item= it++; item; item= it++) + { + if (item->send(this, &str_buffer) || thd->is_error()) + { + this->free(); // Free used buffer + my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); + DBUG_RETURN(TRUE); + } + } + + DBUG_RETURN(FALSE); +} + + +/** Send \\0 end terminated string. @param from NullS or \\0 terminated string @@ -1047,6 +1079,53 @@ bool Protocol_text::store_time(MYSQL_TIM return net_store_data((uchar*) buff, length); } +/** + Assign OUT-parameters to user variables. + + @param sp_params List of PS/SP parameters (both input and output). + + @return Error status. + @retval FALSE Success. + @retval TRUE Error. +*/ + +bool Protocol_text::send_out_parameters(List *sp_params) +{ + DBUG_ASSERT(sp_params->elements == + thd->lex->prepared_stmt_params.elements); + + List_iterator_fast item_param_it(*sp_params); + List_iterator_fast user_var_name_it(thd->lex->prepared_stmt_params); + + while (true) + { + Item_param *item_param= item_param_it++; + LEX_STRING *user_var_name= user_var_name_it++; + + if (!item_param || !user_var_name) + break; + + if (!item_param->get_out_param_info()) + continue; // It's an IN-parameter. + + Item_func_set_user_var *suv= + new Item_func_set_user_var(*user_var_name, item_param); + /* + Item_func_set_user_var is not fixed after construction, call + fix_fields(). + */ + if (suv->fix_fields(thd, NULL)) + return TRUE; + + if (suv->check(FALSE)) + return TRUE; + + if (suv->update()) + return TRUE; + } + + return FALSE; +} /**************************************************************************** Functions to handle the binary protocol used with prepared statements @@ -1067,14 +1146,13 @@ bool Protocol_text::store_time(MYSQL_TIM [..]..[[length]data] data ****************************************************************************/ -bool Protocol_binary::prepare_for_send(List *item_list) +bool Protocol_binary::prepare_for_send(uint num_columns) { - Protocol::prepare_for_send(item_list); + Protocol::prepare_for_send(num_columns); bit_fields= (field_count+9)/8; - if (packet->alloc(bit_fields+1)) - return 1; + return packet->alloc(bit_fields+1); + /* prepare_for_resend will be called after this one */ - return 0; } @@ -1262,3 +1340,80 @@ bool Protocol_binary::store_time(MYSQL_T buff[0]=(char) length; // Length is stored first return packet->append(buff, length+1, PACKET_BUFFER_EXTRA_ALLOC); } + +/** + Send a result set with OUT-parameter values by means of PS-protocol. + + @param sp_params List of PS/SP parameters (both input and output). + + @return Error status. + @retval FALSE Success. + @retval TRUE Error. +*/ + +bool Protocol_binary::send_out_parameters(List *sp_params) +{ + if (!(thd->client_capabilities & CLIENT_PS_MULTI_RESULTS)) + { + /* The client does not support OUT-parameters. */ + return FALSE; + } + + List out_param_lst; + + { + List_iterator_fast item_param_it(*sp_params); + + while (true) + { + Item_param *item_param= item_param_it++; + + if (!item_param) + break; + + if (!item_param->get_out_param_info()) + continue; // It's an IN-parameter. + + if (out_param_lst.push_back(item_param)) + return TRUE; + } + } + + if (!out_param_lst.elements) + return FALSE; + + /* + We have to set SERVER_PS_OUT_PARAMS in THD::server_status, because it + is used in send_result_set_metadata(). + */ + + thd->server_status|= SERVER_PS_OUT_PARAMS | SERVER_MORE_RESULTS_EXISTS; + + /* Send meta-data. */ + if (send_result_set_metadata(&out_param_lst, SEND_NUM_ROWS | SEND_EOF)) + return TRUE; + + /* Send data. */ + + prepare_for_resend(); + + if (send_result_set_row(&out_param_lst)) + return TRUE; + + if (write()) + return TRUE; + + /* Restore THD::server_status. */ + thd->server_status&= ~SERVER_PS_OUT_PARAMS; + + /* + Reset SERVER_MORE_RESULTS_EXISTS bit, because this is the last packet + for sure. + */ + thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS; + + /* Send EOF-packet. */ + net_send_eof(thd, thd->server_status, 0); + + return FALSE; +} === modified file 'sql/protocol.h' --- a/sql/protocol.h 2008-02-11 16:11:22 +0000 +++ b/sql/protocol.h 2008-08-07 17:52:43 +0000 @@ -20,6 +20,7 @@ class i_string; class THD; +class Item_param; typedef struct st_mysql_field MYSQL_FIELD; typedef struct st_mysql_rows MYSQL_ROWS; @@ -53,7 +54,8 @@ public: void init(THD* thd_arg); enum { SEND_NUM_ROWS= 1, SEND_DEFAULTS= 2, SEND_EOF= 4 }; - virtual bool send_fields(List *list, uint flags); + virtual bool send_result_set_metadata(List *list, uint flags); + bool send_result_set_row(List *row_items); bool store(I_List *str_list); bool store(const char *from, CHARSET_INFO *cs); @@ -71,9 +73,9 @@ public: inline bool store(String *str) { return store((char*) str->ptr(), str->length(), str->charset()); } - virtual bool prepare_for_send(List *item_list) + virtual bool prepare_for_send(uint num_columns) { - field_count=item_list->elements; + field_count= num_columns; return 0; } virtual bool flush(); @@ -95,6 +97,8 @@ public: virtual bool store_date(MYSQL_TIME *time)=0; virtual bool store_time(MYSQL_TIME *time)=0; virtual bool store(Field *field)=0; + + virtual bool send_out_parameters(List *sp_params)=0; #ifdef EMBEDDED_LIBRARY int begin_dataset(); virtual void remove_last_row() {} @@ -136,6 +140,8 @@ public: virtual bool store(float nr, uint32 decimals, String *buffer); virtual bool store(double from, uint32 decimals, String *buffer); virtual bool store(Field *field); + + virtual bool send_out_parameters(List *sp_params); #ifdef EMBEDDED_LIBRARY void remove_last_row(); #endif @@ -150,7 +156,7 @@ private: public: Protocol_binary() {} Protocol_binary(THD *thd_arg) :Protocol(thd_arg) {} - virtual bool prepare_for_send(List *item_list); + virtual bool prepare_for_send(uint num_columns); virtual void prepare_for_resend(); #ifdef EMBEDDED_LIBRARY virtual bool write(); @@ -171,6 +177,9 @@ public: virtual bool store(float nr, uint32 decimals, String *buffer); virtual bool store(double from, uint32 decimals, String *buffer); virtual bool store(Field *field); + + virtual bool send_out_parameters(List *sp_params); + virtual enum enum_protocol_type type() { return PROTOCOL_BINARY; }; }; === modified file 'sql/records.cc' --- a/sql/records.cc 2008-07-17 19:55:18 +0000 +++ b/sql/records.cc 2008-08-11 12:40:09 +0000 @@ -13,6 +13,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifdef USE_PRAGMA_INTERFACE +#pragma implementation /* gcc class implementation */ +#endif /** @file @@ -21,8 +24,10 @@ Functions for easy reading of records, possible through a cache */ +#include "records.h" #include "mysql_priv.h" + static int rr_quick(READ_RECORD *info); int rr_sequential(READ_RECORD *info); static int rr_from_tempfile(READ_RECORD *info); === added file 'sql/records.h' --- a/sql/records.h 1970-01-01 00:00:00 +0000 +++ b/sql/records.h 2008-09-12 09:09:27 +0000 @@ -0,0 +1,76 @@ +#ifndef SQL_RECORDS_H +#define SQL_RECORDS_H +/* Copyright (C) 2008 Sun/MySQL + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef USE_PRAGMA_INTERFACE +#pragma interface /* gcc class implementation */ +#endif +#include /* for uint typedefs */ + +struct st_join_table; +class handler; +struct TABLE; +class THD; +class SQL_SELECT; + +/** + A context for reading through a single table using a chosen access method: + index read, scan, etc, use of cache, etc. + + Use by: + READ_RECORD read_record; + init_read_record(&read_record, ...); + while (read_record.read_record()) + { + ... + } + end_read_record(); +*/ + +struct READ_RECORD +{ + typedef int (*Read_func)(READ_RECORD*); + typedef int (*Setup_func)(struct st_join_table*); + + TABLE *table; /* Head-form */ + handler *file; + TABLE **forms; /* head and ref forms */ + Read_func read_record; + THD *thd; + SQL_SELECT *select; + uint cache_records; + uint ref_length,struct_length,reclength,rec_cache_size,error_offset; + uint index; + uchar *ref_pos; /* pointer to form->refpos */ + uchar *record; + uchar *rec_buf; /* to read field values after filesort */ + uchar *cache,*cache_pos,*cache_end,*read_positions; + struct st_io_cache *io_cache; + bool print_error, ignore_not_found_rows; + struct st_join_table *do_insideout_scan; + +public: + READ_RECORD() {} +}; + +void init_read_record(READ_RECORD *info, THD *thd, TABLE *reg_form, + SQL_SELECT *select, int use_record_cache, + bool print_errors, bool disable_rr_cache); +void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, + bool print_error, uint idx); +void end_read_record(READ_RECORD *info); + +#endif /* SQL_RECORDS_H */ === modified file 'sql/repl_failsafe.cc' --- a/sql/repl_failsafe.cc 2008-03-27 19:02:15 +0000 +++ b/sql/repl_failsafe.cc 2008-08-07 17:52:43 +0000 @@ -470,7 +470,7 @@ bool show_new_master(THD* thd) field_list.push_back(new Item_empty_string("Log_name", 20)); field_list.push_back(new Item_return_int("Log_pos", 10, MYSQL_TYPE_LONGLONG)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); protocol->prepare_for_resend(); @@ -664,7 +664,7 @@ bool show_slave_hosts(THD* thd) field_list.push_back(new Item_return_int("Master_id", 10, MYSQL_TYPE_LONG)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); === modified file 'sql/rpl_injector.cc' --- a/sql/rpl_injector.cc 2008-05-29 15:44:11 +0000 +++ b/sql/rpl_injector.cc 2008-08-08 01:33:43 +0000 @@ -15,6 +15,7 @@ #include "mysql_priv.h" #include "rpl_injector.h" +#include "transaction.h" /* injector::transaction - member definitions @@ -35,8 +36,7 @@ injector::transaction::transaction(MYSQL m_start_pos.m_file_name= my_strdup(log_info.log_file_name, MYF(0)); m_start_pos.m_file_pos= log_info.pos; - m_thd->lex->start_transaction_opt= 0; /* for begin_trans() */ - begin_trans(m_thd); + trans_begin(m_thd); thd->set_current_stmt_binlog_row_based(); } @@ -82,8 +82,8 @@ int injector::transaction::commit() is committed by committing the statement transaction explicitly. */ - ha_autocommit_or_rollback(m_thd, 0); - end_trans(m_thd, COMMIT); + trans_commit_stmt(m_thd); + trans_commit(m_thd); DBUG_RETURN(0); } === modified file 'sql/rpl_injector.h' --- a/sql/rpl_injector.h 2008-05-29 15:44:11 +0000 +++ b/sql/rpl_injector.h 2008-08-10 14:49:52 +0000 @@ -25,9 +25,8 @@ /* Forward declarations */ class handler; class MYSQL_BIN_LOG; -struct st_table; +struct TABLE; -typedef st_table TABLE; /* Injector to inject rows into the MySQL server. === modified file 'sql/rpl_rli.cc' --- a/sql/rpl_rli.cc 2008-06-28 11:00:59 +0000 +++ b/sql/rpl_rli.cc 2008-08-08 01:33:43 +0000 @@ -20,6 +20,7 @@ #include // For MY_STAT #include "sql_repl.h" // For check_binlog_magic #include "rpl_utility.h" +#include "transaction.h" static int count_relay_log_space(Relay_log_info* rli); @@ -1162,8 +1163,8 @@ void Relay_log_info::cleanup_context(THD */ if (error) { - ha_autocommit_or_rollback(thd, 1); // if a "statement transaction" - end_trans(thd, ROLLBACK); // if a "real transaction" + trans_rollback_stmt(thd); // if a "statement transaction" + trans_rollback(thd); // if a "real transaction" } m_table_map.clear_tables(); slave_close_thread_tables(thd); === modified file 'sql/rpl_tblmap.cc' --- a/sql/rpl_tblmap.cc 2008-08-20 16:21:14 +0000 +++ b/sql/rpl_tblmap.cc 2008-09-04 18:30:34 +0000 @@ -53,7 +53,7 @@ table_mapping::~table_mapping() free_root(&m_mem_root, MYF(0)); } -TABLE* table_mapping::get_table(ulong table_id) +TABLE *table_mapping::get_table(ulong table_id) { DBUG_ENTER("table_mapping::get_table(ulong)"); DBUG_PRINT("enter", ("table_id: %lu", table_id)); === modified file 'sql/rpl_tblmap.h' --- a/sql/rpl_tblmap.h 2008-08-20 14:06:31 +0000 +++ b/sql/rpl_tblmap.h 2008-09-04 18:30:34 +0000 @@ -18,8 +18,7 @@ /* Forward declarations */ #ifndef MYSQL_CLIENT -struct st_table; -typedef st_table TABLE; +struct TABLE; #else class Table_map_log_event; typedef Table_map_log_event TABLE; === modified file 'sql/set_var.cc' --- a/sql/set_var.cc 2008-09-04 13:46:04 +0000 +++ b/sql/set_var.cc 2008-09-05 14:16:07 +0000 @@ -61,6 +61,7 @@ #include #include "events.h" +#include "transaction.h" /* WITH_NDBCLUSTER_STORAGE_ENGINE */ #ifdef WITH_NDBCLUSTER_STORAGE_ENGINE @@ -3181,7 +3182,7 @@ static bool set_option_autocommit(THD *t */ if (var->save_result.ulong_value != 0 && (thd->options & OPTION_NOT_AUTOCOMMIT) && - ha_commit(thd)) + trans_commit(thd)) return 1; if (var->save_result.ulong_value != 0) === modified file 'sql/slave.cc' --- a/sql/slave.cc 2008-07-21 03:55:09 +0000 +++ b/sql/slave.cc 2008-08-08 01:39:23 +0000 @@ -34,6 +34,7 @@ #include "sql_repl.h" #include "rpl_filter.h" #include "repl_failsafe.h" +#include "transaction.h" #include #include #include @@ -1240,7 +1241,7 @@ bool show_master_info(THD* thd, Master_i field_list.push_back(new Item_return_int("Last_SQL_Errno", 4, MYSQL_TYPE_LONG)); field_list.push_back(new Item_empty_string("Last_SQL_Error", 20)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); @@ -1953,7 +1954,7 @@ static int exec_relay_log_event(THD* thd else { exec_res= 0; - end_trans(thd, ROLLBACK); + trans_rollback(thd); /* chance for concurrent connection to get more locks */ safe_sleep(thd, min(rli->trans_retries, MAX_SLAVE_RETRY_PAUSE), (CHECK_KILLED_FUNC)sql_slave_killed, (void*)rli); === modified file 'sql/sp_head.cc' --- a/sql/sp_head.cc 2008-08-20 10:29:58 +0000 +++ b/sql/sp_head.cc 2008-09-04 18:30:34 +0000 @@ -2010,6 +2010,16 @@ sp_head::execute_procedure(THD *thd, Lis err_status= TRUE; break; } + + Send_field *out_param_info= new (thd->mem_root) Send_field(); + nctx->get_item(i)->make_field(out_param_info); + out_param_info->db_name= m_db.str; + out_param_info->table_name= m_name.str; + out_param_info->org_table_name= m_name.str; + out_param_info->col_name= spvar->name.str; + out_param_info->org_col_name= spvar->name.str; + + srp->set_out_param_info(out_param_info); } } @@ -2418,7 +2428,7 @@ sp_head::show_create_routine(THD *thd, i fields.push_back(new Item_empty_string("Database Collation", MY_CS_NAME_SIZE)); - if (protocol->send_fields(&fields, + if (protocol->send_result_set_metadata(&fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) { DBUG_RETURN(TRUE); @@ -2603,7 +2613,7 @@ sp_head::show_routine_code(THD *thd) // 1024 is for not to confuse old clients field_list.push_back(new Item_empty_string("Instruction", max(buffer.length(), 1024))); - if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); === modified file 'sql/sql_acl.cc' --- a/sql/sql_acl.cc 2008-08-07 03:05:33 +0000 +++ b/sql/sql_acl.cc 2008-08-12 16:46:23 +0000 @@ -4626,7 +4626,7 @@ bool mysql_show_grants(THD *thd,LEX_USER strxmov(buff,"Grants for ",lex_user->user.str,"@", lex_user->host.str,NullS); field_list.push_back(field); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) { pthread_mutex_unlock(&acl_cache->lock); === modified file 'sql/sql_base.cc' --- a/sql/sql_base.cc 2008-08-26 10:20:41 +0000 +++ b/sql/sql_base.cc 2008-09-04 18:30:34 +0000 @@ -21,6 +21,7 @@ #include "sp_head.h" #include "sp.h" #include "sql_trigger.h" +#include "transaction.h" #include #include #include @@ -1359,7 +1360,7 @@ void close_thread_tables(THD *thd, if (!(thd->state_flags & Open_tables_state::BACKUPS_AVAIL)) { thd->main_da.can_overwrite_status= TRUE; - ha_autocommit_or_rollback(thd, thd->is_error()); + thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); thd->main_da.can_overwrite_status= FALSE; /* @@ -2612,7 +2613,7 @@ bool open_table(THD *thd, TABLE_LIST *ta VIEW not really opened, only frm were read. Set 1 as a flag here */ - table_list->view= (st_lex*)1; + table_list->view= (LEX*)1; } else { === modified file 'sql/sql_cache.h' --- a/sql/sql_cache.h 2008-07-17 19:55:18 +0000 +++ b/sql/sql_cache.h 2008-08-10 14:49:52 +0000 @@ -65,7 +65,7 @@ struct Query_cache_query; struct Query_cache_result; class Query_cache; struct Query_cache_tls; -struct st_lex; +struct LEX; /** This class represents a node in the linked chain of queries @@ -413,7 +413,7 @@ protected: */ TABLE_COUNTER_TYPE is_cacheable(THD *thd, size_t query_len, const char *query, - struct st_lex *lex, TABLE_LIST *tables_used, + LEX *lex, TABLE_LIST *tables_used, uint8 *tables_type); TABLE_COUNTER_TYPE process_and_count_tables(THD *thd, TABLE_LIST *tables_used, === modified file 'sql/sql_class.cc' --- a/sql/sql_class.cc 2008-09-04 13:46:04 +0000 +++ b/sql/sql_class.cc 2008-09-05 14:16:07 +0000 @@ -42,6 +42,7 @@ #include "sp_rcontext.h" #include "sp_cache.h" +#include "transaction.h" /* The following is used to initialise Table_ident with a internal @@ -848,7 +849,8 @@ void THD::cleanup(void) } #endif { - ha_rollback(this); + transaction.xid_state.xa_state= XA_NOTR; + trans_rollback(this); xid_cache_delete(&transaction.xid_state); } locked_tables_list.unlock_locked_tables(this); @@ -1409,7 +1411,7 @@ int THD::send_explain_fields(select_resu } item->maybe_null= 1; field_list.push_back(new Item_empty_string("Extra", 255, cs)); - return (result->send_fields(field_list, + return (result->send_result_set_metadata(field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)); } @@ -1555,10 +1557,10 @@ sql_exchange::sql_exchange(char *name, b cs= NULL; } -bool select_send::send_fields(List &list, uint flags) +bool select_send::send_result_set_metadata(List &list, uint flags) { bool res; - if (!(res= thd->protocol->send_fields(&list, flags))) + if (!(res= thd->protocol->send_result_set_metadata(&list, flags))) is_result_set_started= 1; return res; } @@ -1601,10 +1603,13 @@ void select_send::cleanup() bool select_send::send_data(List &items) { + Protocol *protocol= thd->protocol; + DBUG_ENTER("select_send::send_data"); + if (unit->offset_limit_cnt) { // using limit offset,count unit->offset_limit_cnt--; - return 0; + DBUG_RETURN(FALSE); } /* @@ -1614,31 +1619,18 @@ bool select_send::send_data(List & */ ha_release_temporary_latches(thd); - List_iterator_fast li(items); - Protocol *protocol= thd->protocol; - char buff[MAX_FIELD_WIDTH]; - String buffer(buff, sizeof(buff), &my_charset_bin); - DBUG_ENTER("select_send::send_data"); - protocol->prepare_for_resend(); - Item *item; - while ((item=li++)) - { - if (item->send(protocol, &buffer) || thd->is_error()) - { - protocol->free(); // Free used buffer - my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); - break; - } - } - if (thd->is_error()) + if (protocol->send_result_set_row(&items)) { protocol->remove_last_row(); - DBUG_RETURN(1); + DBUG_RETURN(TRUE); } + thd->sent_row_count++; + if (thd->vio_ok()) DBUG_RETURN(protocol->write()); + DBUG_RETURN(0); } === modified file 'sql/sql_class.h' --- a/sql/sql_class.h 2008-09-04 13:46:04 +0000 +++ b/sql/sql_class.h 2008-09-05 14:16:07 +0000 @@ -2458,7 +2458,7 @@ public: */ virtual uint field_count(List &fields) const { return fields.elements; } - virtual bool send_fields(List &list, uint flags)=0; + virtual bool send_result_set_metadata(List &list, uint flags)=0; virtual bool send_data(List &items)=0; virtual bool initialize_tables (JOIN *join=0) { return 0; } virtual void send_error(uint errcode,const char *err); @@ -2497,7 +2497,7 @@ class select_result_interceptor: public public: select_result_interceptor() {} /* Remove gcc warning */ uint field_count(List &fields) const { return 0; } - bool send_fields(List &fields, uint flag) { return FALSE; } + bool send_result_set_metadata(List &fields, uint flag) { return FALSE; } }; @@ -2510,7 +2510,7 @@ class select_send :public select_result bool is_result_set_started; public: select_send() :is_result_set_started(FALSE) {} - bool send_fields(List &list, uint flags); + bool send_result_set_metadata(List &list, uint flags); bool send_data(List &items); bool send_eof(); virtual bool check_simple_select() const { return FALSE; } === modified file 'sql/sql_cursor.cc' --- a/sql/sql_cursor.cc 2008-08-20 10:29:58 +0000 +++ b/sql/sql_cursor.cc 2008-09-04 18:30:34 +0000 @@ -88,7 +88,7 @@ class Materialized_cursor: public Server public: Materialized_cursor(select_result *result, TABLE *table); - int fill_item_list(THD *thd, List &send_fields); + int fill_item_list(THD *thd, List &send_result_set_metadata); virtual bool is_open() const { return table != 0; } virtual int open(JOIN *join __attribute__((unused))); virtual void fetch(ulong num_rows); @@ -113,7 +113,7 @@ public: Materialized_cursor *materialized_cursor; Select_materialize(select_result *result_arg) :result(result_arg), materialized_cursor(0) {} - virtual bool send_fields(List &list, uint flags); + virtual bool send_result_set_metadata(List &list, uint flags); }; @@ -362,12 +362,12 @@ Sensitive_cursor::open(JOIN *join_arg) join->change_result(result); /* Send fields description to the client; server_status is sent - in 'EOF' packet, which follows send_fields(). - We don't simply use SEND_EOF flag of send_fields because we also + in 'EOF' packet, which follows send_result_set_metadata(). + We don't simply use SEND_EOF flag of send_result_set_metadata because we also want to flush the network buffer, which is done only in a standalone send_eof(). */ - result->send_fields(*join->fields, Protocol::SEND_NUM_ROWS); + result->send_result_set_metadata(*join->fields, Protocol::SEND_NUM_ROWS); thd->server_status|= SERVER_STATUS_CURSOR_EXISTS; result->send_eof(); thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS; @@ -551,14 +551,14 @@ Materialized_cursor::Materialized_cursor Preserve the original metadata that would be sent to the client. @param thd Thread identifier. - @param send_fields List of fields that would be sent. + @param send_result_set_metadata List of fields that would be sent. */ -int Materialized_cursor::fill_item_list(THD *thd, List &send_fields) +int Materialized_cursor::fill_item_list(THD *thd, List &send_result_set_metadata) { Query_arena backup_arena; int rc; - List_iterator_fast it_org(send_fields); + List_iterator_fast it_org(send_result_set_metadata); List_iterator_fast it_dst(item_list); Item *item_org; Item *item_dst; @@ -568,7 +568,7 @@ int Materialized_cursor::fill_item_list( if ((rc= table->fill_item_list(&item_list))) goto end; - DBUG_ASSERT(send_fields.elements == item_list.elements); + DBUG_ASSERT(send_result_set_metadata.elements == item_list.elements); /* Unless we preserve the original metadata, it will be lost, @@ -607,17 +607,17 @@ int Materialized_cursor::open(JOIN *join { /* Now send the result set metadata to the client. We need to - do it here, as in Select_materialize::send_fields the items - for column types are not yet created (send_fields requires + do it here, as in Select_materialize::send_result_set_metadata the items + for column types are not yet created (send_result_set_metadata requires a list of items). The new types may differ from the original ones sent at prepare if some of them were altered by MySQL HEAP tables mechanism -- used when create_tmp_field_from_item may alter the original column type. - We can't simply supply SEND_EOF flag to send_fields, because - send_fields doesn't flush the network buffer. + We can't simply supply SEND_EOF flag to send_result_set_metadata, because + send_result_set_metadata doesn't flush the network buffer. */ - rc= result->send_fields(item_list, Protocol::SEND_NUM_ROWS); + rc= result->send_result_set_metadata(item_list, Protocol::SEND_NUM_ROWS); thd->server_status|= SERVER_STATUS_CURSOR_EXISTS; result->send_eof(); thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS; @@ -700,7 +700,7 @@ Materialized_cursor::~Materialized_curso Select_materialize ****************************************************************************/ -bool Select_materialize::send_fields(List &list, uint flags) +bool Select_materialize::send_result_set_metadata(List &list, uint flags) { DBUG_ASSERT(table == 0); if (create_result_table(unit->thd, unit->get_unit_column_types(), === modified file 'sql/sql_do.cc' --- a/sql/sql_do.cc 2008-02-19 12:45:21 +0000 +++ b/sql/sql_do.cc 2008-08-08 01:33:43 +0000 @@ -17,6 +17,7 @@ /* Execute DO statement */ #include "mysql_priv.h" +#include "transaction.h" bool mysql_do(THD *thd, List &values) { @@ -36,7 +37,7 @@ bool mysql_do(THD *thd, List &valu will clear the error and the rollback in the end of dispatch_command() won't work. */ - ha_autocommit_or_rollback(thd, thd->is_error()); + trans_rollback_stmt(thd); thd->clear_error(); // DO always is OK } my_ok(thd); === modified file 'sql/sql_error.cc' --- a/sql/sql_error.cc 2008-07-29 22:03:57 +0000 +++ b/sql/sql_error.cc 2008-08-12 16:46:23 +0000 @@ -216,7 +216,7 @@ bool mysqld_show_warnings(THD *thd, ulon field_list.push_back(new Item_return_int("Code",4, MYSQL_TYPE_LONG)); field_list.push_back(new Item_empty_string("Message",MYSQL_ERRMSG_SIZE)); - if (thd->protocol->send_fields(&field_list, + if (thd->protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); === modified file 'sql/sql_handler.cc' --- a/sql/sql_handler.cc 2008-06-20 13:11:20 +0000 +++ b/sql/sql_handler.cc 2008-08-07 17:52:43 +0000 @@ -539,7 +539,7 @@ retry: tables->db, tables->alias, &it, 0)) goto err; - protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); + protocol->send_result_set_metadata(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); /* In ::external_lock InnoDB resets the fields which tell it that @@ -661,18 +661,11 @@ retry: continue; if (num_rows >= offset_limit_cnt) { - Item *item; protocol->prepare_for_resend(); - it.rewind(); - while ((item=it++)) - { - if (item->send(thd->protocol, &buffer)) - { - protocol->free(); // Free used - my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); - goto err; - } - } + + if (protocol->send_result_set_row(&list)) + goto err; + protocol->write(); } num_rows++; === modified file 'sql/sql_help.cc' --- a/sql/sql_help.cc 2008-07-17 18:26:55 +0000 +++ b/sql/sql_help.cc 2008-08-07 17:52:43 +0000 @@ -431,7 +431,7 @@ int send_answer_1(Protocol *protocol, St field_list.push_back(new Item_empty_string("description",1000)); field_list.push_back(new Item_empty_string("example",1000)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); @@ -463,7 +463,7 @@ int send_answer_1(Protocol *protocol, St +- -+ RETURN VALUES - result of protocol->send_fields + result of protocol->send_result_set_metadata */ int send_header_2(Protocol *protocol, bool for_category) @@ -474,7 +474,7 @@ int send_header_2(Protocol *protocol, bo field_list.push_back(new Item_empty_string("source_category_name",64)); field_list.push_back(new Item_empty_string("name",64)); field_list.push_back(new Item_empty_string("is_it_category",1)); - DBUG_RETURN(protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | + DBUG_RETURN(protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)); } === modified file 'sql/sql_insert.cc' --- a/sql/sql_insert.cc 2008-09-11 18:36:05 +0000 +++ b/sql/sql_insert.cc 2008-09-16 17:58:49 +0000 @@ -62,6 +62,7 @@ #include "slave.h" #include "rpl_mi.h" #include "sql_audit.h" +#include "transaction.h" #ifndef EMBEDDED_LIBRARY static bool delayed_get_table(THD *thd, TABLE_LIST *table_list); @@ -2504,7 +2505,7 @@ pthread_handler_t handle_delayed_insert( */ di->table->file->ha_release_auto_increment(); mysql_unlock_tables(thd, lock); - ha_autocommit_or_rollback(thd, 0); + trans_commit_stmt(thd); di->group_count=0; mysql_audit_release(thd); pthread_mutex_lock(&di->mutex); @@ -2525,7 +2526,7 @@ err: first call to ha_*_row() instead. Remove code that are used to cover for the case outlined above. */ - ha_autocommit_or_rollback(thd, 1); + trans_rollback_stmt(thd); #ifndef __WIN__ end: @@ -3765,8 +3766,8 @@ bool select_create::send_eof() */ if (!table->s->tmp_table) { - ha_autocommit_or_rollback(thd, 0); - end_active_trans(thd); + trans_commit_stmt(thd); + trans_commit_implicit(thd); } table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); === modified file 'sql/sql_lex.cc' --- a/sql/sql_lex.cc 2008-07-15 16:29:51 +0000 +++ b/sql/sql_lex.cc 2008-08-10 14:49:52 +0000 @@ -2110,7 +2110,7 @@ void st_select_lex::print_limit(THD *thd to implement the clean up. */ -void st_lex::cleanup_lex_after_parse_error(THD *thd) +void LEX::cleanup_lex_after_parse_error(THD *thd) { /* Delete sphead for the side effect of restoring of the original @@ -2200,7 +2200,7 @@ void Query_tables_list::destroy_query_ta Initialize LEX object. SYNOPSIS - st_lex::st_lex() + LEX::LEX() NOTE LEX object initialized with this constructor can be used as part of @@ -2210,7 +2210,7 @@ void Query_tables_list::destroy_query_ta for this. */ -st_lex::st_lex() +LEX::LEX() :result(0), sql_command(SQLCOM_END), option_type(OPT_DEFAULT), is_lex_started(0) { @@ -2227,7 +2227,7 @@ st_lex::st_lex() Check whether the merging algorithm can be used on this VIEW SYNOPSIS - st_lex::can_be_merged() + LEX::can_be_merged() DESCRIPTION We can apply merge algorithm if it is single SELECT view with @@ -2241,7 +2241,7 @@ st_lex::st_lex() TRUE - merge algorithm can be used */ -bool st_lex::can_be_merged() +bool LEX::can_be_merged() { // TODO: do not forget implement case when select_lex.table_list.elements==0 @@ -2278,19 +2278,19 @@ bool st_lex::can_be_merged() check if command can use VIEW with MERGE algorithm (for top VIEWs) SYNOPSIS - st_lex::can_use_merged() + LEX::can_use_merged() DESCRIPTION Only listed here commands can use merge algorithm in top level SELECT_LEX (for subqueries will be used merge algorithm if - st_lex::can_not_use_merged() is not TRUE). + LEX::can_not_use_merged() is not TRUE). RETURN FALSE - command can't use merged VIEWs TRUE - VIEWs with MERGE algorithms can be used */ -bool st_lex::can_use_merged() +bool LEX::can_use_merged() { switch (sql_command) { @@ -2315,18 +2315,18 @@ bool st_lex::can_use_merged() Check if command can't use merged views in any part of command SYNOPSIS - st_lex::can_not_use_merged() + LEX::can_not_use_merged() DESCRIPTION Temporary table algorithm will be used on all SELECT levels for queries - listed here (see also st_lex::can_use_merged()). + listed here (see also LEX::can_use_merged()). RETURN FALSE - command can't use merged VIEWs TRUE - VIEWs with MERGE algorithms can be used */ -bool st_lex::can_not_use_merged() +bool LEX::can_not_use_merged() { switch (sql_command) { @@ -2355,7 +2355,7 @@ bool st_lex::can_not_use_merged() FALSE no, we need data */ -bool st_lex::only_view_structure() +bool LEX::only_view_structure() { switch (sql_command) { case SQLCOM_SHOW_CREATE: @@ -2384,7 +2384,7 @@ bool st_lex::only_view_structure() */ -bool st_lex::need_correct_ident() +bool LEX::need_correct_ident() { switch(sql_command) { @@ -2414,7 +2414,7 @@ bool st_lex::need_correct_ident() VIEW_CHECK_CASCADED CHECK OPTION CASCADED */ -uint8 st_lex::get_effective_with_check(TABLE_LIST *view) +uint8 LEX::get_effective_with_check(TABLE_LIST *view) { if (view->select_lex->master_unit() == &unit && which_check_option_applicable()) @@ -2443,7 +2443,7 @@ uint8 st_lex::get_effective_with_check(T */ bool -st_lex::copy_db_to(char **p_db, size_t *p_db_length) const +LEX::copy_db_to(char **p_db, size_t *p_db_length) const { if (sphead) { @@ -2515,7 +2515,7 @@ void st_select_lex_unit::set_limit(st_se clause. */ -void st_lex::set_trg_event_type_for_tables() +void LEX::set_trg_event_type_for_tables() { uint8 new_trg_event_map= 0; @@ -2658,7 +2658,7 @@ void st_lex::set_trg_event_type_for_tabl In this case link_to_local is set. */ -TABLE_LIST *st_lex::unlink_first_table(bool *link_to_local) +TABLE_LIST *LEX::unlink_first_table(bool *link_to_local) { TABLE_LIST *first; if ((first= query_tables)) @@ -2698,7 +2698,7 @@ TABLE_LIST *st_lex::unlink_first_table(b table list SYNOPSYS - st_lex::first_lists_tables_same() + LEX::first_lists_tables_same() NOTES In many cases (for example, usual INSERT/DELETE/...) the first table of @@ -2709,7 +2709,7 @@ TABLE_LIST *st_lex::unlink_first_table(b the global list first. */ -void st_lex::first_lists_tables_same() +void LEX::first_lists_tables_same() { TABLE_LIST *first_table= (TABLE_LIST*) select_lex.table_list.first; if (query_tables != first_table && first_table != 0) @@ -2745,7 +2745,7 @@ void st_lex::first_lists_tables_same() global list */ -void st_lex::link_first_table_back(TABLE_LIST *first, +void LEX::link_first_table_back(TABLE_LIST *first, bool link_to_local) { if (first) @@ -2772,7 +2772,7 @@ void st_lex::link_first_table_back(TABLE cleanup lex for case when we open table by table for processing SYNOPSIS - st_lex::cleanup_after_one_table_open() + LEX::cleanup_after_one_table_open() NOTE This method is mostly responsible for cleaning up of selects lists and @@ -2780,7 +2780,7 @@ void st_lex::link_first_table_back(TABLE to call Query_tables_list::reset_query_tables_list(FALSE). */ -void st_lex::cleanup_after_one_table_open() +void LEX::cleanup_after_one_table_open() { /* thd->lex->derived_tables & additional units may be set if we open @@ -2815,7 +2815,7 @@ void st_lex::cleanup_after_one_table_ope backup Pointer to Query_tables_list instance to be used for backup */ -void st_lex::reset_n_backup_query_tables_list(Query_tables_list *backup) +void LEX::reset_n_backup_query_tables_list(Query_tables_list *backup) { backup->set_query_tables_list(this); /* @@ -2834,7 +2834,7 @@ void st_lex::reset_n_backup_query_tables backup Pointer to Query_tables_list instance used for backup */ -void st_lex::restore_backup_query_tables_list(Query_tables_list *backup) +void LEX::restore_backup_query_tables_list(Query_tables_list *backup) { this->destroy_query_tables_list(); this->set_query_tables_list(backup); @@ -2845,14 +2845,14 @@ void st_lex::restore_backup_query_tables Checks for usage of routines and/or tables in a parsed statement SYNOPSIS - st_lex:table_or_sp_used() + LEX:table_or_sp_used() RETURN FALSE No routines and tables used TRUE Either or both routines and tables are used. */ -bool st_lex::table_or_sp_used() +bool LEX::table_or_sp_used() { DBUG_ENTER("table_or_sp_used"); @@ -3013,7 +3013,7 @@ bool st_select_lex::add_index_hint (THD @retval FALSE No, not a management partition command */ -bool st_lex::is_partition_management() const +bool LEX::is_partition_management() const { return (sql_command == SQLCOM_ALTER_TABLE && (alter_info.flags == ALTER_ADD_PARTITION || === modified file 'sql/sql_lex.h' --- a/sql/sql_lex.h 2008-08-20 10:29:58 +0000 +++ b/sql/sql_lex.h 2008-09-04 18:30:34 +0000 @@ -390,7 +390,7 @@ public: Base class for st_select_lex (SELECT_LEX) & st_select_lex_unit (SELECT_LEX_UNIT) */ -struct st_lex; +struct LEX; class st_select_lex; class st_select_lex_unit; class st_select_lex_node { @@ -460,7 +460,7 @@ public: virtual void set_lock_for_tables(thr_lock_type lock_type) {} friend class st_select_lex_unit; - friend bool mysql_new_select(struct st_lex *lex, bool move_down); + friend bool mysql_new_select(LEX *lex, bool move_down); friend bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, uint flags); private: @@ -585,7 +585,7 @@ public: /* Saved values of the WHERE and HAVING clauses*/ Item::cond_result cond_value, having_value; /* point on lex in which it was created, used in view subquery detection */ - st_lex *parent_lex; + LEX *parent_lex; enum olap_type olap; /* FROM clause - points to the beginning of the TABLE_LIST::next_local list. */ SQL_LIST table_list; @@ -953,7 +953,7 @@ extern const LEX_STRING null_lex_str; stored functions/triggers to this list in order to pre-open and lock them. - Also used by st_lex::reset_n_backup/restore_backup_query_tables_list() + Also used by LEX::reset_n_backup/restore_backup_query_tables_list() methods to save and restore this information. */ @@ -1513,7 +1513,7 @@ public: /* The state of the lex parsing. This is saved in the THD struct */ -typedef struct st_lex : public Query_tables_list +struct LEX: public Query_tables_list { SELECT_LEX_UNIT unit; /* most upper unit */ SELECT_LEX select_lex; /* first SELECT_LEX */ @@ -1757,9 +1757,9 @@ typedef struct st_lex : public Query_tab bool escape_used; bool is_lex_started; /* If lex_start() did run. For debugging. */ - st_lex(); + LEX(); - virtual ~st_lex() + virtual ~LEX() { destroy_query_tables_list(); plugin_unlock_list(NULL, (plugin_ref *)plugins.buffer, plugins.elements); @@ -1801,7 +1801,7 @@ typedef struct st_lex : public Query_tab Is this update command where 'WHITH CHECK OPTION' clause is important SYNOPSIS - st_lex::which_check_option_applicable() + LEX::which_check_option_applicable() RETURN TRUE have to take 'WHITH CHECK OPTION' clause into account @@ -1873,7 +1873,7 @@ typedef struct st_lex : public Query_tab } return FALSE; } -} LEX; +}; /** @@ -1928,7 +1928,7 @@ public: }; -struct st_lex_local: public st_lex +struct st_lex_local: public LEX { static void *operator new(size_t size) throw() { === modified file 'sql/sql_parse.cc' --- a/sql/sql_parse.cc 2008-09-04 13:46:04 +0000 +++ b/sql/sql_parse.cc 2008-09-05 14:16:07 +0000 @@ -29,6 +29,7 @@ #include "sql_trigger.h" #include #include "sql_audit.h" +#include "transaction.h" #ifdef BACKUP_TEST #include "backup/backup_test.h" @@ -97,65 +98,6 @@ const char *xa_state_names[]={ extern DDL_blocker_class *DDL_blocker; -bool end_active_trans(THD *thd) -{ - int error=0; - DBUG_ENTER("end_active_trans"); - if (unlikely(thd->in_sub_stmt)) - { - my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0)); - DBUG_RETURN(1); - } - if (thd->transaction.xid_state.xa_state != XA_NOTR) - { - my_error(ER_XAER_RMFAIL, MYF(0), - xa_state_names[thd->transaction.xid_state.xa_state]); - DBUG_RETURN(1); - } - if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN | - OPTION_TABLE_LOCK)) - { - DBUG_PRINT("info",("options: 0x%llx", thd->options)); - /* Safety if one did "drop table" on locked tables */ - if (!thd->locked_tables_mode) - thd->options&= ~OPTION_TABLE_LOCK; - thd->server_status&= ~SERVER_STATUS_IN_TRANS; - if (ha_commit(thd)) - error=1; -#ifdef WITH_MARIA_STORAGE_ENGINE - ha_maria::implicit_commit(thd, TRUE); -#endif - } - thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); - thd->transaction.all.modified_non_trans_table= FALSE; - DBUG_RETURN(error); -} - - -bool begin_trans(THD *thd) -{ - int error=0; - if (unlikely(thd->in_sub_stmt)) - { - my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0)); - return 1; - } - - thd->locked_tables_list.unlock_locked_tables(thd); - - if (end_active_trans(thd)) - error= -1; - else - { - LEX *lex= thd->lex; - thd->options|= OPTION_BEGIN; - thd->server_status|= SERVER_STATUS_IN_TRANS; - if (lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT) - error= ha_start_consistent_snapshot(thd); - } - return error; -} - #ifdef HAVE_REPLICATION /** Returns true if all tables should be ignored. @@ -216,9 +158,9 @@ static bool opt_implicit_commit(THD *thd if (!skip) { /* Commit or rollback the statement transaction. */ - ha_autocommit_or_rollback(thd, thd->is_error()); + thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); /* Commit the normal transaction if one is active. */ - res= end_active_trans(thd); + res= trans_commit_implicit(thd); } DBUG_RETURN(res); @@ -643,81 +585,6 @@ void cleanup_items(Item *item) DBUG_VOID_RETURN; } -/** - Ends the current transaction and (maybe) begin the next. - - @param thd Current thread - @param completion Completion type - - @retval - 0 OK -*/ - -int end_trans(THD *thd, enum enum_mysql_completiontype completion) -{ - bool do_release= 0; - int res= 0; - DBUG_ENTER("end_trans"); - - if (unlikely(thd->in_sub_stmt)) - { - my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0)); - DBUG_RETURN(1); - } - if (thd->transaction.xid_state.xa_state != XA_NOTR) - { - my_error(ER_XAER_RMFAIL, MYF(0), - xa_state_names[thd->transaction.xid_state.xa_state]); - DBUG_RETURN(1); - } - thd->lex->start_transaction_opt= 0; /* for begin_trans() */ - switch (completion) { - case COMMIT: - /* - We don't use end_active_trans() here to ensure that this works - even if there is a problem with the OPTION_AUTO_COMMIT flag - (Which of course should never happen...) - */ - thd->server_status&= ~SERVER_STATUS_IN_TRANS; - res= ha_commit(thd); - thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); - thd->transaction.all.modified_non_trans_table= FALSE; - break; - case COMMIT_RELEASE: - do_release= 1; /* fall through */ - case COMMIT_AND_CHAIN: - res= end_active_trans(thd); - if (!res && completion == COMMIT_AND_CHAIN) - res= begin_trans(thd); - break; - case ROLLBACK_RELEASE: - do_release= 1; /* fall through */ - case ROLLBACK: - case ROLLBACK_AND_CHAIN: - { - thd->server_status&= ~SERVER_STATUS_IN_TRANS; - if (ha_rollback(thd)) - res= -1; - thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); - thd->transaction.all.modified_non_trans_table= FALSE; - if (!res && (completion == ROLLBACK_AND_CHAIN)) - res= begin_trans(thd); - break; - } - default: - res= -1; - my_error(ER_UNKNOWN_COM_ERROR, MYF(0)); - DBUG_RETURN(-1); - } - - if (res < 0) - my_error(thd->killed_errno(), MYF(0)); - else if ((res == 0) && do_release) - thd->killed= THD::KILL_CONNECTION; - - DBUG_RETURN(res); -} - #ifndef EMBEDDED_LIBRARY /** @@ -1340,14 +1207,14 @@ bool dispatch_command(enum enum_server_c bool not_used; status_var_increment(thd->status_var.com_stat[SQLCOM_FLUSH]); ulong options= (ulong) (uchar) packet[0]; - if (end_active_trans(thd)) + if (trans_commit_implicit(thd)) break; if (check_global_access(thd,RELOAD_ACL)) break; general_log_print(thd, command, NullS); if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, ¬_used)) break; - if (end_active_trans(thd)) + if (trans_commit_implicit(thd)) break; my_ok(thd); break; @@ -1497,7 +1364,7 @@ bool dispatch_command(enum enum_server_c /* If commit fails, we should be able to reset the OK status. */ thd->main_da.can_overwrite_status= TRUE; - ha_autocommit_or_rollback(thd, thd->is_error()); + thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); thd->main_da.can_overwrite_status= FALSE; thd->transaction.stmt.reset(); @@ -3432,7 +3299,7 @@ ddl_blocker_err: thd->locked_tables_list.unlock_locked_tables(thd); if (thd->options & OPTION_TABLE_LOCK) { - end_active_trans(thd); + trans_commit_implicit(thd); thd->options&= ~(OPTION_TABLE_LOCK); } if (thd->global_read_lock) @@ -3486,7 +3353,7 @@ ddl_blocker_err: goto error; thd->locked_tables_list.unlock_locked_tables(thd); /* we must end the trasaction first, regardless of anything */ - if (end_active_trans(thd)) + if (trans_commit_implicit(thd)) goto error; alloc_mdl_locks(all_tables, thd->locked_tables_list.locked_tables_root()); @@ -3509,8 +3376,8 @@ ddl_blocker_err: can free its locks if LOCK TABLES locked some tables before finding that it can't lock a table in its list */ - ha_autocommit_or_rollback(thd, 1); - end_active_trans(thd); + trans_rollback_stmt(thd); + trans_commit_implicit(thd); thd->options&= ~(OPTION_TABLE_LOCK); } else @@ -4003,129 +3870,52 @@ ddl_blocker_err: break; case SQLCOM_BEGIN: - if (thd->transaction.xid_state.xa_state != XA_NOTR) - { - my_error(ER_XAER_RMFAIL, MYF(0), - xa_state_names[thd->transaction.xid_state.xa_state]); - break; - } DEBUG_SYNC(thd, "before_begin_trans"); - if (begin_trans(thd)) + if (trans_begin(thd, lex->start_transaction_opt)) goto error; my_ok(thd); break; case SQLCOM_COMMIT: DBUG_ASSERT(thd->lock == NULL || thd->locked_tables_mode == LTM_LOCK_TABLES); - if (end_trans(thd, lex->tx_release ? COMMIT_RELEASE : - lex->tx_chain ? COMMIT_AND_CHAIN : COMMIT)) + if (trans_commit(thd)) + goto error; + /* Begin transaction with the same isolation level. */ + if (lex->tx_chain && trans_begin(thd)) goto error; + /* Disconnect the current client connection. */ + if (lex->tx_release) + thd->killed= THD::KILL_CONNECTION; DEBUG_SYNC(thd, "after_commit"); my_ok(thd); break; case SQLCOM_ROLLBACK: DBUG_ASSERT(thd->lock == NULL || thd->locked_tables_mode == LTM_LOCK_TABLES); - if (end_trans(thd, lex->tx_release ? ROLLBACK_RELEASE : - lex->tx_chain ? ROLLBACK_AND_CHAIN : ROLLBACK)) + if (trans_rollback(thd)) + goto error; + /* Begin transaction with the same isolation level. */ + if (lex->tx_chain && trans_begin(thd)) goto error; + /* Disconnect the current client connection. */ + if (lex->tx_release) + thd->killed= THD::KILL_CONNECTION; my_ok(thd); break; case SQLCOM_RELEASE_SAVEPOINT: - { - SAVEPOINT *sv; - for (sv=thd->transaction.savepoints; sv; sv=sv->prev) - { - if (my_strnncoll(system_charset_info, - (uchar *)lex->ident.str, lex->ident.length, - (uchar *)sv->name, sv->length) == 0) - break; - } - if (sv) - { - if (ha_release_savepoint(thd, sv)) - res= TRUE; // cannot happen - else - my_ok(thd); - thd->transaction.savepoints=sv->prev; - } - else - my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", lex->ident.str); + if (trans_release_savepoint(thd, lex->ident)) + goto error; + my_ok(thd); break; - } case SQLCOM_ROLLBACK_TO_SAVEPOINT: - { - SAVEPOINT *sv; - for (sv=thd->transaction.savepoints; sv; sv=sv->prev) - { - if (my_strnncoll(system_charset_info, - (uchar *)lex->ident.str, lex->ident.length, - (uchar *)sv->name, sv->length) == 0) - break; - } - if (sv) - { - if (ha_rollback_to_savepoint(thd, sv)) - res= TRUE; // cannot happen - else - { - if (((thd->options & OPTION_KEEP_LOG) || - thd->transaction.all.modified_non_trans_table) && - !thd->slave_thread) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARNING_NOT_COMPLETE_ROLLBACK, - ER(ER_WARNING_NOT_COMPLETE_ROLLBACK)); - my_ok(thd); - } - thd->transaction.savepoints=sv; - } - else - my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", lex->ident.str); + if (trans_rollback_to_savepoint(thd, lex->ident)) + goto error; + my_ok(thd); break; - } case SQLCOM_SAVEPOINT: - if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) || - thd->in_sub_stmt) || !opt_using_transactions) - my_ok(thd); - else - { - SAVEPOINT **sv, *newsv; - for (sv=&thd->transaction.savepoints; *sv; sv=&(*sv)->prev) - { - if (my_strnncoll(system_charset_info, - (uchar *)lex->ident.str, lex->ident.length, - (uchar *)(*sv)->name, (*sv)->length) == 0) - break; - } - if (*sv) /* old savepoint of the same name exists */ - { - newsv=*sv; - ha_release_savepoint(thd, *sv); // it cannot fail - *sv=(*sv)->prev; - } - else if ((newsv=(SAVEPOINT *) alloc_root(&thd->transaction.mem_root, - savepoint_alloc_size)) == 0) - { - my_error(ER_OUT_OF_RESOURCES, MYF(0)); - break; - } - newsv->name=strmake_root(&thd->transaction.mem_root, - lex->ident.str, lex->ident.length); - newsv->length=lex->ident.length; - /* - if we'll get an error here, don't add new savepoint to the list. - we'll lose a little bit of memory in transaction mem_root, but it'll - be free'd when transaction ends anyway - */ - if (ha_savepoint(thd, newsv)) - res= TRUE; - else - { - newsv->prev=thd->transaction.savepoints; - thd->transaction.savepoints=newsv; - my_ok(thd); - } - } + if (trans_savepoint(thd, lex->ident)) + goto error; + my_ok(thd); break; case SQLCOM_CREATE_PROCEDURE: case SQLCOM_CREATE_SPFUNCTION: @@ -4467,7 +4257,7 @@ create_sp_error: lex->sql_command == SQLCOM_DROP_PROCEDURE, 0)) goto error; - if (end_active_trans(thd)) + if (trans_commit_implicit(thd)) goto error; #ifndef NO_EMBEDDED_ACCESS_CHECKS if (sp_automatic_privileges && !opt_noacl && @@ -4597,173 +4387,29 @@ create_sp_error: break; } case SQLCOM_XA_START: - if (thd->transaction.xid_state.xa_state == XA_IDLE && - thd->lex->xa_opt == XA_RESUME) - { - if (! thd->transaction.xid_state.xid.eq(thd->lex->xid)) - { - my_error(ER_XAER_NOTA, MYF(0)); - break; - } - thd->transaction.xid_state.xa_state=XA_ACTIVE; - my_ok(thd); - break; - } - if (thd->lex->xa_opt != XA_NONE) - { // JOIN is not supported yet. TODO - my_error(ER_XAER_INVAL, MYF(0)); - break; - } - if (thd->transaction.xid_state.xa_state != XA_NOTR) - { - my_error(ER_XAER_RMFAIL, MYF(0), - xa_state_names[thd->transaction.xid_state.xa_state]); - break; - } - if (thd->locked_tables_mode || thd->active_transaction()) - { - my_error(ER_XAER_OUTSIDE, MYF(0)); - break; - } - if (xid_cache_search(thd->lex->xid)) - { - my_error(ER_XAER_DUPID, MYF(0)); - break; - } - DBUG_ASSERT(thd->transaction.xid_state.xid.is_null()); - thd->transaction.xid_state.xa_state=XA_ACTIVE; - thd->transaction.xid_state.xid.set(thd->lex->xid); - xid_cache_insert(&thd->transaction.xid_state); - thd->transaction.all.modified_non_trans_table= FALSE; - thd->options= ((thd->options & ~(OPTION_KEEP_LOG)) | OPTION_BEGIN); - thd->server_status|= SERVER_STATUS_IN_TRANS; + if (trans_xa_start(thd)) + goto error; my_ok(thd); break; case SQLCOM_XA_END: - /* fake it */ - if (thd->lex->xa_opt != XA_NONE) - { // SUSPEND and FOR MIGRATE are not supported yet. TODO - my_error(ER_XAER_INVAL, MYF(0)); - break; - } - if (thd->transaction.xid_state.xa_state != XA_ACTIVE) - { - my_error(ER_XAER_RMFAIL, MYF(0), - xa_state_names[thd->transaction.xid_state.xa_state]); - break; - } - if (!thd->transaction.xid_state.xid.eq(thd->lex->xid)) - { - my_error(ER_XAER_NOTA, MYF(0)); - break; - } - thd->transaction.xid_state.xa_state=XA_IDLE; + if (trans_xa_end(thd)) + goto error; my_ok(thd); break; case SQLCOM_XA_PREPARE: - if (thd->transaction.xid_state.xa_state != XA_IDLE) - { - my_error(ER_XAER_RMFAIL, MYF(0), - xa_state_names[thd->transaction.xid_state.xa_state]); - break; - } - if (!thd->transaction.xid_state.xid.eq(thd->lex->xid)) - { - my_error(ER_XAER_NOTA, MYF(0)); - break; - } - if (ha_prepare(thd)) - { - my_error(ER_XA_RBROLLBACK, MYF(0)); - xid_cache_delete(&thd->transaction.xid_state); - thd->transaction.xid_state.xa_state=XA_NOTR; - break; - } - thd->transaction.xid_state.xa_state=XA_PREPARED; + if (trans_xa_prepare(thd)) + goto error; my_ok(thd); break; case SQLCOM_XA_COMMIT: - if (!thd->transaction.xid_state.xid.eq(thd->lex->xid)) - { - XID_STATE *xs=xid_cache_search(thd->lex->xid); - if (!xs || xs->in_thd) - my_error(ER_XAER_NOTA, MYF(0)); - else - { - ha_commit_or_rollback_by_xid(thd->lex->xid, 1); - xid_cache_delete(xs); - my_ok(thd); - } - break; - } - if (thd->transaction.xid_state.xa_state == XA_IDLE && - thd->lex->xa_opt == XA_ONE_PHASE) - { - int r; - if ((r= ha_commit(thd))) - my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0)); - else - my_ok(thd); - } - else if (thd->transaction.xid_state.xa_state == XA_PREPARED && - thd->lex->xa_opt == XA_NONE) - { - if (wait_if_global_read_lock(thd, 0, 0)) - { - ha_rollback(thd); - my_error(ER_XAER_RMERR, MYF(0)); - } - else - { - if (ha_commit_one_phase(thd, 1)) - my_error(ER_XAER_RMERR, MYF(0)); - else - my_ok(thd); - start_waiting_global_read_lock(thd); - } - } - else - { - my_error(ER_XAER_RMFAIL, MYF(0), - xa_state_names[thd->transaction.xid_state.xa_state]); - break; - } - thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); - thd->transaction.all.modified_non_trans_table= FALSE; - thd->server_status&= ~SERVER_STATUS_IN_TRANS; - xid_cache_delete(&thd->transaction.xid_state); - thd->transaction.xid_state.xa_state=XA_NOTR; + if (trans_xa_commit(thd)) + goto error; + my_ok(thd); break; case SQLCOM_XA_ROLLBACK: - if (!thd->transaction.xid_state.xid.eq(thd->lex->xid)) - { - XID_STATE *xs=xid_cache_search(thd->lex->xid); - if (!xs || xs->in_thd) - my_error(ER_XAER_NOTA, MYF(0)); - else - { - ha_commit_or_rollback_by_xid(thd->lex->xid, 0); - xid_cache_delete(xs); - my_ok(thd); - } - break; - } - if (thd->transaction.xid_state.xa_state != XA_IDLE && - thd->transaction.xid_state.xa_state != XA_PREPARED) - { - my_error(ER_XAER_RMFAIL, MYF(0), - xa_state_names[thd->transaction.xid_state.xa_state]); - break; - } - if (ha_rollback(thd)) - my_error(ER_XAER_RMERR, MYF(0)); - else - my_ok(thd); - thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); - thd->transaction.all.modified_non_trans_table= FALSE; - thd->server_status&= ~SERVER_STATUS_IN_TRANS; - xid_cache_delete(&thd->transaction.xid_state); - thd->transaction.xid_state.xa_state=XA_NOTR; + if (trans_xa_rollback(thd)) + goto error; + my_ok(thd); break; case SQLCOM_XA_RECOVER: res= mysql_xa_recover(thd); === modified file 'sql/sql_partition.cc' --- a/sql/sql_partition.cc 2008-08-11 19:37:53 +0000 +++ b/sql/sql_partition.cc 2008-09-04 18:30:34 +0000 @@ -37,6 +37,7 @@ #include #include #include "my_md5.h" +#include "transaction.h" #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" @@ -3966,8 +3967,8 @@ static int fast_end_partition(THD *thd, if (!is_empty) query_cache_invalidate3(thd, table_list, 0); - error= ha_autocommit_or_rollback(thd, 0); - if (end_active_trans(thd)) + error= trans_commit_stmt(thd); + if (trans_commit_implicit(thd)) error= 1; if (error) === modified file 'sql/sql_prepare.cc' --- a/sql/sql_prepare.cc 2008-07-15 16:29:51 +0000 +++ b/sql/sql_prepare.cc 2008-08-07 17:52:43 +0000 @@ -105,7 +105,7 @@ class Select_fetch_protocol_binary: publ Protocol_binary protocol; public: Select_fetch_protocol_binary(THD *thd); - virtual bool send_fields(List &list, uint flags); + virtual bool send_result_set_metadata(List &list, uint flags); virtual bool send_data(List &items); virtual bool send_eof(); #ifdef EMBEDDED_LIBRARY @@ -258,7 +258,7 @@ static bool send_prep_stmt(Prepared_stat error= my_net_write(net, buff, sizeof(buff)); if (stmt->param_count && ! error) { - error= thd->protocol_text.send_fields((List *) + error= thd->protocol_text.send_result_set_metadata((List *) &stmt->lex->param_list, Protocol::SEND_EOF); } @@ -1373,7 +1373,7 @@ static int mysql_test_select(Prepared_st unit->prepare call above. */ if (send_prep_stmt(stmt, lex->result->field_count(fields)) || - lex->result->send_fields(fields, Protocol::SEND_EOF) || + lex->result->send_result_set_metadata(fields, Protocol::SEND_EOF) || thd->protocol->flush()) goto error; DBUG_RETURN(2); @@ -2774,19 +2774,19 @@ Select_fetch_protocol_binary::Select_fet :protocol(thd_arg) {} -bool Select_fetch_protocol_binary::send_fields(List &list, uint flags) +bool Select_fetch_protocol_binary::send_result_set_metadata(List &list, uint flags) { bool rc; Protocol *save_protocol= thd->protocol; /* - Protocol::send_fields caches the information about column types: + Protocol::send_result_set_metadata caches the information about column types: this information is later used to send data. Therefore, the same dedicated Protocol object must be used for all operations with a cursor. */ thd->protocol= &protocol; - rc= select_send::send_fields(list, flags); + rc= select_send::send_result_set_metadata(list, flags); thd->protocol= save_protocol; return rc; @@ -3597,6 +3597,9 @@ bool Prepared_statement::execute(String if (state == Query_arena::PREPARED) state= Query_arena::EXECUTED; + if (this->lex->sql_command == SQLCOM_CALL) + protocol->send_out_parameters(&this->lex->param_list); + /* Log COM_EXECUTE to the general log. Note, that in case of SQL prepared statements this causes two records to be output: === modified file 'sql/sql_profile.cc' --- a/sql/sql_profile.cc 2008-06-25 16:28:57 +0000 +++ b/sql/sql_profile.cc 2008-08-07 17:52:43 +0000 @@ -412,7 +412,7 @@ bool PROFILING::show_profiles() MYSQL_TYPE_DOUBLE)); field_list.push_back(new Item_empty_string("Query", 40)); - if (thd->protocol->send_fields(&field_list, + if (thd->protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); === modified file 'sql/sql_repl.cc' --- a/sql/sql_repl.cc 2008-08-22 06:48:55 +0000 +++ b/sql/sql_repl.cc 2008-09-04 18:30:34 +0000 @@ -1448,7 +1448,7 @@ bool mysql_show_binlog_events(THD* thd) DBUG_ENTER("mysql_show_binlog_events"); Log_event::init_show_field_list(&field_list); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); @@ -1598,7 +1598,7 @@ bool show_binlog_info(THD* thd) field_list.push_back(new Item_empty_string("Binlog_Do_DB",255)); field_list.push_back(new Item_empty_string("Binlog_Ignore_DB",255)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); protocol->prepare_for_resend(); @@ -1653,7 +1653,7 @@ bool show_binlogs(THD* thd) field_list.push_back(new Item_empty_string("Log_name", 255)); field_list.push_back(new Item_return_int("File_size", 20, MYSQL_TYPE_LONGLONG)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); === modified file 'sql/sql_select.cc' --- a/sql/sql_select.cc 2008-08-28 11:17:29 +0000 +++ b/sql/sql_select.cc 2008-09-04 18:30:34 +0000 @@ -2289,7 +2289,7 @@ JOIN::exec() (zero_result_cause?zero_result_cause:"No tables used")); else { - result->send_fields(*columns_list, + result->send_result_set_metadata(*columns_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); /* We have to test for 'conds' here as the WHERE may not be constant @@ -2681,7 +2681,7 @@ JOIN::exec() } if (curr_join->group_list || curr_join->order) { - DBUG_PRINT("info",("Sorting for send_fields")); + DBUG_PRINT("info",("Sorting for send_result_set_metadata")); thd_proc_info(thd, "Sorting result"); /* If we have already done the group, add HAVING to sorted table */ if (curr_join->tmp_having && ! curr_join->group_list && @@ -2821,7 +2821,7 @@ JOIN::exec() { thd_proc_info(thd, "Sending data"); DBUG_PRINT("info", ("%s", thd->proc_info)); - result->send_fields((procedure ? curr_join->procedure_fields_list : + result->send_result_set_metadata((procedure ? curr_join->procedure_fields_list : *curr_fields_list), Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); error= do_select(curr_join, curr_fields_list, NULL, procedure); @@ -9021,7 +9021,7 @@ return_zero_rows(JOIN *join, select_resu if (having && having->val_int() == 0) send_row=0; } - if (!(result->send_fields(fields, + if (!(result->send_result_set_metadata(fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))) { if (send_row) @@ -11641,7 +11641,7 @@ void setup_tmp_table_column_bitmaps(TABL Create a temp table according to a field list. Given field pointers are changed to point at tmp_table for - send_fields. The table object is self contained: it's + send_result_set_metadata. The table object is self contained: it's allocated in its own memory root, as well as Field objects created for table columns. This function will replace Item_sum items in 'fields' list with @@ -17832,7 +17832,7 @@ int test_if_item_cache_changed(List &field_list, List &send_fields, +bool JOIN::make_sum_func_list(List &field_list, List &send_result_set_metadata, bool before_group_by, bool recompute) { List_iterator_fast it(field_list); @@ -18095,7 +18095,7 @@ bool JOIN::make_sum_func_list(List if (before_group_by && rollup.state == ROLLUP::STATE_INITED) { rollup.state= ROLLUP::STATE_READY; - if (rollup_make_fields(field_list, send_fields, &func)) + if (rollup_make_fields(field_list, send_result_set_metadata, &func)) DBUG_RETURN(TRUE); // Should never happen } else if (rollup.state == ROLLUP::STATE_NONE) === modified file 'sql/sql_select.h' --- a/sql/sql_select.h 2008-08-16 15:22:47 +0000 +++ b/sql/sql_select.h 2008-09-04 18:30:34 +0000 @@ -174,7 +174,6 @@ class SJ_TMP_TABLE; typedef enum_nested_loop_state (*Next_select_func)(JOIN *, struct st_join_table *, bool); -typedef int (*Read_record_func)(struct st_join_table *tab); Next_select_func setup_end_select_func(JOIN *join); @@ -210,7 +209,7 @@ typedef struct st_join_table { */ uint packed_info; - Read_record_func read_first_record; + READ_RECORD::Setup_func read_first_record; Next_select_func next_select; READ_RECORD read_record; /* @@ -218,8 +217,8 @@ typedef struct st_join_table { if it is executed by an alternative full table scan when the left operand of the subquery predicate is evaluated to NULL. */ - Read_record_func save_read_first_record;/* to save read_first_record */ - int (*save_read_record) (READ_RECORD *);/* to save read_record.read_record */ + READ_RECORD::Setup_func save_read_first_record;/* to save read_first_record */ + READ_RECORD::Read_func save_read_record;/* to save read_record.read_record */ double worst_seeks; key_map const_keys; /**< Keys with constant part */ key_map checked_keys; /**< Keys checked in find_best */ @@ -648,7 +647,7 @@ public: bool alloc_func_list(); bool flatten_subqueries(); bool setup_subquery_materialization(); - bool make_sum_func_list(List &all_fields, List &send_fields, + bool make_sum_func_list(List &all_fields, List &send_result_set_metadata, bool before_group_by, bool recompute= FALSE); inline void set_items_ref_array(Item **ptr) === modified file 'sql/sql_show.cc' --- a/sql/sql_show.cc 2008-09-11 18:36:05 +0000 +++ b/sql/sql_show.cc 2008-09-16 17:58:49 +0000 @@ -216,7 +216,7 @@ bool mysqld_show_authors(THD *thd) field_list.push_back(new Item_empty_string("Location",40)); field_list.push_back(new Item_empty_string("Comment",80)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); @@ -250,7 +250,7 @@ bool mysqld_show_contributors(THD *thd) field_list.push_back(new Item_empty_string("Location",40)); field_list.push_back(new Item_empty_string("Comment",80)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); @@ -326,7 +326,7 @@ bool mysqld_show_privileges(THD *thd) field_list.push_back(new Item_empty_string("Context",15)); field_list.push_back(new Item_empty_string("Comment",NAME_CHAR_LEN)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); @@ -563,7 +563,7 @@ mysqld_show_create(THD *thd, TABLE_LIST max(buffer.length(),1024))); } - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); protocol->prepare_for_resend(); @@ -641,7 +641,7 @@ bool mysqld_show_create_db(THD *thd, cha field_list.push_back(new Item_empty_string("Database",NAME_CHAR_LEN)); field_list.push_back(new Item_empty_string("Create Database",1024)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); @@ -691,7 +691,7 @@ mysqld_list_fields(THD *thd, TABLE_LIST } restore_record(table, s->default_values); // Get empty record table->use_all_columns(); - if (thd->protocol->send_fields(&field_list, Protocol::SEND_DEFAULTS)) + if (thd->protocol->send_result_set_metadata(&field_list, Protocol::SEND_DEFAULTS)) DBUG_VOID_RETURN; my_eof(thd); DBUG_VOID_RETURN; @@ -1657,7 +1657,7 @@ void mysqld_list_processes(THD *thd,cons field->maybe_null=1; field_list.push_back(field=new Item_empty_string("Info",max_query_length)); field->maybe_null=1; - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_VOID_RETURN; @@ -3145,7 +3145,7 @@ static int fill_schema_table_from_frm(TH OPEN_VIEW_NO_PARSE, thd->open_options, &tbl, &table_list, thd->mem_root)) goto err_share; - table_list.view= (st_lex*) share->is_view; + table_list.view= (LEX*) share->is_view; res= schema_table->process_table(thd, &table_list, table, res, db_name, table_name); goto err_share; @@ -3154,7 +3154,7 @@ static int fill_schema_table_from_frm(TH { tbl.s= share; table_list.table= &tbl; - table_list.view= (st_lex*) share->is_view; + table_list.view= (LEX*) share->is_view; res= schema_table->process_table(thd, &table_list, table, res, db_name, table_name); } @@ -7215,7 +7215,7 @@ static bool show_create_trigger_impl(THD fields.push_back(new Item_empty_string("Database Collation", MY_CS_NAME_SIZE)); - if (p->send_fields(&fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) + if (p->send_result_set_metadata(&fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) return TRUE; /* Send data. */ === modified file 'sql/sql_table.cc' --- a/sql/sql_table.cc 2008-09-04 08:47:10 +0000 +++ b/sql/sql_table.cc 2008-09-04 20:00:04 +0000 @@ -22,6 +22,7 @@ #include "sp_head.h" #include "sql_trigger.h" #include "sql_show.h" +#include "transaction.h" #ifdef __WIN__ #include @@ -4165,7 +4166,7 @@ static bool mysql_admin_table(THD* thd, item->maybe_null = 1; field_list.push_back(item = new Item_empty_string("Msg_text", 255, cs)); item->maybe_null = 1; - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); @@ -4257,8 +4258,8 @@ static bool mysql_admin_table(THD* thd, DBUG_PRINT("admin", ("calling prepare_func")); switch ((*prepare_func)(thd, table, check_opt)) { case 1: // error, message written to net - ha_autocommit_or_rollback(thd, 1); - end_trans(thd, ROLLBACK); + trans_rollback_stmt(thd); + trans_rollback(thd); close_thread_tables(thd); DBUG_PRINT("admin", ("simple error, admin next table")); continue; @@ -4316,8 +4317,8 @@ static bool mysql_admin_table(THD* thd, length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY), table_name); protocol->store(buff, length, system_charset_info); - ha_autocommit_or_rollback(thd, 0); - end_trans(thd, COMMIT); + trans_commit_stmt(thd); + trans_commit(thd); close_thread_tables(thd); lex->reset_query_tables_list(FALSE); table->table=0; // For query cache @@ -4366,7 +4367,7 @@ static bool mysql_admin_table(THD* thd, HA_ADMIN_NEEDS_ALTER)) { DBUG_PRINT("admin", ("recreating table")); - ha_autocommit_or_rollback(thd, 1); + trans_rollback_stmt(thd); close_thread_tables(thd); tmp_disable_binlog(thd); // binlogging is done by caller if wanted result_code= mysql_recreate_table(thd, table); @@ -4480,13 +4481,13 @@ send_result_message: reopen the table and do ha_innobase::analyze() on it. We have to end the row, so analyze could return more rows. */ + trans_commit_stmt(thd); protocol->store(STRING_WITH_LEN("note"), system_charset_info); protocol->store(STRING_WITH_LEN( "Table does not support optimize, doing recreate + analyze instead"), system_charset_info); if (protocol->write()) goto err; - ha_autocommit_or_rollback(thd, 0); close_thread_tables(thd); DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze...")); TABLE_LIST *save_next_local= table->next_local, @@ -4503,7 +4504,7 @@ send_result_message: */ if (thd->main_da.is_ok()) thd->main_da.reset_diagnostics_area(); - ha_autocommit_or_rollback(thd, 0); + trans_commit_stmt(thd); close_thread_tables(thd); if (!result_code) // recreation went ok { @@ -4597,8 +4598,8 @@ send_result_message: query_cache_invalidate3(thd, table->table, 0); } } - ha_autocommit_or_rollback(thd, 0); - end_trans(thd, COMMIT); + trans_commit_stmt(thd); + trans_commit_implicit(thd); close_thread_tables(thd); table->table=0; // For query cache if (protocol->write()) @@ -4609,8 +4610,8 @@ send_result_message: DBUG_RETURN(FALSE); err: - ha_autocommit_or_rollback(thd, 1); - end_trans(thd, ROLLBACK); + trans_rollback_stmt(thd); + trans_rollback(thd); close_thread_tables(thd); // Shouldn't be needed if (table) table->table=0; @@ -5102,15 +5103,15 @@ mysql_discard_or_import_tablespace(THD * query_cache_invalidate3(thd, table_list, 0); /* The ALTER TABLE is always in its own transaction */ - error = ha_autocommit_or_rollback(thd, 0); - if (end_active_trans(thd)) + error= trans_commit_stmt(thd); + if (trans_commit_implicit(thd)) error=1; if (error) goto err; write_bin_log(thd, FALSE, thd->query, thd->query_length); err: - ha_autocommit_or_rollback(thd, error); + trans_rollback_stmt(thd); thd->tablespace_op=FALSE; if (error == 0) @@ -5867,8 +5868,8 @@ mysql_fast_or_online_alter_table(THD *th wait_if_global_read_lock(), which could create a deadlock if called with LOCK_open. */ - error= ha_autocommit_or_rollback(thd, 0); - if (ha_commit(thd)) + error= trans_commit_stmt(thd); + if (trans_commit_implicit(thd)) error= 1; if (error) @@ -7001,8 +7002,8 @@ view_err: } else { - error= ha_autocommit_or_rollback(thd, 0); - if (end_active_trans(thd)) + error= trans_commit_stmt(thd); + if (trans_commit_implicit(thd)) error= 1; } thd->count_cuted_fields= CHECK_FIELD_IGNORE; @@ -7446,9 +7447,9 @@ err: Ensure that the new table is saved properly to disk so that we can do a rename */ - if (ha_autocommit_or_rollback(thd, 0)) + if (trans_commit_stmt(thd)) error=1; - if (end_active_trans(thd)) + if (trans_commit_implicit(thd)) error=1; thd->variables.sql_mode= save_sql_mode; @@ -7513,7 +7514,7 @@ bool mysql_checksum_table(THD *thd, TABL field_list.push_back(item= new Item_int("Checksum", (longlong) 1, MY_INT64_NUM_DECIMAL_DIGITS)); item->maybe_null= 1; - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); === modified file 'sql/sql_yacc.yy' --- a/sql/sql_yacc.yy 2008-08-22 11:16:08 +0000 +++ b/sql/sql_yacc.yy 2008-09-04 18:30:34 +0000 @@ -575,7 +575,7 @@ bool setup_select_in_parentheses(LEX *le struct sp_cond_type *spcondtype; struct { int vars, conds, hndlrs, curs; } spblock; sp_name *spname; - struct st_lex *lex; + LEX *lex; sp_head *sphead; struct p_elem_val *p_elem_value; enum index_hint_type index_hint; === modified file 'sql/structs.h' --- a/sql/structs.h 2008-07-10 16:02:38 +0000 +++ b/sql/structs.h 2008-09-04 18:30:34 +0000 @@ -16,8 +16,9 @@ /* The old structures from unireg */ -struct st_table; +struct TABLE; class Field; +class THD; typedef struct st_date_time_format { uchar positions[8]; @@ -100,7 +101,7 @@ typedef struct st_key { union { int bdb_return_if_eq; } handler; - struct st_table *table; + TABLE *table; LEX_STRING comment; } KEY; @@ -115,30 +116,6 @@ typedef struct st_reginfo { /* Extra in } REGINFO; -struct st_read_record; /* For referense later */ -class SQL_SELECT; -class THD; -class handler; -struct st_join_table; - -typedef struct st_read_record { /* Parameter to read_record */ - struct st_table *table; /* Head-form */ - handler *file; - struct st_table **forms; /* head and ref forms */ - int (*read_record)(struct st_read_record *); - THD *thd; - SQL_SELECT *select; - uint cache_records; - uint ref_length,struct_length,reclength,rec_cache_size,error_offset; - uint index; - uchar *ref_pos; /* pointer to form->refpos */ - uchar *record; - uchar *rec_buf; /* to read field values after filesort */ - uchar *cache,*cache_pos,*cache_end,*read_positions; - IO_CACHE *io_cache; - bool print_error, ignore_not_found_rows; -} READ_RECORD; - /* Originally MySQL used MYSQL_TIME structure inside server only, but since === modified file 'sql/table.cc' --- a/sql/table.cc 2008-08-07 03:05:33 +0000 +++ b/sql/table.cc 2008-08-12 16:46:23 +0000 @@ -3019,7 +3019,7 @@ table_check_intact(TABLE *table, const u Create Item_field for each column in the table. SYNPOSIS - st_table::fill_item_list() + TABLE::fill_item_list() item_list a pointer to an empty list used to store items DESCRIPTION @@ -3032,7 +3032,7 @@ table_check_intact(TABLE *table, const u 1 out of memory */ -bool st_table::fill_item_list(List *item_list) const +bool TABLE::fill_item_list(List *item_list) const { /* All Item_field's created using a direct pointer to a field @@ -3052,7 +3052,7 @@ bool st_table::fill_item_list(List Fields of this table. SYNPOSIS - st_table::fill_item_list() + TABLE::fill_item_list() item_list a non-empty list with Item_fields DESCRIPTION @@ -3062,7 +3062,7 @@ bool st_table::fill_item_list(List is the same as the number of columns in the table. */ -void st_table::reset_item_list(List *item_list) const +void TABLE::reset_item_list(List *item_list) const { List_iterator_fast it(*item_list); for (Field **ptr= field; *ptr; ptr++) @@ -3988,7 +3988,7 @@ const char *Natural_join_column::db_name return table_ref->view_db.str; /* - Test that TABLE_LIST::db is the same as st_table_share::db to + Test that TABLE_LIST::db is the same as TABLE_SHARE::db to ensure consistency. An exception are I_S schema tables, which are inconsistent in this respect. */ @@ -4207,7 +4207,7 @@ const char *Field_iterator_table_ref::db return natural_join_it.column_ref()->db_name(); /* - Test that TABLE_LIST::db is the same as st_table_share::db to + Test that TABLE_LIST::db is the same as TABLE_SHARE::db to ensure consistency. An exception are I_S schema tables, which are inconsistent in this respect. */ @@ -4379,7 +4379,7 @@ Field_iterator_table_ref::get_natural_co /* Reset all columns bitmaps */ -void st_table::clear_column_bitmaps() +void TABLE::clear_column_bitmaps() { /* Reset column read/write usage. It's identical to: @@ -4400,9 +4400,9 @@ void st_table::clear_column_bitmaps() key fields. */ -void st_table::prepare_for_position() +void TABLE::prepare_for_position() { - DBUG_ENTER("st_table::prepare_for_position"); + DBUG_ENTER("TABLE::prepare_for_position"); if ((file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) && s->primary_key < MAX_KEY) @@ -4421,14 +4421,14 @@ void st_table::prepare_for_position() NOTE: This changes the bitmap to use the tmp bitmap After this, you can't access any other columns in the table until - bitmaps are reset, for example with st_table::clear_column_bitmaps() - or st_table::restore_column_maps_after_mark_index() + bitmaps are reset, for example with TABLE::clear_column_bitmaps() + or TABLE::restore_column_maps_after_mark_index() */ -void st_table::mark_columns_used_by_index(uint index) +void TABLE::mark_columns_used_by_index(uint index) { MY_BITMAP *bitmap= &tmp_set; - DBUG_ENTER("st_table::mark_columns_used_by_index"); + DBUG_ENTER("TABLE::mark_columns_used_by_index"); (void) file->extra(HA_EXTRA_KEYREAD); bitmap_clear_all(bitmap); @@ -4449,9 +4449,9 @@ void st_table::mark_columns_used_by_inde when calling mark_columns_used_by_index */ -void st_table::restore_column_maps_after_mark_index() +void TABLE::restore_column_maps_after_mark_index() { - DBUG_ENTER("st_table::restore_column_maps_after_mark_index"); + DBUG_ENTER("TABLE::restore_column_maps_after_mark_index"); key_read= 0; (void) file->extra(HA_EXTRA_NO_KEYREAD); @@ -4465,7 +4465,7 @@ void st_table::restore_column_maps_after mark columns used by key, but don't reset other fields */ -void st_table::mark_columns_used_by_index_no_reset(uint index, +void TABLE::mark_columns_used_by_index_no_reset(uint index, MY_BITMAP *bitmap) { KEY_PART_INFO *key_part= key_info[index].key_part; @@ -4484,7 +4484,7 @@ void st_table::mark_columns_used_by_inde always set and sometimes read. */ -void st_table::mark_auto_increment_column() +void TABLE::mark_auto_increment_column() { DBUG_ASSERT(found_next_number_field); /* @@ -4517,7 +4517,7 @@ void st_table::mark_auto_increment_colum retrieve the row again. */ -void st_table::mark_columns_needed_for_delete() +void TABLE::mark_columns_needed_for_delete() { if (triggers) triggers->mark_fields_used(TRG_EVENT_DELETE); @@ -4570,7 +4570,7 @@ void st_table::mark_columns_needed_for_d retrieve the row again. */ -void st_table::mark_columns_needed_for_update() +void TABLE::mark_columns_needed_for_update() { DBUG_ENTER("mark_columns_needed_for_update"); if (triggers) @@ -4616,7 +4616,7 @@ void st_table::mark_columns_needed_for_u as changed. */ -void st_table::mark_columns_needed_for_insert() +void TABLE::mark_columns_needed_for_insert() { if (triggers) { @@ -4692,9 +4692,9 @@ Item_subselect *TABLE_LIST::containing_s DESCRIPTION The parser collects the index hints for each table in a "tagged list" (TABLE_LIST::index_hints). Using the information in this tagged list - this function sets the members st_table::keys_in_use_for_query, - st_table::keys_in_use_for_group_by, st_table::keys_in_use_for_order_by, - st_table::force_index and st_table::covering_keys. + this function sets the members TABLE::keys_in_use_for_query, + TABLE::keys_in_use_for_group_by, TABLE::keys_in_use_for_order_by, + TABLE::force_index and TABLE::covering_keys. Current implementation of the runtime does not allow mixing FORCE INDEX and USE INDEX, so this is checked here. Then the FORCE INDEX list === modified file 'sql/table.h' --- a/sql/table.h 2008-09-11 18:36:05 +0000 +++ b/sql/table.h 2008-09-16 17:58:49 +0000 @@ -245,9 +245,9 @@ struct TABLE_share; instance of table share per one table in the database. */ -typedef struct st_table_share +struct TABLE_SHARE { - st_table_share() {} /* Remove gcc warning */ + TABLE_SHARE() {} /* Remove gcc warning */ /** Category of this table. */ TABLE_CATEGORY table_category; @@ -259,8 +259,7 @@ typedef struct st_table_share TYPELIB fieldnames; /* Pointer to fieldnames */ TYPELIB *intervals; /* pointer to interval info */ pthread_mutex_t LOCK_ha_data; /* To protect access to ha_data */ - struct st_table_share *next, /* Link to unused shares */ - **prev; + TABLE_SHARE *next, **prev; /* Link to unused shares */ /* Doubly-linked (back-linked) lists of used and unused TABLE objects @@ -550,7 +549,7 @@ typedef struct st_table_share return (tmp_table == SYSTEM_TMP_TABLE || is_view) ? 0 : table_map_id; } -} TABLE_SHARE; +}; extern ulong refresh_version; @@ -563,8 +562,9 @@ enum index_hint_type INDEX_HINT_FORCE }; -struct st_table { - st_table() {} /* Remove gcc warning */ +struct TABLE +{ + TABLE() {} /* Remove gcc warning */ TABLE_SHARE *s; handler *file; @@ -575,11 +575,11 @@ private: Declared as private to avoid direct manipulation with those objects. One should use methods of I_P_List template instead. */ - struct st_table *share_next, **share_prev; + TABLE *share_next, **share_prev; friend struct TABLE_share; public: - struct st_table *next, *prev; + TABLE *next, *prev; THD *in_use; /* Which thread uses this */ Field **field; /* Pointer to fields */ @@ -716,9 +716,7 @@ public: my_bool force_index; my_bool distinct,const_table,no_rows; my_bool key_read, no_keyread; - my_bool locked_by_logger; my_bool no_replicate; - my_bool locked_by_name; my_bool fulltext_searched; my_bool no_cache; /* To signal that the table is associated with a HANDLER statement */ @@ -729,7 +727,6 @@ public: Used only in the MODE_NO_AUTO_VALUE_ON_ZERO mode. */ my_bool auto_increment_field_not_null; - my_bool insert_or_update; /* Can be used by the handler */ my_bool alias_name_used; /* true if table_name is alias */ my_bool get_fields_in_item_tree; /* Signal to fix_field */ @@ -911,7 +908,6 @@ typedef struct st_schema_table /** The threshold size a blob field buffer before it is freed */ #define MAX_TDC_BLOB_SIZE 65536 -struct st_lex; class select_union; class TMP_TABLE_PARAM; @@ -991,6 +987,7 @@ public: ; */ +struct LEX; class Index_hint; struct TABLE_LIST { @@ -1106,7 +1103,7 @@ struct TABLE_LIST TMP_TABLE_PARAM *schema_table_param; /* link to select_lex where this table was used */ st_select_lex *select_lex; - st_lex *view; /* link on VIEW lex for merging */ + LEX *view; /* link on VIEW lex for merging */ Field_translator *field_translation; /* array of VIEW fields */ /* pointer to element after last one in translation table above */ Field_translator *field_translation_end; @@ -1335,9 +1332,9 @@ struct TABLE_LIST Item_subselect *containing_subselect(); /* - Compiles the tagged hints list and fills up st_table::keys_in_use_for_query, - st_table::keys_in_use_for_group_by, st_table::keys_in_use_for_order_by, - st_table::force_index and st_table::covering_keys. + Compiles the tagged hints list and fills up TABLE::keys_in_use_for_query, + TABLE::keys_in_use_for_group_by, TABLE::keys_in_use_for_order_by, + TABLE::force_index and TABLE::covering_keys. */ bool process_index_hints(TABLE *table); === added file 'sql/transaction.cc' --- a/sql/transaction.cc 1970-01-01 00:00:00 +0000 +++ b/sql/transaction.cc 2008-08-12 22:30:55 +0000 @@ -0,0 +1,584 @@ +/* Copyright (C) 2008 Sun/MySQL + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +#ifdef USE_PRAGMA_IMPLEMENTATION +#pragma implementation // gcc: Class implementation +#endif + +#include "transaction.h" +#include "mysql_priv.h" + +#ifdef WITH_MARIA_STORAGE_ENGINE +#include "../storage/maria/ha_maria.h" +#endif + +/* Conditions under which the transaction state must not change. */ +static bool trans_check(THD *thd) +{ + enum xa_states xa_state= thd->transaction.xid_state.xa_state; + DBUG_ENTER("trans_check"); + + if (unlikely(thd->in_sub_stmt)) + my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0)); + if (xa_state != XA_NOTR) + my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]); + else + DBUG_RETURN(FALSE); + + DBUG_RETURN(TRUE); +} + + +/** + Begin a new transaction. + + @note Beginning a transaction implicitly commits any current + transaction and releases existing locks. + + @param thd Current thread + @param flags Transaction flags + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_begin(THD *thd, uint flags) +{ + int res= FALSE; + DBUG_ENTER("trans_begin"); + + if (trans_check(thd)) + DBUG_RETURN(TRUE); + + thd->locked_tables_list.unlock_locked_tables(thd); + + if (trans_commit_implicit(thd)) + DBUG_RETURN(TRUE); + + thd->options|= OPTION_BEGIN; + thd->server_status|= SERVER_STATUS_IN_TRANS; + + if (flags & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT) + res= ha_start_consistent_snapshot(thd); + + DBUG_RETURN(test(res)); +} + + +/** + Commit the current transaction, making its changes permanent. + + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_commit(THD *thd) +{ + int res; + DBUG_ENTER("trans_commit"); + + if (trans_check(thd)) + DBUG_RETURN(TRUE); + + thd->server_status&= ~SERVER_STATUS_IN_TRANS; + res= ha_commit_trans(thd, TRUE); + thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); + thd->transaction.all.modified_non_trans_table= FALSE; + thd->lex->start_transaction_opt= 0; + + DBUG_RETURN(test(res)); +} + + +/** + Implicitly commit the current transaction. + + @note A implicit commit does not releases existing table locks. + + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_commit_implicit(THD *thd) +{ + bool res= FALSE; + DBUG_ENTER("trans_commit_implicit"); + + if (trans_check(thd)) + DBUG_RETURN(TRUE); + + if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN | + OPTION_TABLE_LOCK)) + { + /* Safety if one did "drop table" on locked tables */ + if (!thd->locked_tables_mode) + thd->options&= ~OPTION_TABLE_LOCK; + thd->server_status&= ~SERVER_STATUS_IN_TRANS; + res= test(ha_commit_trans(thd, TRUE)); +#ifdef WITH_MARIA_STORAGE_ENGINE + ha_maria::implicit_commit(thd, TRUE); +#endif + } + + thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); + thd->transaction.all.modified_non_trans_table= FALSE; + + DBUG_RETURN(res); +} + + +/** + Rollback the current transaction, canceling its changes. + + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_rollback(THD *thd) +{ + int res; + DBUG_ENTER("trans_rollback"); + + if (trans_check(thd)) + DBUG_RETURN(TRUE); + + thd->server_status&= ~SERVER_STATUS_IN_TRANS; + res= ha_rollback_trans(thd, TRUE); + thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); + thd->transaction.all.modified_non_trans_table= FALSE; + thd->lex->start_transaction_opt= 0; + + DBUG_RETURN(test(res)); +} + + +/** + Commit the single statement transaction. + + @note Note that if the autocommit is on, then the following call + inside InnoDB will commit or rollback the whole transaction + (= the statement). The autocommit mechanism built into InnoDB + is based on counting locks, but if the user has used LOCK + TABLES then that mechanism does not know to do the commit. + + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_commit_stmt(THD *thd) +{ + DBUG_ENTER("trans_commit_stmt"); + int res= FALSE; + if (thd->transaction.stmt.ha_list) + res= ha_commit_trans(thd, FALSE); + DBUG_RETURN(test(res)); +} + + +/** + Rollback the single statement transaction. + + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure +*/ +bool trans_rollback_stmt(THD *thd) +{ + DBUG_ENTER("trans_rollback_stmt"); + + if (thd->transaction.stmt.ha_list) + { + thd->transaction_rollback_request= FALSE; + ha_rollback_trans(thd, FALSE); + if (thd->transaction_rollback_request && !thd->in_sub_stmt) + ha_rollback_trans(thd, TRUE); + } + + DBUG_RETURN(FALSE); +} + +/* Find a named savepoint in the current transaction. */ +static SAVEPOINT ** +find_savepoint(THD *thd, LEX_STRING name) +{ + SAVEPOINT **sv= &thd->transaction.savepoints; + + while (*sv) + { + if (my_strnncoll(system_charset_info, (uchar *) name.str, name.length, + (uchar *) (*sv)->name, (*sv)->length) == 0) + break; + sv= &(*sv)->prev; + } + + return sv; +} + + +/** + Set a named transaction savepoint. + + @param thd Current thread + @param name Savepoint name + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_savepoint(THD *thd, LEX_STRING name) +{ + SAVEPOINT **sv, *newsv; + DBUG_ENTER("trans_savepoint"); + + if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) || + thd->in_sub_stmt) || !opt_using_transactions) + DBUG_RETURN(FALSE); + + sv= find_savepoint(thd, name); + + if (*sv) /* old savepoint of the same name exists */ + { + newsv= *sv; + ha_release_savepoint(thd, *sv); + *sv= (*sv)->prev; + } + else if ((newsv= (SAVEPOINT *) alloc_root(&thd->transaction.mem_root, + savepoint_alloc_size)) == NULL) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + DBUG_RETURN(TRUE); + } + + newsv->name= strmake_root(&thd->transaction.mem_root, name.str, name.length); + newsv->length= name.length; + + /* + if we'll get an error here, don't add new savepoint to the list. + we'll lose a little bit of memory in transaction mem_root, but it'll + be free'd when transaction ends anyway + */ + if (ha_savepoint(thd, newsv)) + DBUG_RETURN(TRUE); + + newsv->prev= thd->transaction.savepoints; + thd->transaction.savepoints= newsv; + + DBUG_RETURN(FALSE); +} + + +/** + Rollback a transaction to the named savepoint. + + @note Modifications that the current transaction made to + rows after the savepoint was set are undone in the + rollback. + + @note Savepoints that were set at a later time than the + named savepoint are deleted. + + @param thd Current thread + @param name Savepoint name + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name) +{ + int res= FALSE; + SAVEPOINT *sv= *find_savepoint(thd, name); + DBUG_ENTER("trans_rollback_to_savepoint"); + + if (sv == NULL) + { + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", name.str); + DBUG_RETURN(TRUE); + } + + if (ha_rollback_to_savepoint(thd, sv)) + res= TRUE; + else if (((thd->options & OPTION_KEEP_LOG) || + thd->transaction.all.modified_non_trans_table) && + !thd->slave_thread) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARNING_NOT_COMPLETE_ROLLBACK, + ER(ER_WARNING_NOT_COMPLETE_ROLLBACK)); + + thd->transaction.savepoints= sv; + + DBUG_RETURN(test(res)); +} + + +/** + Remove the named savepoint from the set of savepoints of + the current transaction. + + @note No commit or rollback occurs. It is an error if the + savepoint does not exist. + + @param thd Current thread + @param name Savepoint name + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_release_savepoint(THD *thd, LEX_STRING name) +{ + int res= FALSE; + SAVEPOINT *sv= *find_savepoint(thd, name); + DBUG_ENTER("trans_release_savepoint"); + + if (sv == NULL) + { + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", name.str); + DBUG_RETURN(TRUE); + } + + if (ha_release_savepoint(thd, sv)) + res= TRUE; + + thd->transaction.savepoints= sv->prev; + + DBUG_RETURN(test(res)); +} + + +/** + Starts an XA transaction with the given xid value. + + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_xa_start(THD *thd) +{ + enum xa_states xa_state= thd->transaction.xid_state.xa_state; + DBUG_ENTER("trans_xa_start"); + + if (xa_state == XA_IDLE && thd->lex->xa_opt == XA_RESUME) + { + bool not_equal= !thd->transaction.xid_state.xid.eq(thd->lex->xid); + if (not_equal) + my_error(ER_XAER_NOTA, MYF(0)); + else + thd->transaction.xid_state.xa_state= XA_ACTIVE; + DBUG_RETURN(not_equal); + } + + /* TODO: JOIN is not supported yet. */ + if (thd->lex->xa_opt != XA_NONE) + my_error(ER_XAER_INVAL, MYF(0)); + else if (xa_state != XA_NOTR) + my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]); + else if (thd->locked_tables_mode || thd->active_transaction()) + my_error(ER_XAER_OUTSIDE, MYF(0)); + else if (xid_cache_search(thd->lex->xid)) + my_error(ER_XAER_DUPID, MYF(0)); + else if (!trans_begin(thd)) + { + DBUG_ASSERT(thd->transaction.xid_state.xid.is_null()); + thd->transaction.xid_state.xa_state= XA_ACTIVE; + thd->transaction.xid_state.xid.set(thd->lex->xid); + xid_cache_insert(&thd->transaction.xid_state); + DBUG_RETURN(FALSE); + } + + DBUG_RETURN(TRUE); +} + + +/** + Put a XA transaction in the IDLE state. + + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_xa_end(THD *thd) +{ + DBUG_ENTER("trans_xa_end"); + + /* TODO: SUSPEND and FOR MIGRATE are not supported yet. */ + if (thd->lex->xa_opt != XA_NONE) + my_error(ER_XAER_INVAL, MYF(0)); + else if (thd->transaction.xid_state.xa_state != XA_ACTIVE) + my_error(ER_XAER_RMFAIL, MYF(0), + xa_state_names[thd->transaction.xid_state.xa_state]); + else if (!thd->transaction.xid_state.xid.eq(thd->lex->xid)) + my_error(ER_XAER_NOTA, MYF(0)); + else + thd->transaction.xid_state.xa_state= XA_IDLE; + + DBUG_RETURN(thd->transaction.xid_state.xa_state != XA_IDLE); +} + + +/** + Put a XA transaction in the PREPARED state. + + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_xa_prepare(THD *thd) +{ + DBUG_ENTER("trans_xa_prepare"); + + if (thd->transaction.xid_state.xa_state != XA_IDLE) + my_error(ER_XAER_RMFAIL, MYF(0), + xa_state_names[thd->transaction.xid_state.xa_state]); + else if (!thd->transaction.xid_state.xid.eq(thd->lex->xid)) + my_error(ER_XAER_NOTA, MYF(0)); + else if (ha_prepare(thd)) + { + xid_cache_delete(&thd->transaction.xid_state); + thd->transaction.xid_state.xa_state= XA_NOTR; + my_error(ER_XA_RBROLLBACK, MYF(0)); + } + else + thd->transaction.xid_state.xa_state= XA_PREPARED; + + DBUG_RETURN(thd->transaction.xid_state.xa_state != XA_PREPARED); +} + + +/** + Commit and terminate the a XA transaction. + + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_xa_commit(THD *thd) +{ + bool res= TRUE; + enum xa_states xa_state= thd->transaction.xid_state.xa_state; + DBUG_ENTER("trans_xa_commit"); + + if (!thd->transaction.xid_state.xid.eq(thd->lex->xid)) + { + XID_STATE *xs= xid_cache_search(thd->lex->xid); + bool not_found= !xs || xs->in_thd; + if (not_found) + my_error(ER_XAER_NOTA, MYF(0)); + else + { + ha_commit_or_rollback_by_xid(thd->lex->xid, 1); + xid_cache_delete(xs); + } + DBUG_RETURN(not_found); + } + + if (xa_state == XA_IDLE && thd->lex->xa_opt == XA_ONE_PHASE) + { + int r= ha_commit_trans(thd, TRUE); + if ((res= test(r))) + my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0)); + } + else if (xa_state == XA_PREPARED && thd->lex->xa_opt == XA_NONE) + { + if (wait_if_global_read_lock(thd, 0, 0)) + { + ha_rollback_trans(thd, TRUE); + my_error(ER_XAER_RMERR, MYF(0)); + } + else + { + res= test(ha_commit_one_phase(thd, 1)); + if (res) + my_error(ER_XAER_RMERR, MYF(0)); + start_waiting_global_read_lock(thd); + } + } + else + my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]); + + thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); + thd->transaction.all.modified_non_trans_table= FALSE; + thd->server_status&= ~SERVER_STATUS_IN_TRANS; + xid_cache_delete(&thd->transaction.xid_state); + thd->transaction.xid_state.xa_state= XA_NOTR; + + DBUG_RETURN(res); +} + + +/** + Roll back and terminate a XA transaction. + + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure +*/ + +bool trans_xa_rollback(THD *thd) +{ + bool res= TRUE; + enum xa_states xa_state= thd->transaction.xid_state.xa_state; + DBUG_ENTER("trans_xa_rollback"); + + if (!thd->transaction.xid_state.xid.eq(thd->lex->xid)) + { + XID_STATE *xs= xid_cache_search(thd->lex->xid); + bool not_found= !xs || xs->in_thd; + if (not_found) + my_error(ER_XAER_NOTA, MYF(0)); + else + { + ha_commit_or_rollback_by_xid(thd->lex->xid, 0); + xid_cache_delete(xs); + } + DBUG_RETURN(not_found); + } + + if (xa_state != XA_IDLE && xa_state != XA_PREPARED) + { + my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]); + DBUG_RETURN(TRUE); + } + + if ((res= test(ha_rollback_trans(thd, TRUE)))) + my_error(ER_XAER_RMERR, MYF(0)); + + thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); + thd->transaction.all.modified_non_trans_table= FALSE; + thd->server_status&= ~SERVER_STATUS_IN_TRANS; + xid_cache_delete(&thd->transaction.xid_state); + thd->transaction.xid_state.xa_state= XA_NOTR; + + DBUG_RETURN(res); +} === added file 'sql/transaction.h' --- a/sql/transaction.h 1970-01-01 00:00:00 +0000 +++ b/sql/transaction.h 2008-08-12 22:30:55 +0000 @@ -0,0 +1,46 @@ +/* Copyright (C) 2008 Sun/MySQL + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef USE_PRAGMA_INTERFACE +#pragma interface /* gcc class implementation */ +#endif + +#ifndef TRANSACTION_H +#define TRANSACTION_H + +#include +#include + +class THD; + +bool trans_begin(THD *thd, uint flags= 0); +bool trans_commit(THD *thd); +bool trans_commit_implicit(THD *thd); +bool trans_rollback(THD *thd); + +bool trans_commit_stmt(THD *thd); +bool trans_rollback_stmt(THD *thd); + +bool trans_savepoint(THD *thd, LEX_STRING name); +bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name); +bool trans_release_savepoint(THD *thd, LEX_STRING name); + +bool trans_xa_start(THD *thd); +bool trans_xa_end(THD *thd); +bool trans_xa_prepare(THD *thd); +bool trans_xa_commit(THD *thd); +bool trans_xa_rollback(THD *thd); + +#endif /* TRANSACTION_H */ === modified file 'storage/falcon/InfoTable.h' --- a/storage/falcon/InfoTable.h 2008-08-07 12:58:45 +0000 +++ b/storage/falcon/InfoTable.h 2008-09-16 17:58:49 +0000 @@ -25,7 +25,7 @@ typedef long long INT64; class THD; struct charset_info_st; struct TABLE_LIST; -struct st_table; +struct TABLE; class InfoTable { @@ -57,7 +57,7 @@ public: virtual void setNotNull(int column); int error; - st_table *table; + TABLE *table; THD *mySqlThread; charset_info_st *charSetInfo; }; === modified file 'storage/falcon/ha_falcon.cpp' --- a/storage/falcon/ha_falcon.cpp 2008-09-11 16:28:29 +0000 +++ b/storage/falcon/ha_falcon.cpp 2008-09-16 17:58:49 +0000 @@ -414,7 +414,7 @@ typedef longlong dec2; // Construction/Destruction ////////////////////////////////////////////////////////////////////// -StorageInterface::StorageInterface(handlerton *hton, st_table_share *table_arg) +StorageInterface::StorageInterface(handlerton *hton, TABLE_SHARE *table_arg) : handler(hton, table_arg) { ref_length = sizeof(lastRecord); @@ -1051,7 +1051,7 @@ int StorageInterface::delete_all_rows() DBUG_RETURN(my_errno=HA_ERR_WRONG_COMMAND); int ret = 0; - struct st_table_share *tableShare = table_share; + TABLE_SHARE *tableShare = table_share; const char *tableName = tableShare->normalized_path.str; if (!storageShare) === modified file 'storage/falcon/ha_falcon.h' --- a/storage/falcon/ha_falcon.h 2008-09-09 23:15:17 +0000 +++ b/storage/falcon/ha_falcon.h 2008-09-16 17:58:49 +0000 @@ -28,14 +28,14 @@ static const int TRANSACTION_WRITE_COMMI static const int TRANSACTION_CONSISTENT_READ = 8; // Dirty reads and non-repeatable reads are prevented; phantom reads can occur. static const int TRANSACTION_SERIALIZABLE = 16; // Dirty reads, non-repeatable reads and phantom reads are prevented. -struct st_table_share; +struct TABLE_SHARE; class StorageIndexDesc; struct StorageBlob; class StorageInterface : public handler { public: - StorageInterface(handlerton *, st_table_share *table_arg); + StorageInterface(handlerton *, TABLE_SHARE *table_arg); ~StorageInterface(void); virtual int open(const char *name, int mode, uint test_if_locked); @@ -180,7 +180,7 @@ public: const char* errorText; THR_LOCK_DATA lockData; // MySQL lock THD *mySqlThread; - st_table_share *share; + TABLE_SHARE *share; uint recordLength; int lastRecord; int nextRecord; === modified file 'storage/innobase/handler/ha_innodb.cc' --- a/storage/innobase/handler/ha_innodb.cc 2008-08-21 23:38:19 +0000 +++ b/storage/innobase/handler/ha_innodb.cc 2008-09-04 18:30:34 +0000 @@ -3650,7 +3650,7 @@ calc_row_difference( upd_t* uvect, /* in/out: update vector */ uchar* old_row, /* in: old row in MySQL format */ uchar* new_row, /* in: new row in MySQL format */ - struct st_table* table, /* in: table in MySQL data + TABLE* table, /* in: table in MySQL data dictionary */ uchar* upd_buff, /* in: buffer to use */ ulint buff_len, /* in: buffer length */ === modified file 'storage/maria/ha_maria.cc' --- a/storage/maria/ha_maria.cc 2008-08-25 18:34:43 +0000 +++ b/storage/maria/ha_maria.cc 2008-09-04 18:30:34 +0000 @@ -2319,8 +2319,8 @@ int ha_maria::start_stmt(THD *thd, thr_l This can be considered a hack. When Maria loses HA_NO_TRANSACTIONS it will be participant in the connection's transaction and so the implicit commits - (ha_commit()) (like in end_active_trans()) will do the implicit commit - without need to call this function which can then be removed. + (ha_commit_trans()) (like in trans_commit_implicit()) will do the implicit + commit without need to call this function which can then be removed. @param thd THD object @param new_trn if a new transaction should be created; a new === modified file 'tests/mysql_client_test.c' --- a/tests/mysql_client_test.c 2008-08-22 11:16:08 +0000 +++ b/tests/mysql_client_test.c 2008-09-15 20:37:25 +0000 @@ -1500,6 +1500,544 @@ static void test_prepare_simple() myquery(rc); } +/************************************************************************/ + +#define FILE_PATH_SIZE 4096 +#define CMD_BUFFER_SIZE 8192 + +char mct_log_file_path[FILE_PATH_SIZE]; +FILE *mct_log_file; + +void mct_start_logging(const char *test_case_name) +{ + const char *tmp_dir= getenv("MYSQL_TMP_DIR"); + + if (!tmp_dir) + { + printf("Warning: MYSQL_TMP_DIR is not set. Logging is disabled.\n"); + return; + } + + /* + Path is: /.out.log + 10 is length of '/' + '.out.log' + \0 + */ + + if (strlen(tmp_dir) + strlen(test_case_name) + 10 > FILE_PATH_SIZE) + { + printf("Warning: MYSQL_TMP_DIR is too long. Logging is disabled.\n"); + return; + } + + my_snprintf(mct_log_file_path, FILE_PATH_SIZE, + "%s/%s.out.log", + (const char *) tmp_dir, + (const char *) test_case_name); + + mct_log_file= my_fopen(mct_log_file_path, O_WRONLY | O_BINARY, MYF(MY_WME)); + + if (!mct_log_file) + { + printf("Warning: can not open log file (%s): %s. Logging is disabled.\n", + (const char *) mct_log_file_path, + (const char *) strerror(errno)); + return; + } +} + +void mct_log(const char *format, ...) +{ + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); + + if (mct_log_file) + { + va_list args; + va_start(args, format); + vfprintf(mct_log_file, format, args); + va_end(args); + } +} + +int mct_check_result(const char *result_file_name) +{ + char diff_cmd[CMD_BUFFER_SIZE]; + + const char *test_dir= getenv("MYSQL_TEST_DIR"); + char result_file_path[FILE_PATH_SIZE]; + + if (!mct_log_file) + { + printf("Warning: logging was disabled. Result checking is impossible.\n"); + return 0; + } + + if (!test_dir) + { + printf("Warning: MYSQL_TEST_DIR is not set. " + "Result checking is impossible.\n"); + return 0; + } + + /* + Path is: / + 2 is length of '/' + \0 + */ + + if (strlen(test_dir) + strlen(result_file_name) + 2 > FILE_PATH_SIZE) + { + printf("Warning: MYSQL_TEST_DIR is too long. " + "Result checking is impossible.\n"); + return 0; + } + + my_snprintf(result_file_path, FILE_PATH_SIZE, + "%s/%s", + (const char *) test_dir, + (const char *) result_file_name); + + my_fclose(mct_log_file, MYF(0)); + mct_log_file= NULL; + + my_snprintf(diff_cmd, CMD_BUFFER_SIZE, "diff -u '%s' '%s'", + (const char *) result_file_path, + (const char *) mct_log_file_path); + + puts(""); + fflush(stdout); + fflush(stderr); + + return system(diff_cmd); +} + +#define WL4435_NUM_PARAMS 10 +#define WL4435_STRING_SIZE 30 + +static void test_wl4435() +{ + MYSQL_STMT *stmt; + int rc; + char query[MAX_TEST_QUERY_LENGTH]; + + char str_data[20][WL4435_STRING_SIZE]; + double dbl_data[20]; + char dec_data[20][WL4435_STRING_SIZE]; + int int_data[20]; + ulong str_length= WL4435_STRING_SIZE; + my_bool is_null; + MYSQL_BIND ps_params[WL4435_NUM_PARAMS]; + + int exec_counter; + + myheader("test_wl4435"); + mct_start_logging("test_wl4435"); + + rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1"); + myquery(rc); + + rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p2"); + myquery(rc); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + myquery(rc); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t2"); + myquery(rc); + + rc= mysql_query(mysql, "CREATE TABLE t1(a1 INT, a2 CHAR(32), " + " a3 DOUBLE(4, 2), a4 DECIMAL(3, 1))"); + myquery(rc); + + rc= mysql_query(mysql, "CREATE TABLE t2(b0 INT, b1 INT, b2 CHAR(32), " + " b3 DOUBLE(4, 2), b4 DECIMAL(3, 1))"); + myquery(rc); + + rc= mysql_query(mysql, "INSERT INTO t1 VALUES" + "(1, '11', 12.34, 56.7), " + "(2, '12', 56.78, 90.1), " + "(3, '13', 23.45, 67.8)"); + myquery(rc); + + rc= mysql_query(mysql, "INSERT INTO t2 VALUES" + "(100, 10, '110', 70.70, 10.1), " + "(200, 20, '120', 80.80, 20.2), " + "(300, 30, '130', 90.90, 30.3)"); + myquery(rc); + + rc= mysql_query(mysql, + "CREATE PROCEDURE p1(" + " IN v0 INT, " + " OUT v_str_1 CHAR(32), " + " OUT v_dbl_1 DOUBLE(4, 2), " + " OUT v_dec_1 DECIMAL(6, 3), " + " OUT v_int_1 INT, " + " IN v1 INT, " + " INOUT v_str_2 CHAR(64), " + " INOUT v_dbl_2 DOUBLE(5, 3), " + " INOUT v_dec_2 DECIMAL(7, 4), " + " INOUT v_int_2 INT)" + "BEGIN " + " SET v0 = -1; " + " SET v1 = -1; " + " SET v_str_1 = 'test_1'; " + " SET v_dbl_1 = 12.34; " + " SET v_dec_1 = 567.891; " + " SET v_int_1 = 2345; " + " SET v_str_2 = 'test_2'; " + " SET v_dbl_2 = 67.891; " + " SET v_dec_2 = 234.6789; " + " SET v_int_2 = 6789; " + " SELECT * FROM t1; " + " SELECT * FROM t2; " + "END"); + myquery(rc); + + rc= mysql_query(mysql, + "CREATE PROCEDURE p2(" + " IN i1 VARCHAR(255) CHARACTER SET koi8r, " + " OUT o1 VARCHAR(255) CHARACTER SET cp1251, " + " OUT o2 VARBINARY(255)) " + "BEGIN " + " SET o1 = i1; " + " SET o2 = i1; " + "END"); + myquery(rc); + + strmov(query, "CALL p1(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + stmt= mysql_simple_prepare(mysql, query); + check_stmt(stmt); + + /* Init PS-parameters. */ + + bzero((char *) ps_params, sizeof (ps_params)); + + /* - v0 -- INT */ + + ps_params[0].buffer_type= MYSQL_TYPE_LONG; + ps_params[0].buffer= (char *) &int_data[0]; + ps_params[0].length= 0; + ps_params[0].is_null= 0; + + /* - v_str_1 -- CHAR(32) */ + + ps_params[1].buffer_type= MYSQL_TYPE_STRING; + ps_params[1].buffer= (char *) str_data[0]; + ps_params[1].buffer_length= WL4435_STRING_SIZE; + ps_params[1].length= &str_length; + ps_params[1].is_null= 0; + + /* - v_dbl_1 -- DOUBLE */ + + ps_params[2].buffer_type= MYSQL_TYPE_DOUBLE; + ps_params[2].buffer= (char *) &dbl_data[0]; + ps_params[2].length= 0; + ps_params[2].is_null= 0; + + /* - v_dec_1 -- DECIMAL */ + + ps_params[3].buffer_type= MYSQL_TYPE_NEWDECIMAL; + ps_params[3].buffer= (char *) dec_data[0]; + ps_params[3].buffer_length= WL4435_STRING_SIZE; + ps_params[3].length= 0; + ps_params[3].is_null= 0; + + /* - v_int_1 -- INT */ + + ps_params[4].buffer_type= MYSQL_TYPE_LONG; + ps_params[4].buffer= (char *) &int_data[0]; + ps_params[4].length= 0; + ps_params[4].is_null= 0; + + /* - v1 -- INT */ + + ps_params[5].buffer_type= MYSQL_TYPE_LONG; + ps_params[5].buffer= (char *) &int_data[0]; + ps_params[5].length= 0; + ps_params[5].is_null= 0; + + /* - v_str_2 -- CHAR(32) */ + + ps_params[6].buffer_type= MYSQL_TYPE_STRING; + ps_params[6].buffer= (char *) str_data[0]; + ps_params[6].buffer_length= WL4435_STRING_SIZE; + ps_params[6].length= &str_length; + ps_params[6].is_null= 0; + + /* - v_dbl_2 -- DOUBLE */ + + ps_params[7].buffer_type= MYSQL_TYPE_DOUBLE; + ps_params[7].buffer= (char *) &dbl_data[0]; + ps_params[7].length= 0; + ps_params[7].is_null= 0; + + /* - v_dec_2 -- DECIMAL */ + + ps_params[8].buffer_type= MYSQL_TYPE_DECIMAL; + ps_params[8].buffer= (char *) dec_data[0]; + ps_params[8].buffer_length= WL4435_STRING_SIZE; + ps_params[8].length= 0; + ps_params[8].is_null= 0; + + /* - v_int_2 -- INT */ + + ps_params[9].buffer_type= MYSQL_TYPE_LONG; + ps_params[9].buffer= (char *) &int_data[0]; + ps_params[9].length= 0; + ps_params[9].is_null= 0; + + /* Bind parameters. */ + + rc= mysql_stmt_bind_param(stmt, ps_params); + + /* Execute! */ + + for (exec_counter= 0; exec_counter < 3; ++exec_counter) + { + int i; + int num_fields; + MYSQL_BIND *rs_bind; + + mct_log("\nexec_counter: %d\n", (int) exec_counter); + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + while (1) + { + MYSQL_FIELD *fields; + + MYSQL_RES *rs_metadata= mysql_stmt_result_metadata(stmt); + + num_fields= mysql_num_fields(rs_metadata); + fields= mysql_fetch_fields(rs_metadata); + + rs_bind= (MYSQL_BIND *) malloc(sizeof (MYSQL_BIND) * num_fields); + bzero(rs_bind, sizeof (MYSQL_BIND) * num_fields); + + mct_log("num_fields: %d\n", (int) num_fields); + + for (i = 0; i < num_fields; ++i) + { + mct_log(" - %d: name: '%s'/'%s'; table: '%s'/'%s'; " + "db: '%s'; catalog: '%s'; length: %d; max_length: %d; " + "type: %d; decimals: %d\n", + (int) i, + (const char *) fields[i].name, + (const char *) fields[i].org_name, + (const char *) fields[i].table, + (const char *) fields[i].org_table, + (const char *) fields[i].db, + (const char *) fields[i].catalog, + (int) fields[i].length, + (int) fields[i].max_length, + (int) fields[i].type, + (int) fields[i].decimals); + + rs_bind[i].buffer_type= fields[i].type; + rs_bind[i].is_null= &is_null; + + switch (fields[i].type) + { + case MYSQL_TYPE_LONG: + rs_bind[i].buffer= (char *) &(int_data[i]); + rs_bind[i].buffer_length= sizeof (int_data); + break; + + case MYSQL_TYPE_STRING: + rs_bind[i].buffer= (char *) str_data[i]; + rs_bind[i].buffer_length= WL4435_STRING_SIZE; + rs_bind[i].length= &str_length; + break; + + case MYSQL_TYPE_DOUBLE: + rs_bind[i].buffer= (char *) &dbl_data[i]; + rs_bind[i].buffer_length= sizeof (dbl_data); + break; + + case MYSQL_TYPE_NEWDECIMAL: + rs_bind[i].buffer= (char *) dec_data[i]; + rs_bind[i].buffer_length= WL4435_STRING_SIZE; + rs_bind[i].length= &str_length; + break; + + default: + fprintf(stderr, "ERROR: unexpected type: %d.\n", fields[i].type); + exit(1); + } + } + + rc= mysql_stmt_bind_result(stmt, rs_bind); + check_execute(stmt, rc); + + mct_log("Data:\n"); + + while (1) + { + int rc= mysql_stmt_fetch(stmt); + + if (rc == 1 || rc == MYSQL_NO_DATA) + break; + + mct_log(" "); + + for (i = 0; i < num_fields; ++i) + { + switch (rs_bind[i].buffer_type) + { + case MYSQL_TYPE_LONG: + mct_log(" int: %ld;", + (long) *((int *) rs_bind[i].buffer)); + break; + + case MYSQL_TYPE_STRING: + mct_log(" str: '%s';", + (char *) rs_bind[i].buffer); + break; + + case MYSQL_TYPE_DOUBLE: + mct_log(" dbl: %lf;", + (double) *((double *) rs_bind[i].buffer)); + break; + + case MYSQL_TYPE_NEWDECIMAL: + mct_log(" dec: '%s';", + (char *) rs_bind[i].buffer); + break; + + default: + printf(" unexpected type (%d)\n", + rs_bind[i].buffer_type); + } + } + mct_log("\n"); + } + + mct_log("EOF\n"); + + rc= mysql_stmt_next_result(stmt); + mct_log("mysql_stmt_next_result(): %d; field_count: %d\n", + (int) rc, (int) mysql->field_count); + + free(rs_bind); + mysql_free_result(rs_metadata); + + if (rc > 0) + { + printf("Error: %s (errno: %d)\n", + mysql_error(mysql), mysql_errno(mysql)); + DIE(rc > 0); + } + + if (rc) + break; + + if (!mysql->field_count) + { + /* This is the last OK-packet. No more resultsets. */ + break; + } + } + + } + + mysql_stmt_close(stmt); + + rc= mysql_commit(mysql); + myquery(rc); + + rc= mct_check_result("r/test_wl4435.result"); + mytest_r(rc); + + /* i18n part of test case. */ + + { + const char *str_koi8r= "\xee\xd5\x2c\x20\xda\xc1\x20\xd2\xd9\xc2\xc1\xcc\xcb\xd5"; + const char *str_cp1251= "\xcd\xf3\x2c\x20\xe7\xe0\x20\xf0\xfb\xe1\xe0\xeb\xea\xf3"; + char o1_buffer[255]; + ulong o1_length; + char o2_buffer[255]; + ulong o2_length; + + MYSQL_BIND rs_bind[2]; + + strmov(query, "CALL p2(?, ?, ?)"); + stmt= mysql_simple_prepare(mysql, query); + check_stmt(stmt); + + /* Init PS-parameters. */ + + bzero((char *) ps_params, sizeof (ps_params)); + + ps_params[0].buffer_type= MYSQL_TYPE_STRING; + ps_params[0].buffer= (char *) str_koi8r; + ps_params[0].buffer_length= strlen(str_koi8r); + + ps_params[1].buffer_type= MYSQL_TYPE_STRING; + ps_params[1].buffer= o1_buffer; + ps_params[1].buffer_length= 0; + + ps_params[2].buffer_type= MYSQL_TYPE_STRING; + ps_params[2].buffer= o2_buffer; + ps_params[2].buffer_length= 0; + + /* Bind parameters. */ + + rc= mysql_stmt_bind_param(stmt, ps_params); + check_execute(stmt, rc); + + /* Prevent converting to character_set_results. */ + + rc= mysql_query(mysql, "SET NAMES binary"); + myquery(rc); + + /* Execute statement. */ + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + /* Bind result. */ + + bzero(rs_bind, sizeof (rs_bind)); + + rs_bind[0].buffer_type= MYSQL_TYPE_STRING; + rs_bind[0].buffer= o1_buffer; + rs_bind[0].buffer_length= sizeof (o1_buffer); + rs_bind[0].length= &o1_length; + + rs_bind[1].buffer_type= MYSQL_TYPE_BLOB; + rs_bind[1].buffer= o2_buffer; + rs_bind[1].buffer_length= sizeof (o2_buffer); + rs_bind[1].length= &o2_length; + + rc= mysql_stmt_bind_result(stmt, rs_bind); + check_execute(stmt, rc); + + /* Fetch result. */ + + rc= mysql_stmt_fetch(stmt); + check_execute(stmt, rc); + + /* Check result. */ + + DIE_UNLESS(o1_length == strlen(str_cp1251)); + DIE_UNLESS(o2_length == strlen(str_koi8r)); + DIE_UNLESS(!memcmp(o1_buffer, str_cp1251, o1_length)); + DIE_UNLESS(!memcmp(o2_buffer, str_koi8r, o2_length)); + + rc= mysql_stmt_fetch(stmt); + DIE_UNLESS(rc == MYSQL_NO_DATA); + + rc= mysql_stmt_next_result(stmt); + DIE_UNLESS(rc == 0 && mysql->field_count == 0); + + mysql_stmt_close(stmt); + + rc= mysql_commit(mysql); + myquery(rc); + } +} /* Test simple prepare field results */ @@ -14495,9 +15033,8 @@ static void test_bug12001() /* Create connection that supports multi statements */ if (!mysql_real_connect(mysql_local, opt_host, opt_user, - opt_password, current_db, opt_port, - opt_unix_socket, CLIENT_MULTI_STATEMENTS | - CLIENT_MULTI_RESULTS)) + opt_password, current_db, opt_port, + opt_unix_socket, CLIENT_MULTI_STATEMENTS)) { fprintf(stdout, "\n mysql_real_connect() failed"); exit(1); @@ -15869,7 +16406,7 @@ static void test_bug15752() if (! mysql_real_connect(&mysql_local, opt_host, opt_user, opt_password, current_db, opt_port, opt_unix_socket, - CLIENT_MULTI_STATEMENTS|CLIENT_MULTI_RESULTS)) + CLIENT_MULTI_STATEMENTS)) { printf("Unable connect to MySQL server: %s\n", mysql_error(&mysql_local)); DIE_UNLESS(0); @@ -17831,6 +18368,7 @@ static void test_bug36004() query_int_variable(mysql, "@@warning_count", &warning_count); DIE_UNLESS(warning_count == 0); + mysql_stmt_close(stmt); DBUG_VOID_RETURN; } @@ -18230,6 +18768,7 @@ static struct my_tests_st my_tests[]= { { "test_wl4166_4", test_wl4166_4 }, { "test_bug36004", test_bug36004 }, { "test_wl4284_1", test_wl4284_1 }, + { "test_wl4435", test_wl4435 }, { "test_bug38486", test_bug38486 }, { 0, 0 } };