|
1 | 1 | package sqlancer.oceanbase.oracle; |
2 | 2 |
|
3 | 3 | import java.sql.SQLException; |
4 | | -import java.util.Arrays; |
5 | | -import java.util.Collections; |
6 | | -import java.util.List; |
7 | | -import java.util.stream.Collectors; |
8 | 4 |
|
9 | | -import sqlancer.Randomly; |
10 | | -import sqlancer.common.oracle.NoRECBase; |
| 5 | +import sqlancer.Reproducer; |
| 6 | +import sqlancer.common.oracle.NoRECOracle; |
11 | 7 | import sqlancer.common.oracle.TestOracle; |
12 | | -import sqlancer.common.query.SQLQueryAdapter; |
13 | | -import sqlancer.common.query.SQLancerResultSet; |
| 8 | +import sqlancer.common.query.ExpectedErrors; |
| 9 | +import sqlancer.oceanbase.OceanBaseErrors; |
14 | 10 | import sqlancer.oceanbase.OceanBaseGlobalState; |
15 | 11 | import sqlancer.oceanbase.OceanBaseSchema; |
16 | | -import sqlancer.oceanbase.OceanBaseVisitor; |
17 | | -import sqlancer.oceanbase.ast.OceanBaseAggregate; |
18 | | -import sqlancer.oceanbase.ast.OceanBaseBinaryLogicalOperation; |
19 | | -import sqlancer.oceanbase.ast.OceanBaseColumnName; |
20 | | -import sqlancer.oceanbase.ast.OceanBaseComputableFunction; |
21 | | -import sqlancer.oceanbase.ast.OceanBaseComputableFunction.OceanBaseFunction; |
22 | | -import sqlancer.oceanbase.ast.OceanBaseConstant; |
| 12 | +import sqlancer.oceanbase.OceanBaseSchema.OceanBaseColumn; |
| 13 | +import sqlancer.oceanbase.OceanBaseSchema.OceanBaseTable; |
23 | 14 | import sqlancer.oceanbase.ast.OceanBaseExpression; |
| 15 | +import sqlancer.oceanbase.ast.OceanBaseJoin; |
24 | 16 | import sqlancer.oceanbase.ast.OceanBaseSelect; |
25 | | -import sqlancer.oceanbase.ast.OceanBaseTableReference; |
26 | | -import sqlancer.oceanbase.ast.OceanBaseText; |
27 | | -import sqlancer.oceanbase.ast.OceanBaseUnaryPostfixOperation; |
28 | | -import sqlancer.oceanbase.ast.OceanBaseUnaryPrefixOperation; |
29 | 17 | import sqlancer.oceanbase.gen.OceanBaseExpressionGenerator; |
30 | 18 |
|
31 | | -public class OceanBaseNoRECOracle extends NoRECBase<OceanBaseGlobalState> implements TestOracle<OceanBaseGlobalState> { |
| 19 | +public class OceanBaseNoRECOracle implements TestOracle<OceanBaseGlobalState> { |
32 | 20 |
|
33 | | - // SELECT COUNT(*) FROM t0 WHERE <cond>; |
34 | | - // SELECT SUM(count) FROM (SELECT <cond> IS TRUE as count FROM t0); |
35 | | - // SELECT (SELECT COUNT(*) FROM t0 WHERE c0 IS NOT 0) = (SELECT COUNT(*) FROM |
36 | | - // (SELECT c0 is NOT 0 FROM t0)); |
37 | | - private final OceanBaseSchema s; |
38 | | - private String firstQueryString; |
39 | | - private static final int NOT_FOUND = -1; |
40 | | - |
41 | | - private enum Option { |
42 | | - TRUE, FALSE_NULL, NOT_NOT_TRUE, NOT_FALSE_NOT_NULL, IF, IFNULL, COALESCE |
43 | | - }; |
| 21 | + NoRECOracle<OceanBaseSelect, OceanBaseJoin, OceanBaseExpression, OceanBaseSchema, OceanBaseTable, OceanBaseColumn, OceanBaseGlobalState> oracle; |
44 | 22 |
|
45 | 23 | public OceanBaseNoRECOracle(OceanBaseGlobalState globalState) { |
46 | | - super(globalState); |
47 | | - this.s = globalState.getSchema(); |
48 | | - errors.add("is out of range"); |
49 | | - // regex |
50 | | - errors.add("unmatched parentheses"); |
51 | | - errors.add("nothing to repeat at offset"); |
52 | | - errors.add("missing )"); |
53 | | - errors.add("missing terminating ]"); |
54 | | - errors.add("range out of order in character class"); |
55 | | - errors.add("unrecognized character after "); |
56 | | - errors.add("Got error '(*VERB) not recognized or malformed"); |
57 | | - errors.add("must be followed by"); |
58 | | - errors.add("malformed number or name after"); |
59 | | - errors.add("digit expected after"); |
| 24 | + OceanBaseExpressionGenerator gen = new OceanBaseExpressionGenerator(globalState); |
| 25 | + ExpectedErrors errors = ExpectedErrors.newErrors().with(OceanBaseErrors.getExpressionErrors()) |
| 26 | + .withRegex(OceanBaseErrors.getExpressionErrorsRegex()) |
| 27 | + .with("canceling statement due to statement timeout").with("unmatched parentheses") |
| 28 | + .with("nothing to repeat at offset").with("missing )").with("missing terminating ]") |
| 29 | + .with("range out of order in character class").with("unrecognized character after ") |
| 30 | + .with("Got error '(*VERB) not recognized or malformed").with("must be followed by") |
| 31 | + .with("malformed number or name after").with("digit expected after").build(); |
| 32 | + this.oracle = new NoRECOracle<>(globalState, gen, errors); |
60 | 33 | } |
61 | 34 |
|
62 | 35 | @Override |
63 | 36 | public void check() throws SQLException { |
64 | | - OceanBaseSchema.OceanBaseTable randomTable = s.getRandomTable(); |
65 | | - List<OceanBaseSchema.OceanBaseColumn> columns = randomTable.getColumns(); |
66 | | - OceanBaseExpressionGenerator gen = new OceanBaseExpressionGenerator(state).setColumns(columns); |
67 | | - OceanBaseExpression randomWhereCondition = gen.generateExpression(); |
68 | | - List<OceanBaseExpression> groupBys = Collections.emptyList(); // getRandomExpressions(columns); |
69 | | - List<OceanBaseExpression> tableList = Arrays.asList(randomTable).stream() |
70 | | - .map(t -> new OceanBaseTableReference(t)).collect(Collectors.toList()); |
71 | | - int firstCount = getFirstQueryCount(tableList, randomWhereCondition, groupBys); |
72 | | - int secondCount = getSecondQuery(tableList, randomWhereCondition, groupBys); |
73 | | - if (firstCount != secondCount && firstCount != NOT_FOUND && secondCount != NOT_FOUND) { |
74 | | - String queryFormatString = "-- %s;\n-- count: %d"; |
75 | | - String firstQueryStringWithCount = String.format(queryFormatString, optimizedQueryString, firstCount); |
76 | | - String secondQueryStringWithCount = String.format(queryFormatString, unoptimizedQueryString, secondCount); |
77 | | - state.getState().getLocalState() |
78 | | - .log(String.format("%s\n%s", firstQueryStringWithCount, secondQueryStringWithCount)); |
79 | | - String assertionMessage = String.format("the counts mismatch (%d and %d)!\n%s\n%s", firstCount, secondCount, |
80 | | - firstQueryStringWithCount, secondQueryStringWithCount); |
81 | | - throw new AssertionError(assertionMessage); |
82 | | - } |
| 37 | + oracle.check(); |
83 | 38 | } |
84 | 39 |
|
85 | | - private int getSecondQuery(List<OceanBaseExpression> tableList, OceanBaseExpression randomWhereCondition, |
86 | | - List<OceanBaseExpression> groupBys) throws SQLException { |
87 | | - OceanBaseSelect select = new OceanBaseSelect(); |
88 | | - select.setGroupByClause(groupBys); |
89 | | - OceanBaseExpression expr = getTrueExpr(randomWhereCondition); |
90 | | - |
91 | | - OceanBaseText asText = new OceanBaseText(expr, " as count", false); |
92 | | - select.setFetchColumns(Arrays.asList(asText)); |
93 | | - select.setFromList(tableList); |
94 | | - select.setSelectType(OceanBaseSelect.SelectType.ALL); |
95 | | - int secondCount = 0; |
96 | | - |
97 | | - unoptimizedQueryString = "SELECT SUM(count) FROM (" + OceanBaseVisitor.asString(select) + ") as asdf"; |
98 | | - SQLQueryAdapter q = new SQLQueryAdapter(unoptimizedQueryString, errors); |
99 | | - SQLancerResultSet rs; |
100 | | - if (options.logEachSelect()) { |
101 | | - logger.writeCurrent(unoptimizedQueryString); |
102 | | - } |
103 | | - try { |
104 | | - rs = q.executeAndGet(state); |
105 | | - } catch (Exception e) { |
106 | | - throw new AssertionError(optimizedQueryString, e); |
107 | | - } |
108 | | - if (rs == null) { |
109 | | - return -1; |
110 | | - } |
111 | | - if (rs.next()) { |
112 | | - secondCount += rs.getLong(1); |
113 | | - } |
114 | | - rs.close(); |
115 | | - return secondCount; |
116 | | - } |
117 | | - |
118 | | - private int getFirstQueryCount(List<OceanBaseExpression> tableList, OceanBaseExpression randomWhereCondition, |
119 | | - List<OceanBaseExpression> groupBys) throws SQLException { |
120 | | - OceanBaseSelect select = new OceanBaseSelect(); |
121 | | - select.setGroupByClause(groupBys); |
122 | | - // SELECT COUNT(t1.c3) FROM t1 WHERE (- (t1.c2)); |
123 | | - // SELECT SUM(count) FROM (SELECT ((- (t1.c2)) IS TRUE) as count FROM t1);; |
124 | | - OceanBaseAggregate aggr = new OceanBaseAggregate(new OceanBaseColumnName( |
125 | | - new OceanBaseSchema.OceanBaseColumn("*", OceanBaseSchema.OceanBaseDataType.INT, false, 0, false)), |
126 | | - OceanBaseAggregate.OceanBaseAggregateFunction.COUNT); |
127 | | - select.setFetchColumns(Arrays.asList(aggr)); |
128 | | - select.setFromList(tableList); |
129 | | - select.setWhereClause(randomWhereCondition); |
130 | | - select.setSelectType(OceanBaseSelect.SelectType.ALL); |
131 | | - int firstCount = 0; |
132 | | - optimizedQueryString = OceanBaseVisitor.asString(select); |
133 | | - SQLQueryAdapter q = new SQLQueryAdapter(optimizedQueryString, errors); |
134 | | - SQLancerResultSet rs; |
135 | | - if (options.logEachSelect()) { |
136 | | - logger.writeCurrent(optimizedQueryString); |
137 | | - } |
138 | | - try { |
139 | | - rs = q.executeAndGet(state); |
140 | | - } catch (Exception e) { |
141 | | - throw new AssertionError(firstQueryString, e); |
142 | | - } |
143 | | - if (rs == null) { |
144 | | - return -1; |
145 | | - } |
146 | | - if (rs.next()) { |
147 | | - firstCount += rs.getLong(1); |
148 | | - } |
149 | | - rs.close(); |
150 | | - return firstCount; |
| 40 | + @Override |
| 41 | + public Reproducer<OceanBaseGlobalState> getLastReproducer() { |
| 42 | + return oracle.getLastReproducer(); |
151 | 43 | } |
152 | 44 |
|
153 | | - private OceanBaseExpression getTrueExpr(OceanBaseExpression randomWhereCondition) { |
154 | | - // we can treat "is true" as combinations of "is flase" and "not","is not true" and "not",etc. |
155 | | - OceanBaseUnaryPostfixOperation isTrue = new OceanBaseUnaryPostfixOperation(randomWhereCondition, |
156 | | - OceanBaseUnaryPostfixOperation.UnaryPostfixOperator.IS_TRUE, false); |
157 | | - |
158 | | - OceanBaseUnaryPostfixOperation isFalse = new OceanBaseUnaryPostfixOperation(randomWhereCondition, |
159 | | - OceanBaseUnaryPostfixOperation.UnaryPostfixOperator.IS_FALSE, false); |
160 | | - |
161 | | - OceanBaseUnaryPostfixOperation isNotFalse = new OceanBaseUnaryPostfixOperation(randomWhereCondition, |
162 | | - OceanBaseUnaryPostfixOperation.UnaryPostfixOperator.IS_FALSE, true); |
163 | | - |
164 | | - OceanBaseUnaryPostfixOperation isNULL = new OceanBaseUnaryPostfixOperation(randomWhereCondition, |
165 | | - OceanBaseUnaryPostfixOperation.UnaryPostfixOperator.IS_NULL, false); |
166 | | - |
167 | | - OceanBaseUnaryPostfixOperation isNotNULL = new OceanBaseUnaryPostfixOperation(randomWhereCondition, |
168 | | - OceanBaseUnaryPostfixOperation.UnaryPostfixOperator.IS_NULL, true); |
169 | | - |
170 | | - OceanBaseExpression expr = OceanBaseConstant.createNullConstant(); |
171 | | - Option a = Randomly.fromOptions(Option.values()); |
172 | | - switch (a) { |
173 | | - case TRUE: |
174 | | - expr = isTrue; |
175 | | - break; |
176 | | - case FALSE_NULL: |
177 | | - // not((is false) or (is null)) |
178 | | - expr = new OceanBaseUnaryPrefixOperation( |
179 | | - new OceanBaseBinaryLogicalOperation(isFalse, isNULL, |
180 | | - OceanBaseBinaryLogicalOperation.OceanBaseBinaryLogicalOperator.OR), |
181 | | - OceanBaseUnaryPrefixOperation.OceanBaseUnaryPrefixOperator.NOT); |
182 | | - break; |
183 | | - case NOT_NOT_TRUE: |
184 | | - // not(not(is true))) |
185 | | - expr = new OceanBaseUnaryPrefixOperation( |
186 | | - new OceanBaseUnaryPrefixOperation(isTrue, |
187 | | - OceanBaseUnaryPrefixOperation.OceanBaseUnaryPrefixOperator.NOT), |
188 | | - OceanBaseUnaryPrefixOperation.OceanBaseUnaryPrefixOperator.NOT); |
189 | | - break; |
190 | | - case NOT_FALSE_NOT_NULL: |
191 | | - // (is not false) and (is not null) |
192 | | - expr = new OceanBaseBinaryLogicalOperation(isNotFalse, isNotNULL, |
193 | | - OceanBaseBinaryLogicalOperation.OceanBaseBinaryLogicalOperator.AND); |
194 | | - break; |
195 | | - case IF: |
196 | | - // if(1, xx is true, 0) |
197 | | - OceanBaseExpression[] args = new OceanBaseExpression[3]; |
198 | | - args[0] = OceanBaseConstant.createIntConstant(1); |
199 | | - args[1] = isTrue; |
200 | | - args[2] = OceanBaseConstant.createIntConstant(0); |
201 | | - expr = new OceanBaseComputableFunction(OceanBaseFunction.IF, args); |
202 | | - break; |
203 | | - case IFNULL: |
204 | | - // ifnull(null, xx is true) |
205 | | - OceanBaseExpression[] ifArgs = new OceanBaseExpression[2]; |
206 | | - ifArgs[0] = OceanBaseConstant.createNullConstant(); |
207 | | - ifArgs[1] = isTrue; |
208 | | - expr = new OceanBaseComputableFunction(OceanBaseFunction.IFNULL, ifArgs); |
209 | | - break; |
210 | | - case COALESCE: |
211 | | - // coalesce(null, xx is true) |
212 | | - OceanBaseExpression[] coalesceArgs = new OceanBaseExpression[2]; |
213 | | - coalesceArgs[0] = OceanBaseConstant.createNullConstant(); |
214 | | - coalesceArgs[1] = isTrue; |
215 | | - expr = new OceanBaseComputableFunction(OceanBaseFunction.COALESCE, coalesceArgs); |
216 | | - break; |
217 | | - default: |
218 | | - expr = isTrue; |
219 | | - break; |
220 | | - } |
221 | | - return expr; |
| 45 | + @Override |
| 46 | + public String getLastQueryString() { |
| 47 | + return oracle.getLastQueryString(); |
222 | 48 | } |
223 | | - |
224 | 49 | } |
0 commit comments