| 22 | } |
| 23 | |
| 24 | ColumnPtr FuzzQuerySource::createColumn() |
| 25 | { |
| 26 | auto column = ColumnString::create(); |
| 27 | ColumnString::Chars & data_to = column->getChars(); |
| 28 | ColumnString::Offsets & offsets_to = column->getOffsets(); |
| 29 | |
| 30 | offsets_to.resize(block_size); |
| 31 | IColumn::Offset offset = 0; |
| 32 | |
| 33 | auto fuzz_base = query; |
| 34 | auto base_text = fuzz_base->formatForErrorMessage(); |
| 35 | size_t row_num = 0; |
| 36 | |
| 37 | /// Per-row attempt budget: when fuzzing keeps producing oversized variants we fall back |
| 38 | /// to emitting the original (unfuzzed) query so the loop always makes progress. The |
| 39 | /// formatted base query is guaranteed to fit `max_query_length` because |
| 40 | /// `getConfiguration` rejects configurations where it does not. |
| 41 | constexpr size_t max_attempts_per_row = 1024; |
| 42 | |
| 43 | while (row_num < block_size) |
| 44 | { |
| 45 | /// Stop generating rows promptly on cancellation (e.g. `KILL QUERY` or |
| 46 | /// `max_execution_time`) instead of filling the rest of the block with fallback rows. |
| 47 | if (isCancelled()) |
| 48 | break; |
| 49 | |
| 50 | String text_to_emit; |
| 51 | bool produced = false; |
| 52 | |
| 53 | for (size_t attempt = 0; attempt < max_attempts_per_row; ++attempt) |
| 54 | { |
| 55 | ASTPtr new_query = fuzz_base->clone(); |
| 56 | |
| 57 | auto base_before_fuzz = fuzz_base->formatForErrorMessage(); |
| 58 | fuzzer.fuzzMain(new_query); |
| 59 | auto fuzzed_text = new_query->formatForErrorMessage(); |
| 60 | |
| 61 | if (base_before_fuzz == fuzzed_text) |
| 62 | continue; |
| 63 | |
| 64 | /// AST is too long, will start from the original query. |
| 65 | if (fuzzed_text.size() > config.max_query_length) |
| 66 | { |
| 67 | fuzz_base = query; |
| 68 | continue; |
| 69 | } |
| 70 | |
| 71 | text_to_emit = std::move(fuzzed_text); |
| 72 | fuzz_base = new_query; |
| 73 | produced = true; |
| 74 | break; |
| 75 | } |
| 76 | |
| 77 | if (!produced) |
| 78 | { |
| 79 | /// Fallback: emit the formatted unfuzzed query. Always within `max_query_length` |
| 80 | /// thanks to the upfront check in `getConfiguration`. |
| 81 | text_to_emit = base_text; |
no test coverage detected