mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
MDEV-29095 REGEXP_REPLACE treats empty strings different than REPLACE in ORACLE mode
Turning REGEXP_REPLACE into two schema-qualified functions: - mariadb_schema.regexp_replace() - oracle_schema.regexp_replace() Fixing oracle_schema.regexp_replace(subj,pattern,replacement) to treat NULL in "replacement" as an empty string. Adding new classes implementing oracle_schema.regexp_replace(): - Item_func_regexp_replace_oracle - Create_func_regexp_replace_oracle Adding helper methods: - String *Item::val_str_null_to_empty(String *to) - String *Item::val_str_null_to_empty(String *to, bool null_to_empty) and reusing these methods in both Item_func_replace and Item_func_regexp_replace.
This commit is contained in:
@ -1772,6 +1772,85 @@ Level Code Message
|
||||
Note 1003 select trim(both ' ' from 'a') AS "oracle_schema.TRIM(BOTH ' ' FROM 'a')"
|
||||
Warnings:
|
||||
Note 1003 select trim(both ' ' from 'a') AS "oracle_schema.TRIM(BOTH ' ' FROM 'a')"
|
||||
CALL p3('REGEXP_REPLACE(''test'',''t'','''')');
|
||||
----------
|
||||
sql_mode='' qualifier=''
|
||||
query
|
||||
EXPLAIN EXTENDED SELECT REGEXP_REPLACE('test','t','')
|
||||
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
|
||||
Level Code Message
|
||||
Note 1003 select regexp_replace('test','t','') AS `REGEXP_REPLACE('test','t','')`
|
||||
----------
|
||||
sql_mode='' qualifier='unknown_schema.'
|
||||
query
|
||||
EXPLAIN EXTENDED SELECT unknown_schema.REGEXP_REPLACE('test','t','')
|
||||
errmsg
|
||||
ERROR: FUNCTION unknown_schema.REGEXP_REPLACE does not exist
|
||||
----------
|
||||
sql_mode='' qualifier='mariadb_schema.'
|
||||
query
|
||||
EXPLAIN EXTENDED SELECT mariadb_schema.REGEXP_REPLACE('test','t','')
|
||||
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
|
||||
Level Code Message
|
||||
Note 1003 select regexp_replace('test','t','') AS `mariadb_schema.REGEXP_REPLACE('test','t','')`
|
||||
----------
|
||||
sql_mode='' qualifier='maxdb_schema.'
|
||||
query
|
||||
EXPLAIN EXTENDED SELECT maxdb_schema.REGEXP_REPLACE('test','t','')
|
||||
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
|
||||
Level Code Message
|
||||
Note 1003 select regexp_replace('test','t','') AS `maxdb_schema.REGEXP_REPLACE('test','t','')`
|
||||
----------
|
||||
sql_mode='' qualifier='oracle_schema.'
|
||||
query
|
||||
EXPLAIN EXTENDED SELECT oracle_schema.REGEXP_REPLACE('test','t','')
|
||||
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
|
||||
Level Code Message
|
||||
Note 1003 select oracle_schema.regexp_replace('test','t','') AS `oracle_schema.REGEXP_REPLACE('test','t','')`
|
||||
----------
|
||||
sql_mode='ORACLE' qualifier=''
|
||||
query
|
||||
EXPLAIN EXTENDED SELECT REGEXP_REPLACE('test','t','')
|
||||
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
|
||||
Level Code Message
|
||||
Note 1003 select regexp_replace('test','t','') AS "REGEXP_REPLACE('test','t','')"
|
||||
----------
|
||||
sql_mode='ORACLE' qualifier='unknown_schema.'
|
||||
query
|
||||
EXPLAIN EXTENDED SELECT unknown_schema.REGEXP_REPLACE('test','t','')
|
||||
errmsg
|
||||
ERROR: FUNCTION unknown_schema.REGEXP_REPLACE does not exist
|
||||
----------
|
||||
sql_mode='ORACLE' qualifier='mariadb_schema.'
|
||||
query
|
||||
EXPLAIN EXTENDED SELECT mariadb_schema.REGEXP_REPLACE('test','t','')
|
||||
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
|
||||
Level Code Message
|
||||
Note 1003 select mariadb_schema.regexp_replace('test','t','') AS "mariadb_schema.REGEXP_REPLACE('test','t','')"
|
||||
----------
|
||||
sql_mode='ORACLE' qualifier='maxdb_schema.'
|
||||
query
|
||||
EXPLAIN EXTENDED SELECT maxdb_schema.REGEXP_REPLACE('test','t','')
|
||||
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
|
||||
Level Code Message
|
||||
Note 1003 select mariadb_schema.regexp_replace('test','t','') AS "maxdb_schema.REGEXP_REPLACE('test','t','')"
|
||||
----------
|
||||
sql_mode='ORACLE' qualifier='oracle_schema.'
|
||||
query
|
||||
EXPLAIN EXTENDED SELECT oracle_schema.REGEXP_REPLACE('test','t','')
|
||||
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
|
||||
Level Code Message
|
||||
Note 1003 select regexp_replace('test','t','') AS "oracle_schema.REGEXP_REPLACE('test','t','')"
|
||||
Warnings:
|
||||
Note 1003 select regexp_replace('test','t','') AS "oracle_schema.REGEXP_REPLACE('test','t','')"
|
||||
CALL p3('CONCAT_OPERATOR_ORACLE(''a'')');
|
||||
----------
|
||||
sql_mode='' qualifier=''
|
||||
|
34
mysql-test/suite/compat/oracle/r/func_regexp_replace.result
Normal file
34
mysql-test/suite/compat/oracle/r/func_regexp_replace.result
Normal file
@ -0,0 +1,34 @@
|
||||
SET sql_mode=ORACLE;
|
||||
#
|
||||
# MDEV-29095 REGEXP_REPLACE treats empty strings different than REPLACE in ORACLE mode
|
||||
#
|
||||
CREATE TABLE t1 (replacement VARCHAR(10));
|
||||
INSERT INTO t1 VALUES (NULL), ('');
|
||||
SELECT replacement, REGEXP_REPLACE('abba','a',replacement) FROM t1 ORDER BY replacement;
|
||||
replacement REGEXP_REPLACE('abba','a',replacement)
|
||||
NULL bb
|
||||
bb
|
||||
DROP TABLE t1;
|
||||
SELECT REGEXP_REPLACE('abba','a',null);
|
||||
REGEXP_REPLACE('abba','a',null)
|
||||
bb
|
||||
EXPLAIN EXTENDED SELECT REPLACE('abba','a',null) ;
|
||||
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 replace('abba','a',NULL) AS "REPLACE('abba','a',null)"
|
||||
CREATE VIEW v1 AS SELECT REPLACE('abba','a',null) ;
|
||||
SHOW CREATE VIEW v1;
|
||||
View Create View character_set_client collation_connection
|
||||
v1 CREATE VIEW "v1" AS select replace('abba','a',NULL) AS "REPLACE('abba','a',null)" latin1 latin1_swedish_ci
|
||||
SELECT * FROM v1;
|
||||
REPLACE('abba','a',null)
|
||||
bb
|
||||
SET sql_mode=DEFAULT;
|
||||
SHOW CREATE VIEW v1;
|
||||
View Create View character_set_client collation_connection
|
||||
v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select oracle_schema.replace('abba','a',NULL) AS `REPLACE('abba','a',null)` latin1 latin1_swedish_ci
|
||||
SELECT * FROM v1;
|
||||
REPLACE('abba','a',null)
|
||||
bb
|
||||
DROP VIEW v1;
|
@ -165,6 +165,7 @@ CALL p3('TRIM(1,2)');
|
||||
CALL p3('TRIM(''a'')');
|
||||
CALL p3('TRIM(BOTH '' '' FROM ''a'')');
|
||||
|
||||
CALL p3('REGEXP_REPLACE(''test'',''t'','''')');
|
||||
|
||||
# Deprecated compatibility XXX_ORACLE functions.
|
||||
# These functions are implemented as simple native functions
|
||||
|
26
mysql-test/suite/compat/oracle/t/func_regexp_replace.test
Normal file
26
mysql-test/suite/compat/oracle/t/func_regexp_replace.test
Normal file
@ -0,0 +1,26 @@
|
||||
SET sql_mode=ORACLE;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-29095 REGEXP_REPLACE treats empty strings different than REPLACE in ORACLE mode
|
||||
--echo #
|
||||
|
||||
#SELECT REGEXP_REPLACE(null,'a','b') ;
|
||||
#SELECT REGEXP_REPLACE('ab',null,'b') ;
|
||||
#SELECT REGEXP_REPLACE('ab','a',null) ;
|
||||
#SELECT REGEXP_REPLACE('ab',null,null) ;
|
||||
|
||||
CREATE TABLE t1 (replacement VARCHAR(10));
|
||||
INSERT INTO t1 VALUES (NULL), ('');
|
||||
SELECT replacement, REGEXP_REPLACE('abba','a',replacement) FROM t1 ORDER BY replacement;
|
||||
DROP TABLE t1;
|
||||
|
||||
SELECT REGEXP_REPLACE('abba','a',null);
|
||||
EXPLAIN EXTENDED SELECT REPLACE('abba','a',null) ;
|
||||
|
||||
CREATE VIEW v1 AS SELECT REPLACE('abba','a',null) ;
|
||||
SHOW CREATE VIEW v1;
|
||||
SELECT * FROM v1;
|
||||
SET sql_mode=DEFAULT;
|
||||
SHOW CREATE VIEW v1;
|
||||
SELECT * FROM v1;
|
||||
DROP VIEW v1;
|
13
sql/item.h
13
sql/item.h
@ -904,6 +904,19 @@ public:
|
||||
expressions with subqueries in the ORDER/GROUP clauses.
|
||||
*/
|
||||
String *val_str() { return val_str(&str_value); }
|
||||
String *val_str_null_to_empty(String *to)
|
||||
{
|
||||
String *res= val_str(to);
|
||||
if (res)
|
||||
return res;
|
||||
to->set_charset(collation.collation);
|
||||
to->length(0);
|
||||
return to;
|
||||
}
|
||||
String *val_str_null_to_empty(String *to, bool null_to_empty)
|
||||
{
|
||||
return null_to_empty ? val_str_null_to_empty(to) : val_str(to);
|
||||
}
|
||||
virtual Item_func *get_item_func() { return NULL; }
|
||||
|
||||
const MY_LOCALE *locale_from_val_str();
|
||||
|
@ -2666,7 +2666,10 @@ protected:
|
||||
class Create_func_regexp_replace : public Create_func_arg3
|
||||
{
|
||||
public:
|
||||
virtual Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3);
|
||||
Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3) override
|
||||
{
|
||||
return new (thd->mem_root) Item_func_regexp_replace(thd, arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
static Create_func_regexp_replace s_singleton;
|
||||
|
||||
@ -2675,6 +2678,28 @@ protected:
|
||||
virtual ~Create_func_regexp_replace() = default;
|
||||
};
|
||||
|
||||
Create_func_regexp_replace Create_func_regexp_replace::s_singleton;
|
||||
|
||||
|
||||
class Create_func_regexp_replace_oracle : public Create_func_arg3
|
||||
{
|
||||
public:
|
||||
Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3) override
|
||||
{
|
||||
return new (thd->mem_root) Item_func_regexp_replace_oracle(thd, arg1,
|
||||
arg2, arg3);
|
||||
}
|
||||
|
||||
static Create_func_regexp_replace_oracle s_singleton;
|
||||
|
||||
protected:
|
||||
Create_func_regexp_replace_oracle() = default;
|
||||
virtual ~Create_func_regexp_replace_oracle() = default;
|
||||
};
|
||||
|
||||
Create_func_regexp_replace_oracle
|
||||
Create_func_regexp_replace_oracle::s_singleton;
|
||||
|
||||
|
||||
class Create_func_regexp_substr : public Create_func_arg2
|
||||
{
|
||||
@ -6464,15 +6489,6 @@ Create_func_regexp_instr::create_2_arg(THD *thd, Item *arg1, Item *arg2)
|
||||
}
|
||||
|
||||
|
||||
Create_func_regexp_replace Create_func_regexp_replace::s_singleton;
|
||||
|
||||
Item*
|
||||
Create_func_regexp_replace::create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3)
|
||||
{
|
||||
return new (thd->mem_root) Item_func_regexp_replace(thd, arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
|
||||
Create_func_regexp_substr Create_func_regexp_substr::s_singleton;
|
||||
|
||||
Item*
|
||||
@ -7616,6 +7632,8 @@ const Native_func_registry func_array_oracle_overrides[] =
|
||||
{ { STRING_WITH_LEN("LENGTH") }, BUILDER(Create_func_char_length)},
|
||||
{ { STRING_WITH_LEN("LPAD") }, BUILDER(Create_func_lpad_oracle)},
|
||||
{ { STRING_WITH_LEN("LTRIM") }, BUILDER(Create_func_ltrim_oracle)},
|
||||
{ { STRING_WITH_LEN("REGEXP_REPLACE") },
|
||||
BUILDER(Create_func_regexp_replace_oracle)},
|
||||
{ { STRING_WITH_LEN("RPAD") }, BUILDER(Create_func_rpad_oracle)},
|
||||
{ { STRING_WITH_LEN("RTRIM") }, BUILDER(Create_func_rtrim_oracle)},
|
||||
|
||||
|
@ -1149,8 +1149,7 @@ bool Item_func_reverse::fix_length_and_dec()
|
||||
Fix that this works with binary strings when using USE_MB
|
||||
*/
|
||||
|
||||
String *Item_func_replace::val_str_internal(String *str,
|
||||
String *empty_string_for_null)
|
||||
String *Item_func_replace::val_str_internal(String *str, bool null_to_empty)
|
||||
{
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
String *res,*res2,*res3;
|
||||
@ -1168,13 +1167,8 @@ String *Item_func_replace::val_str_internal(String *str,
|
||||
res=args[0]->val_str(str);
|
||||
if (args[0]->null_value)
|
||||
goto null;
|
||||
res2=args[1]->val_str(&tmp_value);
|
||||
if (args[1]->null_value)
|
||||
{
|
||||
if (!empty_string_for_null)
|
||||
goto null;
|
||||
res2= empty_string_for_null;
|
||||
}
|
||||
if (!(res2= args[1]->val_str_null_to_empty(&tmp_value, null_to_empty)))
|
||||
goto null;
|
||||
res->set_charset(collation.collation);
|
||||
|
||||
#ifdef USE_MB
|
||||
@ -1191,12 +1185,8 @@ String *Item_func_replace::val_str_internal(String *str,
|
||||
if (binary_cmp && (offset=res->strstr(*res2)) < 0)
|
||||
return res;
|
||||
#endif
|
||||
if (!(res3=args[2]->val_str(&tmp_value2)))
|
||||
{
|
||||
if (!empty_string_for_null)
|
||||
goto null;
|
||||
res3= empty_string_for_null;
|
||||
}
|
||||
if (!(res3= args[2]->val_str_null_to_empty(&tmp_value2, null_to_empty)))
|
||||
goto null;
|
||||
from_length= res2->length();
|
||||
to_length= res3->length();
|
||||
|
||||
@ -1279,7 +1269,7 @@ redo:
|
||||
}
|
||||
while ((offset=res->strstr(*res2,(uint) offset)) >= 0);
|
||||
}
|
||||
if (empty_string_for_null && !res->length())
|
||||
if (null_to_empty && !res->length())
|
||||
goto null;
|
||||
|
||||
return res;
|
||||
@ -1385,20 +1375,22 @@ bool Item_func_regexp_replace::append_replacement(String *str,
|
||||
}
|
||||
|
||||
|
||||
String *Item_func_regexp_replace::val_str(String *str)
|
||||
String *Item_func_regexp_replace::val_str_internal(String *str,
|
||||
bool null_to_empty)
|
||||
{
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
char buff0[MAX_FIELD_WIDTH];
|
||||
char buff2[MAX_FIELD_WIDTH];
|
||||
String tmp0(buff0,sizeof(buff0),&my_charset_bin);
|
||||
String tmp2(buff2,sizeof(buff2),&my_charset_bin);
|
||||
String *source= args[0]->val_str(&tmp0);
|
||||
String *replace= args[2]->val_str(&tmp2);
|
||||
String *source, *replace;
|
||||
LEX_CSTRING src, rpl;
|
||||
int startoffset= 0;
|
||||
|
||||
if ((null_value= (args[0]->null_value || args[2]->null_value ||
|
||||
re.recompile(args[1]))))
|
||||
if ((null_value=
|
||||
(!(source= args[0]->val_str(&tmp0)) ||
|
||||
!(replace= args[2]->val_str_null_to_empty(&tmp2, null_to_empty)) ||
|
||||
re.recompile(args[1]))))
|
||||
return (String *) 0;
|
||||
|
||||
if (!(source= re.convert_if_needed(source, &re.subject_converter)) ||
|
||||
|
@ -353,12 +353,13 @@ public:
|
||||
class Item_func_replace :public Item_str_func
|
||||
{
|
||||
String tmp_value,tmp_value2;
|
||||
protected:
|
||||
String *val_str_internal(String *str, bool null_to_empty);
|
||||
public:
|
||||
Item_func_replace(THD *thd, Item *org, Item *find, Item *replace):
|
||||
Item_str_func(thd, org, find, replace) {}
|
||||
String *val_str(String *to) { return val_str_internal(to, NULL); };
|
||||
String *val_str(String *to) { return val_str_internal(to, false); };
|
||||
bool fix_length_and_dec();
|
||||
String *val_str_internal(String *str, String *empty_string_for_null);
|
||||
const Schema *schema() const { return &mariadb_schema; }
|
||||
void print(String *str, enum_query_type query_type)
|
||||
{
|
||||
@ -377,7 +378,7 @@ class Item_func_replace_oracle :public Item_func_replace
|
||||
public:
|
||||
Item_func_replace_oracle(THD *thd, Item *org, Item *find, Item *replace):
|
||||
Item_func_replace(thd, org, find, replace) {}
|
||||
String *val_str(String *to) { return val_str_internal(to, &tmp_emtpystr); };
|
||||
String *val_str(String *to) { return val_str_internal(to, true); };
|
||||
const Schema *schema() const { return &oracle_schema_ref; }
|
||||
void print(String *str, enum_query_type query_type)
|
||||
{
|
||||
@ -401,10 +402,18 @@ class Item_func_regexp_replace :public Item_str_func
|
||||
bool append_replacement(String *str,
|
||||
const LEX_CSTRING *source,
|
||||
const LEX_CSTRING *replace);
|
||||
protected:
|
||||
String *val_str_internal(String *str, bool null_to_empty);
|
||||
public:
|
||||
Item_func_regexp_replace(THD *thd, Item *a, Item *b, Item *c):
|
||||
Item_str_func(thd, a, b, c)
|
||||
{}
|
||||
const Schema *schema() const { return &mariadb_schema; }
|
||||
void print(String *str, enum_query_type query_type)
|
||||
{
|
||||
print_sql_mode_qualified_name(str, query_type);
|
||||
print_args_parenthesized(str, query_type);
|
||||
}
|
||||
void cleanup()
|
||||
{
|
||||
DBUG_ENTER("Item_func_regex::cleanup");
|
||||
@ -412,7 +421,10 @@ public:
|
||||
re.cleanup();
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
String *val_str(String *str);
|
||||
String *val_str(String *str)
|
||||
{
|
||||
return val_str_internal(str, false);
|
||||
}
|
||||
bool fix_fields(THD *thd, Item **ref);
|
||||
bool fix_length_and_dec();
|
||||
const char *func_name() const { return "regexp_replace"; }
|
||||
@ -420,6 +432,26 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class Item_func_regexp_replace_oracle: public Item_func_regexp_replace
|
||||
{
|
||||
public:
|
||||
Item_func_regexp_replace_oracle(THD *thd, Item *a, Item *b, Item *c)
|
||||
:Item_func_regexp_replace(thd, a, b, c)
|
||||
{}
|
||||
const Schema *schema() const { return &oracle_schema_ref; }
|
||||
bool fix_length_and_dec()
|
||||
{
|
||||
bool rc= Item_func_regexp_replace::fix_length_and_dec();
|
||||
maybe_null= true; // Empty result is converted to NULL
|
||||
return rc;
|
||||
}
|
||||
String *val_str(String *str)
|
||||
{
|
||||
return val_str_internal(str, true);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Item_func_regexp_substr :public Item_str_func
|
||||
{
|
||||
Regexp_processor_pcre re;
|
||||
|
Reference in New Issue
Block a user