feat(SQLParser): add POCO::Data::SQLParser #4230

This commit is contained in:
Aleksandar Fabijanic 2023-10-29 15:03:26 +01:00
parent 8c4b166737
commit e174be8660
90 changed files with 20441 additions and 1 deletions

View File

@ -34,6 +34,9 @@ class ODBCSQLServerTest: public ODBCTest
/// SQL Server | 10.00.22621.1992 | 16.0.1000.6 (64-bit) | Windows 11
/// ODBC Driver 17 for SQL Server | 2017.1710.03.01 | 16.0.1000.6 (64-bit) | Windows 11
/// ODBC Driver 18 for SQL Server | 2018.183.01.01 | 16.0.1000.6 (64-bit) | Windows 11
///
/// Drivers download (x86, x64, ARM64):
/// https://learn.microsoft.com/en-us/sql/connect/odbc/download-odbc-driver-for-sql-server?view=sql-server-ver16
{
public:
ODBCSQLServerTest(const std::string& name);

View File

@ -23,7 +23,7 @@
#include "SQLExecutor.h"
#define POCO_ODBC_TEST_DATABASE_SERVER "10.211.55.5"//"localhost"
#define POCO_ODBC_TEST_DATABASE_SERVER "localhost"
class ODBCTest: public CppUnit::TestCase

View File

@ -0,0 +1,41 @@
//
// SQLParser.h
//
// Library: Data
// Package: SQLParser
// Module: SQLParser
//
// Forward header for the SQLParser class.
//
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef Data_SQLParser_INCLUDED
#define Data_SQLParser_INCLUDED
#include "Poco/Config.h"
#ifdef POCO_DATA_ENABLE_SQL_PARSER
#include "sql-parser/src/SQLParser.h"
#include "sql-parser/src/SQLParserResult.h"
#include "sql-parser/src/util/sqlhelper.h"
namespace Poco {
namespace Data = hsql;
} // namespace Poco
#endif // POCO_DATA_ENABLE_SQL_PARSER
#endif // Data_SQLParser_INCLUDED

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2012-2017 Hasso-Plattner-Institut
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,164 @@
all: library
#######################################
############# Directories #############
#######################################
BIN = bin
SRC = src
SRCPARSER = src/parser
INSTALL = /usr/local
######################################
############ Compile Mode ############
######################################
# Set compile mode to -g or -O3.
# Debug mode: make mode=debug
mode ?= release
MODE_LOG = ""
OPT_FLAG =
ifeq ($(mode), debug)
OPT_FLAG = -g
MODE_LOG = "Building in \033[1;31mdebug\033[0m mode"
else
OPT_FLAG = -O3
MODE_LOG = "Building in \033[0;32mrelease\033[0m mode ('make mode=debug' for debug mode)"
endif
GMAKE = make mode=$(mode)
#######################################
############### Library ###############
#######################################
NAME := sqlparser
PARSER_CPP = $(SRCPARSER)/bison_parser.cpp $(SRCPARSER)/flex_lexer.cpp
PARSER_H = $(SRCPARSER)/bison_parser.h $(SRCPARSER)/flex_lexer.h
LIB_CFLAGS = -std=c++17 $(OPT_FLAG)
relaxed_build ?= "off"
ifeq ($(relaxed_build), on)
$(warning $(NAME) will be built with most compiler warnings deactivated. This is fine if you want to test $(NAME) but will become an issue when you want to contribute code.)
else
LIB_CLFAGS += -Wall -Werror
endif
static ?= no
ifeq ($(static), yes)
LIB_BUILD = lib$(NAME).a
LIBLINKER = $(AR)
LIB_LFLAGS = rs
else
LIB_BUILD = lib$(NAME).so
LIBLINKER = $(CXX)
LIB_CFLAGS += -fPIC
LIB_LFLAGS = -shared -o
endif
LIB_CPP = $(sort $(shell find $(SRC) -name '*.cpp' -not -path "$(SRCPARSER)/*") $(PARSER_CPP))
LIB_H = $(shell find $(SRC) -name '*.h' -not -path "$(SRCPARSER)/*") $(PARSER_H)
LIB_ALL = $(shell find $(SRC) -name '*.cpp' -not -path "$(SRCPARSER)/*") $(shell find $(SRC) -name '*.h' -not -path "$(SRCPARSER)/*")
LIB_OBJ = $(LIB_CPP:%.cpp=%.o)
library: $(LIB_BUILD)
$(LIB_BUILD): $(LIB_OBJ)
$(LIBLINKER) $(LIB_LFLAGS) $(LIB_BUILD) $(LIB_OBJ)
$(SRCPARSER)/flex_lexer.o: $(SRCPARSER)/flex_lexer.cpp $(SRCPARSER)/bison_parser.cpp
$(CXX) $(LIB_CFLAGS) -c -o $@ $< -Wno-sign-compare -Wno-unneeded-internal-declaration -Wno-register
%.o: %.cpp $(PARSER_CPP) $(LIB_H)
$(CXX) $(LIB_CFLAGS) -c -o $@ $<
$(SRCPARSER)/bison_parser.cpp: $(SRCPARSER)/bison_parser.y
$(GMAKE) -C $(SRCPARSER)/ bison_parser.cpp
$(SRCPARSER)/flex_lexer.cpp: $(SRCPARSER)/flex_lexer.l
$(GMAKE) -C $(SRCPARSER)/ flex_lexer.cpp
$(SRCPARSER)/bison_parser.h: $(SRCPARSER)/bison_parser.cpp
$(SRCPARSER)/flex_lexer.h: $(SRCPARSER)/flex_lexer.cpp
clean:
rm -f lib$(NAME).a lib$(NAME).so
rm -rf $(BIN)
find $(SRC) -type f -name '*.o' -delete
cleanparser:
$(GMAKE) -C $(SRCPARSER)/ clean
cleanall: clean cleanparser
install:
cp $(LIB_BUILD) $(INSTALL)/lib/$(LIB_BUILD)
rm -rf $(INSTALL)/include/hsql
cp -r src $(INSTALL)/include/hsql
find $(INSTALL)/include/hsql -not -name '*.h' -type f | xargs rm
#######################################
############## Benchmark ##############
#######################################
BM_BUILD = $(BIN)/benchmark
BM_CFLAGS = -std=c++17 -Wall -Isrc/ -L./ $(OPT_FLAG)
BM_PATH = benchmark
BM_CPP = $(shell find $(BM_PATH)/ -name '*.cpp')
BM_ALL = $(shell find $(BM_PATH)/ -name '*.cpp' -or -name '*.h')
benchmark: $(BM_BUILD)
run_benchmarks: benchmark
./$(BM_BUILD) --benchmark_counters_tabular=true
# --benchmark_filter="abc
save_benchmarks: benchmark
./$(BM_BUILD) --benchmark_format=csv > benchmarks.csv
$(BM_BUILD): $(BM_ALL) $(LIB_BUILD)
@mkdir -p $(BIN)/
$(CXX) $(BM_CFLAGS) $(BM_CPP) -o $(BM_BUILD) -lbenchmark -lpthread -lsqlparser -lstdc++ -lstdc++fs
########################################
############ Test & Example ############
########################################
TEST_BUILD = $(BIN)/tests
TEST_CFLAGS = -std=c++1z -Wall -Werror -Isrc/ -Itest/ -L./ $(OPT_FLAG)
TEST_CPP = $(shell find test/ -name '*.cpp')
TEST_ALL = $(shell find test/ -name '*.cpp') $(shell find test/ -name '*.h')
EXAMPLE_SRC = $(shell find example/ -name '*.cpp') $(shell find example/ -name '*.h')
test: $(TEST_BUILD)
bash test/test.sh
$(TEST_BUILD): $(TEST_ALL) $(LIB_BUILD)
@mkdir -p $(BIN)/
$(CXX) $(TEST_CFLAGS) $(TEST_CPP) -o $(TEST_BUILD) -lsqlparser -lstdc++
test_example:
$(GMAKE) -C example/
LD_LIBRARY_PATH=./ \
./example/example "SELECT * FROM students WHERE name = 'Max Mustermann';"
test_format:
@! astyle --options=astyle.options $(LIB_ALL) | grep -q "Formatted"
@! astyle --options=astyle.options $(TEST_ALL) | grep -q "Formatted"
########################################
################# Misc #################
########################################
format:
astyle --options=astyle.options $(LIB_ALL)
astyle --options=astyle.options $(TEST_ALL)
astyle --options=astyle.options $(EXAMPLE_SRC)
log_mode:
@echo $(MODE_LOG)

View File

@ -0,0 +1,63 @@
C++ SQL Parser
=========================
[![Build Status](https://github.com/hyrise/sql-parser/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/hyrise/sql-parser/actions?query=branch%3Amaster)
This is a SQL Parser for C++. It parses the given SQL query into C++ objects.
It has been developed for integration in [Hyrise](https://github.com/hyrise/hyrise), but can be used perfectly well in other environments as well.
In March 2015 we've also written a short paper outlining discussing some development details and the integration into our database Hyrise. You can find the paper [here](docs/technical_documentation.pdf).
## Usage
**Note:** You can also find a detailed usage description [here](docs/basic-usage.md).
To use the SQL parser in your own projects you simply have to follow these few steps.
1. Download the [latest release here](https://github.com/hyrise/sql-parser/releases)
2. Compile the library `make` to create `libsqlparser.so`
3. *(Optional, Recommended)* Run `make install` to copy the library to `/usr/local/lib/`
4. Run the tests `make test` to make sure everything worked
5. Include the `SQLParser.h` from `src/` (or from `/usr/local/lib/hsql/` if you installed it) and link the library in your project
6. Take a look at the [example project here](https://github.com/hyrise/sql-parser/tree/master/example)
```cpp
#include "hsql/SQLParser.h"
/* ... */
{
// Basic Usage Example
const std::string query = "...";
hsql::SQLParserResult result;
hsql::SQLParser::parse(query, &result);
if (result.isValid() && result.size() > 0) {
const hsql::SQLStatement* statement = result.getStatement(0);
if (statement->isType(hsql::kStmtSelect)) {
const auto* select = static_cast<const hsql::SelectStatement*>(statement);
/* ... */
}
}
}
```
Quick Links:
* [SQLParser.h](src/SQLParser.h)
* [SQLParserResult.h](src/SQLParserResult.h)
* [SelectStatement.h](src/sql/SelectStatement.h)
## How to Contribute
**[Developer Documentation](docs/)**
We strongly encourage you to contribute to this project! If you want to contribute to this project there are several options. If you've noticed a bug or would like an improvement let us know by creating a [new issue](https://github.com/hyrise/sql-parser/issues). If you want to develop a new feature yourself or just improve the quality of the system, feel free to fork the reposistory and implement your changes. Open a pull request as soon as your done and we will look over it. If we think it's good then your pull request will be merged into this repository.
## License
HYRISE sql-parser is licensed as open source after the MIT License which is declared in the LICENSE file of this project.

View File

@ -0,0 +1,14 @@
# Benchmark
This directory contains the scripts to execute benchmarks of the parser. We use [Google Benchmark](https://github.com/google/benchmark) to define and run benchmarks.
## Install Google Benchmark
```bash
cmake -DCMAKE_BUILD_TYPE=Release
make
make install
```

View File

@ -0,0 +1,28 @@
#include "benchmark/benchmark.h"
#include "benchmark_utils.h"
#include "queries.h"
int main(int argc, char** argv) {
// Create parse and tokenize benchmarks for TPC-H queries.
const auto tpch_queries = getTPCHQueries();
for (const auto& query : tpch_queries) {
std::string p_name = query.first + "-parse";
benchmark::RegisterBenchmark(p_name.c_str(), &BM_ParseBenchmark, query.second);
std::string t_name = query.first + "-tokenize";
benchmark::RegisterBenchmark(t_name.c_str(), &BM_TokenizeBenchmark, query.second);
}
// Create parse and tokenize benchmarks for all queries in sql_queries array.
for (unsigned i = 0; i < sql_queries.size(); ++i) {
const auto& query = sql_queries[i];
std::string p_name = getQueryName(i) + "-parse";
benchmark::RegisterBenchmark(p_name.c_str(), &BM_ParseBenchmark, query.second);
std::string t_name = getQueryName(i) + "-tokenize";
benchmark::RegisterBenchmark(t_name.c_str(), &BM_TokenizeBenchmark, query.second);
}
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
}

View File

@ -0,0 +1,44 @@
#include "benchmark_utils.h"
#include <fstream>
#include <iostream>
#include "SQLParser.h"
size_t getNumTokens(const std::string& query) {
std::vector<int16_t> tokens;
hsql::SQLParser::tokenize(query, &tokens);
return tokens.size();
}
void BM_TokenizeBenchmark(benchmark::State& st, const std::string& query) {
st.counters["num_tokens"] = getNumTokens(query);
st.counters["num_chars"] = query.size();
while (st.KeepRunning()) {
std::vector<int16_t> tokens(512);
hsql::SQLParser::tokenize(query, &tokens);
}
}
void BM_ParseBenchmark(benchmark::State& st, const std::string& query) {
st.counters["num_tokens"] = getNumTokens(query);
st.counters["num_chars"] = query.size();
while (st.KeepRunning()) {
hsql::SQLParserResult result;
hsql::SQLParser::parse(query, &result);
if (!result.isValid()) {
std::cout << query << std::endl;
std::cout << result.errorMsg() << std::endl;
st.SkipWithError("Parsing failed!");
}
}
}
std::string readFileContents(const std::string& file_path) {
std::ifstream t(file_path.c_str());
std::string text((std::istreambuf_iterator<char>(t)),
std::istreambuf_iterator<char>());
return text;
}

View File

@ -0,0 +1,41 @@
#ifndef __BENCHMARK_UTILS_H__
#define __BENCHMARK_UTILS_H__
#include "benchmark/benchmark.h"
size_t getNumTokens(const std::string& query);
void BM_TokenizeBenchmark(benchmark::State& st, const std::string& query);
void BM_ParseBenchmark(benchmark::State& st, const std::string& query);
std::string readFileContents(const std::string& file_path);
#define TIME_DIFF(end, start)\
std::chrono::duration_cast<std::chrono::duration<double>>(end - start);
#define NOW()\
std::chrono::high_resolution_clock::now();
#define PARSE_QUERY_BENCHMARK(name, query)\
static void name(benchmark::State& st) {\
BM_ParseBenchmark(st, query);\
}\
BENCHMARK(name);
#define TOKENIZE_QUERY_BENCHMARK(name, query)\
static void name(benchmark::State& st) {\
BM_TokenizeBenchmark(st, query);\
}\
BENCHMARK(name);
#define BENCHMARK_QUERY(test_name, query)\
TOKENIZE_QUERY_BENCHMARK(test_name##Tokenize, query)\
PARSE_QUERY_BENCHMARK(test_name##Parse, query)
#endif

View File

@ -0,0 +1,87 @@
#include <chrono>
#include <sstream>
#include "benchmark/benchmark.h"
#include "SQLParser.h"
#include "parser/bison_parser.h"
#include "parser/flex_lexer.h"
#include "benchmark_utils.h"
// Benchmark the influence of increasing size of the query, while
// the number of tokens remains unchanged.
static void BM_CharacterCount(benchmark::State& st) {
const size_t querySize = st.range(0);
// Base query has size of 18 characters.
std::string query = "SELECT %name% FROM test;";
const uint pad = querySize - 18;
const std::string filler = std::string(pad, 'a');
query.replace(7, 6, filler);
st.counters["num_tokens"] = getNumTokens(query);
st.counters["num_chars"] = query.size();
while (st.KeepRunning()) {
hsql::SQLParserResult result;
hsql::SQLParser::parse(query, &result);
}
}
BENCHMARK(BM_CharacterCount)
->RangeMultiplier(1 << 2)
->Ranges({{1 << 5, 1 << 15},
{5, 5}});
// Benchmark the influence of increasing number of tokens, while
// the number of characters remains unchanged.
static void BM_ConditionalTokens(benchmark::State& st) {
const size_t targetSize = st.range(0);
const size_t numTokens = st.range(1);
// Base query contains 6 tokens.
std::string query = "SELECT * FROM test";
// Create conditional.
std::stringstream condStream;
size_t missingTokens = numTokens - 4;
if (missingTokens > 0) {
condStream << " WHERE a";
missingTokens -= 2;
while (missingTokens > 0) {
condStream << " AND a";
missingTokens -= 2;
}
}
query += condStream.str();
if (targetSize >= query.size()) {
const size_t pad = targetSize - query.size();
const std::string filler = std::string(pad, 'a');
query.replace(7, 1, filler);
} else {
// Query can't be the same length as in the other benchmarks.
// Running this will result in unusable data.
fprintf(stderr, "Too many tokens. Query too long for benchmark char limit (%lu > %lu).\n",
query.size(), targetSize);
return;
}
st.counters["num_tokens"] = getNumTokens(query);
st.counters["num_chars"] = query.size();
while (st.KeepRunning()) {
hsql::SQLParserResult result;
hsql::SQLParser::parse(query, &result);
if (!result.isValid()) st.SkipWithError("Parsing failed!");
}
}
BENCHMARK(BM_ConditionalTokens)
->RangeMultiplier(1 << 2)
->Ranges({{1 << 14, 1 << 14},
{1 << 2, 1 << 11}});

View File

@ -0,0 +1,47 @@
#include "queries.h"
#include <filesystem>
#include <algorithm>
#include <iostream>
#include <regex>
#include "benchmark_utils.h"
namespace filesystem = std::filesystem;
std::string getQueryName(unsigned i) {
if (sql_queries[i].first.empty()) {
std::string name = "#" + std::to_string(i + 1);
return name;
}
return std::string("") + sql_queries[i].first;
}
std::vector<SQLQuery> getQueriesFromDirectory(const std::string& dir_path) {
std::regex query_file_regex("\\.sql$");
std::vector<std::string> files;
for (auto& entry : filesystem::directory_iterator(dir_path)) {
if (filesystem::is_regular_file(entry)) {
std::string path_str = filesystem::path(entry);
if (std::regex_search(path_str, query_file_regex)) {
files.push_back(path_str);
}
}
}
std::sort(files.begin(), files.end());
std::vector<SQLQuery> queries;
for (const std::string& file_path : files) {
const filesystem::path p(file_path);
const std::string query = readFileContents(file_path);
queries.emplace_back(p.filename(), query);
}
return queries;
}
std::vector<SQLQuery> getTPCHQueries() {
return getQueriesFromDirectory("test/queries/");
}

View File

@ -0,0 +1,56 @@
#ifndef __QUERIES_H__
#define __QUERIES_H__
#include <string>
#include <vector>
typedef std::pair<std::string, std::string> SQLQuery;
// name, query
static std::vector<SQLQuery> sql_queries = {
{"Q1", "SELECT * FROM test;"},
{"Q2", "SELECT a, b AS address FROM (SELECT * FROM test WHERE c < 100 AND b > 3) t1 WHERE a < 10 AND b < 100;"},
{"Q3", "SELECT \"left\".a, \"left\".b, \"right\".a, \"right\".b FROM table_a AS \"left\" JOIN table_b AS \"right\" ON \"left\".a = \"right\".a;"},
{"Q4", ""
"SELECT"
" l_orderkey,"
" SUM(l_extendedprice * (1 - l_discount)) AS revenue,"
" o_orderdate,"
" o_shippriority"
" FROM"
" customer,"
" orders,"
" lineitem"
" WHERE"
" c_mktsegment = '%s'"
" and c_custkey = o_custkey"
" and l_orderkey = o_orderkey"
" and o_orderdate < '%s'"
" and l_shipdate > '%s'"
" GROUP BY"
" l_orderkey,"
" o_orderdate,"
" o_shippriority"
" ORDER BY"
" revenue DESC,"
" o_orderdate;"
},
{"LongSelectList26", "SELECT a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z FROM test;"},
{"LongSelectElement26", "SELECT abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxy FROM test;"},
{"LongSelectList52", "SELECT a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z FROM test;"},
{"LongSelectElement52", "SELECT abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxy FROM test;"},
{"TwoSelects", "SELECT * FROM test; SELECT age, street AS address FROM data;"},
{"ExecuteNoParams", "EXECUTE procedure;"},
{"Execute2Params", "EXECUTE procedure(11, 'test');"},
{"Execute10Params", "EXECUTE procedure(11, 'test', 5.6, 4.2, 'abc', 6, 7, 8, 9, 10000);"},
// {"name", "query"},
};
std::string getQueryName(unsigned i);
std::vector<SQLQuery> getQueriesFromDirectory(const std::string& dir_path);
std::vector<SQLQuery> getTPCHQueries();
#endif

View File

@ -0,0 +1,74 @@
#include "SQLParser.h"
#include <stdio.h>
#include <string>
#include "parser/bison_parser.h"
#include "parser/flex_lexer.h"
namespace hsql {
SQLParser::SQLParser() { fprintf(stderr, "SQLParser only has static methods atm! Do not initialize!\n"); }
// static
bool SQLParser::parse(const std::string& sql, SQLParserResult* result) {
yyscan_t scanner;
YY_BUFFER_STATE state;
if (hsql_lex_init(&scanner)) {
// Couldn't initialize the lexer.
fprintf(stderr, "SQLParser: Error when initializing lexer!\n");
return false;
}
const char* text = sql.c_str();
state = hsql__scan_string(text, scanner);
// Parse the tokens.
// If parsing fails, the result will contain an error object.
int ret = hsql_parse(result, scanner);
bool success = (ret == 0);
result->setIsValid(success);
hsql__delete_buffer(state, scanner);
hsql_lex_destroy(scanner);
return true;
}
// static
bool SQLParser::parseSQLString(const char* sql, SQLParserResult* result) { return parse(sql, result); }
bool SQLParser::parseSQLString(const std::string& sql, SQLParserResult* result) { return parse(sql, result); }
// static
bool SQLParser::tokenize(const std::string& sql, std::vector<int16_t>* tokens) {
// Initialize the scanner.
yyscan_t scanner;
if (hsql_lex_init(&scanner)) {
fprintf(stderr, "SQLParser: Error when initializing lexer!\n");
return false;
}
YY_BUFFER_STATE state;
state = hsql__scan_string(sql.c_str(), scanner);
YYSTYPE yylval;
YYLTYPE yylloc;
// Step through the string until EOF is read.
// Note: hsql_lex returns int, but we know that its range is within 16 bit.
int16_t token = hsql_lex(&yylval, &yylloc, scanner);
while (token != 0) {
tokens->push_back(token);
token = hsql_lex(&yylval, &yylloc, scanner);
if (token == SQL_IDENTIFIER || token == SQL_STRING) {
free(yylval.sval);
}
}
hsql__delete_buffer(state, scanner);
hsql_lex_destroy(scanner);
return true;
}
} // namespace hsql

View File

@ -0,0 +1,35 @@
#ifndef SQLPARSER_SQLPARSER_H
#define SQLPARSER_SQLPARSER_H
#include "SQLParserResult.h"
#include "sql/statements.h"
namespace hsql {
// Static methods used to parse SQL strings.
class SQLParser_API SQLParser {
public:
// Parses a given constant character SQL string into the result object.
// Returns true if the lexer and parser could run without internal errors.
// This does NOT mean that the SQL string was valid SQL. To check that
// you need to check result->isValid();
static bool parse(const std::string& sql, SQLParserResult* result);
// Run tokenization on the given string and store the tokens in the output vector.
static bool tokenize(const std::string& sql, std::vector<int16_t>* tokens);
// Deprecated.
// Old method to parse SQL strings. Replaced by parse().
static bool parseSQLString(const char* sql, SQLParserResult* result);
// Deprecated.
// Old method to parse SQL strings. Replaced by parse().
static bool parseSQLString(const std::string& sql, SQLParserResult* result);
private:
SQLParser();
};
} // namespace hsql
#endif

View File

@ -0,0 +1,87 @@
#include "SQLParserResult.h"
#include <algorithm>
namespace hsql {
SQLParserResult::SQLParserResult() : isValid_(false), errorMsg_(nullptr)
{
statements_ = new std::vector<SQLStatement*>;
parameters_ = new std::vector<Expr*>;
}
SQLParserResult::SQLParserResult(SQLStatement* stmt) : isValid_(false), errorMsg_(nullptr) { addStatement(stmt); }
// Move constructor.
SQLParserResult::SQLParserResult(SQLParserResult&& moved) { *this = std::forward<SQLParserResult>(moved); }
SQLParserResult& SQLParserResult::operator=(SQLParserResult&& moved) {
isValid_ = moved.isValid_;
errorMsg_ = moved.errorMsg_;
statements_ = std::move(moved.statements_);
moved.errorMsg_ = nullptr;
moved.reset();
return *this;
}
SQLParserResult::~SQLParserResult() { reset(); }
void SQLParserResult::addStatement(SQLStatement* stmt) { statements_->push_back(stmt); }
const SQLStatement* SQLParserResult::getStatement(size_t index) const { return (*statements_)[index]; }
SQLStatement* SQLParserResult::getMutableStatement(size_t index) { return (*statements_)[index]; }
size_t SQLParserResult::size() const { return statements_->size(); }
bool SQLParserResult::isValid() const { return isValid_; }
const char* SQLParserResult::errorMsg() const { return errorMsg_; }
int SQLParserResult::errorLine() const { return errorLine_; }
int SQLParserResult::errorColumn() const { return errorColumn_; }
void SQLParserResult::setIsValid(bool isValid) { isValid_ = isValid; }
void SQLParserResult::setErrorDetails(char* errorMsg, int errorLine, int errorColumn) {
errorMsg_ = errorMsg;
errorLine_ = errorLine;
errorColumn_ = errorColumn;
}
const std::vector<SQLStatement*>& SQLParserResult::getStatements() const { return *statements_; }
std::vector<SQLStatement*> SQLParserResult::releaseStatements() {
std::vector<SQLStatement*> copy = *statements_;
statements_->clear();
return copy;
}
void SQLParserResult::reset() {
for (SQLStatement* statement : *statements_) {
delete statement;
}
delete statements_;
delete parameters_;
isValid_ = false;
free(errorMsg_);
errorMsg_ = nullptr;
errorLine_ = -1;
errorColumn_ = -1;
}
// Does NOT take ownership.
void SQLParserResult::addParameter(Expr* parameter) {
parameters_->push_back(parameter);
std::sort(parameters_->begin(), parameters_->end(), [](const Expr* a, const Expr* b) { return a->ival < b->ival; });
}
const std::vector<Expr*>& SQLParserResult::parameters() { return *parameters_; }
} // namespace hsql

View File

@ -0,0 +1,95 @@
#ifndef SQLPARSER_SQLPARSER_RESULT_H
#define SQLPARSER_SQLPARSER_RESULT_H
#include "sqlparser_win.h"
#include "sql/SQLStatement.h"
namespace hsql {
// Represents the result of the SQLParser.
// If parsing was successful it contains a list of SQLStatement.
class SQLParser_API SQLParserResult {
public:
// Initialize with empty statement list.
SQLParserResult();
// Initialize with a single statement.
// Takes ownership of the statement.
SQLParserResult(SQLStatement* stmt);
// Move constructor.
SQLParserResult(SQLParserResult&& moved);
SQLParserResult& operator=(SQLParserResult&& moved);
// Deletes all statements in the result.
virtual ~SQLParserResult();
// Set whether parsing was successful.
void setIsValid(bool isValid);
// Returns true if parsing was successful.
bool isValid() const;
// Returns the number of statements in the result.
size_t size() const;
// Set the details of the error, if available.
// Takes ownership of errorMsg.
void setErrorDetails(char* errorMsg, int errorLine, int errorColumn);
// Returns the error message, if an error occurred.
const char* errorMsg() const;
// Returns the line number of the occurrance of the error in the query.
int errorLine() const;
// Returns the column number of the occurrance of the error in the query.
int errorColumn() const;
// Adds a statement to the result list of statements.
// SQLParserResult takes ownership of the statement.
void addStatement(SQLStatement* stmt);
// Gets the SQL statement with the given index.
const SQLStatement* getStatement(size_t index) const;
// Gets the non const SQL statement with the given index.
SQLStatement* getMutableStatement(size_t index);
// Get the list of all statements.
const std::vector<SQLStatement*>& getStatements() const;
// Returns a copy of the list of all statements in this result.
// Removes them from this result.
std::vector<SQLStatement*> releaseStatements();
// Deletes all statements and other data within the result.
void reset();
// Does NOT take ownership.
void addParameter(Expr* parameter);
const std::vector<Expr*>& parameters();
private:
// List of statements within the result.
std::vector<SQLStatement*>* statements_;
// Flag indicating the parsing was successful.
bool isValid_;
// Error message, if an error occurred.
char* errorMsg_;
// Line number of the occurrance of the error in the query.
int errorLine_;
// Column number of the occurrance of the error in the query.
int errorColumn_;
// Does NOT have ownership.
std::vector<Expr*>* parameters_;
};
} // namespace hsql
#endif // SQLPARSER_SQLPARSER_RESULT_H

View File

@ -0,0 +1,2 @@
*.output
conflict_test.cpp

View File

@ -0,0 +1,39 @@
# bison's version is too old on OSX, allow user to pass in custom path
BISON?=bison
FLEX?=flex
OS_TYPE=$(shell uname)
ifeq ($(OS_TYPE), Darwin)
BREW_PREFIX=$(shell brew --prefix)
BREW_INSTALLED=$(shell echo $(BREW_PREFIX) | wc -w | xargs)
ifeq ($(BREW_INSTALLED), 0)
$(error On macOS, Homebrew (see https://brew.sh) is required to install recent Bison and Flex versions)
endif
endif
BISON_VERSION=$(shell $(BISON) --version | head -n 1 | grep -o '[0-9]\.[0-9]\+')
BISON_VERSION_SUPPORTED=$(shell awk -v a=$(BISON_VERSION) -v b="3.0" 'BEGIN { print (a >= b) ? 1 : 0 }')
ifneq ($(BISON_VERSION_SUPPORTED), 1)
$(error Bison version $(BISON_VERSION) not supported. If you are using macOS, `bison` uses the system default instead of the brew version. Run BISON=$(BREW_PREFIX)/opt/bison/bin/bison make)
endif
FLEX_VERSION=$(shell $(FLEX) --version | head -n 1 | grep -o '[0-9]\.[0-9]\+')
FLEX_VERSION_SUPPORTED=$(shell awk -v a=$(FLEX_VERSION) -v b="2.6" 'BEGIN { print (a >= b) ? 1 : 0 }')
ifneq ($(FLEX_VERSION_SUPPORTED), 1)
$(error Flex version $(FLEX_VERSION) not supported. If you are using macOS, `flex` uses the system default instead of the brew version. Run FLEX=$(BREW_PREFIX)/opt/flex/bin/flex make)
endif
all: bison_parser.cpp flex_lexer.cpp
bison_parser.cpp: bison_parser.y
$(BISON) bison_parser.y --output=bison_parser.cpp --defines=bison_parser.h --verbose
flex_lexer.cpp: flex_lexer.l
! $(FLEX) flex_lexer.l 2>&1 | grep "warning"
clean:
rm -f bison_parser.cpp flex_lexer.cpp bison_parser.h flex_lexer.h *.output
# Tests if the parser builds correctly and doesn't contain conflicts.
test:
! $(BISON) bison_parser.y -v --output=conflict_test.cpp 2>&1 | grep "conflict" >&2

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,370 @@
/* A Bison parser, made by GNU Bison 3.8.2. */
/* Bison interface for Yacc-like parsers in C
Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation,
Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
under terms of your choice, so long as that work isn't itself a
parser generator using the skeleton or a modified version thereof
as a parser skeleton. Alternatively, if you modify or redistribute
the parser skeleton itself, you may (at your option) remove this
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
especially those whose name start with YY_ or yy_. They are
private implementation details that can be changed or removed. */
#ifndef YY_HSQL_BISON_PARSER_H_INCLUDED
# define YY_HSQL_BISON_PARSER_H_INCLUDED
/* Debug traces. */
#ifndef HSQL_DEBUG
# if defined YYDEBUG
#if YYDEBUG
# define HSQL_DEBUG 1
# else
# define HSQL_DEBUG 0
# endif
# else /* ! defined YYDEBUG */
# define HSQL_DEBUG 0
# endif /* ! defined YYDEBUG */
#endif /* ! defined HSQL_DEBUG */
#if HSQL_DEBUG
extern int hsql_debug;
#endif
/* "%code requires" blocks. */
#line 38 "bison_parser.y"
// %code requires block
#include "../SQLParserResult.h"
#include "../sql/statements.h"
#include "parser_typedef.h"
// Auto update column and line number
#define YY_USER_ACTION \
yylloc->first_line = yylloc->last_line; \
yylloc->first_column = yylloc->last_column; \
for (int i = 0; yytext[i] != '\0'; i++) { \
yylloc->total_column++; \
yylloc->string_length++; \
if (yytext[i] == '\n') { \
yylloc->last_line++; \
yylloc->last_column = 0; \
} else { \
yylloc->last_column++; \
} \
}
#line 80 "bison_parser.h"
/* Token kinds. */
#ifndef HSQL_TOKENTYPE
# define HSQL_TOKENTYPE
enum hsql_tokentype
{
SQL_HSQL_EMPTY = -2,
SQL_YYEOF = 0, /* "end of file" */
SQL_HSQL_error = 256, /* error */
SQL_HSQL_UNDEF = 257, /* "invalid token" */
SQL_IDENTIFIER = 258, /* IDENTIFIER */
SQL_STRING = 259, /* STRING */
SQL_FLOATVAL = 260, /* FLOATVAL */
SQL_INTVAL = 261, /* INTVAL */
SQL_DEALLOCATE = 262, /* DEALLOCATE */
SQL_PARAMETERS = 263, /* PARAMETERS */
SQL_INTERSECT = 264, /* INTERSECT */
SQL_TEMPORARY = 265, /* TEMPORARY */
SQL_TIMESTAMP = 266, /* TIMESTAMP */
SQL_DISTINCT = 267, /* DISTINCT */
SQL_NVARCHAR = 268, /* NVARCHAR */
SQL_RESTRICT = 269, /* RESTRICT */
SQL_TRUNCATE = 270, /* TRUNCATE */
SQL_ANALYZE = 271, /* ANALYZE */
SQL_BETWEEN = 272, /* BETWEEN */
SQL_CASCADE = 273, /* CASCADE */
SQL_COLUMNS = 274, /* COLUMNS */
SQL_CONTROL = 275, /* CONTROL */
SQL_DEFAULT = 276, /* DEFAULT */
SQL_EXECUTE = 277, /* EXECUTE */
SQL_EXPLAIN = 278, /* EXPLAIN */
SQL_INTEGER = 279, /* INTEGER */
SQL_NATURAL = 280, /* NATURAL */
SQL_PREPARE = 281, /* PREPARE */
SQL_PRIMARY = 282, /* PRIMARY */
SQL_SCHEMAS = 283, /* SCHEMAS */
SQL_CHARACTER_VARYING = 284, /* CHARACTER_VARYING */
SQL_REAL = 285, /* REAL */
SQL_DECIMAL = 286, /* DECIMAL */
SQL_SMALLINT = 287, /* SMALLINT */
SQL_BIGINT = 288, /* BIGINT */
SQL_SPATIAL = 289, /* SPATIAL */
SQL_VARCHAR = 290, /* VARCHAR */
SQL_VIRTUAL = 291, /* VIRTUAL */
SQL_DESCRIBE = 292, /* DESCRIBE */
SQL_BEFORE = 293, /* BEFORE */
SQL_COLUMN = 294, /* COLUMN */
SQL_CREATE = 295, /* CREATE */
SQL_DELETE = 296, /* DELETE */
SQL_DIRECT = 297, /* DIRECT */
SQL_DOUBLE = 298, /* DOUBLE */
SQL_ESCAPE = 299, /* ESCAPE */
SQL_EXCEPT = 300, /* EXCEPT */
SQL_EXISTS = 301, /* EXISTS */
SQL_EXTRACT = 302, /* EXTRACT */
SQL_CAST = 303, /* CAST */
SQL_FORMAT = 304, /* FORMAT */
SQL_GLOBAL = 305, /* GLOBAL */
SQL_HAVING = 306, /* HAVING */
SQL_IMPORT = 307, /* IMPORT */
SQL_INSERT = 308, /* INSERT */
SQL_ISNULL = 309, /* ISNULL */
SQL_OFFSET = 310, /* OFFSET */
SQL_RENAME = 311, /* RENAME */
SQL_SCHEMA = 312, /* SCHEMA */
SQL_SELECT = 313, /* SELECT */
SQL_SORTED = 314, /* SORTED */
SQL_TABLES = 315, /* TABLES */
SQL_UNIQUE = 316, /* UNIQUE */
SQL_UNLOAD = 317, /* UNLOAD */
SQL_UPDATE = 318, /* UPDATE */
SQL_VALUES = 319, /* VALUES */
SQL_AFTER = 320, /* AFTER */
SQL_ALTER = 321, /* ALTER */
SQL_CROSS = 322, /* CROSS */
SQL_DELTA = 323, /* DELTA */
SQL_FLOAT = 324, /* FLOAT */
SQL_GROUP = 325, /* GROUP */
SQL_INDEX = 326, /* INDEX */
SQL_INNER = 327, /* INNER */
SQL_LIMIT = 328, /* LIMIT */
SQL_LOCAL = 329, /* LOCAL */
SQL_MERGE = 330, /* MERGE */
SQL_MINUS = 331, /* MINUS */
SQL_ORDER = 332, /* ORDER */
SQL_OVER = 333, /* OVER */
SQL_OUTER = 334, /* OUTER */
SQL_RIGHT = 335, /* RIGHT */
SQL_TABLE = 336, /* TABLE */
SQL_UNION = 337, /* UNION */
SQL_USING = 338, /* USING */
SQL_WHERE = 339, /* WHERE */
SQL_CALL = 340, /* CALL */
SQL_CASE = 341, /* CASE */
SQL_CHAR = 342, /* CHAR */
SQL_COPY = 343, /* COPY */
SQL_DATE = 344, /* DATE */
SQL_DATETIME = 345, /* DATETIME */
SQL_DESC = 346, /* DESC */
SQL_DROP = 347, /* DROP */
SQL_ELSE = 348, /* ELSE */
SQL_FILE = 349, /* FILE */
SQL_FROM = 350, /* FROM */
SQL_FULL = 351, /* FULL */
SQL_HASH = 352, /* HASH */
SQL_HINT = 353, /* HINT */
SQL_INTO = 354, /* INTO */
SQL_JOIN = 355, /* JOIN */
SQL_LEFT = 356, /* LEFT */
SQL_LIKE = 357, /* LIKE */
SQL_LOAD = 358, /* LOAD */
SQL_LONG = 359, /* LONG */
SQL_NULL = 360, /* NULL */
SQL_PARTITION = 361, /* PARTITION */
SQL_PLAN = 362, /* PLAN */
SQL_SHOW = 363, /* SHOW */
SQL_TEXT = 364, /* TEXT */
SQL_THEN = 365, /* THEN */
SQL_TIME = 366, /* TIME */
SQL_VIEW = 367, /* VIEW */
SQL_WHEN = 368, /* WHEN */
SQL_WITH = 369, /* WITH */
SQL_ADD = 370, /* ADD */
SQL_ALL = 371, /* ALL */
SQL_AND = 372, /* AND */
SQL_ASC = 373, /* ASC */
SQL_END = 374, /* END */
SQL_FOR = 375, /* FOR */
SQL_INT = 376, /* INT */
SQL_KEY = 377, /* KEY */
SQL_NOT = 378, /* NOT */
SQL_OFF = 379, /* OFF */
SQL_SET = 380, /* SET */
SQL_TOP = 381, /* TOP */
SQL_AS = 382, /* AS */
SQL_BY = 383, /* BY */
SQL_IF = 384, /* IF */
SQL_IN = 385, /* IN */
SQL_IS = 386, /* IS */
SQL_OF = 387, /* OF */
SQL_ON = 388, /* ON */
SQL_OR = 389, /* OR */
SQL_TO = 390, /* TO */
SQL_NO = 391, /* NO */
SQL_ARRAY = 392, /* ARRAY */
SQL_CONCAT = 393, /* CONCAT */
SQL_ILIKE = 394, /* ILIKE */
SQL_SECOND = 395, /* SECOND */
SQL_MINUTE = 396, /* MINUTE */
SQL_HOUR = 397, /* HOUR */
SQL_DAY = 398, /* DAY */
SQL_MONTH = 399, /* MONTH */
SQL_YEAR = 400, /* YEAR */
SQL_SECONDS = 401, /* SECONDS */
SQL_MINUTES = 402, /* MINUTES */
SQL_HOURS = 403, /* HOURS */
SQL_DAYS = 404, /* DAYS */
SQL_MONTHS = 405, /* MONTHS */
SQL_YEARS = 406, /* YEARS */
SQL_INTERVAL = 407, /* INTERVAL */
SQL_TRUE = 408, /* TRUE */
SQL_FALSE = 409, /* FALSE */
SQL_BOOLEAN = 410, /* BOOLEAN */
SQL_TRANSACTION = 411, /* TRANSACTION */
SQL_BEGIN = 412, /* BEGIN */
SQL_COMMIT = 413, /* COMMIT */
SQL_ROLLBACK = 414, /* ROLLBACK */
SQL_NOWAIT = 415, /* NOWAIT */
SQL_SKIP = 416, /* SKIP */
SQL_LOCKED = 417, /* LOCKED */
SQL_SHARE = 418, /* SHARE */
SQL_RANGE = 419, /* RANGE */
SQL_ROWS = 420, /* ROWS */
SQL_GROUPS = 421, /* GROUPS */
SQL_UNBOUNDED = 422, /* UNBOUNDED */
SQL_FOLLOWING = 423, /* FOLLOWING */
SQL_PRECEDING = 424, /* PRECEDING */
SQL_CURRENT_ROW = 425, /* CURRENT_ROW */
SQL_EQUALS = 426, /* EQUALS */
SQL_NOTEQUALS = 427, /* NOTEQUALS */
SQL_LESS = 428, /* LESS */
SQL_GREATER = 429, /* GREATER */
SQL_LESSEQ = 430, /* LESSEQ */
SQL_GREATEREQ = 431, /* GREATEREQ */
SQL_NOTNULL = 432, /* NOTNULL */
SQL_UMINUS = 433 /* UMINUS */
};
typedef enum hsql_tokentype hsql_token_kind_t;
#endif
/* Value type. */
#if ! defined HSQL_STYPE && ! defined HSQL_STYPE_IS_DECLARED
union HSQL_STYPE
{
#line 96 "bison_parser.y"
// clang-format on
bool bval;
char* sval;
double fval;
int64_t ival;
uintmax_t uval;
// statements
hsql::AlterStatement* alter_stmt;
hsql::CreateStatement* create_stmt;
hsql::DeleteStatement* delete_stmt;
hsql::DropStatement* drop_stmt;
hsql::ExecuteStatement* exec_stmt;
hsql::ExportStatement* export_stmt;
hsql::ImportStatement* import_stmt;
hsql::InsertStatement* insert_stmt;
hsql::PrepareStatement* prep_stmt;
hsql::SelectStatement* select_stmt;
hsql::ShowStatement* show_stmt;
hsql::SQLStatement* statement;
hsql::TransactionStatement* transaction_stmt;
hsql::UpdateStatement* update_stmt;
hsql::Alias* alias_t;
hsql::AlterAction* alter_action_t;
hsql::ColumnDefinition* column_t;
hsql::ColumnType column_type_t;
hsql::ConstraintType column_constraint_t;
hsql::DatetimeField datetime_field;
hsql::DropColumnAction* drop_action_t;
hsql::Expr* expr;
hsql::FrameBound* frame_bound;
hsql::FrameDescription* frame_description;
hsql::FrameType frame_type;
hsql::GroupByDescription* group_t;
hsql::ImportType import_type_t;
hsql::JoinType join_type;
hsql::LimitDescription* limit;
hsql::LockingClause* locking_t;
hsql::OrderDescription* order;
hsql::OrderType order_type;
hsql::SetOperation* set_operator_t;
hsql::TableConstraint* table_constraint_t;
hsql::TableElement* table_element_t;
hsql::TableName table_name;
hsql::TableRef* table;
hsql::UpdateClause* update_t;
hsql::WindowDescription* window_description;
hsql::WithDescription* with_description_t;
std::vector<char*>* str_vec;
std::unordered_set<hsql::ConstraintType>* column_constraint_set;
std::vector<hsql::Expr*>* expr_vec;
std::vector<hsql::OrderDescription*>* order_vec;
std::vector<hsql::SQLStatement*>* stmt_vec;
std::vector<hsql::TableElement*>* table_element_vec;
std::vector<hsql::TableRef*>* table_vec;
std::vector<hsql::UpdateClause*>* update_vec;
std::vector<hsql::WithDescription*>* with_description_vec;
std::vector<hsql::LockingClause*>* locking_clause_vec;
std::pair<int64_t, int64_t>* ival_pair;
hsql::RowLockMode lock_mode_t;
hsql::RowLockWaitPolicy lock_wait_policy_t;
#line 343 "bison_parser.h"
};
typedef union HSQL_STYPE HSQL_STYPE;
# define HSQL_STYPE_IS_TRIVIAL 1
# define HSQL_STYPE_IS_DECLARED 1
#endif
/* Location type. */
#if ! defined HSQL_LTYPE && ! defined HSQL_LTYPE_IS_DECLARED
typedef struct HSQL_LTYPE HSQL_LTYPE;
struct HSQL_LTYPE
{
int first_line;
int first_column;
int last_line;
int last_column;
};
# define HSQL_LTYPE_IS_DECLARED 1
# define HSQL_LTYPE_IS_TRIVIAL 1
#endif
int hsql_parse (hsql::SQLParserResult* result, yyscan_t scanner);
#endif /* !YY_HSQL_BISON_PARSER_H_INCLUDED */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,742 @@
#ifndef hsql_HEADER_H
#define hsql_HEADER_H 1
#define hsql_IN_HEADER 1
#line 5 "flex_lexer.h"
#line 7 "flex_lexer.h"
#define YY_INT_ALIGNED short int
/* A lexical scanner generated by flex */
#define FLEX_SCANNER
#define YY_FLEX_MAJOR_VERSION 2
#define YY_FLEX_MINOR_VERSION 6
#define YY_FLEX_SUBMINOR_VERSION 4
#if YY_FLEX_SUBMINOR_VERSION > 0
#define FLEX_BETA
#endif
#ifdef yy_create_buffer
#define hsql__create_buffer_ALREADY_DEFINED
#else
#define yy_create_buffer hsql__create_buffer
#endif
#ifdef yy_delete_buffer
#define hsql__delete_buffer_ALREADY_DEFINED
#else
#define yy_delete_buffer hsql__delete_buffer
#endif
#ifdef yy_scan_buffer
#define hsql__scan_buffer_ALREADY_DEFINED
#else
#define yy_scan_buffer hsql__scan_buffer
#endif
#ifdef yy_scan_string
#define hsql__scan_string_ALREADY_DEFINED
#else
#define yy_scan_string hsql__scan_string
#endif
#ifdef yy_scan_bytes
#define hsql__scan_bytes_ALREADY_DEFINED
#else
#define yy_scan_bytes hsql__scan_bytes
#endif
#ifdef yy_init_buffer
#define hsql__init_buffer_ALREADY_DEFINED
#else
#define yy_init_buffer hsql__init_buffer
#endif
#ifdef yy_flush_buffer
#define hsql__flush_buffer_ALREADY_DEFINED
#else
#define yy_flush_buffer hsql__flush_buffer
#endif
#ifdef yy_load_buffer_state
#define hsql__load_buffer_state_ALREADY_DEFINED
#else
#define yy_load_buffer_state hsql__load_buffer_state
#endif
#ifdef yy_switch_to_buffer
#define hsql__switch_to_buffer_ALREADY_DEFINED
#else
#define yy_switch_to_buffer hsql__switch_to_buffer
#endif
#ifdef yypush_buffer_state
#define hsql_push_buffer_state_ALREADY_DEFINED
#else
#define yypush_buffer_state hsql_push_buffer_state
#endif
#ifdef yypop_buffer_state
#define hsql_pop_buffer_state_ALREADY_DEFINED
#else
#define yypop_buffer_state hsql_pop_buffer_state
#endif
#ifdef yyensure_buffer_stack
#define hsql_ensure_buffer_stack_ALREADY_DEFINED
#else
#define yyensure_buffer_stack hsql_ensure_buffer_stack
#endif
#ifdef yylex
#define hsql_lex_ALREADY_DEFINED
#else
#define yylex hsql_lex
#endif
#ifdef yyrestart
#define hsql_restart_ALREADY_DEFINED
#else
#define yyrestart hsql_restart
#endif
#ifdef yylex_init
#define hsql_lex_init_ALREADY_DEFINED
#else
#define yylex_init hsql_lex_init
#endif
#ifdef yylex_init_extra
#define hsql_lex_init_extra_ALREADY_DEFINED
#else
#define yylex_init_extra hsql_lex_init_extra
#endif
#ifdef yylex_destroy
#define hsql_lex_destroy_ALREADY_DEFINED
#else
#define yylex_destroy hsql_lex_destroy
#endif
#ifdef yyget_debug
#define hsql_get_debug_ALREADY_DEFINED
#else
#define yyget_debug hsql_get_debug
#endif
#ifdef yyset_debug
#define hsql_set_debug_ALREADY_DEFINED
#else
#define yyset_debug hsql_set_debug
#endif
#ifdef yyget_extra
#define hsql_get_extra_ALREADY_DEFINED
#else
#define yyget_extra hsql_get_extra
#endif
#ifdef yyset_extra
#define hsql_set_extra_ALREADY_DEFINED
#else
#define yyset_extra hsql_set_extra
#endif
#ifdef yyget_in
#define hsql_get_in_ALREADY_DEFINED
#else
#define yyget_in hsql_get_in
#endif
#ifdef yyset_in
#define hsql_set_in_ALREADY_DEFINED
#else
#define yyset_in hsql_set_in
#endif
#ifdef yyget_out
#define hsql_get_out_ALREADY_DEFINED
#else
#define yyget_out hsql_get_out
#endif
#ifdef yyset_out
#define hsql_set_out_ALREADY_DEFINED
#else
#define yyset_out hsql_set_out
#endif
#ifdef yyget_leng
#define hsql_get_leng_ALREADY_DEFINED
#else
#define yyget_leng hsql_get_leng
#endif
#ifdef yyget_text
#define hsql_get_text_ALREADY_DEFINED
#else
#define yyget_text hsql_get_text
#endif
#ifdef yyget_lineno
#define hsql_get_lineno_ALREADY_DEFINED
#else
#define yyget_lineno hsql_get_lineno
#endif
#ifdef yyset_lineno
#define hsql_set_lineno_ALREADY_DEFINED
#else
#define yyset_lineno hsql_set_lineno
#endif
#ifdef yyget_column
#define hsql_get_column_ALREADY_DEFINED
#else
#define yyget_column hsql_get_column
#endif
#ifdef yyset_column
#define hsql_set_column_ALREADY_DEFINED
#else
#define yyset_column hsql_set_column
#endif
#ifdef yywrap
#define hsql_wrap_ALREADY_DEFINED
#else
#define yywrap hsql_wrap
#endif
#ifdef yyget_lval
#define hsql_get_lval_ALREADY_DEFINED
#else
#define yyget_lval hsql_get_lval
#endif
#ifdef yyset_lval
#define hsql_set_lval_ALREADY_DEFINED
#else
#define yyset_lval hsql_set_lval
#endif
#ifdef yyget_lloc
#define hsql_get_lloc_ALREADY_DEFINED
#else
#define yyget_lloc hsql_get_lloc
#endif
#ifdef yyset_lloc
#define hsql_set_lloc_ALREADY_DEFINED
#else
#define yyset_lloc hsql_set_lloc
#endif
#ifdef yyalloc
#define hsql_alloc_ALREADY_DEFINED
#else
#define yyalloc hsql_alloc
#endif
#ifdef yyrealloc
#define hsql_realloc_ALREADY_DEFINED
#else
#define yyrealloc hsql_realloc
#endif
#ifdef yyfree
#define hsql_free_ALREADY_DEFINED
#else
#define yyfree hsql_free
#endif
/* First, we deal with platform-specific or compiler-specific issues. */
/* begin standard C headers. */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
/* end standard C headers. */
/* flex integer type definitions */
#ifndef FLEXINT_H
#define FLEXINT_H
/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
* if you want the limit (max/min) macros for int types.
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS 1
#endif
#include <inttypes.h>
typedef int8_t flex_int8_t;
typedef uint8_t flex_uint8_t;
typedef int16_t flex_int16_t;
typedef uint16_t flex_uint16_t;
typedef int32_t flex_int32_t;
typedef uint32_t flex_uint32_t;
#else
typedef signed char flex_int8_t;
typedef short int flex_int16_t;
typedef int flex_int32_t;
typedef unsigned char flex_uint8_t;
typedef unsigned short int flex_uint16_t;
typedef unsigned int flex_uint32_t;
/* Limits of integral types. */
#ifndef INT8_MIN
#define INT8_MIN (-128)
#endif
#ifndef INT16_MIN
#define INT16_MIN (-32767-1)
#endif
#ifndef INT32_MIN
#define INT32_MIN (-2147483647-1)
#endif
#ifndef INT8_MAX
#define INT8_MAX (127)
#endif
#ifndef INT16_MAX
#define INT16_MAX (32767)
#endif
#ifndef INT32_MAX
#define INT32_MAX (2147483647)
#endif
#ifndef UINT8_MAX
#define UINT8_MAX (255U)
#endif
#ifndef UINT16_MAX
#define UINT16_MAX (65535U)
#endif
#ifndef UINT32_MAX
#define UINT32_MAX (4294967295U)
#endif
#ifndef SIZE_MAX
#define SIZE_MAX (~(size_t)0)
#endif
#endif /* ! C99 */
#endif /* ! FLEXINT_H */
/* begin standard C++ headers. */
/* TODO: this is always defined, so inline it */
#define yyconst const
#if defined(__GNUC__) && __GNUC__ >= 3
#define yynoreturn __attribute__((__noreturn__))
#else
#define yynoreturn
#endif
/* An opaque pointer. */
#ifndef YY_TYPEDEF_YY_SCANNER_T
#define YY_TYPEDEF_YY_SCANNER_T
typedef void* yyscan_t;
#endif
/* For convenience, these vars (plus the bison vars far below)
are macros in the reentrant scanner. */
#define yyin yyg->yyin_r
#define yyout yyg->yyout_r
#define yyextra yyg->yyextra_r
#define yyleng yyg->yyleng_r
#define yytext yyg->yytext_r
#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
#define yy_flex_debug yyg->yy_flex_debug_r
/* Size of default input buffer. */
#ifndef YY_BUF_SIZE
#ifdef __ia64__
/* On IA-64, the buffer size is 16k, not 8k.
* Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
* Ditto for the __ia64__ case accordingly.
*/
#define YY_BUF_SIZE 32768
#else
#define YY_BUF_SIZE 16384
#endif /* __ia64__ */
#endif
#ifndef YY_TYPEDEF_YY_BUFFER_STATE
#define YY_TYPEDEF_YY_BUFFER_STATE
typedef struct yy_buffer_state *YY_BUFFER_STATE;
#endif
#ifndef YY_TYPEDEF_YY_SIZE_T
#define YY_TYPEDEF_YY_SIZE_T
typedef size_t yy_size_t;
#endif
#ifndef YY_STRUCT_YY_BUFFER_STATE
#define YY_STRUCT_YY_BUFFER_STATE
struct yy_buffer_state
{
FILE *yy_input_file;
char *yy_ch_buf; /* input buffer */
char *yy_buf_pos; /* current position in input buffer */
/* Size of input buffer in bytes, not including room for EOB
* characters.
*/
int yy_buf_size;
/* Number of characters read into yy_ch_buf, not including EOB
* characters.
*/
int yy_n_chars;
/* Whether we "own" the buffer - i.e., we know we created it,
* and can realloc() it to grow it, and should free() it to
* delete it.
*/
int yy_is_our_buffer;
/* Whether this is an "interactive" input source; if so, and
* if we're using stdio for input, then we want to use getc()
* instead of fread(), to make sure we stop fetching input after
* each newline.
*/
int yy_is_interactive;
/* Whether we're considered to be at the beginning of a line.
* If so, '^' rules will be active on the next match, otherwise
* not.
*/
int yy_at_bol;
int yy_bs_lineno; /**< The line count. */
int yy_bs_column; /**< The column count. */
/* Whether to try to fill the input buffer when we reach the
* end of it.
*/
int yy_fill_buffer;
int yy_buffer_status;
};
#endif /* !YY_STRUCT_YY_BUFFER_STATE */
void yyrestart ( FILE *input_file , yyscan_t yyscanner );
void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner );
void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
void yypop_buffer_state ( yyscan_t yyscanner );
YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner );
YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner );
YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner );
void *yyalloc ( yy_size_t , yyscan_t yyscanner );
void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner );
void yyfree ( void * , yyscan_t yyscanner );
/* Begin user sect3 */
#define hsql_wrap(yyscanner) (/*CONSTCOND*/1)
#define YY_SKIP_YYWRAP
#define yytext_ptr yytext_r
#ifdef YY_HEADER_EXPORT_START_CONDITIONS
#define INITIAL 0
#define singlequotedstring 1
#define COMMENT 2
#endif
#ifndef YY_NO_UNISTD_H
/* Special case for "unistd.h", since it is non-ANSI. We include it way
* down here because we want the user's section 1 to have been scanned first.
* The user has a chance to override it with an option.
*/
#if defined(_WIN32) || defined(_WIN64)
#include <io.h>
#else
#include <unistd.h>
#endif
#endif
#ifndef YY_EXTRA_TYPE
#define YY_EXTRA_TYPE void *
#endif
int yylex_init (yyscan_t* scanner);
int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner);
/* Accessor methods to globals.
These are made visible to non-reentrant scanners for convenience. */
int yylex_destroy ( yyscan_t yyscanner );
int yyget_debug ( yyscan_t yyscanner );
void yyset_debug ( int debug_flag , yyscan_t yyscanner );
YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner );
void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner );
FILE *yyget_in ( yyscan_t yyscanner );
void yyset_in ( FILE * _in_str , yyscan_t yyscanner );
FILE *yyget_out ( yyscan_t yyscanner );
void yyset_out ( FILE * _out_str , yyscan_t yyscanner );
int yyget_leng ( yyscan_t yyscanner );
char *yyget_text ( yyscan_t yyscanner );
int yyget_lineno ( yyscan_t yyscanner );
void yyset_lineno ( int _line_number , yyscan_t yyscanner );
int yyget_column ( yyscan_t yyscanner );
void yyset_column ( int _column_no , yyscan_t yyscanner );
YYSTYPE * yyget_lval ( yyscan_t yyscanner );
void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner );
YYLTYPE *yyget_lloc ( yyscan_t yyscanner );
void yyset_lloc ( YYLTYPE * yylloc_param , yyscan_t yyscanner );
/* Macros after this point can all be overridden by user definitions in
* section 1.
*/
#ifndef YY_SKIP_YYWRAP
#ifdef __cplusplus
extern "C" int yywrap ( yyscan_t yyscanner );
#else
extern int yywrap ( yyscan_t yyscanner );
#endif
#endif
#ifndef yytext_ptr
static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner);
#endif
#ifdef YY_NEED_STRLEN
static int yy_flex_strlen ( const char * , yyscan_t yyscanner);
#endif
#ifndef YY_NO_INPUT
#endif
/* Amount of stuff to slurp up with each read. */
#ifndef YY_READ_BUF_SIZE
#ifdef __ia64__
/* On IA-64, the buffer size is 16k, not 8k */
#define YY_READ_BUF_SIZE 16384
#else
#define YY_READ_BUF_SIZE 8192
#endif /* __ia64__ */
#endif
/* Number of entries by which start-condition stack grows. */
#ifndef YY_START_STACK_INCR
#define YY_START_STACK_INCR 25
#endif
/* Default declaration of generated scanner - a define so the user can
* easily add parameters.
*/
#ifndef YY_DECL
#define YY_DECL_IS_OURS 1
extern int yylex \
(YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner);
#define YY_DECL int yylex \
(YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner)
#endif /* !YY_DECL */
/* yy_get_previous_state - get the state just before the EOB char was reached */
#undef YY_NEW_FILE
#undef YY_FLUSH_BUFFER
#undef yy_set_bol
#undef yy_new_buffer
#undef yy_set_interactive
#undef YY_DO_BEFORE_ACTION
#ifdef YY_DECL_IS_OURS
#undef YY_DECL_IS_OURS
#undef YY_DECL
#endif
#ifndef hsql__create_buffer_ALREADY_DEFINED
#undef yy_create_buffer
#endif
#ifndef hsql__delete_buffer_ALREADY_DEFINED
#undef yy_delete_buffer
#endif
#ifndef hsql__scan_buffer_ALREADY_DEFINED
#undef yy_scan_buffer
#endif
#ifndef hsql__scan_string_ALREADY_DEFINED
#undef yy_scan_string
#endif
#ifndef hsql__scan_bytes_ALREADY_DEFINED
#undef yy_scan_bytes
#endif
#ifndef hsql__init_buffer_ALREADY_DEFINED
#undef yy_init_buffer
#endif
#ifndef hsql__flush_buffer_ALREADY_DEFINED
#undef yy_flush_buffer
#endif
#ifndef hsql__load_buffer_state_ALREADY_DEFINED
#undef yy_load_buffer_state
#endif
#ifndef hsql__switch_to_buffer_ALREADY_DEFINED
#undef yy_switch_to_buffer
#endif
#ifndef hsql_push_buffer_state_ALREADY_DEFINED
#undef yypush_buffer_state
#endif
#ifndef hsql_pop_buffer_state_ALREADY_DEFINED
#undef yypop_buffer_state
#endif
#ifndef hsql_ensure_buffer_stack_ALREADY_DEFINED
#undef yyensure_buffer_stack
#endif
#ifndef hsql_lex_ALREADY_DEFINED
#undef yylex
#endif
#ifndef hsql_restart_ALREADY_DEFINED
#undef yyrestart
#endif
#ifndef hsql_lex_init_ALREADY_DEFINED
#undef yylex_init
#endif
#ifndef hsql_lex_init_extra_ALREADY_DEFINED
#undef yylex_init_extra
#endif
#ifndef hsql_lex_destroy_ALREADY_DEFINED
#undef yylex_destroy
#endif
#ifndef hsql_get_debug_ALREADY_DEFINED
#undef yyget_debug
#endif
#ifndef hsql_set_debug_ALREADY_DEFINED
#undef yyset_debug
#endif
#ifndef hsql_get_extra_ALREADY_DEFINED
#undef yyget_extra
#endif
#ifndef hsql_set_extra_ALREADY_DEFINED
#undef yyset_extra
#endif
#ifndef hsql_get_in_ALREADY_DEFINED
#undef yyget_in
#endif
#ifndef hsql_set_in_ALREADY_DEFINED
#undef yyset_in
#endif
#ifndef hsql_get_out_ALREADY_DEFINED
#undef yyget_out
#endif
#ifndef hsql_set_out_ALREADY_DEFINED
#undef yyset_out
#endif
#ifndef hsql_get_leng_ALREADY_DEFINED
#undef yyget_leng
#endif
#ifndef hsql_get_text_ALREADY_DEFINED
#undef yyget_text
#endif
#ifndef hsql_get_lineno_ALREADY_DEFINED
#undef yyget_lineno
#endif
#ifndef hsql_set_lineno_ALREADY_DEFINED
#undef yyset_lineno
#endif
#ifndef hsql_get_column_ALREADY_DEFINED
#undef yyget_column
#endif
#ifndef hsql_set_column_ALREADY_DEFINED
#undef yyset_column
#endif
#ifndef hsql_wrap_ALREADY_DEFINED
#undef yywrap
#endif
#ifndef hsql_get_lval_ALREADY_DEFINED
#undef yyget_lval
#endif
#ifndef hsql_set_lval_ALREADY_DEFINED
#undef yyset_lval
#endif
#ifndef hsql_get_lloc_ALREADY_DEFINED
#undef yyget_lloc
#endif
#ifndef hsql_set_lloc_ALREADY_DEFINED
#undef yyset_lloc
#endif
#ifndef hsql_alloc_ALREADY_DEFINED
#undef yyalloc
#endif
#ifndef hsql_realloc_ALREADY_DEFINED
#undef yyrealloc
#endif
#ifndef hsql_free_ALREADY_DEFINED
#undef yyfree
#endif
#ifndef hsql_text_ALREADY_DEFINED
#undef yytext
#endif
#ifndef hsql_leng_ALREADY_DEFINED
#undef yyleng
#endif
#ifndef hsql_in_ALREADY_DEFINED
#undef yyin
#endif
#ifndef hsql_out_ALREADY_DEFINED
#undef yyout
#endif
#ifndef hsql__flex_debug_ALREADY_DEFINED
#undef yy_flex_debug
#endif
#ifndef hsql_lineno_ALREADY_DEFINED
#undef yylineno
#endif
#ifndef hsql_tables_fload_ALREADY_DEFINED
#undef yytables_fload
#endif
#ifndef hsql_tables_destroy_ALREADY_DEFINED
#undef yytables_destroy
#endif
#ifndef hsql_TABLES_NAME_ALREADY_DEFINED
#undef yyTABLES_NAME
#endif
#line 285 "flex_lexer.l"
#line 736 "flex_lexer.h"
#undef hsql_IN_HEADER
#endif /* hsql_HEADER_H */

View File

@ -0,0 +1,292 @@
/**
* lexer
*
*
*/
/***************************
** Section 1: Definitions
***************************/
%{
#include "../sql/Expr.h"
#include "bison_parser.h"
#include <climits>
#include <stdio.h>
#include <sstream>
#define TOKEN(name) { return SQL_##name; }
static thread_local std::stringstream strbuf;
%}
%x singlequotedstring
/***************************
** Section 2: Rules
***************************/
/* Define the output files */
%option header-file="flex_lexer.h"
%option outfile="flex_lexer.cpp"
/* Make reentrant */
%option reentrant
%option bison-bridge
/* performance tweeks */
%option never-interactive
%option batch
/* other flags */
%option noyywrap
%option nounput
%option warn
%option case-insensitive
%option prefix="hsql_"
%option bison-locations
/* %option nodefault */
%s COMMENT
/***************************
** Section 3: Rules
***************************/
%%
-- BEGIN(COMMENT);
<COMMENT>[^\n]* /* skipping comment content until a end of line is read */;
<COMMENT>\n BEGIN(INITIAL);
[ \t\n]+ /* skip whitespace */;
ADD TOKEN(ADD)
AFTER TOKEN(AFTER)
ALL TOKEN(ALL)
ALTER TOKEN(ALTER)
ANALYZE TOKEN(ANALYZE)
AND TOKEN(AND)
ARRAY TOKEN(ARRAY)
AS TOKEN(AS)
ASC TOKEN(ASC)
BEFORE TOKEN(BEFORE)
BEGIN TOKEN(BEGIN)
BETWEEN TOKEN(BETWEEN)
BIGINT TOKEN(BIGINT)
BOOLEAN TOKEN(BOOLEAN)
BY TOKEN(BY)
CALL TOKEN(CALL)
CASCADE TOKEN(CASCADE)
CASE TOKEN(CASE)
CAST TOKEN(CAST)
CHAR TOKEN(CHAR)
COLUMN TOKEN(COLUMN)
COLUMNS TOKEN(COLUMNS)
COMMIT TOKEN(COMMIT)
CONTROL TOKEN(CONTROL)
COPY TOKEN(COPY)
CREATE TOKEN(CREATE)
CROSS TOKEN(CROSS)
DATE TOKEN(DATE)
DATETIME TOKEN(DATETIME)
DAY TOKEN(DAY)
DAYS TOKEN(DAYS)
DEALLOCATE TOKEN(DEALLOCATE)
DECIMAL TOKEN(DECIMAL)
DEFAULT TOKEN(DEFAULT)
DELETE TOKEN(DELETE)
DELTA TOKEN(DELTA)
DESC TOKEN(DESC)
DESCRIBE TOKEN(DESCRIBE)
DIRECT TOKEN(DIRECT)
DISTINCT TOKEN(DISTINCT)
DOUBLE TOKEN(DOUBLE)
DROP TOKEN(DROP)
ELSE TOKEN(ELSE)
END TOKEN(END)
ESCAPE TOKEN(ESCAPE)
EXCEPT TOKEN(EXCEPT)
EXECUTE TOKEN(EXECUTE)
EXISTS TOKEN(EXISTS)
EXPLAIN TOKEN(EXPLAIN)
EXTRACT TOKEN(EXTRACT)
FALSE TOKEN(FALSE)
FILE TOKEN(FILE)
FLOAT TOKEN(FLOAT)
FOLLOWING TOKEN(FOLLOWING)
FOR TOKEN(FOR)
FORMAT TOKEN(FORMAT)
FROM TOKEN(FROM)
FULL TOKEN(FULL)
GLOBAL TOKEN(GLOBAL)
GROUP TOKEN(GROUP)
GROUPS TOKEN(GROUPS)
HASH TOKEN(HASH)
HAVING TOKEN(HAVING)
HINT TOKEN(HINT)
HOUR TOKEN(HOUR)
HOURS TOKEN(HOURS)
IF TOKEN(IF)
ILIKE TOKEN(ILIKE)
IMPORT TOKEN(IMPORT)
IN TOKEN(IN)
INDEX TOKEN(INDEX)
INNER TOKEN(INNER)
INSERT TOKEN(INSERT)
INT TOKEN(INT)
INTEGER TOKEN(INTEGER)
INTERSECT TOKEN(INTERSECT)
INTERVAL TOKEN(INTERVAL)
INTO TOKEN(INTO)
IS TOKEN(IS)
ISNULL TOKEN(ISNULL)
JOIN TOKEN(JOIN)
KEY TOKEN(KEY)
LEFT TOKEN(LEFT)
LIKE TOKEN(LIKE)
LIMIT TOKEN(LIMIT)
LOAD TOKEN(LOAD)
LOCAL TOKEN(LOCAL)
LOCKED TOKEN(LOCKED)
LONG TOKEN(LONG)
MERGE TOKEN(MERGE)
MINUS TOKEN(MINUS)
MINUTE TOKEN(MINUTE)
MINUTES TOKEN(MINUTES)
MONTH TOKEN(MONTH)
MONTHS TOKEN(MONTHS)
NATURAL TOKEN(NATURAL)
NO TOKEN(NO)
NOT TOKEN(NOT)
NOWAIT TOKEN(NOWAIT)
NULL TOKEN(NULL)
NVARCHAR TOKEN(NVARCHAR)
OF TOKEN(OF)
OFF TOKEN(OFF)
OFFSET TOKEN(OFFSET)
ON TOKEN(ON)
OR TOKEN(OR)
ORDER TOKEN(ORDER)
OUTER TOKEN(OUTER)
OVER TOKEN(OVER)
PARAMETERS TOKEN(PARAMETERS)
PARTITION TOKEN(PARTITION)
PLAN TOKEN(PLAN)
PRECEDING TOKEN(PRECEDING)
PREPARE TOKEN(PREPARE)
PRIMARY TOKEN(PRIMARY)
RANGE TOKEN(RANGE)
REAL TOKEN(REAL)
RENAME TOKEN(RENAME)
RESTRICT TOKEN(RESTRICT)
RIGHT TOKEN(RIGHT)
ROLLBACK TOKEN(ROLLBACK)
ROWS TOKEN(ROWS)
SCHEMA TOKEN(SCHEMA)
SCHEMAS TOKEN(SCHEMAS)
SECOND TOKEN(SECOND)
SECONDS TOKEN(SECONDS)
SELECT TOKEN(SELECT)
SET TOKEN(SET)
SHARE TOKEN(SHARE)
SHOW TOKEN(SHOW)
SKIP TOKEN(SKIP)
SMALLINT TOKEN(SMALLINT)
SORTED TOKEN(SORTED)
SPATIAL TOKEN(SPATIAL)
TABLE TOKEN(TABLE)
TABLES TOKEN(TABLES)
TEMPORARY TOKEN(TEMPORARY)
TEXT TOKEN(TEXT)
THEN TOKEN(THEN)
TIME TOKEN(TIME)
TIMESTAMP TOKEN(TIMESTAMP)
TO TOKEN(TO)
TOP TOKEN(TOP)
TRANSACTION TOKEN(TRANSACTION)
TRUE TOKEN(TRUE)
TRUNCATE TOKEN(TRUNCATE)
UNBOUNDED TOKEN(UNBOUNDED)
UNION TOKEN(UNION)
UNIQUE TOKEN(UNIQUE)
UNLOAD TOKEN(UNLOAD)
UPDATE TOKEN(UPDATE)
USING TOKEN(USING)
VALUES TOKEN(VALUES)
VARCHAR TOKEN(VARCHAR)
VIEW TOKEN(VIEW)
VIRTUAL TOKEN(VIRTUAL)
WHEN TOKEN(WHEN)
WHERE TOKEN(WHERE)
WITH TOKEN(WITH)
YEAR TOKEN(YEAR)
YEARS TOKEN(YEARS)
CURRENT[ \t\n]+ROW TOKEN(CURRENT_ROW)
CHARACTER[ \t\n]+VARYING TOKEN(CHARACTER_VARYING)
/* Allow =/== see https://sqlite.org/lang_expr.html#collateop */
"==" TOKEN(EQUALS)
"!=" TOKEN(NOTEQUALS)
"<>" TOKEN(NOTEQUALS)
"<=" TOKEN(LESSEQ)
">=" TOKEN(GREATEREQ)
"||" TOKEN(CONCAT)
[-+*/(){},.;<>=^%:?[\]|] { return yytext[0]; }
[0-9]+"."[0-9]* |
"."[0-9]* {
yylval->fval = atof(yytext);
return SQL_FLOATVAL;
}
/*
* Regularly, negative literals are treated as <unary minus> <positive literal>. This does not work for LLONG_MIN, as it has no
* positive equivalent. We thus match for LLONG_MIN specifically. This is not an issue for floats, where
* numeric_limits<double>::lowest() == -numeric_limits<double>::max();
*/
-9223372036854775808 {
yylval->ival = LLONG_MIN;
return SQL_INTVAL;
}
[0-9]+ {
errno = 0;
yylval->ival = strtoll(yytext, nullptr, 0);
if (errno) {
return fprintf(stderr, "[SQL-Lexer-Error] Integer cannot be parsed - is it out of range?");
return 0;
}
return SQL_INTVAL;
}
\"[^\"\n]+\" {
// Crop the leading and trailing quote char
yylval->sval = hsql::substr(yytext, 1, strlen(yytext)-1);
return SQL_IDENTIFIER;
}
[A-Za-z][A-Za-z0-9_]* {
yylval->sval = strdup(yytext);
return SQL_IDENTIFIER;
}
\' { BEGIN singlequotedstring; strbuf.clear(); strbuf.str(""); } // Clear strbuf manually, see #170
<singlequotedstring>\'\' { strbuf << '\''; }
<singlequotedstring>[^']* { strbuf << yytext; }
<singlequotedstring>\' { BEGIN 0; yylval->sval = strdup(strbuf.str().c_str()); return SQL_STRING; }
<singlequotedstring><<EOF>> { fprintf(stderr, "[SQL-Lexer-Error] Unterminated string\n"); return 0; }
. { fprintf(stderr, "[SQL-Lexer-Error] Unknown Character: %c\n", yytext[0]); return 0; }
%%
/***************************
** Section 3: User code
***************************/
int yyerror(const char *msg) {
fprintf(stderr, "[SQL-Lexer-Error] %s\n",msg); return 0;
}

View File

@ -0,0 +1,46 @@
from __future__ import print_function
import math
with open("sql_keywords.txt", 'r') as fh:
keywords = [line.strip() for line in fh.readlines() if not line.strip().startswith("//") and len(line.strip()) > 0]
keywords = sorted(set(keywords)) # Sort by name
keywords = sorted(keywords, key=lambda x: len(x), reverse=True) # Sort by length
#################
# Flex
max_len = len(max(keywords, key=lambda x: len(x))) + 1
max_len = 4 * int(math.ceil(max_len / 4.0))
for keyword in keywords:
len_diff = (max_len) - len(keyword)
num_tabs = int(math.floor(len_diff / 4.0))
if len_diff % 4 != 0: num_tabs += 1
tabs = ''.join(['\t' for _ in range(num_tabs)])
print("%s%sTOKEN(%s)" % (keyword, tabs, keyword))
#
#################
#################
# Bison
line = "%token"
max_len = 60
print("/* SQL Keywords */")
for keyword in keywords:
if len(line + " " + keyword) > max_len:
print(line)
line = "%token " + keyword
else:
line = line + " " + keyword
print(line)
#
#################

View File

@ -0,0 +1,33 @@
#ifndef __PARSER_TYPEDEF_H__
#define __PARSER_TYPEDEF_H__
#include <vector>
#ifndef YYtypeDEF_YY_SCANNER_T
#define YYtypeDEF_YY_SCANNER_T
typedef void* yyscan_t;
#endif
#define YYSTYPE HSQL_STYPE
#define YYLTYPE HSQL_LTYPE
struct HSQL_CUST_LTYPE {
int first_line;
int first_column;
int last_line;
int last_column;
int total_column;
// Length of the string in the SQL query string
int string_length;
// Parameters.
// int param_id;
std::vector<void*> param_list;
};
#define HSQL_LTYPE HSQL_CUST_LTYPE
#define HSQL_LTYPE_IS_DECLARED 1
#endif

View File

@ -0,0 +1,163 @@
// Possible source for more tokens https://www.sqlite.org/lang_keywords.html
//////////////////////////
// Select Statement
SELECT
TOP
FROM
WHERE
GROUP
BY
HAVING
ORDER
ASC
DESC
LIMIT
DISTINCT
OFFSET
UNION
ALL
EXCEPT
MINUS
INTERSECT
// Join clause
JOIN
ON
INNER
OUTER
LEFT
RIGHT
FULL
CROSS
USING
NATURAL
// Select Statement
//////////////////////
// Data Definition
CREATE
TABLE
SCHEMA
INDEX
VIEW
IF
NOT
EXISTS
GLOBAL
LOCAL
TEMPORARY
UNIQUE
VIRTUAL
INDEX
UNIQUE
HASH
SPATIAL
PRIMARY
KEY
ON
DROP
TABLE
SCHEMA
RESTRICT
CASCADE
ALTER
ADD
COLUMN
BEFORE
AFTER
// Data Definition
////////////////////////
// Data Manipulation
INSERT
VALUES
DIRECT
SORTED
COPY
FORMAT
IMPORT
FILE
CONTROL
UPDATE
SET
DELETE
TRUNCATE
MERGE
DELTA
OF
LOAD
UNLOAD
DELETE
// Prepared Statements
DEALLOCATE
PREPARE
EXECUTE
///////////////////////////////
// other statements
RENAME
EXPLAIN
PLAN
ANALYZE
SHOW
SCHEMAS
TABLES
COLUMNS
// misc.
COLUMN
INTO
AS
SET
DEFAULT
CALL
FOR
TO
ARRAY
// Expressions
NOT
AND
OR
NULL
LIKE
IN
IS
ISNULL
BETWEEN
ESCAPE
CASE
WHEN
THEN
ELSE
END
// With
WITH
HINT
PARAMETERS
ON
OFF
// Data types
DATE
TIME
TIMESTAMP
INTEGER
INT
DOUBLE
NVARCHAR
TEXT

View File

@ -0,0 +1,40 @@
#ifndef SQLPARSER_ALTER_STATEMENT_H
#define SQLPARSER_ALTER_STATEMENT_H
#include "SQLStatement.h"
// Note: Implementations of constructors and destructors can be found in statements.cpp.
namespace hsql {
enum ActionType {
DropColumn,
};
struct AlterAction {
AlterAction(ActionType type);
ActionType type;
virtual ~AlterAction();
};
struct DropColumnAction : AlterAction {
DropColumnAction(char* column_name);
char* columnName;
bool ifExists;
~DropColumnAction() override;
};
// Represents SQL Alter Table statements.
// Example "ALTER TABLE students DROP COLUMN name;"
struct SQLParser_API AlterStatement : SQLStatement {
AlterStatement(char* name, AlterAction* action);
~AlterStatement() override;
char* schema;
bool ifTableExists;
char* name;
AlterAction* action;
};
} // namespace hsql
#endif

View File

@ -0,0 +1,43 @@
#ifndef SQLPARSER_COLUMN_TYPE_H
#define SQLPARSER_COLUMN_TYPE_H
#include"sql-parser/src/sqlparser_win.h"
#include <ostream>
namespace hsql {
enum class DataType {
UNKNOWN,
BIGINT,
BOOLEAN,
CHAR,
DATE,
DATETIME,
DECIMAL,
DOUBLE,
FLOAT,
INT,
LONG,
REAL,
SMALLINT,
TEXT,
TIME,
VARCHAR,
};
// Represents the type of a column, e.g., FLOAT or VARCHAR(10)
struct SQLParser_API ColumnType {
ColumnType() = default;
ColumnType(DataType data_type, int64_t length = 0, int64_t precision = 0, int64_t scale = 0);
DataType data_type;
int64_t length; // Used for, e.g., VARCHAR(10)
int64_t precision; // Used for, e.g., DECIMAL (6, 4) or TIME (5)
int64_t scale; // Used for DECIMAL (6, 4)
};
bool operator==(const ColumnType& lhs, const ColumnType& rhs);
bool operator!=(const ColumnType& lhs, const ColumnType& rhs);
std::ostream& operator<<(std::ostream&, const ColumnType&);
} // namespace hsql
#endif

View File

@ -0,0 +1,70 @@
#include "CreateStatement.h"
#include "SelectStatement.h"
namespace hsql {
// CreateStatemnet
CreateStatement::CreateStatement(CreateType type)
: SQLStatement(kStmtCreate),
type(type),
ifNotExists(false),
filePath(nullptr),
schema(nullptr),
tableName(nullptr),
indexName(nullptr),
indexColumns(nullptr),
columns(nullptr),
tableConstraints(nullptr),
viewColumns(nullptr),
select(nullptr) {}
CreateStatement::~CreateStatement() {
free(filePath);
free(schema);
free(tableName);
free(indexName);
delete select;
if (columns) {
for (ColumnDefinition* def : *columns) {
delete def;
}
delete columns;
}
if (tableConstraints) {
for (TableConstraint* def : *tableConstraints) {
delete def;
}
delete tableConstraints;
}
if (indexColumns) {
for (char* column : *indexColumns) {
free(column);
}
delete indexColumns;
}
if (viewColumns) {
for (char* column : *viewColumns) {
free(column);
}
delete viewColumns;
}
}
void CreateStatement::setColumnDefsAndConstraints(std::vector<TableElement*>* tableElements) {
columns = new std::vector<ColumnDefinition*>();
tableConstraints = new std::vector<TableConstraint*>();
for (auto tableElem : *tableElements) {
if (auto* colDef = dynamic_cast<ColumnDefinition*>(tableElem)) {
columns->emplace_back(colDef);
} else if (auto* tableConstraint = dynamic_cast<TableConstraint*>(tableElem)) {
tableConstraints->emplace_back(tableConstraint);
}
}
}
} // namespace hsql

View File

@ -0,0 +1,86 @@
#ifndef SQLPARSER_CREATE_STATEMENT_H
#define SQLPARSER_CREATE_STATEMENT_H
#include "ColumnType.h"
#include "SQLStatement.h"
#include <ostream>
#include <unordered_set>
// Note: Implementations of constructors and destructors can be found in statements.cpp.
namespace hsql {
struct SQLParser_API SelectStatement;
enum struct ConstraintType { None, NotNull, Null, PrimaryKey, Unique };
// Superclass for both TableConstraint and Column Definition
struct SQLParser_API TableElement {
virtual ~TableElement() {}
};
// Represents definition of a table constraint
struct SQLParser_API TableConstraint : TableElement {
TableConstraint(ConstraintType keyType, std::vector<char*>* columnNames);
~TableConstraint() override;
ConstraintType type;
std::vector<char*>* columnNames;
};
// Represents definition of a table column
struct SQLParser_API ColumnDefinition : TableElement {
ColumnDefinition(char* name, ColumnType type, std::unordered_set<ConstraintType>* column_constraints);
~ColumnDefinition() override;
// By default, columns are nullable. However, we track if a column is explicitly requested to be nullable to
// notice conflicts with PRIMARY KEY table constraints.
bool trySetNullableExplicit() {
if (column_constraints->count(ConstraintType::NotNull) || column_constraints->count(ConstraintType::PrimaryKey)) {
if (column_constraints->count(ConstraintType::Null)) {
return false;
}
nullable = false;
}
return true;
}
std::unordered_set<ConstraintType>* column_constraints;
char* name;
ColumnType type;
bool nullable;
};
enum CreateType {
kCreateTable,
kCreateTableFromTbl, // Hyrise file format
kCreateView,
kCreateIndex
};
// Represents SQL Create statements.
// Example: "CREATE TABLE students (name TEXT, student_number INTEGER, city TEXT, grade DOUBLE)"
struct CreateStatement : SQLStatement {
CreateStatement(CreateType type);
~CreateStatement() override;
void setColumnDefsAndConstraints(std::vector<TableElement*>* tableElements);
CreateType type;
bool ifNotExists; // default: false
char* filePath; // default: nullptr
char* schema; // default: nullptr
char* tableName; // default: nullptr
char* indexName; // default: nullptr
std::vector<char*>* indexColumns; // default: nullptr
std::vector<ColumnDefinition*>* columns; // default: nullptr
std::vector<TableConstraint*>* tableConstraints; // default: nullptr
std::vector<char*>* viewColumns;
SelectStatement* select;
};
} // namespace hsql
#endif

View File

@ -0,0 +1,23 @@
#ifndef SQLPARSER_DELETE_STATEMENT_H
#define SQLPARSER_DELETE_STATEMENT_H
#include "SQLStatement.h"
// Note: Implementations of constructors and destructors can be found in statements.cpp.
namespace hsql {
// Represents SQL Delete statements.
// Example: "DELETE FROM students WHERE grade > 3.0"
// Note: if (expr == nullptr) => delete all rows (truncate)
struct SQLParser_API DeleteStatement : SQLStatement {
DeleteStatement();
~DeleteStatement() override;
char* schema;
char* tableName;
Expr* expr;
};
} // namespace hsql
#endif

View File

@ -0,0 +1,25 @@
#ifndef SQLPARSER_DROP_STATEMENT_H
#define SQLPARSER_DROP_STATEMENT_H
#include "SQLStatement.h"
// Note: Implementations of constructors and destructors can be found in statements.cpp.
namespace hsql {
enum DropType { kDropTable, kDropSchema, kDropIndex, kDropView, kDropPreparedStatement };
// Represents SQL Delete statements.
// Example "DROP TABLE students;"
struct SQLParser_API DropStatement : SQLStatement {
DropStatement(DropType type);
~DropStatement() override;
DropType type;
bool ifExists;
char* schema;
char* name;
char* indexName;
};
} // namespace hsql
#endif

View File

@ -0,0 +1,20 @@
#ifndef SQLPARSER_EXECUTE_STATEMENT_H
#define SQLPARSER_EXECUTE_STATEMENT_H
#include "SQLStatement.h"
namespace hsql {
// Represents SQL Execute statements.
// Example: "EXECUTE ins_prep(100, "test", 2.3);"
struct SQLParser_API ExecuteStatement : SQLStatement {
ExecuteStatement();
~ExecuteStatement() override;
char* name;
std::vector<Expr*>* parameters;
};
} // namespace hsql
#endif

View File

@ -0,0 +1,24 @@
#ifndef SQLPARSER_EXPORT_STATEMENT_H
#define SQLPARSER_EXPORT_STATEMENT_H
#include "ImportStatement.h"
#include "SQLStatement.h"
#include "SelectStatement.h"
namespace hsql {
// Represents SQL Export statements.
struct SQLParser_API ExportStatement : SQLStatement {
ExportStatement(ImportType type);
~ExportStatement() override;
// ImportType is used for compatibility reasons
ImportType type;
char* filePath;
char* schema;
char* tableName;
SelectStatement* select;
};
} // namespace hsql
#endif

View File

@ -0,0 +1,315 @@
#include "Expr.h"
#include <stdio.h>
#include <string.h>
#include "SelectStatement.h"
namespace hsql {
FrameBound::FrameBound(int64_t offset, FrameBoundType type, bool unbounded)
: offset{offset}, type{type}, unbounded{unbounded} {}
FrameDescription::FrameDescription(FrameType type, FrameBound* start, FrameBound* end)
: type{type}, start{start}, end{end} {}
FrameDescription::~FrameDescription() {
delete start;
delete end;
}
WindowDescription::WindowDescription(std::vector<Expr*>* partitionList, std::vector<OrderDescription*>* orderList,
FrameDescription* frameDescription)
: partitionList{partitionList}, orderList{orderList}, frameDescription{frameDescription} {}
WindowDescription::~WindowDescription() {
if (partitionList) {
for (Expr* e : *partitionList) {
delete e;
}
delete partitionList;
}
if (orderList) {
for (OrderDescription* orderDescription : *orderList) {
delete orderDescription;
}
delete orderList;
}
delete frameDescription;
}
Expr::Expr(ExprType type)
: type(type),
expr(nullptr),
expr2(nullptr),
exprList(nullptr),
select(nullptr),
name(nullptr),
table(nullptr),
alias(nullptr),
fval(0),
ival(0),
ival2(0),
datetimeField(kDatetimeNone),
columnType(DataType::UNKNOWN, 0),
isBoolLiteral(false),
opType(kOpNone),
distinct(false),
windowDescription(nullptr) {}
Expr::~Expr() {
delete expr;
delete expr2;
delete select;
delete windowDescription;
free(name);
free(table);
free(alias);
if (exprList) {
for (Expr* e : *exprList) {
delete e;
}
delete exprList;
}
}
Expr* Expr::make(ExprType type) {
Expr* e = new Expr(type);
return e;
}
Expr* Expr::makeOpUnary(OperatorType op, Expr* expr) {
Expr* e = new Expr(kExprOperator);
e->opType = op;
e->expr = expr;
e->expr2 = nullptr;
return e;
}
Expr* Expr::makeOpBinary(Expr* expr1, OperatorType op, Expr* expr2) {
Expr* e = new Expr(kExprOperator);
e->opType = op;
e->expr = expr1;
e->expr2 = expr2;
return e;
}
Expr* Expr::makeBetween(Expr* expr, Expr* left, Expr* right) {
Expr* e = new Expr(kExprOperator);
e->expr = expr;
e->opType = kOpBetween;
e->exprList = new std::vector<Expr*>();
e->exprList->push_back(left);
e->exprList->push_back(right);
return e;
}
Expr* Expr::makeCaseList(Expr* caseListElement) {
Expr* e = new Expr(kExprOperator);
// Case list expressions are temporary and will be integrated into the case
// expressions exprList - thus assign operator type kOpNone
e->opType = kOpNone;
e->exprList = new std::vector<Expr*>();
e->exprList->push_back(caseListElement);
return e;
}
Expr* Expr::makeCaseListElement(Expr* when, Expr* then) {
Expr* e = new Expr(kExprOperator);
e->opType = kOpCaseListElement;
e->expr = when;
e->expr2 = then;
return e;
}
Expr* Expr::caseListAppend(Expr* caseList, Expr* caseListElement) {
caseList->exprList->push_back(caseListElement);
return caseList;
}
Expr* Expr::makeCase(Expr* expr, Expr* caseList, Expr* elseExpr) {
Expr* e = new Expr(kExprOperator);
e->opType = kOpCase;
e->expr = expr;
e->expr2 = elseExpr;
e->exprList = caseList->exprList;
caseList->exprList = nullptr;
delete caseList;
return e;
}
Expr* Expr::makeLiteral(int64_t val) {
Expr* e = new Expr(kExprLiteralInt);
e->ival = val;
return e;
}
Expr* Expr::makeLiteral(double value) {
Expr* e = new Expr(kExprLiteralFloat);
e->fval = value;
return e;
}
Expr* Expr::makeLiteral(char* string) {
Expr* e = new Expr(kExprLiteralString);
e->name = string;
return e;
}
Expr* Expr::makeLiteral(bool val) {
Expr* e = new Expr(kExprLiteralInt);
e->ival = (int)val;
e->isBoolLiteral = true;
return e;
}
Expr* Expr::makeNullLiteral() {
Expr* e = new Expr(kExprLiteralNull);
return e;
}
Expr* Expr::makeDateLiteral(char* string) {
Expr* e = new Expr(kExprLiteralDate);
e->name = string;
return e;
}
Expr* Expr::makeIntervalLiteral(int64_t duration, DatetimeField unit) {
Expr* e = new Expr(kExprLiteralInterval);
e->ival = duration;
e->datetimeField = unit;
return e;
}
Expr* Expr::makeColumnRef(char* name) {
Expr* e = new Expr(kExprColumnRef);
e->name = name;
return e;
}
Expr* Expr::makeColumnRef(char* table, char* name) {
Expr* e = new Expr(kExprColumnRef);
e->name = name;
e->table = table;
return e;
}
Expr* Expr::makeStar(void) {
Expr* e = new Expr(kExprStar);
return e;
}
Expr* Expr::makeStar(char* table) {
Expr* e = new Expr(kExprStar);
e->table = table;
return e;
}
Expr* Expr::makeFunctionRef(char* func_name, std::vector<Expr*>* exprList, bool distinct, WindowDescription* window) {
Expr* e = new Expr(kExprFunctionRef);
e->name = func_name;
e->exprList = exprList;
e->distinct = distinct;
e->windowDescription = window;
return e;
}
Expr* Expr::makeArray(std::vector<Expr*>* exprList) {
Expr* e = new Expr(kExprArray);
e->exprList = exprList;
return e;
}
Expr* Expr::makeArrayIndex(Expr* expr, int64_t index) {
Expr* e = new Expr(kExprArrayIndex);
e->expr = expr;
e->ival = index;
return e;
}
Expr* Expr::makeParameter(int id) {
Expr* e = new Expr(kExprParameter);
e->ival = id;
return e;
}
Expr* Expr::makeSelect(SelectStatement* select) {
Expr* e = new Expr(kExprSelect);
e->select = select;
return e;
}
Expr* Expr::makeExists(SelectStatement* select) {
Expr* e = new Expr(kExprOperator);
e->opType = kOpExists;
e->select = select;
return e;
}
Expr* Expr::makeInOperator(Expr* expr, std::vector<Expr*>* exprList) {
Expr* e = new Expr(kExprOperator);
e->opType = kOpIn;
e->expr = expr;
e->exprList = exprList;
return e;
}
Expr* Expr::makeInOperator(Expr* expr, SelectStatement* select) {
Expr* e = new Expr(kExprOperator);
e->opType = kOpIn;
e->expr = expr;
e->select = select;
return e;
}
Expr* Expr::makeExtract(DatetimeField datetimeField, Expr* expr) {
Expr* e = new Expr(kExprExtract);
e->datetimeField = datetimeField;
e->expr = expr;
return e;
}
Expr* Expr::makeCast(Expr* expr, ColumnType columnType) {
Expr* e = new Expr(kExprCast);
e->columnType = columnType;
e->expr = expr;
return e;
}
bool Expr::isType(ExprType exprType) const { return exprType == type; }
bool Expr::isLiteral() const {
return isType(kExprLiteralInt) || isType(kExprLiteralFloat) || isType(kExprLiteralString) || isType(kExprParameter) ||
isType(kExprLiteralNull) || isType(kExprLiteralDate) || isType(kExprLiteralInterval);
}
bool Expr::hasAlias() const { return alias != nullptr; }
bool Expr::hasTable() const { return table != nullptr; }
const char* Expr::getName() const {
if (alias)
return alias;
else
return name;
}
char* substr(const char* source, int from, int to) {
int len = to - from;
char* copy = (char*)malloc(len + 1);
;
#if defined(_WIN32) || defined(_WIN64)
#pragma warning(disable : 4996)
#endif
strncpy(copy, source + from, len);
copy[len] = '\0';
return copy;
#if defined(_WIN32) || defined(_WIN64)
#pragma warning(default : 4996)
#endif
}
} // namespace hsql

View File

@ -0,0 +1,237 @@
#ifndef SQLPARSER_EXPR_H
#define SQLPARSER_EXPR_H
#include <stdlib.h>
#include <memory>
#include <vector>
#include "ColumnType.h"
namespace hsql {
struct SelectStatement;
struct OrderDescription;
// Helper function used by the lexer.
// TODO: move to more appropriate place.
char* substr(const char* source, int from, int to);
enum ExprType {
kExprLiteralFloat,
kExprLiteralString,
kExprLiteralInt,
kExprLiteralNull,
kExprLiteralDate,
kExprLiteralInterval,
kExprStar,
kExprParameter,
kExprColumnRef,
kExprFunctionRef,
kExprOperator,
kExprSelect,
kExprHint,
kExprArray,
kExprArrayIndex,
kExprExtract,
kExprCast
};
// Operator types. These are important for expressions of type kExprOperator.
enum OperatorType {
kOpNone,
// Ternary operator
kOpBetween,
// n-nary special case
kOpCase,
kOpCaseListElement, // `WHEN expr THEN expr`
// Binary operators.
kOpPlus,
kOpMinus,
kOpAsterisk,
kOpSlash,
kOpPercentage,
kOpCaret,
kOpEquals,
kOpNotEquals,
kOpLess,
kOpLessEq,
kOpGreater,
kOpGreaterEq,
kOpLike,
kOpNotLike,
kOpILike,
kOpAnd,
kOpOr,
kOpIn,
kOpConcat,
// Unary operators.
kOpNot,
kOpUnaryMinus,
kOpIsNull,
kOpExists
};
enum DatetimeField {
kDatetimeNone,
kDatetimeSecond,
kDatetimeMinute,
kDatetimeHour,
kDatetimeDay,
kDatetimeMonth,
kDatetimeYear,
};
// Description of the frame clause within a window expression.
enum FrameBoundType { kFollowing, kPreceding, kCurrentRow };
struct FrameBound {
FrameBound(int64_t offset, FrameBoundType type, bool unbounded);
int64_t offset;
FrameBoundType type;
bool unbounded;
};
enum FrameType { kRange, kRows, kGroups };
struct SQLParser_API FrameDescription {
FrameDescription(FrameType type, FrameBound* start, FrameBound* end);
virtual ~FrameDescription();
FrameType type;
FrameBound* start;
FrameBound* end;
};
typedef struct Expr Expr;
// Description of additional fields for a window expression.
struct SQLParser_API WindowDescription {
WindowDescription(std::vector<Expr*>* partitionList, std::vector<OrderDescription*>* orderList,
FrameDescription* frameDescription);
virtual ~WindowDescription();
std::vector<Expr*>* partitionList;
std::vector<OrderDescription*>* orderList;
FrameDescription* frameDescription;
};
// Represents SQL expressions (i.e. literals, operators, column_refs).
// TODO: When destructing a placeholder expression, we might need to alter the
// placeholder_list.
struct SQLParser_API Expr {
Expr(ExprType type);
virtual ~Expr();
ExprType type;
// TODO: Replace expressions by list.
Expr* expr;
Expr* expr2;
std::vector<Expr*>* exprList;
SelectStatement* select;
char* name;
char* table;
char* alias;
double fval;
int64_t ival;
int64_t ival2;
DatetimeField datetimeField;
ColumnType columnType;
bool isBoolLiteral;
OperatorType opType;
bool distinct;
WindowDescription* windowDescription;
// Convenience accessor methods.
bool isType(ExprType exprType) const;
bool isLiteral() const;
bool hasAlias() const;
bool hasTable() const;
const char* getName() const;
// Static constructors.
static Expr* make(ExprType type);
static Expr* makeOpUnary(OperatorType op, Expr* expr);
static Expr* makeOpBinary(Expr* expr1, OperatorType op, Expr* expr2);
static Expr* makeBetween(Expr* expr, Expr* left, Expr* right);
static Expr* makeCaseList(Expr* caseListElement);
static Expr* makeCaseListElement(Expr* when, Expr* then);
static Expr* caseListAppend(Expr* caseList, Expr* caseListElement);
static Expr* makeCase(Expr* expr, Expr* when, Expr* elseExpr);
static Expr* makeLiteral(int64_t val);
static Expr* makeLiteral(double val);
static Expr* makeLiteral(char* val);
static Expr* makeLiteral(bool val);
static Expr* makeNullLiteral();
static Expr* makeDateLiteral(char* val);
static Expr* makeIntervalLiteral(int64_t duration, DatetimeField unit);
static Expr* makeColumnRef(char* name);
static Expr* makeColumnRef(char* table, char* name);
static Expr* makeStar(void);
static Expr* makeStar(char* table);
static Expr* makeFunctionRef(char* func_name, std::vector<Expr*>* exprList, bool distinct, WindowDescription* window);
static Expr* makeArray(std::vector<Expr*>* exprList);
static Expr* makeArrayIndex(Expr* expr, int64_t index);
static Expr* makeParameter(int id);
static Expr* makeSelect(SelectStatement* select);
static Expr* makeExists(SelectStatement* select);
static Expr* makeInOperator(Expr* expr, std::vector<Expr*>* exprList);
static Expr* makeInOperator(Expr* expr, SelectStatement* select);
static Expr* makeExtract(DatetimeField datetimeField1, Expr* expr);
static Expr* makeCast(Expr* expr, ColumnType columnType);
};
// Zero initializes an Expr object and assigns it to a space in the heap
// For Hyrise we still had to put in the explicit NULL constructor
// http://www.ex-parrot.com/~chris/random/initialise.html
// Unused
#define ALLOC_EXPR(var, type) \
Expr* var; \
do { \
Expr zero = {type}; \
var = (Expr*)malloc(sizeof *var); \
*var = zero; \
} while (0);
#undef ALLOC_EXPR
} // namespace hsql
#endif

View File

@ -0,0 +1,28 @@
#ifndef SQLPARSER_IMPORT_STATEMENT_H
#define SQLPARSER_IMPORT_STATEMENT_H
#include "SQLStatement.h"
namespace hsql {
enum ImportType {
kImportCSV,
kImportTbl, // Hyrise file format
kImportBinary,
kImportAuto
};
// Represents SQL Import statements.
struct SQLParser_API ImportStatement : SQLStatement {
ImportStatement(ImportType type);
~ImportStatement() override;
ImportType type;
char* filePath;
char* schema;
char* tableName;
Expr* whereClause;
};
} // namespace hsql
#endif

View File

@ -0,0 +1,26 @@
#ifndef SQLPARSER_INSERT_STATEMENT_H
#define SQLPARSER_INSERT_STATEMENT_H
#include "SQLStatement.h"
#include "SelectStatement.h"
namespace hsql {
enum InsertType { kInsertValues, kInsertSelect };
// Represents SQL Insert statements.
// Example: "INSERT INTO students VALUES ('Max', 1112233, 'Musterhausen', 2.3)"
struct SQLParser_API InsertStatement : SQLStatement {
InsertStatement(InsertType type);
~InsertStatement() override;
InsertType type;
char* schema;
char* tableName;
std::vector<char*>* columns;
std::vector<Expr*>* values;
SelectStatement* select;
};
} // namespace hsql
#endif

View File

@ -0,0 +1,12 @@
#include "PrepareStatement.h"
namespace hsql {
// PrepareStatement
PrepareStatement::PrepareStatement() : SQLStatement(kStmtPrepare), name(nullptr), query(nullptr) {}
PrepareStatement::~PrepareStatement() {
free(name);
free(query);
}
} // namespace hsql

View File

@ -0,0 +1,22 @@
#ifndef SQLPARSER_PREPARE_STATEMENT_H
#define SQLPARSER_PREPARE_STATEMENT_H
#include "SQLStatement.h"
namespace hsql {
// Represents SQL Prepare statements.
// Example: PREPARE test FROM 'SELECT * FROM test WHERE a = ?;'
struct SQLParser_API PrepareStatement : SQLStatement {
PrepareStatement();
~PrepareStatement() override;
char* name;
// The query that is supposed to be prepared.
char* query;
};
} // namespace hsql
#endif

View File

@ -0,0 +1,24 @@
#include "SQLStatement.h"
namespace hsql {
// SQLStatement
SQLStatement::SQLStatement(StatementType type) : hints(nullptr), type_(type) {}
SQLStatement::~SQLStatement() {
if (hints) {
for (Expr* hint : *hints) {
delete hint;
}
}
delete hints;
}
StatementType SQLStatement::type() const { return type_; }
bool SQLStatement::isType(StatementType type) const { return (type_ == type); }
bool SQLStatement::is(StatementType type) const { return isType(type); }
} // namespace hsql

View File

@ -0,0 +1,51 @@
#ifndef SQLPARSER_SQLSTATEMENT_H
#define SQLPARSER_SQLSTATEMENT_H
#include <vector>
#include "sql-parser/src/sqlparser_win.h"
#include "Expr.h"
namespace hsql {
enum StatementType {
kStmtError, // unused
kStmtSelect,
kStmtImport,
kStmtInsert,
kStmtUpdate,
kStmtDelete,
kStmtCreate,
kStmtDrop,
kStmtPrepare,
kStmtExecute,
kStmtExport,
kStmtRename,
kStmtAlter,
kStmtShow,
kStmtTransaction
};
// Base struct for every SQL statement
struct SQLParser_API SQLStatement {
SQLStatement(StatementType type);
virtual ~SQLStatement();
StatementType type() const;
bool isType(StatementType type) const;
// Shorthand for isType(type).
bool is(StatementType type) const;
// Length of the string in the SQL query string
size_t stringLength;
std::vector<Expr*>* hints;
private:
StatementType type_;
};
} // namespace hsql
#endif // SQLPARSER_SQLSTATEMENT_H

View File

@ -0,0 +1,113 @@
#ifndef SQLPARSER_SELECT_STATEMENT_H
#define SQLPARSER_SELECT_STATEMENT_H
#include "Expr.h"
#include "SQLStatement.h"
#include "Table.h"
namespace hsql {
enum OrderType { kOrderAsc, kOrderDesc };
enum SetType { kSetUnion, kSetIntersect, kSetExcept };
enum RowLockMode { ForUpdate, ForNoKeyUpdate, ForShare, ForKeyShare };
enum RowLockWaitPolicy { NoWait, SkipLocked, None };
// Description of the order by clause within a select statement.
struct SQLParser_API OrderDescription {
OrderDescription(OrderType type, Expr* expr);
virtual ~OrderDescription();
OrderType type;
Expr* expr;
};
// Description of the limit clause within a select statement.
struct SQLParser_API LimitDescription {
LimitDescription(Expr* limit, Expr* offset);
virtual ~LimitDescription();
Expr* limit;
Expr* offset;
};
// Description of the group-by clause within a select statement.
struct SQLParser_API GroupByDescription {
GroupByDescription();
virtual ~GroupByDescription();
std::vector<Expr*>* columns;
Expr* having;
};
struct SQLParser_API WithDescription {
~WithDescription();
char* alias;
SelectStatement* select;
};
struct SQLParser_API SetOperation {
SetOperation();
virtual ~SetOperation();
SetType setType;
bool isAll;
SelectStatement* nestedSelectStatement;
std::vector<OrderDescription*>* resultOrder;
LimitDescription* resultLimit;
};
struct SQLParser_API LockingClause {
RowLockMode rowLockMode;
RowLockWaitPolicy rowLockWaitPolicy;
std::vector<char*>* tables;
};
// Representation of a full SQL select statement.
struct SQLParser_API SelectStatement : SQLStatement {
SelectStatement();
~SelectStatement() override;
TableRef* fromTable;
bool selectDistinct;
std::vector<Expr*>* selectList;
Expr* whereClause;
GroupByDescription* groupBy;
// Note that a SetOperation is always connected to a
// different SelectStatement. This statement can itself
// have SetOperation connections to other SelectStatements.
// To evaluate the operations in the correct order:
// Iterate over the setOperations vector:
// 1. Fully evaluate the nestedSelectStatement within the SetOperation
// 2. Connect the original statement with the
// evaluated nestedSelectStatement
// 3. Apply the resultOrder and the resultLimit
// 4. The result now functions as the the original statement
// for the next iteration
//
// Example:
//
// (SELECT * FROM students INTERSECT SELECT * FROM students_2) UNION SELECT * FROM students_3 ORDER BY grade ASC;
//
// 1. We evaluate `Select * FROM students`
// 2. Then we iterate over the setOperations vector
// 3. We evalute the nestedSelectStatement of the first entry, which is: `SELECT * FROM students_2`
// 4. We connect the result of 1. with the results of 3. using the setType, which is INTERSECT
// 5. We continue the iteration of the setOperations vector
// 6. We evaluate the new nestedSelectStatement which is: `SELECT * FROM students_3`
// 7. We apply a Union-Operation to connect the results of 4. and 6.
// 8. Finally, we apply the resultOrder of the last SetOperation (ORDER BY grade ASC)
std::vector<SetOperation*>* setOperations;
std::vector<OrderDescription*>* order;
std::vector<WithDescription*>* withDescriptions;
LimitDescription* limit;
std::vector<LockingClause*>* lockings;
};
} // namespace hsql
#endif

View File

@ -0,0 +1,23 @@
#ifndef SQLPARSER_SHOW_STATEMENT_H
#define SQLPARSER_SHOW_STATEMENT_H
#include "SQLStatement.h"
// Note: Implementations of constructors and destructors can be found in statements.cpp.
namespace hsql {
enum ShowType { kShowColumns, kShowTables };
// Represents SQL SHOW statements.
// Example "SHOW TABLES;"
struct SQLParser_API ShowStatement : SQLStatement {
ShowStatement(ShowType type);
~ShowStatement() override;
ShowType type;
char* schema;
char* name;
};
} // namespace hsql
#endif

View File

@ -0,0 +1,68 @@
#ifndef SQLPARSER_TABLEREF_H
#define SQLPARSER_TABLEREF_H
#include <stdio.h>
#include <vector>
#include "Expr.h"
namespace hsql {
struct SelectStatement;
struct JoinDefinition;
struct TableRef;
// Possible table reference types.
enum TableRefType { kTableName, kTableSelect, kTableJoin, kTableCrossProduct };
struct SQLParser_API TableName {
char* schema;
char* name;
};
struct SQLParser_API Alias {
Alias(char* name, std::vector<char*>* columns = nullptr);
~Alias();
char* name;
std::vector<char*>* columns;
};
// Holds reference to tables. Can be either table names or a select statement.
struct SQLParser_API TableRef {
TableRef(TableRefType type);
virtual ~TableRef();
TableRefType type;
char* schema;
char* name;
Alias* alias;
SelectStatement* select;
std::vector<TableRef*>* list;
JoinDefinition* join;
// Returns true if a schema is set.
bool hasSchema() const;
// Returns the alias, if it is set. Otherwise the name.
const char* getName() const;
};
// Possible types of joins.
enum JoinType { kJoinInner, kJoinFull, kJoinLeft, kJoinRight, kJoinCross, kJoinNatural };
// Definition of a join construct.
struct SQLParser_API JoinDefinition {
JoinDefinition();
virtual ~JoinDefinition();
TableRef* left;
TableRef* right;
Expr* condition;
JoinType type;
};
} // namespace hsql
#endif

View File

@ -0,0 +1,21 @@
#ifndef HYRISE_TRANSACTIONSTATEMENT_H
#define HYRISE_TRANSACTIONSTATEMENT_H
#include "SQLStatement.h"
namespace hsql {
// Represents SQL Transaction statements.
// Example: BEGIN TRANSACTION;
enum TransactionCommand { kBeginTransaction, kCommitTransaction, kRollbackTransaction };
struct SQLParser_API TransactionStatement : SQLStatement {
TransactionStatement(TransactionCommand command);
~TransactionStatement() override;
TransactionCommand command;
};
} // namespace hsql
#endif

View File

@ -0,0 +1,27 @@
#ifndef SQLPARSER_UPDATE_STATEMENT_H
#define SQLPARSER_UPDATE_STATEMENT_H
#include "SQLStatement.h"
namespace hsql {
// Represents "column = value" expressions.
struct UpdateClause {
char* column;
Expr* value;
};
// Represents SQL Update statements.
struct SQLParser_API UpdateStatement : SQLStatement {
UpdateStatement();
~UpdateStatement() override;
// TODO: switch to char* instead of TableRef
TableRef* table;
std::vector<UpdateClause*>* updates;
Expr* where;
};
} // namespace hsql
#endif

View File

@ -0,0 +1,393 @@
#include "statements.h"
#include "AlterStatement.h"
namespace hsql {
// KeyConstraints
TableConstraint::TableConstraint(ConstraintType type, std::vector<char*>* columnNames)
: type(type), columnNames(columnNames) {}
TableConstraint::~TableConstraint() {
for (char* def : *columnNames) {
free(def);
}
delete columnNames;
}
// ColumnDefinition
ColumnDefinition::ColumnDefinition(char* name, ColumnType type, std::unordered_set<ConstraintType>* column_constraints)
: column_constraints(column_constraints), name(name), type(type), nullable(true) {}
ColumnDefinition::~ColumnDefinition() {
free(name);
delete column_constraints;
}
ColumnType::ColumnType(DataType data_type, int64_t length, int64_t precision, int64_t scale)
: data_type(data_type), length(length), precision(precision), scale(scale) {}
bool operator==(const ColumnType& lhs, const ColumnType& rhs) {
if (lhs.data_type != rhs.data_type) return false;
return lhs.length == rhs.length && lhs.precision == rhs.precision && lhs.scale == rhs.scale;
}
bool operator!=(const ColumnType& lhs, const ColumnType& rhs) { return !(lhs == rhs); }
std::ostream& operator<<(std::ostream& stream, const ColumnType& column_type) {
switch (column_type.data_type) {
case DataType::UNKNOWN:
stream << "UNKNOWN";
break;
case DataType::INT:
stream << "INT";
break;
case DataType::BIGINT:
stream << "BIGINT";
break;
case DataType::LONG:
stream << "LONG";
break;
case DataType::FLOAT:
stream << "FLOAT";
break;
case DataType::DOUBLE:
stream << "DOUBLE";
break;
case DataType::REAL:
stream << "REAL";
break;
case DataType::CHAR:
stream << "CHAR(" << column_type.length << ")";
break;
case DataType::VARCHAR:
stream << "VARCHAR(" << column_type.length << ")";
break;
case DataType::DECIMAL:
stream << "DECIMAL";
break;
case DataType::TEXT:
stream << "TEXT";
break;
case DataType::DATETIME:
stream << "DATETIME";
break;
case DataType::DATE:
stream << "DATE";
break;
case DataType::TIME:
stream << "TIME";
break;
case DataType::SMALLINT:
stream << "SMALLINT";
break;
case DataType::BOOLEAN:
stream << "BOOLEAN";
break;
}
return stream;
}
// DeleteStatement
DeleteStatement::DeleteStatement() : SQLStatement(kStmtDelete), schema(nullptr), tableName(nullptr), expr(nullptr) {}
DeleteStatement::~DeleteStatement() {
free(schema);
free(tableName);
delete expr;
}
// DropStatement
DropStatement::DropStatement(DropType type)
: SQLStatement(kStmtDrop), type(type), schema(nullptr), name(nullptr), indexName(nullptr) {}
DropStatement::~DropStatement() {
free(schema);
free(name);
free(indexName);
}
// AlterStatement and supportive classes
AlterAction::AlterAction(ActionType type) : type(type) {}
AlterAction::~AlterAction() = default;
DropColumnAction::DropColumnAction(char* column_name)
: AlterAction(ActionType::DropColumn), columnName(column_name), ifExists(false) {}
DropColumnAction::~DropColumnAction() { free(columnName); }
AlterStatement::AlterStatement(char* name, AlterAction* action)
: SQLStatement(kStmtAlter), schema(nullptr), ifTableExists(false), name(name), action(action) {}
AlterStatement::~AlterStatement() {
free(schema);
free(name);
delete action;
}
// TransactionStatement
TransactionStatement::TransactionStatement(TransactionCommand command)
: SQLStatement(kStmtTransaction), command(command) {}
TransactionStatement::~TransactionStatement() {}
// ExecuteStatement
ExecuteStatement::ExecuteStatement() : SQLStatement(kStmtExecute), name(nullptr), parameters(nullptr) {}
ExecuteStatement::~ExecuteStatement() {
free(name);
if (parameters) {
for (Expr* param : *parameters) {
delete param;
}
delete parameters;
}
}
// ExportStatement
ExportStatement::ExportStatement(ImportType type)
: SQLStatement(kStmtExport), type(type), filePath(nullptr), schema(nullptr), tableName(nullptr), select(nullptr) {}
ExportStatement::~ExportStatement() {
free(filePath);
free(schema);
free(tableName);
delete select;
}
// ImportStatement
ImportStatement::ImportStatement(ImportType type)
: SQLStatement(kStmtImport),
type(type),
filePath(nullptr),
schema(nullptr),
tableName(nullptr),
whereClause(nullptr) {}
ImportStatement::~ImportStatement() {
free(filePath);
free(schema);
free(tableName);
delete whereClause;
}
// InsertStatement
InsertStatement::InsertStatement(InsertType type)
: SQLStatement(kStmtInsert),
type(type),
schema(nullptr),
tableName(nullptr),
columns(nullptr),
values(nullptr),
select(nullptr) {}
InsertStatement::~InsertStatement() {
free(schema);
free(tableName);
delete select;
if (columns) {
for (char* column : *columns) {
free(column);
}
delete columns;
}
if (values) {
for (Expr* expr : *values) {
delete expr;
}
delete values;
}
}
// ShowStatament
ShowStatement::ShowStatement(ShowType type) : SQLStatement(kStmtShow), type(type), schema(nullptr), name(nullptr) {}
ShowStatement::~ShowStatement() {
free(schema);
free(name);
}
// SelectStatement.h
// OrderDescription
OrderDescription::OrderDescription(OrderType type, Expr* expr) : type(type), expr(expr) {}
OrderDescription::~OrderDescription() { delete expr; }
// LimitDescription
LimitDescription::LimitDescription(Expr* limit, Expr* offset) : limit(limit), offset(offset) {}
LimitDescription::~LimitDescription() {
delete limit;
delete offset;
}
// GroypByDescription
GroupByDescription::GroupByDescription() : columns(nullptr), having(nullptr) {}
GroupByDescription::~GroupByDescription() {
delete having;
if (columns) {
for (Expr* expr : *columns) {
delete expr;
}
delete columns;
}
}
WithDescription::~WithDescription() {
free(alias);
delete select;
}
// SelectStatement
SelectStatement::SelectStatement()
: SQLStatement(kStmtSelect),
fromTable(nullptr),
selectDistinct(false),
selectList(nullptr),
whereClause(nullptr),
groupBy(nullptr),
setOperations(nullptr),
order(nullptr),
withDescriptions(nullptr),
limit(nullptr),
lockings(nullptr) {}
SelectStatement::~SelectStatement() {
delete fromTable;
delete whereClause;
delete groupBy;
delete limit;
// Delete each element in the select list.
if (selectList) {
for (Expr* expr : *selectList) {
delete expr;
}
delete selectList;
}
if (order) {
for (OrderDescription* desc : *order) {
delete desc;
}
delete order;
}
if (withDescriptions) {
for (WithDescription* desc : *withDescriptions) {
delete desc;
}
delete withDescriptions;
}
if (setOperations) {
for (SetOperation* setOperation : *setOperations) {
delete setOperation;
}
delete setOperations;
}
if (lockings) {
for (LockingClause* lockingClause : *lockings) {
if (lockingClause->tables) {
for (char* dtable : *lockingClause->tables) {
free(dtable);
}
delete lockingClause->tables;
}
delete lockingClause;
}
delete lockings;
}
}
// UpdateStatement
UpdateStatement::UpdateStatement() : SQLStatement(kStmtUpdate), table(nullptr), updates(nullptr), where(nullptr) {}
UpdateStatement::~UpdateStatement() {
delete table;
delete where;
if (updates) {
for (UpdateClause* update : *updates) {
free(update->column);
delete update->value;
delete update;
}
delete updates;
}
}
// Alias
Alias::Alias(char* name, std::vector<char*>* columns) : name(name), columns(columns) {}
Alias::~Alias() {
free(name);
if (columns) {
for (char* column : *columns) {
free(column);
}
delete columns;
}
}
// TableRef
TableRef::TableRef(TableRefType type)
: type(type), schema(nullptr), name(nullptr), alias(nullptr), select(nullptr), list(nullptr), join(nullptr) {}
TableRef::~TableRef() {
free(schema);
free(name);
delete select;
delete join;
delete alias;
if (list) {
for (TableRef* table : *list) {
delete table;
}
delete list;
}
}
bool TableRef::hasSchema() const { return schema != nullptr; }
const char* TableRef::getName() const {
if (alias)
return alias->name;
else
return name;
}
// JoinDefinition
JoinDefinition::JoinDefinition() : left(nullptr), right(nullptr), condition(nullptr), type(kJoinInner) {}
JoinDefinition::~JoinDefinition() {
delete left;
delete right;
delete condition;
}
SetOperation::SetOperation() : nestedSelectStatement(nullptr), resultOrder(nullptr), resultLimit(nullptr) {}
SetOperation::~SetOperation() {
delete nestedSelectStatement;
delete resultLimit;
if (resultOrder) {
for (OrderDescription* desc : *resultOrder) {
delete desc;
}
delete resultOrder;
}
}
} // namespace hsql

View File

@ -0,0 +1,18 @@
#ifndef SQLPARSER_STATEMENTS_H
#define SQLPARSER_STATEMENTS_H
#include "AlterStatement.h"
#include "CreateStatement.h"
#include "DeleteStatement.h"
#include "DropStatement.h"
#include "ExecuteStatement.h"
#include "ExportStatement.h"
#include "ImportStatement.h"
#include "InsertStatement.h"
#include "PrepareStatement.h"
#include "SelectStatement.h"
#include "ShowStatement.h"
#include "TransactionStatement.h"
#include "UpdateStatement.h"
#endif // SQLPARSER_STATEMENTS_H

View File

@ -0,0 +1,18 @@
#ifndef SQLPARSER_SQLPARSER_WIN_H
#define SQLPARSER_SQLPARSER_WIN_H
#if defined(_WIN32) || defined(_WIN64)
#if defined(_MSC_VER)
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#endif
#if defined(SQLParser_EXPORTS)
#define SQLParser_API __declspec(dllexport)
#else
#define SQLParser_API __declspec(dllimport)
#endif
#endif
#endif // SQLPARSER_SQLPARSER_WIN_H

View File

@ -0,0 +1,497 @@
#include "sqlhelper.h"
#include <iostream>
#include <map>
#include <sstream>
#include <string>
namespace hsql {
void printOperatorExpression(Expr* expr, uintmax_t num_indent);
void printAlias(Alias* alias, uintmax_t num_indent);
std::ostream& operator<<(std::ostream& os, const OperatorType& op);
std::ostream& operator<<(std::ostream& os, const DatetimeField& datetime);
std::ostream& operator<<(std::ostream& os, const FrameBound& frame_bound);
std::string indent(uintmax_t num_indent) { return std::string(num_indent, '\t'); }
void inprint(int64_t val, uintmax_t num_indent) { std::cout << indent(num_indent).c_str() << val << " " << std::endl; }
void inprint(double val, uintmax_t num_indent) { std::cout << indent(num_indent).c_str() << val << std::endl; }
void inprint(const char* val, uintmax_t num_indent) { std::cout << indent(num_indent).c_str() << val << std::endl; }
void inprint(const char* val, const char* val2, uintmax_t num_indent) {
std::cout << indent(num_indent).c_str() << val << "->" << val2 << std::endl;
}
void inprintC(char val, uintmax_t num_indent) { std::cout << indent(num_indent).c_str() << val << std::endl; }
void inprint(const OperatorType& op, uintmax_t num_indent) { std::cout << indent(num_indent) << op << std::endl; }
void inprint(const ColumnType& colType, uintmax_t num_indent) {
std::cout << indent(num_indent) << colType << std::endl;
}
void inprint(const DatetimeField& colType, uintmax_t num_indent) {
std::cout << indent(num_indent) << colType << std::endl;
}
void printTableRefInfo(TableRef* table, uintmax_t num_indent) {
switch (table->type) {
case kTableName:
inprint(table->name, num_indent);
if (table->schema) {
inprint("Schema", num_indent + 1);
inprint(table->schema, num_indent + 2);
}
break;
case kTableSelect:
printSelectStatementInfo(table->select, num_indent);
break;
case kTableJoin:
inprint("Join Table", num_indent);
inprint("Left", num_indent + 1);
printTableRefInfo(table->join->left, num_indent + 2);
inprint("Right", num_indent + 1);
printTableRefInfo(table->join->right, num_indent + 2);
inprint("Join Condition", num_indent + 1);
printExpression(table->join->condition, num_indent + 2);
break;
case kTableCrossProduct:
for (TableRef* tbl : *table->list) printTableRefInfo(tbl, num_indent);
break;
}
if (table->alias) {
printAlias(table->alias, num_indent);
}
}
void printAlias(Alias* alias, uintmax_t num_indent) {
inprint("Alias", num_indent + 1);
inprint(alias->name, num_indent + 2);
if (alias->columns) {
for (char* column : *(alias->columns)) {
inprint(column, num_indent + 3);
}
}
}
void printOperatorExpression(Expr* expr, uintmax_t num_indent) {
if (expr == nullptr) {
inprint("null", num_indent);
return;
}
inprint(expr->opType, num_indent);
printExpression(expr->expr, num_indent + 1);
if (expr->expr2) {
printExpression(expr->expr2, num_indent + 1);
} else if (expr->exprList) {
for (Expr* e : *expr->exprList) printExpression(e, num_indent + 1);
}
}
void printExpression(Expr* expr, uintmax_t num_indent) {
if (!expr) return;
switch (expr->type) {
case kExprStar:
inprint("*", num_indent);
break;
case kExprColumnRef:
inprint(expr->name, num_indent);
if (expr->table) {
inprint("Table:", num_indent + 1);
inprint(expr->table, num_indent + 2);
}
break;
// case kExprTableColumnRef: inprint(expr->table, expr->name, num_indent); break;
case kExprLiteralFloat:
inprint(expr->fval, num_indent);
break;
case kExprLiteralInt:
inprint(expr->ival, num_indent);
break;
case kExprLiteralString:
inprint(expr->name, num_indent);
break;
case kExprLiteralDate:
inprint(expr->name, num_indent);
break;
case kExprLiteralNull:
inprint("NULL", num_indent);
break;
case kExprLiteralInterval:
inprint("INTERVAL", num_indent);
inprint(expr->ival, num_indent + 1);
inprint(expr->datetimeField, num_indent + 1);
break;
case kExprFunctionRef:
inprint(expr->name, num_indent);
for (Expr* e : *expr->exprList) {
printExpression(e, num_indent + 1);
}
if (expr->windowDescription) {
printWindowDescription(expr->windowDescription, num_indent + 1);
}
break;
case kExprExtract:
inprint("EXTRACT", num_indent);
inprint(expr->datetimeField, num_indent + 1);
printExpression(expr->expr, num_indent + 1);
break;
case kExprCast:
inprint("CAST", num_indent);
inprint(expr->columnType, num_indent + 1);
printExpression(expr->expr, num_indent + 1);
break;
case kExprOperator:
printOperatorExpression(expr, num_indent);
break;
case kExprSelect:
printSelectStatementInfo(expr->select, num_indent);
break;
case kExprParameter:
inprint(expr->ival, num_indent);
break;
case kExprArray:
for (Expr* e : *expr->exprList) {
printExpression(e, num_indent + 1);
}
break;
case kExprArrayIndex:
printExpression(expr->expr, num_indent + 1);
inprint(expr->ival, num_indent);
break;
default:
std::cerr << "Unrecognized expression type " << expr->type << std::endl;
return;
}
if (expr->alias) {
inprint("Alias", num_indent + 1);
inprint(expr->alias, num_indent + 2);
}
}
void printOrderBy(const std::vector<OrderDescription*>* expr, uintmax_t num_indent) {
if (!expr) return;
for (const auto& order_description : *expr) {
printExpression(order_description->expr, num_indent);
if (order_description->type == kOrderAsc) {
inprint("ascending", num_indent);
} else {
inprint("descending", num_indent);
}
}
}
void printWindowDescription(WindowDescription* window_description, uintmax_t num_indent) {
inprint("OVER", num_indent);
if (window_description->partitionList) {
inprint("PARTITION BY", num_indent + 1);
for (const auto e : *window_description->partitionList) {
printExpression(e, num_indent + 2);
}
}
if (window_description->orderList) {
inprint("ORDER BY", num_indent + 1);
printOrderBy(window_description->orderList, num_indent + 2);
}
std::stringstream stream;
switch (window_description->frameDescription->type) {
case kRows:
stream << "ROWS";
break;
case kRange:
stream << "RANGE";
break;
case kGroups:
stream << "GROUPS";
break;
}
stream << " BETWEEN " << *window_description->frameDescription->start << " AND "
<< *window_description->frameDescription->end;
inprint(stream.str().c_str(), num_indent + 1);
}
void printSelectStatementInfo(const SelectStatement* stmt, uintmax_t num_indent) {
inprint("SelectStatement", num_indent);
inprint("Fields:", num_indent + 1);
for (Expr* expr : *stmt->selectList) printExpression(expr, num_indent + 2);
if (stmt->fromTable) {
inprint("Sources:", num_indent + 1);
printTableRefInfo(stmt->fromTable, num_indent + 2);
}
if (stmt->whereClause) {
inprint("Search Conditions:", num_indent + 1);
printExpression(stmt->whereClause, num_indent + 2);
}
if (stmt->groupBy) {
inprint("GroupBy:", num_indent + 1);
for (Expr* expr : *stmt->groupBy->columns) printExpression(expr, num_indent + 2);
if (stmt->groupBy->having) {
inprint("Having:", num_indent + 1);
printExpression(stmt->groupBy->having, num_indent + 2);
}
}
if (stmt->lockings) {
inprint("Lock Info:", num_indent + 1);
for (LockingClause* lockingClause : *stmt->lockings) {
inprint("Type", num_indent + 2);
if (lockingClause->rowLockMode == RowLockMode::ForUpdate) {
inprint("FOR UPDATE", num_indent + 3);
} else if (lockingClause->rowLockMode == RowLockMode::ForNoKeyUpdate) {
inprint("FOR NO KEY UPDATE", num_indent + 3);
} else if (lockingClause->rowLockMode == RowLockMode::ForShare) {
inprint("FOR SHARE", num_indent + 3);
} else if (lockingClause->rowLockMode == RowLockMode::ForKeyShare) {
inprint("FOR KEY SHARE", num_indent + 3);
}
if (lockingClause->tables) {
inprint("Target tables:", num_indent + 2);
for (char* dtable : *lockingClause->tables) {
inprint(dtable, num_indent + 3);
}
}
if (lockingClause->rowLockWaitPolicy != RowLockWaitPolicy::None) {
inprint("Waiting policy: ", num_indent + 2);
if (lockingClause->rowLockWaitPolicy == RowLockWaitPolicy::NoWait)
inprint("NOWAIT", num_indent + 3);
else
inprint("SKIP LOCKED", num_indent + 3);
}
}
}
if (stmt->setOperations) {
for (SetOperation* setOperation : *stmt->setOperations) {
switch (setOperation->setType) {
case SetType::kSetIntersect:
inprint("Intersect:", num_indent + 1);
break;
case SetType::kSetUnion:
inprint("Union:", num_indent + 1);
break;
case SetType::kSetExcept:
inprint("Except:", num_indent + 1);
break;
}
printSelectStatementInfo(setOperation->nestedSelectStatement, num_indent + 2);
if (setOperation->resultOrder) {
inprint("SetResultOrderBy:", num_indent + 1);
printOrderBy(setOperation->resultOrder, num_indent + 2);
}
if (setOperation->resultLimit) {
if (setOperation->resultLimit->limit) {
inprint("SetResultLimit:", num_indent + 1);
printExpression(setOperation->resultLimit->limit, num_indent + 2);
}
if (setOperation->resultLimit->offset) {
inprint("SetResultOffset:", num_indent + 1);
printExpression(setOperation->resultLimit->offset, num_indent + 2);
}
}
}
}
if (stmt->order) {
inprint("OrderBy:", num_indent + 1);
printOrderBy(stmt->order, num_indent + 2);
}
if (stmt->limit && stmt->limit->limit) {
inprint("Limit:", num_indent + 1);
printExpression(stmt->limit->limit, num_indent + 2);
}
if (stmt->limit && stmt->limit->offset) {
inprint("Offset:", num_indent + 1);
printExpression(stmt->limit->offset, num_indent + 2);
}
}
void printImportStatementInfo(const ImportStatement* stmt, uintmax_t num_indent) {
inprint("ImportStatement", num_indent);
inprint(stmt->filePath, num_indent + 1);
switch (stmt->type) {
case ImportType::kImportCSV:
inprint("CSV", num_indent + 1);
break;
case ImportType::kImportTbl:
inprint("TBL", num_indent + 1);
break;
case ImportType::kImportBinary:
inprint("BINARY", num_indent + 1);
break;
case ImportType::kImportAuto:
inprint("AUTO", num_indent + 1);
break;
}
inprint(stmt->tableName, num_indent + 1);
if (stmt->whereClause) {
inprint("WHERE:", num_indent + 1);
printExpression(stmt->whereClause, num_indent + 2);
}
}
void printExportStatementInfo(const ExportStatement* stmt, uintmax_t num_indent) {
inprint("ExportStatement", num_indent);
inprint(stmt->filePath, num_indent + 1);
switch (stmt->type) {
case ImportType::kImportCSV:
inprint("CSV", num_indent + 1);
break;
case ImportType::kImportTbl:
inprint("TBL", num_indent + 1);
break;
case ImportType::kImportBinary:
inprint("BINARY", num_indent + 1);
break;
case ImportType::kImportAuto:
inprint("AUTO", num_indent + 1);
break;
}
if (stmt->tableName) {
inprint(stmt->tableName, num_indent + 1);
} else {
printSelectStatementInfo(stmt->select, num_indent + 1);
}
}
void printCreateStatementInfo(const CreateStatement* stmt, uintmax_t num_indent) {
inprint("CreateStatement", num_indent);
inprint(stmt->tableName, num_indent + 1);
if (stmt->filePath) inprint(stmt->filePath, num_indent + 1);
}
void printInsertStatementInfo(const InsertStatement* stmt, uintmax_t num_indent) {
inprint("InsertStatement", num_indent);
inprint(stmt->tableName, num_indent + 1);
if (stmt->columns) {
inprint("Columns", num_indent + 1);
for (char* col_name : *stmt->columns) {
inprint(col_name, num_indent + 2);
}
}
switch (stmt->type) {
case kInsertValues:
inprint("Values", num_indent + 1);
for (Expr* expr : *stmt->values) {
printExpression(expr, num_indent + 2);
}
break;
case kInsertSelect:
printSelectStatementInfo(stmt->select, num_indent + 1);
break;
}
}
void printTransactionStatementInfo(const TransactionStatement* stmt, uintmax_t num_indent) {
inprint("TransactionStatement", num_indent);
switch (stmt->command) {
case kBeginTransaction:
inprint("BEGIN", num_indent + 1);
break;
case kCommitTransaction:
inprint("COMMIT", num_indent + 1);
break;
case kRollbackTransaction:
inprint("ROLLBACK", num_indent + 1);
break;
}
}
void printStatementInfo(const SQLStatement* stmt) {
switch (stmt->type()) {
case kStmtSelect:
printSelectStatementInfo((const SelectStatement*)stmt, 0);
break;
case kStmtInsert:
printInsertStatementInfo((const InsertStatement*)stmt, 0);
break;
case kStmtCreate:
printCreateStatementInfo((const CreateStatement*)stmt, 0);
break;
case kStmtImport:
printImportStatementInfo((const ImportStatement*)stmt, 0);
break;
case kStmtExport:
printExportStatementInfo((const ExportStatement*)stmt, 0);
break;
case kStmtTransaction:
printTransactionStatementInfo((const TransactionStatement*)stmt, 0);
break;
default:
break;
}
}
std::ostream& operator<<(std::ostream& os, const OperatorType& op) {
static const std::map<const OperatorType, const std::string> operatorToToken = {
{kOpNone, "None"}, {kOpBetween, "BETWEEN"},
{kOpCase, "CASE"}, {kOpCaseListElement, "CASE LIST ELEMENT"},
{kOpPlus, "+"}, {kOpMinus, "-"},
{kOpAsterisk, "*"}, {kOpSlash, "/"},
{kOpPercentage, "%"}, {kOpCaret, "^"},
{kOpEquals, "="}, {kOpNotEquals, "!="},
{kOpLess, "<"}, {kOpLessEq, "<="},
{kOpGreater, ">"}, {kOpGreaterEq, ">="},
{kOpLike, "LIKE"}, {kOpNotLike, "NOT LIKE"},
{kOpILike, "ILIKE"}, {kOpAnd, "AND"},
{kOpOr, "OR"}, {kOpIn, "IN"},
{kOpConcat, "CONCAT"}, {kOpNot, "NOT"},
{kOpUnaryMinus, "-"}, {kOpIsNull, "IS NULL"},
{kOpExists, "EXISTS"}};
const auto found = operatorToToken.find(op);
if (found == operatorToToken.cend()) {
return os << static_cast<uint64_t>(op);
} else {
return os << (*found).second;
}
}
std::ostream& operator<<(std::ostream& os, const DatetimeField& datetime) {
static const std::map<const DatetimeField, const std::string> operatorToToken = {
{kDatetimeNone, "None"}, {kDatetimeSecond, "SECOND"}, {kDatetimeMinute, "MINUTE"}, {kDatetimeHour, "HOUR"},
{kDatetimeDay, "DAY"}, {kDatetimeMonth, "MONTH"}, {kDatetimeYear, "YEAR"}};
const auto found = operatorToToken.find(datetime);
if (found == operatorToToken.cend()) {
return os << static_cast<uint64_t>(datetime);
} else {
return os << (*found).second;
}
}
std::ostream& operator<<(std::ostream& os, const FrameBound& frame_bound) {
if (frame_bound.type == kCurrentRow) {
os << "CURRENT ROW";
return os;
}
if (frame_bound.unbounded) {
os << "UNBOUNDED";
} else {
os << frame_bound.offset;
}
os << " ";
if (frame_bound.type == kPreceding) {
os << "PRECEDING";
} else {
os << "FOLLOWING";
}
return os;
}
} // namespace hsql

View File

@ -0,0 +1,41 @@
#ifndef SQLPARSER_SQLHELPER_H
#define SQLPARSER_SQLHELPER_H
#include "../sqlparser_win.h"
#include "../sql/statements.h"
namespace hsql {
// Prints a summary of the given SQLStatement.
void SQLParser_API printStatementInfo(const SQLStatement* stmt);
// Prints a summary of the given SelectStatement with the given indentation.
void SQLParser_API printSelectStatementInfo(const SelectStatement* stmt, uintmax_t num_indent);
// Prints a summary of the given ImportStatement with the given indentation.
void SQLParser_API printImportStatementInfo(const ImportStatement* stmt, uintmax_t num_indent);
// Prints a summary of the given CopyStatement with the given indentation.
void SQLParser_API printExportStatementInfo(const ExportStatement* stmt, uintmax_t num_indent);
// Prints a summary of the given InsertStatement with the given indentation.
void SQLParser_API printInsertStatementInfo(const InsertStatement* stmt, uintmax_t num_indent);
// Prints a summary of the given CreateStatement with the given indentation.
void SQLParser_API printCreateStatementInfo(const CreateStatement* stmt, uintmax_t num_indent);
// Prints a summary of the given TransactionStatement with the given indentation.
void SQLParser_API printTransactionStatementInfo(const TransactionStatement* stmt, uintmax_t num_indent);
// Prints a summary of the given Expression with the given indentation.
void SQLParser_API printExpression(Expr* expr, uintmax_t num_indent);
// Prints an ORDER BY clause
void SQLParser_API printOrderBy(const std::vector<OrderDescription*>* expr, uintmax_t num_indent);
// Prints WindowDescription.
void SQLParser_API printWindowDescription(WindowDescription* window_description, uintmax_t num_indent);
} // namespace hsql
#endif

View File

@ -0,0 +1,97 @@
#include <stdio.h>
#include <chrono>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include "SQLParser.h"
#include "thirdparty/microtest/microtest.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;
}

