aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/Tests/ParserTest/tst_parsertest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'SQLiteStudio3/Tests/ParserTest/tst_parsertest.cpp')
-rw-r--r--SQLiteStudio3/Tests/ParserTest/tst_parsertest.cpp198
1 files changed, 186 insertions, 12 deletions
diff --git a/SQLiteStudio3/Tests/ParserTest/tst_parsertest.cpp b/SQLiteStudio3/Tests/ParserTest/tst_parsertest.cpp
index eb2a76f..6d537dc 100644
--- a/SQLiteStudio3/Tests/ParserTest/tst_parsertest.cpp
+++ b/SQLiteStudio3/Tests/ParserTest/tst_parsertest.cpp
@@ -8,6 +8,9 @@
#include "parser/lexer.h"
#include "parser/parsererror.h"
#include "common/utils_sql.h"
+#include "parser/ast/sqlitewindowdefinition.h"
+#include "parser/ast/sqlitefilterover.h"
+#include "common/compatibility.h"
#include <QString>
#include <QtTest>
@@ -19,11 +22,15 @@ class ParserTest : public QObject
ParserTest();
private:
- Parser* parser2 = nullptr;
Parser* parser3 = nullptr;
+ void verifyWindowClause(const QString& sql, SqliteSelectPtr& select, bool& ok);
private Q_SLOTS:
+ void initTestCase();
+ void cleanupTestCase();
+
void test();
+ void testString();
void testScientificNumber();
void testUniqConflict();
void testGetTableTokens();
@@ -49,8 +56,11 @@ class ParserTest : public QObject
void testRebuildTokensUpdate();
void testRebuildTokensInsertUpsert();
void testGetColumnTokensFromInsertUpsert();
- void initTestCase();
- void cleanupTestCase();
+ void testGeneratedColumn();
+ void testWindowClause();
+ void testFilterClause();
+ void testUpdateFrom();
+ void testStringAsTableId();
};
ParserTest::ParserTest()
@@ -82,10 +92,26 @@ void ParserTest::test()
TokenList tokens = query->getContextTableTokens();
}
+void ParserTest::testString()
+{
+ QString sql = "SELECT 1 = '1';";
+
+ parser3->parse(sql);
+ QCOMPARE(parser3->getErrors().size(), 0);
+
+ SqliteQueryPtr query = parser3->getQueries()[0];
+ query->rebuildTokens();
+
+ QCOMPARE(query->tokens.detokenize(), sql);
+ QCOMPARE(query->tokens.size(), 8);
+ QCOMPARE(query->tokens[2]->type, Token::Type::INTEGER);
+ QCOMPARE(query->tokens[6]->type, Token::Type::STRING);
+}
+
void ParserTest::testScientificNumber()
{
QString sql = "SELECT 1e100;";
- TokenList tokens = Lexer::tokenize(sql, Dialect::Sqlite3);
+ TokenList tokens = Lexer::tokenize(sql);
QVERIFY(tokens.size() == 4);
QVERIFY(tokens[2]->type == Token::Type::FLOAT);
@@ -96,7 +122,7 @@ void ParserTest::testGetTableTokens()
QString sql = "select someTable.* FROM someTable;";
parser3->parse(sql);
- QVERIFY(parser3->getErrors().size() == 0);
+ QCOMPARE(parser3->getErrors().size(), 0);
SqliteQueryPtr query = parser3->getQueries()[0];
TokenList tokens = query->getContextTableTokens();
@@ -110,7 +136,7 @@ void ParserTest::testGetTableTokens2()
QString sql = "select db.tab.col FROM someTable;";
parser3->parse(sql);
- QVERIFY(parser3->getErrors().size() == 0);
+ QCOMPARE(parser3->getErrors().size(), 0);
SqliteQueryPtr query = parser3->getQueries()[0];
TokenList tokens = query->getContextTableTokens();
@@ -228,7 +254,7 @@ void ParserTest::testCommentEnding2()
void ParserTest::testOper1()
{
QString sql = "SELECT dfgd<=2";
- TokenList tokens = Lexer::tokenize(sql, Dialect::Sqlite3);
+ TokenList tokens = Lexer::tokenize(sql);
QVERIFY(tokens[2]->value == "dfgd");
QVERIFY(tokens[3]->value == "<=");
QVERIFY(tokens[4]->value == "2");
@@ -381,7 +407,7 @@ void ParserTest::testExpr()
void ParserTest::testCommentBeginMultiline()
{
QString sql = "/*";
- TokenList tokens = Lexer::tokenize(sql, Dialect::Sqlite3);
+ TokenList tokens = Lexer::tokenize(sql);
QVERIFY(tokens.size() == 1);
QVERIFY(tokens[0]->type == Token::COMMENT);
}
@@ -494,22 +520,170 @@ void ParserTest::testGetColumnTokensFromInsertUpsert()
QVERIFY(insert->upsert);
TokenList tk = insert->getContextColumnTokens();
- qSort(tk.begin(), tk.end(), [](const TokenPtr& t1, const TokenPtr& t2) {return t1->start < t2->start;});
+ sSort(tk, [](const TokenPtr& t1, const TokenPtr& t2) {return t1->start < t2->start;});
QVERIFY(tk.toValueList().join(" ") == "a1 a2 b1 b2 b3 col1 col2 col3 x");
}
+void ParserTest::testGeneratedColumn()
+{
+ QString sql = "create table t2 (a INTEGER PRIMARY KEY AUTOINCREMENT, b INTEGER GENERATED ALWAYS AS (a+2) STORED);";
+ bool res = parser3->parse(sql);
+ QVERIFY(res);
+ QVERIFY(parser3->getErrors().isEmpty());
+
+ SqliteCreateTablePtr create = parser3->getQueries().first().dynamicCast<SqliteCreateTable>();
+ QVERIFY(create->columns.size() == 2);
+ QVERIFY(create->columns[1]->constraints.size() == 1);
+ QVERIFY(create->columns[1]->constraints[0]->type == SqliteCreateTable::Column::Constraint::GENERATED);
+ QVERIFY(create->columns[1]->constraints[0]->generatedKw == true);
+ QVERIFY(create->columns[1]->constraints[0]->generatedType == SqliteCreateTable::Column::Constraint::GeneratedType::STORED);
+ QVERIFY(create->columns[1]->constraints[0]->expr);
+}
+
+void ParserTest::testWindowClause()
+{
+ QString sql = "SELECT x, y, row_number() OVER win1, rank() OVER win2 "
+ " FROM t0 "
+ "WINDOW win1 AS (ORDER BY y RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),"
+ " win2 AS (PARTITION BY y ORDER BY x)"
+ " ORDER BY x;";
+
+ SqliteSelectPtr select;
+ bool ok = false;
+ verifyWindowClause(sql, select, ok);
+ if (!ok)
+ return;
+
+ qInfo() << "first run PASS, runing second time, after detokenizing";
+ sql = select->detokenize();
+ verifyWindowClause(sql, select, ok);
+}
+
+void ParserTest::verifyWindowClause(const QString& sql, SqliteSelectPtr& select, bool& ok)
+{
+ bool res = parser3->parse(sql);
+ QVERIFY(res);
+ QVERIFY(parser3->getErrors().isEmpty());
+
+ select = parser3->getQueries().first().dynamicCast<SqliteSelect>();
+ QCOMPARE(select->coreSelects.size(), 1);
+ SqliteSelect::Core* core = select->coreSelects[0];
+
+ // Result Columns
+ QCOMPARE(core->resultColumns.size(), 4);
+
+ QVERIFY(core->resultColumns[0]->expr);
+ QCOMPARE(core->resultColumns[0]->expr->column, "x");
+
+ QVERIFY(core->resultColumns[1]->expr);
+ QCOMPARE(core->resultColumns[1]->expr->column, "y");
+
+ QVERIFY(core->resultColumns[2]->expr);
+ QCOMPARE(core->resultColumns[2]->expr->function, "row_number");
+ QVERIFY(core->resultColumns[2]->expr->filterOver);
+ QCOMPARE(core->resultColumns[2]->expr->filterOver->over->name, "win1");
+
+ QVERIFY(core->resultColumns[3]->expr);
+ QCOMPARE(core->resultColumns[3]->expr->function, "rank");
+ QVERIFY(core->resultColumns[3]->expr->filterOver);
+ QCOMPARE(core->resultColumns[3]->expr->filterOver->over->name, "win2");
+
+ // Windows
+ QCOMPARE(core->windows.size(), 2);
+ SqliteWindowDefinition* winDef1 = core->windows[0];
+ QVERIFY(!winDef1->name.isNull());
+ QVERIFY(winDef1->window);
+
+ SqliteWindowDefinition::Window* win1 = winDef1->window;
+ QVERIFY(win1->mode == SqliteWindowDefinition::Window::Mode::ORDER_BY);
+ QVERIFY(win1->name.isNull());
+ QVERIFY(win1->frame);
+ QVERIFY(win1->orderBy.size() == 1);
+ QVERIFY(win1->frame->rangeOrRows == SqliteWindowDefinition::Window::Frame::RangeOrRows::RANGE);
+ QVERIFY(win1->frame->startBound);
+ QVERIFY(win1->frame->startBound->type == SqliteWindowDefinition::Window::Frame::Bound::Type::UNBOUNDED_PRECEDING);
+ QVERIFY(win1->frame->endBound);
+ QVERIFY(win1->frame->endBound->type == SqliteWindowDefinition::Window::Frame::Bound::Type::CURRENT_ROW);
+
+ SqliteWindowDefinition* winDef2 = core->windows[1];
+ QVERIFY(!winDef2->name.isNull());
+ QVERIFY(winDef2->window);
+
+ SqliteWindowDefinition::Window* win2 = winDef2->window;
+ QVERIFY(win2->mode == SqliteWindowDefinition::Window::Mode::PARTITION_BY);
+ QVERIFY(win2->name.isNull());
+ QVERIFY(win2->exprList.size() == 1);
+ QVERIFY(win2->orderBy.size() == 1);
+ QVERIFY(!win2->frame);
+
+ ok = true;
+}
+
+void ParserTest::testFilterClause()
+{
+ QString sql = "SELECT c, a, b, group_concat(b, '.') FILTER (WHERE c!='two') OVER ("
+ " ORDER BY a"
+ " ) AS group_concat"
+ " FROM t1 ORDER BY a;";
+ bool res = parser3->parse(sql);
+ QVERIFY(res);
+ QVERIFY(parser3->getErrors().isEmpty());
+
+ SqliteSelectPtr select = parser3->getQueries().first().dynamicCast<SqliteSelect>();
+ QVERIFY(select->coreSelects.size() == 1);
+ SqliteSelect::Core* core = select->coreSelects[0];
+ QVERIFY(core->windows.size() == 0);
+
+ QVERIFY(core->resultColumns.size() == 4);
+ SqliteSelect::Core::ResultColumn* resCol = core->resultColumns[3];
+ QVERIFY(resCol->alias == "group_concat");
+ QVERIFY(resCol->expr);
+ QVERIFY(resCol->expr->filterOver);
+ QVERIFY(resCol->expr->filterOver->filter->expr);
+ QVERIFY(resCol->expr->filterOver->over);
+ QVERIFY(resCol->expr->filterOver->over->mode == SqliteFilterOver::Over::Mode::WINDOW);
+ QVERIFY(resCol->expr->filterOver->over->window);
+ QVERIFY(resCol->expr->filterOver->over->window->mode == SqliteWindowDefinition::Window::Mode::ORDER_BY);
+}
+
+void ParserTest::testUpdateFrom()
+{
+ QString sql = "UPDATE inventory"
+ " SET quantity = quantity - daily.amt"
+ " FROM (SELECT sum(quantity) AS amt, itemId FROM sales GROUP BY 2) AS daily"
+ " WHERE inventory.itemId = daily.itemId;";
+
+ bool res = parser3->parse(sql);
+ QVERIFY(res);
+ QVERIFY(parser3->getErrors().isEmpty());
+
+ SqliteUpdatePtr update = parser3->getQueries().first().dynamicCast<SqliteUpdate>();
+ QVERIFY(update->where);
+ QCOMPARE(update->keyValueMap.size(), 1);
+ QVERIFY(update->from);
+ QVERIFY(update->from->singleSource);
+ QVERIFY(update->from->singleSource->select);
+ QCOMPARE(update->from->singleSource->alias, "daily");
+}
+
+void ParserTest::testStringAsTableId()
+{
+ QString sql = "select 'bb'.id1 = 'bb'.id2;";
+ bool res = parser3->parse(sql);
+ QVERIFY(res);
+ QVERIFY(parser3->getErrors().isEmpty());
+}
+
void ParserTest::initTestCase()
{
initKeywords();
Lexer::staticInit();
initUtilsSql();
- parser2 = new Parser(Dialect::Sqlite2);
- parser3 = new Parser(Dialect::Sqlite3);
+ parser3 = new Parser();
}
void ParserTest::cleanupTestCase()
{
- delete parser2;
delete parser3;
}