Skip to content

Commit 9ca19ce

Browse files
authored
Correctly parse named columns for joins (#240)
Allows a list of identifiers (a.k.a column names) when joins name columns with `USING (...)`, which is in line with the SQL standard and PostgreSQL.
1 parent c247124 commit 9ca19ce

File tree

8 files changed

+1897
-1793
lines changed

8 files changed

+1897
-1793
lines changed

src/parser/bison_parser.cpp

Lines changed: 1379 additions & 1392 deletions
Large diffs are not rendered by default.

src/parser/bison_parser.h

Lines changed: 201 additions & 201 deletions
Large diffs are not rendered by default.

src/parser/bison_parser.y

Lines changed: 160 additions & 172 deletions
Large diffs are not rendered by default.

src/sql/Table.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ struct JoinDefinition {
6262
Expr* condition;
6363

6464
JoinType type;
65+
std::vector<char*>* namedColumns;
6566
};
6667

6768
} // namespace hsql

test/prepare_tests.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ TEST(PrepareSingleStatementTest) {
3030
TEST_PARSE_SINGLE_SQL(prepare->query, kStmtSelect, SelectStatement, result2, select);
3131

3232
ASSERT_EQ(result2.parameters().size(), 1);
33-
ASSERT(select->whereClause->expr2->isType(kExprParameter))
34-
ASSERT_EQ(select->whereClause->expr2->ival, 0)
33+
ASSERT(select->whereClause->expr2->isType(kExprParameter));
34+
ASSERT_EQ(select->whereClause->expr2->ival, 0);
3535
}
3636

3737
TEST(DeallocatePrepareStatementTest) {
@@ -49,16 +49,16 @@ TEST(StatementWithParameters) {
4949

5050
ASSERT_EQ(result.parameters().size(), 2);
5151

52-
ASSERT_EQ(eq1->opType, hsql::kOpEquals)
53-
ASSERT(eq1->expr->isType(hsql::kExprColumnRef))
54-
ASSERT(eq1->expr2->isType(kExprParameter))
55-
ASSERT_EQ(eq1->expr2->ival, 0)
52+
ASSERT_EQ(eq1->opType, hsql::kOpEquals);
53+
ASSERT(eq1->expr->isType(hsql::kExprColumnRef));
54+
ASSERT(eq1->expr2->isType(kExprParameter));
55+
ASSERT_EQ(eq1->expr2->ival, 0);
5656
ASSERT_EQ(result.parameters()[0], eq1->expr2);
5757

58-
ASSERT_EQ(eq2->opType, hsql::kOpEquals)
59-
ASSERT(eq2->expr->isType(hsql::kExprColumnRef))
60-
ASSERT(eq2->expr2->isType(kExprParameter))
61-
ASSERT_EQ(eq2->expr2->ival, 1)
58+
ASSERT_EQ(eq2->opType, hsql::kOpEquals);
59+
ASSERT(eq2->expr->isType(hsql::kExprColumnRef));
60+
ASSERT(eq2->expr2->isType(kExprParameter));
61+
ASSERT_EQ(eq2->expr2->ival, 1);
6262
ASSERT_EQ(result.parameters()[1], eq2->expr2);
6363
}
6464

test/queries/queries-bad.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,11 @@
6060
!SELECT rank() OVER (ROWS UNBOUNDED PRECEDINGG) FROM test;
6161
!SELECT test1, rank() OVER (ROWS -1 PRECEDING) FROM test;
6262
!SELECT test1, rank() OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND -1 FOLLOWING) FROM test;
63+
# Join USING
64+
!SELECT * FROM foo INNER JOIN bar USING ();
65+
!SELECT * FROM foo INNER JOIN bar USING (*);
66+
# Both the SQL standard and Postgres allow column names only (no column references).
67+
!SELECT * FROM foo INNER JOIN bar USING (foo.a);
68+
!SELECT * FROM foo INNER JOIN bar USING (a b);
69+
!SELECT * FROM foo INNER JOIN bar USING (a AS b);
70+
!SELECT * FROM foo INNER JOIN bar USING (1);

test/select_tests.cpp

