From 517e39474b609d9c582c9f8266e2a3f8568f3070 Mon Sep 17 00:00:00 2001 From: ZHENGLIN LI <63448884+ZhengLin-Li@users.noreply.github.com> Date: Wed, 12 Jul 2023 22:13:22 +0800 Subject: [PATCH] fix & refactor: fix StoneDB implementation bugs and refactor code, Part 2 (#854) * fix: add space when creating table * fix: add expected errors and key length specification * feat: generate comments randomly * fix: add expected errors and key restriction * feat: add support for more map actions * fix: add space between INDEX and indexName * fix: add space and {} * fix: remove pre-added errors before testing * refactor: rename delete useless as when deleting from table * fix: add missing space * fix: add missing ( * fix: add missing length for varbinary type * fix: string generation strategy in comments * fix: add provider * fix: use global randomly * fix: insert one row * feat: add expected errors * feat: no length * feat: no length * style: format * fix: Tianmu engine does not support fulltext index. * fix: fix alter table and getwithoutnull * fix: add expected errors * feat: support StringVisitor of StoneSelect * fix: delete useless import * fix: syntax error in index create * style: run formate * fix: syntax errors * fix: delete AS in DELETE * feat: add expectedErrors for all generator * refactor: change cast operation to lambda and fix dataType toString error * feat: add expected errors * refactor: remove redundant identifier * refactor: remove unused expected errors * refactor: use parent StringBuilder * refactor: add more expected errors * feat: add some options according to docs * refactor: change action perform times * refactor: rename and change action perform times * fix: delete unexcepted errors * feat: add one expected error * feat: add another option when assigning assignment list * refactor: refactor append type, reused function * refactor: refactor append type argus * fix: fix null pointer exception * refactor: delete useless code * fix: fix cast operation bug in StoneDB * feat: add some options according to docs * feat: add one expected error * feat: add another option when assigning assignment list * fix: fix null pointer exception * fix: fix cast operation bug in StoneDB * fix: fix cast operation bug in StoneDB * fix: Tianmu engine does not support fulltext index. * feat: add expectedErrors for all generator * refactor: remove unused expected errors * refactor: delete useless code * feat: add some options according to docs * feat: add one expected error * feat: add another option when assigning assignment list * fix: fix null pointer exception * fix: fix cast operation bug in StoneDB * fix: fix cast operation bug in StoneDB * fix: delete rename column * feat: add expected errors * refactor: reuse code * refactor: refactor append type argus * refactor: reuse code, rename, fix * fix: syntax error * fix: change / alter column error * fix: change / alter column error * fix: change / alter column error * fix: duplicate data type when add column in alter table * fix: duplicate data type when add column in alter table * fix: no space error * fix: fix as count in NoREC Oracle * fix: fix as count in NoREC Oracle * fix: fix as count in NoREC Oracle * fix: fix bitwise operation * fix: fix binary operation * fix: fix in operation * feat: add expected errors * feat: add expected errors * feat: fix errors in fetch size * feat: add expected errors * fix: regex match string * feat: add expected errors * git: merge main * feat: disable delete order by * feat: disable delete order by * feat: disable delete order by --- src/sqlancer/stonedb/StoneDBSchema.java | 81 +++++++++++ .../stonedb/StoneDBToStringVisitor.java | 12 ++ .../gen/StoneDBExpressionGenerator.java | 53 ++++++-- .../gen/StoneDBIndexCreateGenerator.java | 20 ++- .../gen/StoneDBTableAlterGenerator.java | 85 ++++++++---- .../gen/StoneDBTableCreateGenerator.java | 127 +++--------------- .../gen/StoneDBTableDeleteGenerator.java | 9 +- .../gen/StoneDBTableInsertGenerator.java | 5 + .../gen/StoneDBTableUpdateGenerator.java | 7 +- .../stonedb/oracle/StoneDBNoRECOracle.java | 20 ++- 10 files changed, 245 insertions(+), 174 deletions(-) diff --git a/src/sqlancer/stonedb/StoneDBSchema.java b/src/sqlancer/stonedb/StoneDBSchema.java index c24b67793..2150cbba1 100644 --- a/src/sqlancer/stonedb/StoneDBSchema.java +++ b/src/sqlancer/stonedb/StoneDBSchema.java @@ -92,6 +92,87 @@ public static Node getRandomValue(StoneDBDataType dataType) { } return null; } + + public static String getTypeAndValue(StoneDBDataType dataType) { + StringBuilder sb = new StringBuilder(); + switch (dataType) { + case TINYINT: + return "TINYINT"; + case SMALLINT: + return "SMALLINT"; + case MEDIUMINT: + return "MEDIUMINT"; + case INT: + return "INT"; + case BIGINT: + return "BIGINT"; + case FLOAT: + sb.append("FLOAT"); + optionallyAddPrecisionAndScale(sb); + return sb.toString(); + case DOUBLE: + sb.append("DOUBLE"); + optionallyAddPrecisionAndScale(sb); + return sb.toString(); + case DECIMAL: + return "DECIMAL"; // The default value is P(10,0); + case YEAR: + return "YEAR"; + case TIME: + return "TIME"; + case DATE: + return "DATE"; + case DATETIME: + return "DATETIME"; + case TIMESTAMP: + return "TIMESTAMP"; + case CHAR: + sb.append("CHAR").append(Randomly.fromOptions("", "(" + new Randomly().getInteger(0, 255) + ")")); + return sb.toString(); + case VARCHAR: + sb.append("VARCHAR").append("(").append(new Randomly().getInteger(0, 65535)).append(")"); + return sb.toString(); + case TINYTEXT: + return "TINYTEXT"; + case TEXT: + return "TEXT"; + case MEDIUMTEXT: + return "MEDIUMTEXT"; + case LONGTEXT: + return "LONGTEXT"; + case BINARY: + return "BINARY"; + case VARBINARY: + sb.append("VARBINARY").append("(").append(new Randomly().getInteger(0, 65535)).append(")"); + return sb.toString(); + case TINYBLOB: + return "TINYBLOB"; + case BLOB: + return "BLOB"; + case MEDIUMBLOB: + return "MEDIUMBLOB"; + case LONGBLOB: + return "LONGBLOB"; + default: + throw new AssertionError(); + } + } + + private static void optionallyAddPrecisionAndScale(StringBuilder sb) { + if (Randomly.getBoolean()) { + 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(")"); + } + } } public static class StoneDBTable diff --git a/src/sqlancer/stonedb/StoneDBToStringVisitor.java b/src/sqlancer/stonedb/StoneDBToStringVisitor.java index 9d7711873..45b050b50 100644 --- a/src/sqlancer/stonedb/StoneDBToStringVisitor.java +++ b/src/sqlancer/stonedb/StoneDBToStringVisitor.java @@ -2,10 +2,12 @@ import sqlancer.common.ast.newast.NewToStringVisitor; import sqlancer.common.ast.newast.Node; +import sqlancer.stonedb.StoneDBSchema.StoneDBDataType; import sqlancer.stonedb.ast.StoneDBConstant; import sqlancer.stonedb.ast.StoneDBExpression; import sqlancer.stonedb.ast.StoneDBJoin; import sqlancer.stonedb.ast.StoneDBSelect; +import sqlancer.stonedb.gen.StoneDBExpressionGenerator.StoneDBCastOperation; public class StoneDBToStringVisitor extends NewToStringVisitor { @Override @@ -16,6 +18,8 @@ public void visitSpecific(Node expr) { visit((StoneDBSelect) expr); } else if (expr instanceof StoneDBJoin) { visit((StoneDBJoin) expr); + } else if (expr instanceof StoneDBCastOperation) { + visit((StoneDBCastOperation) expr); } else { throw new AssertionError(expr.getClass()); } @@ -81,6 +85,14 @@ private void visit(StoneDBJoin join) { } } + private void visit(StoneDBCastOperation cast) { + sb.append("CAST("); + visit(cast.getExpr()); + sb.append(" AS "); + sb.append(cast.getType() == StoneDBDataType.INT ? "UNSIGNED" : cast.getType().toString()); + sb.append(") "); + } + public static String asString(Node expr) { StoneDBToStringVisitor visitor = new StoneDBToStringVisitor(); visitor.visit(expr); diff --git a/src/sqlancer/stonedb/gen/StoneDBExpressionGenerator.java b/src/sqlancer/stonedb/gen/StoneDBExpressionGenerator.java index 385154815..87876c5cc 100644 --- a/src/sqlancer/stonedb/gen/StoneDBExpressionGenerator.java +++ b/src/sqlancer/stonedb/gen/StoneDBExpressionGenerator.java @@ -2,11 +2,14 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; import sqlancer.IgnoreMeException; import sqlancer.Randomly; import sqlancer.common.ast.BinaryOperatorNode.Operator; +import sqlancer.common.ast.newast.ColumnReferenceNode; import sqlancer.common.ast.newast.NewBetweenOperatorNode; import sqlancer.common.ast.newast.NewBinaryOperatorNode; import sqlancer.common.ast.newast.NewCaseOperatorNode; @@ -32,25 +35,35 @@ public StoneDBExpressionGenerator(StoneDBGlobalState globalState) { private enum Expression { UNARY_PREFIX, UNARY_POSTFIX, BINARY_COMPARISON, BINARY_LOGICAL, BINARY_ARITHMETIC, BINARY_BITWISE, BETWEEN, IN, - CASE + NOT_IN, CASE } - public static class StoneDBCastOperation extends NewUnaryPostfixOperatorNode { + public static class StoneDBCastOperation implements Node { + Node expr; + StoneDBDataType type; public StoneDBCastOperation(Node expr, StoneDBDataType type) { - super(expr, () -> "::" + type.toString()); + this.expr = expr; + this.type = type; } + public Node getExpr() { + return expr; + } + + public StoneDBDataType getType() { + return type; + } } @Override public Node negatePredicate(Node predicate) { - return null; + return new NewUnaryPrefixOperatorNode<>(predicate, StoneDBUnaryPrefixOperator.NOT); } @Override public Node isNull(Node expr) { - return null; + return new NewUnaryPostfixOperatorNode<>(expr, StoneDBUnaryPostfixOperator.IS_NULL); } @Override @@ -111,6 +124,12 @@ protected Node generateExpression(int depth) { case BINARY_COMPARISON: op = StoneDBBinaryComparisonOperator.getRandom(); return new NewBinaryOperatorNode<>(generateExpression(depth + 1), generateExpression(depth + 1), op); + case IN: + return new NewInOperatorNode<>(generateExpression(depth + 1), + generateExpressions(Randomly.smallNumber() + 1, depth + 1), false); + case NOT_IN: + return new NewInOperatorNode<>(generateExpression(depth + 1), + generateExpressions(Randomly.smallNumber() + 1, depth + 1), true); case BINARY_LOGICAL: op = StoneDBBinaryLogicalOperator.getRandom(); return new NewBinaryOperatorNode<>(generateExpression(depth + 1), generateExpression(depth + 1), op); @@ -123,9 +142,6 @@ protected Node generateExpression(int depth) { case BETWEEN: return new NewBetweenOperatorNode<>(generateExpression(depth + 1), generateExpression(depth + 1), generateExpression(depth + 1), Randomly.getBoolean()); - case IN: - return new NewInOperatorNode<>(generateExpression(depth + 1), - generateExpressions(Randomly.smallNumber() + 1, depth + 1), Randomly.getBoolean()); case CASE: int nr = Randomly.smallNumber() + 1; return new NewCaseOperatorNode<>(generateExpression(depth + 1), generateExpressions(nr, depth + 1), @@ -137,7 +153,17 @@ protected Node generateExpression(int depth) { @Override protected Node generateColumn() { - return null; + StoneDBColumn column = Randomly.fromList(columns); + return new ColumnReferenceNode<>(column); + } + + protected List> generateColumns() { + int size = globalState.getRandomly().getInteger(1, columns.size()); + Set> set = new HashSet<>(); + while (set.size() < size) { + set.add(generateColumn()); + } + return new ArrayList<>(set); } public enum StoneDBAggregateFunction { @@ -160,7 +186,7 @@ public int getNrArgs() { public enum StoneDBUnaryPrefixOperator implements Operator { - NOT("NOT"), PLUS("+"), MINUS("-"); + NOT("NOT"), PLUS("+"), MINUS("-"), INVERSION("!"); private final String textRepr; @@ -203,8 +229,7 @@ public static StoneDBUnaryPostfixOperator getRandom() { */ public enum StoneDBBinaryComparisonOperator implements Operator { EQUAL("="), GREATER(">"), LESS("<"), GREATER_EQUAL(">="), LESS_EQUAL("<="), - NOT_EQUALS(Randomly.fromList(Arrays.asList("!=", "<>"))), NULL_SAFE_EQUAL("<=>"), IN("IN"), NOT_IN("NOT_IN"), - LIKE("LIKE"), IS_NULL("IS NULL"), IS_NOT_NULL("IS NOT NULL"); + NOT_EQUALS(Randomly.fromList(Arrays.asList("!=", "<>"))), NULL_SAFE_EQUAL("<=>"), LIKE("LIKE"); private final String textRepr; @@ -227,7 +252,7 @@ public String getTextRepresentation() { */ public enum StoneDBBinaryLogicalOperator implements Operator { - NOT("NOT"), AND("AND"), OR("OR"), XOR("XOR"); + AND("AND"), OR("OR"), XOR("XOR"); private final String textRepr; @@ -274,7 +299,7 @@ public String getTextRepresentation() { * Bitwise operators supported by StoneDB: https://stonedb.io/docs/SQL-reference/operators/bitwise-operators */ public enum StoneDBBinaryBitwiseOperator implements Operator { - AND("&"), OR("|"), XOR("^"), INVERSION("!"), LEFTSHIFT("<<"), RIGHTSHIFT(">>"); + AND("&"), OR("|"), XOR("^"), LEFTSHIFT("<<"), RIGHTSHIFT(">>"); private final String textRepr; diff --git a/src/sqlancer/stonedb/gen/StoneDBIndexCreateGenerator.java b/src/sqlancer/stonedb/gen/StoneDBIndexCreateGenerator.java index 3b7e63422..b50f07a6a 100644 --- a/src/sqlancer/stonedb/gen/StoneDBIndexCreateGenerator.java +++ b/src/sqlancer/stonedb/gen/StoneDBIndexCreateGenerator.java @@ -5,6 +5,7 @@ import sqlancer.common.query.ExpectedErrors; import sqlancer.common.query.SQLQueryAdapter; import sqlancer.stonedb.StoneDBProvider.StoneDBGlobalState; +import sqlancer.stonedb.StoneDBSchema.StoneDBColumn; import sqlancer.stonedb.StoneDBSchema.StoneDBTable; public class StoneDBIndexCreateGenerator { @@ -30,10 +31,12 @@ private SQLQueryAdapter getQuery() { sb.append(Randomly.fromOptions("UNIQUE", "FULLTEXT", "SPATIAL")); sb.append(" INDEX "); sb.append(globalState.getSchema().getFreeIndexName()); - appendIndexType(); + if (Randomly.getBoolean()) { + appendIndexType(); + } sb.append(" ON "); sb.append(table.getName()); - appendKeyPart(); + appendKeyParts(); appendIndexOption(); appendAlgoOrLockOption(); addExpectedErrors(); @@ -67,9 +70,20 @@ private void appendIndexType() { sb.append(Randomly.fromOptions("BTREE", "HASH")); } + private void appendKeyParts() { + int numberOfKeyParts = Randomly.fromOptions(1, 1, 1, 1, table.getColumns().size()); + for (int i = 0; i < numberOfKeyParts; i++) { + appendKeyPart(); + } + } + private void appendKeyPart() { sb.append("("); - sb.append(table.getRandomColumn().getName()); + StoneDBColumn randomColumn = table.getRandomColumn(); + sb.append(randomColumn.getName()); + if (Randomly.getBoolean()) { + sb.append(" (").append(Randomly.smallNumber()).append(")"); + } if (Randomly.getBoolean()) { sb.append(Randomly.fromOptions(" ASC", " DESC")); } diff --git a/src/sqlancer/stonedb/gen/StoneDBTableAlterGenerator.java b/src/sqlancer/stonedb/gen/StoneDBTableAlterGenerator.java index f25174948..b1e4872aa 100644 --- a/src/sqlancer/stonedb/gen/StoneDBTableAlterGenerator.java +++ b/src/sqlancer/stonedb/gen/StoneDBTableAlterGenerator.java @@ -1,24 +1,29 @@ package sqlancer.stonedb.gen; +import java.util.List; +import java.util.regex.Pattern; + import sqlancer.Randomly; import sqlancer.common.query.ExpectedErrors; import sqlancer.common.query.SQLQueryAdapter; import sqlancer.stonedb.StoneDBProvider.StoneDBGlobalState; import sqlancer.stonedb.StoneDBSchema.StoneDBColumn; -import sqlancer.stonedb.StoneDBSchema.StoneDBCompositeDataType; +import sqlancer.stonedb.StoneDBSchema.StoneDBDataType; import sqlancer.stonedb.StoneDBSchema.StoneDBTable; public class StoneDBTableAlterGenerator { private final StoneDBGlobalState globalState; private final StringBuilder sb = new StringBuilder(); + private final StoneDBTable table; ExpectedErrors errors = new ExpectedErrors(); enum Action { - ADD_COLUMN, ALTER_COLUMN, DROP_COLUMN, CHANGE_COLUMN, RENAME_COLUMN + ADD_COLUMN, ALTER_COLUMN, DROP_COLUMN, CHANGE_COLUMN } public StoneDBTableAlterGenerator(StoneDBGlobalState globalState) { this.globalState = globalState; + table = globalState.getSchema().getRandomTable(t -> !t.isView()); } public static SQLQueryAdapter generate(StoneDBGlobalState globalState) { @@ -27,19 +32,56 @@ public static SQLQueryAdapter generate(StoneDBGlobalState globalState) { private SQLQueryAdapter getQuery() { sb.append("ALTER TABLE "); - StoneDBTable table = globalState.getSchema().getRandomTable(t -> !t.isView()); - StoneDBExpressionGenerator generator = new StoneDBExpressionGenerator(globalState) - .setColumns(table.getColumns()); sb.append(table.getName()); sb.append(" "); - Action action = Randomly.fromOptions(Action.values()); + appendAlterOptions(); + addExpectedErrors(); + return new SQLQueryAdapter(sb.toString(), errors, true); + } + + private void addExpectedErrors() { + // com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: Data too long for column 'c0' at row 2 + errors.addRegex(Pattern.compile("Data truncation: Data too long for column 'c\\d{1,3}' at row \\d{1,3}")); + // java.sql.SQLSyntaxErrorException: Specified key was too long; max key length is 3072 bytes + errors.add("Specified key was too long; max key length is 3072 bytes"); + // java.sql.SQLSyntaxErrorException: You can't delete all columns with ALTER TABLE; use DROP TABLE instead + errors.add("You can't delete all columns with ALTER TABLE; use DROP TABLE instead"); + // java.sql.SQLSyntaxErrorException: Unknown column 'c0' in 't1' + errors.addRegex(Pattern.compile("Unknown column 'c\\d{1,3}' in 't\\d{1,3}'")); + // java.sql.SQLSyntaxErrorException: BLOB, TEXT, GEOMETRY or JSON column 'c0' can't have a default value + errors.addRegex(Pattern.compile("BLOB, TEXT, GEOMETRY or JSON column 'c\\d{1,3}' can't have a default value")); + // java.sql.SQLSyntaxErrorException: Column length too big for column 'c91' (max = 16383); use BLOB or TEXT + // instead + errors.addRegex(Pattern + .compile("Column length too big for column 'c\\d{1,3}' \\(max = 16383\\); use BLOB or TEXT instead")); + } + + private void appendAlterOptions() { + List actions; + if (Randomly.getBooleanWithSmallProbability()) { + actions = Randomly.subset(Action.values()); + } else { + actions = List.of(Randomly.fromOptions(Action.values())); + } + for (Action action : actions) { + appendAlterOption(action); + sb.append(" "); + } + } + + private void appendAlterOption(Action action) { + StoneDBExpressionGenerator generator = new StoneDBExpressionGenerator(globalState) + .setColumns(table.getColumns()); switch (action) { case ADD_COLUMN: sb.append("ADD COLUMN "); String columnName = table.getFreeColumnName(); - sb.append(columnName); - sb.append(" "); - sb.append(StoneDBCompositeDataType.getRandomWithoutNull().getPrimitiveDataType().toString()); + sb.append(" ").append(columnName).append(" "); + sb.append(StoneDBDataType.getTypeAndValue(StoneDBDataType.getRandomWithoutNull())); + // java.sql.SQLSyntaxErrorException: Column length too big for column 'c1' (max = 16383); use BLOB or TEXT + // instead + errors.addRegex(Pattern + .compile("Column length too big for column 'c\\d{1,3}' (max = 16383); use BLOB or TEXT instead")); if (Randomly.getBoolean()) { if (Randomly.getBoolean()) { sb.append(" FIRST"); @@ -63,17 +105,17 @@ private SQLQueryAdapter getQuery() { } else { sb.append(" DROP DEFAULT"); } - if (Randomly.getBoolean()) { - sb.append(" SET ").append(Randomly.fromOptions("VISIBLE", "INVISIBLE")); - } break; case CHANGE_COLUMN: sb.append(Randomly.fromOptions("CHANGE COLUMN ", "CHANGE ")); String oldColumnName = table.getRandomColumn().getName(); String newColumnName = table.getFreeColumnName(); - sb.append(oldColumnName).append(" ").append(newColumnName); - sb.append(" "); - sb.append(StoneDBCompositeDataType.getRandomWithoutNull().getPrimitiveDataType().toString()); + sb.append(oldColumnName).append(" ").append(newColumnName).append(" "); + sb.append(StoneDBDataType.getTypeAndValue(StoneDBDataType.getRandomWithoutNull())); + // java.sql.SQLSyntaxErrorException: Column length too big for column 'c1' (max = 16383); use BLOB or TEXT + // instead + errors.addRegex(Pattern + .compile("Column length too big for column 'c\\d{1,3}' (max = 16383); use BLOB or TEXT instead")); if (Randomly.getBoolean()) { if (Randomly.getBoolean()) { sb.append(" FIRST"); @@ -83,21 +125,8 @@ private SQLQueryAdapter getQuery() { } } break; - case RENAME_COLUMN: - sb.append("RENAME COLUMN "); - sb.append(table.getRandomColumn().getName()); - sb.append(" TO "); - sb.append(table.getFreeColumnName()); - break; default: throw new AssertionError(action); } - addExpectedErrors(); - return new SQLQueryAdapter(sb.toString(), errors, true); - } - - private void addExpectedErrors() { - // java.sql.SQLSyntaxErrorException: You can't delete all columns with ALTER TABLE; use DROP TABLE instead - errors.add("You can't delete all columns with ALTER TABLE; use DROP TABLE instead"); } } diff --git a/src/sqlancer/stonedb/gen/StoneDBTableCreateGenerator.java b/src/sqlancer/stonedb/gen/StoneDBTableCreateGenerator.java index 622c86c3c..3b87e99d5 100644 --- a/src/sqlancer/stonedb/gen/StoneDBTableCreateGenerator.java +++ b/src/sqlancer/stonedb/gen/StoneDBTableCreateGenerator.java @@ -1,12 +1,13 @@ package sqlancer.stonedb.gen; import static sqlancer.stonedb.gen.StoneDBTableCreateGenerator.ColumnOptions.PRIMARY_KEY; -import static sqlancer.stonedb.gen.StoneDBTableCreateGenerator.ColumnOptions.UNIQUE; +import static sqlancer.stonedb.gen.StoneDBTableCreateGenerator.ColumnOptions.UNIQUE_KEY; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.regex.Pattern; import sqlancer.Randomly; import sqlancer.Randomly.StringGenerationStrategy; @@ -41,7 +42,7 @@ public static SQLQueryAdapter generate(StoneDBGlobalState globalState, String ta } public SQLQueryAdapter getQuery() { - sb.append(Randomly.fromOptions("CREATE TABLE ", "CREATE TEMPORARY TABLE ")); + sb.append(Randomly.fromOptions("CREATE TABLE "/* , "CREATE TEMPORARY TABLE " */)); if (Randomly.getBoolean()) { sb.append("IF NOT EXISTS "); } @@ -52,8 +53,10 @@ public SQLQueryAdapter getQuery() { sb.append(schema.getRandomTable().getName()); } else { appendColumns(); - sb.append(" "); - appendTableOptions(); + if (Randomly.getBoolean()) { + sb.append(" "); + appendTableOptions(); + } } addExpectedErrors(); return new SQLQueryAdapter(sb.toString(), errors, true); @@ -71,6 +74,8 @@ private void addExpectedErrors() { // java.sql.SQLSyntaxErrorException: Column length too big for column 'c1' (max = 16383); use BLOB or TEXT // instead errors.add("Column length too big for column"); + // BLOB/TEXT column 'c0' used in key specification without a key length + errors.addRegex(Pattern.compile("BLOB/TEXT column 'c.*' used in key specification without a key length")); } private enum TableOptions { @@ -187,7 +192,7 @@ private void appendColumnDefinition() { } protected enum ColumnOptions { - NULL_OR_NOT_NULL, UNIQUE, COMMENT, COLUMN_FORMAT, STORAGE, PRIMARY_KEY + NULL_OR_NOT_NULL, PRIMARY_KEY, UNIQUE_KEY, COMMENT, COLUMN_FORMAT, STORAGE } private void appendColumnOption(StoneDBDataType type) { @@ -200,13 +205,13 @@ private void appendColumnOption(StoneDBDataType type) { // tableHasNullableColumn = true; // } // only use one key, unique key or primary key, but not both - if (columnOptions.contains(PRIMARY_KEY) && columnOptions.contains(UNIQUE)) { - columnOptions.remove(Randomly.fromOptions(PRIMARY_KEY, UNIQUE)); + if (columnOptions.contains(PRIMARY_KEY) && columnOptions.contains(UNIQUE_KEY)) { + columnOptions.remove(Randomly.fromOptions(PRIMARY_KEY, UNIQUE_KEY)); } if (isTextType) { // TODO: restriction due to the limited key length columnOptions.remove(PRIMARY_KEY); - columnOptions.remove(UNIQUE); + columnOptions.remove(UNIQUE_KEY); } for (ColumnOptions o : columnOptions) { sb.append(" "); @@ -223,7 +228,7 @@ private void appendColumnOption(StoneDBDataType type) { sb.append("NOT NULL"); } break; - case UNIQUE: + case UNIQUE_KEY: sb.append("UNIQUE"); if (Randomly.getBoolean()) { sb.append(" KEY"); @@ -256,108 +261,6 @@ private void appendColumnOption(StoneDBDataType type) { } 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").append(Randomly.fromOptions("", "(" + r.getInteger(0, 255) + ")")); - break; - case VARCHAR: - sb.append("VARCHAR").append("(").append(r.getInteger(0, 65535)).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").append("(").append(r.getInteger(0, 65535)).append(")"); - 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; - default: - throw new AssertionError(); - } - } - - public static void optionallyAddPrecisionAndScale(StringBuilder sb) { - if (Randomly.getBoolean()) { - 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(")"); - } + sb.append(StoneDBDataType.getTypeAndValue(randomType)); } } diff --git a/src/sqlancer/stonedb/gen/StoneDBTableDeleteGenerator.java b/src/sqlancer/stonedb/gen/StoneDBTableDeleteGenerator.java index 3ff809740..ab0828d30 100644 --- a/src/sqlancer/stonedb/gen/StoneDBTableDeleteGenerator.java +++ b/src/sqlancer/stonedb/gen/StoneDBTableDeleteGenerator.java @@ -45,7 +45,7 @@ public SQLQueryAdapter getQuery() { sb.append(StoneDBToStringVisitor.asString(new StoneDBExpressionGenerator(globalState) .setColumns(randomTable.getColumns()).generateExpression())); } - if (Randomly.getBoolean()) { + if (!StoneDBBugs.bug1933 && Randomly.getBoolean()) { sb.append(" ORDER BY "); sb.append(String.join(", ", Randomly.fromOptions( randomTable.getColumns().stream().map(AbstractTableColumn::getName).collect(Collectors.toList()))) @@ -55,13 +55,6 @@ public SQLQueryAdapter getQuery() { sb.append(" LIMIT "); sb.append(r.getInteger(0, (int) randomTable.getNrRows(globalState))); } - addExpectedErrors(); return new SQLQueryAdapter(sb.toString(), errors); } - - private void addExpectedErrors() { - if (StoneDBBugs.bug1933) { - errors.add("assert failed on i < m_idx.size() at tianmu_attr.h:387, msg: [bad dpn index 0/0]"); - } - } } diff --git a/src/sqlancer/stonedb/gen/StoneDBTableInsertGenerator.java b/src/sqlancer/stonedb/gen/StoneDBTableInsertGenerator.java index 7a0e3847f..2fdb7b6fa 100644 --- a/src/sqlancer/stonedb/gen/StoneDBTableInsertGenerator.java +++ b/src/sqlancer/stonedb/gen/StoneDBTableInsertGenerator.java @@ -1,6 +1,7 @@ package sqlancer.stonedb.gen; import java.util.List; +import java.util.regex.Pattern; import java.util.stream.Collectors; import sqlancer.Randomly; @@ -57,6 +58,10 @@ private void addExpectedErrors() { errors.add("Insert duplicate key on row: "); // com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: Incorrect datetime value: errors.add("Data truncation: Incorrect datetime value: "); + // java.sql.SQLException: Field 'c0' doesn't have a default value + errors.add("doesn't have a default value"); + // java.sql.SQLException: Data truncated for column 'c0' at row 1 + errors.addRegex(Pattern.compile("Data truncated for column 'c.*' at row .*")); } private void appendPartition() { diff --git a/src/sqlancer/stonedb/gen/StoneDBTableUpdateGenerator.java b/src/sqlancer/stonedb/gen/StoneDBTableUpdateGenerator.java index bff098a64..4812a6ee3 100644 --- a/src/sqlancer/stonedb/gen/StoneDBTableUpdateGenerator.java +++ b/src/sqlancer/stonedb/gen/StoneDBTableUpdateGenerator.java @@ -49,8 +49,11 @@ public void appendAssignmentList() { } sb.append(table.getColumns().get(i)); sb.append(" = "); - sb.append(StoneDBToStringVisitor.asString(StoneDBSchema.StoneDBDataType - .getRandomValue(table.getColumns().get(i).getType().getPrimitiveDataType()))); + sb.append( + Randomly.fromOptions( + StoneDBToStringVisitor.asString(StoneDBSchema.StoneDBDataType + .getRandomValue(table.getColumns().get(i).getType().getPrimitiveDataType())), + "DEFAULT")); } } diff --git a/src/sqlancer/stonedb/oracle/StoneDBNoRECOracle.java b/src/sqlancer/stonedb/oracle/StoneDBNoRECOracle.java index 1d3dac312..2625bbb7c 100644 --- a/src/sqlancer/stonedb/oracle/StoneDBNoRECOracle.java +++ b/src/sqlancer/stonedb/oracle/StoneDBNoRECOracle.java @@ -11,6 +11,7 @@ import sqlancer.Randomly; import sqlancer.SQLConnection; import sqlancer.common.ast.newast.ColumnReferenceNode; +import sqlancer.common.ast.newast.NewBinaryOperatorNode; import sqlancer.common.ast.newast.NewPostfixTextNode; import sqlancer.common.ast.newast.Node; import sqlancer.common.ast.newast.TableReferenceNode; @@ -29,6 +30,7 @@ import sqlancer.stonedb.ast.StoneDBJoin; import sqlancer.stonedb.ast.StoneDBSelect; import sqlancer.stonedb.gen.StoneDBExpressionGenerator; +import sqlancer.stonedb.gen.StoneDBExpressionGenerator.StoneDBBinaryLogicalOperator; import sqlancer.stonedb.gen.StoneDBExpressionGenerator.StoneDBCastOperation; public class StoneDBNoRECOracle extends NoRECBase implements TestOracle { @@ -65,14 +67,16 @@ public void check() throws Exception { private int getUnoptimizedQueryCount(List> tableList, Node randomWhereCondition, List> joins) throws SQLException { StoneDBSelect select = new StoneDBSelect(); - Node asText = new NewPostfixTextNode<>(new StoneDBCastOperation( - new NewPostfixTextNode<>(randomWhereCondition, - " IS NOT NULL AND " + StoneDBToStringVisitor.asString(randomWhereCondition)), - StoneDBDataType.INT), "as count"); + Node asText = new NewPostfixTextNode<>( + new StoneDBCastOperation( + new NewBinaryOperatorNode<>(new NewPostfixTextNode<>(randomWhereCondition, " IS NOT NULL "), + randomWhereCondition, StoneDBBinaryLogicalOperator.AND), + StoneDBDataType.INT), + " as count"); select.setFetchColumns(List.of(asText)); select.setFromList(tableList); select.setJoinList(joins); - unoptimizedQueryString = "SELECT SUM(count) FROM (" + StoneDBToStringVisitor.asString(select) + ") as res"; + unoptimizedQueryString = "SELECT SUM(count) FROM (" + StoneDBToStringVisitor.asString(select) + ") as res;"; SQLQueryAdapter q = new SQLQueryAdapter(unoptimizedQueryString, errors); SQLancerResultSet rs; try { @@ -104,14 +108,16 @@ private int getOptimizedQueryCount(SQLConnection con, List