diff --git a/mysql-test/suite/perfschema/r/unary_digest.result b/mysql-test/suite/perfschema/r/unary_digest.result new file mode 100644 index 00000000000..40fcc026fad --- /dev/null +++ b/mysql-test/suite/perfschema/r/unary_digest.result @@ -0,0 +1,47 @@ +TRUNCATE TABLE performance_schema.events_statements_summary_by_digest; +ERROR 42S02: Table 'test.expect_unary' doesn't exist +ERROR 42S02: Table 'test.expect_unary' doesn't exist +ERROR 42S02: Table 'test.expect_unary' doesn't exist +ERROR 42S02: Table 'test.expect_unary' doesn't exist +ERROR 42S02: Table 'test.expect_unary' doesn't exist +ERROR 42S02: Table 'test.expect_binary' doesn't exist +ERROR 42S02: Table 'test.expect_binary' doesn't exist +ERROR 42S02: Table 'test.expect_binary' doesn't exist +ERROR 42S02: Table 'test.expect_binary' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_full_reduce' doesn't exist +ERROR 42S02: Table 'test.expect_unchanged' doesn't exist +SELECT SCHEMA_NAME, DIGEST_TEXT, COUNT_STAR +FROM performance_schema.events_statements_summary_by_digest; +SCHEMA_NAME DIGEST_TEXT COUNT_STAR +test TRUNCATE TABLE performance_schema . events_statements_summary_by_digest 1 +test SELECT ? FROM expect_unary 5 +test SELECT ? + ? FROM expect_binary 2 +test SELECT ? - ? FROM expect_binary 2 +test INSERT INTO expect_full_reduce VALUES (...) 27 +test SELECT a - b , a + b , - a , - b , + a , + b FROM expect_unchanged 1 diff --git a/mysql-test/suite/perfschema/t/unary_digest.test b/mysql-test/suite/perfschema/t/unary_digest.test new file mode 100644 index 00000000000..c4583484f36 --- /dev/null +++ b/mysql-test/suite/perfschema/t/unary_digest.test @@ -0,0 +1,98 @@ +# ---------------------------------------------------- +# Tests for the performance schema statement Digests. +# ---------------------------------------------------- + +# Test case to show behavior of statements digest when +# statement-digest-size is 0 + +--source include/not_embedded.inc +--source include/have_perfschema.inc +--source ../include/no_protocol.inc + +TRUNCATE TABLE performance_schema.events_statements_summary_by_digest; + +--disable_query_log + +--error ER_NO_SUCH_TABLE +select 1 from expect_unary; +--error ER_NO_SUCH_TABLE +select +1 from expect_unary; +--error ER_NO_SUCH_TABLE +select -1 from expect_unary; +--error ER_NO_SUCH_TABLE +select ++++++++++++++++++++++++++++++++++++++++++++++++1 from expect_unary; +--error ER_NO_SUCH_TABLE +select ------------------------------------------------1 from expect_unary; + +--error ER_NO_SUCH_TABLE +select 0+1 from expect_binary; +--error ER_NO_SUCH_TABLE +select 0-1 from expect_binary; +--error ER_NO_SUCH_TABLE +select 0 ++++++++++++++++++++++++++++++++++++++++++++++++1 from expect_binary; +--error ER_NO_SUCH_TABLE +select 0 ------------------------------------------------1 from expect_binary; + +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (0, 0, 0); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (0, 0, -1); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (0, 0, +1); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (0, -1, 0); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (0, -1, -1); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (0, -1, +1); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (0, +1, 0); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (0, +1, -1); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (0, +1, +1); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (-1, 0, 0); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (-1, 0, -1); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (-1, 0, +1); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (-1, -1, 0); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (-1, -1, -1); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (-1, -1, +1); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (-1, +1, 0); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (-1, +1, -1); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (-1, +1, +1); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (+1, 0, 0); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (+1, 0, -1); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (+1, 0, +1); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (+1, -1, 0); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (+1, -1, -1); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (+1, -1, +1); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (+1, +1, 0); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (+1, +1, -1); +--error ER_NO_SUCH_TABLE +insert into expect_full_reduce values (+1, +1, +1); + +--error ER_NO_SUCH_TABLE +select a-b, a+b, -a, -b, +a, +b from expect_unchanged; + +--enable_query_log + +SELECT SCHEMA_NAME, DIGEST_TEXT, COUNT_STAR + FROM performance_schema.events_statements_summary_by_digest; + diff --git a/storage/perfschema/gen_pfs_lex_token.cc b/storage/perfschema/gen_pfs_lex_token.cc index 7581255b284..c67f2920b75 100644 --- a/storage/perfschema/gen_pfs_lex_token.cc +++ b/storage/perfschema/gen_pfs_lex_token.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -36,10 +36,13 @@ See also YYMAXUTOK. */ #define MY_MAX_TOKEN 1000 +/** Generated token. */ struct gen_lex_token_string { const char *m_token_string; int m_token_length; + bool m_append_space; + bool m_start_expr; }; gen_lex_token_string compiled_token_array[MY_MAX_TOKEN]; @@ -76,6 +79,13 @@ void set_token(int tok, const char *str) compiled_token_array[tok].m_token_string= str; compiled_token_array[tok].m_token_length= strlen(str); + compiled_token_array[tok].m_append_space= true; + compiled_token_array[tok].m_start_expr= false; +} + +void set_start_expr_token(int tok) +{ + compiled_token_array[tok].m_start_expr= true; } void compute_tokens() @@ -91,6 +101,8 @@ void compute_tokens() { compiled_token_array[tok].m_token_string= "(unknown)"; compiled_token_array[tok].m_token_length= 9; + compiled_token_array[tok].m_append_space= true; + compiled_token_array[tok].m_start_expr= false; } /* @@ -102,6 +114,7 @@ void compute_tokens() str[0]= (char) tok; compiled_token_array[tok].m_token_string= str; compiled_token_array[tok].m_token_length= 1; + compiled_token_array[tok].m_append_space= true; } max_token_seen= 255; @@ -202,6 +215,71 @@ void compute_tokens() max_token_seen++; tok_pfs_unused= max_token_seen; set_token(tok_pfs_unused, "UNUSED"); + + /* + Fix whitespace for some special tokens. + */ + + /* + The lexer parses "@@variable" as '@', '@', 'variable', + returning a token for '@' alone. + + This is incorrect, '@' is not really a token, + because the syntax "@ @ variable" (with spaces) is not accepted: + The lexer keeps some internal state after the '@' fake token. + + To work around this, digest text are printed as "@@variable". + */ + compiled_token_array[(int) '@'].m_append_space= false; + + /* + Define additional properties for tokens. + + List all the token that are followed by an expression. + This is needed to differentiate unary from binary + '+' and '-' operators, because we want to: + - reduce to , + - preserve <...> as is. + */ + set_start_expr_token('('); + set_start_expr_token(','); + set_start_expr_token(EVERY_SYM); + set_start_expr_token(AT_SYM); + set_start_expr_token(STARTS_SYM); + set_start_expr_token(ENDS_SYM); + set_start_expr_token(DEFAULT); + set_start_expr_token(RETURN_SYM); + set_start_expr_token(IF); + set_start_expr_token(ELSEIF_SYM); + set_start_expr_token(CASE_SYM); + set_start_expr_token(WHEN_SYM); + set_start_expr_token(WHILE_SYM); + set_start_expr_token(UNTIL_SYM); + set_start_expr_token(SELECT_SYM); + + set_start_expr_token(OR_SYM); + set_start_expr_token(OR2_SYM); + set_start_expr_token(XOR); + set_start_expr_token(AND_SYM); + set_start_expr_token(AND_AND_SYM); + set_start_expr_token(NOT_SYM); + set_start_expr_token(BETWEEN_SYM); + set_start_expr_token(LIKE); + set_start_expr_token(REGEXP); + + set_start_expr_token('|'); + set_start_expr_token('&'); + set_start_expr_token(SHIFT_LEFT); + set_start_expr_token(SHIFT_RIGHT); + set_start_expr_token('+'); + set_start_expr_token('-'); + set_start_expr_token(INTERVAL_SYM); + set_start_expr_token('*'); + set_start_expr_token('/'); + set_start_expr_token('%'); + set_start_expr_token(DIV_SYM); + set_start_expr_token(MOD_SYM); + set_start_expr_token('^'); } void print_tokens() @@ -214,20 +292,26 @@ void print_tokens() for (tok= 0; tok<256; tok++) { - printf("/* %03d */ { \"\\x%02x\", 1},\n", tok, tok); + printf("/* %03d */ { \"\\x%02x\", 1, %s, %s},\n", + tok, + tok, + compiled_token_array[tok].m_append_space ? "true" : "false", + compiled_token_array[tok].m_start_expr ? "true" : "false"); } printf("/* PART 2: named tokens. */\n"); for (tok= 256; tok<= max_token_seen; tok++) { - printf("/* %03d */ { \"%s\", %d},\n", + printf("/* %03d */ { \"%s\", %d, %s, %s},\n", tok, compiled_token_array[tok].m_token_string, - compiled_token_array[tok].m_token_length); + compiled_token_array[tok].m_token_length, + compiled_token_array[tok].m_append_space ? "true" : "false", + compiled_token_array[tok].m_start_expr ? "true" : "false"); } - printf("/* DUMMY */ { \"\", 0}\n"); + printf("/* DUMMY */ { \"\", 0, false, false}\n"); printf("};\n"); printf("/* PFS specific tokens. */\n"); @@ -254,6 +338,8 @@ int main(int argc,char **argv) printf("{\n"); printf(" const char *m_token_string;\n"); printf(" int m_token_length;\n"); + printf(" bool m_append_space;\n"); + printf(" bool m_start_expr;\n"); printf("};\n"); printf("typedef struct lex_token_string lex_token_string;\n"); diff --git a/storage/perfschema/pfs_digest.cc b/storage/perfschema/pfs_digest.cc index addfac1f034..8fbd5741565 100644 --- a/storage/perfschema/pfs_digest.cc +++ b/storage/perfschema/pfs_digest.cc @@ -602,16 +602,67 @@ PSI_digest_locker* pfs_digest_add_token_v1(PSI_digest_locker *locker, switch (token) { - case BIN_NUM: + case NUM: + case LONG_NUM: + case ULONGLONG_NUM: case DECIMAL_NUM: case FLOAT_NUM: + case BIN_NUM: case HEX_NUM: + { + bool found_unary; + do + { + found_unary= false; + peek_last_two_tokens(digest_storage, state->m_last_id_index, + &last_token, &last_token2); + + if ((last_token == '-') || (last_token == '+')) + { + /* + We need to differentiate: + - a operator + - a operator + from + - a operator + - a operator + to only reduce "a = -1" to "a = ?", and not change "b - 1" to "b ?" + + Binary operators are found inside an expression, + while unary operators are found at the beginning of an expression, or after operators. + + To achieve this, every token that is followed by an expression + in the SQL grammar is flagged. + See sql/sql_yacc.yy + See sql/gen_lex_token.cc + + For example, + "(-1)" is parsed as "(", "-", NUM, ")", and lex_token_array["("].m_start_expr is true, + so reduction of the "-" NUM is done, the result is "(?)". + "(a-1)" is parsed as "(", ID, "-", NUM, ")", and lex_token_array[ID].m_start_expr is false, + so the operator is binary, no reduction is done, and the result is "(a-?)". + */ + if (lex_token_array[last_token2].m_start_expr) + { + /* + REDUCE: + TOK_PFS_GENERIC_VALUE := (UNARY_PLUS | UNARY_MINUS) (NUM | LOG_NUM | ... | FLOAT_NUM) + + REDUCE: + TOK_PFS_GENERIC_VALUE := (UNARY_PLUS | UNARY_MINUS) TOK_PFS_GENERIC_VALUE + */ + token= TOK_PFS_GENERIC_VALUE; + digest_storage->m_byte_count-= PFS_SIZE_OF_A_TOKEN; + found_unary= true; + } + } + } while (found_unary); + } + /* fall through, for case NULL_SYM below */ case LEX_HOSTNAME: - case LONG_NUM: - case NUM: case TEXT_STRING: case NCHAR_STRING: - case ULONGLONG_NUM: + case PARAM_MARKER: { /* REDUCE: