The editor now has context aware completions
This commit is contained in:
parent
1a1bbd0838
commit
960967c8a6
18 changed files with 1984 additions and 0 deletions
|
|
@ -9,6 +9,8 @@ add_executable(pglabtests
|
|||
tst_escapeConnectionStringValue.cpp
|
||||
tst_expected.cpp
|
||||
tst_SqlLexer.cpp
|
||||
tst_SqlContext.cpp
|
||||
tst_SqlCompletionProvider.cpp
|
||||
tst_scopeguard.cpp
|
||||
tst_CsvWriter.cpp
|
||||
tst_PasswordManager.cpp
|
||||
|
|
|
|||
237
tests/pglabtests/tst_SqlCompletionProvider.cpp
Normal file
237
tests/pglabtests/tst_SqlCompletionProvider.cpp
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
#include <gtest/gtest.h>
|
||||
#include "SqlCompletionProvider.h"
|
||||
#include "PrintTo_Qt.h"
|
||||
|
||||
using namespace testing;
|
||||
|
||||
namespace {
|
||||
|
||||
CompletionCatalog testCatalog()
|
||||
{
|
||||
CompletionCatalog c;
|
||||
c.schemas = { "public", "sales" };
|
||||
c.relations = {
|
||||
{ "public", "customers", 'r', { "id", "name", "select" } },
|
||||
{ "public", "orders", 'r', { "id", "customer_id", "Total" } },
|
||||
{ "public", "order_view", 'v', { "id" } },
|
||||
{ "sales", "invoices", 'r', { "inv_id" } },
|
||||
{ "public", "My Table", 'r', { "a" } },
|
||||
};
|
||||
return c;
|
||||
}
|
||||
|
||||
std::vector<CompletionItem> complete(QString text)
|
||||
{
|
||||
int pos = text.indexOf('|');
|
||||
text.remove(pos, 1);
|
||||
SqlCompletionProvider p(testCatalog());
|
||||
return p.complete(analyzeSqlContext(text, pos));
|
||||
}
|
||||
|
||||
const CompletionItem* find(const std::vector<CompletionItem> &items, const QString &display, const QString &detail = QString())
|
||||
{
|
||||
for (auto &i : items)
|
||||
if (i.display == display && (detail.isEmpty() || i.detail == detail))
|
||||
return &i;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(SqlCompletionProvider, relationsAndSchemasAfterFrom)
|
||||
{
|
||||
auto items = complete("SELECT * FROM |");
|
||||
ASSERT_TRUE(find(items, "sales"));
|
||||
EXPECT_EQ(find(items, "sales")->kind, CompletionKind::Schema);
|
||||
ASSERT_TRUE(find(items, "customers"));
|
||||
EXPECT_EQ(find(items, "customers")->insertText, "customers");
|
||||
EXPECT_FALSE(find(items, "invoices"));
|
||||
}
|
||||
|
||||
TEST(SqlCompletionProvider, relationNeedingQuotesIsQuoted)
|
||||
{
|
||||
auto items = complete("SELECT * FROM my|");
|
||||
// prefix match is case-insensitive
|
||||
ASSERT_TRUE(find(items, "My Table"));
|
||||
EXPECT_EQ(find(items, "My Table")->insertText, "\"My Table\"");
|
||||
}
|
||||
|
||||
TEST(SqlCompletionProvider, relationsOfSchema)
|
||||
{
|
||||
auto items = complete("SELECT * FROM sales.|");
|
||||
ASSERT_EQ(items.size(), 1u);
|
||||
EXPECT_EQ(items[0].display, "invoices");
|
||||
}
|
||||
|
||||
TEST(SqlCompletionProvider, prefixFilters)
|
||||
{
|
||||
auto items = complete("SELECT * FROM ord|");
|
||||
EXPECT_TRUE(find(items, "orders"));
|
||||
EXPECT_TRUE(find(items, "order_view"));
|
||||
EXPECT_FALSE(find(items, "customers"));
|
||||
}
|
||||
|
||||
TEST(SqlCompletionProvider, reservedWordAndMixedCaseColumnsAreQuoted)
|
||||
{
|
||||
auto items = complete("SELECT | FROM customers, orders o");
|
||||
ASSERT_TRUE(find(items, "select"));
|
||||
EXPECT_EQ(find(items, "select")->insertText, "\"select\"");
|
||||
ASSERT_TRUE(find(items, "Total"));
|
||||
EXPECT_EQ(find(items, "Total")->insertText, "\"Total\"");
|
||||
ASSERT_TRUE(find(items, "name"));
|
||||
EXPECT_EQ(find(items, "name")->insertText, "name");
|
||||
}
|
||||
|
||||
TEST(SqlCompletionProvider, ambiguousColumnsAreQualified)
|
||||
{
|
||||
auto items = complete("SELECT | FROM customers, orders o");
|
||||
auto c = find(items, "id", "customers");
|
||||
auto o = find(items, "id", "o");
|
||||
ASSERT_TRUE(c);
|
||||
ASSERT_TRUE(o);
|
||||
EXPECT_TRUE(c->ambiguous);
|
||||
EXPECT_TRUE(o->ambiguous);
|
||||
EXPECT_EQ(c->insertText, "customers.id");
|
||||
EXPECT_EQ(o->insertText, "o.id");
|
||||
EXPECT_FALSE(find(items, "customer_id")->ambiguous);
|
||||
}
|
||||
|
||||
TEST(SqlCompletionProvider, qualifiedByAlias)
|
||||
{
|
||||
auto items = complete("SELECT o.| FROM customers c, orders o");
|
||||
ASSERT_EQ(items.size(), 3u);
|
||||
EXPECT_EQ(items[1].display, "customer_id");
|
||||
EXPECT_EQ(items[0].insertText, "id");
|
||||
}
|
||||
|
||||
TEST(SqlCompletionProvider, qualifiedByTableName)
|
||||
{
|
||||
auto items = complete("SELECT customers.na| FROM customers");
|
||||
ASSERT_EQ(items.size(), 1u);
|
||||
EXPECT_EQ(items[0].display, "name");
|
||||
}
|
||||
|
||||
TEST(SqlCompletionProvider, quotedPrefixIsCaseSensitive)
|
||||
{
|
||||
auto items = complete("SELECT \"To| FROM orders");
|
||||
ASSERT_EQ(items.size(), 1u);
|
||||
EXPECT_EQ(items[0].insertText, "\"Total\"");
|
||||
}
|
||||
|
||||
TEST(SqlCompletionProvider, unknownRelationGivesNoColumns)
|
||||
{
|
||||
auto items = complete("SELECT | FROM nonexistent");
|
||||
EXPECT_TRUE(items.empty() || !find(items, "id"));
|
||||
}
|
||||
|
||||
TEST(SqlCompletionProvider, qualifierWithoutMatchIsSchema)
|
||||
{
|
||||
auto items = complete("SELECT sales.| FROM customers");
|
||||
ASSERT_EQ(items.size(), 1u);
|
||||
EXPECT_EQ(items[0].display, "invoices");
|
||||
}
|
||||
|
||||
TEST(SqlCompletionProvider, insertColumnListOnlyTargetColumns)
|
||||
{
|
||||
auto items = complete("INSERT INTO orders (|");
|
||||
ASSERT_EQ(items.size(), 4u); // 3 columns + the relation name
|
||||
EXPECT_TRUE(find(items, "customer_id"));
|
||||
}
|
||||
|
||||
TEST(SqlCompletionProvider, updateSetColumns)
|
||||
{
|
||||
auto items = complete("UPDATE customers SET na| = 1");
|
||||
ASSERT_TRUE(find(items, "name"));
|
||||
}
|
||||
|
||||
TEST(SqlCompletionProvider, functionsOnlyWithPrefix)
|
||||
{
|
||||
CompletionCatalog c = testCatalog();
|
||||
c.functions = { { "pg_catalog", "count", 'a' }, { "pg_catalog", "coalesce", 'f' }, { "other", "cosmic", 'f' }, { "pg_catalog", "count", 'f' } };
|
||||
SqlCompletionProvider p(c);
|
||||
auto run = [&](QString text) {
|
||||
int pos = text.indexOf('|');
|
||||
text.remove(pos, 1);
|
||||
return p.complete(analyzeSqlContext(text, pos));
|
||||
};
|
||||
auto items = run("SELECT co| FROM customers");
|
||||
ASSERT_TRUE(find(items, "count"));
|
||||
EXPECT_EQ(find(items, "count")->insertText, "count(");
|
||||
EXPECT_EQ(find(items, "count")->kind, CompletionKind::Function);
|
||||
EXPECT_TRUE(find(items, "coalesce"));
|
||||
EXPECT_FALSE(find(items, "cosmic"));
|
||||
EXPECT_FALSE(find(run("SELECT | FROM customers"), "count"));
|
||||
EXPECT_FALSE(find(run("INSERT INTO customers (co|"), "count"));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<CompletionItem> completeJoin(QString text)
|
||||
{
|
||||
CompletionCatalog c = testCatalog();
|
||||
c.relations.push_back({ "public", "order_lines", 'r', { "order_id", "line_no", "sku" } });
|
||||
c.relations.push_back({ "public", "employees", 'r', { "id", "boss_id" } });
|
||||
c.foreignKeys = {
|
||||
{ "public", "orders", "public", "customers", { "customer_id" }, { "id" } },
|
||||
{ "public", "order_lines", "public", "orders", { "order_id", "line_no" }, { "id", "line_no" } },
|
||||
{ "sales", "invoices", "public", "customers", { "inv_id" }, { "id" } },
|
||||
{ "public", "employees", "public", "employees", { "boss_id" }, { "id" } },
|
||||
};
|
||||
int pos = text.indexOf('|');
|
||||
text.remove(pos, 1);
|
||||
SqlCompletionProvider p(c);
|
||||
return p.complete(analyzeSqlContext(text, pos));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(SqlCompletionProvider, joinFromChildToParent)
|
||||
{
|
||||
auto items = completeJoin("SELECT * FROM orders o JOIN |");
|
||||
auto *item = find(items, "customers c");
|
||||
ASSERT_TRUE(item);
|
||||
EXPECT_EQ(item->kind, CompletionKind::JoinTarget);
|
||||
EXPECT_EQ(item->insertText, "customers c ON o.customer_id = c.id");
|
||||
// plain relations are still offered
|
||||
bool plain = false;
|
||||
for (auto &i : items)
|
||||
plain |= i.kind == CompletionKind::Relation && i.display == "customers";
|
||||
EXPECT_TRUE(plain);
|
||||
}
|
||||
|
||||
TEST(SqlCompletionProvider, joinFromParentToChildMultiColumn)
|
||||
{
|
||||
auto items = completeJoin("SELECT * FROM orders JOIN order_l|");
|
||||
auto *item = find(items, "order_lines ol");
|
||||
ASSERT_TRUE(item);
|
||||
EXPECT_EQ(item->insertText, "order_lines ol ON orders.id = ol.order_id AND orders.line_no = ol.line_no");
|
||||
}
|
||||
|
||||
TEST(SqlCompletionProvider, joinAliasAvoidsClash)
|
||||
{
|
||||
auto items = completeJoin("SELECT * FROM orders c JOIN |");
|
||||
auto *item = find(items, "customers c2");
|
||||
ASSERT_TRUE(item);
|
||||
EXPECT_EQ(item->insertText, "customers c2 ON c.customer_id = c2.id");
|
||||
}
|
||||
|
||||
TEST(SqlCompletionProvider, joinOtherSchemaQualified)
|
||||
{
|
||||
auto items = completeJoin("SELECT * FROM customers JOIN |");
|
||||
auto *item = find(items, "invoices i");
|
||||
ASSERT_TRUE(item);
|
||||
EXPECT_EQ(item->insertText, "sales.invoices i ON customers.id = i.inv_id");
|
||||
}
|
||||
|
||||
TEST(SqlCompletionProvider, joinSelfReference)
|
||||
{
|
||||
auto items = completeJoin("SELECT * FROM employees e JOIN |");
|
||||
auto *item = find(items, "employees e2");
|
||||
ASSERT_TRUE(item);
|
||||
// both directions give a different condition
|
||||
int count = 0;
|
||||
for (auto &i : items)
|
||||
if (i.kind == CompletionKind::JoinTarget && i.display == "employees e2")
|
||||
++count;
|
||||
EXPECT_EQ(count, 2);
|
||||
}
|
||||
271
tests/pglabtests/tst_SqlContext.cpp
Normal file
271
tests/pglabtests/tst_SqlContext.cpp
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
#include <gtest/gtest.h>
|
||||
#include "SqlContext.h"
|
||||
#include "PrintTo_Qt.h"
|
||||
|
||||
using namespace testing;
|
||||
|
||||
namespace {
|
||||
|
||||
// '|' in the text marks the cursor
|
||||
SqlContext analyze(QString text)
|
||||
{
|
||||
int pos = text.indexOf('|');
|
||||
text.remove(pos, 1);
|
||||
return analyzeSqlContext(text, pos);
|
||||
}
|
||||
|
||||
QStringList names(const SqlContext &ctx)
|
||||
{
|
||||
QStringList l;
|
||||
for (auto &r : ctx.relations)
|
||||
l << (r.schema.isEmpty() ? r.name : r.schema + "." + r.name) + (r.alias.isEmpty() ? "" : "/" + r.alias);
|
||||
return l;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(SqlContext, emptyDocument)
|
||||
{
|
||||
auto ctx = analyzeSqlContext("", 0);
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::None);
|
||||
}
|
||||
|
||||
TEST(SqlContext, selectListColumnWithFromAfterCursor)
|
||||
{
|
||||
auto ctx = analyze("SELECT | FROM t");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::Column);
|
||||
EXPECT_EQ(ctx.clause, SqlClause::Select);
|
||||
EXPECT_EQ(names(ctx), QStringList({ "t" }));
|
||||
EXPECT_TRUE(ctx.prefix.isEmpty());
|
||||
}
|
||||
|
||||
TEST(SqlContext, prefixIsReplaceRange)
|
||||
{
|
||||
auto ctx = analyze("SELECT na| FROM t");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::Column);
|
||||
EXPECT_EQ(ctx.prefix, "na");
|
||||
EXPECT_EQ(ctx.prefixStart, 7);
|
||||
EXPECT_EQ(ctx.prefixLen, 2);
|
||||
EXPECT_EQ(names(ctx), QStringList({ "t" }));
|
||||
}
|
||||
|
||||
TEST(SqlContext, relationAfterFrom)
|
||||
{
|
||||
auto ctx = analyze("SELECT * FROM us|");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::Relation);
|
||||
EXPECT_EQ(ctx.prefix, "us");
|
||||
EXPECT_TRUE(ctx.relations.empty());
|
||||
}
|
||||
|
||||
TEST(SqlContext, relationAfterSchemaDot)
|
||||
{
|
||||
auto ctx = analyze("SELECT * FROM sch.|");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::Relation);
|
||||
EXPECT_EQ(ctx.qualifier, "sch");
|
||||
EXPECT_TRUE(ctx.relations.empty());
|
||||
}
|
||||
|
||||
TEST(SqlContext, noRelationAfterAlias)
|
||||
{
|
||||
auto ctx = analyze("SELECT * FROM t a|");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::None);
|
||||
}
|
||||
|
||||
TEST(SqlContext, relationAfterComma)
|
||||
{
|
||||
auto ctx = analyze("SELECT * FROM t, |");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::Relation);
|
||||
EXPECT_EQ(names(ctx), QStringList({ "t" }));
|
||||
}
|
||||
|
||||
TEST(SqlContext, aliasQualifier)
|
||||
{
|
||||
auto ctx = analyze("SELECT a.| FROM public.tbl AS a JOIN other o ON o.id = a.id");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::QualifiedColumn);
|
||||
EXPECT_EQ(ctx.qualifier, "a");
|
||||
EXPECT_EQ(names(ctx), QStringList({ "public.tbl/a", "other/o" }));
|
||||
}
|
||||
|
||||
TEST(SqlContext, schemaQualifiedTableQualifier)
|
||||
{
|
||||
auto ctx = analyze("SELECT s.t.c| FROM s.t");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::QualifiedColumn);
|
||||
EXPECT_EQ(ctx.qualifierSchema, "s");
|
||||
EXPECT_EQ(ctx.qualifier, "t");
|
||||
EXPECT_EQ(ctx.prefix, "c");
|
||||
}
|
||||
|
||||
TEST(SqlContext, onClause)
|
||||
{
|
||||
auto ctx = analyze("SELECT * FROM a x JOIN b y ON x.id = |");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::Column);
|
||||
EXPECT_EQ(ctx.clause, SqlClause::On);
|
||||
EXPECT_EQ(names(ctx), QStringList({ "a/x", "b/y" }));
|
||||
}
|
||||
|
||||
TEST(SqlContext, relationAfterJoin)
|
||||
{
|
||||
auto ctx = analyze("SELECT * FROM a x LEFT JOIN |");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::JoinTarget);
|
||||
EXPECT_EQ(ctx.clause, SqlClause::Join);
|
||||
EXPECT_EQ(names(ctx), QStringList({ "a/x" }));
|
||||
|
||||
EXPECT_EQ(analyze("SELECT * FROM a x JOIN b|").kind, CompletionKind::JoinTarget);
|
||||
EXPECT_EQ(analyze("SELECT * FROM a x JOIN sch.|").kind, CompletionKind::Relation);
|
||||
EXPECT_EQ(analyze("SELECT * FROM a x JOIN b y |").kind, CompletionKind::None);
|
||||
}
|
||||
|
||||
TEST(SqlContext, whereGroupOrderHaving)
|
||||
{
|
||||
EXPECT_EQ(analyze("SELECT * FROM t WHERE |").clause, SqlClause::Where);
|
||||
EXPECT_EQ(analyze("SELECT * FROM t GROUP BY |").clause, SqlClause::GroupBy);
|
||||
EXPECT_EQ(analyze("SELECT * FROM t ORDER BY |").clause, SqlClause::OrderBy);
|
||||
EXPECT_EQ(analyze("SELECT * FROM t GROUP BY a HAVING |").clause, SqlClause::Having);
|
||||
}
|
||||
|
||||
TEST(SqlContext, quotedPrefix)
|
||||
{
|
||||
auto ctx = analyze("SELECT \"My| FROM t");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::Column);
|
||||
EXPECT_TRUE(ctx.prefixWasQuoted);
|
||||
EXPECT_EQ(ctx.prefix, "My");
|
||||
EXPECT_EQ(ctx.prefixStart, 7);
|
||||
EXPECT_EQ(ctx.prefixLen, 3);
|
||||
EXPECT_EQ(names(ctx), QStringList({ "t" }));
|
||||
}
|
||||
|
||||
TEST(SqlContext, quotedRelationNamesKeepCase)
|
||||
{
|
||||
auto ctx = analyze("SELECT | FROM \"My Tab\" AS \"X\", Other O");
|
||||
EXPECT_EQ(names(ctx), QStringList({ "My Tab/X", "other/o" }));
|
||||
EXPECT_TRUE(ctx.relations[0].quoted);
|
||||
}
|
||||
|
||||
TEST(SqlContext, multipleStatements)
|
||||
{
|
||||
auto ctx = analyze("SELECT * FROM a; SELECT | FROM b; SELECT 1 FROM c");
|
||||
EXPECT_EQ(names(ctx), QStringList({ "b" }));
|
||||
}
|
||||
|
||||
TEST(SqlContext, subQueryHasOwnScopeAndSeesParent)
|
||||
{
|
||||
auto ctx = analyze("SELECT * FROM a WHERE a.id IN (SELECT | FROM b)");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::Column);
|
||||
EXPECT_EQ(names(ctx), QStringList({ "b", "a" }));
|
||||
}
|
||||
|
||||
TEST(SqlContext, derivedTableDoesNotSeeParent)
|
||||
{
|
||||
auto ctx = analyze("SELECT * FROM a, (SELECT | FROM b) d");
|
||||
EXPECT_EQ(names(ctx), QStringList({ "b" }));
|
||||
auto ctx2 = analyze("SELECT | FROM a, (SELECT 1 FROM b) d");
|
||||
EXPECT_EQ(names(ctx2), QStringList({ "a", "d/d" }));
|
||||
EXPECT_TRUE(ctx2.relations[1].derived);
|
||||
}
|
||||
|
||||
TEST(SqlContext, functionCallParenInheritsClause)
|
||||
{
|
||||
auto ctx = analyze("SELECT count(|) FROM t");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::Column);
|
||||
EXPECT_EQ(names(ctx), QStringList({ "t" }));
|
||||
}
|
||||
|
||||
TEST(SqlContext, cte)
|
||||
{
|
||||
auto ctx = analyze("WITH x (a) AS (SELECT 1), y AS MATERIALIZED (SELECT 2) SELECT | FROM x JOIN t ON true");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::Column);
|
||||
EXPECT_EQ(names(ctx), QStringList({ "x", "t", "x", "y" }));
|
||||
EXPECT_TRUE(ctx.relations[2].derived);
|
||||
}
|
||||
|
||||
TEST(SqlContext, unionSegments)
|
||||
{
|
||||
auto ctx = analyze("SELECT * FROM a UNION SELECT | FROM b");
|
||||
EXPECT_EQ(names(ctx), QStringList({ "b" }));
|
||||
}
|
||||
|
||||
TEST(SqlContext, noCompletionInCommentOrString)
|
||||
{
|
||||
EXPECT_EQ(analyze("SELECT * FROM t -- co|").kind, CompletionKind::None);
|
||||
EXPECT_EQ(analyze("SELECT * FROM t /* co| */").kind, CompletionKind::None);
|
||||
EXPECT_EQ(analyze("SELECT 'ab|c' FROM t").kind, CompletionKind::None);
|
||||
}
|
||||
|
||||
TEST(SqlContext, incompleteSql)
|
||||
{
|
||||
auto ctx = analyze("SELECT a.id, b.| FROM a, b WHERE");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::QualifiedColumn);
|
||||
EXPECT_EQ(ctx.qualifier, "b");
|
||||
EXPECT_EQ(names(ctx), QStringList({ "a", "b" }));
|
||||
}
|
||||
|
||||
TEST(SqlContext, updateSetAndWhere)
|
||||
{
|
||||
auto ctx = analyze("UPDATE sch.t AS x SET |");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::Column);
|
||||
EXPECT_EQ(ctx.clause, SqlClause::Set);
|
||||
EXPECT_EQ(names(ctx), QStringList({ "sch.t/x" }));
|
||||
|
||||
auto ctx2 = analyze("UPDATE t SET a = 1, b = 2 WHERE |");
|
||||
EXPECT_EQ(ctx2.clause, SqlClause::Where);
|
||||
EXPECT_EQ(names(ctx2), QStringList({ "t" }));
|
||||
}
|
||||
|
||||
TEST(SqlContext, updateRelation)
|
||||
{
|
||||
auto ctx = analyze("UPDATE |");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::Relation);
|
||||
}
|
||||
|
||||
TEST(SqlContext, updateFrom)
|
||||
{
|
||||
auto ctx = analyze("UPDATE t SET a = u.| FROM u WHERE t.id = u.id");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::QualifiedColumn);
|
||||
EXPECT_EQ(names(ctx), QStringList({ "t", "u" }));
|
||||
}
|
||||
|
||||
TEST(SqlContext, deleteFrom)
|
||||
{
|
||||
auto ctx = analyze("DELETE FROM |");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::Relation);
|
||||
auto ctx2 = analyze("DELETE FROM t WHERE |");
|
||||
EXPECT_EQ(ctx2.kind, CompletionKind::Column);
|
||||
EXPECT_EQ(names(ctx2), QStringList({ "t" }));
|
||||
}
|
||||
|
||||
TEST(SqlContext, insertIntoRelation)
|
||||
{
|
||||
EXPECT_EQ(analyze("INSERT INTO |").kind, CompletionKind::Relation);
|
||||
EXPECT_EQ(analyze("INSERT INTO sch.t|").kind, CompletionKind::Relation);
|
||||
}
|
||||
|
||||
TEST(SqlContext, insertColumnList)
|
||||
{
|
||||
auto ctx = analyze("INSERT INTO s.t (|");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::Column);
|
||||
EXPECT_EQ(ctx.clause, SqlClause::InsertInto);
|
||||
EXPECT_EQ(names(ctx), QStringList({ "s.t" }));
|
||||
|
||||
auto ctx2 = analyze("INSERT INTO t (a, b|) VALUES (1, 2)");
|
||||
EXPECT_EQ(ctx2.kind, CompletionKind::Column);
|
||||
EXPECT_EQ(ctx2.prefix, "b");
|
||||
EXPECT_EQ(names(ctx2), QStringList({ "t" }));
|
||||
}
|
||||
|
||||
TEST(SqlContext, insertValuesAndSelect)
|
||||
{
|
||||
auto ctx = analyze("INSERT INTO t (a) VALUES (|)");
|
||||
EXPECT_EQ(ctx.clause, SqlClause::Values);
|
||||
auto ctx2 = analyze("INSERT INTO t (a) SELECT | FROM u");
|
||||
EXPECT_EQ(ctx2.kind, CompletionKind::Column);
|
||||
EXPECT_EQ(names(ctx2), QStringList({ "u" }));
|
||||
}
|
||||
|
||||
TEST(SqlContext, returning)
|
||||
{
|
||||
auto ctx = analyze("INSERT INTO t (a) VALUES (1) RETURNING |");
|
||||
EXPECT_EQ(ctx.kind, CompletionKind::Column);
|
||||
EXPECT_EQ(ctx.clause, SqlClause::Returning);
|
||||
EXPECT_EQ(names(ctx), QStringList({ "t" }));
|
||||
EXPECT_EQ(analyze("DELETE FROM t RETURNING |").clause, SqlClause::Returning);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue