Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
MariaDB
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexedi
MariaDB
Commits
e26d0491
Commit
e26d0491
authored
Nov 12, 2019
by
Alexander Barkov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
MDEV-21023 Move LEX methods and related functions from sql_yacc.yy to sql_lex.cc
parent
68ed3a81
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
518 additions
and
514 deletions
+518
-514
sql/sql_lex.cc
sql/sql_lex.cc
+518
-0
sql/sql_yacc.yy
sql/sql_yacc.yy
+0
-512
sql/sql_yacc_ora.yy
sql/sql_yacc_ora.yy
+0
-2
No files found.
sql/sql_lex.cc
View file @
e26d0491
...
...
@@ -52,6 +52,524 @@ const LEX_CSTRING empty_clex_str= {"", 0};
const
LEX_CSTRING
star_clex_str
=
{
"*"
,
1
};
const
LEX_CSTRING
param_clex_str
=
{
"?"
,
1
};
/**
Helper action for a case expression statement (the expr in 'CASE expr').
This helper is used for 'searched' cases only.
@param lex the parser lex context
@param expr the parsed expression
@return 0 on success
*/
int
LEX
::
case_stmt_action_expr
(
Item
*
expr
)
{
int
case_expr_id
=
spcont
->
register_case_expr
();
sp_instr_set_case_expr
*
i
;
if
(
spcont
->
push_case_expr_id
(
case_expr_id
))
return
1
;
i
=
new
(
thd
->
mem_root
)
sp_instr_set_case_expr
(
sphead
->
instructions
(),
spcont
,
case_expr_id
,
expr
,
this
);
sphead
->
add_cont_backpatch
(
i
);
return
sphead
->
add_instr
(
i
);
}
/**
Helper action for a case when condition.
This helper is used for both 'simple' and 'searched' cases.
@param lex the parser lex context
@param when the parsed expression for the WHEN clause
@param simple true for simple cases, false for searched cases
*/
int
LEX
::
case_stmt_action_when
(
Item
*
when
,
bool
simple
)
{
uint
ip
=
sphead
->
instructions
();
sp_instr_jump_if_not
*
i
;
Item_case_expr
*
var
;
Item
*
expr
;
if
(
simple
)
{
var
=
new
(
thd
->
mem_root
)
Item_case_expr
(
thd
,
spcont
->
get_current_case_expr_id
());
#ifdef DBUG_ASSERT_EXISTS
if
(
var
)
{
var
->
m_sp
=
sphead
;
}
#endif
expr
=
new
(
thd
->
mem_root
)
Item_func_eq
(
thd
,
var
,
when
);
i
=
new
(
thd
->
mem_root
)
sp_instr_jump_if_not
(
ip
,
spcont
,
expr
,
this
);
}
else
i
=
new
(
thd
->
mem_root
)
sp_instr_jump_if_not
(
ip
,
spcont
,
when
,
this
);
/*
BACKPATCH: Registering forward jump from
"case_stmt_action_when" to "case_stmt_action_then"
(jump_if_not from instruction 2 to 5, 5 to 8 ... in the example)
*/
return
!
MY_TEST
(
i
)
||
sphead
->
push_backpatch
(
thd
,
i
,
spcont
->
push_label
(
thd
,
&
empty_clex_str
,
0
))
||
sphead
->
add_cont_backpatch
(
i
)
||
sphead
->
add_instr
(
i
);
}
/**
Helper action for a case then statements.
This helper is used for both 'simple' and 'searched' cases.
@param lex the parser lex context
*/
int
LEX
::
case_stmt_action_then
()
{
uint
ip
=
sphead
->
instructions
();
sp_instr_jump
*
i
=
new
(
thd
->
mem_root
)
sp_instr_jump
(
ip
,
spcont
);
if
(
!
MY_TEST
(
i
)
||
sphead
->
add_instr
(
i
))
return
1
;
/*
BACKPATCH: Resolving forward jump from
"case_stmt_action_when" to "case_stmt_action_then"
(jump_if_not from instruction 2 to 5, 5 to 8 ... in the example)
*/
sphead
->
backpatch
(
spcont
->
pop_label
());
/*
BACKPATCH: Registering forward jump from
"case_stmt_action_then" to after END CASE
(jump from instruction 4 to 12, 7 to 12 ... in the example)
*/
return
sphead
->
push_backpatch
(
thd
,
i
,
spcont
->
last_label
());
}
/**
Helper action for a SET statement.
Used to push a system variable into the assignment list.
@param tmp the system variable with base name
@param var_type the scope of the variable
@param val the value being assigned to the variable
@return TRUE if error, FALSE otherwise.
*/
bool
LEX
::
set_system_variable
(
enum
enum_var_type
var_type
,
sys_var
*
sysvar
,
const
Lex_ident_sys_st
*
base_name
,
Item
*
val
)
{
set_var
*
setvar
;
/* No AUTOCOMMIT from a stored function or trigger. */
if
(
spcont
&&
sysvar
==
Sys_autocommit_ptr
)
sphead
->
m_flags
|=
sp_head
::
HAS_SET_AUTOCOMMIT_STMT
;
if
(
val
&&
val
->
type
()
==
Item
::
FIELD_ITEM
&&
((
Item_field
*
)
val
)
->
table_name
.
str
)
{
my_error
(
ER_WRONG_TYPE_FOR_VAR
,
MYF
(
0
),
sysvar
->
name
.
str
);
return
TRUE
;
}
if
(
!
(
setvar
=
new
(
thd
->
mem_root
)
set_var
(
thd
,
var_type
,
sysvar
,
base_name
,
val
)))
return
TRUE
;
return
var_list
.
push_back
(
setvar
,
thd
->
mem_root
);
}
/**
Helper action for a SET statement.
Used to SET a field of NEW row.
@param name the field name
@param val the value being assigned to the row
@return TRUE if error, FALSE otherwise.
*/
bool
LEX
::
set_trigger_new_row
(
const
LEX_CSTRING
*
name
,
Item
*
val
)
{
Item_trigger_field
*
trg_fld
;
sp_instr_set_trigger_field
*
sp_fld
;
/* QQ: Shouldn't this be field's default value ? */
if
(
!
val
)
val
=
new
(
thd
->
mem_root
)
Item_null
(
thd
);
DBUG_ASSERT
(
trg_chistics
.
action_time
==
TRG_ACTION_BEFORE
&&
(
trg_chistics
.
event
==
TRG_EVENT_INSERT
||
trg_chistics
.
event
==
TRG_EVENT_UPDATE
));
trg_fld
=
new
(
thd
->
mem_root
)
Item_trigger_field
(
thd
,
current_context
(),
Item_trigger_field
::
NEW_ROW
,
*
name
,
UPDATE_ACL
,
FALSE
);
if
(
unlikely
(
trg_fld
==
NULL
))
return
TRUE
;
sp_fld
=
new
(
thd
->
mem_root
)
sp_instr_set_trigger_field
(
sphead
->
instructions
(),
spcont
,
trg_fld
,
val
,
this
);
if
(
unlikely
(
sp_fld
==
NULL
))
return
TRUE
;
/*
Let us add this item to list of all Item_trigger_field
objects in trigger.
*/
trg_table_fields
.
link_in_list
(
trg_fld
,
&
trg_fld
->
next_trg_field
);
return
sphead
->
add_instr
(
sp_fld
);
}
/**
Create an object to represent a SP variable in the Item-hierarchy.
@param name The SP variable name.
@param spvar The SP variable (optional).
@param start_in_q Start position of the SP variable name in the query.
@param end_in_q End position of the SP variable name in the query.
@remark If spvar is not specified, the name is used to search for the
variable in the parse-time context. If the variable does not
exist, a error is set and NULL is returned to the caller.
@return An Item_splocal object representing the SP variable, or NULL on error.
*/
Item_splocal
*
LEX
::
create_item_for_sp_var
(
const
Lex_ident_cli_st
*
cname
,
sp_variable
*
spvar
)
{
const
Sp_rcontext_handler
*
rh
;
Item_splocal
*
item
;
const
char
*
start_in_q
=
cname
->
pos
();
const
char
*
end_in_q
=
cname
->
end
();
uint
pos_in_q
,
len_in_q
;
Lex_ident_sys
name
(
thd
,
cname
);
if
(
name
.
is_null
())
return
NULL
;
// EOM
/* If necessary, look for the variable. */
if
(
spcont
&&
!
spvar
)
spvar
=
find_variable
(
&
name
,
&
rh
);
if
(
!
spvar
)
{
my_error
(
ER_SP_UNDECLARED_VAR
,
MYF
(
0
),
name
.
str
);
return
NULL
;
}
DBUG_ASSERT
(
spcont
&&
spvar
);
/* Position and length of the SP variable name in the query. */
pos_in_q
=
(
uint
)(
start_in_q
-
sphead
->
m_tmp_query
);
len_in_q
=
(
uint
)(
end_in_q
-
start_in_q
);
item
=
new
(
thd
->
mem_root
)
Item_splocal
(
thd
,
rh
,
&
name
,
spvar
->
offset
,
spvar
->
type_handler
(),
pos_in_q
,
len_in_q
);
#ifdef DBUG_ASSERT_EXISTS
if
(
item
)
item
->
m_sp
=
sphead
;
#endif
return
item
;
}
/**
Helper to resolve the SQL:2003 Syntax exception 1) in <in predicate>.
See SQL:2003, Part 2, section 8.4 <in predicate>, Note 184, page 383.
This function returns the proper item for the SQL expression
<code>left [NOT] IN ( expr )</code>
@param thd the current thread
@param left the in predicand
@param equal true for IN predicates, false for NOT IN predicates
@param expr first and only expression of the in value list
@return an expression representing the IN predicate.
*/
Item
*
handle_sql2003_note184_exception
(
THD
*
thd
,
Item
*
left
,
bool
equal
,
Item
*
expr
)
{
/*
Relevant references for this issue:
- SQL:2003, Part 2, section 8.4 <in predicate>, page 383,
- SQL:2003, Part 2, section 7.2 <row value expression>, page 296,
- SQL:2003, Part 2, section 6.3 <value expression primary>, page 174,
- SQL:2003, Part 2, section 7.15 <subquery>, page 370,
- SQL:2003 Feature F561, "Full value expressions".
The exception in SQL:2003 Note 184 means:
Item_singlerow_subselect, which corresponds to a <scalar subquery>,
should be re-interpreted as an Item_in_subselect, which corresponds
to a <table subquery> when used inside an <in predicate>.
Our reading of Note 184 is reccursive, so that all:
- IN (( <subquery> ))
- IN ((( <subquery> )))
- IN '('^N <subquery> ')'^N
- etc
should be interpreted as a <table subquery>, no matter how deep in the
expression the <subquery> is.
*/
Item
*
result
;
DBUG_ENTER
(
"handle_sql2003_note184_exception"
);
if
(
expr
->
type
()
==
Item
::
SUBSELECT_ITEM
)
{
Item_subselect
*
expr2
=
(
Item_subselect
*
)
expr
;
if
(
expr2
->
substype
()
==
Item_subselect
::
SINGLEROW_SUBS
)
{
Item_singlerow_subselect
*
expr3
=
(
Item_singlerow_subselect
*
)
expr2
;
st_select_lex
*
subselect
;
/*
Implement the mandated change, by altering the semantic tree:
left IN Item_singlerow_subselect(subselect)
is modified to
left IN (subselect)
which is represented as
Item_in_subselect(left, subselect)
*/
subselect
=
expr3
->
invalidate_and_restore_select_lex
();
result
=
new
(
thd
->
mem_root
)
Item_in_subselect
(
thd
,
left
,
subselect
);
if
(
!
equal
)
result
=
negate_expression
(
thd
,
result
);
DBUG_RETURN
(
result
);
}
}
if
(
equal
)
result
=
new
(
thd
->
mem_root
)
Item_func_eq
(
thd
,
left
,
expr
);
else
result
=
new
(
thd
->
mem_root
)
Item_func_ne
(
thd
,
left
,
expr
);
DBUG_RETURN
(
result
);
}
/**
Create a separate LEX for each assignment if in SP.
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
overhead on preparation for execution stage and IMO less
robust).
QQ: May be we should simply prohibit group assignments in SP?
@see sp_create_assignment_instr
@param thd Thread context
@param pos The position in the raw SQL buffer
*/
bool
sp_create_assignment_lex
(
THD
*
thd
,
const
char
*
pos
)
{
if
(
thd
->
lex
->
sphead
)
{
sp_lex_local
*
new_lex
;
if
(
!
(
new_lex
=
new
(
thd
->
mem_root
)
sp_lex_set_var
(
thd
,
thd
->
lex
))
||
new_lex
->
main_select_push
())
return
true
;
new_lex
->
sphead
->
m_tmp_query
=
pos
;
return
thd
->
lex
->
sphead
->
reset_lex
(
thd
,
new_lex
);
}
return
false
;
}
/**
Create a SP instruction for a SET assignment.
@see sp_create_assignment_lex
@param thd - Thread context
@param no_lookahead - True if the parser has no lookahead
@param need_set_keyword - if a SET statement "SET a=10",
or a direct assignment overwise "a:=10"
@return false if success, true otherwise.
*/
bool
sp_create_assignment_instr
(
THD
*
thd
,
bool
no_lookahead
,
bool
need_set_keyword
)
{
LEX
*
lex
=
thd
->
lex
;
if
(
lex
->
sphead
)
{
if
(
!
lex
->
var_list
.
is_empty
())
{
/*
- Every variable assignment from the same SET command, e.g.:
SET @var1=expr1, @var2=expr2;
produce each own sp_create_assignment_instr() call
lex->var_list.elements is 1 in this case.
- This query:
SET TRANSACTION READ ONLY, ISOLATION LEVEL SERIALIZABLE;
in translated to:
SET tx_read_only=1, tx_isolation=ISO_SERIALIZABLE;
but produces a single sp_create_assignment_instr() call
which includes the query fragment covering both options.
*/
DBUG_ASSERT
(
lex
->
var_list
.
elements
>=
1
&&
lex
->
var_list
.
elements
<=
2
);
/*
sql_mode=ORACLE's direct assignment of a global variable
is not possible by the grammar.
*/
DBUG_ASSERT
(
lex
->
option_type
!=
OPT_GLOBAL
||
need_set_keyword
);
/*
We have assignment to user or system variable or
option setting, so we should construct sp_instr_stmt
for it.
*/
Lex_input_stream
*
lip
=
&
thd
->
m_parser_state
->
m_lip
;
/*
Extract the query statement from the tokenizer. The
end is either lip->ptr, if there was no lookahead,
lip->tok_end otherwise.
*/
static
const
LEX_CSTRING
setlc
=
{
STRING_WITH_LEN
(
"SET "
)
};
static
const
LEX_CSTRING
setgl
=
{
STRING_WITH_LEN
(
"SET GLOBAL "
)
};
const
char
*
qend
=
no_lookahead
?
lip
->
get_ptr
()
:
lip
->
get_tok_end
();
Lex_cstring
qbuf
(
lex
->
sphead
->
m_tmp_query
,
qend
);
if
(
lex
->
new_sp_instr_stmt
(
thd
,
lex
->
option_type
==
OPT_GLOBAL
?
setgl
:
need_set_keyword
?
setlc
:
null_clex_str
,
qbuf
))
return
true
;
}
lex
->
pop_select
();
if
(
lex
->
check_main_unit_semantics
())
{
/*
"lex" can be referrenced by:
- sp_instr_set SET a= expr;
- sp_instr_set_row_field SET r.a= expr;
- sp_instr_stmt (just generated above) SET @a= expr;
In this case, "lex" is fully owned by sp_instr_xxx and it will
be deleted by the destructor ~sp_instr_xxx().
So we should remove "lex" from the stack sp_head::m_lex,
to avoid double free.
Note, in case "lex" is not owned by any sp_instr_xxx,
it's also safe to remove it from the stack right now.
So we can remove it unconditionally, without testing lex->sp_lex_in_use.
*/
lex
->
sphead
->
restore_lex
(
thd
);
return
true
;
}
enum_var_type
inner_option_type
=
lex
->
option_type
;
if
(
lex
->
sphead
->
restore_lex
(
thd
))
return
true
;
/* Copy option_type to outer lex in case it has changed. */
thd
->
lex
->
option_type
=
inner_option_type
;
}
return
false
;
}
void
LEX
::
add_key_to_list
(
LEX_CSTRING
*
field_name
,
enum
Key
::
Keytype
type
,
bool
check_exists
)
{
Key
*
key
;
MEM_ROOT
*
mem_root
=
thd
->
mem_root
;
key
=
new
(
mem_root
)
Key
(
type
,
&
null_clex_str
,
HA_KEY_ALG_UNDEF
,
false
,
DDL_options
(
check_exists
?
DDL_options
::
OPT_IF_NOT_EXISTS
:
DDL_options
::
OPT_NONE
));
key
->
columns
.
push_back
(
new
(
mem_root
)
Key_part_spec
(
field_name
,
0
),
mem_root
);
alter_info
.
key_list
.
push_back
(
key
,
mem_root
);
}
bool
LEX
::
add_alter_list
(
const
char
*
name
,
Virtual_column_info
*
expr
,
bool
exists
)
{
MEM_ROOT
*
mem_root
=
thd
->
mem_root
;
Alter_column
*
ac
=
new
(
mem_root
)
Alter_column
(
name
,
expr
,
exists
);
if
(
unlikely
(
ac
==
NULL
))
return
true
;
alter_info
.
alter_list
.
push_back
(
ac
,
mem_root
);
alter_info
.
flags
|=
ALTER_CHANGE_COLUMN_DEFAULT
;
return
false
;
}
void
LEX
::
init_last_field
(
Column_definition
*
field
,
const
LEX_CSTRING
*
field_name
,
const
CHARSET_INFO
*
cs
)
{
last_field
=
field
;
field
->
field_name
=
*
field_name
;
/* reset LEX fields that are used in Create_field::set_and_check() */
charset
=
cs
;
}
bool
LEX
::
set_bincmp
(
CHARSET_INFO
*
cs
,
bool
bin
)
{
/*
if charset is NULL - we're parsing a field declaration.
we cannot call find_bin_collation for a field here, because actual
field charset is determined in get_sql_field_charset() much later.
so we only set a flag.
*/
if
(
!
charset
)
{
charset
=
cs
;
last_field
->
flags
|=
bin
?
BINCMP_FLAG
:
0
;
return
false
;
}
charset
=
bin
?
find_bin_collation
(
cs
?
cs
:
charset
)
:
cs
?
cs
:
charset
;
return
charset
==
NULL
;
}
Virtual_column_info
*
add_virtual_expression
(
THD
*
thd
,
Item
*
expr
)
{
Virtual_column_info
*
v
=
new
(
thd
->
mem_root
)
Virtual_column_info
();
if
(
unlikely
(
!
v
))
return
0
;
v
->
expr
=
expr
;
v
->
utf8
=
0
;
/* connection charset */
return
v
;
}
/**
@note The order of the elements of this array must correspond to
the order of elements in enum_binlog_stmt_unsafe.
...
...
sql/sql_yacc.yy
View file @
e26d0491
...
...
@@ -175,506 +175,6 @@ void turn_parser_debug_on()
#endif
/**
Helper action for a case expression statement (the expr in 'CASE expr').
This helper is used for 'searched' cases only.
@param lex the parser lex context
@param expr the parsed expression
@return 0 on success
*/
int LEX::case_stmt_action_expr(Item* expr)
{
int case_expr_id= spcont->register_case_expr();
sp_instr_set_case_expr *i;
if (spcont->push_case_expr_id(case_expr_id))
return 1;
i= new (thd->mem_root)
sp_instr_set_case_expr(sphead->instructions(), spcont, case_expr_id, expr,
this);
sphead->add_cont_backpatch(i);
return sphead->add_instr(i);
}
/**
Helper action for a case when condition.
This helper is used for both 'simple' and 'searched' cases.
@param lex the parser lex context
@param when the parsed expression for the WHEN clause
@param simple true for simple cases, false for searched cases
*/
int LEX::case_stmt_action_when(Item *when, bool simple)
{
uint ip= sphead->instructions();
sp_instr_jump_if_not *i;
Item_case_expr *var;
Item *expr;
if (simple)
{
var= new (thd->mem_root)
Item_case_expr(thd, spcont->get_current_case_expr_id());
#ifdef DBUG_ASSERT_EXISTS
if (var)
{
var->m_sp= sphead;
}
#endif
expr= new (thd->mem_root) Item_func_eq(thd, var, when);
i= new (thd->mem_root) sp_instr_jump_if_not(ip, spcont, expr, this);
}
else
i= new (thd->mem_root) sp_instr_jump_if_not(ip, spcont, when, this);
/*
BACKPATCH: Registering forward jump from
"case_stmt_action_when" to "case_stmt_action_then"
(jump_if_not from instruction 2 to 5, 5 to 8 ... in the example)
*/
return
!MY_TEST(i) ||
sphead->push_backpatch(thd, i, spcont->push_label(thd, &empty_clex_str, 0)) ||
sphead->add_cont_backpatch(i) ||
sphead->add_instr(i);
}
/**
Helper action for a case then statements.
This helper is used for both 'simple' and 'searched' cases.
@param lex the parser lex context
*/
int LEX::case_stmt_action_then()
{
uint ip= sphead->instructions();
sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(ip, spcont);
if (!MY_TEST(i) || sphead->add_instr(i))
return 1;
/*
BACKPATCH: Resolving forward jump from
"case_stmt_action_when" to "case_stmt_action_then"
(jump_if_not from instruction 2 to 5, 5 to 8 ... in the example)
*/
sphead->backpatch(spcont->pop_label());
/*
BACKPATCH: Registering forward jump from
"case_stmt_action_then" to after END CASE
(jump from instruction 4 to 12, 7 to 12 ... in the example)
*/
return sphead->push_backpatch(thd, i, spcont->last_label());
}
/**
Helper action for a SET statement.
Used to push a system variable into the assignment list.
@param tmp the system variable with base name
@param var_type the scope of the variable
@param val the value being assigned to the variable
@return TRUE if error, FALSE otherwise.
*/
bool
LEX::set_system_variable(enum enum_var_type var_type,
sys_var *sysvar, const Lex_ident_sys_st *base_name,
Item *val)
{
set_var *setvar;
/* No AUTOCOMMIT from a stored function or trigger. */
if (spcont && sysvar == Sys_autocommit_ptr)
sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
if (val && val->type() == Item::FIELD_ITEM &&
((Item_field*)val)->table_name.str)
{
my_error(ER_WRONG_TYPE_FOR_VAR, MYF(0), sysvar->name.str);
return TRUE;
}
if (!(setvar= new (thd->mem_root) set_var(thd, var_type, sysvar,
base_name, val)))
return TRUE;
return var_list.push_back(setvar, thd->mem_root);
}
/**
Helper action for a SET statement.
Used to SET a field of NEW row.
@param name the field name
@param val the value being assigned to the row
@return TRUE if error, FALSE otherwise.
*/
bool LEX::set_trigger_new_row(const LEX_CSTRING *name, Item *val)
{
Item_trigger_field *trg_fld;
sp_instr_set_trigger_field *sp_fld;
/* QQ: Shouldn't this be field's default value ? */
if (! val)
val= new (thd->mem_root) Item_null(thd);
DBUG_ASSERT(trg_chistics.action_time == TRG_ACTION_BEFORE &&
(trg_chistics.event == TRG_EVENT_INSERT ||
trg_chistics.event == TRG_EVENT_UPDATE));
trg_fld= new (thd->mem_root)
Item_trigger_field(thd, current_context(),
Item_trigger_field::NEW_ROW,
*name, UPDATE_ACL, FALSE);
if (unlikely(trg_fld == NULL))
return TRUE;
sp_fld= new (thd->mem_root)
sp_instr_set_trigger_field(sphead->instructions(),
spcont, trg_fld, val, this);
if (unlikely(sp_fld == NULL))
return TRUE;
/*
Let us add this item to list of all Item_trigger_field
objects in trigger.
*/
trg_table_fields.link_in_list(trg_fld, &trg_fld->next_trg_field);
return sphead->add_instr(sp_fld);
}
/**
Create an object to represent a SP variable in the Item-hierarchy.
@param name The SP variable name.
@param spvar The SP variable (optional).
@param start_in_q Start position of the SP variable name in the query.
@param end_in_q End position of the SP variable name in the query.
@remark If spvar is not specified, the name is used to search for the
variable in the parse-time context. If the variable does not
exist, a error is set and NULL is returned to the caller.
@return An Item_splocal object representing the SP variable, or NULL on error.
*/
Item_splocal*
LEX::create_item_for_sp_var(const Lex_ident_cli_st *cname, sp_variable *spvar)
{
const Sp_rcontext_handler *rh;
Item_splocal *item;
const char *start_in_q= cname->pos();
const char *end_in_q= cname->end();
uint pos_in_q, len_in_q;
Lex_ident_sys name(thd, cname);
if (name.is_null())
return NULL; // EOM
/* If necessary, look for the variable. */
if (spcont && !spvar)
spvar= find_variable(&name, &rh);
if (!spvar)
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0), name.str);
return NULL;
}
DBUG_ASSERT(spcont && spvar);
/* Position and length of the SP variable name in the query. */
pos_in_q= (uint)(start_in_q - sphead->m_tmp_query);
len_in_q= (uint)(end_in_q - start_in_q);
item= new (thd->mem_root)
Item_splocal(thd, rh, &name, spvar->offset, spvar->type_handler(),
pos_in_q, len_in_q);
#ifdef DBUG_ASSERT_EXISTS
if (item)
item->m_sp= sphead;
#endif
return item;
}
/**
Helper to resolve the SQL:2003 Syntax exception 1) in <in predicate>.
See SQL:2003, Part 2, section 8.4 <in predicate>, Note 184, page 383.
This function returns the proper item for the SQL expression
<code>left [NOT] IN ( expr )</code>
@param thd the current thread
@param left the in predicand
@param equal true for IN predicates, false for NOT IN predicates
@param expr first and only expression of the in value list
@return an expression representing the IN predicate.
*/
Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal,
Item *expr)
{
/*
Relevant references for this issue:
- SQL:2003, Part 2, section 8.4 <in predicate>, page 383,
- SQL:2003, Part 2, section 7.2 <row value expression>, page 296,
- SQL:2003, Part 2, section 6.3 <value expression primary>, page 174,
- SQL:2003, Part 2, section 7.15 <subquery>, page 370,
- SQL:2003 Feature F561, "Full value expressions".
The exception in SQL:2003 Note 184 means:
Item_singlerow_subselect, which corresponds to a <scalar subquery>,
should be re-interpreted as an Item_in_subselect, which corresponds
to a <table subquery> when used inside an <in predicate>.
Our reading of Note 184 is reccursive, so that all:
- IN (( <subquery> ))
- IN ((( <subquery> )))
- IN '('^N <subquery> ')'^N
- etc
should be interpreted as a <table subquery>, no matter how deep in the
expression the <subquery> is.
*/
Item *result;
DBUG_ENTER("handle_sql2003_note184_exception");
if (expr->type() == Item::SUBSELECT_ITEM)
{
Item_subselect *expr2 = (Item_subselect*) expr;
if (expr2->substype() == Item_subselect::SINGLEROW_SUBS)
{
Item_singlerow_subselect *expr3 = (Item_singlerow_subselect*) expr2;
st_select_lex *subselect;
/*
Implement the mandated change, by altering the semantic tree:
left IN Item_singlerow_subselect(subselect)
is modified to
left IN (subselect)
which is represented as
Item_in_subselect(left, subselect)
*/
subselect= expr3->invalidate_and_restore_select_lex();
result= new (thd->mem_root) Item_in_subselect(thd, left, subselect);
if (! equal)
result = negate_expression(thd, result);
DBUG_RETURN(result);
}
}
if (equal)
result= new (thd->mem_root) Item_func_eq(thd, left, expr);
else
result= new (thd->mem_root) Item_func_ne(thd, left, expr);
DBUG_RETURN(result);
}
/**
Create a separate LEX for each assignment if in SP.
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
overhead on preparation for execution stage and IMO less
robust).
QQ: May be we should simply prohibit group assignments in SP?
@see sp_create_assignment_instr
@param thd Thread context
@param pos The position in the raw SQL buffer
*/
bool sp_create_assignment_lex(THD *thd, const char *pos)
{
if (thd->lex->sphead)
{
sp_lex_local *new_lex;
if (!(new_lex= new (thd->mem_root) sp_lex_set_var(thd, thd->lex)) ||
new_lex->main_select_push())
return true;
new_lex->sphead->m_tmp_query= pos;
return thd->lex->sphead->reset_lex(thd, new_lex);
}
return false;
}
/**
Create a SP instruction for a SET assignment.
@see sp_create_assignment_lex
@param thd - Thread context
@param no_lookahead - True if the parser has no lookahead
@param need_set_keyword - if a SET statement "SET a=10",
or a direct assignment overwise "a:=10"
@return false if success, true otherwise.
*/
bool sp_create_assignment_instr(THD *thd, bool no_lookahead,
bool need_set_keyword)
{
LEX *lex= thd->lex;
if (lex->sphead)
{
if (!lex->var_list.is_empty())
{
/*
- Every variable assignment from the same SET command, e.g.:
SET @var1=expr1, @var2=expr2;
produce each own sp_create_assignment_instr() call
lex->var_list.elements is 1 in this case.
- This query:
SET TRANSACTION READ ONLY, ISOLATION LEVEL SERIALIZABLE;
in translated to:
SET tx_read_only=1, tx_isolation=ISO_SERIALIZABLE;
but produces a single sp_create_assignment_instr() call
which includes the query fragment covering both options.
*/
DBUG_ASSERT(lex->var_list.elements >= 1 && lex->var_list.elements <= 2);
/*
sql_mode=ORACLE's direct assignment of a global variable
is not possible by the grammar.
*/
DBUG_ASSERT(Lex->option_type != OPT_GLOBAL || need_set_keyword);
/*
We have assignment to user or system variable or
option setting, so we should construct sp_instr_stmt
for it.
*/
Lex_input_stream *lip= &thd->m_parser_state->m_lip;
/*
Extract the query statement from the tokenizer. The
end is either lip->ptr, if there was no lookahead,
lip->tok_end otherwise.
*/
static const LEX_CSTRING setlc= { STRING_WITH_LEN("SET ") };
static const LEX_CSTRING setgl= { STRING_WITH_LEN("SET GLOBAL ") };
const char *qend= no_lookahead ? lip->get_ptr() : lip->get_tok_end();
Lex_cstring qbuf(lex->sphead->m_tmp_query, qend);
if (lex->new_sp_instr_stmt(thd,
Lex->option_type == OPT_GLOBAL ? setgl :
need_set_keyword ? setlc :
null_clex_str,
qbuf))
return true;
}
lex->pop_select();
if (lex->check_main_unit_semantics())
{
/*
"lex" can be referrenced by:
- sp_instr_set SET a= expr;
- sp_instr_set_row_field SET r.a= expr;
- sp_instr_stmt (just generated above) SET @a= expr;
In this case, "lex" is fully owned by sp_instr_xxx and it will
be deleted by the destructor ~sp_instr_xxx().
So we should remove "lex" from the stack sp_head::m_lex,
to avoid double free.
Note, in case "lex" is not owned by any sp_instr_xxx,
it's also safe to remove it from the stack right now.
So we can remove it unconditionally, without testing lex->sp_lex_in_use.
*/
lex->sphead->restore_lex(thd);
return true;
}
enum_var_type inner_option_type= lex->option_type;
if (lex->sphead->restore_lex(thd))
return true;
/* Copy option_type to outer lex in case it has changed. */
thd->lex->option_type= inner_option_type;
}
return false;
}
void LEX::add_key_to_list(LEX_CSTRING *field_name,
enum Key::Keytype type, bool check_exists)
{
Key *key;
MEM_ROOT *mem_root= thd->mem_root;
key= new (mem_root)
Key(type, &null_clex_str, HA_KEY_ALG_UNDEF, false,
DDL_options(check_exists ?
DDL_options::OPT_IF_NOT_EXISTS :
DDL_options::OPT_NONE));
key->columns.push_back(new (mem_root) Key_part_spec(field_name, 0),
mem_root);
alter_info.key_list.push_back(key, mem_root);
}
bool LEX::add_alter_list(const char *name, Virtual_column_info *expr,
bool exists)
{
MEM_ROOT *mem_root= thd->mem_root;
Alter_column *ac= new (mem_root) Alter_column(name, expr, exists);
if (unlikely(ac == NULL))
return true;
alter_info.alter_list.push_back(ac, mem_root);
alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT;
return false;
}
void LEX::init_last_field(Column_definition *field,
const LEX_CSTRING *field_name,
const CHARSET_INFO *cs)
{
last_field= field;
field->field_name= *field_name;
/* reset LEX fields that are used in Create_field::set_and_check() */
charset= cs;
}
bool LEX::set_bincmp(CHARSET_INFO *cs, bool bin)
{
/*
if charset is NULL - we're parsing a field declaration.
we cannot call find_bin_collation for a field here, because actual
field charset is determined in get_sql_field_charset() much later.
so we only set a flag.
*/
if (!charset)
{
charset= cs;
last_field->flags|= bin ? BINCMP_FLAG : 0;
return false;
}
charset= bin ? find_bin_collation(cs ? cs : charset)
: cs ? cs : charset;
return charset == NULL;
}
#define bincmp_collation(X,Y) \
do \
{ \
...
...
@@ -682,18 +182,6 @@ bool LEX::set_bincmp(CHARSET_INFO *cs, bool bin)
MYSQL_YYABORT; \
} while(0)
Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
{
Virtual_column_info *v= new (thd->mem_root) Virtual_column_info();
if (unlikely(!v))
return 0;
v->expr= expr;
v->utf8= 0; /* connection charset */
return v;
}
%}
%union {
int num;
...
...
sql/sql_yacc_ora.yy
View file @
e26d0491
...
...
@@ -152,8 +152,6 @@ void ORAerror(THD *thd, const char *s)
}
#define bincmp_collation(X,Y) \
do \
{ \
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment