1111
1212import sqlancer .IgnoreMeException ;
1313import sqlancer .Randomly ;
14+ import sqlancer .StateToReproduce .OracleRunReproductionState ;
1415import sqlancer .common .oracle .PivotedQuerySynthesisBase ;
1516import sqlancer .common .query .Query ;
1617import sqlancer .common .query .QueryAdapter ;
@@ -46,29 +47,32 @@ public class SQLite3PivotedQuerySynthesisOracle
4647 extends PivotedQuerySynthesisBase <SQLite3GlobalState , SQLite3RowValue , SQLite3Expression > {
4748
4849 private List <SQLite3Column > fetchColumns ;
49- private List <SQLite3Expression > colExpressions ;
50+ private List <SQLite3Expression > pivotRowExpression ;
51+ private OracleRunReproductionState localState ;
5052
51- public SQLite3PivotedQuerySynthesisOracle (SQLite3GlobalState globalState ) throws SQLException {
53+ public SQLite3PivotedQuerySynthesisOracle (SQLite3GlobalState globalState ) {
5254 super (globalState );
5355 }
5456
5557 @ Override
5658 public Query getQueryThatContainsAtLeastOneRow () throws SQLException {
57- SQLite3Select selectStatement = getQuery (globalState );
59+ SQLite3Select selectStatement = getQuery ();
5860 SQLite3ToStringVisitor visitor = new SQLite3ToStringVisitor ();
5961 visitor .visit (selectStatement );
6062 String queryString = visitor .get ();
6163 SQLite3Errors .addExpectedExpressionErrors (errors );
6264 return new QueryAdapter (queryString , errors );
6365 }
6466
65- public SQLite3Select getQuery (SQLite3GlobalState globalState ) throws SQLException {
66- if ( globalState .getSchema ().getDatabaseTables ().isEmpty ()) {
67- throw new IgnoreMeException ();
68- }
67+ public SQLite3Select getQuery () throws SQLException {
68+ assert ! globalState .getSchema ().getDatabaseTables ().isEmpty ();
69+ localState = globalState . getState (). getLocalState ();
70+ assert localState != null ;
6971 SQLite3Tables randomFromTables = globalState .getSchema ().getRandomTableNonEmptyTables ();
7072 List <SQLite3Table > tables = randomFromTables .getTables ();
7173
74+ pivotRow = randomFromTables .getRandomRowValue (globalState .getConnection ());
75+
7276 globalState .getState ().queryTargetedTablesString = randomFromTables .tableNamesAsString ();
7377 SQLite3Select selectStatement = new SQLite3Select ();
7478 selectStatement .setSelectType (Randomly .fromOptions (SQLite3Select .SelectType .values ()));
@@ -78,10 +82,8 @@ public SQLite3Select getQuery(SQLite3GlobalState globalState) throws SQLExceptio
7882 columns .add (t .getRowid ());
7983 }
8084 }
81- pivotRow = randomFromTables .getRandomRowValue (globalState .getConnection ());
8285
8386 List <Join > joinStatements = getJoinStatements (globalState , tables , columns );
84-
8587 selectStatement .setJoinClauses (joinStatements );
8688 selectStatement .setFromTables (SQLite3Common .getTableRefs (tables , globalState .getSchema ()));
8789
@@ -91,41 +93,14 @@ public SQLite3Select getQuery(SQLite3GlobalState globalState) throws SQLExceptio
9193 List <SQLite3Column > columnsWithoutRowid = columns .stream ()
9294 .filter (c -> !SQLite3Schema .ROWID_STRINGS .contains (c .getName ())).collect (Collectors .toList ());
9395 fetchColumns = Randomly .nonEmptySubset (columnsWithoutRowid );
94- colExpressions = new ArrayList <>();
9596 List <SQLite3Table > allTables = new ArrayList <>();
9697 allTables .addAll (tables );
9798 allTables .addAll (joinStatements .stream ().map (join -> join .getTable ()).collect (Collectors .toList ()));
9899 boolean allTablesContainOneRow = allTables .stream ().allMatch (t -> t .getNrRows () == 1 );
99- for (SQLite3Column c : fetchColumns ) {
100- SQLite3Expression colName = new SQLite3ColumnName (c , pivotRow .getValues ().get (c ));
101- if (allTablesContainOneRow && Randomly .getBoolean ()) {
102- boolean generateDistinct = Randomly .getBoolean ();
103- if (generateDistinct ) {
104- colName = new SQLite3Distinct (colName );
105- }
106- SQLite3AggregateFunction aggFunc = SQLite3AggregateFunction .getRandom (c .getType ());
107- colName = new SQLite3Aggregate (Arrays .asList (colName ), aggFunc );
108- if (Randomly .getBoolean () && !generateDistinct ) {
109- colName = generateWindowFunction (columns , colName , true );
110- }
111- errors .add ("second argument to nth_value must be a positive integer" );
112- }
113- if (Randomly .getBoolean ()) {
114- SQLite3Expression randomExpression = new SQLite3ExpressionGenerator (globalState ).setColumns (columns )
115- .generateResultKnownExpression ();
116- colExpressions .add (randomExpression );
117- } else {
118- colExpressions .add (colName );
119- }
120- }
121- if (Randomly .getBoolean () && allTablesContainOneRow ) {
122- SQLite3WindowFunction windowFunction = SQLite3WindowFunction .getRandom (columnsWithoutRowid , globalState );
123- SQLite3Expression windowExpr = generateWindowFunction (columnsWithoutRowid , windowFunction , false );
124- colExpressions .add (windowExpr );
125- }
126- selectStatement .setFetchColumns (colExpressions );
127- globalState .getState ().queryTargetedColumnsString = fetchColumns .stream ().map (c -> c .getFullQualifiedName ())
128- .collect (Collectors .joining (", " ));
100+ pivotRowExpression = getColExpressions (allTablesContainOneRow , columns , columnsWithoutRowid );
101+ selectStatement .setFetchColumns (pivotRowExpression );
102+ localState .log ("queryTargetedColumnsString: "
103+ + fetchColumns .stream ().map (c -> c .getFullQualifiedName ()).collect (Collectors .joining (", " )));
129104 SQLite3Expression whereClause = generateRectifiedExpression (columns , pivotRow );
130105 selectStatement .setWhereClause (whereClause );
131106 List <SQLite3Expression > groupByClause = generateGroupByClause (columns , pivotRow , allTablesContainOneRow );
@@ -137,8 +112,8 @@ public SQLite3Select getQuery(SQLite3GlobalState globalState) throws SQLExceptio
137112 SQLite3Expression offsetClause = generateOffset ();
138113 selectStatement .setOffsetClause (offsetClause );
139114 }
140- List < SQLite3Expression > orderBy = new SQLite3ExpressionGenerator ( globalState ). setColumns ( columns )
141- .generateOrderBys ();
115+ /* PQS does not check for ordering, so we can generate any ORDER BY clause */
116+ List < SQLite3Expression > orderBy = new SQLite3ExpressionGenerator ( globalState ) .generateOrderBys ();
142117 selectStatement .setOrderByExpressions (orderBy );
143118 if (!groupByClause .isEmpty () && Randomly .getBoolean ()) {
144119 SQLite3Expression randomExpression = SQLite3Common .getTrueExpression (columns , globalState );
@@ -166,6 +141,52 @@ private List<Join> getJoinStatements(SQLite3GlobalState globalState, List<SQLite
166141 return joinStatements ;
167142 }
168143
144+ private List <SQLite3Expression > getColExpressions (boolean allTablesContainOneRow , List <SQLite3Column > columns ,
145+ List <SQLite3Column > columnsWithoutRowid ) {
146+ List <SQLite3Expression > colExpressions = new ArrayList <>();
147+
148+ for (SQLite3Column c : fetchColumns ) {
149+ SQLite3Expression colName = new SQLite3ColumnName (c , pivotRow .getValues ().get (c ));
150+ if (allTablesContainOneRow && Randomly .getBoolean ()) {
151+
152+ /*
153+ * PQS cannot detect omitted or incorrectly-fetched duplicate rows, so we can generate DISTINCT
154+ * statements
155+ */
156+ boolean generateDistinct = Randomly .getBooleanWithRatherLowProbability ();
157+ if (generateDistinct ) {
158+ colName = new SQLite3Distinct (colName );
159+ }
160+
161+ SQLite3AggregateFunction aggFunc = SQLite3AggregateFunction .getRandom (c .getType ());
162+ colName = new SQLite3Aggregate (Arrays .asList (colName ), aggFunc );
163+ if (Randomly .getBoolean () && !generateDistinct ) {
164+ colName = generateWindowFunction (columns , colName , true );
165+ }
166+ errors .add ("second argument to nth_value must be a positive integer" );
167+ }
168+ if (Randomly .getBoolean ()) {
169+ SQLite3Expression randomExpression ;
170+ randomExpression = new SQLite3ExpressionGenerator (globalState ).setColumns (columns )
171+ .generateResultKnownExpression ();
172+ colExpressions .add (randomExpression );
173+ } else {
174+ colExpressions .add (colName );
175+ }
176+ }
177+ if (allTablesContainOneRow ) {
178+ SQLite3WindowFunction windowFunction = SQLite3WindowFunction .getRandom (columnsWithoutRowid , globalState );
179+ SQLite3Expression windowExpr = generateWindowFunction (columnsWithoutRowid , windowFunction , false );
180+ colExpressions .add (windowExpr );
181+ }
182+ for (SQLite3Expression expr : colExpressions ) {
183+ if (expr .getExpectedValue () == null ) {
184+ throw new IgnoreMeException (); // TODO: aggregates
185+ }
186+ }
187+ return colExpressions ;
188+ }
189+
169190 private SQLite3Expression generateOffset () {
170191 if (Randomly .getBoolean ()) {
171192 // OFFSET 0
@@ -182,9 +203,11 @@ protected boolean isContainedIn(Query query) throws SQLException {
182203
183204 StringBuilder sb = new StringBuilder ();
184205 sb .append ("SELECT " );
185- addExpectedValues (sb );
186- StringBuilder sb2 = new StringBuilder ();
187- addExpectedValues (sb2 );
206+ String checkForContainmentValues = getGeneralizedPivotRowValues ();
207+ sb .append (checkForContainmentValues );
208+ globalState .getState ().getLocalState ()
209+ .log ("-- we expect the following expression to be contained in the result set: "
210+ + checkForContainmentValues );
188211 sb .append (" INTERSECT SELECT * FROM (" ); // ANOTHER SELECT TO USE ORDER BY without restrictions
189212 if (query .getQueryString ().endsWith (";" )) {
190213 sb .append (query .getQueryString ().substring (0 , query .getQueryString ().length () - 1 ));
@@ -193,6 +216,7 @@ protected boolean isContainedIn(Query query) throws SQLException {
193216 }
194217 sb .append (")" );
195218 String resultingQueryString = sb .toString ();
219+ globalState .getState ().getLocalState ().log (resultingQueryString );
196220 Query finalQuery = new QueryAdapter (resultingQueryString , query .getExpectedErrors ());
197221 try (ResultSet result = createStatement .executeQuery (finalQuery .getQueryString ())) {
198222 boolean isContainedIn = !result .isClosed ();
@@ -207,14 +231,16 @@ protected boolean isContainedIn(Query query) throws SQLException {
207231 }
208232 }
209233
210- private void addExpectedValues (StringBuilder sb ) {
211- for (int i = 0 ; i < colExpressions .size (); i ++) {
234+ private String getGeneralizedPivotRowValues () {
235+ StringBuilder sb = new StringBuilder ();
236+ for (int i = 0 ; i < pivotRowExpression .size (); i ++) {
212237 if (i != 0 ) {
213238 sb .append (", " );
214239 }
215- SQLite3Constant expectedValue = colExpressions .get (i ).getExpectedValue ();
240+ SQLite3Constant expectedValue = pivotRowExpression .get (i ).getExpectedValue ();
216241 sb .append (SQLite3Visitor .asString (expectedValue ));
217242 }
243+ return sb .toString ();
218244 }
219245
220246 private SQLite3Expression generateLimit (long l ) {
0 commit comments