| 8 | { |
| 9 | |
| 10 | UInt64 normalizedQueryHash(const char * begin, const char * end, bool keep_names) |
| 11 | { |
| 12 | SipHash hash; |
| 13 | Lexer lexer(begin, end); |
| 14 | |
| 15 | /// Coalesce a list of comma separated literals. |
| 16 | size_t num_literals_in_sequence = 0; |
| 17 | bool prev_comma = false; |
| 18 | |
| 19 | while (true) |
| 20 | { |
| 21 | Token token = lexer.nextToken(); |
| 22 | |
| 23 | if (!token.isSignificant()) |
| 24 | continue; |
| 25 | |
| 26 | /// Literals. |
| 27 | if (token.type == TokenType::Number || token.type == TokenType::StringLiteral || token.type == TokenType::HereDoc) |
| 28 | { |
| 29 | if (0 == num_literals_in_sequence) |
| 30 | hash.update("\x00", 1); |
| 31 | ++num_literals_in_sequence; |
| 32 | prev_comma = false; |
| 33 | continue; |
| 34 | } |
| 35 | if (token.type == TokenType::Comma) |
| 36 | { |
| 37 | if (num_literals_in_sequence) |
| 38 | { |
| 39 | prev_comma = true; |
| 40 | continue; |
| 41 | } |
| 42 | } |
| 43 | else |
| 44 | { |
| 45 | if (num_literals_in_sequence > 1) |
| 46 | hash.update("\x00", 1); |
| 47 | |
| 48 | if (prev_comma) |
| 49 | hash.update(",", 1); |
| 50 | |
| 51 | num_literals_in_sequence = 0; |
| 52 | prev_comma = false; |
| 53 | } |
| 54 | |
| 55 | /// Slightly normalize something that look like aliases - if they are complex, replace them to `?` placeholders. |
| 56 | if (token.type == TokenType::QuotedIdentifier |
| 57 | /// Differentiate identifier from function (example: SHA224(x)). |
| 58 | /// However, it does not account for whitespaces and comments between the function name and the parentheses. |
| 59 | || (token.type == TokenType::BareWord && (token.end == end || *token.end != '('))) |
| 60 | { |
| 61 | /// Explicitly ask to keep identifier names |
| 62 | if (keep_names) |
| 63 | { |
| 64 | hash.update(token.begin, token.size()); |
| 65 | } |
| 66 | else |
| 67 | { |
no test coverage detected