View File

@ -0,0 +1,84 @@
#include "SQLParser.h"
#include "sql_asserts.h"
#include "thirdparty/microtest/microtest.h"
using hsql::kExprLiteralInt;
using hsql::kExprParameter;
using hsql::kStmtDrop;
using hsql::kStmtExecute;
using hsql::kStmtInsert;
using hsql::kStmtPrepare;
using hsql::kStmtSelect;
using hsql::kDropPreparedStatement;
using hsql::DropStatement;
using hsql::ExecuteStatement;
using hsql::InsertStatement;
using hsql::PrepareStatement;
using hsql::SelectStatement;
TEST(PrepareSingleStatementTest) {
TEST_PARSE_SINGLE_SQL("PREPARE test FROM 'SELECT * FROM students WHERE grade = ?';", kStmtPrepare, PrepareStatement,
result, prepare);
ASSERT_STREQ(prepare->name, "test");
ASSERT_STREQ(prepare->query, "SELECT * FROM students WHERE grade = ?");
TEST_PARSE_SINGLE_SQL(prepare->query, kStmtSelect, SelectStatement, result2, select);
ASSERT_EQ(result2.parameters().size(), 1);
ASSERT(select->whereClause->expr2->isType(kExprParameter))
ASSERT_EQ(select->whereClause->expr2->ival, 0)
}
TEST(DeallocatePrepareStatementTest) {
TEST_PARSE_SINGLE_SQL("DEALLOCATE PREPARE test;", kStmtDrop, DropStatement, result, drop);
ASSERT_EQ(drop->type, kDropPreparedStatement);
ASSERT_STREQ(drop->name, "test");
}
TEST(StatementWithParameters) {
TEST_PARSE_SINGLE_SQL("SELECT * FROM test WHERE a = ? AND b = ?", kStmtSelect, SelectStatement, result, stmt);
const hsql::Expr* eq1 = stmt->whereClause->expr;
const hsql::Expr* eq2 = stmt->whereClause->expr2;
ASSERT_EQ(result.parameters().size(), 2);
ASSERT_EQ(eq1->opType, hsql::kOpEquals)
ASSERT(eq1->expr->isType(hsql::kExprColumnRef))
ASSERT(eq1->expr2->isType(kExprParameter))
ASSERT_EQ(eq1->expr2->ival, 0)
ASSERT_EQ(result.parameters()[0], eq1->expr2);
ASSERT_EQ(eq2->opType, hsql::kOpEquals)
ASSERT(eq2->expr->isType(hsql::kExprColumnRef))
ASSERT(eq2->expr2->isType(kExprParameter))
ASSERT_EQ(eq2->expr2->ival, 1)
ASSERT_EQ(result.parameters()[1], eq2->expr2);
}
TEST(ExecuteStatementTest) {
TEST_PARSE_SINGLE_SQL("EXECUTE test(1, 2);", kStmtExecute, ExecuteStatement, result, stmt);
ASSERT_STREQ(stmt->name, "test");
ASSERT_EQ(stmt->parameters->size(), 2);
}
TEST(ExecuteStatementTestNoParam) {
TEST_PARSE_SINGLE_SQL("EXECUTE test();", kStmtExecute, ExecuteStatement, result, stmt);
ASSERT_STREQ(stmt->name, "test");
ASSERT_EQ(stmt->parameters, 0);
}
TEST(ExecuteStatementTestNoParamList) {
TEST_PARSE_SINGLE_SQL("EXECUTE test;", kStmtExecute, ExecuteStatement, result, stmt);
ASSERT_STREQ(stmt->name, "test");
ASSERT_EQ(stmt->parameters, 0);
}

