Commit f0137bcd authored by pem@mysql.com's avatar pem@mysql.com

Fixed bugs in the parameter evaluation and modified the execution engine

for better jump support. Some flow control support added too (but not
complete).
parent aae07a4d
......@@ -59,6 +59,7 @@ static SYMBOL symbols[] = {
{ "ANY", SYM(ANY_SYM),0,0},
{ "AS", SYM(AS),0,0},
{ "ASC", SYM(ASC),0,0},
{ "ASENSITIVE", SYM(ASENSITIVE_SYM),0,0},
{ "AVG", SYM(AVG_SYM),0,0},
{ "AVG_ROW_LENGTH", SYM(AVG_ROW_LENGTH),0,0},
{ "AUTO_INCREMENT", SYM(AUTO_INC),0,0},
......@@ -107,6 +108,7 @@ static SYMBOL symbols[] = {
{ "CURRENT_DATE", SYM(CURDATE),0,0},
{ "CURRENT_TIME", SYM(CURTIME),0,0},
{ "CURRENT_TIMESTAMP", SYM(NOW_SYM),0,0},
{ "CURSOR", SYM(CURSOR_SYM),0,0},
{ "DATA", SYM(DATA_SYM),0,0},
{ "DATABASE", SYM(DATABASE),0,0},
{ "DATABASES", SYM(DATABASES),0,0},
......@@ -141,6 +143,7 @@ static SYMBOL symbols[] = {
{ "ERRORS", SYM(ERRORS),0,0},
{ "END", SYM(END),0,0},
{ "ELSE", SYM(ELSE),0,0},
{ "ELSEIF", SYM(ELSEIF_SYM),0,0},
{ "ESCAPE", SYM(ESCAPE_SYM),0,0},
{ "ESCAPED", SYM(ESCAPED),0,0},
{ "ENABLE", SYM(ENABLE_SYM),0,0},
......@@ -196,6 +199,7 @@ static SYMBOL symbols[] = {
{ "INNOBASE", SYM(INNOBASE_SYM),0,0},
{ "INNODB", SYM(INNOBASE_SYM),0,0},
{ "INOUT", SYM(INOUT_SYM),0,0},
{ "INSENSITIVE", SYM(INSENSITIVE_SYM),0,0},
{ "INSERT", SYM(INSERT),0,0},
{ "INSERT_METHOD", SYM(INSERT_METHOD),0,0},
{ "INT", SYM(INT_SYM),0,0},
......@@ -213,12 +217,14 @@ static SYMBOL symbols[] = {
{ "ISOLATION", SYM(ISOLATION),0,0},
{ "ISAM", SYM(ISAM_SYM),0,0},
{ "ISSUER", SYM(ISSUER_SYM),0,0},
{ "ITERATE", SYM(ITERATE_SYM),0,0},
{ "JOIN", SYM(JOIN_SYM),0,0},
{ "KEY", SYM(KEY_SYM),0,0},
{ "KEYS", SYM(KEYS),0,0},
{ "KILL", SYM(KILL_SYM),0,0},
{ "LAST", SYM(LAST_SYM),0,0},
{ "LEADING", SYM(LEADING),0,0},
{ "LEAVE", SYM(LEAVE_SYM),0,0},
{ "LEFT", SYM(LEFT),0,0},
{ "LEVEL", SYM(LEVEL_SYM),0,0},
{ "LIKE", SYM(LIKE),0,0},
......@@ -233,6 +239,7 @@ static SYMBOL symbols[] = {
{ "LOGS", SYM(LOGS_SYM),0,0},
{ "LONG", SYM(LONG_SYM),0,0},
{ "LONGBLOB", SYM(LONGBLOB),0,0},
{ "LOOP", SYM(LOOP_SYM),0,0},
{ "LONGTEXT", SYM(LONGTEXT),0,0},
{ "LOW_PRIORITY", SYM(LOW_PRIORITY),0,0},
{ "MASTER", SYM(MASTER_SYM),0,0},
......@@ -313,6 +320,7 @@ static SYMBOL symbols[] = {
{ "REPEATABLE", SYM(REPEATABLE_SYM),0,0},
{ "REQUIRE", SYM(REQUIRE_SYM),0,0},
{ "RESET", SYM(RESET_SYM),0,0},
{ "UNTIL", SYM(UNTIL_SYM),0,0},
{ "USER_RESOURCES", SYM(RESOURCES),0,0},
{ "RESTORE", SYM(RESTORE_SYM),0,0},
{ "RESTRICT", SYM(RESTRICT),0,0},
......@@ -327,6 +335,7 @@ static SYMBOL symbols[] = {
{ "RTREE", SYM(RTREE_SYM),0,0},
{ "SECOND", SYM(SECOND_SYM),0,0},
{ "SELECT", SYM(SELECT_SYM),0,0},
{ "SENSITIVE", SYM(SENSITIVE_SYM),0,0},
{ "SERIAL", SYM(SERIAL_SYM),0,0},
{ "SERIALIZABLE", SYM(SERIALIZABLE_SYM),0,0},
{ "SESSION", SYM(SESSION_SYM),0,0},
......@@ -401,6 +410,7 @@ static SYMBOL symbols[] = {
{ "WRITE", SYM(WRITE_SYM),0,0},
{ "WHEN", SYM(WHEN_SYM),0,0},
{ "WHERE", SYM(WHERE),0,0},
{ "WHILE", SYM(WHILE_SYM),0,0},
{ "XOR", SYM(XOR),0,0},
{ "X509", SYM(X509_SYM),0,0},
{ "YEAR", SYM(YEAR_SYM),0,0},
......
......@@ -59,9 +59,9 @@ eval_func_item(Item *it, enum enum_field_types type)
{
char buffer[MAX_FIELD_WIDTH];
String tmp(buffer, sizeof(buffer), default_charset_info);
String *s= it->val_str(&tmp);
(void)it->val_str(&tmp);
it= new Item_string(buffer, sizeof(buffer), default_charset_info);
it= new Item_string(s->c_ptr_quick(), s->length(), default_charset_info);
break;
}
case MYSQL_TYPE_NULL:
......@@ -149,15 +149,21 @@ sp_head::execute(THD *thd)
{
sp_pvar_t *pvar = pctx->find_pvar(i);
// QQ Passing an argument is, in a sense, a "SET". We have to evaluate
// any expression at this point.
nctx->push_item(it->this_item());
// Note: If it's OUT or INOUT, it must be a variable.
// QQ: Need to handle "global" user/host variables too!!!
if (!pvar || pvar->mode == sp_param_in)
nctx->set_oindex(i, -1);
if (! pvar)
nctx->set_oindex(i, -1); // Shouldn't happen
else
nctx->set_oindex(i, static_cast<Item_splocal *>(it)->get_offset());
{
if (pvar->mode == sp_param_out)
nctx->push_item(it->this_item()); // OUT
else
nctx->push_item(eval_func_item(it, pvar->type)); // IN or INOUT
// Note: If it's OUT or INOUT, it must be a variable.
// QQ: Need to handle "global" user/host variables too!!!
if (pvar->mode == sp_param_in)
nctx->set_oindex(i, -1); // IN
else // OUT or INOUT
nctx->set_oindex(i, static_cast<Item_splocal *>(it)->get_offset());
}
}
// The rest of the frame are local variables which are all IN.
// QQ We haven't found any hint of what the value is when unassigned,
......@@ -176,14 +182,12 @@ sp_head::execute(THD *thd)
while (ret == 0)
{
int offset;
sp_instr *i;
i = get_instr(ip); // Returns NULL when we're done.
if (i == NULL)
break;
ret= i->execute(thd, &offset);
ip += offset;
ret= i->execute(thd, &ip);
}
thd->net.no_send_ok= nsok;
......@@ -250,6 +254,27 @@ sp_head::restore_lex(THD *thd)
memcpy(&thd->lex, &m_lex, sizeof(LEX)); // Restore lex
}
void
sp_head::push_backpatch(uint ip)
{
(void)m_backpatch.push_front(&ip);
}
void
sp_head::backpatch(uint dest)
{
while (! m_backpatch.is_empty())
{
uint *ip= m_backpatch.pop();
sp_instr_jump *i= static_cast<sp_instr_jump *>(get_instr(*ip));
i->set_destination(dest);
}
}
// ------------------------------------------------------------------
// Finds the SP 'name'. Currently this always reads from the database
// and prepares (parse) it, but in the future it will first look in
// the in-memory cache for SPs. (And store newly prepared SPs there of
......@@ -350,12 +375,13 @@ sp_drop(THD *thd, char *name, uint namelen)
}
// ------------------------------------------------------------------
//
// sp_instr_stmt
//
int
sp_instr_stmt::execute(THD *thd, int *offsetp)
sp_instr_stmt::execute(THD *thd, uint *nextp)
{
LEX olex; // The other lex
......@@ -368,7 +394,7 @@ sp_instr_stmt::execute(THD *thd, int *offsetp)
memcpy(&thd->lex, &olex, sizeof(LEX)); // Restore the other lex
*offsetp = 1;
*nextp = m_ip+1;
return 0;
}
......@@ -376,9 +402,41 @@ sp_instr_stmt::execute(THD *thd, int *offsetp)
// sp_instr_set
//
int
sp_instr_set::execute(THD *thd, int *offsetp)
sp_instr_set::execute(THD *thd, uint *nextp)
{
thd->spcont->set_item(m_offset, eval_func_item(m_value, m_type));
*offsetp = 1;
*nextp = m_ip+1;
return 0;
}
//
// sp_instr_jump_if
//
int
sp_instr_jump_if::execute(THD *thd, uint *nextp)
{
Item *it= eval_func_item(m_expr, MYSQL_TYPE_TINY);
if (it->val_int())
*nextp = m_dest;
else
*nextp = m_ip+1;
return 0;
}
//
// sp_instr_jump_if_not
//
int
sp_instr_jump_if_not::execute(THD *thd, uint *nextp)
{
Item *it= eval_func_item(m_expr, MYSQL_TYPE_TINY);
if (! it->val_int())
*nextp = m_dest;
else
*nextp = m_ip+1;
return 0;
}
......@@ -72,6 +72,12 @@ public:
void
restore_lex(THD *thd);
void
push_backpatch(uint ip);
void
backpatch(uint dest);
private:
Item_string *m_name;
......@@ -79,6 +85,7 @@ private:
LEX *m_mylex; // My own lex
LEX m_lex; // Temp. store for the other lex
DYNAMIC_ARRAY m_instr; // The "instructions"
List<uint> m_backpatch; // Instructions needing backpaching
inline sp_instr *
get_instr(uint i)
......@@ -106,6 +113,10 @@ int
sp_drop(THD *thd, char *name, uint namelen);
//
// "Instructions"...
//
class sp_instr : public Sql_alloc
{
sp_instr(const sp_instr &); /* Prevent use of these */
......@@ -114,24 +125,28 @@ class sp_instr : public Sql_alloc
public:
// Should give each a name or type code for debugging purposes?
sp_instr()
: Sql_alloc()
sp_instr(uint ip)
: Sql_alloc(), m_ip(ip)
{}
virtual ~sp_instr()
{}
// Execute this instrution. '*offsetp' will be set to an offset to the
// next instruction to execute. (For most instruction this will be 1,
// i.e. the following instruction.)
// Execute this instrution. '*nextp' will be set to the index of the next
// instruction to execute. (For most instruction this will be the
// instruction following this one.)
// Returns 0 on success, non-zero if some error occured.
virtual int
execute(THD *thd, int *offsetp)
execute(THD *thd, uint *nextp)
{ // Default is a no-op.
*offsetp = 1; // Next instruction
*nextp = m_ip+1; // Next instruction
return 0;
}
protected:
uint m_ip; // My index
}; // class sp_instr : public Sql_alloc
......@@ -145,14 +160,14 @@ class sp_instr_stmt : public sp_instr
public:
sp_instr_stmt()
: sp_instr()
sp_instr_stmt(uint ip)
: sp_instr(ip)
{}
virtual ~sp_instr_stmt()
{}
virtual int execute(THD *thd, int *offsetp);
virtual int execute(THD *thd, uint *nextp);
inline void
set_lex(LEX *lex)
......@@ -180,21 +195,114 @@ class sp_instr_set : public sp_instr
public:
sp_instr_set(uint offset, Item *val, enum enum_field_types type)
: sp_instr(), m_offset(offset), m_value(val), m_type(type)
sp_instr_set(uint ip, uint offset, Item *val, enum enum_field_types type)
: sp_instr(ip), m_offset(offset), m_value(val), m_type(type)
{}
virtual ~sp_instr_set()
{}
virtual int execute(THD *thd, int *offsetp);
virtual int execute(THD *thd, uint *nextp);
private:
uint m_offset;
uint m_offset; // Frame offset
Item *m_value;
enum enum_field_types m_type; // The declared type
}; // class sp_instr_set : public sp_instr
class sp_instr_jump : public sp_instr
{
sp_instr_jump(const sp_instr_jump &); /* Prevent use of these */
void operator=(sp_instr_jump &);
public:
sp_instr_jump(uint ip)
: sp_instr(ip)
{}
sp_instr_jump(uint ip, uint dest)
: sp_instr(ip), m_dest(dest)
{}
virtual ~sp_instr_jump()
{}
virtual int execute(THD *thd, uint *nextp)
{
*nextp= m_dest;
return 0;
}
virtual void
set_destination(uint dest)
{
m_dest= dest;
}
private:
int m_dest; // Where we will go
}; // class sp_instr_jump : public sp_instr
class sp_instr_jump_if : public sp_instr_jump
{
sp_instr_jump_if(const sp_instr_jump_if &); /* Prevent use of these */
void operator=(sp_instr_jump_if &);
public:
sp_instr_jump_if(uint ip, Item *i)
: sp_instr_jump(ip), m_expr(i)
{}
sp_instr_jump_if(uint ip, Item *i, uint dest)
: sp_instr_jump(ip, dest), m_expr(i)
{}
virtual ~sp_instr_jump_if()
{}
virtual int execute(THD *thd, uint *nextp);
private:
int m_dest; // Where we will go
Item *m_expr; // The condition
}; // class sp_instr_jump_if : public sp_instr_jump
class sp_instr_jump_if_not : public sp_instr_jump
{
sp_instr_jump_if_not(const sp_instr_jump_if_not &); /* Prevent use of these */
void operator=(sp_instr_jump_if_not &);
public:
sp_instr_jump_if_not(uint ip, Item *i)
: sp_instr_jump(ip), m_expr(i)
{}
sp_instr_jump_if_not(uint ip, Item *i, uint dest)
: sp_instr_jump(ip, dest), m_expr(i)
{}
virtual ~sp_instr_jump_if_not()
{}
virtual int execute(THD *thd, uint *nextp);
private:
int m_dest; // Where we will go
Item *m_expr; // The condition
}; // class sp_instr_jump_if_not : public sp_instr_jump
#endif /* _SP_HEAD_H_ */
......@@ -24,14 +24,16 @@
#include "mysql_priv.h"
#include "sp_pcontext.h"
#include "sp_head.h"
sp_pcontext::sp_pcontext()
: m_params(0), m_framesize(0), m_i(0)
: m_params(0), m_framesize(0), m_i(0), m_genlab(0)
{
m_pvar_size = 16;
m_pvar = (sp_pvar_t *)my_malloc(m_pvar_size * sizeof(sp_pvar_t), MYF(MY_WME));
if (m_pvar)
memset(m_pvar, 0, m_pvar_size * sizeof(sp_pvar_t));
m_label.empty();
}
void
......@@ -89,3 +91,41 @@ sp_pcontext::push(LEX_STRING *name, enum enum_field_types type,
m_i += 1;
}
}
void
sp_pcontext::push_label(char *name, uint ip)
{
sp_label_t *lab = (sp_label_t *)my_malloc(sizeof(sp_label_t), MYF(MY_WME));
if (lab)
{
lab->name= name;
lab->ip= ip;
m_label.push_front(lab);
}
}
void
sp_pcontext::push_gen_label(uint ip)
{
char *s= my_malloc(10, MYF(MY_WME)); // 10=...
if (s)
{
sprintf(s, ".%08x", m_genlab++); // ...9+1
push_label(s, ip);
}
}
sp_label_t *
sp_pcontext::find_label(char *name)
{
List_iterator_fast<sp_label_t> li(m_label);
sp_label_t *lab;
while ((lab= li++))
if (strcasecmp(name, lab->name) == 0)
return lab;
return NULL;
}
......@@ -38,6 +38,12 @@ typedef struct
my_bool isset;
} sp_pvar_t;
typedef struct
{
char *name;
uint ip; // Instruction index
} sp_label_t;
class sp_pcontext : public Sql_alloc
{
sp_pcontext(const sp_pcontext &); /* Prevent use of these */
......@@ -89,6 +95,7 @@ class sp_pcontext : public Sql_alloc
void
push(LEX_STRING *name, enum enum_field_types type, sp_param_mode_t mode);
// Pop the last 'num' slots of the frame
inline void
pop(uint num = 1)
{
......@@ -109,6 +116,21 @@ class sp_pcontext : public Sql_alloc
return m_pvar+i;
}
void
push_label(char *name, uint ip);
void
push_gen_label(uint ip);
sp_label_t *
find_label(char *name);
inline void
pop_label()
{
m_label.pop();
}
private:
uint m_params; // The number of parameters
......@@ -121,6 +143,9 @@ private:
void
grow();
List<sp_label_t> m_label; // The label list
uint m_genlab; // Gen. label counter
}; // class sp_pcontext : public Sql_alloc
......
......@@ -526,6 +526,16 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token SQL_SMALL_RESULT
%token SQL_BUFFER_RESULT
%token CURSOR_SYM
%token ELSEIF_SYM
%token ITERATE_SYM
%token LEAVE_SYM
%token LOOP_SYM
%token UNTIL_SYM
%token WHILE_SYM
%token ASENSITIVE_SYM
%token INSENSITIVE_SYM
%token SENSITIVE_SYM
/* QQ This is a dummy, until we have solved the SET syntax problem. */
%token SPSET_SYM
......@@ -680,7 +690,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
statement
END_OF_INPUT
%type <NONE> call sp_proc_body sp_proc_stmts sp_proc_stmt
%type <NONE> call sp_proc_stmts sp_proc_stmt
%type <num> sp_decls sp_decl sp_decl_idents sp_opt_inout
%type <NONE>
......@@ -906,7 +916,7 @@ create:
{
Lex->spcont->set_params();
}
sp_proc_body
sp_proc_stmt
{
Lex->sql_command= SQLCOM_CREATE_PROCEDURE;
}
......@@ -974,26 +984,9 @@ sp_opt_locator:
| AS LOCATOR_SYM
;
sp_proc_body:
{
Lex->sphead->reset_lex(YYTHD);
}
sp_proc_stmt
{
Lex->sphead->restore_lex(YYTHD);
}
| begin
sp_decls
sp_proc_stmts
END
{
Lex->spcont->pop($2);
}
;
sp_proc_stmts:
sp_proc_body ';'
| sp_proc_stmts sp_proc_body ';'
sp_proc_stmt ';'
| sp_proc_stmts sp_proc_stmt ';'
;
sp_decls:
......@@ -1037,14 +1030,69 @@ sp_decl_idents:
/* Dummy for the spset thing. Will go away when the SET problem is fixed. */
sp_proc_stmt:
{
Lex->sphead->reset_lex(YYTHD);
}
statement
{
LEX *lex= Lex;
sp_instr_stmt *i= new sp_instr_stmt();
sp_instr_stmt *i= new sp_instr_stmt(lex->sphead->instructions());
i->set_lex(lex);
lex->sphead->add_instr(i);
lex->sphead->restore_lex(YYTHD);
}
/* | sp_if
| sp_case */
| sp_labeled_control
{}
| { /* Unlabeled controls get a secret label. */
LEX *lex= Lex;
Lex->spcont->push_gen_label(lex->sphead->instructions());
}
sp_unlabeled_control
{
/* QQ backpatch here */
Lex->spcont->pop_label();
}
| LEAVE_SYM IDENT
{
LEX *lex= Lex;
sp_label_t *lab= lex->spcont->find_label($2.str);
if (! lab)
{
printf("QQ LEAVE with no matching label\n");
YYABORT;
}
else
{
uint ip= lex->sphead->instructions();
sp_instr_jump *i= new sp_instr_jump(ip, 0);
lex->sphead->push_backpatch(ip);
lex->sphead->add_instr(i);
}
}
| ITERATE_SYM IDENT
{
LEX *lex= Lex;
sp_label_t *lab= lex->spcont->find_label($2.str);
if (! lab)
{
printf("QQ ITERATE with no matching label\n");
YYABORT;
}
else
{
uint ip= lex->sphead->instructions();
sp_instr_jump *i= new sp_instr_jump(ip, lab->ip);
lex->sphead->add_instr(i);
}
}
|
/* QQ Dummy. We need to fix the old SET syntax to make it work for
local SP variables as well. */
......@@ -1059,7 +1107,8 @@ sp_proc_stmt:
else
{
/* QQ Check type match! */
sp_instr_set *i = new sp_instr_set(spv->offset, $4, spv->type);
sp_instr_set *i = new sp_instr_set(lex->sphead->instructions(),
spv->offset, $4, spv->type);
lex->sphead->add_instr(i);
spv->isset= TRUE;
......@@ -1067,6 +1116,63 @@ sp_proc_stmt:
}
;
sp_labeled_control:
IDENT ':'
{
LEX *lex= Lex;
sp_label_t *lab= lex->spcont->find_label($1.str);
if (lab)
{
printf("QQ Redefining label\n");
YYABORT;
}
else
lex->spcont->push_label($1.str,
lex->sphead->instructions());
}
sp_unlabeled_control IDENT
{
LEX *lex= Lex;
sp_label_t *lab= lex->spcont->find_label($5.str);
if (! lab)
{
printf("QQ end-label without match\n");
YYABORT;
}
else if (strcasecmp($5.str, lab->name) != 0)
{
printf("QQ mismatching labels\n");
YYABORT;
}
else
{
/* QQ backpatch here */
lex->spcont->pop_label();
}
}
;
sp_unlabeled_control:
begin
sp_decls
sp_proc_stmts
END
{
Lex->spcont->pop($2);
}
| LOOP_SYM
sp_proc_stmts
END LOOP_SYM
| WHILE_SYM expr DO_SYM
sp_proc_stmts
END WHILE_SYM
| FUNC_ARG2 /* "REPEAT" actually... */
sp_proc_stmts
UNTIL_SYM expr END FUNC_ARG2
;
create2:
'(' field_list ')' opt_create_table_options create3 {}
| opt_create_table_options create3 {}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment