Commit 34f36a33 authored by halfspawn's avatar halfspawn

MDEV-14012 - sql_mode=Oracle: substr(): treat position 0 as position 1

parent 3e397715
SET sql_mode=ORACLE;
SELECT SUBSTR('abc',2,1),SUBSTR('abc',1,1), SUBSTR('abc',0,1) FROM dual;
SUBSTR('abc',2,1) SUBSTR('abc',1,1) SUBSTR('abc',0,1)
b a a
SELECT SUBSTR('abc',2),SUBSTR('abc',1), SUBSTR('abc',0) FROM dual;
SUBSTR('abc',2) SUBSTR('abc',1) SUBSTR('abc',0)
bc abc abc
SELECT SUBSTR(null,2,1),SUBSTR(null,1), SUBSTR(null,0) FROM dual;
SUBSTR(null,2,1) SUBSTR(null,1) SUBSTR(null,0)
NULL NULL NULL
SELECT SUBSTR('abc',-2),SUBSTR('abc',-1), SUBSTR('abc',-0) FROM dual;
SUBSTR('abc',-2) SUBSTR('abc',-1) SUBSTR('abc',-0)
bc c abc
SELECT SUBSTR('abc',-2,1),SUBSTR('abc',-1,1), SUBSTR('abc',-0,1) FROM dual;
SUBSTR('abc',-2,1) SUBSTR('abc',-1,1) SUBSTR('abc',-0,1)
b c a
SELECT SUBSTR('abc',null) FROM dual;
SUBSTR('abc',null)
NULL
SELECT SUBSTR('abc',2,null),SUBSTR('abc',1,null), SUBSTR('abc',0,null) FROM dual;
SUBSTR('abc',2,null) SUBSTR('abc',1,null) SUBSTR('abc',0,null)
NULL NULL NULL
SELECT SUBSTR('abc',2,0),SUBSTR('abc',1,0), SUBSTR('abc',0,0) FROM dual;
SUBSTR('abc',2,0) SUBSTR('abc',1,0) SUBSTR('abc',0,0)
create table t1 (c1 varchar(10),start integer, length integer);
INSERT INTO t1 VALUES ('abc', 1, 1);
INSERT INTO t1 VALUES ('abc', 0, 1);
INSERT INTO t1 VALUES (null, 1, 1);
INSERT INTO t1 VALUES (null, 0, 1);
select substr(c1,start,length) from t1;
substr(c1,start,length)
a
a
NULL
NULL
drop table t1;
EXPLAIN EXTENDED SELECT SUBSTR('abc',2,1) ;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select substr_oracle('abc',2,1) AS "SUBSTR('abc',2,1)"
CREATE VIEW v1 AS SELECT SUBSTR('abc',2,1) ;
SHOW CREATE VIEW v1;
View Create View character_set_client collation_connection
v1 CREATE VIEW "v1" AS select substr_oracle('abc',2,1) AS "SUBSTR('abc',2,1)" latin1 latin1_swedish_ci
SELECT * FROM v1;
SUBSTR('abc',2,1)
b
DROP VIEW v1;
#
# MDEV-14012 - sql_mode=Oracle: substr(): treat position 0 as position 1
#
SET sql_mode=ORACLE;
SELECT SUBSTR('abc',2,1),SUBSTR('abc',1,1), SUBSTR('abc',0,1) FROM dual;
SELECT SUBSTR('abc',2),SUBSTR('abc',1), SUBSTR('abc',0) FROM dual;
SELECT SUBSTR(null,2,1),SUBSTR(null,1), SUBSTR(null,0) FROM dual;
SELECT SUBSTR('abc',-2),SUBSTR('abc',-1), SUBSTR('abc',-0) FROM dual;
SELECT SUBSTR('abc',-2,1),SUBSTR('abc',-1,1), SUBSTR('abc',-0,1) FROM dual;
SELECT SUBSTR('abc',null) FROM dual;
SELECT SUBSTR('abc',2,null),SUBSTR('abc',1,null), SUBSTR('abc',0,null) FROM dual;
SELECT SUBSTR('abc',2,0),SUBSTR('abc',1,0), SUBSTR('abc',0,0) FROM dual;
create table t1 (c1 varchar(10),start integer, length integer);
INSERT INTO t1 VALUES ('abc', 1, 1);
INSERT INTO t1 VALUES ('abc', 0, 1);
INSERT INTO t1 VALUES (null, 1, 1);
INSERT INTO t1 VALUES (null, 0, 1);
select substr(c1,start,length) from t1;
drop table t1;
EXPLAIN EXTENDED SELECT SUBSTR('abc',2,1) ;
CREATE VIEW v1 AS SELECT SUBSTR('abc',2,1) ;
SHOW CREATE VIEW v1;
SELECT * FROM v1;
DROP VIEW v1;
......@@ -2876,6 +2876,20 @@ class Create_func_substr_index : public Create_func_arg3
};
class Create_func_substr_oracle : public Create_native_func
{
public:
virtual Item *create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list);
static Create_func_substr_oracle s_singleton;
protected:
Create_func_substr_oracle() {}
virtual ~Create_func_substr_oracle() {}
};
class Create_func_subtime : public Create_func_arg2
{
public:
......@@ -6466,6 +6480,40 @@ Create_func_substr_index::create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *a
}
Create_func_substr_oracle Create_func_substr_oracle::s_singleton;
Item*
Create_func_substr_oracle::create_native(THD *thd, LEX_CSTRING *name,
List<Item> *item_list)
{
Item *func= NULL;
int arg_count= item_list ? item_list->elements : 0;
switch (arg_count) {
case 2:
{
Item *param_1= item_list->pop();
Item *param_2= item_list->pop();
func= new (thd->mem_root) Item_func_substr_oracle(thd, param_1, param_2);
break;
}
case 3:
{
Item *param_1= item_list->pop();
Item *param_2= item_list->pop();
Item *param_3= item_list->pop();
func= new (thd->mem_root) Item_func_substr_oracle(thd, param_1, param_2, param_3);
break;
}
default:
my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
break;
}
return func;
}
Create_func_subtime Create_func_subtime::s_singleton;
Item*
......@@ -7119,6 +7167,8 @@ static Native_func_registry func_array[] =
{ { C_STRING_WITH_LEN("ST_WITHIN") }, GEOM_BUILDER(Create_func_within)},
{ { C_STRING_WITH_LEN("ST_X") }, GEOM_BUILDER(Create_func_x)},
{ { C_STRING_WITH_LEN("ST_Y") }, GEOM_BUILDER(Create_func_y)},
{ { C_STRING_WITH_LEN("SUBSTR_ORACLE") },
BUILDER(Create_func_substr_oracle)},
{ { C_STRING_WITH_LEN("SUBSTRING_INDEX") }, BUILDER(Create_func_substr_index)},
{ { C_STRING_WITH_LEN("SUBTIME") }, BUILDER(Create_func_subtime)},
{ { C_STRING_WITH_LEN("TAN") }, BUILDER(Create_func_tan)},
......
......@@ -1777,7 +1777,7 @@ String *Item_func_substr::val_str(String *str)
DBUG_ASSERT(fixed == 1);
String *res = args[0]->val_str(str);
/* must be longlong to avoid truncation */
longlong start= args[1]->val_int();
longlong start= get_position();
/* Assumes that the maximum length of a String is < INT_MAX32. */
/* Limit so that code sees out-of-bound value properly. */
longlong length= arg_count == 3 ? args[2]->val_int() : INT_MAX32;
......@@ -1827,7 +1827,7 @@ void Item_func_substr::fix_length_and_dec()
DBUG_ASSERT(collation.collation != NULL);
if (args[1]->const_item())
{
int32 start= (int32) args[1]->val_int();
int32 start= (int32) get_position();
if (args[1]->null_value)
max_length= 0;
else if (start < 0)
......
......@@ -505,9 +505,12 @@ class Item_func_right :public Item_str_func
class Item_func_substr :public Item_str_func
{
String tmp_value;
protected:
virtual longlong get_position() { return args[1]->val_int(); }
public:
Item_func_substr(THD *thd, Item *a, Item *b): Item_str_func(thd, a, b) {}
Item_func_substr(THD *thd, Item *a, Item *b, Item *c): Item_str_func(thd, a, b, c) {}
Item_func_substr(THD *thd, Item *a, Item *b, Item *c):
Item_str_func(thd, a, b, c) {}
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "substr"; }
......@@ -515,6 +518,20 @@ class Item_func_substr :public Item_str_func
{ return get_item_copy<Item_func_substr>(thd, mem_root, this); }
};
class Item_func_substr_oracle :public Item_func_substr
{
protected:
longlong get_position()
{ longlong pos= args[1]->val_int(); return pos == 0 ? 1 : pos; }
public:
Item_func_substr_oracle(THD *thd, Item *a, Item *b):
Item_func_substr(thd, a, b) {}
Item_func_substr_oracle(THD *thd, Item *a, Item *b, Item *c):
Item_func_substr(thd, a, b, c) {}
const char *func_name() const { return "substr_oracle"; }
Item *get_copy(THD *thd, MEM_ROOT *mem_root)
{ return get_item_copy<Item_func_substr_oracle>(thd, mem_root, this); }
};
class Item_func_substr_index :public Item_str_func
{
......
......@@ -7182,6 +7182,22 @@ bool LEX::add_grant_command(THD *thd, enum_sql_command sql_command_arg,
}
Item *LEX::make_item_func_substr(THD *thd, Item *a, Item *b, Item *c)
{
return (thd->variables.sql_mode & MODE_ORACLE) ?
new (thd->mem_root) Item_func_substr_oracle(thd, a, b, c) :
new (thd->mem_root) Item_func_substr(thd, a, b, c);
}
Item *LEX::make_item_func_substr(THD *thd, Item *a, Item *b)
{
return (thd->variables.sql_mode & MODE_ORACLE) ?
new (thd->mem_root) Item_func_substr_oracle(thd, a, b) :
new (thd->mem_root) Item_func_substr(thd, a, b);
}
Item *LEX::make_item_func_replace(THD *thd,
Item *org,
Item *find,
......
......@@ -3355,7 +3355,8 @@ struct LEX: public Query_tables_list
const char *end);
Item *make_item_func_replace(THD *thd, Item *org, Item *find, Item *replace);
Item *make_item_func_substr(THD *thd, Item *a, Item *b, Item *c);
Item *make_item_func_substr(THD *thd, Item *a, Item *b);
/*
Create a my_var instance for a ROW field variable that was used
as an OUT SP parameter: CALL p1(var.field);
......
......@@ -9865,26 +9865,22 @@ function_call_nonkeyword:
}
| SUBSTRING '(' expr ',' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_func_substr(thd, $3, $5, $7);
if ($$ == NULL)
if (!($$= Lex->make_item_func_substr(thd, $3, $5, $7)))
MYSQL_YYABORT;
}
| SUBSTRING '(' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_func_substr(thd, $3, $5);
if ($$ == NULL)
if (!($$= Lex->make_item_func_substr(thd, $3, $5)))
MYSQL_YYABORT;
}
| SUBSTRING '(' expr FROM expr FOR_SYM expr ')'
{
$$= new (thd->mem_root) Item_func_substr(thd, $3, $5, $7);
if ($$ == NULL)
if (!($$= Lex->make_item_func_substr(thd, $3, $5, $7)))
MYSQL_YYABORT;
}
| SUBSTRING '(' expr FROM expr ')'
{
$$= new (thd->mem_root) Item_func_substr(thd, $3, $5);
if ($$ == NULL)
if (!($$= Lex->make_item_func_substr(thd, $3, $5)))
MYSQL_YYABORT;
}
| SYSDATE opt_time_precision
......
......@@ -9895,26 +9895,22 @@ function_call_nonkeyword:
}
| SUBSTRING '(' expr ',' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_func_substr(thd, $3, $5, $7);
if ($$ == NULL)
if (!($$= Lex->make_item_func_substr(thd, $3, $5, $7)))
MYSQL_YYABORT;
}
| SUBSTRING '(' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_func_substr(thd, $3, $5);
if ($$ == NULL)
if (!($$= Lex->make_item_func_substr(thd, $3, $5)))
MYSQL_YYABORT;
}
| SUBSTRING '(' expr FROM expr FOR_SYM expr ')'
{
$$= new (thd->mem_root) Item_func_substr(thd, $3, $5, $7);
if ($$ == NULL)
if (!($$= Lex->make_item_func_substr(thd, $3, $5, $7)))
MYSQL_YYABORT;
}
| SUBSTRING '(' expr FROM expr ')'
{
$$= new (thd->mem_root) Item_func_substr(thd, $3, $5);
if ($$ == NULL)
if (!($$= Lex->make_item_func_substr(thd, $3, $5)))
MYSQL_YYABORT;
}
| SYSDATE opt_time_precision
......
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