View File

@ -0,0 +1,62 @@
# 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;
!CREATE TABLE "table" FROM TBL FILE 'students.tbl';gibberish
!CREATE TABLE "table" FROM TBL FILE 'students.tbl';1
!INSERT INTO test_table VALUESd (1, 2, 'test');
!SELECT * FROM t WHERE a = ? AND b = ?;gibberish;
!SHOW COLUMNS;
!DESCRIBE;
!COPY;
!COPY students;
!COPY students FROM 'students_file' WITH FORMAT XYZ;
!COPY students TO 'students_file' WITH FORMAT XYZ;
!select a + 2 as b(spam, eggs) from B;
!WITH a AS SELECT 1 SELECT 1;
!WITH a AS (SELECT ) SELECT 1;
!WITH a AS (WITH b AS (SELECT 1) SELECT 1) SELECT 1; # We do not support nested WITH clauses
!WITH a AS (SELECT ) b AS (SELECT ) SELECT 1; # Missing comma between WITH descriptions
!BEGIN TRANSACTION transName; # Transaction naming is currently not supported
!SELECT -9223372036854775809; # Out of int64_t range
!SELECT 9223372036854775808; # Out of int64_t range
!SELECT * FROM t WHERE a = DATE 'anystring';
!SELECT * FROM t WHERE a = DATE '1996-12-310';
!SELECT * FROM t WHERE a = DATE '1996-120-31';
!SELECT * FROM t WHERE a = DATE '19960-12-31';
!SELECT * FROM t WHERE a = DATE 'asdf-gh-jkl';
!SELECT * FROM t WHERE a = DATE '2000-01-01' + INTERVAL 30;
!SELECT * FROM t WHERE a = DATE '2000-01-01' + INTERVAL 30 DAYS;
!SELECT * FROM t WHERE a = DATE '2000-01-01' + INTERVAL 30 'DAYS';
!SELECT * FROM t WHERE a = DATE '2000-01-01' + INTERVAL 'DAYS';
!SELECT * FROM t WHERE a = DATE '2000-01-01' + INTERVAL '1' ANYTHING;
!SELECT * FROM t WHERE a = DATE '2000-01-01' + INTERVAL '1 DAY' DAY;
!SELECT * FROM t WHERE a = DATE '2000-01-01' + INTERVAL '30 ANYTHING';
!SELECT * FROM t WHERE a = DATE '2000-01-01' + INTERVAL '30' DAYS;
!SELECT * FROM t WHERE a = DATE '2000-01-01' + x DAYS;
!SELECT * FROM t WHERE a = DATE '2000-01-01' + INTERVAL 'x' DAY;
!SELECT * FROM t WHERE a = DATE '2000-01-01' + INTERVAL '3.3 DAYS';
# ON is not supported by postgres. We follow postgres here since the sql-92 standard does not specify index
# implementation details.
!DROP INDEX myindex ON mytable;
!SELECT * FROM test WHERE val = 2 FOR KEY UPDATE;
!SELECT * FROM test WHERE val = 2 FOR SHARE test1;
!SELECT * FROM test WHERE val = 2 FOR NO KEY SHARE;
!SELECT * FROM test WHERE val = 2 NOWAIT FOR UPDATE;
!CREATE TABLE a_table (a_column INT PRIMARY KEY NULL);
!CREATE TABLE a_table (a_column INT NULL PRIMARY KEY);
!CREATE TABLE a_table (a_column INT NOT NULL NULL);
!CREATE TABLE a_table (a_column INT NULL NOT NULL);
# WINDOW EXPRESSIONS
!SELECT test1, sum(sum(test2)) OVER (PARTITION BY test3 ORDER BY test4 ROWS BETWEEN UNBOUNDED AND CURRENT ROW) FROM test;
!SELECT test1, sum(sum(test2)) OVER (PARTITION BY test3 ORDER BY test4 ROWS BETWEEN -1 PRECEDING AND CURRENT ROW) FROM test;
!SELECT test1, rank() OVER (INVALID UNBOUNDED PRECEDING) FROM test;
!SELECT rank() OVER (INVALID) FROM test;
!SELECT rank OVER () FROM test;
!SELECT a = 1 OVER () FROM test;
!SELECT rank() OVER (ROWS UNBOUNDEDD PRECEDING) FROM test;
!SELECT rank() OVER (ROWS UNBOUNDED PRECEDINGG) FROM test;
!SELECT test1, rank() OVER (ROWS -1 PRECEDING) FROM test;
!SELECT test1, rank() OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND -1 FOLLOWING) FROM test;

