#include #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); }