Extracts candidate table and column tokens from SQL-like context strings.
| 398 | |
| 399 | // Extracts candidate table and column tokens from SQL-like context strings. |
| 400 | static pair<vector<string>, vector<string>> ExtractTableAndColumnTokens( |
| 401 | const vector<string>& contexts) { |
| 402 | unordered_set<string_view> table_set; |
| 403 | unordered_set<string_view> column_set; |
| 404 | unordered_set<string_view> table_leaf_set; |
| 405 | vector<string> table_tokens; |
| 406 | vector<string> column_tokens; |
| 407 | |
| 408 | for (const string& context : contexts) { |
| 409 | const string_view context_view(context); |
| 410 | for (sregex_iterator it(context.cbegin(), context.cend(), QUALIFIED_ID_RE), end; |
| 411 | it != end; ++it) { |
| 412 | const smatch& match = *it; |
| 413 | if (match.length(1) == 0) continue; |
| 414 | string_view fq = |
| 415 | context_view.substr(match.position(1), match.length(1)); |
| 416 | const size_t last_dot = fq.rfind('.'); |
| 417 | if (last_dot == string_view::npos || last_dot == 0 |
| 418 | || last_dot + 1 >= fq.size()) { |
| 419 | continue; |
| 420 | } |
| 421 | const string_view table = fq.substr(0, last_dot); |
| 422 | const string_view col = fq.substr(last_dot + 1); |
| 423 | if (table_set.insert(table).second) table_tokens.emplace_back(table); |
| 424 | if (column_set.insert(col).second) column_tokens.emplace_back(col); |
| 425 | const size_t table_last_dot = table.rfind('.'); |
| 426 | table_leaf_set.insert( |
| 427 | table_last_dot == string_view::npos ? table : table.substr(table_last_dot + 1)); |
| 428 | } |
| 429 | |
| 430 | for (sregex_iterator it(context.cbegin(), context.cend(), FROM_JOIN_TABLE_RE), |
| 431 | end; |
| 432 | it != end; ++it) { |
| 433 | const smatch& match = *it; |
| 434 | if (match.length(1) == 0) continue; |
| 435 | const string_view table = |
| 436 | context_view.substr(match.position(1), match.length(1)); |
| 437 | if (table_set.insert(table).second) table_tokens.emplace_back(table); |
| 438 | const size_t table_last_dot = table.rfind('.'); |
| 439 | table_leaf_set.insert( |
| 440 | table_last_dot == string_view::npos ? table : table.substr(table_last_dot + 1)); |
| 441 | } |
| 442 | |
| 443 | for (sregex_iterator it(context.cbegin(), context.cend(), SNAKE_CASE_ID_RE), end; |
| 444 | it != end; ++it) { |
| 445 | const smatch& match = *it; |
| 446 | if (match.length(1) == 0) continue; |
| 447 | const string_view token = |
| 448 | context_view.substr(match.position(1), match.length(1)); |
| 449 | if (table_set.find(token) != table_set.end()) continue; |
| 450 | if (table_leaf_set.find(token) != table_leaf_set.end()) continue; |
| 451 | if (column_set.insert(token).second) column_tokens.emplace_back(token); |
| 452 | } |
| 453 | } |
| 454 | return {table_tokens, column_tokens}; |
| 455 | } |
| 456 | |
| 457 | // Redacts sensitive profile values and optionally records alias-to-original mappings. |