View File

@ -0,0 +1,110 @@
# 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
SELECT a FROM some_schema.foo WHERE a > 12 OR b > 3 AND NOT c LIMIT 10
SELECT col1 AS myname, col2, 'test' FROM "table", foo AS t WHERE age > 12 AND zipcode = 12345 GROUP BY col1;
SELECT * from "table" JOIN table2 ON a = b WHERE (b OR NOT a) AND a = 12.5
(SELECT a FROM foo WHERE a > 12 OR b > 3 AND c NOT LIKE 's%' LIMIT 10);
SELECT * FROM "table" LIMIT 10 OFFSET 10; SELECT * FROM another;
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY col1;
SELECT * FROM (SELECT * FROM t1);
SELECT * FROM t1 UNION (SELECT * FROM t2 UNION SELECT * FROM t3) ORDER BY col1;
SELECT TOP 10 * FROM t1 ORDER BY col1, col2;
SELECT a, MAX(b), MAX(c, d), CUSTOM(q, UP(r)) AS f FROM t1;
SELECT * FROM t WHERE a BETWEEN 1 and c;
SELECT * FROM t WHERE a = ? AND b = ?;
SELECT City.name, Product.category, SUM(price) FROM fact INNER JOIN City ON fact.city_id = City.id INNER JOIN Product ON fact.product_id = Product.id GROUP BY City.name, Product.category;
SELECT SUBSTR(a, 3, 5) FROM t;
SELECT * FROM t WHERE a = DATE '1996-12-31';
# JOIN
SELECT t1.a, t1.b, t2.c FROM "table" AS t1 JOIN (SELECT * FROM foo JOIN bar ON foo.id = bar.id) t2 ON t1.a = t2.b WHERE (t1.b OR NOT t1.a) AND t2.c = 12.5
SELECT * FROM t1 JOIN t2 ON c1 = c2;
SELECT a, SUM(b) FROM t2 GROUP BY a HAVING SUM(b) > 100;
# CREATE statement
CREATE TABLE "table" FROM TBL FILE 'students.tbl'
CREATE TABLE IF NOT EXISTS "table" FROM TBL FILE 'students.tbl'
CREATE TABLE students (name TEXT, student_number INTEGER, city TEXT, grade DOUBLE, credits BIGINT)
CREATE TABLE students (name TEXT, student_number INTEGER NOT NULL, city TEXT, grade DOUBLE PRIMARY KEY UNIQUE)
CREATE TABLE teachers (name VARCHAR(30), student_number LONG, city CHAR(10), grade FLOAT)
CREATE TABLE teachers (name VARCHAR(30), student_number LONG, PRIMARY KEY (name, student_number), city CHAR(10), grade FLOAT)
CREATE TABLE teachers (name CHARACTER VARYING(30));
CREATE TABLE students_2 AS SELECT * FROM students
CREATE TABLE students_3 AS SELECT city, grade FROM students WHERE grade > 3.0
CREATE TABLE students (date_of_birth DATE, matriculation_date DATETIME, graduation_date TIMESTAMP, graduated BOOLEAN);
# Multiple statements
CREATE TABLE "table" FROM TBL FILE 'students.tbl'; SELECT * FROM "table";
# INSERT
INSERT INTO test_table VALUES (1, 2, 'test');
INSERT INTO test_table (id, value, name) VALUES (1, 2, 'test');
INSERT INTO test_table SELECT * FROM students;
INSERT INTO some_schema.test_table SELECT * FROM another_schema.students;
# DELETE
DELETE FROM students WHERE grade > 3.0
DELETE FROM students
TRUNCATE students
# UPDATE
UPDATE students SET grade = 1.3 WHERE name = 'Max Mustermann';
UPDATE students SET grade = 1.3, name='Felix Fürstenberg' WHERE name = 'Max Mustermann';
UPDATE students SET grade = 1.0;
UPDATE some_schema.students SET grade = 1.0;
# ALTER
ALTER TABLE mytable DROP COLUMN IF EXISTS mycolumn;
ALTER TABLE IF EXISTS mytable DROP COLUMN IF EXISTS mycolumn;
# DROP
DROP TABLE students;
DROP TABLE IF EXISTS students;
DROP VIEW IF EXISTS students;
DROP INDEX myindex;
DROP INDEX IF EXISTS myindex;
# PREPARE
PREPARE prep_inst FROM 'INSERT INTO test VALUES (?, ?, ?)';
PREPARE prep2 FROM 'INSERT INTO test VALUES (?, 0, 0); INSERT INTO test VALUES (0, ?, 0); INSERT INTO test VALUES (0, 0, ?);';
EXECUTE prep_inst(1, 2, 3);
EXECUTE prep;
DEALLOCATE PREPARE prep;
# COPY
COPY students FROM 'student.tbl';
COPY students FROM 'file_path' WITH FORMAT TBL;
COPY students FROM 'file_path' WITH FORMAT CSV;
COPY students FROM 'file_path' WITH FORMAT BIN;
COPY students FROM 'file_path' WITH FORMAT BINARY;
COPY good_students FROM 'file_path' WHERE grade > (SELECT AVG(grade) from alumni);
COPY students TO 'student.tbl';
COPY students TO 'file_path' WITH FORMAT TBL;
COPY students TO 'file_path' WITH FORMAT CSV;
COPY students TO 'file_path' WITH FORMAT BIN;
COPY students TO 'file_path' WITH FORMAT BINARY;
COPY (SELECT firstname, COUNT(*) FROM students GROUP BY firstname) TO 'student_names.csv';
# HINTS
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'));
SHOW TABLES;
SHOW COLUMNS students;
DESCRIBE students;
SELECT * FROM t WHERE a = DATE '2000-01-01' + INTERVAL '30 DAYS';
SELECT * FROM t WHERE a = DATE '2000-01-01' + INTERVAL '10' DAY;
SELECT * FROM t WHERE a BETWEEN '2000-01-01' AND DATE '2000-01-01' - 1 MONTH;
SELECT (CAST('2002-5-01' as DATE) + INTERVAL '60 days');
SELECT CAST(student.student_number as BIGINT) FROM student;
SELECT student.name AS character FROM student;
# ROW LOCKING
SELECT * FROM test WHERE id = 1 FOR UPDATE;
SELECT * FROM test WHERE id = 1 FOR SHARE;
SELECT * FROM test WHERE id = 1 FOR NO KEY UPDATE;
SELECT * FROM test WHERE id = 1 FOR KEY SHARE;
SELECT * FROM test WHERE id = 1 FOR UPDATE SKIP LOCKED;
SELECT * FROM test WHERE id = 1 FOR UPDATE NOWAIT;
SELECT * FROM test1, test2 WHERE test1.id = 10 FOR UPDATE OF test1;
SELECT * FROM test1, test2 WHERE test2.val = 2 FOR SHARE OF test1, test2;
SELECT * FROM test1, test2 WHERE test2.val = 2 FOR UPDATE OF test1 FOR SHARE OF test2;
# WINDOW EXPRESSIONS
SELECT test1, sum(sum(test2)) OVER (PARTITION BY test3 ORDER BY test4 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) an_alias FROM test;
SELECT sum(test2)/sum(sum(test2)) OVER (PARTITION BY test1) FROM test GROUP BY test3;
SELECT test1, sum(sum(test2)) OVER (PARTITION BY test3, test4 ORDER BY test5, test6 ROWS BETWEEN 1 PRECEDING AND 2 FOLLOWING) FROM test;
SELECT test1, rank() OVER (ORDER BY test2 DESC, test3 ASC) rnk FROM test;
SELECT rank() OVER () FROM test;
SELECT rank() OVER (PARTITION BY test1) FROM test;
SELECT rank() OVER (PARTITION BY test1 ORDER BY test2) FROM test;

