diff --git a/src/sqlancer/postgres/PostgresProvider.java b/src/sqlancer/postgres/PostgresProvider.java index 2b2cb8585..4ddb2d1c7 100644 --- a/src/sqlancer/postgres/PostgresProvider.java +++ b/src/sqlancer/postgres/PostgresProvider.java @@ -125,6 +125,7 @@ public enum Action implements AbstractAction { LISTEN((g) -> PostgresNotifyGenerator.createListen()), // UNLISTEN((g) -> PostgresNotifyGenerator.createUnlisten()), // CREATE_SEQUENCE(PostgresSequenceGenerator::createSequence), // + EXPLAIN(PostgresExplainGenerator::create), // CREATE_VIEW(PostgresViewGenerator::create); private final SQLQueryProvider sqlQueryProvider; @@ -192,6 +193,9 @@ protected static int mapActions(PostgresGlobalState globalState, Action a) { case INSERT: nrPerformed = r.getInteger(0, globalState.getOptions().getMaxNumberInserts()); break; + case EXPLAIN: + nrPerformed = r.getInteger(0, 1); + break; default: throw new AssertionError(a); } diff --git a/src/sqlancer/postgres/gen/PostgresAlterTableGenerator.java b/src/sqlancer/postgres/gen/PostgresAlterTableGenerator.java index f12927e90..c8ce1b475 100644 --- a/src/sqlancer/postgres/gen/PostgresAlterTableGenerator.java +++ b/src/sqlancer/postgres/gen/PostgresAlterTableGenerator.java @@ -298,6 +298,7 @@ public SQLQueryAdapter generate() { errors.add("contains null values"); errors.add("insufficient columns in PRIMARY KEY constraint definition"); errors.add("which is part of the partition key"); + errors.add("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables"); break; case VALIDATE_CONSTRAINT: sb.append("VALIDATE CONSTRAINT asdf"); diff --git a/src/sqlancer/postgres/gen/PostgresExplainGenerator.java b/src/sqlancer/postgres/gen/PostgresExplainGenerator.java index d3039394b..effca95b5 100644 --- a/src/sqlancer/postgres/gen/PostgresExplainGenerator.java +++ b/src/sqlancer/postgres/gen/PostgresExplainGenerator.java @@ -1,5 +1,15 @@ package sqlancer.postgres.gen; +import java.util.Arrays; + +import sqlancer.Randomly; +import sqlancer.common.query.SQLQueryAdapter; +import sqlancer.postgres.PostgresGlobalState; +import sqlancer.postgres.PostgresSchema; +import sqlancer.postgres.PostgresSchema.PostgresDataType; +import sqlancer.postgres.PostgresSchema.PostgresTables; +import sqlancer.postgres.ast.PostgresSelect; + public final class PostgresExplainGenerator { private PostgresExplainGenerator() { @@ -13,4 +23,50 @@ public static String explain(String selectStr) throws Exception { return sb.toString(); } + public static String explainGeneral(String selectStr) throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append("EXPLAIN "); + + // Add various EXPLAIN options randomly + if (Randomly.getBoolean()) { + sb.append("(ANALYZE) "); + } + if (Randomly.getBoolean()) { + sb.append("(FORMAT "); + sb.append(Randomly.fromOptions("TEXT", "XML", "JSON", "YAML")); + sb.append(") "); + } + if (Randomly.getBoolean()) { + sb.append("(VERBOSE) "); + } + if (Randomly.getBoolean()) { + sb.append("(COSTS) "); + } + if (Randomly.getBoolean()) { + sb.append("(BUFFERS) "); + } + if (Randomly.getBoolean()) { + sb.append("(TIMING) "); + } + if (Randomly.getBoolean()) { + sb.append("(SUMMARY) "); + } + + sb.append(selectStr); + return sb.toString(); + } + + public static SQLQueryAdapter create(PostgresGlobalState globalState) throws Exception { + PostgresSchema.PostgresTable table = globalState.getSchema().getRandomTable(t -> !t.isView()); + PostgresExpressionGenerator gen = new PostgresExpressionGenerator(globalState); + gen.setTablesAndColumns(new PostgresTables(Arrays.asList(table))); + PostgresSelect select = gen.generateSelect(); + select.setFromList(gen.getTableRefs()); + select.setFetchColumns(gen.generateFetchColumns(false)); + if (Randomly.getBoolean()) { + select.setWhereClause(gen.generateExpression(PostgresDataType.BOOLEAN)); + } + return new SQLQueryAdapter(explainGeneral(select.asString())); + } + }