From efa6d467af1541f2022d2f5f81ca46fe6dda4da7 Mon Sep 17 00:00:00 2001 From: Manuel Rigger Date: Sat, 11 Apr 2026 23:28:57 +0800 Subject: [PATCH] Materialize: Retry readSchema to handle eventual consistency Materialize exhibits eventual consistency where a table can appear in information_schema.tables before its columns are visible in INFORMATION_SCHEMA.COLUMNS. Since readSchema fetches these in separate queries, any updateSchema() call can produce a snapshot with tables that have empty column lists, causing crashes throughout execution (e.g., see https://github.com/sqlancer/sqlancer/actions/runs/24169417215/job/70537769855). Fix this by retrying in readSchema itself until the snapshot is consistent (all tables have at least one column), so every updateSchema() call is guaranteed to return a usable schema. Co-Authored-By: Claude Opus 4.6 --- .../materialize/MaterializeGlobalState.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/sqlancer/materialize/MaterializeGlobalState.java b/src/sqlancer/materialize/MaterializeGlobalState.java index 77cbe5d14..a9eb06d7b 100644 --- a/src/sqlancer/materialize/MaterializeGlobalState.java +++ b/src/sqlancer/materialize/MaterializeGlobalState.java @@ -266,6 +266,21 @@ public String getRandomTableAccessMethod() { @Override public MaterializeSchema readSchema() throws SQLException { + // Materialize exhibits eventual consistency: a table can appear in + // information_schema.tables before its columns are visible in + // INFORMATION_SCHEMA.COLUMNS. Retry until the snapshot is consistent. + for (int tries = 0; tries < 30; tries++) { + MaterializeSchema schema = MaterializeSchema.fromConnection(getConnection(), getDatabaseName()); + if (schema.getDatabaseTables().stream().noneMatch(t -> t.getColumns().isEmpty())) { + return schema; + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } return MaterializeSchema.fromConnection(getConnection(), getDatabaseName()); }