View File

@ -0,0 +1,9 @@
-- http://www.sqlserver-dba.com/2011/09/this-is-a-followup-on-my-earlier-post-of-sql-server-test-data-generation-testing-tools-i-had-some-requests-for-my-set-up-pr.html
SELECT L_RETURNFLAG, L_LINESTATUS, SUM(L_QUANTITY) AS SUM_QTY,
SUM(L_EXTENDEDPRICE) AS SUM_BASE_PRICE, SUM(L_EXTENDEDPRICE*(1-L_DISCOUNT)) AS SUM_DISC_PRICE,
SUM(L_EXTENDEDPRICE*(1-L_DISCOUNT)*(1+L_TAX)) AS SUM_CHARGE, AVG(L_QUANTITY) AS AVG_QTY,
AVG(L_EXTENDEDPRICE) AS AVG_PRICE, AVG(L_DISCOUNT) AS AVG_DISC, COUNT(*) AS COUNT_ORDER
FROM LINEITEM
WHERE L_SHIPDATE <= dateadd(dd, -90, cast('1998-12-01' as datetime))
GROUP BY L_RETURNFLAG, L_LINESTATUS
ORDER BY L_RETURNFLAG,L_LINESTATUS

View File

@ -0,0 +1,10 @@
-- http://www.sqlserver-dba.com/2011/09/this-is-a-followup-on-my-earlier-post-of-sql-server-test-data-generation-testing-tools-i-had-some-requests-for-my-set-up-pr.html
SELECT TOP 100 S_ACCTBAL, S_NAME, N_NAME, P_PARTKEY, P_MFGR, S_ADDRESS, S_PHONE, S_COMMENT
FROM PART, SUPPLIER, PARTSUPP, NATION, REGION
WHERE P_PARTKEY = PS_PARTKEY AND S_SUPPKEY = PS_SUPPKEY AND P_SIZE = 15 AND
P_TYPE LIKE '%%BRASS' AND S_NATIONKEY = N_NATIONKEY AND N_REGIONKEY = R_REGIONKEY AND
R_NAME = 'EUROPE' AND
PS_SUPPLYCOST = (SELECT MIN(PS_SUPPLYCOST) FROM PARTSUPP, SUPPLIER, NATION, REGION
WHERE P_PARTKEY = PS_PARTKEY AND S_SUPPKEY = PS_SUPPKEY
AND S_NATIONKEY = N_NATIONKEY AND N_REGIONKEY = R_REGIONKEY AND R_NAME = 'EUROPE')
ORDER BY S_ACCTBAL DESC, N_NAME, S_NAME, P_PARTKEY

