Add documentation about missing features (#56)
This commit is contained in:
parent
88ffe4822b
commit
f815247510
|
@ -4,7 +4,8 @@ Documentation
|
|||
Internal Links:
|
||||
|
||||
* [Developer Documentation](dev-docs.md)
|
||||
* [Working SQL Syntax Examples](syntax-examples.md)
|
||||
* [Supported SQL Queries](syntax-support.md)
|
||||
* [Known Limitations & Missing Features](known-limitations.md)
|
||||
|
||||
|
||||
External Resources:
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
Known Limitations & Missing Features
|
||||
====================================
|
||||
|
||||
This page contains an overview of known missing limitations and missing features in our SQL parser project. In general, we would like to see all of these features being supported at some point. If you are particularly interested in a specific feature, feel free to contribute to this project through a pull request.
|
||||
|
||||
### Completely Missing Statement Types
|
||||
|
||||
* EXPLAIN
|
||||
* EXPORT
|
||||
* RENAME
|
||||
* ALTER
|
||||
|
||||
Additionally, there are a lot of statement types that are specific to certain database systems. Supporting all of these is not on our roadmap, but if someone implements support for such a statement, we can also integrate it.
|
||||
|
||||
### Other SQL Limitations
|
||||
|
||||
* Tables names ignore the schema name (see grammar rule `table_name`). This affects, for example, `INSERT, IMPORT, DROP, DELETE`.
|
||||
* Column data types only support `INT, DOUBLE, TEXT`.
|
|
@ -1,44 +0,0 @@
|
|||
Syntax Examples
|
||||
===============
|
||||
|
||||
This page contains some samples of SQL statements that can be executed in Hyrise.
|
||||
|
||||
|
||||
**Create Tables**
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS students FROM TBL FILE 'test/students.tbl';
|
||||
CREATE TABLE test (v1 INTEGER, v2 INTEGER, v3 INTEGER);
|
||||
```
|
||||
|
||||
|
||||
**Select with Join**
|
||||
```sql
|
||||
SELECT name, city, * FROM students AS t1 JOIN students AS t2 ON t1.city = t2.city WHERE t1.grade < 2.0 AND t2.grade > 2.0 AND t1.city = 'Frohnau' ORDER BY t1.grade DESC;
|
||||
```
|
||||
|
||||
|
||||
**Group By**
|
||||
```sql
|
||||
SELECT city, AVG(grade) AS average, MIN(grade) AS best, MAX(grade) AS worst FROM students GROUP BY city;
|
||||
```
|
||||
|
||||
|
||||
**Update and Delete**
|
||||
```sql
|
||||
UPDATE students SET name='Max Mustermann' WHERE name = 'Ralf Stiebitz';
|
||||
DELETE FROM students WHERE name = 'Max Mustermann';
|
||||
```
|
||||
|
||||
|
||||
**Prepare and Execute**
|
||||
```sql
|
||||
PREPARE batch_insert {
|
||||
INSERT INTO test VALUES (?, 0, 0);
|
||||
INSERT INTO test VALUES (?, 0, 0);
|
||||
INSERT INTO test VALUES (?, 0, 0);
|
||||
INSERT INTO test VALUES (?, 0, 0);
|
||||
INSERT INTO test VALUES (?, 0, 0);
|
||||
};
|
||||
|
||||
EXECUTE insert_test(1, 2, 3, 4 ,5);
|
||||
```
|
|
@ -0,0 +1,53 @@
|
|||
Supported SQL Queries
|
||||
=====================
|
||||
|
||||
This page contains a short list of queries that can be correctly parsed with our parser. If you are interested in finding out if a certain feature is supported, it is probably the easiest to checkout the repository and try the example project or check our [list of known limitations](known-limitations.md). Also the file [queries-good.sql](../test/queries/queries-good.sql) shows a list of queries which are parsable with the current version.
|
||||
|
||||
|
||||
## Select Statements
|
||||
|
||||
We implement a broad support for the most common elements for `SELECT` statements. Following are a few examples of basic constructs that are supported.
|
||||
|
||||
```sql
|
||||
SELECT name, city, *
|
||||
FROM students AS t1 JOIN students AS t2 ON t1.city = t2.city
|
||||
WHERE t1.grade < 2.0 AND
|
||||
t2.grade > 2.0 AND
|
||||
t1.city = 'Frohnau'
|
||||
ORDER BY t1.grade DESC;
|
||||
|
||||
SELECT city, AVG(grade) AS average,
|
||||
MIN(grade) AS best, MAX(grade) AS worst
|
||||
FROM students
|
||||
GROUP BY city;
|
||||
```
|
||||
|
||||
## Data Definition & Modification
|
||||
|
||||
**Create Tables**
|
||||
```sql
|
||||
CREATE TABLE students (
|
||||
name TEXT,
|
||||
student_number INTEGER,
|
||||
city TEXT,
|
||||
grade DOUBLE
|
||||
);
|
||||
```
|
||||
|
||||
**Update and Delete**
|
||||
```sql
|
||||
UPDATE students SET name='Max Mustermann' WHERE name = 'Ralf Mustermann';
|
||||
|
||||
DELETE FROM students WHERE name = 'Max Mustermann';
|
||||
```
|
||||
|
||||
|
||||
## Prepared Statements
|
||||
|
||||
The definition and execution of prepared statements is supported using the following syntax.
|
||||
|
||||
```sql
|
||||
PREPARE select_test FROM 'SELECT * FROM customer WHERE c_name = ?;';
|
||||
|
||||
EXECUTE select_test('Max Mustermann');
|
||||
```
|
|
@ -29,9 +29,6 @@ namespace hsql {
|
|||
};
|
||||
|
||||
// Operator types. These are important for expressions of type kExprOperator.
|
||||
// Trivial types are those that can be described by a single character e.g:
|
||||
// + - * / < > = %
|
||||
// Non-trivial are: <> <= >= LIKE ISNULL NOT
|
||||
enum OperatorType {
|
||||
kOpNone,
|
||||
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "thirdparty/microtest/microtest.h"
|
||||
#include "SQLParser.h"
|
||||
|
||||
// Read all lines from the given file path. Skips comment lines.
|
||||
std::vector<std::string> readlines(std::string path);
|
||||
|
||||
// Read the queries from all files that were supplied to the test
|
||||
// through the -f argument. For all queries it is checked whether they
|
||||
// can be parsed successfully.
|
||||
TEST(AutoQueryFileTest) {
|
||||
const std::vector<std::string>& args = mt::Runtime::args();
|
||||
|
||||
std::vector<std::string> query_files;
|
||||
|
||||
// Parse command line arguments to retrieve query files.
|
||||
uint i = 1;
|
||||
for (; i < args.size(); ++i) {
|
||||
if (args[i] == "-f") {
|
||||
query_files.push_back(args[++i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Read list of queries from all input files.
|
||||
std::vector<std::string> lines;
|
||||
for (std::string path : query_files) {
|
||||
std::vector<std::string> tmp = readlines(path);
|
||||
lines.insert(lines.end(), tmp.begin(), tmp.end());
|
||||
}
|
||||
|
||||
// Execute queries.
|
||||
size_t num_executed = 0;
|
||||
size_t num_failed = 0;
|
||||
for (std::string line : lines) {
|
||||
bool expected_result = true;
|
||||
std::string query = line;
|
||||
|
||||
// If a line starts with '!' parsing is expected to fail.
|
||||
if (query.at(0) == '!') {
|
||||
expected_result = false;
|
||||
query = query.substr(1);
|
||||
}
|
||||
|
||||
// Measuring the parsing time.
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
// Parse the query.
|
||||
hsql::SQLParserResult result;
|
||||
hsql::SQLParser::parse(query, &result);
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
double us = elapsed_seconds.count() * 1000 * 1000;
|
||||
|
||||
if (expected_result == result.isValid()) {
|
||||
printf("\033[0;32m{ ok} (%.1fus)\033[0m %s\n", us, line.c_str());
|
||||
} else {
|
||||
printf("\033[0;31m{ failed}\033[0m\n");
|
||||
printf("\t\033[0;31m%s (L%d:%d)\n\033[0m", result.errorMsg(), result.errorLine(), result.errorColumn());
|
||||
printf("\t%s\n", line.c_str());
|
||||
++num_failed;
|
||||
}
|
||||
++num_executed;
|
||||
}
|
||||
|
||||
if (num_failed == 0) {
|
||||
printf("\033[0;32m{ ok} \033[0mAll %lu grammar tests completed successfully!\n", num_executed);
|
||||
} else {
|
||||
fprintf(stderr, "\033[0;31m{ failed} \033[0mSome grammar tests failed! %lu out of %lu tests failed!\n", num_failed, num_executed);
|
||||
}
|
||||
ASSERT_EQ(num_failed, 0);
|
||||
}
|
||||
|
||||
std::vector<std::string> readlines(std::string path) {
|
||||
std::ifstream infile(path);
|
||||
std::vector<std::string> lines;
|
||||
std::string line;
|
||||
while (std::getline(infile, line)) {
|
||||
std::istringstream iss(line);
|
||||
|
||||
// Skip comments.
|
||||
if (line[0] == '#' ||
|
||||
(line[0] == '-' && line[1] == '-')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
lines.push_back(line);
|
||||
}
|
||||
return lines;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
# This file contains a list of strings that are NOT valid SQL queries.
|
||||
# Each line contains a single SQL query.
|
||||
# Each line starts with a '!' char to indicate that parsing should fail.
|
||||
!
|
||||
!1
|
||||
!gibberish;
|
||||
!SELECT abc;
|
||||
!CREATE TABLE "table" FROM TBL FILE 'students.tbl';SELECT 1
|
||||
!CREATE TABLE "table" FROM TBL FILE 'students.tbl';1
|
||||
!INSERT INTO test_table VALUESd (1, 2, 'test');
|
||||
!SELECT * FROM t WHERE a = ? AND b = ?;SELECT 1;
|
|
@ -1,3 +1,5 @@
|
|||
# This file contains a list of strings that are NOT valid SQL queries.
|
||||
# Each line contains a single SQL query.
|
||||
# SELECT statement
|
||||
SELECT * FROM orders;
|
||||
SELECT a FROM foo WHERE a > 12 OR b > 3 AND NOT c LIMIT 10
|
||||
|
@ -47,12 +49,3 @@ DEALLOCATE PREPARE prep;
|
|||
SELECT * FROM test WITH HINT(NO_CACHE);
|
||||
SELECT * FROM test WITH HINT(NO_CACHE, NO_SAMPLING);
|
||||
SELECT * FROM test WITH HINT(NO_CACHE, SAMPLE_RATE(0.1), OMW(1.0, 'test'));
|
||||
# Error expeced
|
||||
!
|
||||
!1
|
||||
!gibberish;
|
||||
!SELECT abc;
|
||||
!CREATE TABLE "table" FROM TBL FILE 'students.tbl';SELECT 1
|
||||
!CREATE TABLE "table" FROM TBL FILE 'students.tbl';1
|
||||
!INSERT INTO test_table VALUESd (1, 2, 'test');
|
||||
!SELECT * FROM t WHERE a = ? AND b = ?;SELECT 1;
|
|
@ -1,107 +0,0 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "thirdparty/microtest/microtest.h"
|
||||
#include "SQLParser.h"
|
||||
|
||||
using namespace hsql;
|
||||
|
||||
|
||||
std::vector<std::string> readlines(std::string path) {
|
||||
std::ifstream infile(path);
|
||||
std::vector<std::string> lines;
|
||||
std::string line;
|
||||
while (std::getline(infile, line)) {
|
||||
std::istringstream iss(line);
|
||||
|
||||
// Skip comments
|
||||
if (line[0] == '#' ||
|
||||
(line[0] == '-' && line[1] == '-')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
lines.push_back(line);
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
#define STREQ(a, b) (std::string(a).compare(std::string(b)) == 0)
|
||||
|
||||
TEST(AutoGrammarTest) {
|
||||
const std::vector<std::string>& args = mt::Runtime::args();
|
||||
if (args.size() <= 1) {
|
||||
fprintf(stderr, "Usage: grammar_test [--false] [-f path] query, ...\n");
|
||||
return;
|
||||
}
|
||||
|
||||
bool globalExpectFalse = false;
|
||||
bool useFile = false;
|
||||
std::string filePath = "";
|
||||
|
||||
// Parse command line arguments
|
||||
uint i = 1;
|
||||
for (; i < args.size(); ++i) {
|
||||
if (STREQ(args[i], "--false")) globalExpectFalse = true;
|
||||
else if (STREQ(args[i], "-f")) {
|
||||
useFile = true;
|
||||
filePath = args[++i];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Read list of queries for this rest
|
||||
std::vector<std::string> queries;
|
||||
if (useFile) {
|
||||
queries = readlines(filePath);
|
||||
} else {
|
||||
for (; i < args.size(); ++i) queries.push_back(args[i]);
|
||||
}
|
||||
|
||||
|
||||
// Execute queries
|
||||
int numFailed = 0;
|
||||
for (std::string sql : queries) {
|
||||
bool expectFalse = globalExpectFalse;
|
||||
if (sql.at(0) == '!') {
|
||||
expectFalse = !expectFalse;
|
||||
sql = sql.substr(1);
|
||||
}
|
||||
|
||||
// Measuring the parsing time
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
// Parsing
|
||||
SQLParserResult result;
|
||||
SQLParser::parse(sql.c_str(), &result);
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
double us = elapsed_seconds.count() * 1000 * 1000;
|
||||
|
||||
if (expectFalse == result.isValid()) {
|
||||
printf("\033[0;31m{ failed}\033[0m\n");
|
||||
printf("\t\033[0;31m%s (L%d:%d)\n\033[0m", result.errorMsg(), result.errorLine(), result.errorColumn());
|
||||
printf("\t%s\n", sql.c_str());
|
||||
numFailed++;
|
||||
} else {
|
||||
// TODO: indicate whether expectFalse was set
|
||||
printf("\033[0;32m{ ok} (%.1fus)\033[0m %s\n", us, sql.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (numFailed == 0) {
|
||||
printf("\033[0;32m{ ok} \033[0mAll %lu grammar tests completed successfully!\n", queries.size());
|
||||
} else {
|
||||
fprintf(stderr, "\033[0;31m{ failed} \033[0mSome grammar tests failed! %d out of %lu tests failed!\n", numFailed, queries.size());
|
||||
}
|
||||
ASSERT_EQ(numFailed, 0);
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ CONFLICT_RET=0
|
|||
#################################################
|
||||
# Running SQL parser tests.
|
||||
printf "\n${GREEN}Running SQL parser tests...${NC}\n"
|
||||
bin/tests -f "test/valid_queries.sql"
|
||||
bin/tests -f "test/queries/queries-good.sql" -f "test/queries/queries-bad.sql"
|
||||
SQL_TEST_RET=$?
|
||||
|
||||
if [ $SQL_TEST_RET -eq 0 ]; then
|
||||
|
@ -31,7 +31,8 @@ fi
|
|||
# Running memory leak checks.
|
||||
printf "\n${GREEN}Running memory leak checks...${NC}\n"
|
||||
valgrind --leak-check=full --error-exitcode=200 --log-fd=3 \
|
||||
./bin/tests -f "test/valid_queries.sql" 3>&1 >/dev/null 2>/dev/null
|
||||
bin/tests -f "test/queries/queries-good.sql" -f "test/queries/queries-bad.sql" \
|
||||
3>&1 >/dev/null 2>/dev/null
|
||||
MEM_LEAK_RET=$?
|
||||
|
||||
if [ $MEM_LEAK_RET -ne 200 ]; then
|
||||
|
|
Loading…
Reference in New Issue