summaryrefslogtreecommitdiff
path: root/libmysql/libmysql.c
diff options
context:
space:
mode:
authorMichael Widenius <monty@mysql.com>2008-10-10 18:28:41 +0300
committerMichael Widenius <monty@mysql.com>2008-10-10 18:28:41 +0300
commitf47e003e1bfc56c2bf5d0f144a35517f526b538b (patch)
treee2bfb9834c6e558381465ed2f57a9d873a9b2c90 /libmysql/libmysql.c
parent51a92bbb03cc58ab8688fa9d8226afe32e6156ca (diff)
parent9daa56fd5ce3ccd33c32b5a505ac1d2b2c437460 (diff)
downloadmariadb-git-f47e003e1bfc56c2bf5d0f144a35517f526b538b.tar.gz
Merged 5.1 with maria 5.1
Diffstat (limited to 'libmysql/libmysql.c')
-rw-r--r--libmysql/libmysql.c131
1 files changed, 88 insertions, 43 deletions
diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c
index 98eb7156de0..c7d5997094a 100644
--- a/libmysql/libmysql.c
+++ b/libmysql/libmysql.c
@@ -1706,6 +1706,7 @@ static my_bool setup_one_fetch_function(MYSQL_BIND *, MYSQL_FIELD *field);
#define RESET_SERVER_SIDE 1
#define RESET_LONG_DATA 2
#define RESET_STORE_RESULT 4
+#define RESET_CLEAR_ERROR 8
static my_bool reset_stmt_handle(MYSQL_STMT *stmt, uint flags);
@@ -2090,7 +2091,7 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length)
To be removed when all commands will fully support prepared mode.
*/
-static unsigned int alloc_stmt_fields(MYSQL_STMT *stmt)
+static void alloc_stmt_fields(MYSQL_STMT *stmt)
{
MYSQL_FIELD *fields, *field, *end;
MEM_ROOT *alloc= &stmt->mem_root;
@@ -2108,7 +2109,10 @@ static unsigned int alloc_stmt_fields(MYSQL_STMT *stmt)
!(stmt->bind= (MYSQL_BIND *) alloc_root(alloc,
sizeof(MYSQL_BIND) *
stmt->field_count)))
- return 0;
+ {
+ set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate, NULL);
+ return;
+ }
for (fields= mysql->fields, end= fields+stmt->field_count,
field= stmt->fields;
@@ -2127,13 +2131,15 @@ static unsigned int alloc_stmt_fields(MYSQL_STMT *stmt)
field->def = fields->def ? strdup_root(alloc,fields->def): 0;
field->max_length= 0;
}
- return stmt->field_count;
}
-/*
+/**
Update result set columns metadata if it was sent again in
reply to COM_STMT_EXECUTE.
+
+ @note If the new field count is different from the original one,
+ an error is set and no update is performed.
*/
static void update_stmt_fields(MYSQL_STMT *stmt)
@@ -2143,7 +2149,22 @@ static void update_stmt_fields(MYSQL_STMT *stmt)
MYSQL_FIELD *stmt_field= stmt->fields;
MYSQL_BIND *my_bind= stmt->bind_result_done ? stmt->bind : 0;
- DBUG_ASSERT(stmt->field_count == stmt->mysql->field_count);
+ if (stmt->field_count != stmt->mysql->field_count)
+ {
+ /*
+ The tables used in the statement were altered,
+ and the query now returns a different number of columns.
+ There is no way to continue without reallocating the bind
+ array:
+ - if the number of columns increased, mysql_stmt_fetch()
+ will write beyond allocated memory
+ - if the number of columns decreased, some user-bound
+ buffers will be left unassigned without user knowing
+ that.
+ */
+ set_stmt_error(stmt, CR_NEW_STMT_METADATA, unknown_sqlstate, NULL);
+ return;
+ }
for (; field < field_end; ++field, ++stmt_field)
{
@@ -2792,6 +2813,50 @@ my_bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt,
}
+/**
+ Update statement result set metadata from with the new field
+ information sent during statement execute.
+
+ @pre mysql->field_count is not zero
+
+ @retval TRUE if error: out of memory or the new
+ result set has a different number of columns
+ @retval FALSE success
+*/
+
+static void reinit_result_set_metadata(MYSQL_STMT *stmt)
+{
+ /* Server has sent result set metadata */
+ if (stmt->field_count == 0)
+ {
+ /*
+ This is 'SHOW'/'EXPLAIN'-like query. Current implementation of
+ prepared statements can't send result set metadata for these queries
+ on prepare stage. Read it now.
+ */
+ alloc_stmt_fields(stmt);
+ }
+ else
+ {
+ /*
+ Update result set metadata if it for some reason changed between
+ prepare and execute, i.e.:
+ - in case of 'SELECT ?' we don't know column type unless data was
+ supplied to mysql_stmt_execute, so updated column type is sent
+ now.
+ - if data dictionary changed between prepare and execute, for
+ example a table used in the query was altered.
+ Note, that now (4.1.3) we always send metadata in reply to
+ COM_STMT_EXECUTE (even if it is not necessary), so either this or
+ previous branch always works.
+ TODO: send metadata only when it's really necessary and add a warning
+ 'Metadata changed' when it's sent twice.
+ */
+ update_stmt_fields(stmt);
+ }
+}
+
+
/*
Send placeholders data to server (if there are placeholders)
and execute prepared statement.
@@ -2847,7 +2912,7 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
DBUG_RETURN(1);
}
- if (reset_stmt_handle(stmt, RESET_STORE_RESULT))
+ if (reset_stmt_handle(stmt, RESET_STORE_RESULT | RESET_CLEAR_ERROR))
DBUG_RETURN(1);
/*
No need to check for stmt->state: if the statement wasn't
@@ -2855,40 +2920,10 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
*/
if (mysql->methods->stmt_execute(stmt))
DBUG_RETURN(1);
- if (mysql->field_count)
- {
- /* Server has sent result set metadata */
- if (stmt->field_count == 0)
- {
- /*
- This is 'SHOW'/'EXPLAIN'-like query. Current implementation of
- prepared statements can't send result set metadata for these queries
- on prepare stage. Read it now.
- */
- alloc_stmt_fields(stmt);
- }
- else
- {
- /*
- Update result set metadata if it for some reason changed between
- prepare and execute, i.e.:
- - in case of 'SELECT ?' we don't know column type unless data was
- supplied to mysql_stmt_execute, so updated column type is sent
- now.
- - if data dictionary changed between prepare and execute, for
- example a table used in the query was altered.
- Note, that now (4.1.3) we always send metadata in reply to
- COM_STMT_EXECUTE (even if it is not necessary), so either this or
- previous branch always works.
- TODO: send metadata only when it's really necessary and add a warning
- 'Metadata changed' when it's sent twice.
- */
- update_stmt_fields(stmt);
- }
- }
stmt->state= MYSQL_STMT_EXECUTE_DONE;
- if (stmt->field_count)
+ if (mysql->field_count)
{
+ reinit_result_set_metadata(stmt);
if (stmt->server_status & SERVER_STATUS_CURSOR_EXISTS)
{
mysql->status= MYSQL_STATUS_READY;
@@ -2903,7 +2938,7 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
network or b) is more efficient if all (few) result set rows are
precached on client and server's resources are freed.
*/
- DBUG_RETURN(mysql_stmt_store_result(stmt));
+ mysql_stmt_store_result(stmt);
}
else
{
@@ -2912,7 +2947,7 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
stmt->read_row_func= stmt_read_row_unbuffered;
}
}
- DBUG_RETURN(0);
+ DBUG_RETURN(test(stmt->last_errno));
}
@@ -4765,6 +4800,12 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt)
DBUG_RETURN(1);
}
+ if (stmt->last_errno)
+ {
+ /* An attempt to use an invalid statement handle. */
+ DBUG_RETURN(1);
+ }
+
if (mysql->status == MYSQL_STATUS_READY &&
stmt->server_status & SERVER_STATUS_CURSOR_EXISTS)
{
@@ -4972,9 +5013,10 @@ static my_bool reset_stmt_handle(MYSQL_STMT *stmt, uint flags)
stmt->state= MYSQL_STMT_INIT_DONE;
return 1;
}
- stmt_clear_error(stmt);
}
}
+ if (flags & RESET_CLEAR_ERROR)
+ stmt_clear_error(stmt);
stmt->state= MYSQL_STMT_PREPARE_DONE;
}
return 0;
@@ -4985,7 +5027,8 @@ 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 | RESET_STORE_RESULT));
+ DBUG_RETURN(reset_stmt_handle(stmt, RESET_LONG_DATA | RESET_STORE_RESULT |
+ RESET_CLEAR_ERROR));
}
/********************************************************************
@@ -5066,7 +5109,9 @@ my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt)
DBUG_RETURN(1);
}
/* Reset the client and server sides of the prepared statement */
- DBUG_RETURN(reset_stmt_handle(stmt, RESET_SERVER_SIDE | RESET_LONG_DATA));
+ DBUG_RETURN(reset_stmt_handle(stmt,
+ RESET_SERVER_SIDE | RESET_LONG_DATA |
+ RESET_CLEAR_ERROR));
}
/*