View File

@ -0,0 +1,7 @@
-- http://www.sqlserver-dba.com/2011/09/this-is-a-followup-on-my-earlier-post-of-sql-server-test-data-generation-testing-tools-i-had-some-requests-for-my-set-up-pr.html
SELECT TOP 10 L_ORDERKEY, SUM(L_EXTENDEDPRICE*(1-L_DISCOUNT)) AS REVENUE, O_ORDERDATE, O_SHIPPRIORITY
FROM CUSTOMER, ORDERS, LINEITEM
WHERE C_MKTSEGMENT = 'BUILDING' AND C_CUSTKEY = O_CUSTKEY AND L_ORDERKEY = O_ORDERKEY AND
O_ORDERDATE < '1995-03-15' AND L_SHIPDATE > '1995-03-15'
GROUP BY L_ORDERKEY, O_ORDERDATE, O_SHIPPRIORITY
ORDER BY REVENUE DESC, O_ORDERDATE;

View File

@ -0,0 +1,6 @@
-- http://www.sqlserver-dba.com/2011/09/this-is-a-followup-on-my-earlier-post-of-sql-server-test-data-generation-testing-tools-i-had-some-requests-for-my-set-up-pr.html
SELECT O_ORDERPRIORITY, COUNT(*) AS ORDER_COUNT FROM ORDERS
WHERE O_ORDERDATE >= '1993-07-01' AND O_ORDERDATE < dateadd(mm,3, cast('1993-07-01' as datetime))
AND EXISTS (SELECT * FROM LINEITEM WHERE L_ORDERKEY = O_ORDERKEY AND L_COMMITDATE < L_RECEIPTDATE)
GROUP BY O_ORDERPRIORITY
ORDER BY O_ORDERPRIORITY

View File

@ -0,0 +1,9 @@
-- http://www.sqlserver-dba.com/2011/09/this-is-a-followup-on-my-earlier-post-of-sql-server-test-data-generation-testing-tools-i-had-some-requests-for-my-set-up-pr.html
SELECT N_NAME, SUM(L_EXTENDEDPRICE*(1-L_DISCOUNT)) AS REVENUE
FROM CUSTOMER, ORDERS, LINEITEM, SUPPLIER, NATION, REGION
WHERE C_CUSTKEY = O_CUSTKEY AND L_ORDERKEY = O_ORDERKEY AND L_SUPPKEY = S_SUPPKEY
AND C_NATIONKEY = S_NATIONKEY AND S_NATIONKEY = N_NATIONKEY AND N_REGIONKEY = R_REGIONKEY
AND R_NAME = 'ASIA' AND O_ORDERDATE >= '1994-01-01'
AND O_ORDERDATE < DATEADD(YY, 1, cast('1994-01-01' as datetime))
GROUP BY N_NAME
ORDER BY REVENUE DESC

View File

@ -0,0 +1,5 @@
-- http://www.sqlserver-dba.com/2011/09/this-is-a-followup-on-my-earlier-post-of-sql-server-test-data-generation-testing-tools-i-had-some-requests-for-my-set-up-pr.html
SELECT SUM(L_EXTENDEDPRICE*L_DISCOUNT) AS REVENUE
FROM LINEITEM
WHERE L_SHIPDATE >= '1994-01-01' AND L_SHIPDATE < dateadd(yy, 1, cast('1994-01-01' as datetime))
AND L_DISCOUNT BETWEEN .06 - 0.01 AND .06 + 0.01 AND L_QUANTITY < 24

View File

@ -0,0 +1,11 @@
-- http://www.sqlserver-dba.com/2011/09/this-is-a-followup-on-my-earlier-post-of-sql-server-test-data-generation-testing-tools-i-had-some-requests-for-my-set-up-pr.html
SELECT SUPP_NATION, CUST_NATION, L_YEAR, SUM(VOLUME) AS REVENUE
FROM ( SELECT N1.N_NAME AS SUPP_NATION, N2.N_NAME AS CUST_NATION, datepart(yy, L_SHIPDATE) AS L_YEAR,
L_EXTENDEDPRICE*(1-L_DISCOUNT) AS VOLUME
FROM SUPPLIER, LINEITEM, ORDERS, CUSTOMER, NATION N1, NATION N2
WHERE S_SUPPKEY = L_SUPPKEY AND O_ORDERKEY = L_ORDERKEY AND C_CUSTKEY = O_CUSTKEY
AND S_NATIONKEY = N1.N_NATIONKEY AND C_NATIONKEY = N2.N_NATIONKEY AND ((N1.N_NAME = 'FRANCE' AND N2.N_NAME = 'GERMANY') OR
(N1.N_NAME = 'GERMANY' AND N2.N_NAME = 'FRANCE')) AND
L_SHIPDATE BETWEEN '1995-01-01' AND '1996-12-31' ) AS SHIPPING
GROUP BY SUPP_NATION, CUST_NATION, L_YEAR
ORDER BY SUPP_NATION, CUST_NATION, L_YEAR

View File

@ -0,0 +1,10 @@
-- http://www.sqlserver-dba.com/2011/09/this-is-a-followup-on-my-earlier-post-of-sql-server-test-data-generation-testing-tools-i-had-some-requests-for-my-set-up-pr.html
SELECT O_YEAR, SUM(CASE WHEN NATION = 'BRAZIL' THEN VOLUME ELSE 0 END)/SUM(VOLUME) AS MKT_SHARE
FROM (SELECT datepart(yy,O_ORDERDATE) AS O_YEAR, L_EXTENDEDPRICE*(1-L_DISCOUNT) AS VOLUME, N2.N_NAME AS NATION
FROM "PART", SUPPLIER, LINEITEM, ORDERS, CUSTOMER, NATION N1, NATION N2, REGION
WHERE P_PARTKEY = L_PARTKEY AND S_SUPPKEY = L_SUPPKEY AND L_ORDERKEY = O_ORDERKEY
AND O_CUSTKEY = C_CUSTKEY AND C_NATIONKEY = N1.N_NATIONKEY AND
N1.N_REGIONKEY = R_REGIONKEY AND R_NAME = 'AMERICA' AND S_NATIONKEY = N2.N_NATIONKEY
AND O_ORDERDATE BETWEEN '1995-01-01' AND '1996-12-31' AND P_TYPE= 'ECONOMY ANODIZED STEEL') AS ALL_NATIONS
GROUP BY O_YEAR
ORDER BY O_YEAR

View File

@ -0,0 +1,10 @@
-- http://www.sqlserver-dba.com/2011/09/this-is-a-followup-on-my-earlier-post-of-sql-server-test-data-generation-testing-tools-i-had-some-requests-for-my-set-up-pr.html
SELECT NATION, O_YEAR, SUM(AMOUNT) AS SUM_PROFIT
FROM (SELECT N_NAME AS NATION, datepart(yy, O_ORDERDATE) AS O_YEAR,
L_EXTENDEDPRICE*(1-L_DISCOUNT)-PS_SUPPLYCOST*L_QUANTITY AS AMOUNT
FROM "PART", SUPPLIER, LINEITEM, PARTSUPP, ORDERS, NATION
WHERE S_SUPPKEY = L_SUPPKEY AND PS_SUPPKEY= L_SUPPKEY AND PS_PARTKEY = L_PARTKEY AND
P_PARTKEY= L_PARTKEY AND O_ORDERKEY = L_ORDERKEY AND S_NATIONKEY = N_NATIONKEY AND
P_NAME LIKE '%%green%%') AS PROFIT
GROUP BY NATION, O_YEAR
ORDER BY NATION, O_YEAR DESC

View File

@ -0,0 +1,9 @@
-- http://www.sqlserver-dba.com/2011/09/this-is-a-followup-on-my-earlier-post-of-sql-server-test-data-generation-testing-tools-i-had-some-requests-for-my-set-up-pr.html
SELECT TOP 20 C_CUSTKEY, C_NAME, SUM(L_EXTENDEDPRICE*(1-L_DISCOUNT)) AS REVENUE, C_ACCTBAL,
N_NAME, C_ADDRESS, C_PHONE, C_COMMENT
FROM CUSTOMER, ORDERS, LINEITEM, NATION
WHERE C_CUSTKEY = O_CUSTKEY AND L_ORDERKEY = O_ORDERKEY AND O_ORDERDATE>= '1993-10-01' AND
O_ORDERDATE < dateadd(mm, 3, cast('1993-10-01' as datetime)) AND
L_RETURNFLAG = 'R' AND C_NATIONKEY = N_NATIONKEY
GROUP BY C_CUSTKEY, C_NAME, C_ACCTBAL, C_PHONE, N_NAME, C_ADDRESS, C_COMMENT
ORDER BY REVENUE DESC

View File

@ -0,0 +1,10 @@
-- http://www.sqlserver-dba.com/2011/09/this-is-a-followup-on-my-earlier-post-of-sql-server-test-data-generation-testing-tools-i-had-some-requests-for-my-set-up-pr.html
-- TPC_H Query 11 - Important Stock Identification
SELECT PS_PARTKEY, SUM(PS_SUPPLYCOST*PS_AVAILQTY) AS VALUE
FROM PARTSUPP, SUPPLIER, NATION
WHERE PS_SUPPKEY = S_SUPPKEY AND S_NATIONKEY = N_NATIONKEY AND N_NAME = 'GERMANY'
GROUP BY PS_PARTKEY
HAVING SUM(PS_SUPPLYCOST*PS_AVAILQTY) > (SELECT SUM(PS_SUPPLYCOST*PS_AVAILQTY) * 0.0001000000
FROM PARTSUPP, SUPPLIER, NATION
WHERE PS_SUPPKEY = S_SUPPKEY AND S_NATIONKEY = N_NATIONKEY AND N_NAME = 'GERMANY')
ORDER BY VALUE DESC;

View File

@ -0,0 +1,10 @@
-- TPC_H Query 12 - Shipping Modes and Order Priority
SELECT L_SHIPMODE,
SUM(CASE WHEN O_ORDERPRIORITY = '1-URGENT' OR O_ORDERPRIORITY = '2-HIGH' THEN 1 ELSE 0 END) AS HIGH_LINE_COUNT,
SUM(CASE WHEN O_ORDERPRIORITY <> '1-URGENT' AND O_ORDERPRIORITY <> '2-HIGH' THEN 1 ELSE 0 END ) AS LOW_LINE_COUNT
FROM ORDERS, LINEITEM
WHERE O_ORDERKEY = L_ORDERKEY AND L_SHIPMODE IN ('MAIL','SHIP')
AND L_COMMITDATE < L_RECEIPTDATE AND L_SHIPDATE < L_COMMITDATE AND L_RECEIPTDATE >= '1994-01-01'
AND L_RECEIPTDATE < dateadd(mm, 1, cast('1995-09-01' as datetime))
GROUP BY L_SHIPMODE
ORDER BY L_SHIPMODE;

View File

@ -0,0 +1,8 @@
-- TPC_H Query 13 - Customer Distribution
SELECT C_COUNT, COUNT(*) AS CUSTDIST
FROM (SELECT C_CUSTKEY, COUNT(O_ORDERKEY)
FROM CUSTOMER left outer join ORDERS on C_CUSTKEY = O_CUSTKEY
AND O_COMMENT not like '%%special%%requests%%'
GROUP BY C_CUSTKEY) AS C_ORDERS (C_CUSTKEY, C_COUNT)
GROUP BY C_COUNT
ORDER BY CUSTDIST DESC, C_COUNT DESC;

View File

@ -0,0 +1,5 @@
-- TPC_H Query 14 - Promotion Effect
SELECT 100.00* SUM(CASE WHEN P_TYPE LIKE 'PROMO%%' THEN L_EXTENDEDPRICE*(1-L_DISCOUNT)
ELSE 0 END) / SUM(L_EXTENDEDPRICE*(1-L_DISCOUNT)) AS PROMO_REVENUE
FROM LINEITEM, PART
WHERE L_PARTKEY = P_PARTKEY AND L_SHIPDATE >= '1995-09-01' AND L_SHIPDATE < dateadd(mm, 1, '1995-09-01');

View File

@ -0,0 +1,15 @@
-- TPC_H Query 15.1 - Create View for Top Supplier Query
CREATE VIEW REVENUE0 (SUPPLIER_NO, TOTAL_REVENUE) AS
SELECT L_SUPPKEY, SUM(L_EXTENDEDPRICE*(1-L_DISCOUNT)) FROM LINEITEM
WHERE L_SHIPDATE >= '1996-01-01' AND L_SHIPDATE < dateadd(mm, 3, cast('1996-01-01' as datetime))
GROUP BY L_SUPPKEY;
-- TPC_H Query 15.2 - Top Supplier
SELECT S_SUPPKEY, S_NAME, S_ADDRESS, S_PHONE, TOTAL_REVENUE
FROM SUPPLIER, REVENUE0
WHERE S_SUPPKEY = SUPPLIER_NO AND TOTAL_REVENUE = (SELECT MAX(TOTAL_REVENUE) FROM REVENUE0)
ORDER BY S_SUPPKEY;
-- TPC_H Query 15.3 - Drop View
DROP VIEW REVENUE0;

View File

@ -0,0 +1,9 @@
-- http://www.sqlserver-dba.com/2011/09/this-is-a-followup-on-my-earlier-post-of-sql-server-test-data-generation-testing-tools-i-had-some-requests-for-my-set-up-pr.html
-- TPC_H Query 16 - Parts/Supplier Relationship
SELECT P_BRAND, P_TYPE, P_SIZE, COUNT(DISTINCT PS_SUPPKEY) AS SUPPLIER_CNT
FROM PARTSUPP, "PART"
WHERE P_PARTKEY = PS_PARTKEY AND P_BRAND <> 'Brand#45' AND P_TYPE NOT LIKE 'MEDIUM POLISHED%%'
AND P_SIZE IN (49, 14, 23, 45, 19, 3, 36, 9) AND PS_SUPPKEY NOT IN (SELECT S_SUPPKEY FROM SUPPLIER
WHERE S_COMMENT LIKE '%%Customer%%Complaints%%')
GROUP BY P_BRAND, P_TYPE, P_SIZE
ORDER BY SUPPLIER_CNT DESC, P_BRAND, P_TYPE, P_SIZE;

View File

@ -0,0 +1,4 @@
-- TPC_H Query 17 - Small-Quantity-Order Revenue
SELECT SUM(L_EXTENDEDPRICE)/7.0 AS AVG_YEARLY FROM LINEITEM, "PART"
WHERE P_PARTKEY = L_PARTKEY AND P_BRAND = 'Brand#23' AND P_CONTAINER = 'MED BOX'
AND L_QUANTITY < (SELECT 0.2*AVG(L_QUANTITY) FROM LINEITEM WHERE L_PARTKEY = P_PARTKEY);

View File

@ -0,0 +1,7 @@
-- TPC_H Query 18 - Large Volume Customer
SELECT TOP 100 C_NAME, C_CUSTKEY, O_ORDERKEY, O_ORDERDATE, O_TOTALPRICE, SUM(L_QUANTITY)
FROM CUSTOMER, ORDERS, LINEITEM
WHERE O_ORDERKEY IN (SELECT L_ORDERKEY FROM LINEITEM GROUP BY L_ORDERKEY HAVING
SUM(L_QUANTITY) > 300) AND C_CUSTKEY = O_CUSTKEY AND O_ORDERKEY = L_ORDERKEY
GROUP BY C_NAME, C_CUSTKEY, O_ORDERKEY, O_ORDERDATE, O_TOTALPRICE
ORDER BY O_TOTALPRICE DESC, O_ORDERDATE;

View File

@ -0,0 +1,9 @@
-- TPC_H Query 19 - Discounted Revenue
SELECT SUM(L_EXTENDEDPRICE* (1 - L_DISCOUNT)) AS REVENUE
FROM LINEITEM, "PART"
WHERE (P_PARTKEY = L_PARTKEY AND P_BRAND = 'Brand#12' AND P_CONTAINER IN ('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG') AND L_QUANTITY >= 1 AND L_QUANTITY <= 1 + 10 AND P_SIZE BETWEEN 1 AND 5
AND L_SHIPMODE IN ('AIR', 'AIR REG') AND L_SHIPINSTRUCT = 'DELIVER IN PERSON')
OR (P_PARTKEY = L_PARTKEY AND P_BRAND ='Brand#23' AND P_CONTAINER IN ('MED BAG', 'MED BOX', 'MED PKG', 'MED PACK') AND L_QUANTITY >=10 AND L_QUANTITY <=10 + 10 AND P_SIZE BETWEEN 1 AND 10
AND L_SHIPMODE IN ('AIR', 'AIR REG') AND L_SHIPINSTRUCT = 'DELIVER IN PERSON')
OR (P_PARTKEY = L_PARTKEY AND P_BRAND = 'Brand#34' AND P_CONTAINER IN ( 'LG CASE', 'LG BOX', 'LG PACK', 'LG PKG') AND L_QUANTITY >=20 AND L_QUANTITY <= 20 + 10 AND P_SIZE BETWEEN 1 AND 15
AND L_SHIPMODE IN ('AIR', 'AIR REG') AND L_SHIPINSTRUCT = 'DELIVER IN PERSON');

