Skip to content

Commit

Permalink
Add support for Presto (#881)
Browse files Browse the repository at this point in the history
This is the first Pull Request for [Presto](https://prestodb.io/) implementation.
The following oracles are currently completed:
 * NoRec oracle
 * TLP Where
 * TLP Aggregate (basic aggregate function)

## Data types
The following data types are currently supported:
BOOLEAN, INT, FLOAT, DECIMAL, VARCHAR, CHAR, VARBINARY, JSON, DATE, TIME,
TIMESTAMP, TIME_WITH_TIME_ZONE, TIMESTAMP_WITH_TIME_ZONE,
INTERVAL_YEAR_TO_MONTH, INTERVAL_DAY_TO_SECOND, ARRAY.
Others such as MAP, ROW, IPADDRESS, UID, IPPREFIX, HyperLogLog,
P4HyperLogLog, KHyperLogLog, QDigest and TDigest will be
supported in the future.

## Functions

Large number of Presto supported are implemented, but only small number
are actually used in test. Most of the functions will be supported in
the future.
  • Loading branch information
branimir-vujicic committed Aug 30, 2023
1 parent f852f8c commit 7804a3a
Show file tree
Hide file tree
Showing 49 changed files with 6,150 additions and 4 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,13 @@ Since SQL dialects differ widely, each DBMS to be tested requires a separate imp
| TDEngine | Removed | Untyped | We removed the TDEngine implementation since all but one of our bug reports were still unaddressed five months after we reported them. |
| OceanBase | Working | Untyped | |
| YugabyteDB | Working | Typed (YSQL), Untyped (YCQL) | YSQL implementation based on Postgres code. YCQL implementation is primitive for now and uses Cassandra JDBC driver as a proxy interface. |
| Databend | Working | Typed | |
| QuestDB | Working | Untyped, Generic | The implementation of QuestDB is still WIP, current version covers very basic data types, operations and SQL keywords. |
| Databend | Working | Typed | |
| QuestDB | Working | Untyped, Generic | The implementation of QuestDB is still WIP, current version covers very basic data types, operations and SQL keywords. |
| CnosDB |Working | Typed | The implementation of CnosDB currently uses Restful API. |
| Materialize |Working | Typed |
| Apache Doris | Preliminary | Typed | This is a preliminary implementation, which only contains the common logic of Doris. We have found some errors through it, and hope to improve it in the future.
| Apache Doris | Preliminary | Typed | This is a preliminary implementation, which only contains the common logic of Doris. We have found some errors through it, and hope to improve it in the future.
| Presto | Preliminary | Typed | This is a preliminary implementation, only basic types supported.



# Using SQLancer
Expand Down
7 changes: 6 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,11 @@
<artifactId>duckdb_jdbc</artifactId>
<version>0.5.1</version>
</dependency>
<dependency>
<groupId>com.facebook.presto</groupId>
<artifactId>presto-jdbc</artifactId>
<version>0.283</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
Expand Down Expand Up @@ -467,4 +472,4 @@
</build>
</profile>
</profiles>
</project>
</project>
1 change: 1 addition & 0 deletions src/check_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def verify_all_dbs(name_to_files: dict[str:List[str]]):
name_to_files["MySQL"] = get_java_files(os.path.join(cwd, "src", "sqlancer", "mysql"))
name_to_files["OceanBase"] = get_java_files(os.path.join(cwd, "src", "sqlancer", "oceanbase"))
name_to_files["Postgres"] = get_java_files(os.path.join(cwd, "src", "sqlancer", "postgres"))
name_to_files["Presto"] = get_java_files(os.path.join(cwd, "src", "sqlancer", "presto"))
name_to_files["QuestDB"] = get_java_files(os.path.join(cwd, "src", "sqlancer", "questdb"))
name_to_files["SQLite3"] = get_java_files(os.path.join(cwd, "src", "sqlancer", "sqlite3"))
name_to_files["TiDB"] = get_java_files(os.path.join(cwd, "src", "sqlancer", "tidb"))
Expand Down
2 changes: 2 additions & 0 deletions src/sqlancer/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import sqlancer.mysql.MySQLProvider;
import sqlancer.oceanbase.OceanBaseProvider;
import sqlancer.postgres.PostgresProvider;
import sqlancer.presto.PrestoProvider;
import sqlancer.questdb.QuestDBProvider;
import sqlancer.sqlite3.SQLite3Provider;
import sqlancer.stonedb.StoneDBProvider;
Expand Down Expand Up @@ -719,6 +720,7 @@ private static void checkForIssue799(List<DatabaseProvider<?, ?, ?>> providers)
providers.add(new MongoDBProvider());
providers.add(new MySQLProvider());
providers.add(new OceanBaseProvider());
providers.add(new PrestoProvider());
providers.add(new PostgresProvider());
providers.add(new QuestDBProvider());
providers.add(new SQLite3Provider());
Expand Down
40 changes: 40 additions & 0 deletions src/sqlancer/presto/PrestoConstantUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package sqlancer.presto;

import java.math.BigDecimal;
import java.math.RoundingMode;

public final class PrestoConstantUtils {

private PrestoConstantUtils() {
}

public static String removeNoneAscii(String str) {
return str.replaceAll("[^\\x00-\\x7F]", "");
}

public static String removeNonePrintable(String str) { // All Control Char
return str.replaceAll("[\\p{C}]", "");
}

public static String removeOthersControlChar(String str) { // Some Control Char
return str.replaceAll("[\\p{Cntrl}\\p{Cc}\\p{Cf}\\p{Co}\\p{Cn}]", "");
}

public static String removeAllControlChars(String str) {
return removeOthersControlChar(removeNonePrintable(str)).replaceAll("[\\r\\n\\t]", "");
}

public static BigDecimal getDecimal(double val, int scale, int precision) {
int part = precision - scale;
// long part
long lng = (long) val;
// decimal places
double d1 = val - lng;
String xStr = Long.toString(lng);
String substring = xStr.substring(xStr.length() - part);
long newX = substring.isEmpty() ? 0 : Long.parseLong(substring);
double finalD = newX + d1;
return new BigDecimal(finalD).setScale(scale, RoundingMode.CEILING);
}

}
127 changes: 127 additions & 0 deletions src/sqlancer/presto/PrestoErrors.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package sqlancer.presto;

import sqlancer.common.query.ExpectedErrors;

public final class PrestoErrors {

private PrestoErrors() {
}

public static void addExpressionErrors(ExpectedErrors errors) {
// Presto errors
errors.add("cannot be applied to");
errors.add("LIKE expression must evaluate to a varchar");
errors.add("JOIN ON clause must evaluate to a boolean");
// errors.add("Unexpected parameters");

// SELECT SUM(count) FROM (SELECT
// CAST((-179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0000
// IS NOT NULL AND
// -179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0000)
// AS BIGINT)as count FROM t0) as res
errors.add("Decimal overflow");
errors.add("multiplication overflow");
errors.add("addition overflow");
errors.add("subtraction overflow");

// cast
// errors.add("Cannot cast");
errors.add("Value cannot be cast to");
errors.add("Cannot cast DECIMAL");
errors.add("Cannot cast BIGINT");
errors.add("Cannot cast INTEGER");

// TODO: check
errors.add("io.airlift.slice.Slice cannot be cast to java.lang.Number");
errors.add("Cannot cast java.lang.Long to io.airlift.slice.Slice");
errors.add("Unexpected subquery expression in logical plan");

// 9223372036854775808
errors.add("Invalid numeric literal");

errors.add("Division by zero");
errors.add("/ by zero");

errors.add("Cannot subtract hour, minutes or seconds from a date");
errors.add("Cannot add hour, minutes or seconds to a date");

errors.add("DECIMAL scale must be in range");
errors.add("multiplication overflow");
errors.add("addition overflow");
errors.add("subtraction overflow");
errors.add("Decimal overflow");
errors.add("IN value and list items must be the same type");
errors.add("is not a valid timestamp literal");
errors.add("Unknown time-zone ID");
errors.add("GROUP BY position");

// ARRAY
errors.add("Unknown type: ARRAY");
}

private static void addRegexErrors(ExpectedErrors errors) {
errors.add("missing ]");
errors.add("missing )");
errors.add("invalid escape sequence");
errors.add("no argument for repetition operator: ");
errors.add("bad repetition operator");
errors.add("trailing \\");
errors.add("invalid perl operator");
errors.add("invalid character class range");
errors.add("width is not integer");
}

private static void addFunctionErrors(ExpectedErrors errors) {
errors.add("SUBSTRING cannot handle negative lengths");
errors.add("is undefined outside [-1,1]"); // ACOS etc
errors.add("invalid type specifier"); // PRINTF
errors.add("argument index out of range"); // PRINTF
errors.add("invalid format string"); // PRINTF
errors.add("number is too big"); // PRINTF
errors.add("Like pattern must not end with escape character!"); // LIKE
errors.add("Could not choose a best candidate function for the function call \"date_part"); // date_part
errors.add("extract specifier"); // date_part
errors.add("not recognized"); // date_part
errors.add("not supported"); // date_part
errors.add("Failed to cast");
errors.add("Conversion Error");
errors.add("Could not cast value");
errors.add("Insufficient padding in RPAD"); // RPAD
errors.add("Could not choose a best candidate function for the function call"); // monthname
errors.add("expected a numeric precision field"); // ROUND
errors.add("with non-constant precision is not supported"); // ROUND
}

// TODO: cover presto error
public static void addInsertErrors(ExpectedErrors errors) {
addRegexErrors(errors);
addFunctionErrors(errors);

errors.add("NOT NULL constraint failed");
errors.add("PRIMARY KEY or UNIQUE constraint violated");
errors.add("duplicate key");
errors.add("can't be cast because the value is out of range for the destination type");
errors.add("Could not convert string");
errors.add("Unimplemented type for cast");
errors.add("field value out of range");
errors.add("CHECK constraint failed");
errors.add("Cannot explicitly insert values into rowid column"); // TODO: don't insert into rowid
errors.add(" Column with name rowid does not exist!"); // currently, there doesn't seem to way to determine if
// the table has a primary key
errors.add("Could not cast value");
errors.add("create unique index, table contains duplicate data");
errors.add("Failed to cast");

errors.add("Values rows have mismatched types");
errors.add("Mismatch at column");
errors.add("This connector does not support updates or deletes");
errors.add("Values rows have mismatched types");
errors.add("Invalid numeric literal");

}

public static void addGroupByErrors(ExpectedErrors errors) {
errors.add("must be an aggregate expression or appear in GROUP BY clause");
}

}
25 changes: 25 additions & 0 deletions src/sqlancer/presto/PrestoExpressionToNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package sqlancer.presto;

import java.util.List;
import java.util.stream.Collectors;

import sqlancer.common.ast.newast.Node;
import sqlancer.presto.ast.PrestoExpression;

public final class PrestoExpressionToNode {

private PrestoExpressionToNode() {

}

@SuppressWarnings("unchecked")
public static Node<PrestoExpression> cast(PrestoExpression expression) {
return (Node<PrestoExpression>) expression;
}

@SuppressWarnings("unchecked")
public static List<Node<PrestoExpression>> casts(List<PrestoExpression> expressions) {
return expressions.stream().map(e -> (Node<PrestoExpression>) e).collect(Collectors.toList());
}

}
13 changes: 13 additions & 0 deletions src/sqlancer/presto/PrestoGlobalState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package sqlancer.presto;

import java.sql.SQLException;

import sqlancer.SQLGlobalState;

public class PrestoGlobalState extends SQLGlobalState<PrestoOptions, PrestoSchema> {

@Override
protected PrestoSchema readSchema() throws SQLException {
return PrestoSchema.fromConnection(getConnection(), getDatabaseName());
}
}
Loading

0 comments on commit 7804a3a

Please sign in to comment.