#include "sql_context.h" #include "c3_engine.h" #include "sql_syntax.h" #include #include #include #ifdef TOKEN_QUERY // Conflict with the winnt.h #undef TOKEN_QUERY #endif #include #include #include #include namespace NSQLComplete { template class TSpecializedSqlContextInference: public ISqlContextInference { private: using TDefaultYQLGrammar = TAntlrGrammar< NALADefaultAntlr4::SQLv1Antlr4Lexer, NALADefaultAntlr4::SQLv1Antlr4Parser>; using TAnsiYQLGrammar = TAntlrGrammar< NALAAnsiAntlr4::SQLv1Antlr4Lexer, NALAAnsiAntlr4::SQLv1Antlr4Parser>; using G = std::conditional_t< IsAnsiLexer, TAnsiYQLGrammar, TDefaultYQLGrammar>; public: explicit TSpecializedSqlContextInference(TLexerSupplier lexer) : Grammar(&GetSqlGrammar(IsAnsiLexer)) , Lexer_(lexer(/* ansi = */ IsAnsiLexer)) , C3(ComputeC3Config()) { } TCompletionContext Analyze(TCompletionInput input) override { TStringBuf prefix; if (!GetC3Prefix(input, &prefix)) { return {}; } auto candidates = C3.Complete(prefix); return { .Keywords = SiftedKeywords(candidates), .IsTypeName = IsTypeNameMatched(candidates), }; } private: IC3Engine::TConfig ComputeC3Config() { return { .IgnoredTokens = ComputeIgnoredTokens(), .PreferredRules = ComputePreferredRules(), }; } std::unordered_set ComputeIgnoredTokens() { auto ignoredTokens = Grammar->GetAllTokens(); for (auto keywordToken : Grammar->GetKeywordTokens()) { ignoredTokens.erase(keywordToken); } return ignoredTokens; } std::unordered_set ComputePreferredRules() { const auto& keywordRules = Grammar->GetKeywordRules(); const auto& typeNameRules = Grammar->GetTypeNameRules(); std::unordered_set preferredRules; // Excludes tokens obtained from keyword rules preferredRules.insert(std::begin(keywordRules), std::end(keywordRules)); preferredRules.insert(std::begin(typeNameRules), std::end(typeNameRules)); return preferredRules; } bool GetC3Prefix(TCompletionInput input, TStringBuf* prefix) { *prefix = input.Text.Head(input.CursorPosition); TVector statements; NYql::TIssues issues; if (!NSQLTranslationV1::SplitQueryToStatements( TString(*prefix) + (prefix->EndsWith(';') ? ";" : ""), Lexer_, statements, issues, /* file = */ "", /* areBlankSkipped = */ false)) { return false; } if (statements.empty()) { return true; } *prefix = prefix->Last(statements.back().size()); return true; } TVector SiftedKeywords(const TC3Candidates& candidates) { const auto& vocabulary = Grammar->GetVocabulary(); const auto& keywordTokens = Grammar->GetKeywordTokens(); TVector keywords; for (const auto& token : candidates.Tokens) { if (keywordTokens.contains(token.Number)) { keywords.emplace_back(vocabulary.getDisplayName(token.Number)); } } return keywords; } bool IsTypeNameMatched(const TC3Candidates& candidates) { const auto& typeNameRules = Grammar->GetTypeNameRules(); return FindIf(candidates.Rules, [&](const TMatchedRule& rule) { return Find(typeNameRules, rule.Index) != std::end(typeNameRules); }) != std::end(candidates.Rules); } const ISqlGrammar* Grammar; NSQLTranslation::ILexer::TPtr Lexer_; TC3Engine C3; }; class TSqlContextInference: public ISqlContextInference { public: explicit TSqlContextInference(TLexerSupplier lexer) : DefaultEngine(lexer) , AnsiEngine(lexer) { } TCompletionContext Analyze(TCompletionInput input) override { auto isAnsiLexer = IsAnsiQuery(TString(input.Text)); auto& engine = GetSpecializedEngine(isAnsiLexer); return engine.Analyze(std::move(input)); } private: ISqlContextInference& GetSpecializedEngine(bool isAnsiLexer) { if (isAnsiLexer) { return AnsiEngine; } return DefaultEngine; } TSpecializedSqlContextInference DefaultEngine; TSpecializedSqlContextInference AnsiEngine; }; ISqlContextInference::TPtr MakeSqlContextInference(TLexerSupplier lexer) { return TSqlContextInference::TPtr(new TSqlContextInference(lexer)); } } // namespace NSQLComplete