View File

@ -0,0 +1,8 @@
-- TPC_H Query 20 - Potential Part Promotion
SELECT S_NAME, S_ADDRESS FROM SUPPLIER, NATION
WHERE S_SUPPKEY IN (SELECT PS_SUPPKEY FROM PARTSUPP
WHERE PS_PARTKEY in (SELECT P_PARTKEY FROM "PART" WHERE P_NAME like 'forest%%') AND
PS_AVAILQTY > (SELECT 0.5*sum(L_QUANTITY) FROM LINEITEM WHERE L_PARTKEY = PS_PARTKEY AND
L_SUPPKEY = PS_SUPPKEY AND L_SHIPDATE >= '1994-01-01' AND
L_SHIPDATE < dateadd(yy,1,'1994-01-01'))) AND S_NATIONKEY = N_NATIONKEY AND N_NAME = 'CANADA'
ORDER BY S_NAME;

View File

@ -0,0 +1,11 @@
-- TPC_H Query 21 - Suppliers Who Kept Orders Waiting
SELECT TOP 100 S_NAME, COUNT(*) AS NUMWAIT
FROM SUPPLIER, LINEITEM L1, ORDERS, NATION WHERE S_SUPPKEY = L1.L_SUPPKEY AND
O_ORDERKEY = L1.L_ORDERKEY AND O_ORDERSTATUS = 'F' AND L1.L_RECEIPTDATE> L1.L_COMMITDATE
AND EXISTS (SELECT * FROM LINEITEM L2 WHERE L2.L_ORDERKEY = L1.L_ORDERKEY
AND L2.L_SUPPKEY <> L1.L_SUPPKEY) AND
NOT EXISTS (SELECT * FROM LINEITEM L3 WHERE L3.L_ORDERKEY = L1.L_ORDERKEY AND
L3.L_SUPPKEY <> L1.L_SUPPKEY AND L3.L_RECEIPTDATE > L3.L_COMMITDATE) AND
S_NATIONKEY = N_NATIONKEY AND N_NAME = 'SAUDI ARABIA'
GROUP BY S_NAME
ORDER BY NUMWAIT DESC, S_NAME;

View File

@ -0,0 +1,9 @@
-- TPC_H Query 22 - Global Sales Opportunity */
SELECT CNTRYCODE, COUNT(*) AS NUMCUST, SUM(C_ACCTBAL) AS TOTACCTBAL
FROM (SELECT SUBSTRING(C_PHONE,1,2) AS CNTRYCODE, C_ACCTBAL
FROM CUSTOMER WHERE SUBSTRING(C_PHONE,1,2) IN ('13', '31', '23', '29', '30', '18', '17') AND
C_ACCTBAL > (SELECT AVG(C_ACCTBAL) FROM CUSTOMER WHERE C_ACCTBAL > 0.00 AND
SUBSTRING(C_PHONE,1,2) IN ('13', '31', '23', '29', '30', '18', '17')) AND
NOT EXISTS ( SELECT * FROM ORDERS WHERE O_CUSTKEY = C_CUSTKEY)) AS CUSTSALE
GROUP BY CNTRYCODE
ORDER BY CNTRYCODE;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
#ifndef __HELPER_H__
#define __HELPER_H__
#define TEST_PARSE_SQL_QUERY(query, result, numStatements) \
hsql::SQLParserResult result; \
hsql::SQLParser::parse(query, &result); \
ASSERT(result.isValid()); \
ASSERT_EQ(result.size(), numStatements);
#define TEST_PARSE_SINGLE_SQL(query, stmtType, stmtClass, result, outputVar) \
TEST_PARSE_SQL_QUERY(query, result, 1); \
ASSERT_EQ(result.getStatement(0)->type(), stmtType); \
const stmtClass* outputVar = (const stmtClass*)result.getStatement(0);
#define TEST_CAST_STMT(result, stmt_index, stmtType, stmtClass, outputVar) \
ASSERT_EQ(result.getStatement(stmt_index)->type(), stmtType); \
const stmtClass* outputVar = (const stmtClass*)result.getStatement(stmt_index);
#endif

View File

@ -0,0 +1,44 @@
#include "thirdparty/microtest/microtest.h"
#include <iostream>
#include <map>
#include <string>
#include "SQLParser.h"
#include "parser/bison_parser.h"
#include "sql_asserts.h"
using namespace hsql;
void test_tokens(const std::string& query, const std::vector<int16_t>& expected_tokens) {
std::vector<int16_t> tokens;
ASSERT(SQLParser::tokenize(query, &tokens));
ASSERT_EQ(expected_tokens.size(), tokens.size());
for (unsigned i = 0; i < expected_tokens.size(); ++i) {
ASSERT_EQ(expected_tokens[i], tokens[i]);
}
}
TEST(SQLParserTokenizeTest) {
test_tokens("SELECT * FROM test;", {SQL_SELECT, '*', SQL_FROM, SQL_IDENTIFIER, ';'});
test_tokens("SELECT a, 'b' FROM test WITH HINT;",
{SQL_SELECT, SQL_IDENTIFIER, ',', SQL_STRING, SQL_FROM, SQL_IDENTIFIER, SQL_WITH, SQL_HINT, ';'});
}
TEST(SQLParserTokenizeStringifyTest) {
const std::string query = "SELECT * FROM test;";
std::vector<int16_t> tokens;
ASSERT(SQLParser::tokenize(query, &tokens));
// Make u16string.
std::u16string token_string(tokens.cbegin(), tokens.cend());
// Check if u16 string is cacheable.
std::map<std::u16string, std::string> cache;
cache[token_string] = query;
ASSERT(query == cache[token_string]);
ASSERT(&query != &cache[token_string]);
}

View File

@ -0,0 +1,679 @@
/*
* sql_tests.cpp
*/
#include "thirdparty/microtest/microtest.h"
#include "SQLParser.h"
#include "util/sqlhelper.h"
#include "sql_asserts.h"
using namespace hsql;
TEST(DeleteStatementTest) {
SQLParserResult result;
SQLParser::parse("DELETE FROM students WHERE grade > 2.0;", &result);
ASSERT(result.isValid());
ASSERT_EQ(result.size(), 1);
ASSERT(result.getStatement(0)->type() == kStmtDelete);
const DeleteStatement* stmt = (const DeleteStatement*)result.getStatement(0);
ASSERT_STREQ(stmt->tableName, "students");
ASSERT_NOTNULL(stmt->expr);
ASSERT(stmt->expr->isType(kExprOperator));
ASSERT_STREQ(stmt->expr->expr->name, "grade");
ASSERT_EQ(stmt->expr->expr2->fval, 2.0);
}
TEST(CreateStatementTest) {
SQLParserResult result;
SQLParser::parse(
"CREATE TABLE dummy_table ("
" c_bigint BIGINT, "
" c_boolean BOOLEAN, "
" c_char CHAR(42), "
" c_date DATE, "
" c_datetime DATETIME, "
" c_decimal DECIMAL, "
" c_decimal_precision DECIMAL(13), "
" c_decimal_precision_scale DECIMAL(13,37), "
" c_double_not_null DOUBLE NOT NULL, "
" c_float FLOAT, "
" c_int INT, "
" PRIMARY KEY(c_char, c_int), "
" c_integer_null INTEGER NULL, "
" c_long LONG, "
" c_real REAL, "
" c_smallint SMALLINT, "
" c_text TEXT UNIQUE PRIMARY KEY NOT NULL, "
" c_time TIME, "
" c_time_precision TIME(17), "
" c_timestamp TIMESTAMP, "
" c_varchar VARCHAR(50), "
" c_char_varying CHARACTER VARYING(60)"
")",
&result);
ASSERT(result.isValid());
ASSERT_EQ(result.size(), 1);
ASSERT_EQ(result.getStatement(0)->type(), kStmtCreate);
const CreateStatement* stmt = (const CreateStatement*)result.getStatement(0);
ASSERT_EQ(stmt->type, kCreateTable);
ASSERT_STREQ(stmt->tableName, "dummy_table");
ASSERT_NOTNULL(stmt->columns);
ASSERT_EQ(stmt->columns->size(), 21);
// c_bigint BIGINT
ASSERT_STREQ(stmt->columns->at(0)->name, "c_bigint");
ASSERT_EQ(stmt->columns->at(0)->type, (ColumnType{DataType::BIGINT}));
ASSERT_EQ(stmt->columns->at(0)->nullable, true);
// c_boolean BOOLEAN
ASSERT_STREQ(stmt->columns->at(1)->name, "c_boolean");
ASSERT_EQ(stmt->columns->at(1)->type, (ColumnType{DataType::BOOLEAN}));
ASSERT_EQ(stmt->columns->at(1)->nullable, true);
// c_char CHAR(42)
ASSERT_STREQ(stmt->columns->at(2)->name, "c_char");
ASSERT_EQ(stmt->columns->at(2)->type, (ColumnType{DataType::CHAR, 42}));
ASSERT_NEQ(stmt->columns->at(2)->type, (ColumnType{DataType::CHAR, 43}));
ASSERT_EQ(stmt->columns->at(2)->nullable, true);
// c_date DATE
ASSERT_STREQ(stmt->columns->at(3)->name, "c_date");
ASSERT_EQ(stmt->columns->at(3)->type, (ColumnType{DataType::DATE}));
ASSERT_EQ(stmt->columns->at(3)->nullable, true);
// c_datetime DATETIME
ASSERT_STREQ(stmt->columns->at(4)->name, "c_datetime");
ASSERT_EQ(stmt->columns->at(4)->type, (ColumnType{DataType::DATETIME}));
ASSERT_EQ(stmt->columns->at(4)->nullable, true);
// c_decimal DECIMAL
ASSERT_STREQ(stmt->columns->at(5)->name, "c_decimal");
ASSERT_EQ(stmt->columns->at(5)->type, (ColumnType{DataType::DECIMAL}));
ASSERT_EQ(stmt->columns->at(5)->nullable, true);
// c_decimal_precision DECIMAL(13)
ASSERT_STREQ(stmt->columns->at(6)->name, "c_decimal_precision");
ASSERT_EQ(stmt->columns->at(6)->type, (ColumnType{DataType::DECIMAL, 0, 13}));
ASSERT_NEQ(stmt->columns->at(6)->type, (ColumnType{DataType::DECIMAL, 0, 14}));
ASSERT_NEQ(stmt->columns->at(6)->type, (ColumnType{DataType::DECIMAL, 1, 13}));
ASSERT_EQ(stmt->columns->at(6)->nullable, true);
// c_decimal_precision_scale DECIMAL(13,37)
ASSERT_STREQ(stmt->columns->at(7)->name, "c_decimal_precision_scale");
ASSERT_EQ(stmt->columns->at(7)->type, (ColumnType{DataType::DECIMAL, 0, 13, 37}));
ASSERT_NEQ(stmt->columns->at(7)->type, (ColumnType{DataType::DECIMAL, 0, 14, 37}));
ASSERT_NEQ(stmt->columns->at(7)->type, (ColumnType{DataType::DECIMAL, 0, 13, 38}));
ASSERT_NEQ(stmt->columns->at(7)->type, (ColumnType{DataType::DECIMAL, 1, 13, 37}));
ASSERT_EQ(stmt->columns->at(7)->nullable, true);
// c_double_not_null DOUBLE NOT NULL
ASSERT_STREQ(stmt->columns->at(8)->name, "c_double_not_null");
ASSERT_EQ(stmt->columns->at(8)->type, (ColumnType{DataType::DOUBLE}));
ASSERT_EQ(stmt->columns->at(8)->nullable, false);
ASSERT_EQ(stmt->columns->at(8)->column_constraints->size(), 1);
ASSERT(stmt->columns->at(8)->column_constraints->count(ConstraintType::NotNull));
// c_float FLOAT
ASSERT_STREQ(stmt->columns->at(9)->name, "c_float");
ASSERT_EQ(stmt->columns->at(9)->type, (ColumnType{DataType::FLOAT}));
ASSERT_EQ(stmt->columns->at(9)->nullable, true);
// c_int INT
ASSERT_STREQ(stmt->columns->at(10)->name, "c_int");
ASSERT_EQ(stmt->columns->at(10)->type, (ColumnType{DataType::INT}));
ASSERT_EQ(stmt->columns->at(10)->nullable, true);
// c_integer INTEGER NULL
ASSERT_STREQ(stmt->columns->at(11)->name, "c_integer_null");
ASSERT_EQ(stmt->columns->at(11)->type, (ColumnType{DataType::INT}));
ASSERT_EQ(stmt->columns->at(11)->nullable, true);
ASSERT_EQ(stmt->columns->at(11)->column_constraints->size(), 1);
ASSERT(stmt->columns->at(11)->column_constraints->count(ConstraintType::Null));
// c_long LONG
ASSERT_STREQ(stmt->columns->at(12)->name, "c_long");
ASSERT_EQ(stmt->columns->at(12)->type, (ColumnType{DataType::LONG}));
ASSERT_EQ(stmt->columns->at(12)->nullable, true);
// c_real REAL
ASSERT_STREQ(stmt->columns->at(13)->name, "c_real");
ASSERT_EQ(stmt->columns->at(13)->type, (ColumnType{DataType::REAL}));
ASSERT_EQ(stmt->columns->at(13)->nullable, true);
// c_smallint SMALLINT
ASSERT_STREQ(stmt->columns->at(14)->name, "c_smallint");
ASSERT_EQ(stmt->columns->at(14)->type, (ColumnType{DataType::SMALLINT}));
ASSERT_EQ(stmt->columns->at(14)->nullable, true);
// c_text TEXT UNIQUE PRIMARY KEY NOT NULL
ASSERT_STREQ(stmt->columns->at(15)->name, "c_text");
ASSERT_EQ(stmt->columns->at(15)->type, (ColumnType{DataType::TEXT}));
ASSERT_EQ(stmt->columns->at(15)->nullable, false);
// Expecting two elements in column_constraints since information about NULL constraints is separately stored in
// ColumnDefinition::nullable
ASSERT_EQ(stmt->columns->at(15)->column_constraints->size(), 3);
ASSERT(stmt->columns->at(15)->column_constraints->count(ConstraintType::Unique));
ASSERT(stmt->columns->at(15)->column_constraints->count(ConstraintType::PrimaryKey));
ASSERT(stmt->columns->at(15)->column_constraints->count(ConstraintType::NotNull));
// c_time TIME
ASSERT_STREQ(stmt->columns->at(16)->name, "c_time");
ASSERT_EQ(stmt->columns->at(16)->type, (ColumnType{DataType::TIME}));
ASSERT_EQ(stmt->columns->at(16)->nullable, true);
// c_time_precision TIME(17)
ASSERT_STREQ(stmt->columns->at(17)->name, "c_time_precision");
ASSERT_EQ(stmt->columns->at(17)->type, (ColumnType{DataType::TIME, 0, 17}));
ASSERT_NEQ(stmt->columns->at(17)->type, (ColumnType{DataType::TIME, 0, 18}));
ASSERT_NEQ(stmt->columns->at(17)->type, (ColumnType{DataType::TIME, 1, 17}));
ASSERT_EQ(stmt->columns->at(17)->nullable, true);
// c_timestamp TIMESTAMP
ASSERT_STREQ(stmt->columns->at(18)->name, "c_timestamp");
ASSERT_EQ(stmt->columns->at(18)->type, (ColumnType{DataType::DATETIME}));
ASSERT_EQ(stmt->columns->at(18)->nullable, true);
// c_varchar VARCHAR(50)
ASSERT_STREQ(stmt->columns->at(19)->name, "c_varchar");
ASSERT_EQ(stmt->columns->at(19)->type, (ColumnType{DataType::VARCHAR, 50}));
ASSERT_NEQ(stmt->columns->at(19)->type, (ColumnType{DataType::VARCHAR, 51}));
ASSERT_EQ(stmt->columns->at(19)->nullable, true);
// c_char_varying CHARACTER VARYING(60)
ASSERT_STREQ(stmt->columns->at(20)->name, "c_char_varying");
ASSERT_EQ(stmt->columns->at(20)->type, (ColumnType{DataType::VARCHAR, 60}));
ASSERT_NEQ(stmt->columns->at(20)->type, (ColumnType{DataType::VARCHAR, 61}));
// Table constraints are identified and separated during the parsing of the SQL string
// Table constraints:
// - PRIMARY KEY(c_char, c_int)
ASSERT_EQ(stmt->tableConstraints->size(), 1);
ASSERT(stmt->tableConstraints->at(0)->type == ConstraintType::PrimaryKey);
ASSERT_STREQ(stmt->tableConstraints->at(0)->columnNames->at(0), "c_char");
ASSERT_STREQ(stmt->tableConstraints->at(0)->columnNames->at(1), "c_int");
}
TEST(CreateAsSelectStatementTest) {
SQLParserResult result;
SQLParser::parse("CREATE TABLE students_2 AS SELECT student_number, grade FROM students", &result);
ASSERT(result.isValid());
ASSERT_EQ(result.size(), 1);
ASSERT_EQ(result.getStatement(0)->type(), kStmtCreate);
const CreateStatement* stmt = (const CreateStatement*)result.getStatement(0);
ASSERT_EQ(stmt->type, kCreateTable);
ASSERT_STREQ(stmt->tableName, "students_2");
ASSERT_NULL(stmt->columns);
ASSERT_NOTNULL(stmt->select);
ASSERT(stmt->select->selectList->at(0)->isType(kExprColumnRef));
ASSERT_STREQ(stmt->select->selectList->at(0)->getName(), "student_number");
ASSERT(stmt->select->selectList->at(1)->isType(kExprColumnRef));
ASSERT_STREQ(stmt->select->selectList->at(1)->getName(), "grade");
}
TEST(UpdateStatementTest) {
SQLParserResult result;
SQLParser::parse("UPDATE students SET grade = 5.0, name = 'test' WHERE name = 'Max O''Mustermann';", &result);
ASSERT(result.isValid());
ASSERT_EQ(result.size(), 1);
ASSERT_EQ(result.getStatement(0)->type(), kStmtUpdate);
const UpdateStatement* stmt = (const UpdateStatement*)result.getStatement(0);
ASSERT_NOTNULL(stmt->table);
ASSERT_STREQ(stmt->table->name, "students");
ASSERT_NOTNULL(stmt->updates);
ASSERT_EQ(stmt->updates->size(), 2);
ASSERT_STREQ(stmt->updates->at(0)->column, "grade");
ASSERT_STREQ(stmt->updates->at(1)->column, "name");
ASSERT(stmt->updates->at(0)->value->isType(kExprLiteralFloat));
ASSERT(stmt->updates->at(1)->value->isType(kExprLiteralString));
ASSERT_EQ(stmt->updates->at(0)->value->fval, 5.0);
ASSERT_STREQ(stmt->updates->at(1)->value->name, "test");
ASSERT_NOTNULL(stmt->where);
ASSERT(stmt->where->isType(kExprOperator));
ASSERT_EQ(stmt->where->opType, kOpEquals);
ASSERT_STREQ(stmt->where->expr->name, "name");
ASSERT_STREQ(stmt->where->expr2->name, "Max O'Mustermann");
}
TEST(InsertStatementTest) {
TEST_PARSE_SINGLE_SQL("INSERT INTO students VALUES ('Max Mustermann', 12345, 'Musterhausen', 2.0)", kStmtInsert,
InsertStatement, result, stmt);
ASSERT_EQ(stmt->values->size(), 4);
// TODO
}
TEST(AlterStatementDropActionTest) {
SQLParserResult result;
SQLParser::parse("ALTER TABLE mytable DROP COLUMN IF EXISTS mycolumn", &result);
ASSERT(result.isValid());
ASSERT_EQ(result.size(), 1);
const AlterStatement* stmt = (const AlterStatement*)result.getStatement(0);
ASSERT_STREQ(stmt->name, "mytable");
ASSERT_EQ(stmt->ifTableExists, false);
auto dropAction = (const DropColumnAction*)stmt->action;
ASSERT_EQ(dropAction->type, hsql::ActionType::DropColumn);
ASSERT_STREQ(dropAction->columnName, "mycolumn");
}
TEST(CreateIndexStatementTest) {
SQLParserResult result;
SQLParser::parse("CREATE INDEX myindex ON myTable (col1);", &result);
ASSERT(result.isValid());
ASSERT_EQ(result.size(), 1);
const CreateStatement* stmt = (const CreateStatement*)result.getStatement(0);
ASSERT_STREQ(stmt->indexName, "myindex");
ASSERT_STREQ(stmt->tableName, "myTable");
ASSERT_EQ(stmt->type, kCreateIndex);
ASSERT_EQ(stmt->ifNotExists, false);
ASSERT_EQ(stmt->indexColumns->size(), 1);
}
TEST(CreateIndexStatementIfNotExistsTest) {
SQLParserResult result;
SQLParser::parse("CREATE INDEX IF NOT EXISTS myindex ON myTable (col1, col2);", &result);
ASSERT(result.isValid());
ASSERT_EQ(result.size(), 1);
const CreateStatement* stmt = (const CreateStatement*)result.getStatement(0);
ASSERT_STREQ(stmt->indexName, "myindex");
ASSERT_STREQ(stmt->tableName, "myTable");
ASSERT_EQ(stmt->type, kCreateIndex);
ASSERT_EQ(stmt->ifNotExists, true);
ASSERT_EQ(stmt->indexColumns->size(), 2);
}
TEST(DropIndexTest) {
SQLParserResult result;
SQLParser::parse("DROP INDEX myindex", &result);
ASSERT(result.isValid());
ASSERT_EQ(result.size(), 1);
const DropStatement* stmt = (const DropStatement*)result.getStatement(0);
ASSERT_STREQ(stmt->indexName, "myindex");
ASSERT_EQ(stmt->ifExists, false);
}
TEST(DropIndexIfExistsTest) {
SQLParserResult result;
SQLParser::parse("DROP INDEX IF EXISTS myindex", &result);
ASSERT(result.isValid());
ASSERT_EQ(result.size(), 1);
const DropStatement* stmt = (const DropStatement*)result.getStatement(0);
ASSERT_STREQ(stmt->indexName, "myindex");
ASSERT_EQ(stmt->ifExists, true);
}
TEST(DropTableStatementTest) {
TEST_PARSE_SINGLE_SQL("DROP TABLE students", kStmtDrop, DropStatement, result, stmt);
ASSERT_FALSE(stmt->ifExists);
ASSERT_EQ(stmt->type, kDropTable);
ASSERT_NOTNULL(stmt->name);
ASSERT_STREQ(stmt->name, "students");
}
TEST(DropTableIfExistsStatementTest) {
TEST_PARSE_SINGLE_SQL("DROP TABLE IF EXISTS students", kStmtDrop, DropStatement, result, stmt);
ASSERT_TRUE(stmt->ifExists);
ASSERT_EQ(stmt->type, kDropTable);
ASSERT_NOTNULL(stmt->name);
ASSERT_STREQ(stmt->name, "students");
}
TEST(ReleaseStatementTest) {
TEST_PARSE_SINGLE_SQL("SELECT * FROM students;", kStmtSelect, SelectStatement, result, stmt);
ASSERT_EQ(1, result.size());
ASSERT_NULL(stmt->whereClause);
std::vector<SQLStatement*> statements = result.releaseStatements();
ASSERT_EQ(0, result.size());
for (SQLStatement* stmt : statements) {
delete stmt;
}
}
TEST(ShowTableStatementTest) {
TEST_PARSE_SINGLE_SQL("SHOW TABLES;", kStmtShow, ShowStatement, result, stmt);
ASSERT_EQ(stmt->type, kShowTables);
ASSERT_NULL(stmt->name);
}
TEST(ShowColumnsStatementTest) {
TEST_PARSE_SINGLE_SQL("SHOW COLUMNS students;", kStmtShow, ShowStatement, result, stmt);
ASSERT_EQ(stmt->type, kShowColumns);
ASSERT_NOTNULL(stmt->name);
ASSERT_STREQ(stmt->name, "students");
}
TEST(DescribeStatementTest) {
TEST_PARSE_SINGLE_SQL("DESCRIBE students;", kStmtShow, ShowStatement, result, stmt);
ASSERT_EQ(stmt->type, kShowColumns);
ASSERT_NOTNULL(stmt->name);
ASSERT_STREQ(stmt->name, "students");
}
TEST(ImportStatementTest) {
TEST_PARSE_SINGLE_SQL("IMPORT FROM TBL FILE 'students_file' INTO students;", kStmtImport, ImportStatement, result,
stmt);
ASSERT_EQ(stmt->type, kImportTbl);
ASSERT_NOTNULL(stmt->tableName);
ASSERT_STREQ(stmt->tableName, "students");
ASSERT_STREQ(stmt->filePath, "students_file");
}
TEST(CopyStatementTest) {
TEST_PARSE_SINGLE_SQL("COPY students FROM 'students_file' WITH FORMAT BINARY;", kStmtImport, ImportStatement,
import_result, import_stmt);
ASSERT_EQ(import_stmt->type, kImportBinary);
ASSERT_NOTNULL(import_stmt->tableName);
ASSERT_STREQ(import_stmt->tableName, "students");
ASSERT_NOTNULL(import_stmt->filePath);
ASSERT_STREQ(import_stmt->filePath, "students_file");
ASSERT_NULL(import_stmt->whereClause);
TEST_PARSE_SINGLE_SQL("COPY students FROM 'students_file' WHERE lastname = 'Potter';", kStmtImport, ImportStatement,
import_filter_result, import_filter_stmt);
ASSERT_EQ(import_filter_stmt->type, kImportAuto);
ASSERT_NOTNULL(import_filter_stmt->tableName);
ASSERT_STREQ(import_filter_stmt->tableName, "students");
ASSERT_NOTNULL(import_filter_stmt->filePath);
ASSERT_STREQ(import_filter_stmt->filePath, "students_file");
ASSERT_NOTNULL(import_filter_stmt->whereClause);
ASSERT_EQ(import_filter_stmt->whereClause->opType, kOpEquals);
ASSERT_EQ(import_filter_stmt->whereClause->expr->type, kExprColumnRef);
ASSERT_STREQ(import_filter_stmt->whereClause->expr->name, "lastname");
ASSERT_EQ(import_filter_stmt->whereClause->expr2->type, kExprLiteralString);
ASSERT_STREQ(import_filter_stmt->whereClause->expr2->name, "Potter");
TEST_PARSE_SINGLE_SQL("COPY students TO 'students_file' WITH FORMAT CSV;", kStmtExport, ExportStatement,
export_table_result, export_table_stmt);
ASSERT_EQ(export_table_stmt->type, kImportCSV);
ASSERT_NOTNULL(export_table_stmt->tableName);
ASSERT_STREQ(export_table_stmt->tableName, "students");
ASSERT_NOTNULL(export_table_stmt->filePath);
ASSERT_STREQ(export_table_stmt->filePath, "students_file");
ASSERT_NULL(export_table_stmt->select);
TEST_PARSE_SINGLE_SQL("COPY (SELECT firstname, lastname FROM students) TO 'students_file';", kStmtExport,
ExportStatement, export_select_result, export_select_stmt);
ASSERT_EQ(export_select_stmt->type, kImportAuto);
ASSERT_NULL(export_select_stmt->tableName);
ASSERT_NOTNULL(export_select_stmt->filePath);
ASSERT_STREQ(export_select_stmt->filePath, "students_file");
ASSERT_NOTNULL(export_select_stmt->select);
const auto& select_stmt = export_select_stmt->select;
ASSERT_NULL(select_stmt->whereClause);
ASSERT_NULL(select_stmt->groupBy);
ASSERT_EQ(select_stmt->selectList->size(), 2);
ASSERT(select_stmt->selectList->at(0)->isType(kExprColumnRef));
ASSERT_STREQ(select_stmt->selectList->at(0)->getName(), "firstname");
ASSERT(select_stmt->selectList->at(1)->isType(kExprColumnRef));
ASSERT_STREQ(select_stmt->selectList->at(1)->getName(), "lastname");
ASSERT_NOTNULL(select_stmt->fromTable);
ASSERT_STREQ(select_stmt->fromTable->name, "students");
}
SQLParserResult parse_and_move(std::string query) {
hsql::SQLParserResult result;
hsql::SQLParser::parse(query, &result);
// Moves on return.
return result;
}
SQLParserResult move_in_and_back(SQLParserResult res) {
// Moves on return.
return res;
}
TEST(MoveSQLResultTest) {
SQLParserResult res = parse_and_move("SELECT * FROM test;");
ASSERT(res.isValid());
ASSERT_EQ(1, res.size());
// Moved around.
SQLParserResult new_res = move_in_and_back(std::move(res));
// Original object should be invalid.
ASSERT_FALSE(res.isValid());
ASSERT_EQ(0, res.size());
ASSERT(new_res.isValid());
ASSERT_EQ(1, new_res.size());
}
TEST(HintTest) {
TEST_PARSE_SINGLE_SQL("SELECT * FROM students WITH HINT(NO_CACHE, SAMPLE_RATE(10));", kStmtSelect, SelectStatement,
result, stmt);
ASSERT_NOTNULL(stmt->hints);
ASSERT_EQ(2, stmt->hints->size());
ASSERT_STREQ("NO_CACHE", stmt->hints->at(0)->name);
ASSERT_STREQ("SAMPLE_RATE", stmt->hints->at(1)->name);
ASSERT_EQ(1, stmt->hints->at(1)->exprList->size());
ASSERT_EQ(10, stmt->hints->at(1)->exprList->at(0)->ival);
}
TEST(StringLengthTest) {
TEST_PARSE_SQL_QUERY("SELECT * FROM bar; INSERT INTO foo VALUES (4);\t\n SELECT * FROM foo;", result, 3);
ASSERT_EQ(result.getStatement(0)->stringLength, 18);
ASSERT_EQ(result.getStatement(1)->stringLength, 28);
ASSERT_EQ(result.getStatement(2)->stringLength, 21);
}
TEST(ExceptOperatorTest) {
TEST_PARSE_SINGLE_SQL("SELECT * FROM students EXCEPT SELECT * FROM students_2;", kStmtSelect, SelectStatement, result,
stmt);
ASSERT_STREQ(stmt->setOperations->back()->nestedSelectStatement->fromTable->name, "students_2");
ASSERT_STREQ(stmt->fromTable->name, "students");
ASSERT_EQ(stmt->setOperations->back()->setType, kSetExcept);
}
TEST(IntersectOperatorTest) {
TEST_PARSE_SINGLE_SQL("SELECT * FROM students INTERSECT SELECT * FROM students_2;", kStmtSelect, SelectStatement,
result, stmt);
ASSERT_STREQ(stmt->setOperations->back()->nestedSelectStatement->fromTable->name, "students_2");
ASSERT_STREQ(stmt->fromTable->name, "students");
ASSERT_EQ(stmt->setOperations->back()->setType, kSetIntersect);
}
TEST(UnionOperatorTest) {
TEST_PARSE_SINGLE_SQL("SELECT * FROM students UNION SELECT * FROM students_2;", kStmtSelect, SelectStatement, result,
stmt);
ASSERT_STREQ(stmt->setOperations->back()->nestedSelectStatement->fromTable->name, "students_2");
ASSERT_STREQ(stmt->fromTable->name, "students");
ASSERT_EQ(stmt->setOperations->back()->setType, kSetUnion);
ASSERT_FALSE(stmt->setOperations->back()->isAll);
}
TEST(UnionAllOperatorTest) {
TEST_PARSE_SINGLE_SQL("SELECT * FROM students UNION ALL SELECT * FROM students_2;", kStmtSelect, SelectStatement,
result, stmt);
ASSERT_STREQ(stmt->setOperations->back()->nestedSelectStatement->fromTable->name, "students_2");
ASSERT_STREQ(stmt->fromTable->name, "students");
ASSERT_TRUE(stmt->setOperations->back()->isAll);
}
TEST(NestedSetOperationTest) {
TEST_PARSE_SINGLE_SQL("SELECT * FROM students INTERSECT SELECT grade FROM students_2 UNION SELECT * FROM employees;",
kStmtSelect, SelectStatement, result, stmt);
ASSERT_STREQ(
stmt->setOperations->back()->nestedSelectStatement->setOperations->back()->nestedSelectStatement->fromTable->name,
"employees");
ASSERT_STREQ(stmt->setOperations->back()->nestedSelectStatement->fromTable->name, "students_2");
ASSERT_STREQ(stmt->fromTable->name, "students");
ASSERT_EQ(stmt->setOperations->back()->setType, kSetIntersect);
ASSERT_EQ(stmt->setOperations->back()->nestedSelectStatement->setOperations->back()->setType, kSetUnion);
ASSERT_FALSE(stmt->setOperations->back()->isAll);
}
TEST(OrderByFullStatementTest) {
TEST_PARSE_SINGLE_SQL(
"SELECT * FROM students INTERSECT SELECT grade FROM students_2 UNION SELECT * FROM employees ORDER BY grade ASC;",
kStmtSelect, SelectStatement, result, stmt);
ASSERT_EQ(stmt->setOperations->back()->resultOrder->at(0)->type, kOrderAsc);
ASSERT_STREQ(stmt->setOperations->back()->resultOrder->at(0)->expr->name, "grade");
ASSERT_FALSE(stmt->setOperations->back()->isAll);
}
TEST(SetOperationSubQueryOrder) {
TEST_PARSE_SINGLE_SQL(
"(SELECT * FROM students ORDER BY name DESC) INTERSECT SELECT grade FROM students_2 UNION SELECT * FROM "
"employees ORDER BY grade ASC;",
kStmtSelect, SelectStatement, result, stmt);
ASSERT_EQ(stmt->order->at(0)->type, kOrderDesc);
ASSERT_STREQ(stmt->order->at(0)->expr->name, "name");
ASSERT_EQ(stmt->setOperations->back()->resultOrder->at(0)->type, kOrderAsc);
ASSERT_STREQ(stmt->setOperations->back()->resultOrder->at(0)->expr->name, "grade");
ASSERT_FALSE(stmt->setOperations->back()->isAll);
}
TEST(SetOperationLastSubQueryOrder) {
TEST_PARSE_SINGLE_SQL(
"SELECT * FROM students INTERSECT SELECT grade FROM students_2 UNION (SELECT * FROM employees ORDER BY name "
"DESC) ORDER BY grade ASC;",
kStmtSelect, SelectStatement, result, stmt);
ASSERT_EQ(stmt->setOperations->back()
->nestedSelectStatement->setOperations->back()
->nestedSelectStatement->order->at(0)
->type,
kOrderDesc);
ASSERT_STREQ(stmt->setOperations->back()
->nestedSelectStatement->setOperations->back()
->nestedSelectStatement->order->at(0)
->expr->name,
"name");
ASSERT_EQ(stmt->setOperations->back()->resultOrder->at(0)->type, kOrderAsc);
ASSERT_STREQ(stmt->setOperations->back()->resultOrder->at(0)->expr->name, "grade");
ASSERT_FALSE(stmt->setOperations->back()->isAll);
}
TEST(NestedDifferentSetOperationsWithWithClause) {
TEST_PARSE_SINGLE_SQL(
"WITH UNION_FIRST AS (SELECT * FROM A UNION SELECT * FROM B) SELECT * FROM UNION_FIRST EXCEPT SELECT * FROM C",
kStmtSelect, SelectStatement, result, stmt);
ASSERT_STREQ(stmt->withDescriptions->back()->alias, "UNION_FIRST");
ASSERT_EQ(stmt->withDescriptions->back()->select->setOperations->back()->setType, kSetUnion);
ASSERT_STREQ(stmt->withDescriptions->back()->select->fromTable->name, "A");
ASSERT_STREQ(stmt->withDescriptions->back()->select->setOperations->back()->nestedSelectStatement->fromTable->name,
"B");
ASSERT_EQ(stmt->setOperations->back()->setType, kSetExcept);
ASSERT_STREQ(stmt->fromTable->name, "UNION_FIRST");
ASSERT_STREQ(stmt->setOperations->back()->nestedSelectStatement->fromTable->name, "C");
}
TEST(NestedAllSetOperationsWithWithClause) {
TEST_PARSE_SINGLE_SQL(
"WITH UNION_FIRST AS (SELECT * FROM A UNION SELECT * FROM B) SELECT * FROM UNION_FIRST EXCEPT SELECT * FROM "
"(SELECT * FROM C INTERSECT SELECT * FROM D)",
kStmtSelect, SelectStatement, result, stmt);
ASSERT_STREQ(stmt->withDescriptions->back()->alias, "UNION_FIRST");
ASSERT_EQ(stmt->withDescriptions->back()->select->setOperations->back()->setType, kSetUnion);
ASSERT_STREQ(stmt->withDescriptions->back()->select->fromTable->name, "A");
ASSERT_STREQ(stmt->withDescriptions->back()->select->setOperations->back()->nestedSelectStatement->fromTable->name,
"B");
ASSERT_EQ(stmt->setOperations->back()->setType, kSetExcept);
ASSERT_STREQ(stmt->fromTable->name, "UNION_FIRST");
ASSERT_EQ(stmt->setOperations->back()->nestedSelectStatement->fromTable->select->setOperations->back()->setType,
kSetIntersect);
ASSERT_STREQ(stmt->setOperations->back()->nestedSelectStatement->fromTable->select->fromTable->name, "C");
ASSERT_STREQ(stmt->setOperations->back()
->nestedSelectStatement->fromTable->select->setOperations->back()
->nestedSelectStatement->fromTable->name,
"D");
}
TEST(NestedSetOperationsWithMultipleWithClauses) {
TEST_PARSE_SINGLE_SQL(
"WITH UNION_FIRST AS (SELECT * FROM A UNION SELECT * FROM B),INTERSECT_SECOND AS (SELECT * FROM UNION_FIRST "
"INTERSECT SELECT * FROM C) SELECT * FROM UNION_FIRST EXCEPT SELECT * FROM INTERSECT_SECOND",
kStmtSelect, SelectStatement, result, stmt);
ASSERT_STREQ(stmt->withDescriptions->at(0)->alias, "UNION_FIRST");
ASSERT_STREQ(stmt->withDescriptions->back()->alias, "INTERSECT_SECOND");
ASSERT_EQ(stmt->withDescriptions->at(0)->select->setOperations->back()->setType, kSetUnion);
ASSERT_STREQ(stmt->withDescriptions->at(0)->select->fromTable->name, "A");
ASSERT_STREQ(stmt->withDescriptions->at(0)->select->setOperations->back()->nestedSelectStatement->fromTable->name,
"B");
ASSERT_EQ(stmt->withDescriptions->back()->select->setOperations->back()->setType, kSetIntersect);
ASSERT_STREQ(stmt->withDescriptions->back()->select->fromTable->name, "UNION_FIRST");
ASSERT_STREQ(stmt->withDescriptions->back()->select->setOperations->back()->nestedSelectStatement->fromTable->name,
"C");
ASSERT_EQ(stmt->setOperations->back()->setType, kSetExcept);
ASSERT_STREQ(stmt->fromTable->name, "UNION_FIRST");
ASSERT_STREQ(stmt->setOperations->back()->nestedSelectStatement->fromTable->name, "INTERSECT_SECOND");
}
TEST(WrongOrderByStatementTest) {
SQLParserResult res = parse_and_move("SELECT * FROM students ORDER BY name INTERSECT SELECT grade FROM students_2;");
ASSERT_FALSE(res.isValid());
}
TEST(BeginTransactionTest) {
{
TEST_PARSE_SINGLE_SQL("BEGIN TRANSACTION;", kStmtTransaction, TransactionStatement, transaction_result,
transaction_stmt);
ASSERT_EQ(transaction_stmt->command, kBeginTransaction);
}
{
TEST_PARSE_SINGLE_SQL("BEGIN;", kStmtTransaction, TransactionStatement, transaction_result, transaction_stmt);
ASSERT_EQ(transaction_stmt->command, kBeginTransaction);
}
}
TEST(RollbackTransactionTest) {
TEST_PARSE_SINGLE_SQL("ROLLBACK TRANSACTION;", kStmtTransaction, TransactionStatement, transaction_result,
transaction_stmt);
ASSERT_EQ(transaction_stmt->command, kRollbackTransaction);
}
TEST(CommitTransactionTest) {
TEST_PARSE_SINGLE_SQL("COMMIT TRANSACTION;", kStmtTransaction, TransactionStatement, transaction_result,
transaction_stmt);
ASSERT_EQ(transaction_stmt->command, kCommitTransaction);
}
TEST(CastAsType) {
TEST_PARSE_SINGLE_SQL("SELECT CAST(ID AS VARCHAR(8)) FROM TEST", kStmtSelect, SelectStatement, result, stmt);
ASSERT_TRUE(result.isValid());
ASSERT_EQ(stmt->selectList->size(), 1);
ASSERT_EQ(stmt->selectList->front()->columnType.data_type, DataType::VARCHAR);
ASSERT_EQ(stmt->selectList->front()->columnType.length, 8);
}
TEST_MAIN();

