diff --git a/configure.in b/configure.in index 3341b0217da..2b3944d38b0 100644 --- a/configure.in +++ b/configure.in @@ -359,6 +359,14 @@ AC_SUBST(INSTALL_SCRIPT) export CC CXX CFLAGS LD LDFLAGS AR +if test "$GCC" = "yes" +then + # mysqld requires -fno-implicit-templates. + # Disable exceptions as they seams to create problems with gcc and threads. + # mysqld doesn't use run-time-type-checking, so we disable it. + CXXFLAGS="$CXXFLAGS -fno-implicit-templates -fno-exceptions -fno-rtti" +fi + # Avoid bug in fcntl on some versions of linux AC_MSG_CHECKING("if we should use 'skip-locking' as default for $target_os") # Any variation of Linux diff --git a/include/errmsg.h b/include/errmsg.h index fd3da392df4..1dd5759c104 100644 --- a/include/errmsg.h +++ b/include/errmsg.h @@ -96,6 +96,7 @@ extern const char *client_errors[]; /* Error messages */ #define CR_NO_DATA 2051 #define CR_NO_STMT_METADATA 2052 #define CR_NO_RESULT_SET 2053 -#define CR_ERROR_LAST /*Copy last error nr:*/ 2053 +#define CR_NOT_IMPLEMENTED 2054 +#define CR_ERROR_LAST /*Copy last error nr:*/ 2054 /* Add error numbers before CR_ERROR_LAST and change it accordingly. */ diff --git a/include/mysql.h b/include/mysql.h index bcff8fa8645..b2b2e239758 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -664,6 +664,7 @@ typedef struct st_mysql_stmt unsigned char **row); unsigned long stmt_id; /* Id for prepared statement */ unsigned long flags; /* i.e. type of cursor to open */ + unsigned long prefetch_rows; /* number of rows per one COM_FETCH */ /* Copied from mysql->server_status after execute/fetch to know server-side cursor status for this statement. @@ -702,7 +703,12 @@ enum enum_stmt_attr_type unsigned long with combination of cursor flags (read only, for update, etc) */ - STMT_ATTR_CURSOR_TYPE + STMT_ATTR_CURSOR_TYPE, + /* + Amount of rows to retrieve from server per one fetch if using cursors. + Accepts unsigned long attribute in the range 1 - ulong_max + */ + STMT_ATTR_PREFETCH_ROWS }; diff --git a/libmysql/errmsg.c b/libmysql/errmsg.c index 5d183b478ef..9e1d70a47df 100644 --- a/libmysql/errmsg.c +++ b/libmysql/errmsg.c @@ -81,6 +81,7 @@ const char *client_errors[]= "Attempt to read column without prior row fetch", "Prepared statement contains no metadata", "Attempt to read a row while there is no result set associated with the statement", + "This feature is not implemented yet", "" }; @@ -143,6 +144,7 @@ const char *client_errors[]= "Attempt to read column without prior row fetch", "Prepared statement contains no metadata", "Attempt to read a row while there is no result set associated with the statement", + "This feature is not implemented yet", "" }; @@ -203,6 +205,7 @@ const char *client_errors[]= "Attempt to read column without prior row fetch", "Prepared statement contains no metadata", "Attempt to read a row while there is no result set associated with the statement", + "This feature is not implemented yet", "" }; #endif diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 463f9da6ad5..c87728ab783 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -1736,6 +1736,9 @@ myodbc_remove_escape(MYSQL *mysql,char *name) /******************* Declarations ***********************************/ +/* Default number of rows fetched per one COM_FETCH command. */ + +#define DEFAULT_PREFETCH_ROWS 1UL /* These functions are called by function pointer MYSQL_STMT::read_row_func. @@ -1761,6 +1764,7 @@ static my_bool setup_one_fetch_function(MYSQL_BIND *bind, MYSQL_FIELD *field); #define RESET_SERVER_SIDE 1 #define RESET_LONG_DATA 2 +#define RESET_STORE_RESULT 4 static my_bool reset_stmt_handle(MYSQL_STMT *stmt, uint flags); @@ -2001,6 +2005,7 @@ mysql_stmt_init(MYSQL *mysql) stmt->state= MYSQL_STMT_INIT_DONE; stmt->mysql= mysql; stmt->read_row_func= stmt_read_row_no_data; + stmt->prefetch_rows= DEFAULT_PREFETCH_ROWS; /* The rest of statement members was bzeroed inside malloc */ DBUG_RETURN(stmt); @@ -2059,7 +2064,7 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length) /* This is second prepare with another statement */ char buff[MYSQL_STMT_HEADER]; /* 4 bytes - stmt id */ - if (reset_stmt_handle(stmt, RESET_LONG_DATA)) + if (reset_stmt_handle(stmt, RESET_LONG_DATA | RESET_STORE_RESULT)) DBUG_RETURN(1); /* These members must be reset for API to @@ -2714,7 +2719,7 @@ stmt_read_row_from_cursor(MYSQL_STMT *stmt, unsigned char **row) result->rows= 0; /* Send row request to the server */ int4store(buff, stmt->stmt_id); - int4store(buff + 4, 1); /* number of rows to fetch */ + int4store(buff + 4, stmt->prefetch_rows); /* number of rows to fetch */ if (cli_advanced_command(mysql, COM_FETCH, buff, sizeof(buff), NullS, 0, 1)) { @@ -2772,12 +2777,29 @@ my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt, stmt->update_max_length= value ? *(const my_bool*) value : 0; break; case STMT_ATTR_CURSOR_TYPE: - stmt->flags= value ? *(const unsigned long *) value : 0; + { + ulong cursor_type; + cursor_type= value ? *(ulong*) value : 0UL; + if (cursor_type > (ulong) CURSOR_TYPE_READ_ONLY) + goto err_not_implemented; + stmt->flags= cursor_type; break; + } + case STMT_ATTR_PREFETCH_ROWS: + { + ulong prefetch_rows= value ? *(ulong*) value : DEFAULT_PREFETCH_ROWS; + if (value == 0) + return TRUE; + stmt->prefetch_rows= prefetch_rows; + break; + } default: - return TRUE; + goto err_not_implemented; } return FALSE; +err_not_implemented: + set_stmt_error(stmt, CR_NOT_IMPLEMENTED, unknown_sqlstate); + return TRUE; } @@ -2854,7 +2876,7 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) DBUG_RETURN(1); } - if (reset_stmt_handle(stmt, 0)) + if (reset_stmt_handle(stmt, RESET_STORE_RESULT)) DBUG_RETURN(1); /* No need to check for stmt->state: if the statement wasn't @@ -4862,7 +4884,11 @@ static my_bool reset_stmt_handle(MYSQL_STMT *stmt, uint flags) MYSQL_DATA *result= &stmt->result; my_bool has_cursor= stmt->read_row_func == stmt_read_row_from_cursor; - if (result->data) + /* + Reset stored result set if so was requested or it's a part + of cursor fetch. + */ + if (result->data && (has_cursor || (flags & RESET_STORE_RESULT))) { /* Result buffered */ free_root(&result->alloc, MYF(MY_KEEP_PREALLOC)); @@ -4921,7 +4947,7 @@ my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt) DBUG_ENTER("mysql_stmt_free_result"); /* Free the client side and close the server side cursor if there is one */ - DBUG_RETURN(reset_stmt_handle(stmt, RESET_LONG_DATA)); + DBUG_RETURN(reset_stmt_handle(stmt, RESET_LONG_DATA | RESET_STORE_RESULT)); } /******************************************************************** diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 4190b57418e..84be086ae37 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -1712,3 +1712,17 @@ a b 2 2 4 4 DROP VIEW v2,v1; +DROP TABLE t1, t2; +create table t1 (a int); +create view v1 as select sum(a) from t1 group by a; +create procedure p1() +begin +select * from v1; +end// +call p1(); +sum(a) +call p1(); +sum(a) +drop procedure p1; +drop view v1; +drop table t1; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 6f0cf59bb25..0477ab0ea20 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -1521,8 +1521,10 @@ SELECT a.col1,a.col2,b.col2,b.col3 DROP VIEW v1,v2,v3; DROP TABLE t1,t2; +# # BUG#8490 Select from views containing subqueries causes server to hang # forever. +# create table t1 as select 1 A union select 2 union select 3; create table t2 as select * from t1; create view v1 as select * from t1 where a in (select * from t2); @@ -1537,7 +1539,6 @@ drop table t1, t2, t3; # # Test case for bug #8528: select from view over multi-table view # - CREATE TABLE t1 (a int); CREATE TABLE t2 (b int); INSERT INTO t1 VALUES (1), (2), (3), (4); @@ -1549,3 +1550,22 @@ CREATE VIEW v2 AS SELECT * FROM v1; SELECT * FROM v2; DROP VIEW v2,v1; + +DROP TABLE t1, t2; +# +# Correct restoring view name in SP table locking BUG#9758 +# +create table t1 (a int); +create view v1 as select sum(a) from t1 group by a; +delimiter //; +create procedure p1() +begin +select * from v1; +end// +delimiter ;// +call p1(); +call p1(); +drop procedure p1; +drop view v1; +drop table t1; + diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 2ffcd45b1a7..2f65a446ae8 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2090,15 +2090,10 @@ sp_restore_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp) typedef struct st_sp_table { - LEX_STRING qname; - bool temp; - TABLE_LIST *table; - /* - We can't use table->lock_type as lock type for table - in multi-set since it can be changed by statement during - its execution (e.g. as this happens for multi-update). - */ - thr_lock_type lock_type; + LEX_STRING qname; /* Multi-set key: db_name\0table_name\0alias\0 */ + uint db_length, table_name_length; + bool temp; /* true if corresponds to a temporary table */ + thr_lock_type lock_type; /* lock type used for prelocking */ uint lock_count; uint query_lock_count; } SP_TABLE; @@ -2150,15 +2145,15 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check) for (; table ; table= table->next_global) if (!table->derived && !table->schema_table) { - char tname[64+1+64+1+64+1]; // db.table.alias\0 + char tname[(NAME_LEN + 1) * 3]; // db\0table\0alias\0 uint tlen, alen; tlen= table->db_length; memcpy(tname, table->db, tlen); - tname[tlen++]= '.'; + tname[tlen++]= '\0'; memcpy(tname+tlen, table->table_name, table->table_name_length); tlen+= table->table_name_length; - tname[tlen++]= '.'; + tname[tlen++]= '\0'; alen= strlen(table->alias); memcpy(tname+tlen, table->alias, alen); tlen+= alen; @@ -2181,14 +2176,15 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check) if (!(tab= (SP_TABLE *)thd->calloc(sizeof(SP_TABLE)))) return FALSE; tab->qname.length= tlen; - tab->qname.str= (char *)thd->strmake(tname, tab->qname.length); + tab->qname.str= (char*) thd->memdup(tname, tab->qname.length + 1); if (!tab->qname.str) return FALSE; if (lex_for_tmp_check->sql_command == SQLCOM_CREATE_TABLE && lex_for_tmp_check->query_tables == table && lex_for_tmp_check->create_info.options & HA_LEX_CREATE_TMP_TABLE) tab->temp= TRUE; - tab->table= table; + tab->table_name_length= table->table_name_length; + tab->db_length= table->db_length; tab->lock_type= table->lock_type; tab->lock_count= tab->query_lock_count= 1; my_hash_insert(&m_sptabs, (byte *)tab); @@ -2236,13 +2232,11 @@ sp_head::add_used_tables_to_table_list(THD *thd, for (i=0 ; i < m_sptabs.records ; i++) { char *tab_buff; - TABLE_LIST *table, *otable; + TABLE_LIST *table; SP_TABLE *stab= (SP_TABLE *)hash_element(&m_sptabs, i); if (stab->temp) continue; - otable= stab->table; - if (!(tab_buff= (char *)thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST)) * stab->lock_count))) DBUG_RETURN(FALSE); @@ -2257,11 +2251,11 @@ sp_head::add_used_tables_to_table_list(THD *thd, that the PS will be invalidated if the functions is deleted or changed. */ - table->db= otable->db; - table->db_length= otable->db_length; - table->alias= otable->alias; - table->table_name= otable->table_name; - table->table_name_length= otable->table_name_length; + table->db= stab->qname.str; + table->db_length= stab->db_length; + table->table_name= table->db + table->db_length + 1; + table->table_name_length= stab->table_name_length; + table->alias= table->table_name + table->table_name_length + 1; table->lock_type= stab->lock_type; table->cacheable_table= 1; table->prelocking_placeholder= 1; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 12e6daf1d63..7162d2b61c0 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -3309,7 +3309,7 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, 0) { my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), name->c_ptr(), - table->real_name); + table->s->table_name); map->set_all(); return 1; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 0c79864ed1a..a8f945e8dd6 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -3994,6 +3994,7 @@ select_options: YYABORT; } } + ; select_option_list: select_option_list select_option @@ -7454,8 +7455,8 @@ option_type_value: /* If we are in SP we want have own LEX for each assignment. This is mostly because it is hard for several sp_instr_set - and sp_instr_set_trigger instructions share one LEX. - (Well, it is theoretically possible but adds some extra + and sp_instr_set_trigger instructions share one LEX. + (Well, it is theoretically possible but adds some extra overhead on preparation for execution stage and IMO less robust). @@ -7464,7 +7465,7 @@ option_type_value: LEX *lex; Lex->sphead->reset_lex(YYTHD); lex= Lex; - + /* Set new LEX as if we at start of set rule. */ lex->sql_command= SQLCOM_SET_OPTION; mysql_init_select(lex); @@ -7477,11 +7478,11 @@ option_type_value: option_type option_value { LEX *lex= Lex; - + if (lex->sphead) { sp_head *sp= lex->sphead; - + if (!lex->var_list.is_empty()) { /* @@ -7491,19 +7492,19 @@ option_type_value: */ LEX_STRING qbuff; sp_instr_stmt *i; - + if (!(i= new sp_instr_stmt(sp->instructions(), lex->spcont, lex))) YYABORT; - + if (lex->ptr - lex->tok_end > 1) qbuff.length= lex->ptr - sp->m_tmp_query; else qbuff.length= lex->tok_end - sp->m_tmp_query; - + if (!(qbuff.str= alloc_root(YYTHD->mem_root, qbuff.length + 5))) YYABORT; - + strmake(strmake(qbuff.str, "SET ", 4), (char *)sp->m_tmp_query, qbuff.length); qbuff.length+= 4; diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 3cfed31c750..08ea5f1c1c9 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -13078,6 +13078,68 @@ static void test_bug9478() } +/* + Error message is returned for unsupported features. + Test also cursors with non-default PREFETCH_ROWS +*/ + +static void test_bug9643() +{ + MYSQL_STMT *stmt; + MYSQL_BIND bind[1]; + int32 a; + int rc; + const char *stmt_text; + int num_rows= 0; + ulong type; + ulong prefetch_rows= 5; + + myheader("test_bug9643"); + + mysql_query(mysql, "drop table if exists t1"); + mysql_query(mysql, "create table t1 (id integer not null primary key)"); + rc= mysql_query(mysql, "insert into t1 (id) values " + " (1), (2), (3), (4), (5), (6), (7), (8), (9)"); + myquery(rc); + + stmt= mysql_stmt_init(mysql); + /* Not implemented in 5.0 */ + type= (ulong) CURSOR_TYPE_SCROLLABLE; + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type); + DIE_UNLESS(rc); + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_stmt_error(stmt)); + + type= (ulong) CURSOR_TYPE_READ_ONLY; + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type); + check_execute(stmt, rc); + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_PREFETCH_ROWS, + (void*) &prefetch_rows); + check_execute(stmt, rc); + stmt_text= "select * from t1"; + rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text)); + check_execute(stmt, rc); + + bzero(bind, sizeof(bind)); + bind[0].buffer_type= MYSQL_TYPE_LONG; + bind[0].buffer= (void*) &a; + bind[0].buffer_length= sizeof(a); + mysql_stmt_bind_result(stmt, bind); + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + while ((rc= mysql_stmt_fetch(stmt)) == 0) + ++num_rows; + DIE_UNLESS(num_rows == 9); + + rc= mysql_stmt_close(stmt); + DIE_UNLESS(rc == 0); + + rc= mysql_query(mysql, "drop table t1"); + myquery(rc); +} + /* Read and parse arguments and MySQL options from my.cnf */ @@ -13309,6 +13371,7 @@ static struct my_tests_st my_tests[]= { { "test_bug9159", test_bug9159 }, { "test_bug9520", test_bug9520 }, { "test_bug9478", test_bug9478 }, + { "test_bug9643", test_bug9643 }, { 0, 0 } };