Lines changed: 117 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ TEST(SelectJoin) {
336336
ASSERT_STREQ(outer_join->condition->expr->name, "product_id");
337337
ASSERT_STREQ(outer_join->condition->expr2->table, "Product");
338338
ASSERT_STREQ(outer_join->condition->expr2->name, "id");
339+
ASSERT_FALSE(outer_join->namedColumns);
339340

340341
// Joins are are left associative.
341342
// So the second join should be on the left.
@@ -347,6 +348,7 @@ TEST(SelectJoin) {
347348
ASSERT_STREQ(inner_join->left->name, "fact");
348349
ASSERT_EQ(inner_join->right->type, kTableName);
349350
ASSERT_STREQ(inner_join->right->name, "City");
351+
ASSERT_FALSE(inner_join->namedColumns);
350352

351353
ASSERT_EQ(inner_join->condition->opType, kOpEquals);
352354
ASSERT_STREQ(inner_join->condition->expr->table, "fact");
@@ -355,6 +357,106 @@ TEST(SelectJoin) {
355357
ASSERT_STREQ(inner_join->condition->expr2->name, "id");
356358
}
357359

360+
TEST(SelectJoinUsing) {
361+
TEST_PARSE_SQL_QUERY(
362+
"SELECT * FROM foo INNER JOIN bar USING (a, b);"
363+
"SELECT a, b, c FROM foo LEFT JOIN bar USING (a);"
364+
"SELECT b FROM foo AS baz JOIN bar USING (a);",
365+
result, 3);
366+
367+
auto stmt = (SelectStatement*)result.getStatement(0);
368+
// SELECT * ...
369+
ASSERT_TRUE(stmt->selectList);
370+
ASSERT_EQ(stmt->selectList->size(), 1);
371+
ASSERT_EQ(stmt->selectList->front()->type, kExprStar);
372+
373+
// ... FROM foo INNER JOIN bar ...
374+
ASSERT_TRUE(stmt->fromTable);
375+
ASSERT_EQ(stmt->fromTable->type, kTableJoin);
376+
ASSERT_TRUE(stmt->fromTable->join);
377+
ASSERT_EQ(stmt->fromTable->join->type, kJoinInner);
378+
ASSERT_TRUE(stmt->fromTable->join->left);
379+
ASSERT_EQ(stmt->fromTable->join->left->type, kTableName);
380+
ASSERT_TRUE(stmt->fromTable->join->left->name);
381+
ASSERT_STREQ(stmt->fromTable->join->left->name, "foo");
382+
ASSERT_TRUE(stmt->fromTable->join->right);
383+
ASSERT_EQ(stmt->fromTable->join->right->type, kTableName);
384+
ASSERT_TRUE(stmt->fromTable->join->right->name);
385+
ASSERT_STREQ(stmt->fromTable->join->right->name, "bar");
386+
387+
// ... USING a, b;
388+
ASSERT_FALSE(stmt->fromTable->join->condition);
389+
ASSERT_TRUE(stmt->fromTable->join->namedColumns);
390+
ASSERT_EQ(stmt->fromTable->join->namedColumns->size(), 2);
391+
ASSERT_STREQ(stmt->fromTable->join->namedColumns->at(0), "a");
392+
ASSERT_STREQ(stmt->fromTable->join->namedColumns->at(1), "b");
393+
394+
stmt = (SelectStatement*)result.getStatement(1);
395+
// SELECT a, b, c ...
396+
ASSERT_TRUE(stmt->selectList);
397+
ASSERT_EQ(stmt->selectList->size(), 3);
398+
ASSERT_EQ(stmt->selectList->at(0)->type, kExprColumnRef);
399+
ASSERT_TRUE(stmt->selectList->at(0)->name);
400+
ASSERT_STREQ(stmt->selectList->at(0)->name, "a");
401+
ASSERT_EQ(stmt->selectList->at(1)->type, kExprColumnRef);
402+
ASSERT_TRUE(stmt->selectList->at(1)->name);
403+
ASSERT_STREQ(stmt->selectList->at(1)->name, "b");
404+
ASSERT_EQ(stmt->selectList->at(2)->type, kExprColumnRef);
405+
ASSERT_TRUE(stmt->selectList->at(2)->name);
406+
ASSERT_STREQ(stmt->selectList->at(2)->name, "c");
407+
408+
// ... FROM foo LEFT JOIN bar ...
409+
ASSERT_TRUE(stmt->fromTable);
410+
ASSERT_EQ(stmt->fromTable->type, kTableJoin);
411+
ASSERT_TRUE(stmt->fromTable->join);
412+
ASSERT_EQ(stmt->fromTable->join->type, kJoinLeft);
413+
ASSERT_TRUE(stmt->fromTable->join->left);
414+
ASSERT_EQ(stmt->fromTable->join->left->type, kTableName);
415+
ASSERT_TRUE(stmt->fromTable->join->left->name);
416+
ASSERT_STREQ(stmt->fromTable->join->left->name, "foo");
417+
ASSERT_TRUE(stmt->fromTable->join->right);
418+
ASSERT_EQ(stmt->fromTable->join->right->type, kTableName);
419+
ASSERT_TRUE(stmt->fromTable->join->right->name);
420+
ASSERT_STREQ(stmt->fromTable->join->right->name, "bar");
421+
422+
// ... USING a;
423+
ASSERT_FALSE(stmt->fromTable->join->condition);
424+
ASSERT_TRUE(stmt->fromTable->join->namedColumns);
425+
ASSERT_EQ(stmt->fromTable->join->namedColumns->size(), 1);
426+
ASSERT_STREQ(stmt->fromTable->join->namedColumns->at(0), "a");
427+
428+
stmt = (SelectStatement*)result.getStatement(2);
429+
// SELECT b ...
430+
ASSERT_TRUE(stmt->selectList);
431+
ASSERT_EQ(stmt->selectList->size(), 1);
432+
ASSERT_EQ(stmt->selectList->at(0)->type, kExprColumnRef);
433+
ASSERT_TRUE(stmt->selectList->at(0)->name);
434+
ASSERT_STREQ(stmt->selectList->at(0)->name, "b");
435+
436+
// ... FROM foo as baz JOIN bar ...
437+
ASSERT_TRUE(stmt->fromTable);
438+
ASSERT_EQ(stmt->fromTable->type, kTableJoin);
439+
ASSERT_TRUE(stmt->fromTable->join);
440+
ASSERT_EQ(stmt->fromTable->join->type, kJoinInner);
441+
ASSERT_TRUE(stmt->fromTable->join->left);
442+
ASSERT_EQ(stmt->fromTable->join->left->type, kTableName);
443+
ASSERT_TRUE(stmt->fromTable->join->left->name);
444+
ASSERT_STREQ(stmt->fromTable->join->left->name, "foo");
445+
ASSERT_TRUE(stmt->fromTable->join->left->alias);
446+
ASSERT_TRUE(stmt->fromTable->join->left->alias->name);
447+
ASSERT_STREQ(stmt->fromTable->join->left->alias->name, "baz");
448+
ASSERT_TRUE(stmt->fromTable->join->right);
449+
ASSERT_EQ(stmt->fromTable->join->right->type, kTableName);
450+
ASSERT_TRUE(stmt->fromTable->join->right->name);
451+
ASSERT_STREQ(stmt->fromTable->join->right->name, "bar");
452+
453+
// ... USING a;
454+
ASSERT_FALSE(stmt->fromTable->join->condition);
455+
ASSERT_TRUE(stmt->fromTable->join->namedColumns);
456+
ASSERT_EQ(stmt->fromTable->join->namedColumns->size(), 1);
457+
ASSERT_STREQ(stmt->fromTable->join->namedColumns->at(0), "a");
458+
}
459+
358460
TEST(SelectColumnOrder) {
359461
TEST_PARSE_SINGLE_SQL(
360462
"SELECT *\
@@ -490,36 +592,47 @@ TEST(JoinTypes) {
490592

491593
stmt = (SelectStatement*)result.getStatement(0);
492594
ASSERT_EQ(stmt->fromTable->join->type, kJoinInner);
595+
ASSERT_FALSE(stmt->fromTable->join->namedColumns);
493596

494597
stmt = (SelectStatement*)result.getStatement(1);
495598
ASSERT_EQ(stmt->fromTable->join->type, kJoinInner);
599+
ASSERT_FALSE(stmt->fromTable->join->namedColumns);
496600

497601
stmt = (SelectStatement*)result.getStatement(2);
498602
ASSERT_EQ(stmt->fromTable->join->type, kJoinLeft);
603+
ASSERT_FALSE(stmt->fromTable->join->namedColumns);
499604

500605
stmt = (SelectStatement*)result.getStatement(3);
501606
ASSERT_EQ(stmt->fromTable->join->type, kJoinLeft);
607+
ASSERT_FALSE(stmt->fromTable->join->namedColumns);
502608

503609
stmt = (SelectStatement*)result.getStatement(4);
504610
ASSERT_EQ(stmt->fromTable->join->type, kJoinRight);
611+
ASSERT_FALSE(stmt->fromTable->join->namedColumns);
505612

506613
stmt = (SelectStatement*)result.getStatement(5);
507614
ASSERT_EQ(stmt->fromTable->join->type, kJoinRight);
615+
ASSERT_FALSE(stmt->fromTable->join->namedColumns);
508616

509617
stmt = (SelectStatement*)result.getStatement(6);
510618
ASSERT_EQ(stmt->fromTable->join->type, kJoinFull);
619+
ASSERT_FALSE(stmt->fromTable->join->namedColumns);
511620

512621
stmt = (SelectStatement*)result.getStatement(7);
513622
ASSERT_EQ(stmt->fromTable->join->type, kJoinFull);
623+
ASSERT_FALSE(stmt->fromTable->join->namedColumns);
514624

515625
stmt = (SelectStatement*)result.getStatement(8);
516626
ASSERT_EQ(stmt->fromTable->join->type, kJoinFull);
627+
ASSERT_FALSE(stmt->fromTable->join->namedColumns);
517628

518629
stmt = (SelectStatement*)result.getStatement(9);
519630
ASSERT_EQ(stmt->fromTable->join->type, kJoinNatural);
631+
ASSERT_FALSE(stmt->fromTable->join->namedColumns);
520632

521633
stmt = (SelectStatement*)result.getStatement(10);
522634
ASSERT_EQ(stmt->fromTable->join->type, kJoinCross);
635+
ASSERT_FALSE(stmt->fromTable->join->namedColumns);
523636

524637
stmt = (SelectStatement*)result.getStatement(11);
525638
ASSERT_NULL(stmt->fromTable->join);
@@ -699,7 +812,7 @@ TEST(WithClauseSingle) {
699812
ASSERT_STREQ(stmt->withDescriptions->at(0)->alias, "a");
700813

701814
// with_description – select stmt
702-
ASSERT_EQ(stmt->withDescriptions->at(0)->select->selectList->size(), 1)
815+
ASSERT_EQ(stmt->withDescriptions->at(0)->select->selectList->size(), 1);
703816
ASSERT_STREQ(stmt->withDescriptions->at(0)->select->selectList->at(0)->name, std::string("name"));
704817
ASSERT_STREQ(stmt->withDescriptions->at(0)->select->fromTable->name, std::string("peopleA"));
705818

@@ -725,9 +838,9 @@ TEST(WithClauseDouble) {
725838
ASSERT_STREQ(stmt->withDescriptions->at(1)->alias, "b");
726839

727840
// with_description – select stmts
728-
ASSERT_EQ(stmt->withDescriptions->at(0)->select->selectList->size(), 1)
841+
ASSERT_EQ(stmt->withDescriptions->at(0)->select->selectList->size(), 1);
729842
ASSERT_STREQ(stmt->withDescriptions->at(0)->select->fromTable->name, "peopleA");
730-
ASSERT_EQ(stmt->withDescriptions->at(1)->select->selectList->size(), 2)
843+
ASSERT_EQ(stmt->withDescriptions->at(1)->select->selectList->size(), 2);
731844
ASSERT_STREQ(stmt->withDescriptions->at(1)->select->fromTable->name, "peopleB");
732845

733846
// main select
@@ -990,7 +1103,6 @@ TEST(MultipleLockingClause) {
9901103
}
9911104

9921105
TEST(WindowFunctions) {
993-
SelectStatement* stmt;
9941106
TEST_PARSE_SQL_QUERY(
9951107
"SELECT t2, 1 / avg(t1) OVER(), rank() OVER(ORDER BY t1 DESC) rnk FROM t;"
9961108
"SELECT avg(t1) OVER(PARTITION BY t2, t3 ORDER BY t4, t5 ROWS UNBOUNDED PRECEDING) FROM t;"
@@ -1000,7 +1112,7 @@ TEST(WindowFunctions) {
10001112
"SELECT rank() OVER(PARTITION BY t1 ORDER BY t2 GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM t;",
10011113
result, 5);
10021114

1003-
stmt = (SelectStatement*)result.getStatement(0);
1115+
auto stmt = (SelectStatement*)result.getStatement(0);
10041116
ASSERT_TRUE(stmt->selectList);
10051117
ASSERT_EQ(stmt->selectList->size(), 3);
10061118
ASSERT_TRUE(stmt->fromTable);

test/thirdparty/microtest/microtest.h

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,45 +21,53 @@
2121
// Assertions //
2222
////////////////
2323

24-
#define ASSERT(cond) ASSERT_TRUE(cond);
24+
#define ASSERT(cond) ASSERT_TRUE(cond)
2525

26-
#define ASSERT_TRUE(cond) \
27-
if (!(cond)) throw mt::AssertFailedException(#cond, __FILE__, __LINE__);
26+
#define ASSERT_TRUE(cond) \
27+
if (!(cond)) { \
28+
throw mt::AssertFailedException(#cond, __FILE__, __LINE__); \
29+
} \
30+
static_assert(true, "End call of macro with a semicolon.")
2831

29-
#define ASSERT_FALSE(cond) \
30-
if (cond) throw mt::AssertFailedException(#cond, __FILE__, __LINE__);
32+
#define ASSERT_FALSE(cond) \
33+
if (cond) { \
34+
throw mt::AssertFailedException(#cond, __FILE__, __LINE__); \
35+
} \
36+
static_assert(true, "End call of macro with a semicolon.")
3137

32-
#define ASSERT_NULL(value) ASSERT_TRUE(value == NULL);
38+
#define ASSERT_NULL(value) ASSERT_TRUE(value == NULL)
3339

34-
#define ASSERT_NOTNULL(value) ASSERT_TRUE(value != NULL);
40+
#define ASSERT_NOTNULL(value) ASSERT_TRUE(value != NULL)
3541

3642
#define ASSERT_STREQ(a, b) \
3743
if (std::string(a).compare(std::string(b)) != 0) { \
3844
printf("%s{ info} %s", mt::yellow(), mt::def()); \
3945
std::cout << "Actual values: " << a << " != " << b << std::endl; \
4046
throw mt::AssertFailedException(#a " == " #b, __FILE__, __LINE__); \
41-
}
47+
} \
48+
static_assert(true, "End call of macro with a semicolon.")
4249

4350
#define ASSERT_STRNEQ(a, b) \
4451
if (std::string(a).compare(std::string(b)) != = 0) { \
4552
printf("%s{ info} %s", mt::yellow(), mt::def()); \
4653
std::cout << "Actual values: " << a << " == " << b << std::endl; \
4754
throw mt::AssertFailedException(#a " != " #b, __FILE__, __LINE__); \
48-
}
55+
} \
56+
static_assert(true, "End call of macro with a semicolon.")
4957

5058
#define ASSERT_EQ(a, b) \
5159
if (a != b) { \
5260
printf("%s{ info} %s", mt::yellow(), mt::def()); \
5361
std::cout << "Actual values: " << a << " != " << b << std::endl; \
5462
} \
55-
ASSERT(a == b);
63+
ASSERT(a == b)
5664

5765
#define ASSERT_NEQ(a, b) \
5866
if (a == b) { \
5967
printf("%s{ info} %s", mt::yellow(), mt::def()); \
6068
std::cout << "Actual values: " << a << " == " << b << std::endl; \
6169
} \
62-
ASSERT(a != b);
70+
ASSERT(a != b)
6371

6472
////////////////
6573
// Unit Tests //
@@ -102,7 +110,7 @@ inline void printFailed(const char* message, FILE* file = stdout) {
102110
class AssertFailedException : public std::exception {
103111
public:
104112
AssertFailedException(std::string description, std::string filepath, int line)
105-
: std::exception(), description_(description), filepath_(filepath), line_(line){};
113+
: std::exception(), description_(description), filepath_(filepath), line_(line) {};
106114

107115
virtual const char* what() const throw() { return description_.c_str(); }
108116

@@ -196,4 +204,4 @@ class Runtime {
196204
} \
197205
}
198206

199-
#endif // __MICROTEST_H__
207+
#endif // __MICROTEST_H__

0 commit comments

Comments
 (0)