View File

@ -0,0 +1,95 @@
#!/bin/bash
# Has to be executed from the root of the repository.
# Usually invoked by `make test`.
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./
# Colors
RED='\033[1;31m'
GREEN='\033[1;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
BOLD='\033[1;39m'
RET=0
SQL_TEST_RET=0
MEM_LEAK_EXECUTED=$?
MEM_LEAK_RET=0
CONFLICT_RET=0
#################################################
# Running SQL parser tests.
printf "\n${GREEN}Running SQL parser tests...${NC}\n"
bin/tests -f "test/queries/queries-good.sql" -f "test/queries/queries-bad.sql"
SQL_TEST_RET=$?
if [ $SQL_TEST_RET -eq 0 ]; then
printf "${GREEN}SQL parser tests succeeded!${NC}\n"
else
RET=1
printf "${RED}SQL parser tests failed!${NC}\n"
fi
#################################################
# Running memory leak checks (only on Linux).
unamestr=$(uname)
if [[ "$unamestr" == 'Linux' ]]; then
printf "\n${GREEN}Running memory leak checks...${NC}\n"
valgrind --leak-check=full --error-exitcode=200 --log-fd=3 \
bin/tests -f "test/queries/queries-good.sql" -f "test/queries/queries-bad.sql" \
3>&1>/dev/null;
MEM_LEAK_EXECUTED=true
MEM_LEAK_RET=$?
RET=1
if [ $MEM_LEAK_RET -eq 0 ]; then
printf "${GREEN}Memory leak check succeeded!${NC}\n"
MEM_LEAK_RET=0
RET=0
elif [ $MEM_LEAK_RET -eq 200 ]; then
printf "${RED}Memory leak check failed!${NC}\n"
elif [ $MEM_LEAK_RET -eq 127 ]; then
printf "${RED}Memory leak check failed: command 'valgrind' not found!${NC}\n"
else
printf "${RED}Memory leak check failed: error code ${MEM_LEAK_RET}!${NC}\n"
fi
else
printf "\n${YELLOW}Skipping memory leak checks (can only be executed on Linux)!${NC}\n"
MEM_LEAK_EXECUTED=false
fi
#################################################
# Checking if the grammar is conflict free.
printf "\n${GREEN}Checking for conflicts in the grammar...${NC}\n"
printf "${RED}"
make -C src/parser/ test >>/dev/null
CONFLICT_RET=$?
if [ $CONFLICT_RET -eq 0 ]; then
printf "${GREEN}Conflict check succeeded!${NC}\n"
else
RET=1
printf "${RED}Conflict check failed!${NC}\n"
fi
# Print a summary of the test results.
printf "
----------------------------------
${BOLD}Summary:\n"
if [ $SQL_TEST_RET -eq 0 ]; then printf "SQL Tests: ${GREEN}Success${BOLD}\n";
else printf "SQL Tests: ${RED}Failure${BOLD}\n"; fi
if [ "$MEM_LEAK_EXECUTED" = true ]; then
if [ $MEM_LEAK_RET -eq 0 ]; then printf "Memory Leak Check: ${GREEN}Success${BOLD}\n";
else printf "Memory Leak Check: ${RED}Failure${BOLD}\n"; fi
else printf "Memory Leak Check: ${YELLOW}Skipped${BOLD}\n"
fi
if [ $CONFLICT_RET -eq 0 ]; then printf "Grammar Conflict Check: ${GREEN}Success${BOLD}\n";
else printf "Grammar Conflict Check: ${RED}Failure${BOLD}\n"; fi
if [ $RET -ne 0 ]; then printf "${RED}Some tests failed!${NC}\n"
elif [ "$MEM_LEAK_EXECUTED" = false ]; then printf "${YELLOW}Some tests were skipped!${NC}\n"
else printf "${GREEN}All tests passed!${NC}\n"
fi
printf "${NC}----------------------------------\n"
exit $RET

View File

@ -0,0 +1,199 @@
//
// microtest.h
//
// URL: https://github.com/torpedro/microtest.h
// Author: Pedro Flemming (http://torpedro.com/)
// License: MIT License (https://github.com/torpedro/microtest.h/blob/master/LICENSE)
// Copyright (c) 2017 Pedro Flemming
//
// This is a small header-only C++ unit testing framework.
// It allows to define small unit tests with set of assertions available.
//
#ifndef __MICROTEST_H__
#define __MICROTEST_H__
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
////////////////
// Assertions //
////////////////
#define ASSERT(cond) ASSERT_TRUE(cond);
#define ASSERT_TRUE(cond) \
if (!(cond)) throw mt::AssertFailedException(#cond, __FILE__, __LINE__);
#define ASSERT_FALSE(cond) \
if (cond) throw mt::AssertFailedException(#cond, __FILE__, __LINE__);
#define ASSERT_NULL(value) ASSERT_TRUE(value == NULL);
#define ASSERT_NOTNULL(value) ASSERT_TRUE(value != NULL);
#define ASSERT_STREQ(a, b) \
if (std::string(a).compare(std::string(b)) != 0) { \
printf("%s{ info} %s", mt::yellow(), mt::def()); \
std::cout << "Actual values: " << a << " != " << b << std::endl; \
throw mt::AssertFailedException(#a " == " #b, __FILE__, __LINE__); \
}
#define ASSERT_STRNEQ(a, b) \
if (std::string(a).compare(std::string(b)) != = 0) { \
printf("%s{ info} %s", mt::yellow(), mt::def()); \
std::cout << "Actual values: " << a << " == " << b << std::endl; \
throw mt::AssertFailedException(#a " != " #b, __FILE__, __LINE__); \
}
#define ASSERT_EQ(a, b) \
if (a != b) { \
printf("%s{ info} %s", mt::yellow(), mt::def()); \
std::cout << "Actual values: " << a << " != " << b << std::endl; \
} \
ASSERT(a == b);
#define ASSERT_NEQ(a, b) \
if (a == b) { \
printf("%s{ info} %s", mt::yellow(), mt::def()); \
std::cout << "Actual values: " << a << " == " << b << std::endl; \
} \
ASSERT(a != b);
////////////////
// Unit Tests //
////////////////
#define TEST(name) \
void name(); \
namespace { \
bool __##name = mt::TestsManager::AddTest(name, #name); \
} \
void name()
///////////////
// Framework //
///////////////
namespace mt {
inline const char* red() { return "\033[1;31m"; }
inline const char* green() { return "\033[0;32m"; }
inline const char* yellow() { return "\033[0;33m"; }
inline const char* def() { return "\033[0m"; }
inline void printRunning(const char* message, FILE* file = stdout) {
fprintf(file, "%s{ running}%s %s\n", green(), def(), message);
}
inline void printOk(const char* message, FILE* file = stdout) {
fprintf(file, "%s{ ok}%s %s\n", green(), def(), message);
}
inline void printFailed(const char* message, FILE* file = stdout) {
fprintf(file, "%s{ failed} %s%s\n", red(), message, def());
}
// Exception that is thrown when an assertion fails.
class AssertFailedException : public std::exception {
public:
AssertFailedException(std::string description, std::string filepath, int line)
: std::exception(), description_(description), filepath_(filepath), line_(line){};
virtual const char* what() const throw() { return description_.c_str(); }
inline const char* getFilepath() { return filepath_.c_str(); }
inline int getLine() { return line_; }
protected:
std::string description_;
std::string filepath_;
int line_;
};
class TestsManager {
// Note: static initialization fiasco
// http://www.parashift.com/c++-faq-lite/static-init-order.html
// http://www.parashift.com/c++-faq-lite/static-init-order-on-first-use.html
public:
struct Test {
const char* name;
void (*fn)(void);
};
static std::vector<Test>& tests() {
static std::vector<Test> tests_;
return tests_;
}
// Adds a new test to the current set of tests.
// Returns false if a test with the same name already exists.
inline static bool AddTest(void (*fn)(void), const char* name) {
tests().push_back({name, fn});
return true;
}
// Run all tests that are registered.
// Returns the number of tests that failed.
inline static size_t RunAllTests(FILE* file = stdout) {
size_t num_failed = 0;
for (const Test& test : tests()) {
// Run the test.
// If an AsserFailedException is thrown, the test has failed.
try {
printRunning(test.name, file);
(*test.fn)();
printOk(test.name, file);
} catch (AssertFailedException& e) {
printFailed(test.name, file);
fprintf(file, " %sAssertion failed: %s%s\n", red(), e.what(), def());
fprintf(file, " %s%s:%d%s\n", red(), e.getFilepath(), e.getLine(), def());
++num_failed;
}
}
int return_code = (num_failed > 0) ? 1 : 0;
return return_code;
}
};
// Class that will capture the arguments passed to the program.
class Runtime {
public:
static const std::vector<std::string>& args(int argc = -1, char** argv = NULL) {
static std::vector<std::string> args_;
if (argc >= 0) {
for (int i = 0; i < argc; ++i) {
args_.push_back(argv[i]);
}
}
return args_;
}
};
} // namespace mt
#define TEST_MAIN() \
int main(int argc, char* argv[]) { \
mt::Runtime::args(argc, argv); \
\
size_t num_failed = mt::TestsManager::RunAllTests(stdout); \
if (num_failed == 0) { \
fprintf(stdout, "%s{ summary} All tests succeeded!%s\n", mt::green(), mt::def()); \
return 0; \
} else { \
double percentage = 100.0 * num_failed / mt::TestsManager::tests().size(); \
fprintf(stderr, "%s{ summary} %lu tests failed (%.2f%%)%s\n", mt::red(), num_failed, percentage, mt::def()); \
return -1; \
} \
}
#endif // __MICROTEST_H__

View File

@ -0,0 +1,111 @@
#include "thirdparty/microtest/microtest.h"
#include "SQLParser.h"
#include "util/sqlhelper.h"
#include "sql_asserts.h"
#include <fstream>
#include <iostream>
#include <streambuf>
#include <string>
using namespace hsql;
std::string readFileContents(std::string file_path) {
std::ifstream t(file_path.c_str());
std::string text((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
return text;
}
TEST(TPCHQueryGrammarTests) {
std::vector<std::string> files = {
"test/queries/tpc-h-01.sql", "test/queries/tpc-h-02.sql", "test/queries/tpc-h-03.sql",
"test/queries/tpc-h-04.sql", "test/queries/tpc-h-05.sql", "test/queries/tpc-h-06.sql",
"test/queries/tpc-h-07.sql", "test/queries/tpc-h-08.sql", "test/queries/tpc-h-09.sql",
"test/queries/tpc-h-10.sql", "test/queries/tpc-h-11.sql", "test/queries/tpc-h-12.sql",
"test/queries/tpc-h-13.sql", "test/queries/tpc-h-14.sql", "test/queries/tpc-h-15.sql",
"test/queries/tpc-h-16.sql", "test/queries/tpc-h-17.sql", "test/queries/tpc-h-18.sql",
"test/queries/tpc-h-19.sql", "test/queries/tpc-h-20.sql", "test/queries/tpc-h-21.sql",
"test/queries/tpc-h-22.sql",
};
int testsFailed = 0;
std::string concatenated = "";
for (const std::string& file_path : files) {
std::string query = readFileContents(file_path);
concatenated += query;
if (concatenated.back() != ';') concatenated += ';';
SQLParserResult result;
SQLParser::parse(query.c_str(), &result);
if (!result.isValid()) {
mt::printFailed(file_path.c_str());
printf("%s %s (L%d:%d)%s\n", mt::red(), result.errorMsg(), result.errorLine(), result.errorColumn(),
mt::def());
++testsFailed;
} else {
mt::printOk(file_path.c_str());
}
}
SQLParserResult result;
SQLParser::parse(concatenated.c_str(), &result);
if (!result.isValid()) {
mt::printFailed("TPCHAllConcatenated");
printf("%s %s (L%d:%d)%s\n", mt::red(), result.errorMsg(), result.errorLine(), result.errorColumn(),
mt::def());
++testsFailed;
} else {
mt::printOk("TPCHAllConcatenated");
}
ASSERT_EQ(testsFailed, 0);
}
TEST(TPCHQueryDetailTest) {
std::string query = readFileContents("test/queries/tpc-h-20.sql");
SQLParserResult result;
SQLParser::parse(query.c_str(), &result);
ASSERT(result.isValid());
ASSERT_EQ(result.size(), 1);
const SQLStatement* stmt20 = result.getStatement(0);
ASSERT_EQ(stmt20->type(), kStmtSelect);
const SelectStatement* select20 = (const SelectStatement*)stmt20;
ASSERT_EQ(select20->selectList->size(), 2);
ASSERT_STREQ(select20->selectList->at(0)->getName(), "S_NAME");
ASSERT_STREQ(select20->selectList->at(1)->getName(), "S_ADDRESS");
// Test WHERE Clause.
Expr* where = select20->whereClause;
ASSERT_NOTNULL(where);
ASSERT(where->isType(kExprOperator));
ASSERT_EQ(where->opType, kOpAnd);
Expr* andExpr2 = where->expr;
ASSERT_NOTNULL(andExpr2);
ASSERT(andExpr2->isType(kExprOperator));
ASSERT_EQ(andExpr2->opType, kOpAnd);
// Test IN expression.
Expr* inExpr = andExpr2->expr;
ASSERT_NOTNULL(inExpr);
ASSERT(inExpr->isType(kExprOperator));
ASSERT_EQ(inExpr->opType, kOpIn);
ASSERT_STREQ(inExpr->expr->getName(), "S_SUPPKEY");
ASSERT_NOTNULL(inExpr->select);
ASSERT_EQ(inExpr->select->selectList->size(), 1);
ASSERT(inExpr->select->selectList->at(0)->isType(kExprColumnRef));
ASSERT_STREQ(inExpr->select->selectList->at(0)->getName(), "PS_SUPPKEY");
// Test ORDER BY clause.
ASSERT_NOTNULL(select20->order);
ASSERT_EQ(select20->order->size(), 1);
ASSERT(select20->order->at(0)->expr->isType(kExprColumnRef));
ASSERT_STREQ(select20->order->at(0)->expr->getName(), "S_NAME");
}

View File

@ -11,6 +11,9 @@
#include "DataTestSuite.h"
#include "DataTest.h"
#include "SessionPoolTest.h"
#ifdef POCO_DATA_ENABLE_SQL_PARSER
#include "SQLParserTest.h"
#endif // POCO_DATA_ENABLE_SQL_PARSER
CppUnit::Test* DataTestSuite::suite()
@ -19,6 +22,9 @@ CppUnit::Test* DataTestSuite::suite()
pSuite->addTest(DataTest::suite());
pSuite->addTest(SessionPoolTest::suite());
#ifdef POCO_DATA_ENABLE_SQL_PARSER
pSuite->addTest(SQLParserTest::suite());
#endif // POCO_DATA_ENABLE_SQL_PARSER
return pSuite;
}

View File

@ -0,0 +1,86 @@
//
// SQLParserTest.cpp
//
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "SQLParserTest.h"
#include "CppUnit/TestCaller.h"
#include "CppUnit/TestSuite.h"
#ifdef POCO_DATA_ENABLE_SQL_PARSER
#include "Poco/Data/SQLParser.h"
using namespace Poco::Data;
SQLParserTest::SQLParserTest(const std::string& name): CppUnit::TestCase(name)
{
}
SQLParserTest::~SQLParserTest()
{
}
void SQLParserTest::testSQLParser()
{
std::string query = "INSERT INTO Test VALUES ('1', 2, 3.5);"
"SELECT * FROM Test WHERE First = ?;"
"UPDATE Test SET value=1 WHERE First = '1';"
"DELETE FROM Test WHERE First = ?;";
SQLParserResult result;
SQLParser::parse(query, &result);
int ins = 0, sel = 0, upd = 0, del = 0;
assertTrue(result.isValid());
assertEqual(4, result.size());
for (auto i = 0u; i < result.size(); ++i)
{
const SQLStatement* stmt = result.getStatement(i);
//printStatementInfo(stmt);
switch (stmt->type())
{
case kStmtSelect: ++sel; break;
case kStmtInsert: ++ins; break;
case kStmtUpdate: ++upd; break;
case kStmtDelete: ++del; break;
default: break;
}
}
assertEqual(1, ins);
assertEqual(1, sel);
assertEqual(1, upd);
assertEqual(1, del);
}
void SQLParserTest::setUp()
{
}
void SQLParserTest::tearDown()
{
}
CppUnit::Test* SQLParserTest::suite()
{
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("SQLParserTest");
CppUnit_addTest(pSuite, SQLParserTest, testSQLParser);
return pSuite;
}
#endif // POCO_DATA_ENABLE_SQL_PARSER

View File

@ -0,0 +1,44 @@
//
// SQLParserTest.h
//
// Definition of the SQLParserTest class.
//
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef SQLParserTest_INCLUDED
#define SQLParserTest_INCLUDED
#include "Poco/Config.h"
#ifdef POCO_DATA_ENABLE_SQL_PARSER
#include "Poco/Data/Data.h"
#include "CppUnit/TestCase.h"
class SQLParserTest: public CppUnit::TestCase
{
public:
SQLParserTest(const std::string& name);
~SQLParserTest();
void testSQLParser();
void setUp();
void tearDown();
static CppUnit::Test* suite();
private:
};
#endif // POCO_DATA_ENABLE_SQL_PARSER
#endif // SQLParserTest_INCLUDED