Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow more expressions for selected statements #242

Merged
merged 6 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,19 @@ jobs:
os: ubuntu-latest
container: ubuntu:18.04

- name: gcc-12
cc: gcc-12
cxx: g++-12
- name: gcc-13
cc: gcc-13
cxx: g++-13
os: ubuntu-latest
container: ubuntu:22.04
container: ubuntu:24.04
# We need relaxed builds for debug mode with current GCC versions, see #218.
build_options: "relaxed_build=on"

- name: clang-15
cc: clang-15
cxx: clang++-15
- name: clang-18
cc: clang-18
cxx: clang++-18
os: ubuntu-latest
container: ubuntu:22.04
container: ubuntu:24.04

- name: clang-macOS
cc: clang
Expand Down
2,520 changes: 1,286 additions & 1,234 deletions src/parser/bison_parser.cpp

Large diffs are not rendered by default.

394 changes: 198 additions & 196 deletions src/parser/bison_parser.h

Large diffs are not rendered by default.

49 changes: 32 additions & 17 deletions src/parser/bison_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@
%type <expr> function_expr between_expr expr_alias param_expr
%type <expr> column_name literal int_literal num_literal string_literal bool_literal date_literal interval_literal
%type <expr> comp_expr opt_where join_condition opt_having case_expr case_list in_expr hint
%type <expr> array_expr array_index null_literal
%type <expr> array_expr array_index null_literal extended_literal casted_extended_literal
%type <limit> opt_limit opt_top
%type <order> order_desc
%type <order_type> opt_order_type
Expand All @@ -284,7 +284,7 @@
%type <import_type_t> opt_file_type file_type

