MDEV-10306 Wrong results with combination of CONCAT, SUBSTR and CONVERT in subquery
The bug happens because of a combination of unfortunate circumstances: 1. Arguments args[0] and args[2] of Item_func_concat point recursively (through Item_direct_view_ref's) to the same Item_func_conv_charset. Both args[0]->args[0]->ref[0] and args[2]->args[0]->ref[0] refer to this Item_func_conv_charset. 2. When Item_func_concat::args[0]->val_str() is called, Item_func_conv_charset::val_str() writes its result to Item_func_conc_charset::tmp_value. 3. Then, for optimization purposes (to avoid copying), Item_func_substr::val_str() initializes Item_func_substr::tmp_value to point to the buffer fragment owned by Item_func_conv_charset::tmp_value Item_func_substr::tmp_value is returned as a result of Item_func_concat::args[0]->val_str(). 4. Due to optimization to avoid memory reallocs, Item_func_concat::val_str() remembers the result of args[0]->val_str() in "res" and further uses "res" to collect the return value. 5. When Item_func_concat::args[2]->val_str() is called, Item_func_conv_charset::tmp_value gets overwritten (see #1), which effectively overwrites args[0]'s Item_func_substr::tmp_value (see #3), which effectively overwrites "res" (see #4). This patch does the following: a. Changes Item_func_conv_charset::val_str(String *str) to use tmp_value and str the other way around. After this change tmp_value is used to store a temporary result, while str is used to return the value. The fixes the second problem (without SUBSTR): SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT CONVERT(t USING latin1) t2 FROM t1) sub; As Item_func_concat::val_str() supplies two different buffers when calling args[0]->val_str() and args[2]->val_str(), in the new reduction the result created during args[0]->val_str() does not get overwritten by args[2]->val_str(). b. Fixing the same problem in val_str() for similar classes Item_func_to_base64 Item_func_from_base64 Item_func_weight_string Item_func_hex Item_func_unhex Item_func_quote Item_func_compress Item_func_uncompress Item_func_des_encrypt Item_func_des_decrypt Item_func_conv_charset Item_func_reverse Item_func_soundex Item_func_aes_encrypt Item_func_aes_decrypt Item_func_buffer c. Fixing Item_func::val_str_from_val_str_ascii() the same way. Now Item_str_ascii_func::ascii_buff is used for temporary value, while the parameter passed to val_str() is used to return the result. This fixes the same problem when conversion (from ASCII to e.g. UCS2) takes place. See the ctype_ucs.test for example queries that returned wrong results before the fix. d. Some Item_func descendand classes had temporary String buffers (tmp_value and tmp_str), but did not really use them. Removing these temporary buffers from: Item_func_decode_histogram Item_func_format Item_func_binlog_gtid_pos Item_func_spatial_collection: e. Removing Item_func_buffer::tmp_value, because it's not used any more. f. Renaming Item_func_[un]compress::buffer to "tmp_value", for consistency with other classes. Note, this patch does not fix the following classes (although they have a similar problem): Item_str_conv Item_func_make_set Item_char_typecast They have a complex implementations and simple swapping between "tmp_value" and "str" won't work. These classes will be fixed separately.
This commit is contained in:
parent
f0ad93403f
commit
3a37afec29
@ -5552,5 +5552,22 @@ SELECT 'a','aa';
|
|||||||
a aa
|
a aa
|
||||||
a aa
|
a aa
|
||||||
#
|
#
|
||||||
|
# MDEV-10306 Wrong results with combination of CONCAT, SUBSTR and CONVERT in subquery
|
||||||
|
#
|
||||||
|
SET NAMES utf8, character_set_connection=ucs2;
|
||||||
|
SET @save_optimizer_switch=@@optimizer_switch;
|
||||||
|
SET optimizer_switch=_utf8'derived_merge=on';
|
||||||
|
CREATE TABLE t1 (t VARCHAR(10) CHARSET latin1);
|
||||||
|
INSERT INTO t1 VALUES('abcdefghi');
|
||||||
|
SET NAMES utf8, character_set_connection=ucs2;
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT HEX(t) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
616263646566676869-616263646566676869
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT TO_BASE64(t) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
YWJjZGVmZ2hp-YWJjZGVmZ2hp
|
||||||
|
DROP TABLE t1;
|
||||||
|
SET optimizer_switch=@save_optimizer_switch;
|
||||||
|
#
|
||||||
# End of 10.0 tests
|
# End of 10.0 tests
|
||||||
#
|
#
|
||||||
|
@ -149,3 +149,116 @@ CALL p1();
|
|||||||
########################################40100.000
|
########################################40100.000
|
||||||
DROP PROCEDURE p1;
|
DROP PROCEDURE p1;
|
||||||
# End of 5.1 tests
|
# End of 5.1 tests
|
||||||
|
#
|
||||||
|
# Start of 10.0 tests
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# MDEV-10306 Wrong results with combination of CONCAT, SUBSTR and CONVERT in subquery
|
||||||
|
#
|
||||||
|
SET @save_optimizer_switch=@@optimizer_switch;
|
||||||
|
SET optimizer_switch='derived_merge=on';
|
||||||
|
CREATE TABLE t1 (t VARCHAR(10) CHARSET latin1);
|
||||||
|
INSERT INTO t1 VALUES('1234567');
|
||||||
|
SELECT CONCAT(SUBSTR(t2, 1, 3), SUBSTR(t2, 5)) c1,
|
||||||
|
CONCAT(SUBSTR(t2,1,3),'---',SUBSTR(t2,5)) c2
|
||||||
|
FROM (SELECT CONVERT(t USING latin1) t2 FROM t1) sub;
|
||||||
|
c1 c2
|
||||||
|
123567 123---567
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT CONVERT(t USING latin1) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
1234567-1234567
|
||||||
|
DROP TABLE t1;
|
||||||
|
CREATE TABLE t1 (t VARCHAR(10) CHARSET latin1);
|
||||||
|
INSERT INTO t1 VALUES('1234567');
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT CONVERT(t USING latin1) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
1234567-1234567
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT REVERSE(t) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
7654321-7654321
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT SOUNDEX(t) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
-
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT TO_BASE64(t) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
MTIzNDU2Nw==-MTIzNDU2Nw==
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT WEIGHT_STRING(t) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
1234567-1234567
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT HEX(t) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
31323334353637-31323334353637
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT QUOTE(t) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
'1234567'-'1234567'
|
||||||
|
DROP TABLE t1;
|
||||||
|
CREATE TABLE t1 (t VARCHAR(32) CHARSET latin1);
|
||||||
|
INSERT INTO t1 VALUES(TO_BASE64('abcdefghi'));
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT FROM_BASE64(t) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
abcdefghi-abcdefghi
|
||||||
|
DROP TABLE t1;
|
||||||
|
CREATE TABLE t1 (t VARCHAR(32) CHARSET latin1);
|
||||||
|
INSERT INTO t1 VALUES(HEX('abcdefghi'));
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT UNHEX(t) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
abcdefghi-abcdefghi
|
||||||
|
DROP TABLE t1;
|
||||||
|
CREATE TABLE t1 (t VARCHAR(30) CHARSET latin1);
|
||||||
|
INSERT INTO t1 VALUES('test');
|
||||||
|
SELECT LENGTH(CONCAT(t2)) c2 FROM (SELECT AES_ENCRYPT(t,'x') t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
16
|
||||||
|
SELECT LENGTH(CONCAT(t2,'-',t2)) c2 FROM (SELECT AES_ENCRYPT(t,'x') t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
33
|
||||||
|
SELECT LENGTH(CONCAT(t2,'--',t2)) c2 FROM (SELECT AES_ENCRYPT(t,'x') t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
34
|
||||||
|
SELECT LENGTH(CONCAT(t2)) c2 FROM (SELECT AES_DECRYPT(AES_ENCRYPT(t,'x'),'x') t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
4
|
||||||
|
SELECT LENGTH(CONCAT(t2,'-',t2)) c2 FROM (SELECT AES_DECRYPT(AES_ENCRYPT(t,'x'),'x') t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
9
|
||||||
|
SELECT LENGTH(CONCAT(t2,'--',t2)) c2 FROM (SELECT AES_DECRYPT(AES_ENCRYPT(t,'x'),'x') t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
10
|
||||||
|
DROP TABLE t1;
|
||||||
|
CREATE TABLE t1 (t VARCHAR(64) CHARSET latin1);
|
||||||
|
INSERT INTO t1 VALUES('123456789');
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT MD5(t) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
25f9e794323b453885f5181f1b624d0b-25f9e794323b453885f5181f1b624d0b
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT FORMAT(t,2) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
123,456,789.00-123,456,789.00
|
||||||
|
DROP TABLE t1;
|
||||||
|
CREATE TABLE t1 (t VARCHAR(32) CHARSET latin1);
|
||||||
|
INSERT INTO t1 VALUES('abcdefghi');
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT INSERT(t,3,4,'xxx') t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
abxxxghi-abxxxghi
|
||||||
|
DROP TABLE t1;
|
||||||
|
CREATE TABLE t1 (t VARCHAR(10) CHARSET latin1);
|
||||||
|
INSERT INTO t1 VALUES('abcdefghi');
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT LEFT(t,10) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
abcdefghi-abcdefghi
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT RIGHT(t,10) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
abcdefghi-abcdefghi
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT SUBSTR(t,1,10) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
abcdefghi-abcdefghi
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT LTRIM(t) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
abcdefghi-abcdefghi
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT RTRIM(t) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
abcdefghi-abcdefghi
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT TRIM(t) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
abcdefghi-abcdefghi
|
||||||
|
DROP TABLE t1;
|
||||||
|
SET optimizer_switch=@save_optimizer_switch;
|
||||||
|
@ -106,3 +106,21 @@ OLD_PASSWORD(c1) PASSWORD(c1)
|
|||||||
77023ffe214c04ff *82E58A2C08AAFE72C8EB523069CD8ADB33F78F58
|
77023ffe214c04ff *82E58A2C08AAFE72C8EB523069CD8ADB33F78F58
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
End of 5.0 tests
|
End of 5.0 tests
|
||||||
|
#
|
||||||
|
# Start of 10.0 tests
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# MDEV-10306 Wrong results with combination of CONCAT, SUBSTR and CONVERT in subquery
|
||||||
|
#
|
||||||
|
SET @save_optimizer_switch=@@optimizer_switch;
|
||||||
|
SET optimizer_switch='derived_merge=on';
|
||||||
|
CREATE TABLE t1 (t VARCHAR(32) CHARSET latin1);
|
||||||
|
INSERT INTO t1 VALUES('abcdefghi');
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT ENCRYPT(t,'aa') t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
aaHHlPHAM4sjs-aaHHlPHAM4sjs
|
||||||
|
DROP TABLE t1;
|
||||||
|
SET optimizer_switch=@save_optimizer_switch;
|
||||||
|
#
|
||||||
|
# End of 10.0 tests
|
||||||
|
#
|
||||||
|
@ -1641,5 +1641,23 @@ AsText(g)
|
|||||||
NULL
|
NULL
|
||||||
POINT(1 1)
|
POINT(1 1)
|
||||||
#
|
#
|
||||||
|
# MDEV-10306 Wrong results with combination of CONCAT, SUBSTR and CONVERT in subquery
|
||||||
|
#
|
||||||
|
SET @save_optimizer_switch=@@optimizer_switch;
|
||||||
|
SET optimizer_switch='derived_merge=on';
|
||||||
|
CREATE TABLE t1 (x INT, y INT);
|
||||||
|
INSERT INTO t1 VALUES(0,0);
|
||||||
|
SELECT LENGTH(t2) c2 FROM (SELECT ST_BUFFER(POINT(x,y), 0) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
25
|
||||||
|
SELECT LENGTH(CONCAT(t2,'-',t2)) c2 FROM (SELECT ST_BUFFER(POINT(x,y), 0) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
51
|
||||||
|
SELECT LENGTH(CONCAT(t2,'--',t2)) c2 FROM (SELECT ST_BUFFER(POINT(x,y), 0) t2 FROM t1) sub;
|
||||||
|
c2
|
||||||
|
52
|
||||||
|
DROP TABLE t1;
|
||||||
|
SET optimizer_switch=@save_optimizer_switch;
|
||||||
|
#
|
||||||
# End 10.0 tests
|
# End 10.0 tests
|
||||||
#
|
#
|
||||||
|
@ -923,6 +923,22 @@ SET NAMES utf8, character_set_connection=ucs2;
|
|||||||
SELECT 'a','aa';
|
SELECT 'a','aa';
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-10306 Wrong results with combination of CONCAT, SUBSTR and CONVERT in subquery
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
SET NAMES utf8, character_set_connection=ucs2;
|
||||||
|
SET @save_optimizer_switch=@@optimizer_switch;
|
||||||
|
SET optimizer_switch=_utf8'derived_merge=on';
|
||||||
|
CREATE TABLE t1 (t VARCHAR(10) CHARSET latin1);
|
||||||
|
INSERT INTO t1 VALUES('abcdefghi');
|
||||||
|
SET NAMES utf8, character_set_connection=ucs2;
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT HEX(t) t2 FROM t1) sub;
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT TO_BASE64(t) t2 FROM t1) sub;
|
||||||
|
DROP TABLE t1;
|
||||||
|
SET optimizer_switch=@save_optimizer_switch;
|
||||||
|
|
||||||
|
|
||||||
--echo #
|
--echo #
|
||||||
--echo # End of 10.0 tests
|
--echo # End of 10.0 tests
|
||||||
--echo #
|
--echo #
|
||||||
|
@ -145,3 +145,94 @@ CALL p1();
|
|||||||
DROP PROCEDURE p1;
|
DROP PROCEDURE p1;
|
||||||
|
|
||||||
--echo # End of 5.1 tests
|
--echo # End of 5.1 tests
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Start of 10.0 tests
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-10306 Wrong results with combination of CONCAT, SUBSTR and CONVERT in subquery
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
SET @save_optimizer_switch=@@optimizer_switch;
|
||||||
|
SET optimizer_switch='derived_merge=on';
|
||||||
|
|
||||||
|
CREATE TABLE t1 (t VARCHAR(10) CHARSET latin1);
|
||||||
|
INSERT INTO t1 VALUES('1234567');
|
||||||
|
SELECT CONCAT(SUBSTR(t2, 1, 3), SUBSTR(t2, 5)) c1,
|
||||||
|
CONCAT(SUBSTR(t2,1,3),'---',SUBSTR(t2,5)) c2
|
||||||
|
FROM (SELECT CONVERT(t USING latin1) t2 FROM t1) sub;
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT CONVERT(t USING latin1) t2 FROM t1) sub;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
# Other functions affected by MDEV-10306
|
||||||
|
|
||||||
|
CREATE TABLE t1 (t VARCHAR(10) CHARSET latin1);
|
||||||
|
INSERT INTO t1 VALUES('1234567');
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT CONVERT(t USING latin1) t2 FROM t1) sub;
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT REVERSE(t) t2 FROM t1) sub;
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT SOUNDEX(t) t2 FROM t1) sub;
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT TO_BASE64(t) t2 FROM t1) sub;
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT WEIGHT_STRING(t) t2 FROM t1) sub;
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT HEX(t) t2 FROM t1) sub;
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT QUOTE(t) t2 FROM t1) sub;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
CREATE TABLE t1 (t VARCHAR(32) CHARSET latin1);
|
||||||
|
INSERT INTO t1 VALUES(TO_BASE64('abcdefghi'));
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT FROM_BASE64(t) t2 FROM t1) sub;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
CREATE TABLE t1 (t VARCHAR(32) CHARSET latin1);
|
||||||
|
INSERT INTO t1 VALUES(HEX('abcdefghi'));
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT UNHEX(t) t2 FROM t1) sub;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
CREATE TABLE t1 (t VARCHAR(30) CHARSET latin1);
|
||||||
|
INSERT INTO t1 VALUES('test');
|
||||||
|
SELECT LENGTH(CONCAT(t2)) c2 FROM (SELECT AES_ENCRYPT(t,'x') t2 FROM t1) sub;
|
||||||
|
SELECT LENGTH(CONCAT(t2,'-',t2)) c2 FROM (SELECT AES_ENCRYPT(t,'x') t2 FROM t1) sub;
|
||||||
|
SELECT LENGTH(CONCAT(t2,'--',t2)) c2 FROM (SELECT AES_ENCRYPT(t,'x') t2 FROM t1) sub;
|
||||||
|
SELECT LENGTH(CONCAT(t2)) c2 FROM (SELECT AES_DECRYPT(AES_ENCRYPT(t,'x'),'x') t2 FROM t1) sub;
|
||||||
|
SELECT LENGTH(CONCAT(t2,'-',t2)) c2 FROM (SELECT AES_DECRYPT(AES_ENCRYPT(t,'x'),'x') t2 FROM t1) sub;
|
||||||
|
SELECT LENGTH(CONCAT(t2,'--',t2)) c2 FROM (SELECT AES_DECRYPT(AES_ENCRYPT(t,'x'),'x') t2 FROM t1) sub;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
|
||||||
|
# Functions not affected by MDEV-10306
|
||||||
|
# They only had an unused tmp_value, which was removed.
|
||||||
|
|
||||||
|
CREATE TABLE t1 (t VARCHAR(64) CHARSET latin1);
|
||||||
|
INSERT INTO t1 VALUES('123456789');
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT MD5(t) t2 FROM t1) sub;
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT FORMAT(t,2) t2 FROM t1) sub;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
# Functions not affected by MDEV-10306
|
||||||
|
# They already use tmp_value only for internal purposes and
|
||||||
|
# return the result in the String passed to val_str()
|
||||||
|
|
||||||
|
CREATE TABLE t1 (t VARCHAR(32) CHARSET latin1);
|
||||||
|
INSERT INTO t1 VALUES('abcdefghi');
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT INSERT(t,3,4,'xxx') t2 FROM t1) sub;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
|
||||||
|
# Functions not affected by MDEV-10306
|
||||||
|
# They use this code style:
|
||||||
|
# String *res= args[0]->val_str(str);
|
||||||
|
# tmp_value.set(*res, start, end);
|
||||||
|
# return &tmp_value;
|
||||||
|
|
||||||
|
CREATE TABLE t1 (t VARCHAR(10) CHARSET latin1);
|
||||||
|
INSERT INTO t1 VALUES('abcdefghi');
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT LEFT(t,10) t2 FROM t1) sub;
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT RIGHT(t,10) t2 FROM t1) sub;
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT SUBSTR(t,1,10) t2 FROM t1) sub;
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT LTRIM(t) t2 FROM t1) sub;
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT RTRIM(t) t2 FROM t1) sub;
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT TRIM(t) t2 FROM t1) sub;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
SET optimizer_switch=@save_optimizer_switch;
|
||||||
|
@ -70,3 +70,27 @@ SELECT OLD_PASSWORD(c1), PASSWORD(c1) FROM t1;
|
|||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
|
||||||
--echo End of 5.0 tests
|
--echo End of 5.0 tests
|
||||||
|
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Start of 10.0 tests
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-10306 Wrong results with combination of CONCAT, SUBSTR and CONVERT in subquery
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
SET @save_optimizer_switch=@@optimizer_switch;
|
||||||
|
SET optimizer_switch='derived_merge=on';
|
||||||
|
# ENCRYPT() is not affected by MDEV-10306
|
||||||
|
# It already uses tmp_value only for internal purposes and
|
||||||
|
# returns the result in the String passed to val_str()
|
||||||
|
CREATE TABLE t1 (t VARCHAR(32) CHARSET latin1);
|
||||||
|
INSERT INTO t1 VALUES('abcdefghi');
|
||||||
|
SELECT CONCAT(t2,'-',t2) c2 FROM (SELECT ENCRYPT(t,'aa') t2 FROM t1) sub;
|
||||||
|
DROP TABLE t1;
|
||||||
|
SET optimizer_switch=@save_optimizer_switch;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # End of 10.0 tests
|
||||||
|
--echo #
|
||||||
|
@ -1502,6 +1502,21 @@ DROP VIEW v1;
|
|||||||
--echo #
|
--echo #
|
||||||
SELECT AsText(g) FROM (SELECT NULL AS g UNION SELECT Point(1,1)) AS t1;
|
SELECT AsText(g) FROM (SELECT NULL AS g UNION SELECT Point(1,1)) AS t1;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-10306 Wrong results with combination of CONCAT, SUBSTR and CONVERT in subquery
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
SET @save_optimizer_switch=@@optimizer_switch;
|
||||||
|
SET optimizer_switch='derived_merge=on';
|
||||||
|
CREATE TABLE t1 (x INT, y INT);
|
||||||
|
INSERT INTO t1 VALUES(0,0);
|
||||||
|
SELECT LENGTH(t2) c2 FROM (SELECT ST_BUFFER(POINT(x,y), 0) t2 FROM t1) sub;
|
||||||
|
SELECT LENGTH(CONCAT(t2,'-',t2)) c2 FROM (SELECT ST_BUFFER(POINT(x,y), 0) t2 FROM t1) sub;
|
||||||
|
SELECT LENGTH(CONCAT(t2,'--',t2)) c2 FROM (SELECT ST_BUFFER(POINT(x,y), 0) t2 FROM t1) sub;
|
||||||
|
DROP TABLE t1;
|
||||||
|
SET optimizer_switch=@save_optimizer_switch;
|
||||||
|
|
||||||
|
|
||||||
--echo #
|
--echo #
|
||||||
--echo # End 10.0 tests
|
--echo # End 10.0 tests
|
||||||
--echo #
|
--echo #
|
||||||
|
33
sql/item.h
33
sql/item.h
@ -830,25 +830,20 @@ public:
|
|||||||
store return value of this method.
|
store return value of this method.
|
||||||
|
|
||||||
NOTE
|
NOTE
|
||||||
Buffer passed via argument should only be used if the item itself
|
The caller can modify the returned String, if it's not marked
|
||||||
doesn't have an own String buffer. In case when the item maintains
|
"const" (with the String::mark_as_const() method). That means that
|
||||||
it's own string buffer, it's preferable to return it instead to
|
if the item returns its own internal buffer (e.g. tmp_value), it
|
||||||
minimize number of mallocs/memcpys.
|
*must* be marked "const" [1]. So normally it's preferrable to
|
||||||
The caller of this method can modify returned string, but only in case
|
return the result value in the String, that was passed as an
|
||||||
when it was allocated on heap, (is_alloced() is true). This allows
|
argument. But, for example, SUBSTR() returns a String that simply
|
||||||
the caller to efficiently use a buffer allocated by a child without
|
points into the buffer of SUBSTR()'s args[0]->val_str(). Such a
|
||||||
having to allocate a buffer of it's own. The buffer, given to
|
String is always "const", so it's ok to use tmp_value for that and
|
||||||
val_str() as argument, belongs to the caller and is later used by the
|
avoid reallocating/copying of the argument String.
|
||||||
caller at it's own choosing.
|
|
||||||
A few implications from the above:
|
[1] consider SELECT CONCAT(f, ":", f) FROM (SELECT func() AS f);
|
||||||
- unless you return a string object which only points to your buffer
|
here the return value of f() is used twice in the top-level
|
||||||
but doesn't manages it you should be ready that it will be
|
select, and if they share the same tmp_value buffer, modifying the
|
||||||
modified.
|
first one will implicitly modify the second too.
|
||||||
- even for not allocated strings (is_alloced() == false) the caller
|
|
||||||
can change charset (see Item_func_{typecast/binary}. XXX: is this
|
|
||||||
a bug?
|
|
||||||
- still you should try to minimize data copying and return internal
|
|
||||||
object whenever possible.
|
|
||||||
|
|
||||||
RETURN
|
RETURN
|
||||||
In case of NULL value return 0 (NULL pointer) and set null_value flag
|
In case of NULL value return 0 (NULL pointer) and set null_value flag
|
||||||
|
@ -1268,7 +1268,7 @@ String *Item_func_buffer::val_str(String *str_value)
|
|||||||
{
|
{
|
||||||
DBUG_ENTER("Item_func_buffer::val_str");
|
DBUG_ENTER("Item_func_buffer::val_str");
|
||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
String *obj= args[0]->val_str(&tmp_value);
|
String *obj= args[0]->val_str(str_value);
|
||||||
double dist= args[1]->val_real();
|
double dist= args[1]->val_real();
|
||||||
Geometry_buffer buffer;
|
Geometry_buffer buffer;
|
||||||
Geometry *g;
|
Geometry *g;
|
||||||
|
@ -171,7 +171,6 @@ public:
|
|||||||
|
|
||||||
class Item_func_spatial_collection: public Item_geometry_func
|
class Item_func_spatial_collection: public Item_geometry_func
|
||||||
{
|
{
|
||||||
String tmp_value;
|
|
||||||
enum Geometry::wkbType coll_type;
|
enum Geometry::wkbType coll_type;
|
||||||
enum Geometry::wkbType item_type;
|
enum Geometry::wkbType item_type;
|
||||||
public:
|
public:
|
||||||
@ -325,7 +324,6 @@ protected:
|
|||||||
|
|
||||||
Gcalc_result_receiver res_receiver;
|
Gcalc_result_receiver res_receiver;
|
||||||
Gcalc_operation_reducer operation;
|
Gcalc_operation_reducer operation;
|
||||||
String tmp_value;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Item_func_buffer(Item *obj, Item *distance):
|
Item_func_buffer(Item *obj, Item *distance):
|
||||||
|
@ -73,8 +73,14 @@ size_t username_char_length= 80;
|
|||||||
Conversion happens only in case of "tricky" Item character set (e.g. UCS2).
|
Conversion happens only in case of "tricky" Item character set (e.g. UCS2).
|
||||||
Normally conversion does not happen, and val_str_ascii() is immediately
|
Normally conversion does not happen, and val_str_ascii() is immediately
|
||||||
returned instead.
|
returned instead.
|
||||||
|
|
||||||
|
No matter if conversion is needed or not needed,
|
||||||
|
the result is always returned in "str" (see MDEV-10306 why).
|
||||||
|
|
||||||
|
@param [OUT] str - Store the result here
|
||||||
|
@param [IN] ascii_buffer - Use this temporary buffer to call val_str_ascii()
|
||||||
*/
|
*/
|
||||||
String *Item_func::val_str_from_val_str_ascii(String *str, String *str2)
|
String *Item_func::val_str_from_val_str_ascii(String *str, String *ascii_buffer)
|
||||||
{
|
{
|
||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
|
|
||||||
@ -86,19 +92,19 @@ String *Item_func::val_str_from_val_str_ascii(String *str, String *str2)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
DBUG_ASSERT(str != str2);
|
DBUG_ASSERT(str != ascii_buffer);
|
||||||
|
|
||||||
uint errors;
|
uint errors;
|
||||||
String *res= val_str_ascii(str);
|
String *res= val_str_ascii(ascii_buffer);
|
||||||
if (!res)
|
if (!res)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if ((null_value= str2->copy(res->ptr(), res->length(),
|
if ((null_value= str->copy(res->ptr(), res->length(),
|
||||||
&my_charset_latin1, collation.collation,
|
&my_charset_latin1, collation.collation,
|
||||||
&errors)))
|
&errors)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return str2;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -368,12 +374,12 @@ void Item_func_sha2::fix_length_and_dec()
|
|||||||
|
|
||||||
/* Implementation of AES encryption routines */
|
/* Implementation of AES encryption routines */
|
||||||
|
|
||||||
String *Item_func_aes_encrypt::val_str(String *str)
|
String *Item_func_aes_encrypt::val_str(String *str2)
|
||||||
{
|
{
|
||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
char key_buff[80];
|
char key_buff[80];
|
||||||
String tmp_key_value(key_buff, sizeof(key_buff), system_charset_info);
|
String tmp_key_value(key_buff, sizeof(key_buff), system_charset_info);
|
||||||
String *sptr= args[0]->val_str(str); // String to encrypt
|
String *sptr= args[0]->val_str(&str_value); // String to encrypt
|
||||||
String *key= args[1]->val_str(&tmp_key_value); // key
|
String *key= args[1]->val_str(&tmp_key_value); // key
|
||||||
int aes_length;
|
int aes_length;
|
||||||
if (sptr && key) // we need both arguments to be not NULL
|
if (sptr && key) // we need both arguments to be not NULL
|
||||||
@ -381,15 +387,15 @@ String *Item_func_aes_encrypt::val_str(String *str)
|
|||||||
null_value=0;
|
null_value=0;
|
||||||
aes_length=my_aes_get_size(sptr->length()); // Calculate result length
|
aes_length=my_aes_get_size(sptr->length()); // Calculate result length
|
||||||
|
|
||||||
if (!str_value.alloc(aes_length)) // Ensure that memory is free
|
if (!str2->alloc(aes_length)) // Ensure that memory is free
|
||||||
{
|
{
|
||||||
// finally encrypt directly to allocated buffer.
|
// finally encrypt directly to allocated buffer.
|
||||||
if (my_aes_encrypt(sptr->ptr(),sptr->length(), (char*) str_value.ptr(),
|
if (my_aes_encrypt(sptr->ptr(),sptr->length(), (char*) str2->ptr(),
|
||||||
key->ptr(), key->length()) == aes_length)
|
key->ptr(), key->length()) == aes_length)
|
||||||
{
|
{
|
||||||
// We got the expected result length
|
// We got the expected result length
|
||||||
str_value.length((uint) aes_length);
|
str2->length((uint) aes_length);
|
||||||
return &str_value;
|
return str2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -412,22 +418,22 @@ String *Item_func_aes_decrypt::val_str(String *str)
|
|||||||
String *sptr, *key;
|
String *sptr, *key;
|
||||||
DBUG_ENTER("Item_func_aes_decrypt::val_str");
|
DBUG_ENTER("Item_func_aes_decrypt::val_str");
|
||||||
|
|
||||||
sptr= args[0]->val_str(str); // String to decrypt
|
sptr= args[0]->val_str(&str_value); // String to decrypt
|
||||||
key= args[1]->val_str(&tmp_key_value); // Key
|
key= args[1]->val_str(&tmp_key_value); // Key
|
||||||
if (sptr && key) // Need to have both arguments not NULL
|
if (sptr && key) // Need to have both arguments not NULL
|
||||||
{
|
{
|
||||||
null_value=0;
|
null_value=0;
|
||||||
if (!str_value.alloc(sptr->length())) // Ensure that memory is free
|
if (!str->alloc(sptr->length())) // Ensure that memory is free
|
||||||
{
|
{
|
||||||
// finally decrypt directly to allocated buffer.
|
// finally decrypt directly to allocated buffer.
|
||||||
int length;
|
int length;
|
||||||
length=my_aes_decrypt(sptr->ptr(), sptr->length(),
|
length=my_aes_decrypt(sptr->ptr(), sptr->length(),
|
||||||
(char*) str_value.ptr(),
|
(char*) str->ptr(),
|
||||||
key->ptr(), key->length());
|
key->ptr(), key->length());
|
||||||
if (length >= 0) // if we got correct data data
|
if (length >= 0) // if we got correct data data
|
||||||
{
|
{
|
||||||
str_value.length((uint) length);
|
str->length((uint) length);
|
||||||
DBUG_RETURN(&str_value);
|
DBUG_RETURN(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -464,7 +470,7 @@ void Item_func_to_base64::fix_length_and_dec()
|
|||||||
|
|
||||||
String *Item_func_to_base64::val_str_ascii(String *str)
|
String *Item_func_to_base64::val_str_ascii(String *str)
|
||||||
{
|
{
|
||||||
String *res= args[0]->val_str(str);
|
String *res= args[0]->val_str(&tmp_value);
|
||||||
bool too_long= false;
|
bool too_long= false;
|
||||||
int length;
|
int length;
|
||||||
if (!res ||
|
if (!res ||
|
||||||
@ -472,7 +478,7 @@ String *Item_func_to_base64::val_str_ascii(String *str)
|
|||||||
(too_long=
|
(too_long=
|
||||||
((uint) (length= base64_needed_encoded_length((int) res->length())) >
|
((uint) (length= base64_needed_encoded_length((int) res->length())) >
|
||||||
current_thd->variables.max_allowed_packet)) ||
|
current_thd->variables.max_allowed_packet)) ||
|
||||||
tmp_value.alloc((uint) length))
|
str->alloc((uint) length))
|
||||||
{
|
{
|
||||||
null_value= 1; // NULL input, too long input, or OOM.
|
null_value= 1; // NULL input, too long input, or OOM.
|
||||||
if (too_long)
|
if (too_long)
|
||||||
@ -484,11 +490,11 @@ String *Item_func_to_base64::val_str_ascii(String *str)
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
base64_encode(res->ptr(), (int) res->length(), (char*) tmp_value.ptr());
|
base64_encode(res->ptr(), (int) res->length(), (char*) str->ptr());
|
||||||
DBUG_ASSERT(length > 0);
|
DBUG_ASSERT(length > 0);
|
||||||
tmp_value.length((uint) length - 1); // Without trailing '\0'
|
str->length((uint) length - 1); // Without trailing '\0'
|
||||||
null_value= 0;
|
null_value= 0;
|
||||||
return &tmp_value;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -509,7 +515,7 @@ void Item_func_from_base64::fix_length_and_dec()
|
|||||||
|
|
||||||
String *Item_func_from_base64::val_str(String *str)
|
String *Item_func_from_base64::val_str(String *str)
|
||||||
{
|
{
|
||||||
String *res= args[0]->val_str_ascii(str);
|
String *res= args[0]->val_str_ascii(&tmp_value);
|
||||||
int length;
|
int length;
|
||||||
const char *end_ptr;
|
const char *end_ptr;
|
||||||
|
|
||||||
@ -527,11 +533,11 @@ String *Item_func_from_base64::val_str(String *str)
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmp_value.alloc((uint) length))
|
if (str->alloc((uint) length))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if ((length= base64_decode(res->ptr(), (int) res->length(),
|
if ((length= base64_decode(res->ptr(), (int) res->length(),
|
||||||
(char *) tmp_value.ptr(), &end_ptr, 0)) < 0 ||
|
(char *) str->ptr(), &end_ptr, 0)) < 0 ||
|
||||||
end_ptr < res->ptr() + res->length())
|
end_ptr < res->ptr() + res->length())
|
||||||
{
|
{
|
||||||
push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
|
push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
|
||||||
@ -540,9 +546,9 @@ String *Item_func_from_base64::val_str(String *str)
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp_value.length((uint) length);
|
str->length((uint) length);
|
||||||
null_value= 0;
|
null_value= 0;
|
||||||
return &tmp_value;
|
return str;
|
||||||
err:
|
err:
|
||||||
null_value= 1; // NULL input, too long input, OOM, or badly formed input
|
null_value= 1; // NULL input, too long input, OOM, or badly formed input
|
||||||
return 0;
|
return 0;
|
||||||
@ -797,7 +803,7 @@ String *Item_func_des_encrypt::val_str(String *str)
|
|||||||
struct st_des_keyschedule keyschedule;
|
struct st_des_keyschedule keyschedule;
|
||||||
const char *append_str="********";
|
const char *append_str="********";
|
||||||
uint key_number, res_length, tail;
|
uint key_number, res_length, tail;
|
||||||
String *res= args[0]->val_str(str);
|
String *res= args[0]->val_str(&tmp_value);
|
||||||
|
|
||||||
if ((null_value= args[0]->null_value))
|
if ((null_value= args[0]->null_value))
|
||||||
return 0; // ENCRYPT(NULL) == NULL
|
return 0; // ENCRYPT(NULL) == NULL
|
||||||
@ -821,7 +827,7 @@ String *Item_func_des_encrypt::val_str(String *str)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
String *keystr=args[1]->val_str(&tmp_value);
|
String *keystr= args[1]->val_str(str);
|
||||||
if (!keystr)
|
if (!keystr)
|
||||||
goto error;
|
goto error;
|
||||||
key_number=127; // User key string
|
key_number=127; // User key string
|
||||||
@ -853,23 +859,23 @@ String *Item_func_des_encrypt::val_str(String *str)
|
|||||||
tmp_arg.length(0);
|
tmp_arg.length(0);
|
||||||
tmp_arg.append(res->ptr(), res->length());
|
tmp_arg.append(res->ptr(), res->length());
|
||||||
code= ER_OUT_OF_RESOURCES;
|
code= ER_OUT_OF_RESOURCES;
|
||||||
if (tmp_arg.append(append_str, tail) || tmp_value.alloc(res_length+1))
|
if (tmp_arg.append(append_str, tail) || str->alloc(res_length+1))
|
||||||
goto error;
|
goto error;
|
||||||
tmp_arg[res_length-1]=tail; // save extra length
|
tmp_arg[res_length-1]=tail; // save extra length
|
||||||
tmp_value.realloc(res_length+1);
|
str->realloc(res_length+1);
|
||||||
tmp_value.length(res_length+1);
|
str->length(res_length+1);
|
||||||
tmp_value.set_charset(&my_charset_bin);
|
str->set_charset(&my_charset_bin);
|
||||||
tmp_value[0]=(char) (128 | key_number);
|
(*str)[0]=(char) (128 | key_number);
|
||||||
// Real encryption
|
// Real encryption
|
||||||
bzero((char*) &ivec,sizeof(ivec));
|
bzero((char*) &ivec,sizeof(ivec));
|
||||||
DES_ede3_cbc_encrypt((const uchar*) (tmp_arg.ptr()),
|
DES_ede3_cbc_encrypt((const uchar*) (tmp_arg.ptr()),
|
||||||
(uchar*) (tmp_value.ptr()+1),
|
(uchar*) (str->ptr()+1),
|
||||||
res_length,
|
res_length,
|
||||||
&keyschedule.ks1,
|
&keyschedule.ks1,
|
||||||
&keyschedule.ks2,
|
&keyschedule.ks2,
|
||||||
&keyschedule.ks3,
|
&keyschedule.ks3,
|
||||||
&ivec, TRUE);
|
&ivec, TRUE);
|
||||||
return &tmp_value;
|
return str;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
push_warning_printf(current_thd,Sql_condition::WARN_LEVEL_WARN,
|
push_warning_printf(current_thd,Sql_condition::WARN_LEVEL_WARN,
|
||||||
@ -893,7 +899,7 @@ String *Item_func_des_decrypt::val_str(String *str)
|
|||||||
DES_cblock ivec;
|
DES_cblock ivec;
|
||||||
struct st_des_keyblock keyblock;
|
struct st_des_keyblock keyblock;
|
||||||
struct st_des_keyschedule keyschedule;
|
struct st_des_keyschedule keyschedule;
|
||||||
String *res= args[0]->val_str(str);
|
String *res= args[0]->val_str(&tmp_value);
|
||||||
uint length,tail;
|
uint length,tail;
|
||||||
|
|
||||||
if ((null_value= args[0]->null_value))
|
if ((null_value= args[0]->null_value))
|
||||||
@ -917,7 +923,7 @@ String *Item_func_des_decrypt::val_str(String *str)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// We make good 24-byte (168 bit) key from given plaintext key with MD5
|
// We make good 24-byte (168 bit) key from given plaintext key with MD5
|
||||||
String *keystr=args[1]->val_str(&tmp_value);
|
String *keystr= args[1]->val_str(str);
|
||||||
if (!keystr)
|
if (!keystr)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
@ -932,23 +938,23 @@ String *Item_func_des_decrypt::val_str(String *str)
|
|||||||
DES_set_key_unchecked(&keyblock.key3,&keyschedule.ks3);
|
DES_set_key_unchecked(&keyblock.key3,&keyschedule.ks3);
|
||||||
}
|
}
|
||||||
code= ER_OUT_OF_RESOURCES;
|
code= ER_OUT_OF_RESOURCES;
|
||||||
if (tmp_value.alloc(length-1))
|
if (str->alloc(length-1))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
bzero((char*) &ivec,sizeof(ivec));
|
bzero((char*) &ivec,sizeof(ivec));
|
||||||
DES_ede3_cbc_encrypt((const uchar*) res->ptr()+1,
|
DES_ede3_cbc_encrypt((const uchar*) res->ptr()+1,
|
||||||
(uchar*) (tmp_value.ptr()),
|
(uchar*) (str->ptr()),
|
||||||
length-1,
|
length-1,
|
||||||
&keyschedule.ks1,
|
&keyschedule.ks1,
|
||||||
&keyschedule.ks2,
|
&keyschedule.ks2,
|
||||||
&keyschedule.ks3,
|
&keyschedule.ks3,
|
||||||
&ivec, FALSE);
|
&ivec, FALSE);
|
||||||
/* Restore old length of key */
|
/* Restore old length of key */
|
||||||
if ((tail=(uint) (uchar) tmp_value[length-2]) > 8)
|
if ((tail=(uint) (uchar) (*str)[length-2]) > 8)
|
||||||
goto wrong_key; // Wrong key
|
goto wrong_key; // Wrong key
|
||||||
tmp_value.length(length-1-tail);
|
str->length(length-1-tail);
|
||||||
tmp_value.set_charset(&my_charset_bin);
|
str->set_charset(&my_charset_bin);
|
||||||
return &tmp_value;
|
return str;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
push_warning_printf(current_thd,Sql_condition::WARN_LEVEL_WARN,
|
push_warning_printf(current_thd,Sql_condition::WARN_LEVEL_WARN,
|
||||||
@ -1136,25 +1142,26 @@ void Item_func_concat_ws::fix_length_and_dec()
|
|||||||
String *Item_func_reverse::val_str(String *str)
|
String *Item_func_reverse::val_str(String *str)
|
||||||
{
|
{
|
||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
String *res = args[0]->val_str(str);
|
String *res= args[0]->val_str(&tmp_value);
|
||||||
char *ptr, *end, *tmp;
|
const char *ptr, *end;
|
||||||
|
char *tmp;
|
||||||
|
|
||||||
if ((null_value=args[0]->null_value))
|
if ((null_value=args[0]->null_value))
|
||||||
return 0;
|
return 0;
|
||||||
/* An empty string is a special case as the string pointer may be null */
|
/* An empty string is a special case as the string pointer may be null */
|
||||||
if (!res->length())
|
if (!res->length())
|
||||||
return make_empty_result();
|
return make_empty_result();
|
||||||
if (tmp_value.alloced_length() < res->length() &&
|
if (str->alloced_length() < res->length() &&
|
||||||
tmp_value.realloc(res->length()))
|
str->realloc(res->length()))
|
||||||
{
|
{
|
||||||
null_value= 1;
|
null_value= 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
tmp_value.length(res->length());
|
str->length(res->length());
|
||||||
tmp_value.set_charset(res->charset());
|
str->set_charset(res->charset());
|
||||||
ptr= (char *) res->ptr();
|
ptr= res->ptr();
|
||||||
end= ptr + res->length();
|
end= res->end();
|
||||||
tmp= (char *) tmp_value.ptr() + tmp_value.length();
|
tmp= (char *) str->end();
|
||||||
#ifdef USE_MB
|
#ifdef USE_MB
|
||||||
if (use_mb(res->charset()))
|
if (use_mb(res->charset()))
|
||||||
{
|
{
|
||||||
@ -1178,7 +1185,7 @@ String *Item_func_reverse::val_str(String *str)
|
|||||||
while (ptr < end)
|
while (ptr < end)
|
||||||
*--tmp= *ptr++;
|
*--tmp= *ptr++;
|
||||||
}
|
}
|
||||||
return &tmp_value;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2446,7 +2453,6 @@ void Item_func_soundex::fix_length_and_dec()
|
|||||||
DBUG_ASSERT(collation.collation != NULL);
|
DBUG_ASSERT(collation.collation != NULL);
|
||||||
set_if_bigger(char_length, 4);
|
set_if_bigger(char_length, 4);
|
||||||
fix_char_length(char_length);
|
fix_char_length(char_length);
|
||||||
tmp_value.set_charset(collation.collation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2491,7 +2497,7 @@ static bool my_uni_isalpha(int wc)
|
|||||||
String *Item_func_soundex::val_str(String *str)
|
String *Item_func_soundex::val_str(String *str)
|
||||||
{
|
{
|
||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
String *res =args[0]->val_str(str);
|
String *res= args[0]->val_str(&tmp_value);
|
||||||
char last_ch,ch;
|
char last_ch,ch;
|
||||||
CHARSET_INFO *cs= collation.collation;
|
CHARSET_INFO *cs= collation.collation;
|
||||||
my_wc_t wc;
|
my_wc_t wc;
|
||||||
@ -2501,10 +2507,11 @@ String *Item_func_soundex::val_str(String *str)
|
|||||||
if ((null_value= args[0]->null_value))
|
if ((null_value= args[0]->null_value))
|
||||||
return 0; /* purecov: inspected */
|
return 0; /* purecov: inspected */
|
||||||
|
|
||||||
if (tmp_value.alloc(MY_MAX(res->length(), 4 * cs->mbminlen)))
|
if (str->alloc(MY_MAX(res->length(), 4 * cs->mbminlen)))
|
||||||
return str; /* purecov: inspected */
|
return &tmp_value; /* purecov: inspected */
|
||||||
char *to= (char *) tmp_value.ptr();
|
str->set_charset(collation.collation);
|
||||||
char *to_end= to + tmp_value.alloced_length();
|
char *to= (char *) str->ptr();
|
||||||
|
char *to_end= to + str->alloced_length();
|
||||||
char *from= (char *) res->ptr(), *end= from + res->length();
|
char *from= (char *) res->ptr(), *end= from + res->length();
|
||||||
|
|
||||||
for ( ; ; ) /* Skip pre-space */
|
for ( ; ; ) /* Skip pre-space */
|
||||||
@ -2589,8 +2596,8 @@ String *Item_func_soundex::val_str(String *str)
|
|||||||
to+= nbytes;
|
to+= nbytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp_value.length((uint) (to-tmp_value.ptr()));
|
str->length((uint) (to - str->ptr()));
|
||||||
return &tmp_value;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3399,16 +3406,16 @@ String *Item_func_conv_charset::val_str(String *str)
|
|||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
if (use_cached_value)
|
if (use_cached_value)
|
||||||
return null_value ? 0 : &str_value;
|
return null_value ? 0 : &str_value;
|
||||||
String *arg= args[0]->val_str(str);
|
String *arg= args[0]->val_str(&tmp_value);
|
||||||
uint dummy_errors;
|
uint dummy_errors;
|
||||||
if (args[0]->null_value)
|
if (args[0]->null_value)
|
||||||
{
|
{
|
||||||
null_value=1;
|
null_value=1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
null_value= tmp_value.copy(arg->ptr(), arg->length(), arg->charset(),
|
null_value= str->copy(arg->ptr(), arg->length(), arg->charset(),
|
||||||
conv_charset, &dummy_errors);
|
conv_charset, &dummy_errors);
|
||||||
return null_value ? 0 : check_well_formed_result(&tmp_value);
|
return null_value ? 0 : check_well_formed_result(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Item_func_conv_charset::fix_length_and_dec()
|
void Item_func_conv_charset::fix_length_and_dec()
|
||||||
@ -3551,7 +3558,7 @@ String *Item_func_weight_string::val_str(String *str)
|
|||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
|
|
||||||
if (args[0]->result_type() != STRING_RESULT ||
|
if (args[0]->result_type() != STRING_RESULT ||
|
||||||
!(res= args[0]->val_str(str)))
|
!(res= args[0]->val_str(&tmp_value)))
|
||||||
goto nl;
|
goto nl;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3598,19 +3605,19 @@ String *Item_func_weight_string::val_str(String *str)
|
|||||||
goto nl;
|
goto nl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmp_value.alloc(tmp_length))
|
if (str->alloc(tmp_length))
|
||||||
goto nl;
|
goto nl;
|
||||||
|
|
||||||
frm_length= cs->coll->strnxfrm(cs,
|
frm_length= cs->coll->strnxfrm(cs,
|
||||||
(uchar *) tmp_value.ptr(), tmp_length,
|
(uchar *) str->ptr(), tmp_length,
|
||||||
nweights ? nweights : tmp_length,
|
nweights ? nweights : tmp_length,
|
||||||
(const uchar *) res->ptr(), res->length(),
|
(const uchar *) res->ptr(), res->length(),
|
||||||
flags);
|
flags);
|
||||||
DBUG_ASSERT(frm_length <= tmp_length);
|
DBUG_ASSERT(frm_length <= tmp_length);
|
||||||
|
|
||||||
tmp_value.length(frm_length);
|
str->length(frm_length);
|
||||||
null_value= 0;
|
null_value= 0;
|
||||||
return &tmp_value;
|
return str;
|
||||||
|
|
||||||
nl:
|
nl:
|
||||||
null_value= 1;
|
null_value= 1;
|
||||||
@ -3651,18 +3658,18 @@ String *Item_func_hex::val_str_ascii(String *str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Convert given string to a hex string, character by character */
|
/* Convert given string to a hex string, character by character */
|
||||||
res= args[0]->val_str(str);
|
res= args[0]->val_str(&tmp_value);
|
||||||
if (!res || tmp_value.alloc(res->length()*2+1))
|
if (!res || str->alloc(res->length()*2+1))
|
||||||
{
|
{
|
||||||
null_value=1;
|
null_value=1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
null_value=0;
|
null_value=0;
|
||||||
tmp_value.length(res->length()*2);
|
str->length(res->length()*2);
|
||||||
tmp_value.set_charset(&my_charset_latin1);
|
str->set_charset(&my_charset_latin1);
|
||||||
|
|
||||||
octet2hex((char*) tmp_value.ptr(), res->ptr(), res->length());
|
octet2hex((char*) str->ptr(), res->ptr(), res->length());
|
||||||
return &tmp_value;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Convert given hex string to a binary string. */
|
/** Convert given hex string to a binary string. */
|
||||||
@ -3675,8 +3682,8 @@ String *Item_func_unhex::val_str(String *str)
|
|||||||
uint length;
|
uint length;
|
||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
|
|
||||||
res= args[0]->val_str(str);
|
res= args[0]->val_str(&tmp_value);
|
||||||
if (!res || tmp_value.alloc(length= (1+res->length())/2))
|
if (!res || str->alloc(length= (1+res->length())/2))
|
||||||
{
|
{
|
||||||
null_value=1;
|
null_value=1;
|
||||||
return 0;
|
return 0;
|
||||||
@ -3684,8 +3691,8 @@ String *Item_func_unhex::val_str(String *str)
|
|||||||
|
|
||||||
from= res->ptr();
|
from= res->ptr();
|
||||||
null_value= 0;
|
null_value= 0;
|
||||||
tmp_value.length(length);
|
str->length(length);
|
||||||
to= (char*) tmp_value.ptr();
|
to= (char*) str->ptr();
|
||||||
if (res->length() % 2)
|
if (res->length() % 2)
|
||||||
{
|
{
|
||||||
int hex_char;
|
int hex_char;
|
||||||
@ -3703,7 +3710,7 @@ String *Item_func_unhex::val_str(String *str)
|
|||||||
if ((null_value= (hex_char == -1)))
|
if ((null_value= (hex_char == -1)))
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return &tmp_value;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3949,7 +3956,7 @@ String *Item_func_quote::val_str(String *str)
|
|||||||
|
|
||||||
ulong max_allowed_packet= current_thd->variables.max_allowed_packet;
|
ulong max_allowed_packet= current_thd->variables.max_allowed_packet;
|
||||||
char *from, *to, *end, *start;
|
char *from, *to, *end, *start;
|
||||||
String *arg= args[0]->val_str(str);
|
String *arg= args[0]->val_str(&tmp_value);
|
||||||
uint arg_length, new_length;
|
uint arg_length, new_length;
|
||||||
if (!arg) // Null argument
|
if (!arg) // Null argument
|
||||||
{
|
{
|
||||||
@ -3976,7 +3983,7 @@ String *Item_func_quote::val_str(String *str)
|
|||||||
set_if_smaller(new_length, max_allowed_packet);
|
set_if_smaller(new_length, max_allowed_packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmp_value.alloc(new_length))
|
if (str->alloc(new_length))
|
||||||
goto null;
|
goto null;
|
||||||
|
|
||||||
if (collation.collation->mbmaxlen > 1)
|
if (collation.collation->mbmaxlen > 1)
|
||||||
@ -3984,7 +3991,7 @@ String *Item_func_quote::val_str(String *str)
|
|||||||
CHARSET_INFO *cs= collation.collation;
|
CHARSET_INFO *cs= collation.collation;
|
||||||
int mblen;
|
int mblen;
|
||||||
uchar *to_end;
|
uchar *to_end;
|
||||||
to= (char*) tmp_value.ptr();
|
to= (char*) str->ptr();
|
||||||
to_end= (uchar*) to + new_length;
|
to_end= (uchar*) to + new_length;
|
||||||
|
|
||||||
/* Put leading quote */
|
/* Put leading quote */
|
||||||
@ -4021,14 +4028,14 @@ String *Item_func_quote::val_str(String *str)
|
|||||||
if ((mblen= cs->cset->wc_mb(cs, '\'', (uchar *) to, to_end)) <= 0)
|
if ((mblen= cs->cset->wc_mb(cs, '\'', (uchar *) to, to_end)) <= 0)
|
||||||
goto toolong;
|
goto toolong;
|
||||||
to+= mblen;
|
to+= mblen;
|
||||||
new_length= to - tmp_value.ptr();
|
new_length= to - str->ptr();
|
||||||
goto ret;
|
goto ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We replace characters from the end to the beginning
|
We replace characters from the end to the beginning
|
||||||
*/
|
*/
|
||||||
to= (char*) tmp_value.ptr() + new_length - 1;
|
to= (char*) str->ptr() + new_length - 1;
|
||||||
*to--= '\'';
|
*to--= '\'';
|
||||||
for (start= (char*) arg->ptr(),end= start + arg_length; end-- != start; to--)
|
for (start= (char*) arg->ptr(),end= start + arg_length; end-- != start; to--)
|
||||||
{
|
{
|
||||||
@ -4058,10 +4065,10 @@ String *Item_func_quote::val_str(String *str)
|
|||||||
*to= '\'';
|
*to= '\'';
|
||||||
|
|
||||||
ret:
|
ret:
|
||||||
tmp_value.length(new_length);
|
str->length(new_length);
|
||||||
tmp_value.set_charset(collation.collation);
|
str->set_charset(collation.collation);
|
||||||
null_value= 0;
|
null_value= 0;
|
||||||
return &tmp_value;
|
return str;
|
||||||
|
|
||||||
toolong:
|
toolong:
|
||||||
push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
|
push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
|
||||||
@ -4134,7 +4141,7 @@ String *Item_func_compress::val_str(String *str)
|
|||||||
char *tmp, *last_char;
|
char *tmp, *last_char;
|
||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
|
|
||||||
if (!(res= args[0]->val_str(str)))
|
if (!(res= args[0]->val_str(&tmp_value)))
|
||||||
{
|
{
|
||||||
null_value= 1;
|
null_value= 1;
|
||||||
return 0;
|
return 0;
|
||||||
@ -4155,13 +4162,13 @@ String *Item_func_compress::val_str(String *str)
|
|||||||
|
|
||||||
// Check new_size overflow: new_size <= res->length()
|
// Check new_size overflow: new_size <= res->length()
|
||||||
if (((uint32) (new_size+5) <= res->length()) ||
|
if (((uint32) (new_size+5) <= res->length()) ||
|
||||||
buffer.realloc((uint32) new_size + 4 + 1))
|
str->realloc((uint32) new_size + 4 + 1))
|
||||||
{
|
{
|
||||||
null_value= 1;
|
null_value= 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
body= ((Byte*)buffer.ptr()) + 4;
|
body= ((Byte*)str->ptr()) + 4;
|
||||||
|
|
||||||
// As far as we have checked res->is_empty() we can use ptr()
|
// As far as we have checked res->is_empty() we can use ptr()
|
||||||
if ((err= my_compress_buffer(body, &new_size, (const uchar *)res->ptr(),
|
if ((err= my_compress_buffer(body, &new_size, (const uchar *)res->ptr(),
|
||||||
@ -4173,7 +4180,7 @@ String *Item_func_compress::val_str(String *str)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp= (char*)buffer.ptr(); // int4store is a macro; avoid side effects
|
tmp= (char*) str->ptr(); // int4store is a macro; avoid side effects
|
||||||
int4store(tmp, res->length() & 0x3FFFFFFF);
|
int4store(tmp, res->length() & 0x3FFFFFFF);
|
||||||
|
|
||||||
/* This is to ensure that things works for CHAR fields, which trim ' ': */
|
/* This is to ensure that things works for CHAR fields, which trim ' ': */
|
||||||
@ -4184,15 +4191,15 @@ String *Item_func_compress::val_str(String *str)
|
|||||||
new_size++;
|
new_size++;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.length((uint32)new_size + 4);
|
str->length((uint32)new_size + 4);
|
||||||
return &buffer;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
String *Item_func_uncompress::val_str(String *str)
|
String *Item_func_uncompress::val_str(String *str)
|
||||||
{
|
{
|
||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
String *res= args[0]->val_str(str);
|
String *res= args[0]->val_str(&tmp_value);
|
||||||
ulong new_size;
|
ulong new_size;
|
||||||
int err;
|
int err;
|
||||||
uint code;
|
uint code;
|
||||||
@ -4223,14 +4230,14 @@ String *Item_func_uncompress::val_str(String *str)
|
|||||||
max_allowed_packet));
|
max_allowed_packet));
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
if (buffer.realloc((uint32)new_size))
|
if (str->realloc((uint32)new_size))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if ((err= uncompress((Byte*)buffer.ptr(), &new_size,
|
if ((err= uncompress((Byte*)str->ptr(), &new_size,
|
||||||
((const Bytef*)res->ptr())+4,res->length()-4)) == Z_OK)
|
((const Bytef*)res->ptr())+4,res->length()-4)) == Z_OK)
|
||||||
{
|
{
|
||||||
buffer.length((uint32) new_size);
|
str->length((uint32) new_size);
|
||||||
return &buffer;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
code= ((err == Z_BUF_ERROR) ? ER_ZLIB_Z_BUF_ERROR :
|
code= ((err == Z_BUF_ERROR) ? ER_ZLIB_Z_BUF_ERROR :
|
||||||
|
@ -87,7 +87,6 @@ public:
|
|||||||
|
|
||||||
class Item_func_md5 :public Item_str_ascii_func
|
class Item_func_md5 :public Item_str_ascii_func
|
||||||
{
|
{
|
||||||
String tmp_value;
|
|
||||||
public:
|
public:
|
||||||
Item_func_md5(Item *a) :Item_str_ascii_func(a) {}
|
Item_func_md5(Item *a) :Item_str_ascii_func(a) {}
|
||||||
String *val_str_ascii(String *);
|
String *val_str_ascii(String *);
|
||||||
@ -167,7 +166,6 @@ public:
|
|||||||
|
|
||||||
class Item_func_decode_histogram :public Item_str_func
|
class Item_func_decode_histogram :public Item_str_func
|
||||||
{
|
{
|
||||||
String tmp_value;
|
|
||||||
public:
|
public:
|
||||||
Item_func_decode_histogram(Item *a, Item *b)
|
Item_func_decode_histogram(Item *a, Item *b)
|
||||||
:Item_str_func(a, b) {}
|
:Item_str_func(a, b) {}
|
||||||
@ -675,7 +673,6 @@ public:
|
|||||||
|
|
||||||
class Item_func_format :public Item_str_ascii_func
|
class Item_func_format :public Item_str_ascii_func
|
||||||
{
|
{
|
||||||
String tmp_str;
|
|
||||||
MY_LOCALE *locale;
|
MY_LOCALE *locale;
|
||||||
public:
|
public:
|
||||||
Item_func_format(Item *org, Item *dec): Item_str_ascii_func(org, dec) {}
|
Item_func_format(Item *org, Item *dec): Item_str_ascii_func(org, dec) {}
|
||||||
@ -729,7 +726,6 @@ public:
|
|||||||
|
|
||||||
class Item_func_binlog_gtid_pos :public Item_str_func
|
class Item_func_binlog_gtid_pos :public Item_str_func
|
||||||
{
|
{
|
||||||
String tmp_value;
|
|
||||||
public:
|
public:
|
||||||
Item_func_binlog_gtid_pos(Item *arg1,Item *arg2) :Item_str_func(arg1,arg2) {}
|
Item_func_binlog_gtid_pos(Item *arg1,Item *arg2) :Item_str_func(arg1,arg2) {}
|
||||||
String *val_str(String *);
|
String *val_str(String *);
|
||||||
@ -1106,7 +1102,7 @@ public:
|
|||||||
|
|
||||||
class Item_func_compress: public Item_str_func
|
class Item_func_compress: public Item_str_func
|
||||||
{
|
{
|
||||||
String buffer;
|
String tmp_value;
|
||||||
public:
|
public:
|
||||||
Item_func_compress(Item *a):Item_str_func(a){}
|
Item_func_compress(Item *a):Item_str_func(a){}
|
||||||
void fix_length_and_dec(){max_length= (args[0]->max_length*120)/100+12;}
|
void fix_length_and_dec(){max_length= (args[0]->max_length*120)/100+12;}
|
||||||
@ -1116,7 +1112,7 @@ public:
|
|||||||
|
|
||||||
class Item_func_uncompress: public Item_str_func
|
class Item_func_uncompress: public Item_str_func
|
||||||
{
|
{
|
||||||
String buffer;
|
String tmp_value;
|
||||||
public:
|
public:
|
||||||
Item_func_uncompress(Item *a): Item_str_func(a){}
|
Item_func_uncompress(Item *a): Item_str_func(a){}
|
||||||
void fix_length_and_dec(){ maybe_null= 1; max_length= MAX_BLOB_WIDTH; }
|
void fix_length_and_dec(){ maybe_null= 1; max_length= MAX_BLOB_WIDTH; }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user