2014-12-03 16:32:56 +01:00
|
|
|
|
2014-12-18 12:11:26 +01:00
|
|
|
|
2017-02-10 21:41:34 +01:00
|
|
|
#include "thirdparty/microtest/microtest.h"
|
|
|
|
#include "sql_asserts.h"
|
2014-12-03 16:32:56 +01:00
|
|
|
#include "SQLParser.h"
|
|
|
|
|
|
|
|
using namespace hsql;
|
|
|
|
|
2014-12-18 12:11:26 +01:00
|
|
|
TEST(SelectTest) {
|
2017-02-08 03:54:38 +01:00
|
|
|
TEST_PARSE_SINGLE_SQL(
|
|
|
|
"SELECT * FROM students;",
|
|
|
|
kStmtSelect,
|
|
|
|
SelectStatement,
|
|
|
|
result,
|
|
|
|
stmt);
|
2014-12-18 12:11:26 +01:00
|
|
|
|
2017-02-08 02:06:15 +01:00
|
|
|
ASSERT_NULL(stmt->whereClause);
|
|
|
|
ASSERT_NULL(stmt->groupBy);
|
2014-12-18 12:11:26 +01:00
|
|
|
}
|
|
|
|
|
2017-03-07 02:49:29 +01:00
|
|
|
TEST(SelectExprTest) {
|
|
|
|
TEST_PARSE_SINGLE_SQL(
|
|
|
|
"SELECT a, MAX(b), CUSTOM(c, F(un)) FROM students;",
|
|
|
|
kStmtSelect,
|
|
|
|
SelectStatement,
|
|
|
|
result,
|
|
|
|
stmt);
|
|
|
|
|
|
|
|
ASSERT_NULL(stmt->whereClause);
|
|
|
|
ASSERT_NULL(stmt->groupBy);
|
|
|
|
|
|
|
|
ASSERT_EQ(stmt->selectList->size(), 3);
|
2017-03-07 13:49:56 +01:00
|
|
|
|
2017-03-07 02:49:29 +01:00
|
|
|
ASSERT(stmt->selectList->at(0)->isType(kExprColumnRef));
|
|
|
|
ASSERT_STREQ(stmt->selectList->at(0)->getName(), "a");
|
|
|
|
|
|
|
|
ASSERT(stmt->selectList->at(1)->isType(kExprFunctionRef));
|
|
|
|
ASSERT_STREQ(stmt->selectList->at(1)->getName(), "MAX");
|
|
|
|
ASSERT_NOTNULL(stmt->selectList->at(1)->exprList);
|
|
|
|
ASSERT_EQ(stmt->selectList->at(1)->exprList->size(), 1);
|
|
|
|
ASSERT(stmt->selectList->at(1)->exprList->at(0)->isType(kExprColumnRef));
|
|
|
|
ASSERT_STREQ(stmt->selectList->at(1)->exprList->at(0)->getName(), "b");
|
|
|
|
|
|
|
|
ASSERT(stmt->selectList->at(2)->isType(kExprFunctionRef));
|
|
|
|
ASSERT_STREQ(stmt->selectList->at(2)->getName(), "CUSTOM");
|
|
|
|
ASSERT_NOTNULL(stmt->selectList->at(2)->exprList);
|
|
|
|
ASSERT_EQ(stmt->selectList->at(2)->exprList->size(), 2);
|
|
|
|
ASSERT(stmt->selectList->at(2)->exprList->at(0)->isType(kExprColumnRef));
|
|
|
|
ASSERT_STREQ(stmt->selectList->at(2)->exprList->at(0)->getName(), "c");
|
|
|
|
|
|
|
|
ASSERT(stmt->selectList->at(2)->exprList->at(1)->isType(kExprFunctionRef));
|
|
|
|
ASSERT_STREQ(stmt->selectList->at(2)->exprList->at(1)->getName(), "F");
|
|
|
|
ASSERT_EQ(stmt->selectList->at(2)->exprList->at(1)->exprList->size(), 1);
|
|
|
|
ASSERT(stmt->selectList->at(2)->exprList->at(1)->exprList->at(0)->isType(kExprColumnRef));
|
|
|
|
ASSERT_STREQ(stmt->selectList->at(2)->exprList->at(1)->exprList->at(0)->getName(), "un");
|
|
|
|
}
|
|
|
|
|
2014-12-18 12:11:26 +01:00
|
|
|
|
|
|
|
TEST(SelectHavingTest) {
|
2017-02-08 03:54:38 +01:00
|
|
|
TEST_PARSE_SINGLE_SQL(
|
2017-04-25 17:25:00 +02:00
|
|
|
"SELECT city, AVG(grade) AS avg_grade FROM students GROUP BY city HAVING AVG(grade) < -2.0",
|
2017-02-08 03:54:38 +01:00
|
|
|
kStmtSelect,
|
|
|
|
SelectStatement,
|
|
|
|
result,
|
|
|
|
stmt);
|
|
|
|
|
2017-02-08 02:06:15 +01:00
|
|
|
ASSERT_FALSE(stmt->selectDistinct);
|
|
|
|
|
|
|
|
GroupByDescription* group = stmt->groupBy;
|
|
|
|
ASSERT_NOTNULL(group);
|
|
|
|
ASSERT_EQ(group->columns->size(), 1);
|
2017-06-29 13:40:24 +02:00
|
|
|
ASSERT_EQ(group->having->opType, kOpLess);
|
2017-02-08 02:06:15 +01:00
|
|
|
ASSERT(group->having->expr->isType(kExprFunctionRef));
|
|
|
|
ASSERT(group->having->expr2->isType(kExprLiteralFloat));
|
2017-04-25 17:25:00 +02:00
|
|
|
ASSERT_EQ(group->having->expr2->fval, -2.0);
|
2014-12-18 12:11:26 +01:00
|
|
|
}
|
|
|
|
|
2014-12-03 16:32:56 +01:00
|
|
|
|
2014-12-18 12:11:26 +01:00
|
|
|
TEST(SelectDistinctTest) {
|
2017-02-08 03:54:38 +01:00
|
|
|
TEST_PARSE_SINGLE_SQL(
|
|
|
|
"SELECT DISTINCT grade, city FROM students;",
|
|
|
|
kStmtSelect,
|
2017-03-07 02:01:00 +01:00
|
|
|
SelectStatement,
|
2017-02-08 03:54:38 +01:00
|
|
|
result,
|
|
|
|
stmt);
|
2014-12-18 12:28:24 +01:00
|
|
|
|
2017-02-08 02:06:15 +01:00
|
|
|
ASSERT(stmt->selectDistinct);
|
|
|
|
ASSERT_NULL(stmt->whereClause);
|
2014-12-03 16:32:56 +01:00
|
|
|
}
|
2014-12-18 12:11:26 +01:00
|
|
|
|
|
|
|
TEST(SelectGroupDistinctTest) {
|
2017-02-08 03:54:38 +01:00
|
|
|
TEST_PARSE_SINGLE_SQL(
|
|
|
|
"SELECT city, COUNT(name), COUNT(DISTINCT grade) FROM students GROUP BY city;",
|
|
|
|
kStmtSelect,
|
|
|
|
SelectStatement,
|
|
|
|
result,
|
|
|
|
stmt);
|
2014-12-18 12:28:24 +01:00
|
|
|
|
2017-02-08 02:06:15 +01:00
|
|
|
ASSERT_FALSE(stmt->selectDistinct);
|
|
|
|
ASSERT_EQ(stmt->selectList->size(), 3);
|
|
|
|
ASSERT(!stmt->selectList->at(1)->distinct);
|
|
|
|
ASSERT(stmt->selectList->at(2)->distinct);
|
2014-12-18 12:11:26 +01:00
|
|
|
}
|
|
|
|
|
2017-03-07 02:30:44 +01:00
|
|
|
TEST(OrderByTest) {
|
|
|
|
TEST_PARSE_SINGLE_SQL(
|
|
|
|
"SELECT grade, city FROM students ORDER BY grade, city DESC;",
|
|
|
|
kStmtSelect,
|
|
|
|
SelectStatement,
|
|
|
|
result,
|
|
|
|
stmt);
|
|
|
|
|
|
|
|
ASSERT_NULL(stmt->whereClause);
|
|
|
|
ASSERT_NOTNULL(stmt->order);
|
|
|
|
|
|
|
|
ASSERT_EQ(stmt->order->size(), 2);
|
|
|
|
ASSERT_EQ(stmt->order->at(0)->type, kOrderAsc);
|
|
|
|
ASSERT_STREQ(stmt->order->at(0)->expr->name, "grade");
|
|
|
|
|
|
|
|
ASSERT_EQ(stmt->order->at(1)->type, kOrderDesc);
|
|
|
|
ASSERT_STREQ(stmt->order->at(1)->expr->name, "city");
|
|
|
|
}
|
2017-03-07 13:49:56 +01:00
|
|
|
|
|
|
|
TEST(SelectBetweenTest) {
|
|
|
|
TEST_PARSE_SINGLE_SQL(
|
2017-04-25 17:25:00 +02:00
|
|
|
"SELECT grade, city FROM students WHERE grade BETWEEN -1 and c;",
|
2017-03-07 13:49:56 +01:00
|
|
|
kStmtSelect,
|
|
|
|
SelectStatement,
|
|
|
|
result,
|
|
|
|
stmt);
|
|
|
|
|
|
|
|
|
|
|
|
Expr* where = stmt->whereClause;
|
|
|
|
ASSERT_NOTNULL(where);
|
|
|
|
ASSERT(where->isType(kExprOperator));
|
2017-04-06 17:25:47 +02:00
|
|
|
ASSERT_EQ(where->opType, kOpBetween);
|
2017-03-07 13:49:56 +01:00
|
|
|
|
|
|
|
ASSERT_STREQ(where->expr->getName(), "grade");
|
|
|
|
ASSERT(where->expr->isType(kExprColumnRef));
|
|
|
|
|
|
|
|
ASSERT_EQ(where->exprList->size(), 2);
|
|
|
|
ASSERT(where->exprList->at(0)->isType(kExprLiteralInt));
|
2017-04-25 17:25:00 +02:00
|
|
|
ASSERT_EQ(where->exprList->at(0)->ival, -1);
|
2017-03-07 13:49:56 +01:00
|
|
|
ASSERT(where->exprList->at(1)->isType(kExprColumnRef));
|
|
|
|
ASSERT_STREQ(where->exprList->at(1)->getName(), "c");
|
|
|
|
}
|
2017-03-07 14:21:45 +01:00
|
|
|
|
|
|
|
TEST(SelectConditionalSelectTest) {
|
|
|
|
TEST_PARSE_SINGLE_SQL(
|
2017-03-07 14:37:05 +01:00
|
|
|
"SELECT * FROM t WHERE a = (SELECT MIN(v) FROM tt) AND EXISTS (SELECT * FROM test WHERE x < a);",
|
2017-03-07 14:21:45 +01:00
|
|
|
kStmtSelect,
|
|
|
|
SelectStatement,
|
|
|
|
result,
|
|
|
|
stmt);
|
|
|
|
|
|
|
|
Expr* where = stmt->whereClause;
|
|
|
|
ASSERT_NOTNULL(where);
|
|
|
|
ASSERT(where->isType(kExprOperator));
|
2017-04-06 17:25:47 +02:00
|
|
|
ASSERT_EQ(where->opType, kOpAnd);
|
2017-03-07 14:21:45 +01:00
|
|
|
|
2017-03-07 14:37:05 +01:00
|
|
|
// a = (SELECT ...)
|
|
|
|
Expr* cond1 = where->expr;
|
|
|
|
ASSERT_NOTNULL(cond1);
|
|
|
|
ASSERT_NOTNULL(cond1->expr);
|
2017-06-29 13:40:24 +02:00
|
|
|
ASSERT_EQ(cond1->opType, kOpEquals);
|
2017-03-07 14:37:05 +01:00
|
|
|
ASSERT_STREQ(cond1->expr->getName(), "a");
|
|
|
|
ASSERT(cond1->expr->isType(kExprColumnRef));
|
2017-03-07 14:21:45 +01:00
|
|
|
|
2017-03-07 14:37:05 +01:00
|
|
|
ASSERT_NOTNULL(cond1->expr2);
|
|
|
|
ASSERT(cond1->expr2->isType(kExprSelect));
|
2017-03-07 14:21:45 +01:00
|
|
|
|
2017-03-07 14:37:05 +01:00
|
|
|
SelectStatement* select2 = cond1->expr2->select;
|
2017-03-07 14:21:45 +01:00
|
|
|
ASSERT_NOTNULL(select2);
|
2017-03-07 14:55:51 +01:00
|
|
|
ASSERT_STREQ(select2->fromTable->getName(), "tt");
|
2017-03-07 14:21:45 +01:00
|
|
|
|
2017-03-07 14:37:05 +01:00
|
|
|
|
|
|
|
// EXISTS (SELECT ...)
|
|
|
|
Expr* cond2 = where->expr2;
|
2017-04-06 17:25:47 +02:00
|
|
|
ASSERT_EQ(cond2->opType, kOpExists);
|
2017-03-07 14:37:05 +01:00
|
|
|
ASSERT_NOTNULL(cond2->select);
|
|
|
|
|
|
|
|
SelectStatement* ex_select = cond2->select;
|
2017-03-07 14:55:51 +01:00
|
|
|
ASSERT_STREQ(ex_select->fromTable->getName(), "test");
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(SelectCaseWhen) {
|
|
|
|
TEST_PARSE_SINGLE_SQL(
|
|
|
|
"SELECT MAX(CASE WHEN a = 'foo' THEN x ELSE 0 END) FROM test;",
|
|
|
|
kStmtSelect,
|
|
|
|
SelectStatement,
|
|
|
|
result,
|
|
|
|
stmt);
|
|
|
|
|
|
|
|
ASSERT_EQ(stmt->selectList->size(), 1);
|
|
|
|
Expr* func = stmt->selectList->at(0);
|
|
|
|
|
|
|
|
ASSERT_NOTNULL(func);
|
|
|
|
ASSERT(func->isType(kExprFunctionRef));
|
|
|
|
ASSERT_EQ(func->exprList->size(), 1);
|
|
|
|
|
|
|
|
Expr* caseExpr = func->exprList->at(0);
|
|
|
|
ASSERT_NOTNULL(caseExpr);
|
|
|
|
ASSERT(caseExpr->isType(kExprOperator));
|
2017-04-06 17:25:47 +02:00
|
|
|
ASSERT_EQ(caseExpr->opType, kOpCase);
|
2017-03-07 14:55:51 +01:00
|
|
|
ASSERT(caseExpr->expr->isType(kExprOperator));
|
2017-06-29 13:40:24 +02:00
|
|
|
ASSERT_EQ(caseExpr->expr->opType, kOpEquals);
|
2017-03-07 14:55:51 +01:00
|
|
|
ASSERT_EQ(caseExpr->exprList->size(), 2);
|
2017-03-07 14:21:45 +01:00
|
|
|
}
|
2017-04-21 22:03:12 +02:00
|
|
|
|
|
|
|
TEST(SelectJoin) {
|
|
|
|
TEST_PARSE_SINGLE_SQL(
|
|
|
|
"SELECT City.name, Product.category, SUM(price) FROM fact\
|
|
|
|
INNER JOIN City ON fact.city_id = City.id\
|
|
|
|
OUTER JOIN Product ON fact.product_id = Product.id\
|
|
|
|
GROUP BY City.name, Product.category;",
|
|
|
|
kStmtSelect,
|
|
|
|
SelectStatement,
|
|
|
|
result,
|
|
|
|
stmt);
|
|
|
|
|
|
|
|
const TableRef* table = stmt->fromTable;
|
|
|
|
const JoinDefinition* outer_join = table->join;
|
|
|
|
ASSERT_EQ(table->type, kTableJoin);
|
|
|
|
ASSERT_EQ(outer_join->type, kJoinOuter);
|
|
|
|
|
|
|
|
ASSERT_EQ(outer_join->right->type, kTableName);
|
|
|
|
ASSERT_STREQ(outer_join->right->name, "Product");
|
2017-06-29 13:40:24 +02:00
|
|
|
ASSERT_EQ(outer_join->condition->opType, kOpEquals);
|
2017-04-21 22:03:12 +02:00
|
|
|
ASSERT_STREQ(outer_join->condition->expr->table, "fact");
|
|
|
|
ASSERT_STREQ(outer_join->condition->expr->name, "product_id");
|
|
|
|
ASSERT_STREQ(outer_join->condition->expr2->table, "Product");
|
|
|
|
ASSERT_STREQ(outer_join->condition->expr2->name, "id");
|
|
|
|
|
|
|
|
// Joins are are left associative.
|
|
|
|
// So the second join should be on the left.
|
|
|
|
ASSERT_EQ(outer_join->left->type, kTableJoin);
|
|
|
|
|
|
|
|
const JoinDefinition* inner_join = outer_join->left->join;
|
|
|
|
ASSERT_EQ(inner_join->type, kJoinInner);
|
|
|
|
ASSERT_EQ(inner_join->left->type, kTableName);
|
|
|
|
ASSERT_STREQ(inner_join->left->name, "fact");
|
|
|
|
ASSERT_EQ(inner_join->right->type, kTableName);
|
|
|
|
ASSERT_STREQ(inner_join->right->name, "City");
|
|
|
|
|
2017-06-29 13:40:24 +02:00
|
|
|
ASSERT_EQ(inner_join->condition->opType, kOpEquals);
|
2017-04-21 22:03:12 +02:00
|
|
|
ASSERT_STREQ(inner_join->condition->expr->table, "fact");
|
|
|
|
ASSERT_STREQ(inner_join->condition->expr->name, "city_id");
|
|
|
|
ASSERT_STREQ(inner_join->condition->expr2->table, "City");
|
|
|
|
ASSERT_STREQ(inner_join->condition->expr2->name, "id");
|
|
|
|
}
|