diff --git a/mysql-test/main/func_bitops_binary.result b/mysql-test/main/func_bitops_binary.result new file mode 100644 index 0000000000000..8375e4ccfd32b --- /dev/null +++ b/mysql-test/main/func_bitops_binary.result @@ -0,0 +1,477 @@ +# +# MDEV-10526: Binary string support for bitwise operators & aggregate functions +# +# ========================================================================= +# SECTION 1 — Scalar operators on VARBINARY +# ========================================================================= +# Rationale: Tests basic functional correctness of scalar bitwise operators +# (&, |, ^, ~, <<, >>) when applied to VARBINARY(4) operands. +# ========================================================================= +CREATE TABLE t1 (a VARBINARY(4), b VARBINARY(4)); +INSERT INTO t1 VALUES (x'FFFF0000', x'FF00FF00'); +# Expected output: FF000000, FFFFFF00, 00FFFF00, 0000FFFF, FFFE0000, 7FFF8000 +SELECT HEX(a & b), HEX(a | b), HEX(a ^ b), HEX(~a), HEX(a << 1), HEX(a >> 1) FROM t1; +HEX(a & b) HEX(a | b) HEX(a ^ b) HEX(~a) HEX(a << 1) HEX(a >> 1) +FF000000 FFFFFF00 00FFFF00 0000FFFF FFFE0000 7FFF8000 +DROP TABLE t1; +# ========================================================================= +# SECTION 2 — INET6_ATON real-world use case +# ========================================================================= +# Rationale: Verifies the primary target use case (subnet masking and IP math) +# still operates correctly with INET6_ATON (which returns VARBINARY(16)). +# ========================================================================= +# Expected output: 20010DB8000000000000000000000000 +SELECT HEX(INET6_ATON('ffff:ffff::') & INET6_ATON('2001:db8::1')); +HEX(INET6_ATON('ffff:ffff::') & INET6_ATON('2001:db8::1')) +20010DB8000000000000000000000000 +# Expected output: TBD - verify via --record +SELECT HEX(INET6_ATON('ffff:ffff::') | INET6_ATON('2001:db8::1')); +HEX(INET6_ATON('ffff:ffff::') | INET6_ATON('2001:db8::1')) +FFFFFFFF000000000000000000000001 +# Expected output: TBD - verify via --record +SELECT HEX(INET6_ATON('ffff:ffff::') ^ INET6_ATON('2001:db8::1')); +HEX(INET6_ATON('ffff:ffff::') ^ INET6_ATON('2001:db8::1')) +DFFEF247000000000000000000000001 +# ========================================================================= +# SECTION 3 — NULL handling +# ========================================================================= +# Rationale: Ensures that if any of the operands are NULL, the result of the +# bitwise operation is also NULL. +# ========================================================================= +CREATE TABLE t_null (a VARBINARY(4), b VARBINARY(4)); +INSERT INTO t_null VALUES (x'FFFF0000', NULL); +# Expected output: NULL, NULL, NULL, NULL, NULL, NULL +SELECT HEX(a & b), HEX(a | b), HEX(a ^ b), HEX(~b), HEX(a << b), HEX(a >> b) FROM t_null; +HEX(a & b) HEX(a | b) HEX(a ^ b) HEX(~b) HEX(a << b) HEX(a >> b) +NULL NULL NULL NULL NULL NULL +DROP TABLE t_null; +# ========================================================================= +# SECTION 4 — Mismatched length error (in strict mode via INSERT) +# ========================================================================= +# Rationale: Verifies that operating on binary string operands with mismatched +# lengths throws ER_INVALID_BITWISE_OPERANDS_SIZE (error 4265) when escalating +# to an error under strict mode on INSERT. +# ========================================================================= +CREATE TABLE t_mismatch (a VARBINARY(4), b VARBINARY(2)); +INSERT INTO t_mismatch VALUES (x'FFFF0000', x'FFFF'); +SET sql_mode='STRICT_TRANS_TABLES'; +CREATE TABLE t_insert_mismatch (res VARBINARY(4)); +INSERT INTO t_insert_mismatch SELECT a & b FROM t_mismatch; +ERROR HY000: Mismatched length of operands for bitwise operator: 4 and 2 +INSERT INTO t_insert_mismatch SELECT a | b FROM t_mismatch; +ERROR HY000: Mismatched length of operands for bitwise operator: 4 and 2 +INSERT INTO t_insert_mismatch SELECT a ^ b FROM t_mismatch; +ERROR HY000: Mismatched length of operands for bitwise operator: 4 and 2 +DROP TABLE t_insert_mismatch; +SET sql_mode=DEFAULT; +DROP TABLE t_mismatch; +# ========================================================================= +# SECTION 5 — Literal behavior (FINAL decided behavior) +# ========================================================================= +# Rationale: Records the decided behavior of constant/hex-constant literals and +# string/binary cast expressions, per Alexander and Sergei's review. +# ========================================================================= +# Expected output: 0 +SELECT x'FF' & 12; +x'FF' & 12 +0 +Warnings: +Warning 1292 Truncated incorrect DECIMAL value: '\xFF' +# Expected output: 12 +SELECT 0xFF & 12; +0xFF & 12 +12 +# Expected output: 60 +SELECT HEX(_binary'a' & _binary'b'); +HEX(_binary'a' & _binary'b') +60 +# Expected output: 0 +SELECT 'a' & 'b'; +'a' & 'b' +0 +Warnings: +Warning 1292 Truncated incorrect DECIMAL value: 'a' +Warning 1292 Truncated incorrect DECIMAL value: 'b' +# Verification of binary vs integer mode execution on BLOB operands +CREATE OR REPLACE TABLE t1 (a BLOB); +INSERT INTO t1 VALUES ('a'); +SELECT +a & 51, +a & 0x33, +a & x'33', +a & _latin1 '3', +a & _binary '3', +0x33 & 0x33 +FROM t1; +a & 51 a & 0x33 a & x'33' a & _latin1 '3' a & _binary '3' 0x33 & 0x33 +0 0 ! 0 ! 51 +Warnings: +Warning 1292 Truncated incorrect DECIMAL value: 'a' +Warning 1292 Truncated incorrect DECIMAL value: 'a' +Warning 1292 Truncated incorrect DECIMAL value: 'a' +DROP TABLE t1; +# Verification that COALESCE correctly retains hex-hybrid type handler +SELECT HEX(COALESCE(0x30) & 1); +HEX(COALESCE(0x30) & 1) +0 +# ========================================================================= +# SECTION 6 — Existing INET6/UUID error preservation +# ========================================================================= +# Rationale: Confirms that existing parameter data type restrictions for +# INET6 and UUID types are preserved and throw ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION (4079). +# ========================================================================= +SELECT CAST('::' AS INET6) & CAST('::' AS INET6); +ERROR HY000: Illegal parameter data type inet6 for operation '&' +SELECT CAST(UUID() AS UUID) & CAST(UUID() AS UUID); +ERROR HY000: Illegal parameter data type uuid for operation '&' +SELECT CAST('1.2.3.4' AS INET4) & CAST('1.2.3.4' AS INET4); +ERROR HY000: Illegal parameter data type inet4 for operation '&' +SELECT CAST('1.2.3.4' AS INET4) & 1; +ERROR HY000: Illegal parameter data type inet4 for operation '&' +SELECT ST_GeomFromText('POINT(1 1)') & ST_GeomFromText('POINT(1 1)'); +ERROR HY000: Illegal parameter data type geometry for operation '&' +SELECT ST_GeomFromText('POINT(1 1)') & 1; +ERROR HY000: Illegal parameter data type geometry for operation '&' +# ========================================================================= +# SECTION 7 — Aggregate functions +# ========================================================================= +# Rationale: Verifies BIT_AND, BIT_OR, and BIT_XOR aggregate function +# calculations on VARBINARY columns under normal grouping conditions. +# ========================================================================= +CREATE TABLE t_agg (a VARBINARY(4)); +INSERT INTO t_agg VALUES (x'FF000000'), (x'0F0F0F0F'), (x'00FF0000'); +# Expected output: FFFF0F0F, 00000000, F0F00F0F +SELECT HEX(BIT_OR(a)), HEX(BIT_AND(a)), HEX(BIT_XOR(a)) FROM t_agg; +HEX(BIT_OR(a)) HEX(BIT_AND(a)) HEX(BIT_XOR(a)) +FFFF0F0F 00000000 F0F00F0F +DROP TABLE t_agg; +# GROUP BY test with multiple groups +CREATE TABLE t_grp (grp INT, a VARBINARY(4)); +INSERT INTO t_grp VALUES (1, x'FF000000'), (1, x'0F0F0F0F'), (2, x'00FF0000'), (2, x'0F0F0F0F'); +# Expected output: +# 1, FF0F0F0F, 0F000000, F00F0F0F +# 2, 0FFF0F0F, 000F0000, 0FF00F0F +SELECT grp, HEX(BIT_OR(a)), HEX(BIT_AND(a)), HEX(BIT_XOR(a)) FROM t_grp GROUP BY grp; +grp HEX(BIT_OR(a)) HEX(BIT_AND(a)) HEX(BIT_XOR(a)) +1 FF0F0F0F 0F000000 F00F0F0F +2 0FFF0F0F 000F0000 0FF00F0F +DROP TABLE t_grp; +# ========================================================================= +# SECTION 8 — Aggregate edge cases +# ========================================================================= +# Rationale: Tests aggregate functions with NULL rows, empty tables, +# and verify that operand sizes > 512 bytes are correctly blocked by the +# size guard with ER_INVALID_BITWISE_AGGREGATE_OPERANDS_SIZE (4266). +# ========================================================================= +CREATE TABLE t_edge (a VARBINARY(4)); +INSERT INTO t_edge VALUES (x'FF000000'), (NULL), (x'00FF0000'); +# Expected output: FFFF0000, 00000000, FFFF0000 +SELECT HEX(BIT_OR(a)), HEX(BIT_AND(a)), HEX(BIT_XOR(a)) FROM t_edge; +HEX(BIT_OR(a)) HEX(BIT_AND(a)) HEX(BIT_XOR(a)) +FFFF0000 00000000 FFFF0000 +# Empty result set (no rows) +# Expected output: TBD - verify via --record (likely neutral element: 00000000, FFFFFFFF, 00000000 based on binary_length) +SELECT HEX(BIT_OR(a)), HEX(BIT_AND(a)), HEX(BIT_XOR(a)) FROM t_edge WHERE 1=0; +HEX(BIT_OR(a)) HEX(BIT_AND(a)) HEX(BIT_XOR(a)) +00000000 FFFFFFFF 00000000 +DROP TABLE t_edge; +# Size guard validation (operand size > 512 bytes) +SELECT BIT_OR(CAST('a' AS BINARY(513))); +ERROR HY000: Operand size 513 exceeds maximum limit of 512 bytes for bitwise aggregate function +# ========================================================================= +# SECTION 9 — Backward compatibility — integer mode unchanged +# ========================================================================= +# Rationale: Verifies that operations on integer literals behave exactly +# as they did in pre-patch versions of MariaDB. +# ========================================================================= +# Expected output: 1, 7, 6, 18446744073709551615, 4, 4 +SELECT 5 & 3, 5 | 3, 5 ^ 3, ~0, 1 << 2, 8 >> 1; +5 & 3 5 | 3 5 ^ 3 ~0 1 << 2 8 >> 1 +1 7 6 18446744073709551615 4 4 +# ========================================================================= +# SECTION 10 — CREATE TABLE AS SELECT type preservation +# ========================================================================= +# Rationale: Verifies that CTAS (Create Table As Select) correctly preserves +# the resulting datatype as a VARBINARY/binary type. +# ========================================================================= +CREATE TABLE t_src (a VARBINARY(4), b VARBINARY(4)); +CREATE TABLE t_dest AS SELECT a & b AS res FROM t_src; +# Expected output: SHOW CREATE TABLE shows `res` as VARBINARY(4) or blob-type +SHOW CREATE TABLE t_dest; +Table Create Table +t_dest CREATE TABLE `t_dest` ( + `res` binary(4) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t_dest; +DROP TABLE t_src; +# ========================================================================= +# SECTION 11 — return_type_handler length-boundary tests +# ========================================================================= +# Rationale: Verifies that CTAS picks the correct storage type at each +# boundary in return_type_handler(): <=255 -> CHAR/BINARY (type_handler_string), +# 256..MAX_FIELD_VARCHARLENGTH -> VARCHAR (type_handler_varchar), +# >MAX_FIELD_VARCHARLENGTH (65532) -> BLOB family (blob_type_handler). +# Covers all binary operators so every Handler_str subclass is exercised. +# ========================================================================= +# -- Small (<=255 bytes): expect BINARY(10) / char-family type +CREATE TABLE t_small (a VARBINARY(10), b VARBINARY(10)); +CREATE TABLE t_small_and AS SELECT a & b AS res FROM t_small; +CREATE TABLE t_small_or AS SELECT a | b AS res FROM t_small; +CREATE TABLE t_small_xor AS SELECT a ^ b AS res FROM t_small; +CREATE TABLE t_small_neg AS SELECT ~a AS res FROM t_small; +CREATE TABLE t_small_shl AS SELECT a << 1 AS res FROM t_small; +CREATE TABLE t_small_shr AS SELECT a >> 1 AS res FROM t_small; +SHOW CREATE TABLE t_small_and; +Table Create Table +t_small_and CREATE TABLE `t_small_and` ( + `res` binary(10) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SHOW CREATE TABLE t_small_or; +Table Create Table +t_small_or CREATE TABLE `t_small_or` ( + `res` binary(10) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SHOW CREATE TABLE t_small_xor; +Table Create Table +t_small_xor CREATE TABLE `t_small_xor` ( + `res` binary(10) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SHOW CREATE TABLE t_small_neg; +Table Create Table +t_small_neg CREATE TABLE `t_small_neg` ( + `res` binary(10) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SHOW CREATE TABLE t_small_shl; +Table Create Table +t_small_shl CREATE TABLE `t_small_shl` ( + `res` binary(10) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SHOW CREATE TABLE t_small_shr; +Table Create Table +t_small_shr CREATE TABLE `t_small_shr` ( + `res` binary(10) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t_small_and, t_small_or, t_small_xor, +t_small_neg, t_small_shl, t_small_shr, t_small; +# -- Medium (256 .. 65532 bytes): expect VARCHAR(300) / varchar-family type +CREATE TABLE t_med (a VARBINARY(300), b VARBINARY(300)); +CREATE TABLE t_med_and AS SELECT a & b AS res FROM t_med; +CREATE TABLE t_med_or AS SELECT a | b AS res FROM t_med; +CREATE TABLE t_med_xor AS SELECT a ^ b AS res FROM t_med; +CREATE TABLE t_med_neg AS SELECT ~a AS res FROM t_med; +CREATE TABLE t_med_shl AS SELECT a << 1 AS res FROM t_med; +CREATE TABLE t_med_shr AS SELECT a >> 1 AS res FROM t_med; +SHOW CREATE TABLE t_med_and; +Table Create Table +t_med_and CREATE TABLE `t_med_and` ( + `res` varbinary(300) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SHOW CREATE TABLE t_med_or; +Table Create Table +t_med_or CREATE TABLE `t_med_or` ( + `res` varbinary(300) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SHOW CREATE TABLE t_med_xor; +Table Create Table +t_med_xor CREATE TABLE `t_med_xor` ( + `res` varbinary(300) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SHOW CREATE TABLE t_med_neg; +Table Create Table +t_med_neg CREATE TABLE `t_med_neg` ( + `res` varbinary(300) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SHOW CREATE TABLE t_med_shl; +Table Create Table +t_med_shl CREATE TABLE `t_med_shl` ( + `res` varbinary(300) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SHOW CREATE TABLE t_med_shr; +Table Create Table +t_med_shr CREATE TABLE `t_med_shr` ( + `res` varbinary(300) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t_med_and, t_med_or, t_med_xor, +t_med_neg, t_med_shl, t_med_shr, t_med; +# -- Large (>65532 bytes): expect BLOB family type +# VARBINARY max is 65532 = MAX_FIELD_VARCHARLENGTH; use BLOB columns (max_length=65535) +# to exercise the blob_type_handler branch in return_type_handler(). +CREATE TABLE t_large (a BLOB, b BLOB); +CREATE TABLE t_large_and AS SELECT a & b AS res FROM t_large; +CREATE TABLE t_large_or AS SELECT a | b AS res FROM t_large; +CREATE TABLE t_large_xor AS SELECT a ^ b AS res FROM t_large; +CREATE TABLE t_large_neg AS SELECT ~a AS res FROM t_large; +SHOW CREATE TABLE t_large_and; +Table Create Table +t_large_and CREATE TABLE `t_large_and` ( + `res` blob DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SHOW CREATE TABLE t_large_or; +Table Create Table +t_large_or CREATE TABLE `t_large_or` ( + `res` blob DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SHOW CREATE TABLE t_large_xor; +Table Create Table +t_large_xor CREATE TABLE `t_large_xor` ( + `res` blob DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SHOW CREATE TABLE t_large_neg; +Table Create Table +t_large_neg CREATE TABLE `t_large_neg` ( + `res` blob DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t_large_and, t_large_or, t_large_xor, t_large_neg, t_large; +# -- Medium BLOB (>65535 bytes, <=16777215 bytes): expect MEDIUMBLOB +CREATE TABLE t_mblob (a MEDIUMBLOB, b MEDIUMBLOB); +CREATE TABLE t_mblob_dest AS SELECT a & b AS res FROM t_mblob LIMIT 0; +SHOW CREATE TABLE t_mblob_dest; +Table Create Table +t_mblob_dest CREATE TABLE `t_mblob_dest` ( + `res` mediumblob DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t_mblob_dest, t_mblob; +# -- Long BLOB (>16777215 bytes): expect LONGBLOB +CREATE TABLE t_lblob (a LONGBLOB, b LONGBLOB); +CREATE TABLE t_lblob_dest AS SELECT a & b AS res FROM t_lblob LIMIT 0; +SHOW CREATE TABLE t_lblob_dest; +Table Create Table +t_lblob_dest CREATE TABLE `t_lblob_dest` ( + `res` longblob DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t_lblob_dest, t_lblob; +# ========================================================================= +# SECTION 12 — Large literal tests with REPEAT() +# ========================================================================= +# Rationale: Verifies that byte-loop and max_length logic in the binary +# handlers scale correctly to large binary strings well beyond 4-16 bytes. +# Uses _binary cast to force binary mode on user variables. +# ========================================================================= +# -- 100-byte operands: check result length and spot-check first 4 bytes +SET @a100 = REPEAT('a', 100); +SET @b100 = REPEAT('b', 100); +SELECT LENGTH(CAST(@a100 AS BINARY(100)) & CAST(@b100 AS BINARY(100))); +LENGTH(CAST(@a100 AS BINARY(100)) & CAST(@b100 AS BINARY(100))) +100 +SELECT HEX(LEFT(CAST(@a100 AS BINARY(100)) & CAST(@b100 AS BINARY(100)), 4)); +HEX(LEFT(CAST(@a100 AS BINARY(100)) & CAST(@b100 AS BINARY(100)), 4)) +60606060 +SELECT LENGTH(CAST(@a100 AS BINARY(100)) | CAST(@b100 AS BINARY(100))); +LENGTH(CAST(@a100 AS BINARY(100)) | CAST(@b100 AS BINARY(100))) +100 +SELECT LENGTH(CAST(@a100 AS BINARY(100)) ^ CAST(@b100 AS BINARY(100))); +LENGTH(CAST(@a100 AS BINARY(100)) ^ CAST(@b100 AS BINARY(100))) +100 +# -- 512-byte operands (at aggregate size guard boundary) +SET @a512 = REPEAT('x', 512); +SET @b512 = REPEAT('y', 512); +SELECT LENGTH(CAST(@a512 AS BINARY(512)) & CAST(@b512 AS BINARY(512))); +LENGTH(CAST(@a512 AS BINARY(512)) & CAST(@b512 AS BINARY(512))) +512 +SELECT LENGTH(CAST(@a512 AS BINARY(512)) | CAST(@b512 AS BINARY(512))); +LENGTH(CAST(@a512 AS BINARY(512)) | CAST(@b512 AS BINARY(512))) +512 +SELECT LENGTH(CAST(@a512 AS BINARY(512)) ^ CAST(@b512 AS BINARY(512))); +LENGTH(CAST(@a512 AS BINARY(512)) ^ CAST(@b512 AS BINARY(512))) +512 +# ========================================================================= +# SECTION 13 — COERCIBILITY() validation +# ========================================================================= +# Rationale: Confirms that binary-mode results carry DERIVATION_COERCIBLE +# (value 6), which was explicitly set in the fix_length_and_dec() handlers. +# This validates the collation derivation fix from review feedback. +# Per MariaDB docs, DERIVATION_COERCIBLE maps to COERCIBILITY() = 6. +# ========================================================================= +CREATE TABLE t_coerc (a VARBINARY(4), b VARBINARY(4)); +INSERT INTO t_coerc VALUES (x'FFFF0000', x'FF00FF00'); +# All binary-mode scalar operators should return coercibility 6 +SELECT COERCIBILITY(a & b) FROM t_coerc; +COERCIBILITY(a & b) +6 +SELECT COERCIBILITY(a | b) FROM t_coerc; +COERCIBILITY(a | b) +6 +SELECT COERCIBILITY(a ^ b) FROM t_coerc; +COERCIBILITY(a ^ b) +6 +SELECT COERCIBILITY(~a) FROM t_coerc; +COERCIBILITY(~a) +6 +SELECT COERCIBILITY(a << 1) FROM t_coerc; +COERCIBILITY(a << 1) +6 +SELECT COERCIBILITY(a >> 1) FROM t_coerc; +COERCIBILITY(a >> 1) +6 +DROP TABLE t_coerc; +# ========================================================================= +# SECTION 14 — Mismatched length warning (outside strict mode via SELECT) +# ========================================================================= +# Rationale: Verifies that SELECT with mismatched lengths outside strict mode +# returns NULL with warning 4265 (ER_INVALID_BITWISE_OPERANDS_SIZE) for all +# three binary operators (&, |, ^). +# ========================================================================= +CREATE TABLE t_mismatch (a VARBINARY(4), b VARBINARY(2)); +INSERT INTO t_mismatch VALUES (x'FFFF0000', x'FFFF'); +SET sql_mode=''; +SELECT a & b FROM t_mismatch; +a & b +NULL +Warnings: +Warning 4265 Mismatched length of operands for bitwise operator: 4 and 2 +SHOW WARNINGS; +Level Code Message +Warning 4265 Mismatched length of operands for bitwise operator: 4 and 2 +SELECT a | b FROM t_mismatch; +a | b +NULL +Warnings: +Warning 4265 Mismatched length of operands for bitwise operator: 4 and 2 +SHOW WARNINGS; +Level Code Message +Warning 4265 Mismatched length of operands for bitwise operator: 4 and 2 +SELECT a ^ b FROM t_mismatch; +a ^ b +NULL +Warnings: +Warning 4265 Mismatched length of operands for bitwise operator: 4 and 2 +SHOW WARNINGS; +Level Code Message +Warning 4265 Mismatched length of operands for bitwise operator: 4 and 2 +SET sql_mode=DEFAULT; +DROP TABLE t_mismatch; +# ========================================================================= +# SECTION 15 — Window function support for binary mode aggregates +# ========================================================================= +# Rationale: Per Daniel Black's request, verify BIT_AND/OR/XOR work +# correctly as window functions on binary columns, including proper +# row removal when the window frame slides (not just full partition). +# ========================================================================= +CREATE TABLE t_win_part (grp INT, a VARBINARY(4)); +INSERT INTO t_win_part VALUES +(1, x'FF000000'), (1, x'0F0F0F0F'), +(2, x'00FF0000'), (2, x'000000FF'); +SELECT grp, HEX(a) as hex_a, +HEX(BIT_OR(a) OVER (PARTITION BY grp)) AS or_w, +HEX(BIT_AND(a) OVER (PARTITION BY grp)) AS and_w, +HEX(BIT_XOR(a) OVER (PARTITION BY grp)) AS xor_w +FROM t_win_part ORDER BY grp, hex_a; +grp hex_a or_w and_w xor_w +1 0F0F0F0F FF0F0F0F 0F000000 F00F0F0F +1 FF000000 FF0F0F0F 0F000000 F00F0F0F +2 000000FF 00FF00FF 00000000 00FF00FF +2 00FF0000 00FF00FF 00000000 00FF00FF +DROP TABLE t_win_part; +# Sliding window frame - proves remove_binary_as_window correctly +# evicts rows as the frame moves (not just accumulates everything) +CREATE TABLE t_win_slide (id INT, a VARBINARY(2)); +INSERT INTO t_win_slide VALUES +(1, x'FF00'), (2, x'0F0F'), (3, x'00FF'); +SELECT id, HEX(a) as hex_a, +HEX(BIT_OR(a) OVER (ORDER BY id ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)) AS sliding_or +FROM t_win_slide ORDER BY id; +id hex_a sliding_or +1 FF00 FF00 +2 0F0F FF0F +3 00FF 0FFF +DROP TABLE t_win_slide; diff --git a/mysql-test/main/func_bitops_binary.test b/mysql-test/main/func_bitops_binary.test new file mode 100644 index 0000000000000..dc53d2f5a2560 --- /dev/null +++ b/mysql-test/main/func_bitops_binary.test @@ -0,0 +1,396 @@ +--echo # +--echo # MDEV-10526: Binary string support for bitwise operators & aggregate functions +--echo # + +--echo # ========================================================================= +--echo # SECTION 1 — Scalar operators on VARBINARY +--echo # ========================================================================= +--echo # Rationale: Tests basic functional correctness of scalar bitwise operators +--echo # (&, |, ^, ~, <<, >>) when applied to VARBINARY(4) operands. +--echo # ========================================================================= + +CREATE TABLE t1 (a VARBINARY(4), b VARBINARY(4)); +INSERT INTO t1 VALUES (x'FFFF0000', x'FF00FF00'); + +--echo # Expected output: FF000000, FFFFFF00, 00FFFF00, 0000FFFF, FFFE0000, 7FFF8000 +SELECT HEX(a & b), HEX(a | b), HEX(a ^ b), HEX(~a), HEX(a << 1), HEX(a >> 1) FROM t1; + +DROP TABLE t1; + + +--echo # ========================================================================= +--echo # SECTION 2 — INET6_ATON real-world use case +--echo # ========================================================================= +--echo # Rationale: Verifies the primary target use case (subnet masking and IP math) +--echo # still operates correctly with INET6_ATON (which returns VARBINARY(16)). +--echo # ========================================================================= + +--echo # Expected output: 20010DB8000000000000000000000000 +SELECT HEX(INET6_ATON('ffff:ffff::') & INET6_ATON('2001:db8::1')); + +--echo # Expected output: TBD - verify via --record +SELECT HEX(INET6_ATON('ffff:ffff::') | INET6_ATON('2001:db8::1')); + +--echo # Expected output: TBD - verify via --record +SELECT HEX(INET6_ATON('ffff:ffff::') ^ INET6_ATON('2001:db8::1')); + + +--echo # ========================================================================= +--echo # SECTION 3 — NULL handling +--echo # ========================================================================= +--echo # Rationale: Ensures that if any of the operands are NULL, the result of the +--echo # bitwise operation is also NULL. +--echo # ========================================================================= + +CREATE TABLE t_null (a VARBINARY(4), b VARBINARY(4)); +INSERT INTO t_null VALUES (x'FFFF0000', NULL); + +--echo # Expected output: NULL, NULL, NULL, NULL, NULL, NULL +SELECT HEX(a & b), HEX(a | b), HEX(a ^ b), HEX(~b), HEX(a << b), HEX(a >> b) FROM t_null; + +DROP TABLE t_null; + + +--echo # ========================================================================= +--echo # SECTION 4 — Mismatched length error (in strict mode via INSERT) +--echo # ========================================================================= +--echo # Rationale: Verifies that operating on binary string operands with mismatched +--echo # lengths throws ER_INVALID_BITWISE_OPERANDS_SIZE (error 4265) when escalating +--echo # to an error under strict mode on INSERT. +--echo # ========================================================================= + +CREATE TABLE t_mismatch (a VARBINARY(4), b VARBINARY(2)); +INSERT INTO t_mismatch VALUES (x'FFFF0000', x'FFFF'); + +SET sql_mode='STRICT_TRANS_TABLES'; +CREATE TABLE t_insert_mismatch (res VARBINARY(4)); + +--error ER_INVALID_BITWISE_OPERANDS_SIZE +INSERT INTO t_insert_mismatch SELECT a & b FROM t_mismatch; + +--error ER_INVALID_BITWISE_OPERANDS_SIZE +INSERT INTO t_insert_mismatch SELECT a | b FROM t_mismatch; + +--error ER_INVALID_BITWISE_OPERANDS_SIZE +INSERT INTO t_insert_mismatch SELECT a ^ b FROM t_mismatch; + +DROP TABLE t_insert_mismatch; +SET sql_mode=DEFAULT; +DROP TABLE t_mismatch; + + +--echo # ========================================================================= +--echo # SECTION 5 — Literal behavior (FINAL decided behavior) +--echo # ========================================================================= +--echo # Rationale: Records the decided behavior of constant/hex-constant literals and +--echo # string/binary cast expressions, per Alexander and Sergei's review. +--echo # ========================================================================= + +--echo # Expected output: 0 +SELECT x'FF' & 12; + +--echo # Expected output: 12 +SELECT 0xFF & 12; + +--echo # Expected output: 60 +SELECT HEX(_binary'a' & _binary'b'); + +--echo # Expected output: 0 +SELECT 'a' & 'b'; + +--echo # Verification of binary vs integer mode execution on BLOB operands +CREATE OR REPLACE TABLE t1 (a BLOB); +INSERT INTO t1 VALUES ('a'); +SELECT + a & 51, + a & 0x33, + a & x'33', + a & _latin1 '3', + a & _binary '3', + 0x33 & 0x33 +FROM t1; +DROP TABLE t1; + +--echo # Verification that COALESCE correctly retains hex-hybrid type handler +SELECT HEX(COALESCE(0x30) & 1); + + +--echo # ========================================================================= +--echo # SECTION 6 — Existing INET6/UUID error preservation +--echo # ========================================================================= +--echo # Rationale: Confirms that existing parameter data type restrictions for +--echo # INET6 and UUID types are preserved and throw ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION (4079). +--echo # ========================================================================= + +--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION +SELECT CAST('::' AS INET6) & CAST('::' AS INET6); + +--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION +SELECT CAST(UUID() AS UUID) & CAST(UUID() AS UUID); + +--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION +SELECT CAST('1.2.3.4' AS INET4) & CAST('1.2.3.4' AS INET4); + +--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION +SELECT CAST('1.2.3.4' AS INET4) & 1; + +--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION +SELECT ST_GeomFromText('POINT(1 1)') & ST_GeomFromText('POINT(1 1)'); + +--error ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION +SELECT ST_GeomFromText('POINT(1 1)') & 1; + + +--echo # ========================================================================= +--echo # SECTION 7 — Aggregate functions +--echo # ========================================================================= +--echo # Rationale: Verifies BIT_AND, BIT_OR, and BIT_XOR aggregate function +--echo # calculations on VARBINARY columns under normal grouping conditions. +--echo # ========================================================================= + +CREATE TABLE t_agg (a VARBINARY(4)); +INSERT INTO t_agg VALUES (x'FF000000'), (x'0F0F0F0F'), (x'00FF0000'); + +--echo # Expected output: FFFF0F0F, 00000000, F0F00F0F +SELECT HEX(BIT_OR(a)), HEX(BIT_AND(a)), HEX(BIT_XOR(a)) FROM t_agg; + +DROP TABLE t_agg; + +--echo # GROUP BY test with multiple groups +CREATE TABLE t_grp (grp INT, a VARBINARY(4)); +INSERT INTO t_grp VALUES (1, x'FF000000'), (1, x'0F0F0F0F'), (2, x'00FF0000'), (2, x'0F0F0F0F'); + +--echo # Expected output: +--echo # 1, FF0F0F0F, 0F000000, F00F0F0F +--echo # 2, 0FFF0F0F, 000F0000, 0FF00F0F +SELECT grp, HEX(BIT_OR(a)), HEX(BIT_AND(a)), HEX(BIT_XOR(a)) FROM t_grp GROUP BY grp; + +DROP TABLE t_grp; + + +--echo # ========================================================================= +--echo # SECTION 8 — Aggregate edge cases +--echo # ========================================================================= +--echo # Rationale: Tests aggregate functions with NULL rows, empty tables, +--echo # and verify that operand sizes > 512 bytes are correctly blocked by the +--echo # size guard with ER_INVALID_BITWISE_AGGREGATE_OPERANDS_SIZE (4266). +--echo # ========================================================================= + +CREATE TABLE t_edge (a VARBINARY(4)); +INSERT INTO t_edge VALUES (x'FF000000'), (NULL), (x'00FF0000'); + +--echo # Expected output: FFFF0000, 00000000, FFFF0000 +SELECT HEX(BIT_OR(a)), HEX(BIT_AND(a)), HEX(BIT_XOR(a)) FROM t_edge; + +--echo # Empty result set (no rows) +--echo # Expected output: TBD - verify via --record (likely neutral element: 00000000, FFFFFFFF, 00000000 based on binary_length) +SELECT HEX(BIT_OR(a)), HEX(BIT_AND(a)), HEX(BIT_XOR(a)) FROM t_edge WHERE 1=0; + +DROP TABLE t_edge; + +--echo # Size guard validation (operand size > 512 bytes) +--error ER_INVALID_BITWISE_AGGREGATE_OPERANDS_SIZE +SELECT BIT_OR(CAST('a' AS BINARY(513))); + + +--echo # ========================================================================= +--echo # SECTION 9 — Backward compatibility — integer mode unchanged +--echo # ========================================================================= +--echo # Rationale: Verifies that operations on integer literals behave exactly +--echo # as they did in pre-patch versions of MariaDB. +--echo # ========================================================================= + +--echo # Expected output: 1, 7, 6, 18446744073709551615, 4, 4 +SELECT 5 & 3, 5 | 3, 5 ^ 3, ~0, 1 << 2, 8 >> 1; + + +--echo # ========================================================================= +--echo # SECTION 10 — CREATE TABLE AS SELECT type preservation +--echo # ========================================================================= +--echo # Rationale: Verifies that CTAS (Create Table As Select) correctly preserves +--echo # the resulting datatype as a VARBINARY/binary type. +--echo # ========================================================================= + +CREATE TABLE t_src (a VARBINARY(4), b VARBINARY(4)); +CREATE TABLE t_dest AS SELECT a & b AS res FROM t_src; + +--echo # Expected output: SHOW CREATE TABLE shows `res` as VARBINARY(4) or blob-type +SHOW CREATE TABLE t_dest; + +DROP TABLE t_dest; +DROP TABLE t_src; + + +--echo # ========================================================================= +--echo # SECTION 11 — return_type_handler length-boundary tests +--echo # ========================================================================= +--echo # Rationale: Verifies that CTAS picks the correct storage type at each +--echo # boundary in return_type_handler(): <=255 -> CHAR/BINARY (type_handler_string), +--echo # 256..MAX_FIELD_VARCHARLENGTH -> VARCHAR (type_handler_varchar), +--echo # >MAX_FIELD_VARCHARLENGTH (65532) -> BLOB family (blob_type_handler). +--echo # Covers all binary operators so every Handler_str subclass is exercised. +--echo # ========================================================================= + +--echo # -- Small (<=255 bytes): expect BINARY(10) / char-family type +CREATE TABLE t_small (a VARBINARY(10), b VARBINARY(10)); +CREATE TABLE t_small_and AS SELECT a & b AS res FROM t_small; +CREATE TABLE t_small_or AS SELECT a | b AS res FROM t_small; +CREATE TABLE t_small_xor AS SELECT a ^ b AS res FROM t_small; +CREATE TABLE t_small_neg AS SELECT ~a AS res FROM t_small; +CREATE TABLE t_small_shl AS SELECT a << 1 AS res FROM t_small; +CREATE TABLE t_small_shr AS SELECT a >> 1 AS res FROM t_small; +SHOW CREATE TABLE t_small_and; +SHOW CREATE TABLE t_small_or; +SHOW CREATE TABLE t_small_xor; +SHOW CREATE TABLE t_small_neg; +SHOW CREATE TABLE t_small_shl; +SHOW CREATE TABLE t_small_shr; +DROP TABLE t_small_and, t_small_or, t_small_xor, + t_small_neg, t_small_shl, t_small_shr, t_small; + +--echo # -- Medium (256 .. 65532 bytes): expect VARCHAR(300) / varchar-family type +CREATE TABLE t_med (a VARBINARY(300), b VARBINARY(300)); +CREATE TABLE t_med_and AS SELECT a & b AS res FROM t_med; +CREATE TABLE t_med_or AS SELECT a | b AS res FROM t_med; +CREATE TABLE t_med_xor AS SELECT a ^ b AS res FROM t_med; +CREATE TABLE t_med_neg AS SELECT ~a AS res FROM t_med; +CREATE TABLE t_med_shl AS SELECT a << 1 AS res FROM t_med; +CREATE TABLE t_med_shr AS SELECT a >> 1 AS res FROM t_med; +SHOW CREATE TABLE t_med_and; +SHOW CREATE TABLE t_med_or; +SHOW CREATE TABLE t_med_xor; +SHOW CREATE TABLE t_med_neg; +SHOW CREATE TABLE t_med_shl; +SHOW CREATE TABLE t_med_shr; +DROP TABLE t_med_and, t_med_or, t_med_xor, + t_med_neg, t_med_shl, t_med_shr, t_med; + +--echo # -- Large (>65532 bytes): expect BLOB family type +--echo # VARBINARY max is 65532 = MAX_FIELD_VARCHARLENGTH; use BLOB columns (max_length=65535) +--echo # to exercise the blob_type_handler branch in return_type_handler(). +CREATE TABLE t_large (a BLOB, b BLOB); +CREATE TABLE t_large_and AS SELECT a & b AS res FROM t_large; +CREATE TABLE t_large_or AS SELECT a | b AS res FROM t_large; +CREATE TABLE t_large_xor AS SELECT a ^ b AS res FROM t_large; +CREATE TABLE t_large_neg AS SELECT ~a AS res FROM t_large; +SHOW CREATE TABLE t_large_and; +SHOW CREATE TABLE t_large_or; +SHOW CREATE TABLE t_large_xor; +SHOW CREATE TABLE t_large_neg; +DROP TABLE t_large_and, t_large_or, t_large_xor, t_large_neg, t_large; + +--echo # -- Medium BLOB (>65535 bytes, <=16777215 bytes): expect MEDIUMBLOB +CREATE TABLE t_mblob (a MEDIUMBLOB, b MEDIUMBLOB); +CREATE TABLE t_mblob_dest AS SELECT a & b AS res FROM t_mblob LIMIT 0; +SHOW CREATE TABLE t_mblob_dest; +DROP TABLE t_mblob_dest, t_mblob; + +--echo # -- Long BLOB (>16777215 bytes): expect LONGBLOB +CREATE TABLE t_lblob (a LONGBLOB, b LONGBLOB); +CREATE TABLE t_lblob_dest AS SELECT a & b AS res FROM t_lblob LIMIT 0; +SHOW CREATE TABLE t_lblob_dest; +DROP TABLE t_lblob_dest, t_lblob; + + +--echo # ========================================================================= +--echo # SECTION 12 — Large literal tests with REPEAT() +--echo # ========================================================================= +--echo # Rationale: Verifies that byte-loop and max_length logic in the binary +--echo # handlers scale correctly to large binary strings well beyond 4-16 bytes. +--echo # Uses _binary cast to force binary mode on user variables. +--echo # ========================================================================= + +--echo # -- 100-byte operands: check result length and spot-check first 4 bytes +SET @a100 = REPEAT('a', 100); +SET @b100 = REPEAT('b', 100); +SELECT LENGTH(CAST(@a100 AS BINARY(100)) & CAST(@b100 AS BINARY(100))); +SELECT HEX(LEFT(CAST(@a100 AS BINARY(100)) & CAST(@b100 AS BINARY(100)), 4)); +SELECT LENGTH(CAST(@a100 AS BINARY(100)) | CAST(@b100 AS BINARY(100))); +SELECT LENGTH(CAST(@a100 AS BINARY(100)) ^ CAST(@b100 AS BINARY(100))); + +--echo # -- 512-byte operands (at aggregate size guard boundary) +SET @a512 = REPEAT('x', 512); +SET @b512 = REPEAT('y', 512); +SELECT LENGTH(CAST(@a512 AS BINARY(512)) & CAST(@b512 AS BINARY(512))); +SELECT LENGTH(CAST(@a512 AS BINARY(512)) | CAST(@b512 AS BINARY(512))); +SELECT LENGTH(CAST(@a512 AS BINARY(512)) ^ CAST(@b512 AS BINARY(512))); + + +--echo # ========================================================================= +--echo # SECTION 13 — COERCIBILITY() validation +--echo # ========================================================================= +--echo # Rationale: Confirms that binary-mode results carry DERIVATION_COERCIBLE +--echo # (value 6), which was explicitly set in the fix_length_and_dec() handlers. +--echo # This validates the collation derivation fix from review feedback. +--echo # Per MariaDB docs, DERIVATION_COERCIBLE maps to COERCIBILITY() = 6. +--echo # ========================================================================= + +CREATE TABLE t_coerc (a VARBINARY(4), b VARBINARY(4)); +INSERT INTO t_coerc VALUES (x'FFFF0000', x'FF00FF00'); + +--echo # All binary-mode scalar operators should return coercibility 6 +SELECT COERCIBILITY(a & b) FROM t_coerc; +SELECT COERCIBILITY(a | b) FROM t_coerc; +SELECT COERCIBILITY(a ^ b) FROM t_coerc; +SELECT COERCIBILITY(~a) FROM t_coerc; +SELECT COERCIBILITY(a << 1) FROM t_coerc; +SELECT COERCIBILITY(a >> 1) FROM t_coerc; + +DROP TABLE t_coerc; + + +--echo # ========================================================================= +--echo # SECTION 14 — Mismatched length warning (outside strict mode via SELECT) +--echo # ========================================================================= +--echo # Rationale: Verifies that SELECT with mismatched lengths outside strict mode +--echo # returns NULL with warning 4265 (ER_INVALID_BITWISE_OPERANDS_SIZE) for all +--echo # three binary operators (&, |, ^). +--echo # ========================================================================= + +CREATE TABLE t_mismatch (a VARBINARY(4), b VARBINARY(2)); +INSERT INTO t_mismatch VALUES (x'FFFF0000', x'FFFF'); + +SET sql_mode=''; + +SELECT a & b FROM t_mismatch; +SHOW WARNINGS; + +SELECT a | b FROM t_mismatch; +SHOW WARNINGS; + +SELECT a ^ b FROM t_mismatch; +SHOW WARNINGS; + +SET sql_mode=DEFAULT; +DROP TABLE t_mismatch; + + +--echo # ========================================================================= +--echo # SECTION 15 — Window function support for binary mode aggregates +--echo # ========================================================================= +--echo # Rationale: Per Daniel Black's request, verify BIT_AND/OR/XOR work +--echo # correctly as window functions on binary columns, including proper +--echo # row removal when the window frame slides (not just full partition). +--echo # ========================================================================= + +CREATE TABLE t_win_part (grp INT, a VARBINARY(4)); +INSERT INTO t_win_part VALUES + (1, x'FF000000'), (1, x'0F0F0F0F'), + (2, x'00FF0000'), (2, x'000000FF'); +SELECT grp, HEX(a) as hex_a, + HEX(BIT_OR(a) OVER (PARTITION BY grp)) AS or_w, + HEX(BIT_AND(a) OVER (PARTITION BY grp)) AS and_w, + HEX(BIT_XOR(a) OVER (PARTITION BY grp)) AS xor_w +FROM t_win_part ORDER BY grp, hex_a; +DROP TABLE t_win_part; + +--echo # Sliding window frame - proves remove_binary_as_window correctly +--echo # evicts rows as the frame moves (not just accumulates everything) +CREATE TABLE t_win_slide (id INT, a VARBINARY(2)); +INSERT INTO t_win_slide VALUES + (1, x'FF00'), (2, x'0F0F'), (3, x'00FF'); +SELECT id, HEX(a) as hex_a, + HEX(BIT_OR(a) OVER (ORDER BY id ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)) AS sliding_or +FROM t_win_slide ORDER BY id; +DROP TABLE t_win_slide; + diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index eaa705642d843..0b8417f127d54 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -5062,76 +5062,6 @@ bool Item_func_in::ora_join_processor(void *arg) return FALSE; } - -class Func_handler_bit_or_int_to_ulonglong: - public Item_handled_func::Handler_ulonglong -{ -public: - Longlong_null to_longlong_null(Item_handled_func *item) const override - { - DBUG_ASSERT(item->fixed()); - Longlong_null a= item->arguments()[0]->to_longlong_null(); - return a.is_null() ? a : a | item->arguments()[1]->to_longlong_null(); - } -}; - - -class Func_handler_bit_or_dec_to_ulonglong: - public Item_handled_func::Handler_ulonglong -{ -public: - Longlong_null to_longlong_null(Item_handled_func *item) const override - { - DBUG_ASSERT(item->fixed()); - VDec a(item->arguments()[0]); - return a.is_null() ? Longlong_null() : - a.to_xlonglong_null() | VDec(item->arguments()[1]).to_xlonglong_null(); - } -}; - - -bool Item_func_bit_or::fix_length_and_dec(THD *thd) -{ - static Func_handler_bit_or_int_to_ulonglong ha_int_to_ull; - static Func_handler_bit_or_dec_to_ulonglong ha_dec_to_ull; - return fix_length_and_dec_op2_std(&ha_int_to_ull, &ha_dec_to_ull); -} - - -class Func_handler_bit_and_int_to_ulonglong: - public Item_handled_func::Handler_ulonglong -{ -public: - Longlong_null to_longlong_null(Item_handled_func *item) const override - { - DBUG_ASSERT(item->fixed()); - Longlong_null a= item->arguments()[0]->to_longlong_null(); - return a.is_null() ? a : a & item->arguments()[1]->to_longlong_null(); - } -}; - - -class Func_handler_bit_and_dec_to_ulonglong: - public Item_handled_func::Handler_ulonglong -{ -public: - Longlong_null to_longlong_null(Item_handled_func *item) const override - { - DBUG_ASSERT(item->fixed()); - VDec a(item->arguments()[0]); - return a.is_null() ? Longlong_null() : - a.to_xlonglong_null() & VDec(item->arguments()[1]).to_xlonglong_null(); - } -}; - - -bool Item_func_bit_and::fix_length_and_dec(THD *thd) -{ - static Func_handler_bit_and_int_to_ulonglong ha_int_to_ull; - static Func_handler_bit_and_dec_to_ulonglong ha_dec_to_ull; - return fix_length_and_dec_op2_std(&ha_int_to_ull, &ha_dec_to_ull); -} - Item_cond::Item_cond(THD *thd, Item_cond *item) :Item_bool_func(thd, item), and_tables_cache(item->and_tables_cache) diff --git a/sql/item_func.cc b/sql/item_func.cc index a3519b6d5ba20..97990abc2af55 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2223,10 +2223,123 @@ class Func_handler_shift_left_decimal_to_ulonglong: }; +class Func_handler_shift_left_bin_to_bin: public Item_handled_func::Handler_str +{ +public: + const Type_handler *return_type_handler(const Item_handled_func *item) const override + { + if (item->max_length > MAX_FIELD_VARCHARLENGTH) + return Type_handler::blob_type_handler(item->max_length); + if (item->max_length > 255) + return &type_handler_varchar; + return &type_handler_string; + } + + bool fix_length_and_dec(Item_handled_func *item) const override + { + item->max_length= item->arguments()[0]->max_length; + item->collation.set(&my_charset_bin, DERIVATION_COERCIBLE); + return false; + } + + String *val_str(Item_handled_func *item, String *to) const override + { + DBUG_ASSERT(item->fixed()); + StringBuffer<128> a_buf; + String *a= item->arguments()[0]->val_str(&a_buf); + if (a == nullptr) + { + item->null_value= true; + return nullptr; + } + + Longlong_null shift_count_null= item->arguments()[1]->to_longlong_null(); + if (shift_count_null.is_null()) + { + item->null_value= true; + return nullptr; + } + + longlong shift_count_signed= shift_count_null.value(); + size_t len= a->length(); + if (len == 0) + { + to->length(0); + to->set_charset(&my_charset_bin); + item->null_value= false; + return to; + } + + if (to->realloc(len)) + { + item->null_value= true; + return nullptr; + } + + uchar *out_ptr= (uchar *) to->ptr(); + const uchar *a_ptr= (const uchar *) a->ptr(); + + if (shift_count_signed < 0 || (ulonglong)shift_count_signed >= (ulonglong)len * 8) + { + memset(out_ptr, 0, len); + } + else + { + ulonglong shift_count= (ulonglong) shift_count_signed; + size_t byte_shift= shift_count / 8; + uint bit_shift= shift_count % 8; + + for (size_t i= 0; i < len; ++i) + { + if (byte_shift < len - i) + { + size_t src_idx= i + byte_shift; + if (bit_shift > 0) + { + uchar val= (uchar)(a_ptr[src_idx] << bit_shift); + if (src_idx + 1 < len) + { + val|= (uchar)(a_ptr[src_idx + 1] >> (8 - bit_shift)); + } + out_ptr[i]= val; + } + else + { + out_ptr[i]= a_ptr[src_idx]; + } + } + else + { + out_ptr[i]= 0; + } + } + } + + to->length(len); + to->set_charset(&my_charset_bin); + item->null_value= false; + return to; + } +}; + + bool Item_func_shift_left::fix_length_and_dec(THD *thd) { + static Func_handler_shift_left_bin_to_bin ha_bin_to_bin; static Func_handler_shift_left_int_to_ulonglong ha_int_to_ull; static Func_handler_shift_left_decimal_to_ulonglong ha_dec_to_ull; + + const Type_handler *th= args[0]->type_handler(); + bool is_longstr= dynamic_cast(th) != nullptr; + bool is_hybrid= dynamic_cast(th) != nullptr; + bool is_binary= args[0]->collation.collation == &my_charset_bin; + + if (is_longstr && is_binary && !is_hybrid) + { + set_func_handler(&ha_bin_to_bin); + return m_func_handler->fix_length_and_dec(this); + } + return fix_length_and_dec_op1_std(&ha_int_to_ull, &ha_dec_to_ull); } @@ -2257,10 +2370,123 @@ class Func_handler_shift_right_decimal_to_ulonglong: }; +class Func_handler_shift_right_bin_to_bin: public Item_handled_func::Handler_str +{ +public: + const Type_handler *return_type_handler(const Item_handled_func *item) const override + { + if (item->max_length > MAX_FIELD_VARCHARLENGTH) + return Type_handler::blob_type_handler(item->max_length); + if (item->max_length > 255) + return &type_handler_varchar; + return &type_handler_string; + } + + bool fix_length_and_dec(Item_handled_func *item) const override + { + item->max_length= item->arguments()[0]->max_length; + item->collation.set(&my_charset_bin, DERIVATION_COERCIBLE); + return false; + } + + String *val_str(Item_handled_func *item, String *to) const override + { + DBUG_ASSERT(item->fixed()); + StringBuffer<128> a_buf; + String *a= item->arguments()[0]->val_str(&a_buf); + if (a == nullptr) + { + item->null_value= true; + return nullptr; + } + + Longlong_null shift_count_null= item->arguments()[1]->to_longlong_null(); + if (shift_count_null.is_null()) + { + item->null_value= true; + return nullptr; + } + + longlong shift_count_signed= shift_count_null.value(); + size_t len= a->length(); + if (len == 0) + { + to->length(0); + to->set_charset(&my_charset_bin); + item->null_value= false; + return to; + } + + if (to->realloc(len)) + { + item->null_value= true; + return nullptr; + } + + uchar *out_ptr= (uchar *) to->ptr(); + const uchar *a_ptr= (const uchar *) a->ptr(); + + if (shift_count_signed < 0 || (ulonglong)shift_count_signed >= (ulonglong)len * 8) + { + memset(out_ptr, 0, len); + } + else + { + ulonglong shift_count= (ulonglong) shift_count_signed; + size_t byte_shift= shift_count / 8; + uint bit_shift= shift_count % 8; + + for (size_t i= 0; i < len; ++i) + { + if (i >= byte_shift) + { + size_t src_idx= i - byte_shift; + if (bit_shift > 0) + { + uchar val= (uchar)(a_ptr[src_idx] >> bit_shift); + if (src_idx > 0) + { + val|= (uchar)(a_ptr[src_idx - 1] << (8 - bit_shift)); + } + out_ptr[i]= val; + } + else + { + out_ptr[i]= a_ptr[src_idx]; + } + } + else + { + out_ptr[i]= 0; + } + } + } + + to->length(len); + to->set_charset(&my_charset_bin); + item->null_value= false; + return to; + } +}; + + bool Item_func_shift_right::fix_length_and_dec(THD *thd) { + static Func_handler_shift_right_bin_to_bin ha_bin_to_bin; static Func_handler_shift_right_int_to_ulonglong ha_int_to_ull; static Func_handler_shift_right_decimal_to_ulonglong ha_dec_to_ull; + + const Type_handler *th= args[0]->type_handler(); + bool is_longstr= dynamic_cast(th) != nullptr; + bool is_hybrid= dynamic_cast(th) != nullptr; + bool is_binary= args[0]->collation.collation == &my_charset_bin; + + if (is_longstr && is_binary && !is_hybrid) + { + set_func_handler(&ha_bin_to_bin); + return m_func_handler->fix_length_and_dec(this); + } + return fix_length_and_dec_op1_std(&ha_int_to_ull, &ha_dec_to_ull); } @@ -2289,10 +2515,85 @@ class Func_handler_bit_neg_decimal_to_ulonglong: }; +class Func_handler_bit_neg_bin_to_bin: public Item_handled_func::Handler_str +{ +public: + const Type_handler *return_type_handler(const Item_handled_func *item) const override + { + if (item->max_length > MAX_FIELD_VARCHARLENGTH) + return Type_handler::blob_type_handler(item->max_length); + if (item->max_length > 255) + return &type_handler_varchar; + return &type_handler_string; + } + + bool fix_length_and_dec(Item_handled_func *item) const override + { + item->max_length= item->arguments()[0]->max_length; + item->collation.set(&my_charset_bin, DERIVATION_COERCIBLE); + return false; + } + + String *val_str(Item_handled_func *item, String *to) const override + { + DBUG_ASSERT(item->fixed()); + StringBuffer<128> a_buf; + String *a= item->arguments()[0]->val_str(&a_buf); + if (a == nullptr) + { + item->null_value= true; + return nullptr; + } + + size_t len= a->length(); + if (len == 0) + { + to->length(0); + to->set_charset(&my_charset_bin); + item->null_value= false; + return to; + } + + if (to->realloc(len)) + { + item->null_value= true; + return nullptr; + } + + const uchar *a_ptr= (const uchar *) a->ptr(); + uchar *out_ptr= (uchar *) to->ptr(); + + for (size_t i= 0; i < len; ++i) + { + out_ptr[i]= ~a_ptr[i]; + } + + to->length(len); + to->set_charset(&my_charset_bin); + item->null_value= false; + return to; + } +}; + + bool Item_func_bit_neg::fix_length_and_dec(THD *thd) { + static Func_handler_bit_neg_bin_to_bin ha_bin_to_bin; static Func_handler_bit_neg_int_to_ulonglong ha_int_to_ull; static Func_handler_bit_neg_decimal_to_ulonglong ha_dec_to_ull; + + DBUG_ASSERT(arg_count == 1); + const Type_handler *th= args[0]->type_handler(); + bool is_longstr= dynamic_cast(th) != nullptr; + bool is_hybrid= dynamic_cast(th) != nullptr; + bool is_binary= args[0]->collation.collation == &my_charset_bin; + + if (is_longstr && is_binary && !is_hybrid) + { + set_func_handler(&ha_bin_to_bin); + return m_func_handler->fix_length_and_dec(this); + } + return fix_length_and_dec_op1_std(&ha_int_to_ull, &ha_dec_to_ull); } @@ -6608,10 +6909,113 @@ class Func_handler_bit_xor_dec_to_ulonglong: }; +class Func_handler_bit_xor_bin_to_bin: public Item_handled_func::Handler_str +{ +public: + const Type_handler *return_type_handler(const Item_handled_func *item) const override + { + if (item->max_length > MAX_FIELD_VARCHARLENGTH) + return Type_handler::blob_type_handler(item->max_length); + if (item->max_length > 255) + return &type_handler_varchar; + return &type_handler_string; + } + + bool fix_length_and_dec(Item_handled_func *item) const override + { + item->max_length= MY_MAX(item->arguments()[0]->max_length, + item->arguments()[1]->max_length); + item->collation.set(&my_charset_bin, DERIVATION_COERCIBLE); + return false; + } + + String *val_str(Item_handled_func *item, String *to) const override + { + DBUG_ASSERT(item->fixed()); + StringBuffer<128> a_buf; + String *a= item->arguments()[0]->val_str(&a_buf); + if (a == nullptr) + { + item->null_value= true; + return nullptr; + } + + StringBuffer<128> b_buf; + String *b= item->arguments()[1]->val_str(&b_buf); + if (b == nullptr) + { + item->null_value= true; + return nullptr; + } + + if (a->length() != b->length()) + { + THD *thd= current_thd; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_INVALID_BITWISE_OPERANDS_SIZE, + ER_THD(thd, ER_INVALID_BITWISE_OPERANDS_SIZE), + (int)a->length(), (int)b->length()); + item->null_value= true; + return nullptr; + } + + size_t len= a->length(); + if (len == 0) + { + to->length(0); + to->set_charset(&my_charset_bin); + item->null_value= false; + return to; + } + + if (to->realloc(len)) + { + item->null_value= true; + return nullptr; + } + + const uchar *a_ptr= (const uchar *) a->ptr(); + const uchar *b_ptr= (const uchar *) b->ptr(); + uchar *out_ptr= (uchar *) to->ptr(); + + for (size_t i= 0; i < len; ++i) + { + out_ptr[i]= a_ptr[i] ^ b_ptr[i]; + } + + to->length(len); + to->set_charset(&my_charset_bin); + item->null_value= false; + return to; + } +}; + + bool Item_func_bit_xor::fix_length_and_dec(THD *thd) { + static Func_handler_bit_xor_bin_to_bin ha_bin_to_bin; static const Func_handler_bit_xor_int_to_ulonglong ha_int_to_ull; static const Func_handler_bit_xor_dec_to_ulonglong ha_dec_to_ull; + + DBUG_ASSERT(arg_count == 2); + uint longstr_count= 0; + uint binary_count= 0; + uint hybrid_count= 0; + + for (uint i= 0; i < arg_count; i++) + { + const Type_handler *th= args[i]->type_handler(); + longstr_count+= dynamic_cast(th) != nullptr; + hybrid_count+= dynamic_cast(th) != nullptr; + binary_count+= args[i]->collation.collation == &my_charset_bin; + } + + if (longstr_count == 2 && binary_count == 2 && hybrid_count == 0) + { + set_func_handler(&ha_bin_to_bin); + return m_func_handler->fix_length_and_dec(this); + } + return fix_length_and_dec_op2_std(&ha_int_to_ull, &ha_dec_to_ull); } @@ -7508,3 +7912,279 @@ void pause_execution(THD *thd, double timeout) do_pause(thd, &timed_cond, &cond, timeout); } + + +class Func_handler_bit_and_bin_to_bin: public Item_handled_func::Handler_str +{ +public: + const Type_handler *return_type_handler(const Item_handled_func *item) const override + { + if (item->max_length > MAX_FIELD_VARCHARLENGTH) + return Type_handler::blob_type_handler(item->max_length); + if (item->max_length > 255) + return &type_handler_varchar; + return &type_handler_string; + } + + bool fix_length_and_dec(Item_handled_func *item) const override + { + item->max_length= MY_MAX(item->arguments()[0]->max_length, + item->arguments()[1]->max_length); + item->collation.set(&my_charset_bin, DERIVATION_COERCIBLE); + return false; + } + + String *val_str(Item_handled_func *item, String *to) const override + { + DBUG_ASSERT(item->fixed()); + StringBuffer<128> a_buf; + String *a= item->arguments()[0]->val_str(&a_buf); + if (a == nullptr) + { + item->null_value= true; + return nullptr; + } + + StringBuffer<128> b_buf; + String *b= item->arguments()[1]->val_str(&b_buf); + if (b == nullptr) + { + item->null_value= true; + return nullptr; + } + + if (a->length() != b->length()) + { + THD *thd= current_thd; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_INVALID_BITWISE_OPERANDS_SIZE, + ER_THD(thd, ER_INVALID_BITWISE_OPERANDS_SIZE), + (int)a->length(), (int)b->length()); + item->null_value= true; + return nullptr; + } + + size_t len= a->length(); + if (len == 0) + { + to->length(0); + to->set_charset(&my_charset_bin); + item->null_value= false; + return to; + } + + if (to->realloc(len)) + { + item->null_value= true; + return nullptr; + } + + const uchar *a_ptr= (const uchar *) a->ptr(); + const uchar *b_ptr= (const uchar *) b->ptr(); + uchar *out_ptr= (uchar *) to->ptr(); + + for (size_t i= 0; i < len; ++i) + { + out_ptr[i]= a_ptr[i] & b_ptr[i]; + } + + to->length(len); + to->set_charset(&my_charset_bin); + item->null_value= false; + return to; + } +}; + + +class Func_handler_bit_and_int_to_ulonglong: + public Item_handled_func::Handler_ulonglong +{ +public: + Longlong_null to_longlong_null(Item_handled_func *item) const override + { + DBUG_ASSERT(item->fixed()); + Longlong_null a= item->arguments()[0]->to_longlong_null(); + return a.is_null() ? a : a & item->arguments()[1]->to_longlong_null(); + } +}; + + +class Func_handler_bit_and_dec_to_ulonglong: + public Item_handled_func::Handler_ulonglong +{ +public: + Longlong_null to_longlong_null(Item_handled_func *item) const override + { + DBUG_ASSERT(item->fixed()); + VDec a(item->arguments()[0]); + return a.is_null() ? Longlong_null() : + a.to_xlonglong_null() & VDec(item->arguments()[1]).to_xlonglong_null(); + } +}; + + +bool Item_func_bit_and::fix_length_and_dec(THD *thd) +{ + static Func_handler_bit_and_bin_to_bin ha_bin_to_bin; + static Func_handler_bit_and_int_to_ulonglong ha_int_to_ull; + static Func_handler_bit_and_dec_to_ulonglong ha_dec_to_ull; + + DBUG_ASSERT(arg_count == 2); + uint longstr_count= 0; + uint binary_count= 0; + uint hybrid_count= 0; + + for (uint i= 0; i < arg_count; i++) + { + const Type_handler *th= args[i]->type_handler(); + longstr_count+= dynamic_cast(th) != nullptr; + hybrid_count+= dynamic_cast(th) != nullptr; + binary_count+= args[i]->collation.collation == &my_charset_bin; + } + + if (longstr_count == 2 && binary_count == 2 && hybrid_count == 0) + { + set_func_handler(&ha_bin_to_bin); + return m_func_handler->fix_length_and_dec(this); + } + + return fix_length_and_dec_op2_std(&ha_int_to_ull, &ha_dec_to_ull); +} + + +class Func_handler_bit_or_bin_to_bin: public Item_handled_func::Handler_str +{ +public: + const Type_handler *return_type_handler(const Item_handled_func *item) const override + { + if (item->max_length > MAX_FIELD_VARCHARLENGTH) + return Type_handler::blob_type_handler(item->max_length); + if (item->max_length > 255) + return &type_handler_varchar; + return &type_handler_string; + } + + bool fix_length_and_dec(Item_handled_func *item) const override + { + item->max_length= MY_MAX(item->arguments()[0]->max_length, + item->arguments()[1]->max_length); + item->collation.set(&my_charset_bin, DERIVATION_COERCIBLE); + return false; + } + + String *val_str(Item_handled_func *item, String *to) const override + { + DBUG_ASSERT(item->fixed()); + StringBuffer<128> a_buf; + String *a= item->arguments()[0]->val_str(&a_buf); + if (a == nullptr) + { + item->null_value= true; + return nullptr; + } + + StringBuffer<128> b_buf; + String *b= item->arguments()[1]->val_str(&b_buf); + if (b == nullptr) + { + item->null_value= true; + return nullptr; + } + + if (a->length() != b->length()) + { + THD *thd= current_thd; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_INVALID_BITWISE_OPERANDS_SIZE, + ER_THD(thd, ER_INVALID_BITWISE_OPERANDS_SIZE), + (int)a->length(), (int)b->length()); + item->null_value= true; + return nullptr; + } + + size_t len= a->length(); + if (len == 0) + { + to->length(0); + to->set_charset(&my_charset_bin); + item->null_value= false; + return to; + } + + if (to->realloc(len)) + { + item->null_value= true; + return nullptr; + } + + const uchar *a_ptr= (const uchar *) a->ptr(); + const uchar *b_ptr= (const uchar *) b->ptr(); + uchar *out_ptr= (uchar *) to->ptr(); + + for (size_t i= 0; i < len; ++i) + { + out_ptr[i]= a_ptr[i] | b_ptr[i]; + } + + to->length(len); + to->set_charset(&my_charset_bin); + item->null_value= false; + return to; + } +}; + + +class Func_handler_bit_or_int_to_ulonglong: + public Item_handled_func::Handler_ulonglong +{ +public: + Longlong_null to_longlong_null(Item_handled_func *item) const override + { + DBUG_ASSERT(item->fixed()); + Longlong_null a= item->arguments()[0]->to_longlong_null(); + return a.is_null() ? a : a | item->arguments()[1]->to_longlong_null(); + } +}; + + +class Func_handler_bit_or_dec_to_ulonglong: + public Item_handled_func::Handler_ulonglong +{ +public: + Longlong_null to_longlong_null(Item_handled_func *item) const override + { + DBUG_ASSERT(item->fixed()); + VDec a(item->arguments()[0]); + return a.is_null() ? Longlong_null() : + a.to_xlonglong_null() | VDec(item->arguments()[1]).to_xlonglong_null(); + } +}; + + +bool Item_func_bit_or::fix_length_and_dec(THD *thd) +{ + static Func_handler_bit_or_bin_to_bin ha_bin_to_bin; + static Func_handler_bit_or_int_to_ulonglong ha_int_to_ull; + static Func_handler_bit_or_dec_to_ulonglong ha_dec_to_ull; + + DBUG_ASSERT(arg_count == 2); + uint longstr_count= 0; + uint binary_count= 0; + uint hybrid_count= 0; + + for (uint i= 0; i < arg_count; i++) + { + const Type_handler *th= args[i]->type_handler(); + longstr_count+= dynamic_cast(th) != nullptr; + hybrid_count+= dynamic_cast(th) != nullptr; + binary_count+= args[i]->collation.collation == &my_charset_bin; + } + + if (longstr_count == 2 && binary_count == 2 && hybrid_count == 0) + { + set_func_handler(&ha_bin_to_bin); + return m_func_handler->fix_length_and_dec(this); + } + + return fix_length_and_dec_op2_std(&ha_int_to_ull, &ha_dec_to_ull); +} diff --git a/sql/item_func.h b/sql/item_func.h index c482cdca44c44..65b5ccebda37d 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -2915,6 +2915,13 @@ class Item_func_find_in_set :public Item_long_func { return get_item_copy(thd, this); } }; +class Func_handler_bit_and_bin_to_bin; +class Func_handler_bit_or_bin_to_bin; +class Func_handler_bit_xor_bin_to_bin; +class Func_handler_bit_neg_bin_to_bin; +class Func_handler_shift_left_bin_to_bin; +class Func_handler_shift_right_bin_to_bin; + /* Base class for all bit functions: '~', '|', '^', '&', '>>', '<<' */ class Item_func_bit_operator: public Item_handled_func diff --git a/sql/item_sum.cc b/sql/item_sum.cc index efc5b9450c3f6..793ed7464a9fc 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -2612,6 +2612,39 @@ bool Item_sum_max::add() /* bit_or and bit_and */ +bool Item_sum_bit::fix_length_and_dec(THD *thd) +{ + const Type_handler *th= args[0]->type_handler(); + bool is_longstr= dynamic_cast(th) != nullptr; + bool is_hybrid= dynamic_cast(th) != nullptr; + bool is_binary= args[0]->collation.collation == &my_charset_bin; + + m_binary_mode= is_longstr && is_binary && !is_hybrid; + if (m_binary_mode) + { + // Size guard + if (args[0]->max_length > 512) + { + my_error(ER_INVALID_BITWISE_AGGREGATE_OPERANDS_SIZE, MYF(0), + args[0]->max_length); + return true; + } + m_binary_length= args[0]->max_length; + collation.set(&my_charset_bin, DERIVATION_COERCIBLE); + max_length= m_binary_length; + base_flags&= ~item_base_t::MAYBE_NULL; + null_value= 0; + return false; + } + // Original integer path + if (args[0]->check_type_can_return_int(func_name_cstring())) + return true; + decimals= 0; max_length= 21; unsigned_flag= 1; + base_flags&= ~item_base_t::MAYBE_NULL; + null_value= 0; + return false; +} + longlong Item_sum_bit::val_int() { DBUG_ASSERT(fixed()); @@ -2619,26 +2652,180 @@ longlong Item_sum_bit::val_int() } +Item_sum_bit::Item_sum_bit(THD *thd, Item_sum_bit *item): + Item_sum_int(thd, item), reset_bits(item->reset_bits), bits(item->bits), + as_window_function(item->as_window_function), + num_values_added(item->num_values_added), + m_binary_bit_counters(nullptr), + m_binary_mode(item->m_binary_mode), + m_binary_length(item->m_binary_length), + direct_added(item->direct_added), + direct_reseted_field(item->direct_reseted_field), + direct_sum_is_null(item->direct_sum_is_null), + direct_bits(item->direct_bits) +{ + if (m_binary_mode) + { + if (item->m_binary_bit_counters) + { + m_binary_bit_counters= thd->alloc(m_binary_length * 8); + if (m_binary_bit_counters) + memcpy(m_binary_bit_counters, item->m_binary_bit_counters, + m_binary_length * 8 * sizeof(uint32)); + } + } + else if (as_window_function) + { + memcpy(bit_counters, item->bit_counters, sizeof(bit_counters)); + } + m_str_value.copy(item->m_str_value); + direct_str_value.copy(item->direct_str_value); +} + + +void Item_sum_bit::direct_add(ulonglong add_bits, bool is_null) +{ + DBUG_ASSERT(!as_window_function); + direct_bits= add_bits; + direct_sum_is_null= is_null; + direct_added= true; +} + +void Item_sum_bit::direct_add(const String *add_str, bool is_null) +{ + DBUG_ASSERT(!as_window_function); + if (!is_null && add_str) + direct_str_value.copy(*add_str); + direct_sum_is_null= is_null; + direct_added= true; +} + + + void Item_sum_bit::clear() { - bits= reset_bits; + if (m_binary_mode) + reset_binary_accumulator(); + else + bits= reset_bits; if (as_window_function) clear_as_window(); } +String *Item_sum_bit::val_str(String *str) +{ + if (m_binary_mode) + { + null_value= false; + return &m_str_value; + } + // fall back to integer conversion + longlong nr= val_int(); + if (null_value) return nullptr; + str->set((ulonglong) nr, &my_charset_bin); + return str; +} + Item *Item_sum_or::copy_or_same(THD* thd) { return new (thd->mem_root) Item_sum_or(thd, this); } +void Item_sum_bit::setup_window_func(THD *thd, Window_spec *window_spec) +{ + if (m_binary_mode) + { + m_binary_bit_counters= thd->calloc(m_binary_length * 8); + if (!m_binary_bit_counters) + return; + } + as_window_function= TRUE; + clear_as_window(); +} + bool Item_sum_bit::clear_as_window() { - memset(bit_counters, 0, sizeof(bit_counters)); + if (m_binary_mode) + { + if (m_binary_bit_counters) + memset(m_binary_bit_counters, 0, m_binary_length * 8 * sizeof(uint32)); + } + else + memset(bit_counters, 0, sizeof(bit_counters)); num_values_added= 0; set_bits_from_counters(); return 0; } +bool Item_sum_bit::add_binary_as_window(const String &value) +{ + DBUG_ASSERT(as_window_function); + if (!m_binary_bit_counters) + return true; + + if (value.length() != m_binary_length) + { + THD *thd= current_thd; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_INVALID_BITWISE_OPERANDS_SIZE, + ER_THD(thd, ER_INVALID_BITWISE_OPERANDS_SIZE), + (int)value.length(), (int)m_binary_length); + return true; + } + + const uchar *val_ptr= (const uchar*) value.ptr(); + for (uint b_idx= 0; b_idx < m_binary_length; ++b_idx) + { + uchar byte_val= val_ptr[b_idx]; + for (int bit= 0; bit < 8; ++bit) + { + if (byte_val & (1 << bit)) + m_binary_bit_counters[b_idx * 8 + bit]++; + } + } + num_values_added++; + set_bits_from_counters(); + return 0; +} + +bool Item_sum_bit::remove_binary_as_window(const String &value) +{ + DBUG_ASSERT(as_window_function); + if (!m_binary_bit_counters) + return true; + + if (value.length() != m_binary_length) + { + THD *thd= current_thd; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_INVALID_BITWISE_OPERANDS_SIZE, + ER_THD(thd, ER_INVALID_BITWISE_OPERANDS_SIZE), + (int)value.length(), (int)m_binary_length); + return true; + } + + if (num_values_added == 0) + return 0; + + const uchar *val_ptr= (const uchar*) value.ptr(); + for (uint b_idx= 0; b_idx < m_binary_length; ++b_idx) + { + uchar byte_val= val_ptr[b_idx]; + for (int bit= 0; bit < 8; ++bit) + { + if (byte_val & (1 << bit)) + { + uint32 &counter= m_binary_bit_counters[b_idx * 8 + bit]; + if (counter) + counter--; + } + } + } + num_values_added--; + set_bits_from_counters(); + return 0; +} + bool Item_sum_bit::remove_as_window(ulonglong value) { DBUG_ASSERT(as_window_function); @@ -2677,6 +2864,27 @@ bool Item_sum_bit::add_as_window(ulonglong value) void Item_sum_or::set_bits_from_counters() { + if (m_binary_mode) + { + if (!m_binary_bit_counters) + return; + if (m_str_value.alloc(m_binary_length)) + return; + m_str_value.length(m_binary_length); + + uchar *acc= (uchar*) m_str_value.ptr(); + for (uint b_idx= 0; b_idx < m_binary_length; ++b_idx) + { + uchar byte_val= 0; + for (int bit= 0; bit < 8; ++bit) + { + if (m_binary_bit_counters[b_idx * 8 + bit] > 0) + byte_val|= (1 << bit); + } + acc[b_idx]= byte_val; + } + return; + } ulonglong value= 0; for (uint i= 0; i < NUM_BIT_COUNTERS; i++) { @@ -2685,8 +2893,58 @@ void Item_sum_or::set_bits_from_counters() bits= value | reset_bits; } +void Item_sum_or::reset_binary_accumulator() +{ + m_str_value.alloc(m_binary_length); + m_str_value.length(m_binary_length); + m_str_value.set_charset(&my_charset_bin); + memset((char*) m_str_value.ptr(), 0x00, m_binary_length); +} + bool Item_sum_or::add() { + if (m_binary_mode) + { + if (unlikely(direct_added)) + { + direct_added= FALSE; + if (!direct_sum_is_null) + { + null_value= 0; + uchar *acc= (uchar*) m_str_value.ptr(); + const uchar *val= (const uchar*) direct_str_value.ptr(); + for (uint i= 0; i < m_binary_length; i++) + acc[i]|= val[i]; + } + return 0; + } + StringBuffer<512> arg_buf; + String *arg= args[0]->val_str(&arg_buf); + if (!arg) return 0; // NULL - skip + if (arg->length() != m_binary_length) + { + my_error(ER_INVALID_BITWISE_OPERANDS_SIZE, MYF(0), + (int)arg->length(), (int)m_binary_length); + return true; + } + if (as_window_function) + return add_binary_as_window(*arg); + uchar *acc= (uchar*) m_str_value.ptr(); + const uchar *val= (const uchar*) arg->ptr(); + for (uint i= 0; i < m_binary_length; i++) + acc[i]|= val[i]; + return 0; + } + if (unlikely(direct_added)) + { + direct_added= FALSE; + if (!direct_sum_is_null) + { + null_value= 0; + bits|= direct_bits; + } + return 0; + } ulonglong value= (ulonglong) args[0]->val_int(); if (!args[0]->null_value) { @@ -2699,6 +2957,27 @@ bool Item_sum_or::add() void Item_sum_xor::set_bits_from_counters() { + if (m_binary_mode) + { + if (!m_binary_bit_counters) + return; + if (m_str_value.alloc(m_binary_length)) + return; + m_str_value.length(m_binary_length); + + uchar *acc= (uchar*) m_str_value.ptr(); + for (uint b_idx= 0; b_idx < m_binary_length; ++b_idx) + { + uchar byte_val= 0; + for (int bit= 0; bit < 8; ++bit) + { + if (m_binary_bit_counters[b_idx * 8 + bit] % 2 != 0) + byte_val|= (1 << bit); + } + acc[b_idx]= byte_val; + } + return; + } ulonglong value= 0; for (int i= 0; i < NUM_BIT_COUNTERS; i++) { @@ -2713,8 +2992,58 @@ Item *Item_sum_xor::copy_or_same(THD* thd) } +void Item_sum_xor::reset_binary_accumulator() +{ + m_str_value.alloc(m_binary_length); + m_str_value.length(m_binary_length); + m_str_value.set_charset(&my_charset_bin); + memset((char*) m_str_value.ptr(), 0x00, m_binary_length); +} + bool Item_sum_xor::add() { + if (m_binary_mode) + { + if (unlikely(direct_added)) + { + direct_added= FALSE; + if (!direct_sum_is_null) + { + null_value= 0; + uchar *acc= (uchar*) m_str_value.ptr(); + const uchar *val= (const uchar*) direct_str_value.ptr(); + for (uint i= 0; i < m_binary_length; i++) + acc[i]^= val[i]; + } + return 0; + } + StringBuffer<512> arg_buf; + String *arg= args[0]->val_str(&arg_buf); + if (!arg) return 0; // NULL - skip + if (arg->length() != m_binary_length) + { + my_error(ER_INVALID_BITWISE_OPERANDS_SIZE, MYF(0), + (int)arg->length(), (int)m_binary_length); + return true; + } + if (as_window_function) + return add_binary_as_window(*arg); + uchar *acc= (uchar*) m_str_value.ptr(); + const uchar *val= (const uchar*) arg->ptr(); + for (uint i= 0; i < m_binary_length; i++) + acc[i]^= val[i]; + return 0; + } + if (unlikely(direct_added)) + { + direct_added= FALSE; + if (!direct_sum_is_null) + { + null_value= 0; + bits^= direct_bits; + } + return 0; + } ulonglong value= (ulonglong) args[0]->val_int(); if (!args[0]->null_value) { @@ -2727,6 +3056,32 @@ bool Item_sum_xor::add() void Item_sum_and::set_bits_from_counters() { + if (m_binary_mode) + { + if (!m_binary_bit_counters) + return; + if (m_str_value.alloc(m_binary_length)) + return; + m_str_value.length(m_binary_length); + + uchar *acc= (uchar*) m_str_value.ptr(); + if (num_values_added == 0) + { + memset(acc, 0xFF, m_binary_length); + return; + } + for (uint b_idx= 0; b_idx < m_binary_length; ++b_idx) + { + uchar byte_val= 0; + for (int bit= 0; bit < 8; ++bit) + { + if (m_binary_bit_counters[b_idx * 8 + bit] == num_values_added) + byte_val|= (1 << bit); + } + acc[b_idx]= byte_val; + } + return; + } ulonglong value= 0; if (!num_values_added) { @@ -2748,8 +3103,58 @@ Item *Item_sum_and::copy_or_same(THD* thd) } +void Item_sum_and::reset_binary_accumulator() +{ + m_str_value.alloc(m_binary_length); + m_str_value.length(m_binary_length); + m_str_value.set_charset(&my_charset_bin); + memset((char*) m_str_value.ptr(), 0xFF, m_binary_length); +} + bool Item_sum_and::add() { + if (m_binary_mode) + { + if (unlikely(direct_added)) + { + direct_added= FALSE; + if (!direct_sum_is_null) + { + null_value= 0; + uchar *acc= (uchar*) m_str_value.ptr(); + const uchar *val= (const uchar*) direct_str_value.ptr(); + for (uint i= 0; i < m_binary_length; i++) + acc[i]&= val[i]; + } + return 0; + } + StringBuffer<512> arg_buf; + String *arg= args[0]->val_str(&arg_buf); + if (!arg) return 0; // NULL - skip + if (arg->length() != m_binary_length) + { + my_error(ER_INVALID_BITWISE_OPERANDS_SIZE, MYF(0), + (int)arg->length(), (int)m_binary_length); + return true; + } + if (as_window_function) + return add_binary_as_window(*arg); + uchar *acc= (uchar*) m_str_value.ptr(); + const uchar *val= (const uchar*) arg->ptr(); + for (uint i= 0; i < m_binary_length; i++) + acc[i]&= val[i]; + return 0; + } + if (unlikely(direct_added)) + { + direct_added= FALSE; + if (!direct_sum_is_null) + { + null_value= 0; + bits&= direct_bits; + } + return 0; + } ulonglong value= (ulonglong) args[0]->val_int(); if (!args[0]->null_value) { @@ -2951,19 +3356,112 @@ void Item_sum_avg::reset_field() void Item_sum_bit::reset_field() { + if (m_binary_mode) + { + if (unlikely(direct_added)) + { + direct_added= FALSE; + direct_reseted_field= TRUE; + null_value= direct_sum_is_null; + if (null_value) + { + result_field->set_null(); + result_field->reset(); + } + else + { + result_field->set_notnull(); + result_field->store(direct_str_value.ptr(), direct_str_value.length(), &my_charset_bin); + } + return; + } + reset_and_add(); + if (null_value) + { + result_field->set_null(); + result_field->reset(); + } + else + { + result_field->set_notnull(); + result_field->store(m_str_value.ptr(), m_str_value.length(), &my_charset_bin); + } + return; + } + if (unlikely(direct_added)) + { + direct_added= FALSE; + direct_reseted_field= TRUE; + null_value= direct_sum_is_null; + bits= null_value ? reset_bits : direct_bits; + if (null_value) + result_field->set_null(); + else + result_field->set_notnull(); + int8store(result_field->ptr, bits); + return; + } reset_and_add(); int8store(result_field->ptr, bits); } void Item_sum_bit::update_field() { + if (m_binary_mode) + { + char buff[512]; // matches ER_INVALID_BITWISE_AGGREGATE_OPERANDS_SIZE + // guard (max 512 bytes) in fix_length_and_dec() + String tmp(buff, sizeof(buff), &my_charset_bin); + String *res= result_field->val_str(&tmp, &tmp); + if (!result_field->is_null()) + { + m_str_value.copy(*res); + if (unlikely(direct_added || direct_reseted_field)) + { + direct_added= TRUE; + direct_reseted_field= FALSE; + } + add(); + result_field->set_notnull(); + result_field->store(m_str_value.ptr(), m_str_value.length(), &my_charset_bin); + } + else + { + reset_binary_accumulator(); + if (unlikely(direct_added || direct_reseted_field)) + { + direct_added= TRUE; + direct_reseted_field= FALSE; + } + add(); + if (!null_value) + { + result_field->set_notnull(); + result_field->store(m_str_value.ptr(), m_str_value.length(), &my_charset_bin); + } + } + return; + } // We never call update_field when computing the function as a window // function. Setting bits to a random value invalidates the bits counters and // the result of the bit function becomes erroneous. DBUG_ASSERT(!as_window_function); uchar *res=result_field->ptr; - bits= uint8korr(res); + if (unlikely(direct_added || direct_reseted_field)) + { + direct_added= TRUE; + direct_reseted_field= FALSE; + bits= result_field->is_null() ? reset_bits : uint8korr(res); + } + else + { + bits= uint8korr(res); + } add(); + if (null_value) + result_field->set_null(); + else + result_field->set_notnull(); int8store(res, bits); } diff --git a/sql/item_sum.h b/sql/item_sum.h index 39ed79e7c0203..c29546006ad90 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -1260,51 +1260,50 @@ class Item_sum_bit :public Item_sum_int public: Item_sum_bit(THD *thd, Item *item_par, ulonglong reset_arg): Item_sum_int(thd, item_par), reset_bits(reset_arg), bits(reset_arg), - as_window_function(FALSE), num_values_added(0) {} - Item_sum_bit(THD *thd, Item_sum_bit *item): - Item_sum_int(thd, item), reset_bits(item->reset_bits), bits(item->bits), - as_window_function(item->as_window_function), - num_values_added(item->num_values_added) - { - if (as_window_function) - memcpy(bit_counters, item->bit_counters, sizeof(bit_counters)); - } + as_window_function(FALSE), num_values_added(0), + m_binary_bit_counters(nullptr), m_binary_mode(FALSE), + m_binary_length(0), direct_added(FALSE), direct_reseted_field(FALSE) {} + Item_sum_bit(THD *thd, Item_sum_bit *item); enum Sumfunctype sum_func () const override { return SUM_BIT_FUNC;} void clear() override; longlong val_int() override; void reset_field() override; void update_field() override; const Type_handler *type_handler() const override - { return &type_handler_ulonglong; } - bool fix_length_and_dec(THD *thd) override { - if (args[0]->check_type_can_return_int(func_name_cstring())) - return true; - decimals= 0; max_length=21; unsigned_flag= 1; - base_flags&= ~item_base_t::MAYBE_NULL; - null_value= 0; - return FALSE; + if (m_binary_mode) + return Type_handler::blob_type_handler(max_length); + return &type_handler_ulonglong; } // block standard processor for never null bool add_maybe_null_after_ora_join_processor(void *arg) override { return 0; } + bool fix_length_and_dec(THD *thd) override; + String *val_str(String *str) override; void cleanup() override { bits= reset_bits; + m_binary_bit_counters= nullptr; if (as_window_function) clear_as_window(); + direct_added= FALSE; + direct_reseted_field= FALSE; Item_sum_int::cleanup(); } - void setup_window_func(THD *, Window_spec *) override - { - as_window_function= TRUE; - clear_as_window(); - } + void setup_window_func(THD *thd, Window_spec *window_spec) override; void remove() override { if (as_window_function) { - remove_as_window(args[0]->val_int()); + if (m_binary_mode) + { + StringBuffer<512> arg_buf; + String *arg= args[0]->val_str(&arg_buf); + if (arg) + remove_binary_as_window(*arg); + } + else + remove_as_window(args[0]->val_int()); return; } // Unless we're counting bits, we can not remove anything. @@ -1315,6 +1314,8 @@ class Item_sum_bit :public Item_sum_int { return true; } + void direct_add(ulonglong add_bits, bool is_null); + void direct_add(const String *add_str, bool is_null); protected: enum bit_counters { NUM_BIT_COUNTERS= 64 }; @@ -1327,10 +1328,23 @@ class Item_sum_bit :public Item_sum_int // this additional information. ulonglong num_values_added; ulonglong bit_counters[NUM_BIT_COUNTERS]; + uint32 *m_binary_bit_counters; bool add_as_window(ulonglong value); bool remove_as_window(ulonglong value); bool clear_as_window(); + bool add_binary_as_window(const String &value); + bool remove_binary_as_window(const String &value); + bool clear_binary_as_window(); virtual void set_bits_from_counters()= 0; + bool m_binary_mode; + String m_str_value; // binary accumulator + uint m_binary_length; // byte length of binary operand + virtual void reset_binary_accumulator() {} + bool direct_added; + bool direct_reseted_field; + bool direct_sum_is_null; + ulonglong direct_bits; + String direct_str_value; }; @@ -1349,6 +1363,7 @@ class Item_sum_or final :public Item_sum_bit private: void set_bits_from_counters() override; + void reset_binary_accumulator() override; protected: Item *shallow_copy(THD *thd) const override @@ -1372,6 +1387,7 @@ class Item_sum_and final :public Item_sum_bit private: void set_bits_from_counters() override; + void reset_binary_accumulator() override; protected: Item *shallow_copy(THD *thd) const override @@ -1393,6 +1409,7 @@ class Item_sum_xor final :public Item_sum_bit private: void set_bits_from_counters() override; + void reset_binary_accumulator() override; protected: Item *shallow_copy(THD *thd) const override diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index ef4fd75fc205b..60637e7370983 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -12406,3 +12406,7 @@ ER_WARN_QB_NAME_PATH_VIEW_NOT_FOUND eng "Hint %s is ignored. `%s` required at element #%u of the path is not found in the target query block." ER_WARN_QB_NAME_PATH_NOT_SUPPORTED_INSIDE_VIEW eng "Hint %s is ignored. QB_NAME hints with path are not supported inside view definitions." +ER_INVALID_BITWISE_OPERANDS_SIZE + eng "Mismatched length of operands for bitwise operator: %d and %d" +ER_INVALID_BITWISE_AGGREGATE_OPERANDS_SIZE + eng "Operand size %d exceeds maximum limit of 512 bytes for bitwise aggregate function" diff --git a/storage/spider/mysql-test/spider/r/direct_aggregate_bit.result b/storage/spider/mysql-test/spider/r/direct_aggregate_bit.result new file mode 100644 index 0000000000000..69b889033d6b4 --- /dev/null +++ b/storage/spider/mysql-test/spider/r/direct_aggregate_bit.result @@ -0,0 +1,129 @@ +for master_1 +for child2 +child2_1 +child2_2 +child2_3 +for child3 +child3_1 +child3_2 +child3_3 + +drop and create databases +connection master_1; +DROP DATABASE IF EXISTS auto_test_local; +CREATE DATABASE auto_test_local; +USE auto_test_local; +connection child2_1; +DROP DATABASE IF EXISTS auto_test_remote; +CREATE DATABASE auto_test_remote; +USE auto_test_remote; +connection child2_2; +DROP DATABASE IF EXISTS auto_test_remote2; +CREATE DATABASE auto_test_remote2; +USE auto_test_remote2; + +create child tables + +create spider table on master +connection master_1; +CREATE TABLE ta_l2 ( +id INT, +val_int BIGINT UNSIGNED, +val_bin VARBINARY(4), +PRIMARY KEY(id) +) MASTER_1_ENGINE MASTER_1_COMMENT2_P_2_1 +Warnings: +Warning 138 Spider table params in COMMENT or CONNECTION strings have been deprecated and will be removed in a future release. Please use table options instead. +Warning 138 Spider table params in COMMENT or CONNECTION strings have been deprecated and will be removed in a future release. Please use table options instead. +Warning 138 Spider table params in COMMENT or CONNECTION strings have been deprecated and will be removed in a future release. Please use table options instead. +Warning 138 Spider table params in COMMENT or CONNECTION strings have been deprecated and will be removed in a future release. Please use table options instead. +set @old_spider_direct_aggregate=@@session.spider_direct_aggregate; +set spider_direct_aggregate=1; + +Direct aggregate execution verification +SHOW STATUS LIKE 'Spider_direct_aggregate'; +Variable_name Value +Spider_direct_aggregate 0 +SELECT HEX(BIT_OR(val_bin)), HEX(BIT_AND(val_bin)), HEX(BIT_XOR(val_bin)) FROM ta_l2; +HEX(BIT_OR(val_bin)) HEX(BIT_AND(val_bin)) HEX(BIT_XOR(val_bin)) +FFFFFFFF 00000000 FFF0F0F0 +SHOW STATUS LIKE 'Spider_direct_aggregate'; +Variable_name Value +Spider_direct_aggregate 6 +SELECT BIT_OR(val_int), BIT_AND(val_int), BIT_XOR(val_int) FROM ta_l2; +BIT_OR(val_int) BIT_AND(val_int) BIT_XOR(val_int) +18446744069414584575 0 18446744069414584575 +SHOW STATUS LIKE 'Spider_direct_aggregate'; +Variable_name Value +Spider_direct_aggregate 12 + +Disable direct aggregate and verify correctness +SET spider_direct_aggregate=0; +SELECT HEX(BIT_OR(val_bin)), HEX(BIT_AND(val_bin)), HEX(BIT_XOR(val_bin)) FROM ta_l2; +HEX(BIT_OR(val_bin)) HEX(BIT_AND(val_bin)) HEX(BIT_XOR(val_bin)) +FFFFFFFF 00000000 FFF0F0F0 +SELECT BIT_OR(val_int), BIT_AND(val_int), BIT_XOR(val_int) FROM ta_l2; +BIT_OR(val_int) BIT_AND(val_int) BIT_XOR(val_int) +18446744069414584575 0 18446744069414584575 +SHOW STATUS LIKE 'Spider_direct_aggregate'; +Variable_name Value +Spider_direct_aggregate 12 +set spider_direct_aggregate=@old_spider_direct_aggregate; + +Verify pushed-down aggregate SQL sent to remote child nodes +connection child2_2; +SELECT argument FROM mysql.general_log +WHERE command_type != 'Execute' + AND (argument LIKE '%bit_or%' OR argument LIKE '%bit_and%' OR argument LIKE '%bit_xor%') +ORDER BY event_time; +argument +select bit_xor(`val_bin`),bit_and(`val_bin`),bit_or(`val_bin`),min(`val_bin`) from `auto_test_remote2`.`ta_r3` +select bit_or(`val_int`),bit_and(`val_int`),bit_xor(`val_int`),min(`val_int`) from `auto_test_remote2`.`ta_r3` +SELECT argument FROM mysql.general_log +WHERE command_type != 'Execute' + AND (argument LIKE '%bit_or%' OR argument LIKE '%bit_and%' OR argument LIKE '%bit_xor%') +ORDER BY event_time +SELECT id, val_int, HEX(val_bin) FROM ta_r3 ORDER BY id; +id val_int HEX(val_bin) +4 280375465082880 0000FF00 +5 1095216660735 000000FF +connection child2_1; +SELECT argument FROM mysql.general_log +WHERE command_type != 'Execute' + AND (argument LIKE '%bit_or%' OR argument LIKE '%bit_and%' OR argument LIKE '%bit_xor%') +ORDER BY event_time; +argument +select bit_xor(`val_bin`),bit_and(`val_bin`),bit_or(`val_bin`),min(`val_bin`) from `auto_test_remote`.`ta_r2` +select bit_or(`val_int`),bit_and(`val_int`),bit_xor(`val_int`),min(`val_int`) from `auto_test_remote`.`ta_r2` +SELECT argument FROM mysql.general_log +WHERE command_type != 'Execute' + AND (argument LIKE '%bit_or%' OR argument LIKE '%bit_and%' OR argument LIKE '%bit_xor%') +ORDER BY event_time +SELECT id, val_int, HEX(val_bin) FROM ta_r2 ORDER BY id; +id val_int HEX(val_bin) +1 17293822569102704640 F0000000 +2 1080863910568919040 0F0F0F0F +3 71776119061217280 00FF0000 + +deinit +connection master_1; +DROP DATABASE IF EXISTS auto_test_local; +connection child2_1; +SET @@global.general_log = @general_log_backup; +SET GLOBAL log_output = @old_log_output; +DROP DATABASE IF EXISTS auto_test_remote; +connection child2_2; +SET @@global.general_log = @general_log_backup; +SET GLOBAL log_output = @old_log_output; +DROP DATABASE IF EXISTS auto_test_remote2; +for master_1 +for child2 +child2_1 +child2_2 +child2_3 +for child3 +child3_1 +child3_2 +child3_3 + +end of test diff --git a/storage/spider/mysql-test/spider/t/direct_aggregate_bit.test b/storage/spider/mysql-test/spider/t/direct_aggregate_bit.test new file mode 100644 index 0000000000000..58032311368dd --- /dev/null +++ b/storage/spider/mysql-test/spider/t/direct_aggregate_bit.test @@ -0,0 +1,150 @@ +--disable_warnings +--disable_query_log +--disable_result_log +--source test_init.inc +--enable_result_log +--enable_query_log + + +--echo +--echo drop and create databases +--connection master_1 +DROP DATABASE IF EXISTS auto_test_local; +CREATE DATABASE auto_test_local; +USE auto_test_local; + --connection child2_1 + DROP DATABASE IF EXISTS auto_test_remote; + CREATE DATABASE auto_test_remote; + USE auto_test_remote; + --connection child2_2 + DROP DATABASE IF EXISTS auto_test_remote2; + CREATE DATABASE auto_test_remote2; + USE auto_test_remote2; +--enable_warnings + +--echo +--echo create child tables + --disable_query_log + --disable_result_log + --connection child2_2 + --disable_warnings + DROP TABLE IF EXISTS ta_r3; + --enable_warnings + CREATE TABLE ta_r3 ( + id INT, + val_int BIGINT UNSIGNED, + val_bin VARBINARY(4), + PRIMARY KEY(id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + SET @old_log_output = @@global.log_output; + SET GLOBAL log_output = 'TABLE,FILE'; + SET @general_log_backup = @@global.general_log; + SET @@global.general_log = 1; + TRUNCATE TABLE mysql.general_log; + + --connection child2_1 + --disable_warnings + DROP TABLE IF EXISTS ta_r2; + --enable_warnings + CREATE TABLE ta_r2 ( + id INT, + val_int BIGINT UNSIGNED, + val_bin VARBINARY(4), + PRIMARY KEY(id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + SET @old_log_output = @@global.log_output; + SET GLOBAL log_output = 'TABLE,FILE'; + SET @general_log_backup = @@global.general_log; + SET @@global.general_log = 1; + TRUNCATE TABLE mysql.general_log; + --enable_query_log + --enable_result_log + +--echo +--echo create spider table on master + --connection master_1 + --disable_query_log + echo CREATE TABLE ta_l2 ( + id INT, + val_int BIGINT UNSIGNED, + val_bin VARBINARY(4), + PRIMARY KEY(id) + ) MASTER_1_ENGINE MASTER_1_COMMENT2_P_2_1; + CREATE TABLE ta_l2 ( + id INT, + val_int BIGINT UNSIGNED, + val_bin VARBINARY(4), + PRIMARY KEY(id) + ) ENGINE=Spider COMMENT='table "ta_r3"' + PARTITION BY RANGE(id) ( + PARTITION pt1 VALUES LESS THAN (4) COMMENT='srv "s_2_1", table "ta_r2", priority "1000"', + PARTITION pt2 VALUES LESS THAN MAXVALUE COMMENT='srv "s_2_2", priority "1000001"' + ); + --disable_ps_protocol + INSERT INTO ta_l2 (id, val_int, val_bin) VALUES + (1, 0xF000000000000000, x'F0000000'), + (2, 0x0F00000000000000, x'0F0F0F0F'), + (3, 0x00FF000000000000, x'00FF0000'), + (4, 0x0000FF0000000000, x'0000FF00'), + (5, 0x000000FF000000FF, x'000000FF'); + --enable_ps_protocol + --enable_query_log + + --disable_ps2_protocol + set @old_spider_direct_aggregate=@@session.spider_direct_aggregate; + set spider_direct_aggregate=1; + +--echo +--echo Direct aggregate execution verification +SHOW STATUS LIKE 'Spider_direct_aggregate'; +SELECT HEX(BIT_OR(val_bin)), HEX(BIT_AND(val_bin)), HEX(BIT_XOR(val_bin)) FROM ta_l2; +SHOW STATUS LIKE 'Spider_direct_aggregate'; +SELECT BIT_OR(val_int), BIT_AND(val_int), BIT_XOR(val_int) FROM ta_l2; +SHOW STATUS LIKE 'Spider_direct_aggregate'; + +--echo +--echo Disable direct aggregate and verify correctness +SET spider_direct_aggregate=0; +SELECT HEX(BIT_OR(val_bin)), HEX(BIT_AND(val_bin)), HEX(BIT_XOR(val_bin)) FROM ta_l2; +SELECT BIT_OR(val_int), BIT_AND(val_int), BIT_XOR(val_int) FROM ta_l2; +SHOW STATUS LIKE 'Spider_direct_aggregate'; + +set spider_direct_aggregate=@old_spider_direct_aggregate; +--enable_ps2_protocol + +--echo +--echo Verify pushed-down aggregate SQL sent to remote child nodes + --connection child2_2 + SELECT argument FROM mysql.general_log + WHERE command_type != 'Execute' + AND (argument LIKE '%bit_or%' OR argument LIKE '%bit_and%' OR argument LIKE '%bit_xor%') + ORDER BY event_time; + SELECT id, val_int, HEX(val_bin) FROM ta_r3 ORDER BY id; + --connection child2_1 + SELECT argument FROM mysql.general_log + WHERE command_type != 'Execute' + AND (argument LIKE '%bit_or%' OR argument LIKE '%bit_and%' OR argument LIKE '%bit_xor%') + ORDER BY event_time; + SELECT id, val_int, HEX(val_bin) FROM ta_r2 ORDER BY id; + +--echo +--echo deinit +--disable_warnings +--connection master_1 +DROP DATABASE IF EXISTS auto_test_local; + --connection child2_1 + SET @@global.general_log = @general_log_backup; + SET GLOBAL log_output = @old_log_output; + DROP DATABASE IF EXISTS auto_test_remote; + --connection child2_2 + SET @@global.general_log = @general_log_backup; + SET GLOBAL log_output = @old_log_output; + DROP DATABASE IF EXISTS auto_test_remote2; +--disable_query_log +--disable_result_log +--source test_deinit.inc +--enable_result_log +--enable_query_log +--enable_warnings +--echo +--echo end of test diff --git a/storage/spider/spd_db_conn.cc b/storage/spider/spd_db_conn.cc index ea3d2978cf0ca..5c5ccb26c19ed 100644 --- a/storage/spider/spd_db_conn.cc +++ b/storage/spider/spd_db_conn.cc @@ -2191,13 +2191,63 @@ int spider_db_fetch_for_item_sum_func( row->next(); } break; + case Item_sum::SUM_BIT_FUNC: + { + Item_sum_bit *item_sum_bit = (Item_sum_bit *) item_sum; + if (item_sum_bit->result_type() == STRING_RESULT) + { + if (row->is_null()) + { + item_sum_bit->direct_add((const String *) NULL, TRUE); + } + else + { + char buf[MAX_FIELD_WIDTH]; + spider_string tmp_str(buf, MAX_FIELD_WIDTH, share->access_charset); + tmp_str.init_calc_mem(SPD_MID_DB_FETCH_FOR_ITEM_SUM_FUNC_3); + tmp_str.length(0); + if ((error_num = row->append_to_str(&tmp_str))) + DBUG_RETURN(error_num); + item_sum_bit->direct_add(tmp_str.get_str(), FALSE); + } + } + else + { + /* + * row->val_int() uses atoi() which is a 32-bit signed parser and + * overflows silently for BIGINT UNSIGNED values > INT_MAX. + * BIT_AND/OR/XOR operate on ulonglong (up to ULONGLONG_MAX), so + * we must parse the raw string with my_strtoll10() which correctly + * handles the full 64-bit unsigned range. + */ + if (row->is_null()) + { + item_sum_bit->direct_add((ulonglong) 0, TRUE); + } + else + { + char buf[MAX_FIELD_WIDTH]; + spider_string tmp_str(buf, MAX_FIELD_WIDTH, share->access_charset); + tmp_str.init_calc_mem(SPD_MID_DB_FETCH_FOR_ITEM_SUM_FUNC_3); + tmp_str.length(0); + if ((error_num = row->append_to_str(&tmp_str))) + DBUG_RETURN(error_num); + int conv_error= 0; + ulonglong bits= (ulonglong) my_strtoll10(tmp_str.ptr(), + (char **) NULL, + &conv_error); + item_sum_bit->direct_add(bits, FALSE); + } + } + row->next(); + } + break; case Item_sum::COUNT_DISTINCT_FUNC: case Item_sum::SUM_DISTINCT_FUNC: case Item_sum::AVG_FUNC: case Item_sum::AVG_DISTINCT_FUNC: case Item_sum::STD_FUNC: case Item_sum::VARIANCE_FUNC: - case Item_sum::SUM_BIT_FUNC: case Item_sum::UDF_SUM_FUNC: case Item_sum::GROUP_CONCAT_FUNC: default: diff --git a/storage/spider/spd_db_mysql.cc b/storage/spider/spd_db_mysql.cc index c485b3f6b81fe..fbb90881e410e 100644 --- a/storage/spider/spd_db_mysql.cc +++ b/storage/spider/spd_db_mysql.cc @@ -6108,6 +6108,7 @@ int spider_db_mbase_util::open_item_sum_func( case Item_sum::SUM_FUNC: case Item_sum::MIN_FUNC: case Item_sum::MAX_FUNC: + case Item_sum::SUM_BIT_FUNC: { LEX_CSTRING org_func_name= item_sum->func_name_cstring(); const char *func_name = org_func_name.str; @@ -6198,7 +6199,6 @@ int spider_db_mbase_util::open_item_sum_func( break; case Item_sum::STD_FUNC: case Item_sum::VARIANCE_FUNC: - case Item_sum::SUM_BIT_FUNC: case Item_sum::UDF_SUM_FUNC: case Item_sum::GROUP_CONCAT_FUNC: default: