summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <sergefp@mysql.com>2004-06-03 20:54:46 +0400
committerunknown <sergefp@mysql.com>2004-06-03 20:54:46 +0400
commitf0beff16f37c2adee70d8226190b4c529e8b14b1 (patch)
tree544da240bba2e15bdf1323c90a3d28af7564155d /sql
parent0f7a9f61023d97b60286ed7b72f27a9fb343f083 (diff)
parent1ed53ce4c62dafb47a5e3bc5bc0f2eab8aeabc38 (diff)
downloadmariadb-git-f0beff16f37c2adee70d8226190b4c529e8b14b1.tar.gz
Merge spetrunia@bk-internal.mysql.com:/home/bk/mysql-4.1
into mysql.com:/dbdata/psergey/mysql-4.1-ps-merge sql/mysql_priv.h: Auto merged sql/mysqld.cc: Auto merged sql/sql_class.cc: Auto merged sql/sql_class.h: Auto merged sql/sql_parse.cc: Auto merged sql/share/romanian/errmsg.txt: Auto merged
Diffstat (limited to 'sql')
-rw-r--r--sql/item.cc4
-rw-r--r--sql/item.h2
-rw-r--r--sql/item_func.cc78
-rw-r--r--sql/lex.h2
-rw-r--r--sql/mysql_priv.h7
-rw-r--r--sql/mysqld.cc6
-rw-r--r--sql/share/czech/errmsg.txt2
-rw-r--r--sql/share/dutch/errmsg.txt2
-rw-r--r--sql/share/english/errmsg.txt2
-rw-r--r--sql/share/estonian/errmsg.txt2
-rw-r--r--sql/share/french/errmsg.txt2
-rw-r--r--sql/share/german/errmsg.txt2
-rw-r--r--sql/share/greek/errmsg.txt2
-rw-r--r--sql/share/hungarian/errmsg.txt2
-rw-r--r--sql/share/italian/errmsg.txt2
-rw-r--r--sql/share/japanese/errmsg.txt2
-rw-r--r--sql/share/korean/errmsg.txt2
-rw-r--r--sql/share/norwegian-ny/errmsg.txt2
-rw-r--r--sql/share/norwegian/errmsg.txt2
-rw-r--r--sql/share/polish/errmsg.txt2
-rw-r--r--sql/share/portuguese/errmsg.txt2
-rw-r--r--sql/share/romanian/errmsg.txt2
-rw-r--r--sql/share/russian/errmsg.txt2
-rw-r--r--sql/share/slovak/errmsg.txt2
-rw-r--r--sql/share/spanish/errmsg.txt2
-rw-r--r--sql/share/swedish/errmsg.txt2
-rw-r--r--sql/share/ukrainian/errmsg.txt2
-rw-r--r--sql/sql_class.cc43
-rw-r--r--sql/sql_class.h38
-rw-r--r--sql/sql_lex.h12
-rw-r--r--sql/sql_parse.cc124
-rw-r--r--sql/sql_prepare.cc480
-rw-r--r--sql/sql_yacc.yy88
33 files changed, 786 insertions, 140 deletions
diff --git a/sql/item.cc b/sql/item.cc
index 7db1a448e55..ad209817d8a 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -255,7 +255,7 @@ bool Item::get_time(TIME *ltime)
return 0;
}
-CHARSET_INFO * Item::default_charset() const
+CHARSET_INFO * Item::default_charset()
{
return current_thd->variables.collation_connection;
}
@@ -1666,7 +1666,7 @@ bool Item::send(Protocol *protocol, String *buffer)
}
case MYSQL_TYPE_TINY:
{
- longlong nr;
+ longlong nr;
nr= val_int();
if (!null_value)
result= protocol->store_tiny(nr);
diff --git a/sql/item.h b/sql/item.h
index 885a34dce81..571eaccd33b 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -245,7 +245,7 @@ public:
virtual Item *real_item() { return this; }
virtual Item *get_tmp_table_item(THD *thd) { return copy_or_same(thd); }
- CHARSET_INFO *default_charset() const;
+ static CHARSET_INFO *default_charset();
virtual CHARSET_INFO *compare_collation() { return NULL; }
virtual bool walk(Item_processor processor, byte *arg)
diff --git a/sql/item_func.cc b/sql/item_func.cc
index f221e0dcc5c..2fc1f68b49c 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -2583,27 +2583,39 @@ longlong Item_func_get_user_var::val_int()
/*
+ Get variable by name and, if necessary, put the record of variable
+ use into the binary log.
+
+ SYNOPSIS
+ get_var_with_binlog()
+ thd Current thread
+ name Variable name
+ out_entry [out] variable structure or NULL. The pointer is set
+ regardless of whether function succeeded or not.
+
When a user variable is invoked from an update query (INSERT, UPDATE etc),
stores this variable and its value in thd->user_var_events, so that it can be
written to the binlog (will be written just before the query is written, see
log.cc).
+
+ RETURN
+ 0 OK
+ 1 Failed to put appropiate record into binary log
+
*/
-void Item_func_get_user_var::fix_length_and_dec()
+int get_var_with_binlog(THD *thd, LEX_STRING &name,
+ user_var_entry **out_entry)
{
- THD *thd=current_thd;
BINLOG_USER_VAR_EVENT *user_var_event;
- maybe_null=1;
- decimals=NOT_FIXED_DEC;
- max_length=MAX_BLOB_WIDTH;
-
- if (!(var_entry= get_variable(&thd->user_vars, name, 0)))
- null_value= 1;
- else
- collation.set(var_entry->collation);
+ user_var_entry *var_entry;
+ var_entry= get_variable(&thd->user_vars, name, 0);
if (!(opt_bin_log && is_update_query(thd->lex->sql_command)))
- return;
+ {
+ *out_entry= var_entry;
+ return 0;
+ }
if (!var_entry)
{
@@ -2630,13 +2642,16 @@ void Item_func_get_user_var::fix_length_and_dec()
if (!(var_entry= get_variable(&thd->user_vars, name, 0)))
goto err;
}
- /*
- If this variable was already stored in user_var_events by this query
- (because it's used in more than one place in the query), don't store
- it.
- */
else if (var_entry->used_query_id == thd->query_id)
- return;
+ {
+ /*
+ If this variable was already stored in user_var_events by this query
+ (because it's used in more than one place in the query), don't store
+ it.
+ */
+ *out_entry= var_entry;
+ return 0;
+ }
uint size;
/*
@@ -2671,11 +2686,34 @@ void Item_func_get_user_var::fix_length_and_dec()
var_entry->used_query_id= thd->query_id;
if (insert_dynamic(&thd->user_var_events, (gptr) &user_var_event))
goto err;
-
- return;
+
+ *out_entry= var_entry;
+ return 0;
err:
- thd->fatal_error();
+ *out_entry= var_entry;
+ return 1;
+}
+
+
+void Item_func_get_user_var::fix_length_and_dec()
+{
+ THD *thd=current_thd;
+ int error;
+ maybe_null=1;
+ decimals=NOT_FIXED_DEC;
+ max_length=MAX_BLOB_WIDTH;
+
+ error= get_var_with_binlog(thd, name, &var_entry);
+
+ if (!var_entry)
+ null_value= 1;
+ else
+ collation.set(var_entry->collation);
+
+ if (error)
+ thd->fatal_error();
+
return;
}
diff --git a/sql/lex.h b/sql/lex.h
index e5bc537c213..64cc6b57464 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -133,6 +133,7 @@ static SYMBOL symbols[] = {
{ "DAY_MICROSECOND", SYM(DAY_MICROSECOND_SYM)},
{ "DAY_MINUTE", SYM(DAY_MINUTE_SYM)},
{ "DAY_SECOND", SYM(DAY_SECOND_SYM)},
+ { "DEALLOCATE", SYM(DEALLOCATE_SYM)},
{ "DEC", SYM(DECIMAL_SYM)},
{ "DECIMAL", SYM(DECIMAL_SYM)},
{ "DEFAULT", SYM(DEFAULT)},
@@ -324,6 +325,7 @@ static SYMBOL symbols[] = {
{ "POINT", SYM(POINT_SYM)},
{ "POLYGON", SYM(POLYGON)},
{ "PRECISION", SYM(PRECISION)},
+ { "PREPARE", SYM(PREPARE_SYM)},
{ "PREV", SYM(PREV_SYM)},
{ "PRIMARY", SYM(PRIMARY_SYM)},
{ "PRIVILEGES", SYM(PRIVILEGES)},
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 368774877d0..81dce036ccd 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -639,8 +639,10 @@ int mysqld_show_column_types(THD *thd);
int mysqld_help (THD *thd, const char *text);
/* sql_prepare.cc */
-void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length);
+int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
+ LEX_STRING *name=NULL);
void mysql_stmt_execute(THD *thd, char *packet, uint packet_length);
+void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name);
void mysql_stmt_free(THD *thd, char *packet);
void mysql_stmt_reset(THD *thd, char *packet);
void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
@@ -1068,6 +1070,9 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
LEX_STRING component);
Item *get_system_var(THD *thd, enum_var_type var_type, const char *var_name,
uint length, const char *item_name);
+/* item_func.cc */
+int get_var_with_binlog(THD *thd, LEX_STRING &name,
+ user_var_entry **out_entry);
/* log.cc */
bool flush_error_log(void);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 4f70c72f8ec..55896dc0e14 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -5017,6 +5017,12 @@ struct show_var_st status_vars[]= {
{"Com_unlock_tables", (char*) (com_stat+(uint) SQLCOM_UNLOCK_TABLES),SHOW_LONG},
{"Com_update", (char*) (com_stat+(uint) SQLCOM_UPDATE),SHOW_LONG},
{"Com_update_multi", (char*) (com_stat+(uint) SQLCOM_UPDATE_MULTI),SHOW_LONG},
+ {"Com_prepare_sql", (char*) (com_stat+(uint) SQLCOM_PREPARE),
+ SHOW_LONG},
+ {"Com_execute_sql", (char*) (com_stat+(uint) SQLCOM_EXECUTE),
+ SHOW_LONG},
+ {"Com_dealloc_sql", (char*) (com_stat+(uint)
+ SQLCOM_DEALLOCATE_PREPARE), SHOW_LONG},
{"Connections", (char*) &thread_id, SHOW_LONG_CONST},
{"Created_tmp_disk_tables", (char*) &created_tmp_disk_tables,SHOW_LONG},
{"Created_tmp_files", (char*) &my_tmp_file_created, SHOW_LONG},
diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt
index 20a162a8080..b117587eb9c 100644
--- a/sql/share/czech/errmsg.txt
+++ b/sql/share/czech/errmsg.txt
@@ -255,7 +255,7 @@ character-set=latin2
"Key reference and table reference doesn't match",
"Operand should contain %d column(s)",
"Subquery returns more than 1 row",
-"Unknown prepared statement handler (%ld) given to %s",
+"Unknown prepared statement handler (%.*s) given to %s",
"Help database is corrupt or does not exist",
"Cyclic reference on subqueries",
"Converting column '%s' from %s to %s",
diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt
index 6b318256cd8..f49a6130299 100644
--- a/sql/share/dutch/errmsg.txt
+++ b/sql/share/dutch/errmsg.txt
@@ -257,7 +257,7 @@ character-set=latin1
"Key reference and table reference doesn't match",
"Operand should contain %d column(s)",
"Subquery returns more than 1 row",
-"Unknown prepared statement handler (%ld) given to %s",
+"Unknown prepared statement handler (%.*s) given to %s",
"Help database is corrupt or does not exist",
"Cyclic reference on subqueries",
"Converting column '%s' from %s to %s",
diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt
index 61558f7fae5..59011c802b6 100644
--- a/sql/share/english/errmsg.txt
+++ b/sql/share/english/errmsg.txt
@@ -246,7 +246,7 @@ character-set=latin1
"Key reference and table reference doesn't match",
"Operand should contain %d column(s)",
"Subquery returns more than 1 row",
-"Unknown prepared statement handler (%ld) given to %s",
+"Unknown prepared statement handler (%.*s) given to %s",
"Help database is corrupt or does not exist",
"Cyclic reference on subqueries",
"Converting column '%s' from %s to %s",
diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt
index 28da38a3691..122d8f990ab 100644
--- a/sql/share/estonian/errmsg.txt
+++ b/sql/share/estonian/errmsg.txt
@@ -251,7 +251,7 @@ character-set=latin7
"Key reference and table reference doesn't match",
"Operand should contain %d column(s)",
"Subquery returns more than 1 row",
-"Unknown prepared statement handler (%ld) given to %s",
+"Unknown prepared statement handler (%.*s) given to %s",
"Help database is corrupt or does not exist",
"Cyclic reference on subqueries",
"Converting column '%s' from %s to %s",
diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt
index 697cf5f7233..200edc44cd2 100644
--- a/sql/share/french/errmsg.txt
+++ b/sql/share/french/errmsg.txt
@@ -246,7 +246,7 @@ character-set=latin1
"Key reference and table reference doesn't match",
"Operand should contain %d column(s)",
"Subquery returns more than 1 row",
-"Unknown prepared statement handler (%ld) given to %s",
+"Unknown prepared statement handler (%.*s) given to %s",
"Help database is corrupt or does not exist",
"Cyclic reference on subqueries",
"Converting column '%s' from %s to %s",
diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt
index 3dc6aa34b5e..f33810c5496 100644
--- a/sql/share/german/errmsg.txt
+++ b/sql/share/german/errmsg.txt
@@ -258,7 +258,7 @@ character-set=latin1
"Schlüssel- und Tabellenverweis passen nicht zusammen",
"Operand solle %d Spalte(n) enthalten",
"Unterabfrage lieferte mehr als einen Datensatz zurück",
-"Unbekannter Prepared-Statement-Handler (%ld) für %s angegeben",
+"Unbekannter Prepared-Statement-Handler (%.*s) für %s angegeben",
"Die Hilfe-Datenbank ist beschädigt oder existiert nicht",
"Zyklischer Verweis in Unterabfragen",
"Spalte '%s' wird von %s nach %s umgewandelt",
diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt
index c5f122a2a49..2d742551b59 100644
--- a/sql/share/greek/errmsg.txt
+++ b/sql/share/greek/errmsg.txt
@@ -246,7 +246,7 @@ character-set=greek
"Key reference and table reference doesn't match",
"Operand should contain %d column(s)",
"Subquery returns more than 1 row",
-"Unknown prepared statement handler (%ld) given to %s",
+"Unknown prepared statement handler (%.*s) given to %s",
"Help database is corrupt or does not exist",
"Cyclic reference on subqueries",
"Converting column '%s' from %s to %s",
diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt
index e83f4ca5ca3..0b1a6fe2777 100644
--- a/sql/share/hungarian/errmsg.txt
+++ b/sql/share/hungarian/errmsg.txt
@@ -248,7 +248,7 @@ character-set=latin2
"Key reference and table reference doesn't match",
"Operand should contain %d column(s)",
"Subquery returns more than 1 row",
-"Unknown prepared statement handler (%ld) given to %s",
+"Unknown prepared statement handler (%.*s) given to %s",
"Help database is corrupt or does not exist",
"Cyclic reference on subqueries",
"Converting column '%s' from %s to %s",
diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt
index 294ff333e66..0f79ec8c953 100644
--- a/sql/share/italian/errmsg.txt
+++ b/sql/share/italian/errmsg.txt
@@ -246,7 +246,7 @@ character-set=latin1
"Key reference and table reference doesn't match",
"Operand should contain %d column(s)",
"Subquery returns more than 1 row",
-"Unknown prepared statement handler (%ld) given to %s",
+"Unknown prepared statement handler (%.*s) given to %s",
"Help database is corrupt or does not exist",
"Cyclic reference on subqueries",
"Converting column '%s' from %s to %s",
diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt
index 7969c3b35b8..375efc6dea0 100644
--- a/sql/share/japanese/errmsg.txt
+++ b/sql/share/japanese/errmsg.txt
@@ -248,7 +248,7 @@ character-set=ujis
"Key reference and table reference doesn't match",
"Operand should contain %d column(s)",
"Subquery returns more than 1 row",
-"Unknown prepared statement handler (%ld) given to %s",
+"Unknown prepared statement handler (%.*s) given to %s",
"Help database is corrupt or does not exist",
"Cyclic reference on subqueries",
"Converting column '%s' from %s to %s",
diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt
index 132d4f121b2..7bbed0e9b0c 100644
--- a/sql/share/korean/errmsg.txt
+++ b/sql/share/korean/errmsg.txt
@@ -246,7 +246,7 @@ character-set=euckr
"Key reference and table reference doesn't match",
"Operand should contain %d column(s)",
"Subquery returns more than 1 row",
-"Unknown prepared statement handler (%ld) given to %s",
+"Unknown prepared statement handler (%.*s) given to %s",
"Help database is corrupt or does not exist",
"Cyclic reference on subqueries",
"Converting column '%s' from %s to %s",
diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt
index 0416c4be926..8df547d38fe 100644
--- a/sql/share/norwegian-ny/errmsg.txt
+++ b/sql/share/norwegian-ny/errmsg.txt
@@ -248,7 +248,7 @@ character-set=latin1
"Key reference and table reference doesn't match",
"Operand should contain %d column(s)",
"Subquery returns more than 1 row",
-"Unknown prepared statement handler (%ld) given to %s",
+"Unknown prepared statement handler (%.*s) given to %s",
"Help database is corrupt or does not exist",
"Cyclic reference on subqueries",
"Converting column '%s' from %s to %s",
diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt
index ef6c2ee05b2..262e67f4a6e 100644
--- a/sql/share/norwegian/errmsg.txt
+++ b/sql/share/norwegian/errmsg.txt
@@ -248,7 +248,7 @@ character-set=latin1
"Key reference and table reference doesn't match",
"Operand should contain %d column(s)",
"Subquery returns more than 1 row",
-"Unknown prepared statement handler (%ld) given to %s",
+"Unknown prepared statement handler (%.*s) given to %s",
"Help database is corrupt or does not exist",
"Cyclic reference on subqueries",
"Converting column '%s' from %s to %s",
diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt
index 348c9a07b8c..4c11ea0e73b 100644
--- a/sql/share/polish/errmsg.txt
+++ b/sql/share/polish/errmsg.txt
@@ -250,7 +250,7 @@ character-set=latin2
"Key reference and table reference doesn't match",
"Operand should contain %d column(s)",
"Subquery returns more than 1 row",
-"Unknown prepared statement handler (%ld) given to %s",
+"Unknown prepared statement handler (%.*s) given to %s",
"Help database is corrupt or does not exist",
"Cyclic reference on subqueries",
"Converting column '%s' from %s to %s",
diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt
index 3fe753a71cc..2175bee9474 100644
--- a/sql/share/portuguese/errmsg.txt
+++ b/sql/share/portuguese/errmsg.txt
@@ -247,7 +247,7 @@ character-set=latin1
"Referência da chave e referência da tabela não coincidem",
"Operand should contain %d column(s)",
"Subconsulta retorna mais que 1 registro",
-"Desconhecido manipulador de declaração preparado (%ld) determinado para %s",
+"Desconhecido manipulador de declaração preparado (%.*s) determinado para %s",
"Banco de dado de ajuda corrupto ou não existente",
"Referência cíclica em subconsultas",
"Convertendo coluna '%s' de %s para %s",
diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt
index b013aa1f109..78539c22d99 100644
--- a/sql/share/romanian/errmsg.txt
+++ b/sql/share/romanian/errmsg.txt
@@ -250,7 +250,7 @@ character-set=latin2
"Key reference and table reference doesn't match",
"Operand should contain %d column(s)",
"Subquery returns more than 1 row",
-"Unknown prepared statement handler (%ld) given to %s",
+"Unknown prepared statement handler (%.*s) given to %s",
"Help database is corrupt or does not exist",
"Cyclic reference on subqueries",
"Converting column '%s' from %s to %s",
diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt
index 8ed8aa03efb..7d7a1fd4a20 100644
--- a/sql/share/russian/errmsg.txt
+++ b/sql/share/russian/errmsg.txt
@@ -248,7 +248,7 @@ character-set=koi8r
"Key reference and table reference doesn't match",
"ïÐÅÒÁÎÄ ÄÏÌÖÅÎ ÓÏÄÅÒÖÁÔØ %d ËÏÌÏÎÏË",
"ðÏÄÚÁÐÒÏÓ ×ÏÚ×ÒÁÝÁÅÔ ÂÏÌÅÅ ÏÄÎÏÊ ÚÁÐÉÓÉ",
-"Unknown prepared statement handler (%ld) given to %s",
+"Unknown prepared statement handler (%.*s) given to %s",
"Help database is corrupt or does not exist",
"ãÉËÌÉÞÅÓËÁÑ ÓÓÙÌËÁ ÎÁ ÐÏÄÚÁÐÒÏÓ",
"ðÒÅÏÂÒÁÚÏ×ÁÎÉÅ ÐÏÌÑ '%s' ÉÚ %s × %s",
diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt
index 274f5ffa428..9160d2f7a91 100644
--- a/sql/share/slovak/errmsg.txt
+++ b/sql/share/slovak/errmsg.txt
@@ -254,7 +254,7 @@ character-set=latin2
"Key reference and table reference doesn't match",
"Operand should contain %d column(s)",
"Subquery returns more than 1 row",
-"Unknown prepared statement handler (%ld) given to %s",
+"Unknown prepared statement handler (%.*s) given to %s",
"Help database is corrupt or does not exist",
"Cyclic reference on subqueries",
"Converting column '%s' from %s to %s",
diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt
index 5e3007d2b4e..5f2435feb1c 100644
--- a/sql/share/spanish/errmsg.txt
+++ b/sql/share/spanish/errmsg.txt
@@ -248,7 +248,7 @@ character-set=latin1
"Referencia de llave y referencia de tabla no coinciden",
"Operando debe tener %d columna(s)",
"Subconsulta retorna mas que 1 línea",
-"Desconocido preparado comando handler (%ld) dado para %s",
+"Desconocido preparado comando handler (%.*s) dado para %s",
"Base de datos Help está corrupto o no existe",
"Cíclica referencia en subconsultas",
"Convirtiendo columna '%s' de %s para %s",
diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt
index 516e53fe34d..f045bae4cf6 100644
--- a/sql/share/swedish/errmsg.txt
+++ b/sql/share/swedish/errmsg.txt
@@ -246,7 +246,7 @@ character-set=latin1
"Nyckelreferensen och tabellreferensen stämmer inte överens",
"Operand should contain %d column(s)",
"Subquery returnerade mer än 1 rad",
-"Okänd PREPARED STATEMENT id (%ld) var given till %s",
+"Okänd PREPARED STATEMENT id (%.*s) var given till %s",
"Hjälpdatabasen finns inte eller är skadad",
"Cyklisk referens i subqueries",
"Konvertar kolumn '%s' från %s till %s",
diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt
index cf81afa8a5b..47b003894bd 100644
--- a/sql/share/ukrainian/errmsg.txt
+++ b/sql/share/ukrainian/errmsg.txt
@@ -251,7 +251,7 @@ character-set=koi8u
"Key reference and table reference doesn't match",
"ïÐÅÒÁÎÄ ÍÁ¤ ÓËÌÁÄÁÔÉÓÑ Ú %d ÓÔÏ×Âæ×",
"ð¦ÄÚÁÐÉÔ ÐÏ×ÅÒÔÁ¤ ¦ÌØÛ ÎiÖ 1 ÚÁÐÉÓ",
-"Unknown prepared statement handler (%ld) given to %s",
+"Unknown prepared statement handler (%.*s) given to %s",
"Help database is corrupt or does not exist",
"ãÉË̦ÞÎÅ ÐÏÓÉÌÁÎÎÑ ÎÁ ЦÄÚÁÐÉÔ",
"ðÅÒÅÔ×ÏÒÅÎÎÑ ÓÔÏ×ÂÃÁ '%s' Ú %s Õ %s",
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index d16d1de7607..d8cb5351b66 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -78,7 +78,6 @@ extern "C" void free_user_var(user_var_entry *entry)
my_free((char*) entry,MYF(0));
}
-
bool key_part_spec::operator==(const key_part_spec& other) const
{
return length == other.length && !strcmp(field_name, other.field_name);
@@ -233,7 +232,7 @@ THD::THD():user_time(0), current_statement(0), is_fatal_error(0),
16);
else
bzero((char*) &user_var_events, sizeof(user_var_events));
-
+
/* Protocol */
protocol= &protocol_simple; // Default protocol
protocol_simple.init(this);
@@ -1301,6 +1300,7 @@ Statement::Statement(THD *thd)
query_length(0),
free_list(0)
{
+ name.str= NULL;
init_sql_alloc(&mem_root,
thd->variables.query_alloc_block_size,
thd->variables.query_prealloc_size);
@@ -1384,17 +1384,52 @@ static void delete_statement_as_hash_key(void *key)
delete (Statement *) key;
}
+static byte *get_stmt_name_hash_key(Statement *entry, uint *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length=(uint) entry->name.length;
+ return (byte*) entry->name.str;
+}
+
C_MODE_END
Statement_map::Statement_map() :
last_found_statement(0)
{
- enum { START_HASH_SIZE = 16 };
- hash_init(&st_hash, default_charset_info, START_HASH_SIZE, 0, 0,
+ enum
+ {
+ START_STMT_HASH_SIZE = 16,
+ START_NAME_HASH_SIZE = 16
+ };
+ hash_init(&st_hash, default_charset_info, START_STMT_HASH_SIZE, 0, 0,
get_statement_id_as_hash_key,
delete_statement_as_hash_key, MYF(0));
+ hash_init(&names_hash, &my_charset_bin, START_NAME_HASH_SIZE, 0, 0,
+ (hash_get_key) get_stmt_name_hash_key,
+ NULL,MYF(0));
}
+int Statement_map::insert(Statement *statement)
+{
+ int rc= my_hash_insert(&st_hash, (byte *) statement);
+ if (rc == 0)
+ last_found_statement= statement;
+ if (statement->name.str)
+ {
+ /*
+ If there is a statement with the same name, remove it. It is ok to
+ remove old and fail to insert new one at the same time.
+ */
+ Statement *old_stmt;
+ if ((old_stmt= find_by_name(&statement->name)))
+ erase(old_stmt);
+ if ((rc= my_hash_insert(&names_hash, (byte*)statement)))
+ hash_delete(&st_hash, (byte*)statement);
+ }
+ return rc;
+}
+
+
bool select_dumpvar::send_data(List<Item> &items)
{
List_iterator_fast<Item_func_set_user_var> li(vars);
diff --git a/sql/sql_class.h b/sql/sql_class.h
index a3e398dc1d6..ac662da2ff8 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -461,6 +461,7 @@ public:
*/
bool allow_sum_func;
+ LEX_STRING name; /* name for named prepared statements */
LEX *lex; // parse tree descriptor
/*
Points to the query associated with this statement. It's const, but
@@ -527,8 +528,14 @@ public:
/*
- Used to seek all existing statements in the connection
- Deletes all statements in destructor.
+ Container for all statements created/used in a connection.
+ Statements in Statement_map have unique Statement::id (guaranteed by id
+ assignment in Statement::Statement)
+ Non-empty statement names are unique too: attempt to insert a new statement
+ with duplicate name causes older statement to be deleted
+
+ Statements are auto-deleted when they are removed from the map and when the
+ map is deleted.
*/
class Statement_map
@@ -536,34 +543,47 @@ class Statement_map
public:
Statement_map();
- int insert(Statement *statement)
+ int insert(Statement *statement);
+
+ Statement *find_by_name(LEX_STRING *name)
{
- int rc= my_hash_insert(&st_hash, (byte *) statement);
- if (rc == 0)
- last_found_statement= statement;
- return rc;
+ Statement *stmt;
+ stmt= (Statement*)hash_search(&names_hash, (byte*)name->str,
+ name->length);
+ return stmt;
}
Statement *find(ulong id)
{
if (last_found_statement == 0 || id != last_found_statement->id)
- last_found_statement= (Statement *) hash_search(&st_hash, (byte *) &id,
- sizeof(id));
+ {
+ Statement *stmt;
+ stmt= (Statement *) hash_search(&st_hash, (byte *) &id, sizeof(id));
+ if (stmt->name.str)
+ return NULL;
+ last_found_statement= stmt;
+ }
return last_found_statement;
}
void erase(Statement *statement)
{
if (statement == last_found_statement)
last_found_statement= 0;
+ if (statement->name.str)
+ {
+ hash_delete(&names_hash, (byte *) statement);
+ }
hash_delete(&st_hash, (byte *) statement);
}
~Statement_map()
{
hash_free(&st_hash);
+ hash_free(&names_hash);
}
private:
HASH st_hash;
+ HASH names_hash;
Statement *last_found_statement;
};
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 92ed53cf814..b2211fe1b0e 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -76,6 +76,7 @@ enum enum_sql_command {
SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES,
SQLCOM_HELP, SQLCOM_DROP_USER, SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM,
+ SQLCOM_PREPARE, SQLCOM_EXECUTE, SQLCOM_DEALLOCATE_PREPARE,
/* This should be the last !!! */
SQLCOM_END
};
@@ -616,6 +617,17 @@ typedef struct st_lex
bool derived_tables;
bool safe_to_cache_query;
ALTER_INFO alter_info;
+ /* Prepared statements SQL syntax:*/
+ LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */
+ /*
+ Prepared statement query text or name of variable that holds the
+ prepared statement (in PREPARE ... queries)
+ */
+ LEX_STRING prepared_stmt_code;
+ /* If true, prepared_stmt_code is a name of variable that holds the query */
+ bool prepared_stmt_code_is_varref;
+ /* Names of user variables holding parameters (in EXECUTE) */
+ List<LEX_STRING> prepared_stmt_params;
st_lex() {}
inline void uncacheable(uint8 cause)
{
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index a7ecda72905..3917f2f1b4f 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1424,7 +1424,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
}
case COM_EXECUTE:
{
- thd->free_list= NULL;
mysql_stmt_execute(thd, packet, packet_length);
break;
}
@@ -1976,7 +1975,126 @@ mysql_execute_command(THD *thd)
}
break;
}
-
+ case SQLCOM_PREPARE:
+ {
+ char *query_str;
+ uint query_len;
+ if (lex->prepared_stmt_code_is_varref)
+ {
+ /* This is PREPARE stmt FROM @var. */
+ String str;
+ CHARSET_INFO *to_cs= thd->variables.collation_connection;
+ CHARSET_INFO *from_cs;
+ const char *buf;
+ uint buf_len;
+ bool need_conversion;
+ LINT_INIT(from_cs); /* protected by need_conversion */
+ user_var_entry *entry;
+ uint32 unused;
+ /*
+ Convert @var contents to string in connection character set. Although
+ it is known that int/real/NULL value cannot be a valid query we still
+ convert it for error messages to uniform.
+ */
+ if ((entry=
+ (user_var_entry*)hash_search(&thd->user_vars,
+ (byte*)lex->prepared_stmt_code.str,
+ lex->prepared_stmt_code.length))
+ && entry->value)
+ {
+ switch (entry->type)
+ {
+ case REAL_RESULT:
+ str.set(*(double*)entry->value, NOT_FIXED_DEC, to_cs);
+ buf_len= str.length();
+ buf= str.ptr();
+ need_conversion= false;
+ break;
+ case INT_RESULT:
+ str.set(*(longlong*)entry->value, to_cs);
+ buf_len= str.length();
+ buf= str.ptr();
+ need_conversion= false;
+ break;
+ case STRING_RESULT:
+ buf_len= entry->length;
+ buf= entry->value;
+ from_cs = entry->collation.collation;
+ need_conversion= String::needs_conversion(entry->length, from_cs,
+ to_cs, &unused);
+ break;
+ default:
+ buf= "";
+ need_conversion= false;
+ buf_len= 0;
+ DBUG_ASSERT(0);
+ }
+ }
+ else
+ {
+ from_cs= &my_charset_bin;
+ str.set("NULL", 4, from_cs);
+ buf= str.ptr();
+ buf_len= str.length();
+ need_conversion= String::needs_conversion(str.length(), from_cs,
+ to_cs, &unused);
+ }
+
+ query_len = need_conversion? (buf_len * to_cs->mbmaxlen) : buf_len;
+ if (!(query_str= alloc_root(&thd->mem_root, query_len+1)))
+ send_error(thd, ER_OUT_OF_RESOURCES);
+
+ if (need_conversion)
+ query_len= copy_and_convert(query_str, query_len, to_cs, buf, buf_len,
+ from_cs);
+ else
+ memcpy(query_str, buf, query_len);
+ query_str[query_len]= 0;
+ }
+ else
+ {
+ query_str= lex->prepared_stmt_code.str;
+ query_len= lex->prepared_stmt_code.length;
+ DBUG_PRINT("info", ("PREPARE: %.*s FROM '%.*s' \n",
+ lex->prepared_stmt_name.length,
+ lex->prepared_stmt_name.str,
+ query_len, query_str));
+ }
+ thd->command= COM_PREPARE;
+ if (!mysql_stmt_prepare(thd, query_str, query_len + 1,
+ &lex->prepared_stmt_name))
+ send_ok(thd, 0L, 0L, "Statement prepared");
+ break;
+ }
+ case SQLCOM_EXECUTE:
+ {
+ DBUG_PRINT("info", ("EXECUTE: %.*s\n",
+ lex->prepared_stmt_name.length,
+ lex->prepared_stmt_name.str));
+ mysql_sql_stmt_execute(thd, &lex->prepared_stmt_name);
+ lex->prepared_stmt_params.empty();
+ break;
+ }
+ case SQLCOM_DEALLOCATE_PREPARE:
+ {
+ Statement* stmt;
+ DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n",
+ lex->prepared_stmt_name.length,
+ lex->prepared_stmt_name.str));
+ if ((stmt= thd->stmt_map.find_by_name(&lex->prepared_stmt_name)))
+ {
+ thd->stmt_map.erase(stmt);
+ send_ok(thd);
+ }
+ else
+ {
+ res= -1;
+ my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0),
+ lex->prepared_stmt_name.length, lex->prepared_stmt_name.str,
+ "DEALLOCATE PREPARE");
+ }
+ break;
+ }
case SQLCOM_DO:
if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
(res= open_and_lock_tables(thd,tables))))
@@ -3401,7 +3519,7 @@ error:
*/
int check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables)
-
+
{
if (check_access(thd, privilege, tables->db, &tables->grant.privilege,0,0))
return 1;
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 23b87a41c1a..11263ff0844 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -91,16 +91,22 @@ public:
bool get_longdata_error;
#ifndef EMBEDDED_LIBRARY
bool (*set_params)(Prepared_statement *st, uchar *data, uchar *data_end,
- uchar *read_pos);
+ uchar *read_pos, String *expanded_query);
#else
- bool (*set_params_data)(Prepared_statement *st);
+ bool (*set_params_data)(Prepared_statement *st, String *expanded_query);
#endif
+ bool (*set_params_from_vars)(Prepared_statement *stmt,
+ List<LEX_STRING>& varnames,
+ String *expanded_query);
public:
Prepared_statement(THD *thd_arg);
virtual ~Prepared_statement();
+ void setup_set_params();
virtual Statement::Type type() const;
};
+static void execute_stmt(THD *thd, Prepared_statement *stmt,
+ String *expanded_query, bool set_context=false);
/******************************************************************************
Implementation
@@ -129,7 +135,8 @@ find_prepared_statement(THD *thd, ulong id, const char *where,
if (stmt == 0 || stmt->type() != Statement::PREPARED_STATEMENT)
{
- my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), id, where);
+ char llbuf[22];
+ my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), 22, llstr(id, llbuf), where);
if (se == SEND_ERROR)
send_error(thd);
return 0;
@@ -588,19 +595,20 @@ static void setup_one_conversion_function(THD *thd, Item_param *param,
*/
static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array,
- uchar *read_pos, uchar *data_end)
+ uchar *read_pos, uchar *data_end,
+ String *query)
{
THD *thd= stmt->thd;
Item_param **begin= stmt->param_array;
Item_param **end= begin + stmt->param_count;
uint32 length= 0;
- String str, query;
+ String str;
const String *res;
DBUG_ENTER("insert_params_withlog");
- if (query.copy(stmt->query, stmt->query_length, default_charset_info))
+ if (query->copy(stmt->query, stmt->query_length, default_charset_info))
DBUG_RETURN(1);
for (Item_param **it= begin; it < end; ++it)
@@ -621,20 +629,18 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array,
if (param->convert_str_value(thd))
DBUG_RETURN(1); /* out of memory */
- if (query.replace(param->pos_in_query+length, 1, *res))
+ if (query->replace(param->pos_in_query+length, 1, *res))
DBUG_RETURN(1);
length+= res->length()-1;
}
- if (alloc_query(thd, (char *)query.ptr(), query.length()+1))
- DBUG_RETURN(1);
-
DBUG_RETURN(0);
}
static bool insert_params(Prepared_statement *stmt, uchar *null_array,
- uchar *read_pos, uchar *data_end)
+ uchar *read_pos, uchar *data_end,
+ String *expanded_query)
{
Item_param **begin= stmt->param_array;
Item_param **end= begin + stmt->param_count;
@@ -699,7 +705,7 @@ static bool setup_conversion_functions(Prepared_statement *stmt,
#else
-static bool emb_insert_params(Prepared_statement *stmt)
+static bool emb_insert_params(Prepared_statement *stmt, String *expanded_query)
{
THD *thd= stmt->thd;
Item_param **it= stmt->param_array;
@@ -732,20 +738,20 @@ static bool emb_insert_params(Prepared_statement *stmt)
}
-static bool emb_insert_params_withlog(Prepared_statement *stmt)
+static bool emb_insert_params_withlog(Prepared_statement *stmt, String *query)
{
THD *thd= stmt->thd;
Item_param **it= stmt->param_array;
Item_param **end= it + stmt->param_count;
MYSQL_BIND *client_param= thd->client_params;
- String str, query;
+ String str;
const String *res;
uint32 length= 0;
DBUG_ENTER("emb_insert_params_withlog");
- if (query.copy(stmt->query, stmt->query_length, default_charset_info))
+ if (query->copy(stmt->query, stmt->query_length, default_charset_info))
DBUG_RETURN(1);
for (; it < end; ++it, ++client_param)
@@ -764,24 +770,210 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt)
*client_param->length :
client_param->buffer_length);
}
- res= param->query_val_str(&str);
- if (param->convert_str_value(thd))
- DBUG_RETURN(1); /* out of memory */
}
- if (query.replace(param->pos_in_query+length, 1, *res))
+ res= param->query_val_str(&str);
+ if (param->convert_str_value(thd))
+ DBUG_RETURN(1); /* out of memory */
+
+ if (query->replace(param->pos_in_query+length, 1, *res))
DBUG_RETURN(1);
+
length+= res->length()-1;
}
-
- if (alloc_query(thd, (char *) query.ptr(), query.length()+1))
- DBUG_RETURN(1);
-
DBUG_RETURN(0);
}
#endif /*!EMBEDDED_LIBRARY*/
+/*
+ Set prepared statement parameters from user variables.
+ SYNOPSIS
+ insert_params_from_vars()
+ stmt Statement
+ varnames List of variables. Caller must ensure that number of variables
+ in the list is equal to number of statement parameters
+ query Ignored
+*/
+
+static bool insert_params_from_vars(Prepared_statement *stmt,
+ List<LEX_STRING>& varnames,
+ String *query __attribute__((unused)))
+{
+ Item_param **begin= stmt->param_array;
+ Item_param **end= begin + stmt->param_count;
+ user_var_entry *entry;
+ LEX_STRING *varname;
+ DBUG_ENTER("insert_params_from_vars");
+
+ List_iterator<LEX_STRING> var_it(varnames);
+ for (Item_param **it= begin; it < end; ++it)
+ {
+ Item_param *param= *it;
+ varname= var_it++;
+ if ((entry= (user_var_entry*)hash_search(&stmt->thd->user_vars,
+ (byte*) varname->str,
+ varname->length))
+ && entry->value)
+ {
+ param->item_result_type= entry->type;
+ switch (entry->type)
+ {
+ case REAL_RESULT:
+ param->set_double(*(double*)entry->value);
+ break;
+ case INT_RESULT:
+ param->set_int(*(longlong*)entry->value, 21);
+ break;
+ case STRING_RESULT:
+ {
+ CHARSET_INFO *fromcs= entry->collation.collation;
+ CHARSET_INFO *tocs= stmt->thd->variables.collation_connection;
+ uint32 dummy_offset;
+
+ param->value.cs_info.character_set_client= fromcs;
+
+ /*
+ Setup source and destination character sets so that they
+ are different only if conversion is necessary: this will
+ make later checks easier.
+ */
+ param->value.cs_info.final_character_set_of_str_value=
+ String::needs_conversion(0, fromcs, tocs, &dummy_offset) ?
+ tocs : fromcs;
+ /*
+ Exact value of max_length is not known unless data is converted to
+ charset of connection, so we have to set it later.
+ */
+ param->item_type= Item::STRING_ITEM;
+ param->item_result_type= STRING_RESULT;
+
+ if (param->set_str((const char *)entry->value, entry->length))
+ DBUG_RETURN(1);
+ }
+ break;
+ default:
+ DBUG_ASSERT(0);
+ param->set_null();
+ }
+ }
+ else
+ param->set_null();
+
+ if (param->convert_str_value(stmt->thd))
+ DBUG_RETURN(1); /* out of memory */
+ }
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Do the same as insert_params_from_vars but also construct query text for
+ binary log.
+ SYNOPSIS
+ insert_params_from_vars()
+ stmt Statement
+ varnames List of variables. Caller must ensure that number of variables
+ in the list is equal to number of statement parameters
+ query The query with parameter markers replaced with their values
+*/
+
+static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
+ List<LEX_STRING>& varnames,
+ String *query)
+{
+ Item_param **begin= stmt->param_array;
+ Item_param **end= begin + stmt->param_count;
+ user_var_entry *entry;
+ LEX_STRING *varname;
+ DBUG_ENTER("insert_params_from_vars");
+
+ List_iterator<LEX_STRING> var_it(varnames);
+ String str;
+ const String *res;
+ uint32 length= 0;
+ if (query->copy(stmt->query, stmt->query_length, default_charset_info))
+ DBUG_RETURN(1);
+
+ for (Item_param **it= begin; it < end; ++it)
+ {
+ Item_param *param= *it;
+ varname= var_it++;
+ if (get_var_with_binlog(stmt->thd, *varname, &entry))
+ DBUG_RETURN(1);
+ DBUG_ASSERT(entry);
+ if (entry->value)
+ {
+ param->item_result_type= entry->type;
+ switch (entry->type)
+ {
+ case REAL_RESULT:
+ param->set_double(*(double*)entry->value);
+ break;
+ case INT_RESULT:
+ param->set_int(*(longlong*)entry->value, 21);
+ break;
+ case STRING_RESULT:
+ {
+ CHARSET_INFO *fromcs= entry->collation.collation;
+ CHARSET_INFO *tocs= stmt->thd->variables.collation_connection;
+ uint32 dummy_offset;
+
+ param->value.cs_info.character_set_client= fromcs;
+
+ /*
+ Setup source and destination character sets so that they
+ are different only if conversion is necessary: this will
+ make later checks easier.
+ */
+ param->value.cs_info.final_character_set_of_str_value=
+ String::needs_conversion(0, fromcs, tocs, &dummy_offset) ?
+ tocs : fromcs;
+ /*
+ Exact value of max_length is not known unless data is converted to
+ charset of connection, so we have to set it later.
+ */
+ param->item_type= Item::STRING_ITEM;
+ param->item_result_type= STRING_RESULT;
+
+ if (param->set_str((const char *)entry->value, entry->length))
+ DBUG_RETURN(1);
+ }
+ break;
+ default:
+ DBUG_ASSERT(0);
+ param->set_null();
+ }
+ }
+ else
+ param->set_null();
+
+ /* Insert @'escaped-varname' instead of parameter in the query */
+ char *buf, *ptr;
+ str.length(0);
+ if (str.reserve(entry->name.length*2+3))
+ DBUG_RETURN(1);
+
+ buf= str.c_ptr_quick();
+ ptr= buf;
+ *ptr++= '@';
+ *ptr++= '\'';
+ ptr+=
+ escape_string_for_mysql(&my_charset_utf8_general_ci,
+ ptr, entry->name.str, entry->name.length);
+ *ptr++= '\'';
+ str.length(ptr - buf);
+
+ if (param->convert_str_value(stmt->thd))
+ DBUG_RETURN(1); /* out of memory */
+
+ if (query->replace(param->pos_in_query+length, 1, str))
+ DBUG_RETURN(1);
+ length+= str.length()-1;
+ }
+ DBUG_RETURN(0);
+}
+
/*
Validate INSERT statement:
@@ -978,7 +1170,7 @@ static int mysql_test_delete(Prepared_statement *stmt,
*/
static int mysql_test_select(Prepared_statement *stmt,
- TABLE_LIST *tables)
+ TABLE_LIST *tables, bool text_protocol)
{
THD *thd= stmt->thd;
LEX *lex= stmt->lex;
@@ -1010,7 +1202,7 @@ static int mysql_test_select(Prepared_statement *stmt,
if (lex->describe)
{
- if (send_prep_stmt(stmt, 0))
+ if (!text_protocol && send_prep_stmt(stmt, 0))
goto err;
}
else
@@ -1024,14 +1216,16 @@ static int mysql_test_select(Prepared_statement *stmt,
goto err_prep;
}
- if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) ||
+ if (!text_protocol)
+ {
+ if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) ||
thd->protocol_simple.send_fields(&lex->select_lex.item_list, 0)
#ifndef EMBEDDED_LIBRARY
- || net_flush(&thd->net)
+ || net_flush(&thd->net)
#endif
- )
- goto err_prep;
-
+ )
+ goto err_prep;
+ }
unit->cleanup();
}
thd->free_temporary_memory_pool_for_ps_preparing();
@@ -1309,7 +1503,7 @@ static int mysql_test_insert_select(Prepared_statement *stmt,
0 success
1 error, sent to client
*/
-static int send_prepare_results(Prepared_statement *stmt)
+static int send_prepare_results(Prepared_statement *stmt, bool text_protocol)
{
THD *thd= stmt->thd;
LEX *lex= stmt->lex;
@@ -1346,7 +1540,7 @@ static int send_prepare_results(Prepared_statement *stmt)
break;
case SQLCOM_SELECT:
- if ((res= mysql_test_select(stmt, tables)))
+ if ((res= mysql_test_select(stmt, tables, text_protocol)))
goto error;
/* Statement and field info has already been sent */
DBUG_RETURN(0);
@@ -1404,7 +1598,7 @@ static int send_prepare_results(Prepared_statement *stmt)
goto error;
}
if (res == 0)
- DBUG_RETURN(send_prep_stmt(stmt, 0));
+ DBUG_RETURN(text_protocol? 0 : send_prep_stmt(stmt, 0));
error:
if (res < 0)
send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0);
@@ -1445,20 +1639,36 @@ static bool init_param_array(Prepared_statement *stmt)
/*
- Parse the query and send the total number of parameters
- and resultset metadata information back to client (if any),
- without executing the query i.e. without any log/disk
- writes. This will allow the queries to be re-executed
- without re-parsing during execute.
-
- If parameter markers are found in the query, then store
- the information using Item_param along with maintaining a
- list in lex->param_array, so that a fast and direct
- retrieval can be made without going through all field
- items.
+ Given a query string with parameter markers, create a Prepared Statement
+ from it and send PS info back to the client.
+
+ SYNOPSIS
+ mysql_stmt_prepare()
+ packet query to be prepared
+ packet_length query string length, including ignored trailing NULL or
+ quote char.
+ name NULL or statement name. For unnamed statements binary PS
+ protocol is used, for named statements text protocol is
+ used.
+ RETURN
+ 0 OK, statement prepared successfully
+ other Error
+
+ NOTES
+ This function parses the query and sends the total number of parameters
+ and resultset metadata information back to client (if any), without
+ executing the query i.e. without any log/disk writes. This allows the
+ queries to be re-executed without re-parsing during execute.
+
+ If parameter markers are found in the query, then store the information
+ using Item_param along with maintaining a list in lex->param_array, so
+ that a fast and direct retrieval can be made without going through all
+ field items.
+
*/
-void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
+int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
+ LEX_STRING *name)
{
LEX *lex;
Prepared_statement *stmt= new Prepared_statement(thd);
@@ -1470,14 +1680,26 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
if (stmt == 0)
{
send_error(thd, ER_OUT_OF_RESOURCES);
- DBUG_VOID_RETURN;
+ DBUG_RETURN(1);
+ }
+
+ if (name)
+ {
+ stmt->name.length= name->length;
+ if (!(stmt->name.str= memdup_root(&stmt->mem_root, (byte*)name->str,
+ name->length)))
+ {
+ delete stmt;
+ send_error(thd, ER_OUT_OF_RESOURCES);
+ DBUG_RETURN(1);
+ }
}
if (thd->stmt_map.insert(stmt))
{
delete stmt;
send_error(thd, ER_OUT_OF_RESOURCES);
- DBUG_VOID_RETURN;
+ DBUG_RETURN(1);
}
thd->stmt_backup.set_statement(thd);
@@ -1494,7 +1716,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
/* Statement map deletes statement on erase */
thd->stmt_map.erase(stmt);
send_error(thd, ER_OUT_OF_RESOURCES);
- DBUG_VOID_RETURN;
+ DBUG_RETURN(1);
}
mysql_log.write(thd, COM_PREPARE, "%s", packet);
@@ -1506,7 +1728,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
error= yyparse((void *)thd) || thd->is_fatal_error ||
init_param_array(stmt) ||
- send_prepare_results(stmt);
+ send_prepare_results(stmt, test(name));
/* restore to WAIT_PRIOR: QUERY_PRIOR is set inside alloc_query */
if (!(specialflag & SPECIAL_NO_PRIOR))
@@ -1522,10 +1744,12 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
{
/* Statement map deletes statement on erase */
thd->stmt_map.erase(stmt);
+ stmt= NULL;
/* error is sent inside yyparse/send_prepare_results */
}
else
{
+ stmt->setup_set_params();
SELECT_LEX *sl= stmt->lex->all_selects_list;
/*
Save WHERE clause pointers, because they may be changed during query
@@ -1535,8 +1759,10 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
{
sl->prep_where= sl->where;
}
+
}
- DBUG_VOID_RETURN;
+
+ DBUG_RETURN(!stmt);
}
/* Reinit statement before execution */
@@ -1639,15 +1865,14 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
DBUG_VOID_RETURN;
}
- thd->stmt_backup.set_statement(thd);
- thd->set_statement(stmt);
- reset_stmt_for_execute(stmt);
+ String expanded_query;
#ifndef EMBEDDED_LIBRARY
if (stmt->param_count)
{
uchar *null_array= (uchar *) packet;
if (setup_conversion_functions(stmt, (uchar **) &packet, packet_end) ||
- stmt->set_params(stmt, null_array, (uchar *) packet, packet_end))
+ stmt->set_params(stmt, null_array, (uchar *) packet, packet_end,
+ &expanded_query))
goto set_params_data_err;
}
#else
@@ -1656,53 +1881,124 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
we set params, and also we don't need to parse packet.
So we do it in one function.
*/
- if (stmt->param_count && stmt->set_params_data(stmt))
+ if (stmt->param_count && stmt->set_params_data(stmt, &expanded_query))
goto set_params_data_err;
#endif
+ thd->protocol= &thd->protocol_prep; // Switch to binary protocol
+ execute_stmt(thd, stmt, &expanded_query);
+ thd->protocol= &thd->protocol_simple; // Use normal protocol
+ DBUG_VOID_RETURN;
+
+set_params_data_err:
+ reset_stmt_params(stmt);
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_execute");
+ send_error(thd);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Execute prepared statement using parameter values from
+ lex->prepared_stmt_params and send result to the client using text protocol.
+*/
+
+void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name)
+{
+ Prepared_statement *stmt;
+ String expanded_query;
+ DBUG_ENTER("mysql_sql_stmt_execute");
+
+ if (!(stmt= (Prepared_statement*)thd->stmt_map.find_by_name(stmt_name)))
+ {
+ my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), stmt_name->length,
+ stmt_name->str, "EXECUTE");
+ send_error(thd);
+ DBUG_VOID_RETURN;
+ }
+
+ if (stmt->param_count != thd->lex->prepared_stmt_params.elements)
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute");
+ send_error(thd);
+ DBUG_VOID_RETURN;
+ }
+ /* Item_param allows setting parameters in COM_EXECUTE only */
+ thd->command= COM_EXECUTE;
+
+ thd->free_list= NULL;
+ thd->stmt_backup.set_statement(thd);
+ thd->set_statement(stmt);
+ if (stmt->set_params_from_vars(stmt, thd->stmt_backup.lex->prepared_stmt_params,
+ &expanded_query))
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute");
+ send_error(thd);
+ }
+ execute_stmt(thd, stmt, &expanded_query);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Execute prepared statement.
+ SYNOPSIS
+ execute_stmt()
+ thd Current thread
+ stmt Statement to execute
+ expanded_query If binary log is enabled, query string with parameter
+ placeholders replaced with actual values. Otherwise empty
+ string.
+ NOTES
+ Caller must set parameter values and thd::protocol.
+ thd->free_list is assumed to be garbage.
+*/
+
+static void execute_stmt(THD *thd, Prepared_statement *stmt,
+ String *expanded_query, bool set_context)
+{
+ DBUG_ENTER("execute_stmt");
+ if (set_context)
+ {
+ thd->free_list= NULL;
+ thd->stmt_backup.set_statement(thd);
+ thd->set_statement(stmt);
+ }
+ reset_stmt_for_execute(stmt);
+
+ if (expanded_query->length() &&
+ alloc_query(thd, (char *)expanded_query->ptr(),
+ expanded_query->length()+1))
+ {
+ my_error(ER_OUTOFMEMORY, 0, expanded_query->length());
+ DBUG_VOID_RETURN;
+ }
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
-
- /*
- TODO:
- Also, have checks on basic executions such as mysql_insert(),
- mysql_delete(), mysql_update() and mysql_select() to not to
- have re-check on setup_* and other things ..
- */
- thd->protocol= &thd->protocol_prep; // Switch to binary protocol
mysql_execute_command(thd);
thd->lex->unit.cleanup();
- thd->protocol= &thd->protocol_simple; // Use normal protocol
-
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
- cleanup_items(stmt->free_list);
- reset_stmt_params(stmt);
- close_thread_tables(thd); // to close derived tables
- thd->set_statement(&thd->stmt_backup);
- /*
+ /*
Free Items that were created during this execution of the PS by query
optimizer.
*/
free_items(thd->free_list);
- DBUG_VOID_RETURN;
-
-set_params_data_err:
+ cleanup_items(stmt->free_list);
reset_stmt_params(stmt);
+ close_thread_tables(thd); // to close derived tables
thd->set_statement(&thd->stmt_backup);
- my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_execute");
- send_error(thd);
DBUG_VOID_RETURN;
}
/*
- Reset a prepared statement in case there was a recoverable error.
+ Reset a prepared statement in case there was a recoverable error.
SYNOPSIS
mysql_stmt_reset()
- thd Thread handle
- packet Packet with stmt id
+ thd Thread handle
+ packet Packet with stmt id
DESCRIPTION
This function resets statement to the state it was right after prepare.
@@ -1842,8 +2138,9 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
get_longdata_error(0)
{
*last_error= '\0';
- if (mysql_bin_log.is_open())
+ if (mysql_bin_log.is_open()) //psergey-todo: remove this!
{
+ set_params_from_vars= insert_params_from_vars_with_log;
#ifndef EMBEDDED_LIBRARY
set_params= insert_params_withlog;
#else
@@ -1851,13 +2148,38 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
#endif
}
else
+ {
+ set_params_from_vars= insert_params_from_vars;
#ifndef EMBEDDED_LIBRARY
set_params= insert_params;
#else
set_params_data= emb_insert_params;
#endif
+ }
}
+void Prepared_statement::setup_set_params()
+{
+ /* Setup binary logging */
+ if (mysql_bin_log.is_open() && is_update_query(lex->sql_command))
+ {
+ set_params_from_vars= insert_params_from_vars_with_log;
+#ifndef EMBEDDED_LIBRARY
+ set_params= insert_params_withlog;
+#else
+ set_params_data= emb_insert_params_withlog;
+#endif
+ }
+ else
+ {
+ set_params_from_vars= insert_params_from_vars;
+#ifndef EMBEDDED_LIBRARY
+ set_params= insert_params;
+#else
+ set_params_data= emb_insert_params;
+#endif
+ }
+}
Prepared_statement::~Prepared_statement()
{
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 6a40dc3c23a..aa6011f544d 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -432,6 +432,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token MEDIUMTEXT
%token NUMERIC_SYM
%token PRECISION
+%token PREPARE_SYM
+%token DEALLOCATE_SYM
%token QUICK
%token REAL
%token SIGNED_SYM
@@ -724,6 +726,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
precision subselect_start opt_and charset
subselect_end select_var_list select_var_list_init help opt_len
opt_extended_describe
+ prepare prepare_src execute deallocate
END_OF_INPUT
%type <NONE>
@@ -760,10 +763,12 @@ verb_clause:
| checksum
| commit
| create
+ | deallocate
| delete
| describe
| do
| drop
+ | execute
| flush
| grant
| handler
@@ -775,6 +780,7 @@ verb_clause:
| optimize
| keycache
| preload
+ | prepare
| purge
| rename
| repair
@@ -795,6 +801,86 @@ verb_clause:
| use
;
+deallocate:
+ DEALLOCATE_SYM PREPARE_SYM ident
+ {
+ THD *thd=YYTHD;
+ LEX *lex= thd->lex;
+ if (thd->command == COM_PREPARE)
+ {
+ yyerror(ER(ER_SYNTAX_ERROR));
+ YYABORT;
+ }
+ lex->sql_command= SQLCOM_DEALLOCATE_PREPARE;
+ lex->prepared_stmt_name= $3;
+ };
+
+prepare:
+ PREPARE_SYM ident FROM prepare_src
+ {
+ THD *thd=YYTHD;
+ LEX *lex= thd->lex;
+ if (thd->command == COM_PREPARE)
+ {
+ yyerror(ER(ER_SYNTAX_ERROR));
+ YYABORT;
+ }
+ lex->sql_command= SQLCOM_PREPARE;
+ lex->prepared_stmt_name= $2;
+ };
+
+prepare_src:
+ TEXT_STRING_sys
+ {
+ THD *thd=YYTHD;
+ LEX *lex= thd->lex;
+ lex->prepared_stmt_code= $1;
+ lex->prepared_stmt_code_is_varref= false;
+ }
+ | '@' ident_or_text
+ {
+ THD *thd=YYTHD;
+ LEX *lex= thd->lex;
+ lex->prepared_stmt_code= $2;
+ lex->prepared_stmt_code_is_varref= true;
+ };
+
+execute:
+ EXECUTE_SYM ident
+ {
+ THD *thd=YYTHD;
+ LEX *lex= thd->lex;
+ if (thd->command == COM_PREPARE)
+ {
+ yyerror(ER(ER_SYNTAX_ERROR));
+ YYABORT;
+ }
+ lex->sql_command= SQLCOM_EXECUTE;
+ lex->prepared_stmt_name= $2;
+ }
+ execute_using
+ {}
+ ;
+
+execute_using:
+ /* nothing */
+ | USING execute_var_list
+ ;
+
+execute_var_list:
+ execute_var_list ',' execute_var_ident
+ | execute_var_ident
+ ;
+
+execute_var_ident: '@' ident_or_text
+ {
+ LEX *lex=Lex;
+ LEX_STRING *lexstr= (LEX_STRING*)sql_memdup(&$2, sizeof(LEX_STRING));
+ if (!lexstr || lex->prepared_stmt_params.push_back(lexstr))
+ YYABORT;
+ }
+ ;
+
/* help */
help:
@@ -4911,6 +4997,7 @@ keyword:
| DATETIME {}
| DATE_SYM {}
| DAY_SYM {}
+ | DEALLOCATE_SYM {}
| DELAY_KEY_WRITE_SYM {}
| DES_KEY_FILE {}
| DIRECTORY_SYM {}
@@ -5008,6 +5095,7 @@ keyword:
| PASSWORD {}
| POINT_SYM {}
| POLYGON {}
+ | PREPARE_SYM {}
| PREV_SYM {}
| PROCESS {}
| PROCESSLIST_SYM {}