From 46f45e9b517c344cce2423dc8195e3cc0a60a9c6 Mon Sep 17 00:00:00 2001 From: ZhengLin Li <1125806272@qq.com> Date: Tue, 13 Jun 2023 19:50:00 +0800 Subject: [PATCH] feat: add appendType StoneDBTableGenerator.java --- src/sqlancer/Randomly.java | 21 +- src/sqlancer/stonedb/StoneDBProvider.java | 17 +- .../stonedb/gen/StoneDBTableGenerator.java | 338 +++++++++++------- 3 files changed, 238 insertions(+), 138 deletions(-) diff --git a/src/sqlancer/Randomly.java b/src/sqlancer/Randomly.java index e1250abb3..fab1902b5 100644 --- a/src/sqlancer/Randomly.java +++ b/src/sqlancer/Randomly.java @@ -1,11 +1,8 @@ package sqlancer; import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Random; +import java.math.BigInteger; +import java.util.*; import java.util.function.Supplier; public final class Randomly { @@ -215,7 +212,6 @@ public String getString(Randomly r) { }, ALPHANUMERIC { - @Override public String getString(Randomly r) { return getStringOfAlphabet(r, ALPHANUMERIC_ALPHABET); @@ -224,7 +220,6 @@ public String getString(Randomly r) { }, ALPHANUMERIC_SPECIALCHAR { - @Override public String getString(Randomly r) { return getStringOfAlphabet(r, ALPHANUMERIC_SPECIALCHAR_ALPHABET); @@ -452,6 +447,18 @@ public long getLong(long left, long right) { return getNextLong(left, right); } + public BigInteger getBigInteger(BigInteger left, BigInteger right) { + if (left.equals(right)) { + return left; + } + while (true) { + BigInteger result = new BigInteger(63, new Random()); + if (result.compareTo(left) >= 0 && result.compareTo(right) <= 0) { + return result; + } + } + } + public BigDecimal getRandomBigDecimal() { return BigDecimal.valueOf(getThreadRandom().get().nextDouble()); } diff --git a/src/sqlancer/stonedb/StoneDBProvider.java b/src/sqlancer/stonedb/StoneDBProvider.java index e3dddb41f..032a5943c 100644 --- a/src/sqlancer/stonedb/StoneDBProvider.java +++ b/src/sqlancer/stonedb/StoneDBProvider.java @@ -28,6 +28,7 @@ protected StoneDBSchema readSchema() throws Exception { enum Action implements AbstractAction { SHOW_TABLES((g) -> new SQLQueryAdapter("SHOW TABLES")); + private final SQLQueryProvider sqlQueryProvider; Action(SQLQueryProvider sqlQueryProvider) { @@ -43,10 +44,10 @@ public Query getQuery(StoneDBGlobalState globalState) throws Exception { private static int mapActions(StoneDBGlobalState globalState, Action a) { Randomly r = globalState.getRandomly(); switch (a) { - case SHOW_TABLES: - return 1; - default: - throw new AssertionError(a); + case SHOW_TABLES: + return 1; + default: + throw new AssertionError(a); } } @@ -59,10 +60,10 @@ public void generateDatabase(StoneDBGlobalState globalState) throws Exception { } StatementExecutor se = new StatementExecutor<>(globalState, Action.values(), StoneDBProvider::mapActions, (q) -> { - if (globalState.getSchema().getDatabaseTables().isEmpty()) { - throw new IgnoreMeException(); - } - }); + if (globalState.getSchema().getDatabaseTables().isEmpty()) { + throw new IgnoreMeException(); + } + }); se.executeStatements(); } diff --git a/src/sqlancer/stonedb/gen/StoneDBTableGenerator.java b/src/sqlancer/stonedb/gen/StoneDBTableGenerator.java index 5e22847dc..7c2489624 100644 --- a/src/sqlancer/stonedb/gen/StoneDBTableGenerator.java +++ b/src/sqlancer/stonedb/gen/StoneDBTableGenerator.java @@ -4,6 +4,7 @@ import sqlancer.common.DBMSCommon; import sqlancer.common.query.ExpectedErrors; import sqlancer.common.query.SQLQueryAdapter; +import sqlancer.mysql.MySQLBugs; import sqlancer.stonedb.StoneDBProvider.StoneDBGlobalState; import sqlancer.stonedb.StoneDBSchema; import sqlancer.stonedb.StoneDBSchema.StoneDBDataType; @@ -14,36 +15,28 @@ import java.util.List; public class StoneDBTableGenerator { - private final Randomly r; + private final String tableName; + private final StoneDBSchema schema; + private final List columns = new ArrayList<>(); private final boolean allowPrimaryKey; private boolean setPrimaryKey; private final StringBuilder sb = new StringBuilder(); - - private final StoneDBGlobalState globalState; - private final List columns = new ArrayList<>(); - private boolean tableHasNullableColumn; - private int keysSpecified; - - private final StoneDBSchema schema; - private final String tableName; + private final Randomly r; public StoneDBTableGenerator(StoneDBGlobalState globalState, String tableName) { - this.globalState = globalState; - this.r = globalState.getRandomly(); - allowPrimaryKey = Randomly.getBoolean(); - this.schema = globalState.getSchema(); this.tableName = tableName; + this.schema = globalState.getSchema(); + allowPrimaryKey = Randomly.getBoolean(); + this.r = globalState.getRandomly(); } - public static SQLQueryAdapter generate(StoneDBGlobalState globalState, String tableName) { return new StoneDBTableGenerator(globalState, tableName).getQuery(); } public SQLQueryAdapter getQuery() { ExpectedErrors errors = new ExpectedErrors(); - sb.append("CREATE"); - sb.append(" TABLE"); + sb.append("CREATE TABLE"); if (Randomly.getBoolean()) { sb.append(" IF NOT EXISTS"); } @@ -54,7 +47,7 @@ public SQLQueryAdapter getQuery() { sb.append(schema.getRandomTable().getName()); return new SQLQueryAdapter(sb.toString(), true); } else { - appendColumnsString(); + appendColumns(); sb.append(" "); appendTableOptions(); addCommonErrors(errors); @@ -90,62 +83,62 @@ private void appendTableOptions() { sb.append(", "); } switch (o) { - case AUTO_INCREMENT: - sb.append("AUTO_INCREMENT = "); - sb.append(r.getPositiveInteger()); - break; - // The valid range for avg_row_length is [0,4294967295] - case AVG_ROW_LENGTH: - sb.append("AVG_ROW_LENGTH = "); - sb.append(r.getLong(0, 4294967295L + 1)); - break; - case CHECKSUM: - sb.append("CHECKSUM = 1"); - break; - case COMPRESSION: - sb.append("COMPRESSION = '"); - sb.append(Randomly.fromOptions("ZLIB", "LZ4", "NONE")); - sb.append("'"); - break; - case DELAY_KEY_WRITE: - sb.append("DELAY_KEY_WRITE = "); - sb.append(Randomly.fromOptions(0, 1)); - break; - case INSERT_METHOD: - sb.append("INSERT_METHOD = "); - sb.append(Randomly.fromOptions("NO", "FIRST", "LAST")); - break; - // The valid range for key_block_size is [0,65535] - case KEY_BLOCK_SIZE: - sb.append("KEY_BLOCK_SIZE = "); - sb.append(r.getInteger(0, 65535 + 1)); - break; - case MAX_ROWS: - sb.append("MAX_ROWS = "); - sb.append(r.getLong(0, Long.MAX_VALUE)); - break; - case MIN_ROWS: - sb.append("MIN_ROWS = "); - sb.append(r.getLong(1, Long.MAX_VALUE)); - break; - case PACK_KEYS: - sb.append("PACK_KEYS = "); - sb.append(Randomly.fromOptions("1", "0", "DEFAULT")); - break; - case STATS_AUTO_RECALC: - sb.append("STATS_AUTO_RECALC = "); - sb.append(Randomly.fromOptions("1", "0", "DEFAULT")); - break; - case STATS_PERSISTENT: - sb.append("STATS_PERSISTENT = "); - sb.append(Randomly.fromOptions("1", "0", "DEFAULT")); - break; - case STATS_SAMPLE_PAGES: - sb.append("STATS_SAMPLE_PAGES = "); - sb.append(r.getInteger(1, Short.MAX_VALUE)); - break; - default: - throw new AssertionError(o); + case AUTO_INCREMENT: + sb.append("AUTO_INCREMENT = "); + sb.append(r.getPositiveInteger()); + break; + // The valid range for avg_row_length is [0,4294967295] + case AVG_ROW_LENGTH: + sb.append("AVG_ROW_LENGTH = "); + sb.append(r.getLong(0, 4294967295L + 1)); + break; + case CHECKSUM: + sb.append("CHECKSUM = 1"); + break; + case COMPRESSION: + sb.append("COMPRESSION = '"); + sb.append(Randomly.fromOptions("ZLIB", "LZ4", "NONE")); + sb.append("'"); + break; + case DELAY_KEY_WRITE: + sb.append("DELAY_KEY_WRITE = "); + sb.append(Randomly.fromOptions(0, 1)); + break; + case INSERT_METHOD: + sb.append("INSERT_METHOD = "); + sb.append(Randomly.fromOptions("NO", "FIRST", "LAST")); + break; + // The valid range for key_block_size is [0,65535] + case KEY_BLOCK_SIZE: + sb.append("KEY_BLOCK_SIZE = "); + sb.append(r.getInteger(0, 65535 + 1)); + break; + case MAX_ROWS: + sb.append("MAX_ROWS = "); + sb.append(r.getLong(0, Long.MAX_VALUE)); + break; + case MIN_ROWS: + sb.append("MIN_ROWS = "); + sb.append(r.getLong(1, Long.MAX_VALUE)); + break; + case PACK_KEYS: + sb.append("PACK_KEYS = "); + sb.append(Randomly.fromOptions("1", "0", "DEFAULT")); + break; + case STATS_AUTO_RECALC: + sb.append("STATS_AUTO_RECALC = "); + sb.append(Randomly.fromOptions("1", "0", "DEFAULT")); + break; + case STATS_PERSISTENT: + sb.append("STATS_PERSISTENT = "); + sb.append(Randomly.fromOptions("1", "0", "DEFAULT")); + break; + case STATS_SAMPLE_PAGES: + sb.append("STATS_SAMPLE_PAGES = "); + sb.append(r.getInteger(1, Short.MAX_VALUE)); + break; + default: + throw new AssertionError(o); } } } @@ -162,43 +155,42 @@ private void addCommonErrors(ExpectedErrors list) { list.add("Got error -1 - 'Unknown error -1' from storage engine"); } - private void appendColumnsString() { + private void appendColumns() { sb.append("("); for (int i = 0; i < 1 + Randomly.smallNumber(); i++) { if (i != 0) { sb.append(", "); } - appendColumnString(i); + appendColumn(i); } sb.append(")"); } - - private void appendColumnString(int columnId) { + private void appendColumn(int columnId) { String columnName = DBMSCommon.createColumnName(columnId); columns.add(columnName); sb.append(columnName); - appendColumnDefinitionString(); + appendColumnDefinition(); } - - private void appendColumnDefinitionString() { + private void appendColumnDefinition() { sb.append(" "); StoneDBDataType randomType = StoneDBDataType.getRandomWithoutNull(); - appendTypeString(randomType); + appendType(randomType); sb.append(" "); - appendColumnOptionString(randomType); + appendColumnOption(randomType); } private enum ColumnOptions { NULL_OR_NOT_NULL, UNIQUE, COMMENT, COLUMN_FORMAT, STORAGE, PRIMARY_KEY } - private void appendColumnOptionString(StoneDBDataType type) { + private void appendColumnOption(StoneDBDataType type) { boolean isTextType = type == StoneDBDataType.VARCHAR; boolean isNull = false; boolean columnHasPrimaryKey = false; List columnOptions = Randomly.subset(ColumnOptions.values()); + boolean tableHasNullableColumn; if (!columnOptions.contains(ColumnOptions.NULL_OR_NOT_NULL)) { tableHasNullableColumn = true; } @@ -210,53 +202,153 @@ private void appendColumnOptionString(StoneDBDataType type) { for (ColumnOptions o : columnOptions) { sb.append(" "); switch (o) { - case NULL_OR_NOT_NULL: - // PRIMARY KEYs cannot be NULL - if (!columnHasPrimaryKey) { - if (Randomly.getBoolean()) { - sb.append("NULL"); - } - tableHasNullableColumn = true; - isNull = true; - } else { - sb.append("NOT NULL"); - } - break; - case UNIQUE: - sb.append("UNIQUE"); - keysSpecified++; + case NULL_OR_NOT_NULL: + // PRIMARY KEYs cannot be NULL + if (!columnHasPrimaryKey) { if (Randomly.getBoolean()) { - sb.append(" KEY"); - } - break; - case COMMENT: - // TODO: generate randomly - sb.append(String.format("COMMENT '%s' ", "asdf")); - break; - case COLUMN_FORMAT: - sb.append("COLUMN_FORMAT "); - sb.append(Randomly.fromOptions("FIXED", "DYNAMIC", "DEFAULT")); - break; - case STORAGE: - sb.append("STORAGE "); - sb.append(Randomly.fromOptions("DISK", "MEMORY")); - break; - case PRIMARY_KEY: - // PRIMARY KEYs cannot be NULL - if (allowPrimaryKey && !setPrimaryKey && !isNull) { - sb.append("PRIMARY KEY"); - setPrimaryKey = true; - columnHasPrimaryKey = true; + sb.append("NULL"); } - break; - default: - throw new AssertionError(); + tableHasNullableColumn = true; + isNull = true; + } else { + sb.append("NOT NULL"); + } + break; + case UNIQUE: + sb.append("UNIQUE"); + if (Randomly.getBoolean()) { + sb.append(" KEY"); + } + break; + case COMMENT: + // TODO: generate randomly + sb.append(String.format("COMMENT '%s' ", "asdf")); + break; + case COLUMN_FORMAT: + sb.append("COLUMN_FORMAT "); + sb.append(Randomly.fromOptions("FIXED", "DYNAMIC", "DEFAULT")); + break; + case STORAGE: + sb.append("STORAGE "); + sb.append(Randomly.fromOptions("DISK", "MEMORY")); + break; + case PRIMARY_KEY: + // PRIMARY KEYs cannot be NULL + if (allowPrimaryKey && !setPrimaryKey && !isNull) { + sb.append("PRIMARY KEY"); + setPrimaryKey = true; + columnHasPrimaryKey = true; + } + break; + default: + throw new AssertionError(); } } } - private void appendTypeString(StoneDBDataType randomType) { + private void appendType(StoneDBDataType randomType) { switch (randomType) { + case TINYINT: + sb.append("TINYINT"); + // sb.append(r.getInteger(-128, 127)); + break; + case SMALLINT: + sb.append("SMALLINT"); + // sb.append(r.getInteger(-32768, 32767)); + break; + case MEDIUMINT: + sb.append("MEDIUMINT"); + // sb.append(r.getInteger(-8388608, 8388607)); + break; + case INT: + sb.append("INT"); + // sb.append(r.getInteger(-2147483647, 2147483647)); + break; + case BIGINT: + sb.append("BIGINT"); + // sb.append(r.getBigInteger(new BigInteger("-9223372036854775806"), new + // BigInteger("9223372036854775807"))); + break; + case FLOAT: + sb.append("FLOAT"); + optionallyAddPrecisionAndScale(sb); + break; + case DOUBLE: + sb.append("DOUBLE"); + optionallyAddPrecisionAndScale(sb); + break; + case DECIMAL: + sb.append("DECIMAL"); // The default value is P(10,0); + break; + case YEAR: + sb.append("YEAR"); + break; + case TIME: + sb.append("TIME"); + break; + case DATE: + sb.append("DATE"); + break; + case DATETIME: + sb.append("DATETIME"); + break; + case TIMESTAMP: + sb.append("TIMESTAMP"); + break; + case CHAR: + sb.append("CHAR"); + break; + case VARCHAR: + sb.append("VARCHAR("); + sb.append(r.getInteger(0, 65535)); + sb.append(")"); + break; + case TINYTEXT: + sb.append("TINYTEXT"); + break; + case TEXT: + sb.append("TEXT"); + break; + case MEDIUMTEXT: + sb.append("MEDIUMTEXT"); + break; + case LONGTEXT: + sb.append("LONGTEXT"); + break; + case BINARY: + sb.append("BINARY"); + break; + case VARBINARY: + sb.append("VARBINARY"); + break; + case TINYBLOB: + sb.append("TINYBLOB"); + break; + case BLOB: + sb.append("BLOB"); + break; + case MEDIUMBLOB: + sb.append("MEDIUMBLOB"); + break; + case LONGBLOB: + sb.append("LONGBLOB"); + break; + } + } + + public static void optionallyAddPrecisionAndScale(StringBuilder sb) { + if (Randomly.getBoolean() && !MySQLBugs.bug99183) { + sb.append("("); + // The maximum number of digits (M) for DECIMAL is 65 + long m = Randomly.getNotCachedInteger(1, 65); + sb.append(m); + sb.append(", "); + // The maximum number of supported decimals (D) is 30 + long nCandidate = Randomly.getNotCachedInteger(1, 30); + // For float(M,D), double(M,D) or decimal(M,D), M must be >= D (column 'c0'). + long n = Math.min(nCandidate, m); + sb.append(n); + sb.append(")"); } } }