Skip to content

Commit

Permalink
Add support for ignoring queries that violate an index requirement ba…
Browse files Browse the repository at this point in the history
…sed on callstack
  • Loading branch information
muglug committed Jun 6, 2023
1 parent e3213f7 commit 61a01a7
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 3 deletions.
10 changes: 8 additions & 2 deletions src/AsyncMysql/AsyncMysqlConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public function __construct(private string $host, private int $port, private str
dict<string, string> $_query_attributes = dict[],
): Awaitable<AsyncMysqlQueryResult> {
Logger::log(Verbosity::QUERIES, "SQLFake [verbose]: $query");
QueryContext::$query = $query;

$config = $this->server->config;
$strict_sql_before = QueryContext::$strictSQLMode;
Expand All @@ -62,11 +63,13 @@ public function __construct(private string $host, private int $port, private str
try {
list($results, $rows_affected) = SQLCommandProcessor::execute($query, $this);
} catch (\Exception $e) {
// this makes debugging a failing unit test easier, show the actual query that failed parsing along with the parser error
QueryContext::$strictSQLMode = $strict_sql_before;
QueryContext::$strictSchemaMode = $strict_schema_before;
// Make debugging a failing unit test locally easier,
// by showing the actual query that failed parsing along with the parser error
$msg = $e->getMessage();
$type = \get_class($e);
Logger::log(Verbosity::QUIET, $e->getFile().' '.$e->getLine());
Logger::log(Verbosity::QUIET, "SQL Fake $type: $msg in SQL query: $query");
throw $e;
}
Expand All @@ -82,7 +85,10 @@ public function __construct(private string $host, private int $port, private str
}

<<__Override>>
public function queryf(\HH\FormatString<\HH\SQLFormatter> $query, mixed ...$args): Awaitable<AsyncMysqlQueryResult> {
public function queryf(
\HH\FormatString<\HH\SQLFormatter> $query,
mixed ...$args
): Awaitable<AsyncMysqlQueryResult> {
invariant($query is string, '\\HH\\FormatString<_> is opaque, but we need it to be a string here.');
return $this->query($this->queryStringifier->formatString($query, vec($args)));
}
Expand Down
21 changes: 20 additions & 1 deletion src/Query/QueryPlanner.hack
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,26 @@ abstract class QueryPlanner {
false,
);
} else if (QueryContext::$requireIndexes) {
throw new \Exception('Query without index: '.(QueryContext::$query ?? ''));
$stack = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
C\pop_front(inout $stack);

$grandfathered = false;
foreach ($stack as $stack_frame) {
$class = $stack_frame['class'] ?? null;
$function = $stack_frame['function'];

foreach (QueryContext::$allowed_index_violation_traces as $allowed_trace) {
$allowed_trace_class = $allowed_trace['class'] ?? null;

if ($class === $allowed_trace_class && $allowed_trace['function'] === $function) {
$grandfathered = true;
}
}
}

if (!$grandfathered) {
throw new \Exception('Query without index: '.(QueryContext::$query ?? ''));
}
}

return null;
Expand Down
6 changes: 6 additions & 0 deletions src/QueryContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@
*/
public static bool $requireIndexes = false;

/**
* Allow violations of the must-use-index policy when the stack trace matches any of these
* class-function pairs.
*/
public static vec<shape(?'class' => string, 'function' => string)> $allowed_index_violation_traces = vec[];

/**
* Require the presence of the table's Vitess sharding key
*/
Expand Down

0 comments on commit 61a01a7

Please sign in to comment.