%type <str_vec> ident_commalist opt_column_list
%type <expr_vec> expr_list select_list opt_literal_list literal_list hint_list opt_hints opt_partition
%type <expr_vec> expr_list select_list opt_extended_literal_list extended_literal_list hint_list opt_hints opt_partition
%type <table_vec> table_ref_commalist
%type <order_vec> opt_order order_list
%type <with_description_vec> opt_with_clause with_clause with_description_list
Expand Down Expand Up @@ -397,7 +397,7 @@ hint : IDENTIFIER {
$$ = Expr::make(kExprHint);
$$->name = $1;
}
| IDENTIFIER '(' literal_list ')' {
| IDENTIFIER '(' extended_literal_list ')' {
$$ = Expr::make(kExprHint);
$$->name = $1;
$$->exprList = $3;
Expand All @@ -423,13 +423,13 @@ prepare_statement : PREPARE IDENTIFIER FROM prepare_target_query {
$$->query = $4;
};

prepare_target_query : STRING
prepare_target_query : STRING;

execute_statement : EXECUTE IDENTIFIER {
execute_statement : EXECUTE IDENTIFIER {
$$ = new ExecuteStatement();
$$->name = $2;
}
| EXECUTE IDENTIFIER '(' opt_literal_list ')' {
| EXECUTE IDENTIFIER '(' opt_extended_literal_list ')' {
$$ = new ExecuteStatement();
$$->name = $2;
$$->parameters = $4;
Expand Down Expand Up @@ -708,7 +708,7 @@ truncate_statement : TRUNCATE table_name {
* INSERT INTO students VALUES ('Max', 1112233, 'Musterhausen', 2.3)
* INSERT INTO employees SELECT * FROM stundents
******************************/
insert_statement : INSERT INTO table_name opt_column_list VALUES '(' literal_list ')' {
insert_statement : INSERT INTO table_name opt_column_list VALUES '(' extended_literal_list ')' {
$$ = new InsertStatement(kInsertValues);
$$->schema = $3.schema;
$$->tableName = $3.name;
Expand Down Expand Up @@ -914,18 +914,34 @@ expr_list : expr_alias {
$$ = $1;
};

opt_literal_list : literal_list { $$ = $1; }
// Literals, casted literals, and negative numbers/intervals are allowed for INSERT and EXECUTE statements or hints.
opt_extended_literal_list : extended_literal_list { $$ = $1; }
| /* empty */ { $$ = nullptr; };

literal_list : literal {
extended_literal_list : casted_extended_literal {
$$ = new std::vector<Expr*>();
$$->push_back($1);
}
| literal_list ',' literal {
| extended_literal_list ',' casted_extended_literal {
$1->push_back($3);
$$ = $1;
};

casted_extended_literal : extended_literal | CAST '(' extended_literal AS column_type ')' {
$$ = Expr::makeCast($3, $5);
};

extended_literal : literal {
if ($1->type == ExprType::kExprParameter) {
delete $1;
yyerror(&yyloc, result, scanner, "Parameter ? is not a valid literal.");
YYERROR;
}
$$ = $1;
}
| '-' num_literal { $$ = Expr::makeOpUnary(kOpUnaryMinus, $2); };
| '-' interval_literal { $$ = Expr::makeOpUnary(kOpUnaryMinus, $2); };

expr_alias : expr opt_alias {
$$ = $1;
if ($2) {
Expand Down Expand Up @@ -1078,10 +1094,7 @@ date_literal : DATE STRING {
$$ = Expr::makeDateLiteral($2);
};

interval_literal : int_literal duration_field {
$$ = Expr::makeIntervalLiteral($1->ival, $2);
delete $1;
}
interval_literal : INTVAL duration_field { $$ = Expr::makeIntervalLiteral($1, $2); }
| INTERVAL STRING datetime_field {
int duration{0}, chars_parsed{0};
// If the whole string is parsed, chars_parsed points to the terminating null byte after the last character
Expand Down Expand Up @@ -1314,9 +1327,11 @@ ident_commalist : IDENTIFIER {

// clang-format off
%%
// clang-format on
/*********************************

/*********************************
** Section 4: Additional C code
*********************************/

/* empty */
/* empty */

// clang-format on
2 changes: 1 addition & 1 deletion src/sql/Table.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ struct JoinDefinition {
TableRef* left;
TableRef* right;
Expr* condition;
std::vector<char*>* namedColumns;

JoinType type;
std::vector<char*>* namedColumns;
};

} // namespace hsql
Expand Down
10 changes: 9 additions & 1 deletion src/sql/statements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -368,12 +368,20 @@ const char* TableRef::getName() const {
}

// JoinDefinition
JoinDefinition::JoinDefinition() : left(nullptr), right(nullptr), condition(nullptr), type(kJoinInner) {}
JoinDefinition::JoinDefinition()
: left(nullptr), right(nullptr), condition(nullptr), namedColumns(nullptr), type(kJoinInner) {}

JoinDefinition::~JoinDefinition() {
delete left;
delete right;
delete condition;

if (namedColumns) {
for (auto* column : *namedColumns) {
free(column);
}
delete namedColumns;
}
}

SetOperation::SetOperation() : nestedSelectStatement(nullptr), resultOrder(nullptr), resultLimit(nullptr) {}
Expand Down
10 changes: 10 additions & 0 deletions test/queries/queries-bad.sql
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,13 @@
!SELECT * FROM foo INNER JOIN bar USING (a b);
!SELECT * FROM foo INNER JOIN bar USING (a AS b);
!SELECT * FROM foo INNER JOIN bar USING (1);
# INSERT, EXECUTE, and HINTS only allow specific expressions.
!INSERT INTO foo VALUES (?);
!INSERT INTO foo VALUES (CAST(column_a AS INT));
!INSERT INTO foo VALUES (AVG(another_column));
!EXECUTE statement_a(?);
!EXECUTE statement_a(CAST(column_a AS INT));
!EXECUTE statement_a(AVG(another_column));
!SELECT * FROM foo WITH HINT (?);
!SELECT * FROM foo WITH HINT (CAST(column_a AS INT));
!SELECT * FROM foo WITH HINT (AVG(another_column));
5 changes: 3 additions & 2 deletions test/queries/queries-good.sql
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ 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_another_inst(1, 2, 3, CAST(1 AS LONG), -2.5, INTERVAL '3 HOURS', -2 SECONDS, TRUE, NULL, DATE '2000-01-01');
EXECUTE prep;
DEALLOCATE PREPARE prep;
# COPY
Expand All @@ -80,7 +80,8 @@ COPY (SELECT firstname, COUNT(*) FROM students GROUP BY firstname) TO 'student_n
# 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'));
SELECT * FROM test WITH HINT(NO_CACHE, SAMPLE_RATE(0.1), OMW(1.0, 'test'), START_DATE(CAST('2000-01-01' AS DATE)));
SELECT * FROM test WITH HINT(TIME_DIFFERENCE(-3 HOURS), ALLOW_RESULT_CACHE(FALSE), DEFAULT_VALUE(NULL), TIMETRAVEL_TO(DATE '2000-01-01'));
SHOW TABLES;
SHOW COLUMNS students;
DESCRIBE students;
Expand Down
4 changes: 2 additions & 2 deletions test/select_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,7 @@ TEST(WithClauseSingle) {
"WITH "
"a AS (SELECT name FROM peopleA)"
"SELECT name FROM a;",
kStmtSelect, SelectStatement, result, stmt)
kStmtSelect, SelectStatement, result, stmt);

// with_description_list – count
ASSERT_EQ(stmt->withDescriptions->size(), 1);
Expand All @@ -828,7 +828,7 @@ TEST(WithClauseDouble) {
"a AS (SELECT nameA FROM peopleA), "
"b AS (SELECT nameB, cityB FROM peopleB) "
"SELECT nameA FROM a;",
kStmtSelect, SelectStatement, result, stmt)
kStmtSelect, SelectStatement, result, stmt);

// with_description_list – count
ASSERT_EQ(stmt->withDescriptions->size(), 2);
Expand Down
6 changes: 3 additions & 3 deletions test/sql_asserts.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
hsql::SQLParserResult result; \
hsql::SQLParser::parse(query, &result); \
ASSERT(result.isValid()); \
ASSERT_EQ(result.size(), numStatements);
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);
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);
const stmtClass* outputVar = (const stmtClass*)result.getStatement(stmt_index)

#endif
44 changes: 39 additions & 5 deletions test/sql_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,45 @@ TEST(UpdateStatementTest) {
}

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_PARSE_SINGLE_SQL(
"INSERT INTO students VALUES ('Max Mustermann', 12345, 'Musterhausen', 2.0, -1, 1 month, "
" CAST('2000-02-02' AS DATE), - INTERVAL '3 seconds', DATE '2000-02-02', FALSE, NULL)",
kStmtInsert, InsertStatement, result, stmt);

ASSERT_EQ(stmt->values->size(), 11);
ASSERT_EQ(stmt->values->at(0)->type, kExprLiteralString);
ASSERT_STREQ(stmt->values->at(0)->name, "Max Mustermann");
ASSERT_EQ(stmt->values->at(1)->type, kExprLiteralInt);
ASSERT_EQ(stmt->values->at(1)->ival, 12345);
ASSERT_EQ(stmt->values->at(2)->type, kExprLiteralString);
ASSERT_STREQ(stmt->values->at(2)->name, "Musterhausen");
ASSERT_EQ(stmt->values->at(3)->type, kExprLiteralFloat);
ASSERT_EQ(stmt->values->at(3)->fval, 2.0);
ASSERT_EQ(stmt->values->at(4)->type, kExprOperator);
ASSERT_EQ(stmt->values->at(4)->opType, kOpUnaryMinus);
ASSERT(stmt->values->at(4)->expr);
ASSERT_EQ(stmt->values->at(4)->expr->type, kExprLiteralInt);
ASSERT_EQ(stmt->values->at(4)->expr->ival, 1);
ASSERT_EQ(stmt->values->at(5)->type, kExprLiteralInterval);
ASSERT_EQ(stmt->values->at(5)->ival, 1);
ASSERT_EQ(stmt->values->at(5)->datetimeField, kDatetimeMonth);
ASSERT_EQ(stmt->values->at(6)->type, kExprCast);
ASSERT_EQ(stmt->values->at(6)->columnType, ColumnType{DataType::DATE});
ASSERT(stmt->values->at(6)->expr);
ASSERT_EQ(stmt->values->at(6)->expr->type, kExprLiteralString);
ASSERT_STREQ(stmt->values->at(6)->expr->name, "2000-02-02");
ASSERT_EQ(stmt->values->at(7)->type, kExprOperator);
ASSERT_EQ(stmt->values->at(7)->opType, kOpUnaryMinus);
ASSERT(stmt->values->at(7)->expr);
ASSERT_EQ(stmt->values->at(7)->expr->type, kExprLiteralInterval);
ASSERT_EQ(stmt->values->at(7)->expr->ival, 3);
ASSERT_EQ(stmt->values->at(7)->expr->datetimeField, kDatetimeSecond);
ASSERT_EQ(stmt->values->at(8)->type, kExprLiteralDate);
ASSERT_STREQ(stmt->values->at(8)->name, "2000-02-02");
ASSERT_EQ(stmt->values->at(9)->type, kExprLiteralInt);
ASSERT_EQ(stmt->values->at(9)->ival, 0);
ASSERT_TRUE(stmt->values->at(9)->isBoolLiteral);
ASSERT_EQ(stmt->values->at(10)->type, kExprLiteralNull);
}

TEST(AlterStatementDropActionTest) {
Expand Down
35 changes: 21 additions & 14 deletions test/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ NC='\033[0m'
BOLD='\033[1;39m'

RET=0
SQL_TEST_RET=0
MEM_LEAK_EXECUTED=$?
MEM_LEAK_RET=0
CONFLICT_RET=0
SQL_TEST_RET=1
MEM_LEAK_EXECUTED=false
MEM_LEAK_RET=1
CONFLICT_RET=1

#################################################
# Running SQL parser tests.
Expand All @@ -25,7 +25,6 @@ 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

Expand All @@ -38,14 +37,11 @@ if [[ "$unamestr" == 'Linux' ]]; then
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
MEM_LEAK_EXECUTED=true

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
Expand All @@ -55,7 +51,6 @@ if [[ "$unamestr" == 'Linux' ]]; then
fi
else
printf "\n${YELLOW}Skipping memory leak checks (can only be executed on Linux)!${NC}\n"
MEM_LEAK_EXECUTED=false
fi

#################################################
Expand All @@ -68,7 +63,6 @@ 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

Expand All @@ -86,9 +80,22 @@ 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"
if [[ $SQL_TEST_RET -ne 0 || $CONFLICT_RET -ne 0 ]]; then
RET=1
fi

if [ $MEM_LEAK_RET -ne 0 ]; then
if [ "$MEM_LEAK_EXECUTED" = true ]; then
RET=1
fi
fi


if [ $RET -eq 0 ]; then
if [ "$MEM_LEAK_EXECUTED" = true ]; then printf "${GREEN}All tests passed!${NC}\n"
else printf "${YELLOW}Some tests were skipped!${NC}\n"
fi
else printf "${RED}Some tests failed!${NC}\n"
fi
printf "${NC}----------------------------------\n"

Expand Down
Loading