aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvvvv <vvvv@ydb.tech>2022-07-28 10:19:19 +0300
committervvvv <vvvv@ydb.tech>2022-07-28 10:19:19 +0300
commitb287b481a014232b54316263eb5ba6b2c3cf570c (patch)
treeec81a46e0a92787f17f221cd55511e5a38002fe8
parent1c4ba5fd283d0450ae330335957c84bb1d6f779f (diff)
downloadydb-b287b481a014232b54316263eb5ba6b2c3cf570c.tar.gz
move formatter library
-rw-r--r--CMakeLists.darwin.txt1
-rw-r--r--CMakeLists.linux.txt1
-rw-r--r--ydb/library/yql/sql/CMakeLists.txt1
-rw-r--r--ydb/library/yql/sql/v1/format/CMakeLists.txt45
-rw-r--r--ydb/library/yql/sql/v1/format/sql_format.cpp1908
-rw-r--r--ydb/library/yql/sql/v1/format/sql_format.h32
-rw-r--r--ydb/library/yql/sql/v1/format/sql_format_ut.cpp707
7 files changed, 2695 insertions, 0 deletions
diff --git a/CMakeLists.darwin.txt b/CMakeLists.darwin.txt
index 7c562c5e31f..d72b27c4ac3 100644
--- a/CMakeLists.darwin.txt
+++ b/CMakeLists.darwin.txt
@@ -583,6 +583,7 @@ add_subdirectory(ydb/library/yql/parser/proto_ast/gen/v1_proto)
add_subdirectory(ydb/library/yql/parser/proto_ast/gen/v1_ansi)
add_subdirectory(ydb/library/yql/sql/v1/lexer)
add_subdirectory(ydb/library/yql/sql/v1/proto_parser)
+add_subdirectory(ydb/library/yql/sql/v1/format)
add_subdirectory(ydb/library/yql/core/common_opt)
add_subdirectory(ydb/library/yql/core/peephole_opt)
add_subdirectory(ydb/library/yql/core/type_ann)
diff --git a/CMakeLists.linux.txt b/CMakeLists.linux.txt
index cbb113504ac..2cb740f3848 100644
--- a/CMakeLists.linux.txt
+++ b/CMakeLists.linux.txt
@@ -587,6 +587,7 @@ add_subdirectory(ydb/library/yql/parser/proto_ast/gen/v1_proto)
add_subdirectory(ydb/library/yql/parser/proto_ast/gen/v1_ansi)
add_subdirectory(ydb/library/yql/sql/v1/lexer)
add_subdirectory(ydb/library/yql/sql/v1/proto_parser)
+add_subdirectory(ydb/library/yql/sql/v1/format)
add_subdirectory(ydb/library/yql/core/common_opt)
add_subdirectory(ydb/library/yql/core/peephole_opt)
add_subdirectory(ydb/library/yql/core/type_ann)
diff --git a/ydb/library/yql/sql/CMakeLists.txt b/ydb/library/yql/sql/CMakeLists.txt
index 24b0c63d407..9cb4ffd0799 100644
--- a/ydb/library/yql/sql/CMakeLists.txt
+++ b/ydb/library/yql/sql/CMakeLists.txt
@@ -16,6 +16,7 @@ target_link_libraries(library-yql-sql PUBLIC
yql-sql-v0
sql-v0-lexer
yql-sql-v1
+ sql-v1-format
sql-v1-lexer
sql-v1-proto_parser
library-yql-utils
diff --git a/ydb/library/yql/sql/v1/format/CMakeLists.txt b/ydb/library/yql/sql/v1/format/CMakeLists.txt
new file mode 100644
index 00000000000..9aabc1be21a
--- /dev/null
+++ b/ydb/library/yql/sql/v1/format/CMakeLists.txt
@@ -0,0 +1,45 @@
+
+# This file was gererated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(sql-v1-format)
+target_link_libraries(sql-v1-format PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+ library-cpp-resource
+ yql-parser-lexer_common
+ yql-sql-settings
+ sql-v1-lexer
+ sql-v1-proto_parser
+ cpp-protobuf-util
+)
+target_sources(sql-v1-format PRIVATE
+ ${CMAKE_SOURCE_DIR}/ydb/library/yql/sql/v1/format/sql_format.cpp
+)
+
+add_global_library_for(sql-v1-format.global sql-v1-format)
+target_link_libraries(sql-v1-format.global PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+ library-cpp-resource
+ yql-parser-lexer_common
+ yql-sql-settings
+ sql-v1-lexer
+ sql-v1-proto_parser
+ cpp-protobuf-util
+)
+target_sources(sql-v1-format.global PRIVATE
+ ${CMAKE_BINARY_DIR}/ydb/library/yql/sql/v1/format/8b41f60c03c12372ee6c489297948988.cpp
+)
+resources(sql-v1-format.global
+ ${CMAKE_BINARY_DIR}/ydb/library/yql/sql/v1/format/8b41f60c03c12372ee6c489297948988.cpp
+ INPUTS
+ ${CMAKE_SOURCE_DIR}/ydb/library/yql/sql/v1/SQLv1.g.in
+ KEYS
+ SQLv1.g.in
+)
diff --git a/ydb/library/yql/sql/v1/format/sql_format.cpp b/ydb/library/yql/sql/v1/format/sql_format.cpp
new file mode 100644
index 00000000000..7083c153885
--- /dev/null
+++ b/ydb/library/yql/sql/v1/format/sql_format.cpp
@@ -0,0 +1,1908 @@
+#include "sql_format.h"
+
+#include <ydb/library/yql/parser/lexer_common/lexer.h>
+
+#include <ydb/library/yql/sql/v1/lexer/lexer.h>
+#include <ydb/library/yql/sql/v1/proto_parser/proto_parser.h>
+
+#include <ydb/library/yql/parser/proto_ast/gen/v1_proto/SQLv1Parser.pb.h>
+
+#include <library/cpp/protobuf/util/simple_reflection.h>
+#include <library/cpp/resource/resource.h>
+
+#include <util/string/builder.h>
+#include <util/string/split.h>
+#include <util/string/strip.h>
+#include <util/string/subst.h>
+#include <util/generic/hash_set.h>
+
+
+namespace NSQLFormat {
+
+namespace {
+
+using namespace NSQLv1Generated;
+
+using NSQLTranslation::TParsedTokenList;
+using TTokenIterator = TParsedTokenList::const_iterator;
+
+TTokenIterator SkipWS(TTokenIterator curr, TTokenIterator end) {
+ while (curr != end && curr->Name == "WS") {
+ ++curr;
+ }
+ return curr;
+}
+
+TTokenIterator SkipWSOrComment(TTokenIterator curr, TTokenIterator end) {
+ while (curr != end && (curr->Name == "WS" || curr->Name == "COMMENT")) {
+ ++curr;
+ }
+ return curr;
+}
+
+bool Validate(const TParsedTokenList& query, const TParsedTokenList& formattedQuery) {
+ auto in = query.begin();
+ auto out = formattedQuery.begin();
+ auto inEnd = query.end();
+ auto outEnd = formattedQuery.end();
+
+ while (in != inEnd && out != outEnd) {
+ in = SkipWS(in, inEnd);
+ out = SkipWS(out, outEnd);
+ if (in != inEnd && out != outEnd) {
+ if (in->Name != out->Name) {
+ return false;
+ }
+ if (AsciiEqualsIgnoreCase(in->Name, in->Content)) {
+ if (!AsciiEqualsIgnoreCase(in->Content, out->Content)) {
+ return false;
+ }
+ } else {
+ if (in->Content != out->Content) {
+ return false;
+ }
+ }
+ ++in;
+ ++out;
+ }
+ }
+ in = SkipWS(in, inEnd);
+ out = SkipWS(out, outEnd);
+ return in == inEnd && out == outEnd;
+}
+
+enum EParenType {
+ Open,
+ Close,
+ None
+};
+
+using TAdvanceCallback = std::function<EParenType(TTokenIterator& curr, TTokenIterator end)>;
+
+TTokenIterator SkipToNextBalanced(TTokenIterator begin, TTokenIterator end, const TAdvanceCallback& advance) {
+ i64 level = 0;
+ TTokenIterator curr = begin;
+ while (curr != end) {
+ switch (advance(curr, end)) {
+ case EParenType::Open: {
+ ++level;
+ break;
+ }
+ case EParenType::Close: {
+ --level;
+ if (level < 0) {
+ return end;
+ } else if (level == 0) {
+ return curr;
+ }
+ break;
+ }
+ case EParenType::None:
+ break;
+ }
+ }
+ return curr;
+}
+
+TTokenIterator GetNextStatementBegin(TTokenIterator begin, TTokenIterator end) {
+ TAdvanceCallback advanceLambdaBody = [](TTokenIterator& curr, TTokenIterator end) -> EParenType {
+ Y_UNUSED(end);
+ if (curr->Name == "LBRACE_CURLY") {
+ ++curr;
+ return EParenType::Open;
+ } else if (curr->Name == "RBRACE_CURLY") {
+ ++curr;
+ return EParenType::Close;
+ } else {
+ ++curr;
+ return EParenType::None;
+ }
+ };
+
+ TAdvanceCallback advanceAction = [](TTokenIterator& curr, TTokenIterator end) -> EParenType {
+ auto tmp = curr;
+ if (curr->Name == "DEFINE") {
+ ++curr;
+ curr = SkipWSOrComment(curr, end);
+ if (curr != end && (curr->Name == "ACTION" || curr->Name == "SUBQUERY")) {
+ ++curr;
+ return EParenType::Open;
+ }
+ } else if (curr->Name == "END") {
+ ++curr;
+ curr = SkipWSOrComment(curr, end);
+ if (curr != end && curr->Name == "DEFINE") {
+ ++curr;
+ return EParenType::Close;
+ }
+ }
+
+ curr = tmp;
+ ++curr;
+ return EParenType::None;
+ };
+
+ TAdvanceCallback advanceInlineAction = [](TTokenIterator& curr, TTokenIterator end) -> EParenType {
+ auto tmp = curr;
+ if (curr->Name == "DO") {
+ ++curr;
+ curr = SkipWSOrComment(curr, end);
+ if (curr != end && curr->Name == "BEGIN") {
+ ++curr;
+ return EParenType::Open;
+ }
+ } else if (curr->Name == "END") {
+ ++curr;
+ curr = SkipWSOrComment(curr, end);
+ if (curr != end && curr->Name == "DO") {
+ ++curr;
+ return EParenType::Close;
+ }
+ }
+
+ curr = tmp;
+ ++curr;
+ return EParenType::None;
+ };
+
+ TTokenIterator curr = begin;
+ while (curr != end) {
+ bool matched = false;
+ for (auto cb : {advanceLambdaBody, advanceAction, advanceInlineAction}) {
+ TTokenIterator tmp = curr;
+ if (cb(tmp, end) == EParenType::Open) {
+ curr = SkipToNextBalanced(curr, end, cb);
+ matched = true;
+ if (curr == end) {
+ return curr;
+ }
+ }
+ }
+ if (matched) {
+ continue;
+ }
+ if (curr->Name == "SEMICOLON") {
+ ++curr;
+ break;
+ }
+ ++curr;
+ }
+
+ return curr;
+}
+
+void SplitByStatements(TTokenIterator begin, TTokenIterator end, TVector<TTokenIterator>& output) {
+ output.clear();
+ if (begin == end) {
+ return;
+ }
+ output.push_back(begin);
+ auto curr = begin;
+ while (curr != end) {
+ curr = GetNextStatementBegin(curr, end);
+ output.push_back(curr);
+ }
+}
+
+enum class EScope {
+ Default,
+ TypeName,
+ Identifier,
+ DoubleQuestion
+};
+
+THashSet<TString> GetKeywords() {
+ TString grammar;
+ Y_ENSURE(NResource::FindExact("SQLv1.g.in", &grammar));
+ THashSet<TString> res;
+ TVector<TString> lines;
+ Split(grammar, "\n", lines);
+ for (auto s : lines) {
+ s = StripString(s);
+ if (s.StartsWith("//")) {
+ continue;
+ }
+
+ auto pos1 = s.find(':');
+ auto pos2 = s.find(';');
+ if (pos1 == TString::npos || pos2 == TString::npos || pos2 < pos1 + 2) {
+ continue;
+ }
+
+ auto before = s.substr(0, pos1);
+ auto after = s.substr(pos1 + 1, pos2 - pos1 - 1);
+ SubstGlobal(after, " ", "");
+ SubstGlobal(after, "'", "");
+ if (after == before) {
+ //Cerr << before << "\n";
+ res.insert(before);
+ }
+ }
+
+ return res;
+}
+
+class TVisitor;
+using TFunctor = std::function<void(TVisitor&, const NProtoBuf::Message& msg)>;
+
+struct TStaticData {
+ TStaticData();
+ static const TStaticData& GetInstance() {
+ return *Singleton<TStaticData>();
+ }
+
+ THashSet<TString> Keywords;
+ THashMap<const NProtoBuf::Descriptor*, EScope> ScopeDispatch;
+ THashMap<const NProtoBuf::Descriptor*, TFunctor> VisitDispatch;
+};
+
+class TVisitor {
+ friend struct TStaticData;
+public:
+ TVisitor(const TVector<NSQLTranslation::TParsedToken>& comments)
+ : StaticData(TStaticData::GetInstance())
+ , Comments(comments)
+ {
+ }
+
+ TString Process(const NProtoBuf::Message& msg) {
+ Scopes.push_back(EScope::Default);
+ Visit(msg);
+ for (; LastComment < Comments.size(); ++LastComment) {
+ const auto text = Comments[LastComment].Content;
+ AddComment(text);
+ }
+
+ return SB;
+ }
+
+private:
+ void Out(TStringBuf s) {
+ for (ui32 i = 0; i < s.Size(); ++i) {
+ Out(s[i], i == 0);
+ }
+ }
+
+ void Out(char c, bool useIndent = true) {
+ if (c == '\n' || c == '\r') {
+ SB << c;
+ if (!(c == '\n' && !SB.empty() && SB.back() == '\r')) {
+ // do not increase OutLine if \n is preceded by \r
+ // this way we handle \r, \n, or \r\n as single new line
+ ++OutLine;
+ }
+ OutColumn = 0;
+ } else {
+ if (!OutColumn && useIndent) {
+ ui32 indent = (CurrentIndent >= 0) ? CurrentIndent : 0;
+ for (ui32 i = 0; i < indent; ++i) {
+ SB << ' ';
+ }
+ }
+
+ SB << c;
+ ++OutColumn;
+ }
+ }
+
+ void NewLine() {
+ if (OutColumn) {
+ Out('\n');
+ }
+ }
+
+ void AddComment(TStringBuf text) {
+ if (text.StartsWith("--") && !SB.Empty() && SB.back() == '-') {
+ Out(' ');
+ }
+
+ Out(text);
+ }
+
+ void Visit(const NProtoBuf::Message& msg) {
+ const NProtoBuf::Descriptor* descr = msg.GetDescriptor();
+ //Cerr << descr->name() << "\n";
+ auto scopePtr = StaticData.ScopeDispatch.FindPtr(descr);
+ if (descr == TRule_invoke_expr::GetDescriptor()) {
+ AfterInvokeExpr = true;
+ }
+
+ if (scopePtr) {
+ if (*scopePtr == EScope::TypeName) {
+ ++InsideType;
+ }
+
+ Scopes.push_back(*scopePtr);
+ }
+
+ auto funcPtr = StaticData.VisitDispatch.FindPtr(descr);
+ if (funcPtr) {
+ (*funcPtr)(*this, msg);
+ } else {
+ VisitAllFields(descr, msg);
+ }
+
+ if (scopePtr) {
+ if (*scopePtr == EScope::TypeName) {
+ --InsideType;
+ }
+
+ Scopes.pop_back();
+ }
+ }
+
+ void VisitPragma(const TRule_pragma_stmt& msg) {
+ PosFromToken(msg.GetToken1());
+ NewLine();
+ VisitKeyword(msg.GetToken1());
+ auto prefix = msg.GetRule_opt_id_prefix_or_type2();
+ if (prefix.HasBlock1()) {
+ Visit(prefix.GetBlock1().GetRule_an_id_or_type1());
+ VisitKeyword(prefix.GetBlock1().GetToken2());
+ AfterDot = true;
+ }
+
+ Visit(msg.GetRule_an_id3());
+ if (msg.GetBlock4().HasAlt2()) {
+ AfterInvokeExpr = true;
+ const auto& alt2 = msg.GetBlock4().GetAlt2();
+ VisitKeyword(alt2.GetToken1());
+ Visit(alt2.GetRule_pragma_value2());
+ for (const auto& b : alt2.GetBlock3()) {
+ Visit(b);
+ }
+
+ VisitKeyword(alt2.GetToken4());
+ } else {
+ Visit(msg.GetBlock4());
+ }
+ }
+
+ void VisitSelect(const TRule_select_stmt& msg) {
+ const auto& paren = msg.GetRule_select_kind_parenthesis1();
+ if (paren.Alt_case() == TRule_select_kind_parenthesis::kAltSelectKindParenthesis1) {
+ const auto& partial = paren.GetAlt_select_kind_parenthesis1().GetRule_select_kind_partial1();
+ const auto& kind = partial.GetRule_select_kind1();
+ if (kind.HasBlock1()) { // DISCARD
+ PosFromToken(kind.GetBlock1().GetToken1());
+ } else {
+ switch (kind.GetBlock2().Alt_case()) {
+ case TRule_select_kind_TBlock2::kAlt1:
+ PosFromToken(kind.GetBlock2().GetAlt1().GetRule_process_core1().GetToken1());
+ break;
+ case TRule_select_kind_TBlock2::kAlt2:
+ PosFromToken(kind.GetBlock2().GetAlt2().GetRule_reduce_core1().GetToken1());
+ break;
+ case TRule_select_kind_TBlock2::kAlt3: {
+ const auto& selCore = kind.GetBlock2().GetAlt3().GetRule_select_core1();
+ if (selCore.HasBlock1()) {
+ PosFromToken(selCore.GetBlock1().GetToken1());
+ } else {
+ PosFromToken(selCore.GetToken2());
+ }
+
+ break;
+ }
+
+ default:
+ ythrow yexception() << "Alt is not supported";
+ }
+ }
+ } else {
+ PosFromToken(paren.GetAlt_select_kind_parenthesis2().GetToken1());
+ }
+
+ NewLine();
+ Visit(msg.GetRule_select_kind_parenthesis1());
+ for (const auto& block : msg.GetBlock2()) {
+ NewLine();
+ Visit(block.GetRule_select_op1());
+ NewLine();
+ Visit(block.GetRule_select_kind_parenthesis2());
+ }
+ }
+
+ void VisitNamedNodes(const TRule_named_nodes_stmt& msg) {
+ PosFromToken(msg.GetRule_bind_parameter_list1().GetRule_bind_parameter1().GetToken1());
+ NewLine();
+ Visit(msg.GetRule_bind_parameter_list1());
+ Visit(msg.GetToken2());
+ switch (msg.GetBlock3().Alt_case()) {
+ case TRule_named_nodes_stmt::TBlock3::kAlt1: {
+ const auto& alt = msg.GetBlock3().GetAlt1();
+ Visit(alt);
+ break;
+ }
+
+ case TRule_named_nodes_stmt::TBlock3::kAlt2: {
+ const auto& alt = msg.GetBlock3().GetAlt2();
+ const auto& subselect = alt.GetRule_subselect_stmt1();
+ switch (subselect.GetBlock1().Alt_case()) {
+ case TRule_subselect_stmt::TBlock1::kAlt1: {
+ const auto& alt = subselect.GetBlock1().GetAlt1();
+ Visit(alt.GetToken1());
+ NewLine();
+ PushCurrentIndent();
+ Visit(alt.GetRule_select_stmt2());
+ PopCurrentIndent();
+ NewLine();
+ Visit(alt.GetToken3());
+ break;
+ }
+
+ case TRule_subselect_stmt::TBlock1::kAlt2: {
+ const auto& alt = subselect.GetBlock1().GetAlt2();
+ NewLine();
+ PushCurrentIndent();
+ Visit(alt);
+ PopCurrentIndent();
+ break;
+ }
+
+ default:
+ ythrow yexception() << "Alt is not supported";
+ }
+
+ break;
+ }
+
+ default:
+ ythrow yexception() << "Alt is not supported";
+ }
+ }
+
+ void VisitCreateTable(const TRule_create_table_stmt& msg) {
+ PosFromToken(msg.GetToken1());
+ NewLine();
+ Visit(msg.GetToken1());
+ Visit(msg.GetToken2());
+ Visit(msg.GetRule_simple_table_ref3());
+ Visit(msg.GetToken4());
+ PushCurrentIndent();
+ NewLine();
+ Visit(msg.GetRule_create_table_entry5());
+ for (const auto& b : msg.GetBlock6()) {
+ Visit(b.GetToken1());
+ NewLine();
+ Visit(b.GetRule_create_table_entry2());
+ }
+
+ PopCurrentIndent();
+ NewLine();
+ Visit(msg.GetToken7());
+ if (msg.HasBlock8()) {
+ NewLine();
+ Visit(msg.GetBlock8());
+ }
+ }
+
+ void VisitDropTable(const TRule_drop_table_stmt& msg) {
+ PosFromToken(msg.GetToken1());
+ NewLine();
+ VisitAllFields(TRule_drop_table_stmt::GetDescriptor(), msg);
+ }
+
+ void VisitUse(const TRule_use_stmt& msg) {
+ PosFromToken(msg.GetToken1());
+ NewLine();
+ VisitAllFields(TRule_use_stmt::GetDescriptor(), msg);
+ }
+
+ void VisitIntoTable(const TRule_into_table_stmt& msg) {
+ switch (msg.GetBlock1().Alt_case()) {
+ case TRule_into_table_stmt_TBlock1::AltCase::kAlt1:
+ PosFromToken(msg.GetBlock1().GetAlt1().GetToken1());
+ break;
+ case TRule_into_table_stmt_TBlock1::AltCase::kAlt2:
+ PosFromToken(msg.GetBlock1().GetAlt2().GetToken1());
+ break;
+ case TRule_into_table_stmt_TBlock1::AltCase::kAlt3:
+ PosFromToken(msg.GetBlock1().GetAlt3().GetToken1());
+ break;
+ case TRule_into_table_stmt_TBlock1::AltCase::kAlt4:
+ PosFromToken(msg.GetBlock1().GetAlt4().GetToken1());
+ break;
+ case TRule_into_table_stmt_TBlock1::AltCase::kAlt5:
+ PosFromToken(msg.GetBlock1().GetAlt5().GetToken1());
+ break;
+ case TRule_into_table_stmt_TBlock1::AltCase::kAlt6:
+ PosFromToken(msg.GetBlock1().GetAlt6().GetToken1());
+ break;
+ default:
+ ythrow yexception() << "Alt is not supported";
+ }
+
+ NewLine();
+ VisitAllFields(TRule_into_table_stmt::GetDescriptor(), msg);
+ }
+
+ void VisitCommit(const TRule_commit_stmt& msg) {
+ PosFromToken(msg.GetToken1());
+ NewLine();
+ VisitAllFields(TRule_commit_stmt::GetDescriptor(), msg);
+ }
+
+ void VisitUpdate(const TRule_update_stmt& msg) {
+ PosFromToken(msg.GetToken1());
+ NewLine();
+ Visit(msg.GetToken1());
+ Visit(msg.GetRule_simple_table_ref2());
+ switch (msg.GetBlock3().Alt_case()) {
+ case TRule_update_stmt_TBlock3::kAlt1: {
+ const auto& alt = msg.GetBlock3().GetAlt1();
+ NewLine();
+ PushCurrentIndent();
+ Visit(alt.GetToken1());
+ const auto& choice = alt.GetRule_set_clause_choice2();
+ NewLine();
+
+ PushCurrentIndent();
+ switch (choice.Alt_case()) {
+ case TRule_set_clause_choice::kAltSetClauseChoice1: {
+ const auto& clauses = choice.GetAlt_set_clause_choice1().GetRule_set_clause_list1();
+ Visit(clauses.GetRule_set_clause1());
+ for (auto& block : clauses.GetBlock2()) {
+ Visit(block.GetToken1());
+ NewLine();
+ Visit(block.GetRule_set_clause2());
+ }
+
+ break;
+ }
+ case TRule_set_clause_choice::kAltSetClauseChoice2: {
+ const auto& multiColumn = choice.GetAlt_set_clause_choice2().GetRule_multiple_column_assignment1();
+ const auto& targets = multiColumn.GetRule_set_target_list1();
+ Visit(targets.GetToken1());
+ NewLine();
+ PushCurrentIndent();
+ Visit(targets.GetRule_set_target2());
+ for (auto& block : targets.GetBlock3()) {
+ Visit(block.GetToken1());
+ NewLine();
+ Visit(block.GetRule_set_target2());
+ }
+
+ NewLine();
+ PopCurrentIndent();
+ Visit(targets.GetToken4());
+ Visit(multiColumn.GetToken2());
+ Visit(multiColumn.GetToken3());
+ NewLine();
+ PushCurrentIndent();
+ const auto& simpleValues = multiColumn.GetRule_simple_values_source4();
+ switch (simpleValues.Alt_case()) {
+ case TRule_simple_values_source::kAltSimpleValuesSource1: {
+ const auto& exprs = simpleValues.GetAlt_simple_values_source1().GetRule_expr_list1();
+ Visit(exprs.GetRule_expr1());
+ for (const auto& block : exprs.GetBlock2()) {
+ Visit(block.GetToken1());
+ NewLine();
+ Visit(block.GetRule_expr2());
+ }
+
+ break;
+ }
+ case TRule_simple_values_source::kAltSimpleValuesSource2: {
+ Visit(simpleValues.GetAlt_simple_values_source2());
+ break;
+ }
+ default:
+ ythrow yexception() << "Alt is not supported";
+ }
+
+ PopCurrentIndent();
+ NewLine();
+ Visit(multiColumn.GetToken5());
+ break;
+ }
+ default:
+ ythrow yexception() << "Alt is not supported";
+ }
+
+ PopCurrentIndent();
+ if (alt.HasBlock3()) {
+ NewLine();
+ Visit(alt.GetBlock3());
+ }
+
+ PopCurrentIndent();
+ break;
+ }
+ case TRule_update_stmt_TBlock3::kAlt2: {
+ const auto& alt = msg.GetBlock3().GetAlt2();
+ NewLine();
+ PushCurrentIndent();
+ Visit(alt.GetToken1());
+ Visit(alt.GetRule_into_values_source2());
+ PopCurrentIndent();
+ break;
+ }
+ default:
+ ythrow yexception() << "Alt is not supported";
+ }
+ }
+
+ void VisitDelete(const TRule_delete_stmt& msg) {
+ PosFromToken(msg.GetToken1());
+ NewLine();
+ Visit(msg.GetToken1());
+ Visit(msg.GetToken2());
+ Visit(msg.GetRule_simple_table_ref3());
+ if (msg.HasBlock4()) {
+ switch (msg.GetBlock4().Alt_case()) {
+ case TRule_delete_stmt_TBlock4::kAlt1: {
+ const auto& alt = msg.GetBlock4().GetAlt1();
+ NewLine();
+ PushCurrentIndent();
+ Visit(alt);
+ PopCurrentIndent();
+ break;
+ }
+ case TRule_delete_stmt_TBlock4::kAlt2: {
+ const auto& alt = msg.GetBlock4().GetAlt2();
+ NewLine();
+ PushCurrentIndent();
+ Visit(alt);
+ PopCurrentIndent();
+ break;
+ }
+ default:
+ ythrow yexception() << "Alt is not supported";
+ }
+ }
+ }
+
+ void VisitRollback(const TRule_rollback_stmt& msg) {
+ PosFromToken(msg.GetToken1());
+ NewLine();
+ VisitAllFields(TRule_rollback_stmt::GetDescriptor(), msg);
+ }
+
+ void VisitDeclare(const TRule_declare_stmt& msg) {
+ PosFromToken(msg.GetToken1());
+ NewLine();
+ VisitAllFields(TRule_declare_stmt::GetDescriptor(), msg);
+ }
+
+ void VisitImport(const TRule_import_stmt& msg) {
+ PosFromToken(msg.GetToken1());
+ NewLine();
+ VisitAllFields(TRule_import_stmt::GetDescriptor(), msg);
+ }
+
+ void VisitExport(const TRule_export_stmt& msg) {
+ PosFromToken(msg.GetToken1());
+ NewLine();
+ VisitAllFields(TRule_export_stmt::GetDescriptor(), msg);
+ }
+
+ void VisitAlterTable(const TRule_alter_table_stmt& msg) {
+ PosFromToken(msg.GetToken1());
+ NewLine();
+ VisitKeyword(msg.GetToken1());
+ VisitKeyword(msg.GetToken2());
+ Visit(msg.GetRule_simple_table_ref3());
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetRule_alter_table_action4());
+ for (auto& b : msg.GetBlock5()) {
+ Visit(b.GetToken1());
+ NewLine();
+ Visit(b.GetRule_alter_table_action2());
+ }
+
+ PopCurrentIndent();
+ }
+
+ void VisitDo(const TRule_do_stmt& msg) {
+ PosFromToken(msg.GetToken1());
+ NewLine();
+ VisitKeyword(msg.GetToken1());
+ switch (msg.GetBlock2().Alt_case()) {
+ case TRule_do_stmt_TBlock2::kAlt1: { // CALL
+ const auto& alt = msg.GetBlock2().GetAlt1().GetRule_call_action1();
+ Visit(alt.GetBlock1());
+ AfterInvokeExpr = true;
+ Visit(alt.GetToken2());
+ if (alt.HasBlock3()) {
+ Visit(alt.GetBlock3());
+ }
+
+ Visit(alt.GetToken4());
+ break;
+ }
+ case TRule_do_stmt_TBlock2::kAlt2: { // INLINE
+ const auto& alt = msg.GetBlock2().GetAlt2().GetRule_inline_action1();
+ VisitKeyword(alt.GetToken1());
+ PushCurrentIndent();
+ NewLine();
+ Visit(alt.GetRule_define_action_or_subquery_body2());
+ PopCurrentIndent();
+ NewLine();
+ VisitKeyword(alt.GetToken3());
+ VisitKeyword(alt.GetToken4());
+ break;
+ }
+ default:
+ ythrow yexception() << "Alt is not supported";
+ }
+ }
+
+ void VisitAction(const TRule_define_action_or_subquery_stmt& msg) {
+ PosFromToken(msg.GetToken1());
+ NewLine();
+ VisitKeyword(msg.GetToken1());
+ VisitKeyword(msg.GetToken2());
+ Visit(msg.GetRule_bind_parameter3());
+ AfterInvokeExpr = true;
+ Visit(msg.GetToken4());
+ if (msg.HasBlock5()) {
+ Visit(msg.GetBlock5());
+ }
+
+ Visit(msg.GetToken6());
+ VisitKeyword(msg.GetToken7()); // AS
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetRule_define_action_or_subquery_body8());
+ PopCurrentIndent();
+ NewLine();
+ VisitKeyword(msg.GetToken9());
+ VisitKeyword(msg.GetToken10());
+ }
+
+ void VisitIf(const TRule_if_stmt& msg) {
+ if (msg.HasBlock1()) {
+ PosFromToken(msg.GetBlock1().GetToken1());
+ } else {
+ PosFromToken(msg.GetToken2());
+ }
+
+ NewLine();
+ if (msg.HasBlock1()) {
+ Visit(msg.GetBlock1());
+ }
+
+ Visit(msg.GetToken2());
+ Visit(msg.GetRule_expr3());
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetRule_do_stmt4());
+ PopCurrentIndent();
+ if (msg.HasBlock5()) {
+ NewLine();
+ Visit(msg.GetBlock5().GetToken1());
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetBlock5().GetRule_do_stmt2());
+ PopCurrentIndent();
+ }
+ }
+
+ void VisitFor(const TRule_for_stmt& msg) {
+ if (msg.HasBlock1()) {
+ PosFromToken(msg.GetBlock1().GetToken1());
+ } else {
+ PosFromToken(msg.GetToken2());
+ }
+
+ NewLine();
+ if (msg.HasBlock1()) {
+ Visit(msg.GetBlock1());
+ }
+
+ Visit(msg.GetToken2());
+ Visit(msg.GetRule_bind_parameter3());
+ Visit(msg.GetToken4());
+ Visit(msg.GetRule_expr5());
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetRule_do_stmt6());
+ PopCurrentIndent();
+ if (msg.HasBlock7()) {
+ NewLine();
+ Visit(msg.GetBlock7().GetToken1());
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetBlock7().GetRule_do_stmt2());
+ PopCurrentIndent();
+ }
+ }
+
+ void VisitValues(const TRule_values_stmt& msg) {
+ PosFromToken(msg.GetToken1());
+ NewLine();
+ VisitKeyword(msg.GetToken1());
+ const auto& rowList = msg.GetRule_values_source_row_list2();
+ PushCurrentIndent();
+ NewLine();
+ Visit(rowList.GetRule_values_source_row1());
+ for (const auto& b : rowList.GetBlock2()) {
+ Visit(b.GetToken1());
+ NewLine();
+ Visit(b.GetRule_values_source_row2());
+ }
+
+ PopCurrentIndent();
+ }
+
+ void VisitCreateUser(const TRule_create_user_stmt& msg) {
+ PosFromToken(msg.GetToken1());
+ NewLine();
+ VisitAllFields(TRule_create_user_stmt::GetDescriptor(), msg);
+ }
+
+ void VisitAlterUser(const TRule_alter_user_stmt& msg) {
+ PosFromToken(msg.GetToken1());
+ NewLine();
+ VisitAllFields(TRule_alter_user_stmt::GetDescriptor(), msg);
+ }
+
+ void VisitCreateGroup(const TRule_create_group_stmt& msg) {
+ PosFromToken(msg.GetToken1());
+ NewLine();
+ VisitAllFields(TRule_create_group_stmt::GetDescriptor(), msg);
+ }
+
+ void VisitAlterGroup(const TRule_alter_group_stmt& msg) {
+ PosFromToken(msg.GetToken1());
+ NewLine();
+ VisitAllFields(TRule_alter_group_stmt::GetDescriptor(), msg);
+ }
+
+ void VisitDropRole(const TRule_drop_role_stmt& msg) {
+ PosFromToken(msg.GetToken1());
+ NewLine();
+ VisitAllFields(TRule_drop_role_stmt::GetDescriptor(), msg);
+ }
+
+ void VisitAllFields(const NProtoBuf::Descriptor* descr, const NProtoBuf::Message& msg) {
+ for (int i = 0; i < descr->field_count(); ++i) {
+ const NProtoBuf::FieldDescriptor* fd = descr->field(i);
+ NProtoBuf::TConstField field(msg, fd);
+ if (field.IsMessage()) {
+ for (size_t j = 0; j < field.Size(); ++j) {
+ Visit(*field.template Get<NProtoBuf::Message>(j));
+ }
+ }
+ }
+ }
+
+ void WriteComments() {
+ while (LastComment < Comments.size()) {
+ const auto& c = Comments[LastComment];
+ if (c.Line > LastLine || c.Line == LastLine && c.LinePos > LastColumn) {
+ break;
+ }
+
+ AddComment(c.Content);
+ ++LastComment;
+ }
+ }
+
+ void PosFromToken(const TToken& token) {
+ LastLine = token.GetLine();
+ LastColumn = token.GetColumn();
+ WriteComments();
+ }
+
+ void VisitToken(const TToken& token) {
+ VisitTokenImpl(token, false);
+ }
+
+ void VisitKeyword(const TToken& token) {
+ VisitTokenImpl(token, true);
+ }
+
+ void VisitTokenImpl(const TToken& token, bool forceKeyword) {
+ PosFromToken(token);
+ auto str = token.GetValue();
+
+ if (str == "<EOF>") {
+ return;
+ }
+
+ //Cerr << str << "\n";
+ auto currentScope = Scopes.back();
+ if (AfterLess && str == ">") {
+ Out(' ');
+ } else if (AfterDigits && str == ".") {
+ Out(' ');
+ } else if (OutColumn && (currentScope == EScope::DoubleQuestion || str != "?")
+ && str != ":" && str != "." && str != "," && str != ";" && str != ")" && str != "]"
+ && str != "}" && str != "|>" && str != "::" && !AfterNamespace && !AfterBracket
+ && !AfterInvokeExpr && !AfterDollarOrAt && !AfterDot && (!AfterQuestion || str != "?")
+ && (!InsideType || (str != "<" && str != ">" && str != "<>"))
+ && (!InsideType || !AfterLess)) {
+ Out(' ');
+ }
+
+ AfterInvokeExpr = false;
+ AfterNamespace = (str == "::");
+ AfterBracket = (str == "(" || str == "[" || str == "{" || str == "<|");
+ AfterDot = (str == ".");
+ AfterDigits = !str.Empty() && AllOf(str, [](char c) { return c >= '0' && c <= '9'; });
+ AfterQuestion = (str == "?");
+ AfterLess = (str == "<");
+
+ if (forceKeyword) {
+ str = to_upper(str);
+ } else if (currentScope == EScope::Default) {
+ if (auto p = StaticData.Keywords.find(to_upper(str)); p != StaticData.Keywords.end()) {
+ str = *p;
+ }
+ }
+
+ AfterDollarOrAt = (str == "$" || str == "@");
+
+ Out(str);
+ if (str == ";") {
+ Out('\n');
+ }
+ }
+
+ void VisitIntoValuesSource(const TRule_into_values_source& msg) {
+ switch (msg.Alt_case()) {
+ case TRule_into_values_source::kAltIntoValuesSource1: {
+ const auto& alt = msg.GetAlt_into_values_source1();
+ if (alt.HasBlock1()) {
+ const auto& columns = alt.GetBlock1().GetRule_pure_column_list1();
+ Visit(columns.GetToken1());
+ NewLine();
+ PushCurrentIndent();
+ Visit(columns.GetRule_an_id2());
+ for (const auto& block : columns.GetBlock3()) {
+ Visit(block.GetToken1());
+ NewLine();
+ Visit(block.GetRule_an_id2());
+ }
+
+ PopCurrentIndent();
+ NewLine();
+ Visit(columns.GetToken4());
+ NewLine();
+ }
+
+ Visit(alt.GetRule_values_source2());
+ break;
+ }
+ case TRule_into_values_source::kAltIntoValuesSource2: {
+ VisitAllFields(TRule_into_values_source::GetDescriptor(), msg);
+ break;
+ }
+ default:
+ ythrow yexception() << "Alt is not supported";
+ }
+ }
+
+ void VisitSelectKind(const TRule_select_kind& msg) {
+ if (msg.HasBlock1()) {
+ Visit(msg.GetBlock1());
+ }
+
+ Visit(msg.GetBlock2());
+ if (msg.HasBlock3()) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetBlock3());
+ PopCurrentIndent();
+ }
+ }
+
+ void VisitProcessCore(const TRule_process_core& msg) {
+ Visit(msg.GetToken1());
+ if (msg.HasBlock2()) {
+ Visit(msg.GetBlock2());
+ }
+
+ Visit(msg.GetRule_named_single_source3());
+ for (const auto& block : msg.GetBlock4()) {
+ Visit(block);
+ }
+
+ if (msg.HasBlock5()) {
+ NewLine();
+ PushCurrentIndent();
+ const auto& block5 = msg.GetBlock5();
+ Visit(block5.GetToken1());
+ Visit(block5.GetRule_using_call_expr2());
+ if (block5.HasBlock3()) {
+ Visit(block5.GetBlock3());
+ }
+
+ PopCurrentIndent();
+ if (block5.HasBlock4()) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(block5.GetBlock4());
+ PopCurrentIndent();
+ }
+
+ if (block5.HasBlock5()) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(block5.GetBlock5());
+ PopCurrentIndent();
+ }
+
+ if (block5.HasBlock6()) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(block5.GetBlock6());
+ PopCurrentIndent();
+ }
+
+ if (block5.HasBlock7()) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(block5.GetBlock7());
+ PopCurrentIndent();
+ }
+ }
+ }
+
+ void VisitReduceCore(const TRule_reduce_core& msg) {
+ Visit(msg.GetToken1());
+ Visit(msg.GetRule_named_single_source2());
+ for (const auto& block : msg.GetBlock3()) {
+ Visit(block);
+ }
+
+ if (msg.HasBlock4()) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetBlock4());
+ PopCurrentIndent();
+ }
+
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetToken5());
+ const auto& columns = msg.GetRule_column_list6();
+ NewLine();
+ PushCurrentIndent();
+ Visit(columns.GetRule_column_name1());
+ for (const auto& block : columns.GetBlock2()) {
+ Visit(block.GetToken1());
+ NewLine();
+ Visit(block.GetRule_column_name2());
+ }
+
+ if (columns.HasBlock3()) {
+ Visit(columns.GetBlock3());
+ }
+
+ NewLine();
+ PopCurrentIndent();
+ Visit(msg.GetToken7());
+ if (msg.HasBlock8()) {
+ Visit(msg.GetBlock8());
+ }
+
+ Visit(msg.GetRule_using_call_expr9());
+ if (msg.HasBlock10()) {
+ Visit(msg.GetBlock10());
+ }
+
+ PopCurrentIndent();
+ if (msg.HasBlock11()) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetBlock11());
+ PopCurrentIndent();
+ }
+
+ if (msg.HasBlock12()) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetBlock12());
+ PopCurrentIndent();
+ }
+
+ if (msg.HasBlock13()) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetBlock13());
+ PopCurrentIndent();
+ }
+ }
+
+ void VisitSortSpecificationList(const TRule_sort_specification_list& msg) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetRule_sort_specification1());
+ for (const auto& block : msg.GetBlock2()) {
+ Visit(block.GetToken1());
+ NewLine();
+ Visit(block.GetRule_sort_specification2());
+ }
+
+ PopCurrentIndent();
+ }
+
+ void VisitSelectCore(const TRule_select_core& msg) {
+ if (msg.HasBlock1()) {
+ Visit(msg.GetBlock1());
+ NewLine();
+ }
+
+ Visit(msg.GetToken2());
+ if (msg.HasBlock3()) {
+ Visit(msg.GetBlock3());
+ }
+
+ Visit(msg.GetRule_opt_set_quantifier4());
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetRule_result_column5());
+ for (const auto& block : msg.GetBlock6()) {
+ Visit(block.GetToken1());
+ NewLine();
+ Visit(block.GetRule_result_column2());
+ }
+
+ if (msg.HasBlock7()) {
+ Visit(msg.GetBlock7());
+ }
+
+ if (msg.HasBlock8()) {
+ NewLine();
+ Visit(msg.GetBlock8());
+ }
+
+ PopCurrentIndent();
+ if (msg.HasBlock9()) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetBlock9());
+ PopCurrentIndent();
+ }
+
+ if (msg.HasBlock10()) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetBlock10());
+ PopCurrentIndent();
+ }
+
+ if (msg.HasBlock11()) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetBlock11());
+ PopCurrentIndent();
+ }
+
+ if (msg.HasBlock12()) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetBlock12());
+ PopCurrentIndent();
+ }
+
+ if (msg.HasBlock13()) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetBlock13());
+ PopCurrentIndent();
+ }
+
+ if (msg.HasBlock14()) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetBlock14());
+ PopCurrentIndent();
+ }
+ }
+
+ void VisitJoinSource(const TRule_join_source& msg) {
+ if (msg.HasBlock1()) {
+ Visit(msg.GetBlock1());
+ }
+
+ Visit(msg.GetRule_flatten_source2());
+ for (const auto& block : msg.GetBlock3()) {
+ NewLine();
+ Visit(block.GetRule_join_op1());
+ if (block.HasBlock2()) {
+ Visit(block.GetBlock2());
+ }
+
+ Visit(block.GetRule_flatten_source3());
+ if (block.HasBlock4()) {
+ NewLine();
+ Visit(block.GetBlock4());
+ }
+ }
+ }
+
+ void VisitSingleSource(const TRule_single_source& msg) {
+ switch (msg.Alt_case()) {
+ case TRule_single_source::kAltSingleSource1: {
+ const auto& alt = msg.GetAlt_single_source1();
+ Visit(alt);
+ break;
+ }
+ case TRule_single_source::kAltSingleSource2: {
+ const auto& alt = msg.GetAlt_single_source2();
+ Visit(alt.GetToken1());
+ PushCurrentIndent();
+ Visit(alt.GetRule_select_stmt2());
+ PopCurrentIndent();
+ NewLine();
+ Visit(alt.GetToken3());
+ break;
+ }
+ case TRule_single_source::kAltSingleSource3: {
+ const auto& alt = msg.GetAlt_single_source3();
+ Visit(alt.GetToken1());
+ PushCurrentIndent();
+ Visit(alt.GetRule_values_stmt2());
+ PopCurrentIndent();
+ NewLine();
+ Visit(alt.GetToken3());
+ break;
+ }
+ default:
+ ythrow yexception() << "Alt is not supported";
+ }
+ }
+
+ void VisitFlattenSource(const TRule_flatten_source& msg) {
+ Visit(msg.GetRule_named_single_source1());
+ if (msg.HasBlock2()) {
+ PushCurrentIndent();
+ NewLine();
+ Visit(msg.GetBlock2());
+ PopCurrentIndent();
+ }
+ }
+
+ void VisitNamedSingleSource(const TRule_named_single_source& msg) {
+ Visit(msg.GetRule_single_source1());
+ if (msg.HasBlock2()) {
+ NewLine();
+ PushCurrentIndent();
+ const auto& block2 = msg.GetBlock2();
+ Visit(block2.GetBlock1());
+ if (block2.HasBlock2()) {
+ const auto& columns = block2.GetBlock2().GetRule_pure_column_list1();
+ Visit(columns.GetToken1());
+ NewLine();
+ PushCurrentIndent();
+ Visit(columns.GetRule_an_id2());
+ for (const auto& block : columns.GetBlock3()) {
+ Visit(block.GetToken1());
+ NewLine();
+ Visit(block.GetRule_an_id2());
+ }
+
+ NewLine();
+ PopCurrentIndent();
+ Visit(columns.GetToken4());
+ }
+
+ PopCurrentIndent();
+ }
+
+ if (msg.HasBlock3()) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetBlock3());
+ PopCurrentIndent();
+ }
+ }
+
+ void VisitSimpleTableRef(const TRule_simple_table_ref& msg) {
+ Visit(msg.GetBlock1());
+ if (msg.HasBlock2()) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetBlock2());
+ PopCurrentIndent();
+ }
+ }
+
+ void VisitIntoSimpleTableRef(const TRule_into_simple_table_ref& msg) {
+ Visit(msg.GetRule_simple_table_ref1());
+ if (msg.HasBlock2()) {
+ const auto& block2 = msg.GetBlock2();
+ NewLine();
+ PushCurrentIndent();
+ Visit(block2.GetToken1());
+ Visit(block2.GetToken2());
+ const auto& columns = block2.GetRule_pure_column_list3();
+ Visit(columns.GetToken1());
+ NewLine();
+ PushCurrentIndent();
+ Visit(columns.GetRule_an_id2());
+ for (const auto& block : columns.GetBlock3()) {
+ Visit(block.GetToken1());
+ NewLine();
+ Visit(block.GetRule_an_id2());
+ }
+
+ PopCurrentIndent();
+ NewLine();
+ Visit(columns.GetToken4());
+ PopCurrentIndent();
+ }
+ }
+
+ void VisitSelectKindPartial(const TRule_select_kind_partial& msg) {
+ Visit(msg.GetRule_select_kind1());
+ if (msg.HasBlock2()) {
+ NewLine();
+ Visit(msg.GetBlock2());
+ }
+ }
+
+ void VisitFlattenByArg(const TRule_flatten_by_arg& msg) {
+ switch (msg.Alt_case()) {
+ case TRule_flatten_by_arg::kAltFlattenByArg1: {
+ const auto& alt = msg.GetAlt_flatten_by_arg1();
+ NewLine();
+ PushCurrentIndent();
+ Visit(alt);
+ PopCurrentIndent();
+ break;
+ }
+ case TRule_flatten_by_arg::kAltFlattenByArg2: {
+ const auto& alt = msg.GetAlt_flatten_by_arg2();
+ Visit(alt.GetToken1());
+ NewLine();
+ PushCurrentIndent();
+ const auto& exprs = alt.GetRule_named_expr_list2();
+ Visit(exprs.GetRule_named_expr1());
+ for (const auto& block : exprs.GetBlock2()) {
+ Visit(block.GetToken1());
+ NewLine();
+ Visit(block.GetRule_named_expr2());
+ }
+
+ if (alt.HasBlock3()) {
+ Visit(alt.GetBlock3());
+ }
+
+ NewLine();
+ PopCurrentIndent();
+ Visit(alt.GetToken4());
+ break;
+ }
+ default:
+ ythrow yexception() << "Alt is not supported";
+ }
+ }
+
+ void VisitWithoutColumnList(const TRule_without_column_list& msg) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetRule_without_column_name1());
+ for (const auto& block : msg.GetBlock2()) {
+ Visit(block.GetToken1());
+ NewLine();
+ Visit(block.GetRule_without_column_name2());
+ }
+
+ if (msg.HasBlock3()) {
+ Visit(msg.GetBlock3());
+ }
+
+ PopCurrentIndent();
+ }
+
+ void VisitTableRef(const TRule_table_ref& msg) {
+ if (msg.HasBlock1()) {
+ Visit(msg.GetBlock1());
+ }
+
+ if (msg.HasBlock2()) {
+ Visit(msg.GetBlock2());
+ }
+
+ const auto& block3 = msg.GetBlock3();
+ switch (block3.Alt_case()) {
+ case TRule_table_ref::TBlock3::kAlt1: {
+ const auto& alt = block3.GetAlt1();
+ const auto& key = alt.GetRule_table_key1();
+ Visit(key.GetRule_id_table_or_type1());
+ if (key.HasBlock2()) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(key.GetBlock2());
+ PopCurrentIndent();
+ }
+
+ break;
+ }
+ case TRule_table_ref::TBlock3::kAlt2: {
+ const auto& alt = block3.GetAlt2();
+ Visit(alt.GetRule_an_id_expr1());
+ AfterInvokeExpr = true;
+ Visit(alt.GetToken2());
+ if (alt.HasBlock3()) {
+ Visit(alt.GetBlock3());
+ }
+
+ Visit(alt.GetToken4());
+ break;
+ }
+ case TRule_table_ref::TBlock3::kAlt3: {
+ const auto& alt = block3.GetAlt3();
+ Visit(alt.GetRule_bind_parameter1());
+ if (alt.HasBlock2()) {
+ AfterInvokeExpr = true;
+ Visit(alt.GetBlock2());
+ }
+
+ if (alt.HasBlock3()) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(alt.GetBlock3());
+ PopCurrentIndent();
+ }
+
+ break;
+ }
+ default:
+ ythrow yexception() << "Alt is not supported";
+ }
+
+ if (msg.HasBlock4()) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetBlock4());
+ PopCurrentIndent();
+ }
+ }
+
+ void VisitGroupingElementList(const TRule_grouping_element_list& msg) {
+ NewLine();
+ PushCurrentIndent();
+ Visit(msg.GetRule_grouping_element1());
+ for (const auto& block : msg.GetBlock2()) {
+ Visit(block.GetToken1());
+ NewLine();
+ Visit(block.GetRule_grouping_element2());
+ }
+
+ PopCurrentIndent();
+ }
+
+ void VisitWindowDefinitionList(const TRule_window_definition_list& msg) {
+ NewLine();
+ PushCurrentIndent();
+
+ Visit(msg.GetRule_window_definition1());
+ for (const auto& block : msg.GetBlock2()) {
+ Visit(block.GetToken1());
+ NewLine();
+ Visit(block.GetRule_window_definition2());
+ }
+
+ PopCurrentIndent();
+ }
+
+ void VisitWindowSpecification(const TRule_window_specification& msg) {
+ Visit(msg.GetToken1());
+ NewLine();
+ PushCurrentIndent();
+ const auto& details = msg.GetRule_window_specification_details2();
+ if (details.HasBlock1()) {
+ NewLine();
+ Visit(details.GetBlock1());
+ }
+
+ if (details.HasBlock2()) {
+ NewLine();
+ Visit(details.GetBlock2());
+ }
+
+ if (details.HasBlock3()) {
+ NewLine();
+ Visit(details.GetBlock3());
+ }
+
+ if (details.HasBlock4()) {
+ NewLine();
+ Visit(details.GetBlock4());
+ }
+
+ NewLine();
+ PopCurrentIndent();
+ Visit(msg.GetToken3());
+ }
+
+ void VisitWindowParitionClause(const TRule_window_partition_clause& msg) {
+ Visit(msg.GetToken1());
+ if (msg.HasBlock2()) {
+ Visit(msg.GetBlock2());
+ }
+
+ Visit(msg.GetToken3());
+ const auto& exprs = msg.GetRule_named_expr_list4();
+ PushCurrentIndent();
+ NewLine();
+ Visit(exprs.GetRule_named_expr1());
+ for (const auto& block : exprs.GetBlock2()) {
+ Visit(block.GetToken1());
+ NewLine();
+ Visit(block.GetRule_named_expr2());
+ }
+
+ PopCurrentIndent();
+ }
+
+ void VisitLambdaBody(const TRule_lambda_body& msg) {
+ PushCurrentIndent();
+ NewLine();
+ for (const auto& block : msg.GetBlock1()) {
+ Visit(block);
+ }
+
+ for (const auto& block : msg.GetBlock2()) {
+ Visit(block);
+ NewLine();
+ }
+
+ Visit(msg.GetToken3());
+ Visit(msg.GetRule_expr4());
+ for (const auto& block : msg.GetBlock5()) {
+ Visit(block);
+ }
+
+ PopCurrentIndent();
+ NewLine();
+ }
+
+ void VisitInAtomExpr(const TRule_in_atom_expr& msg) {
+ if (msg.Alt_case() == TRule_in_atom_expr::kAltInAtomExpr7) {
+ const auto& alt = msg.GetAlt_in_atom_expr7();
+ Visit(alt.GetToken1());
+ NewLine();
+ PushCurrentIndent();
+ Visit(alt.GetRule_select_stmt2());
+ NewLine();
+ PopCurrentIndent();
+ Visit(alt.GetToken3());
+ } else {
+ VisitAllFields(TRule_in_atom_expr::GetDescriptor(), msg);
+ }
+ }
+
+ void VisitSelectKindParenthesis(const TRule_select_kind_parenthesis& msg) {
+ if (msg.Alt_case() == TRule_select_kind_parenthesis::kAltSelectKindParenthesis2) {
+ const auto& alt = msg.GetAlt_select_kind_parenthesis2();
+ Visit(alt.GetToken1());
+ NewLine();
+ PushCurrentIndent();
+ Visit(alt.GetRule_select_kind_partial2());
+ PopCurrentIndent();
+ NewLine();
+ Visit(alt.GetToken3());
+ } else {
+ VisitAllFields(TRule_select_kind_parenthesis::GetDescriptor(), msg);
+ }
+ }
+
+ void VisitCastExpr(const TRule_cast_expr& msg) {
+ Visit(msg.GetToken1());
+ AfterInvokeExpr = true;
+ Visit(msg.GetToken2());
+ Visit(msg.GetRule_expr3());
+ Visit(msg.GetToken4());
+ Visit(msg.GetRule_type_name_or_bind5());
+ Visit(msg.GetToken6());
+ }
+
+ void VisitBitCastExpr(const TRule_bitcast_expr& msg) {
+ Visit(msg.GetToken1());
+ AfterInvokeExpr = true;
+ Visit(msg.GetToken2());
+ Visit(msg.GetRule_expr3());
+ Visit(msg.GetToken4());
+ Visit(msg.GetRule_type_name_simple5());
+ Visit(msg.GetToken6());
+ }
+
+ void VisitExtOrderByClause(const TRule_ext_order_by_clause& msg) {
+ PopCurrentIndent();
+ if (msg.HasBlock1()) {
+ Visit(msg.GetBlock1());
+ }
+
+ Visit(msg.GetRule_order_by_clause2());
+ PushCurrentIndent();
+ }
+
+ void PushCurrentIndent() {
+ CurrentIndent += OneIndent;
+ }
+
+ void PopCurrentIndent() {
+ CurrentIndent -= OneIndent;
+ }
+
+private:
+ const TStaticData& StaticData;
+ const TVector<NSQLTranslation::TParsedToken>& Comments;
+ TStringBuilder SB;
+ ui32 OutColumn = 0;
+ ui32 OutLine = 1;
+ ui32 LastLine = 0;
+ ui32 LastColumn = 0;
+ ui32 LastComment = 0;
+ i32 CurrentIndent = 0;
+ TVector<EScope> Scopes;
+ ui64 InsideType = 0;
+ bool AfterNamespace = false;
+ bool AfterBracket = false;
+ bool AfterInvokeExpr = false;
+ bool AfterDollarOrAt = false;
+ bool AfterDot = false;
+ bool AfterDigits = false;
+ bool AfterQuestion = false;
+ bool AfterLess = false;
+};
+
+template <typename T>
+TFunctor MakeFunctor(void (TVisitor::*memberPtr)(const T& msg)) {
+ return [memberPtr](TVisitor& visitor, const NProtoBuf::Message& rawMsg) {
+ (visitor.*memberPtr)(dynamic_cast<const T&>(rawMsg));
+ };
+}
+
+TStaticData::TStaticData()
+ : Keywords(GetKeywords())
+ , ScopeDispatch({
+ {TRule_type_name::GetDescriptor(), EScope::TypeName},
+ {TRule_type_name_composite::GetDescriptor(), EScope::TypeName},
+ {TRule_double_question::GetDescriptor(), EScope::DoubleQuestion},
+ {TRule_id::GetDescriptor(), EScope::Identifier},
+ {TRule_id_or_type::GetDescriptor(), EScope::Identifier},
+ {TRule_id_schema::GetDescriptor(), EScope::Identifier},
+ {TRule_id_expr::GetDescriptor(), EScope::Identifier},
+ {TRule_id_expr_in::GetDescriptor(), EScope::Identifier},
+ {TRule_id_window::GetDescriptor(), EScope::Identifier},
+ {TRule_id_table::GetDescriptor(), EScope::Identifier},
+ {TRule_id_without::GetDescriptor(), EScope::Identifier},
+ {TRule_id_hint::GetDescriptor(), EScope::Identifier},
+ {TRule_identifier::GetDescriptor(), EScope::Identifier},
+ {TRule_id_table_or_type::GetDescriptor(), EScope::Identifier},
+ {TRule_bind_parameter::GetDescriptor(), EScope::Identifier},
+ })
+ , VisitDispatch({
+ {TToken::GetDescriptor(), MakeFunctor(&TVisitor::VisitToken)},
+ {TRule_into_values_source::GetDescriptor(), MakeFunctor(&TVisitor::VisitIntoValuesSource)},
+ {TRule_select_kind::GetDescriptor(), MakeFunctor(&TVisitor::VisitSelectKind)},
+ {TRule_process_core::GetDescriptor(), MakeFunctor(&TVisitor::VisitProcessCore)},
+ {TRule_reduce_core::GetDescriptor(), MakeFunctor(&TVisitor::VisitReduceCore)},
+ {TRule_sort_specification_list::GetDescriptor(), MakeFunctor(&TVisitor::VisitSortSpecificationList)},
+ {TRule_select_core::GetDescriptor(), MakeFunctor(&TVisitor::VisitSelectCore)},
+ {TRule_join_source::GetDescriptor(), MakeFunctor(&TVisitor::VisitJoinSource)},
+ {TRule_single_source::GetDescriptor(), MakeFunctor(&TVisitor::VisitSingleSource)},
+ {TRule_flatten_source::GetDescriptor(), MakeFunctor(&TVisitor::VisitFlattenSource)},
+ {TRule_named_single_source::GetDescriptor(), MakeFunctor(&TVisitor::VisitNamedSingleSource)},
+ {TRule_simple_table_ref::GetDescriptor(), MakeFunctor(&TVisitor::VisitSimpleTableRef)},
+ {TRule_into_simple_table_ref::GetDescriptor(), MakeFunctor(&TVisitor::VisitIntoSimpleTableRef)},
+ {TRule_select_kind_partial::GetDescriptor(), MakeFunctor(&TVisitor::VisitSelectKindPartial)},
+ {TRule_flatten_by_arg::GetDescriptor(), MakeFunctor(&TVisitor::VisitFlattenByArg)},
+ {TRule_without_column_list::GetDescriptor(), MakeFunctor(&TVisitor::VisitWithoutColumnList)},
+ {TRule_table_ref::GetDescriptor(), MakeFunctor(&TVisitor::VisitTableRef)},
+ {TRule_grouping_element_list::GetDescriptor(), MakeFunctor(&TVisitor::VisitGroupingElementList)},
+ {TRule_window_definition_list::GetDescriptor(), MakeFunctor(&TVisitor::VisitWindowDefinitionList)},
+ {TRule_window_specification::GetDescriptor(), MakeFunctor(&TVisitor::VisitWindowSpecification)},
+ {TRule_window_partition_clause::GetDescriptor(), MakeFunctor(&TVisitor::VisitWindowParitionClause)},
+ {TRule_lambda_body::GetDescriptor(), MakeFunctor(&TVisitor::VisitLambdaBody)},
+ {TRule_in_atom_expr::GetDescriptor(), MakeFunctor(&TVisitor::VisitInAtomExpr)},
+ {TRule_select_kind_parenthesis::GetDescriptor(), MakeFunctor(&TVisitor::VisitSelectKindParenthesis)},
+ {TRule_cast_expr::GetDescriptor(), MakeFunctor(&TVisitor::VisitCastExpr)},
+ {TRule_bitcast_expr::GetDescriptor(), MakeFunctor(&TVisitor::VisitBitCastExpr)},
+ {TRule_ext_order_by_clause::GetDescriptor(), MakeFunctor(&TVisitor::VisitExtOrderByClause)},
+
+ {TRule_pragma_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitPragma)},
+ {TRule_select_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitSelect)},
+ {TRule_named_nodes_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitNamedNodes)},
+ {TRule_create_table_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitCreateTable)},
+ {TRule_drop_table_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitDropTable)},
+ {TRule_use_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitUse)},
+ {TRule_into_table_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitIntoTable)},
+ {TRule_commit_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitCommit)},
+ {TRule_update_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitUpdate)},
+ {TRule_delete_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitDelete)},
+ {TRule_rollback_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitRollback)},
+ {TRule_declare_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitDeclare)},
+ {TRule_import_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitImport)},
+ {TRule_export_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitExport)},
+ {TRule_alter_table_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitAlterTable)},
+ {TRule_do_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitDo)},
+ {TRule_define_action_or_subquery_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitAction)},
+ {TRule_if_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitIf)},
+ {TRule_for_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitFor)},
+ {TRule_values_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitValues)},
+ {TRule_create_user_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitCreateUser)},
+ {TRule_alter_user_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitAlterUser)},
+ {TRule_create_group_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitCreateGroup)},
+ {TRule_alter_group_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitAlterGroup)},
+ {TRule_drop_role_stmt::GetDescriptor(), MakeFunctor(&TVisitor::VisitDropRole)},
+ })
+{
+}
+
+class TSqlFormatter : public NSQLFormat::ISqlFormatter {
+public:
+ TSqlFormatter(const NSQLTranslation::TTranslationSettings& settings)
+ : Settings(settings)
+ {}
+
+ bool Format(const TString& query, TString& formattedQuery, NYql::TIssues& issues) override {
+ formattedQuery = query;
+ auto parsedSettings = Settings;
+ if (!NSQLTranslation::ParseTranslationSettings(query, parsedSettings, issues)) {
+ return false;
+ }
+
+ if (parsedSettings.PgParser) {
+ return true;
+ }
+
+ auto lexer = NSQLTranslationV1::MakeLexer(parsedSettings.AnsiLexer);
+ TParsedTokenList allTokens;
+ auto onNextToken = [&](NSQLTranslation::TParsedToken&& token) {
+ if (token.Name != "EOF") {
+ allTokens.push_back(token);
+ }
+ };
+
+ if (!lexer->Tokenize(query, "Query", onNextToken, issues, NSQLTranslation::SQL_MAX_PARSER_ERRORS)) {
+ return false;
+ }
+
+ TVector<TTokenIterator> statements;
+ SplitByStatements(allTokens.begin(), allTokens.end(), statements);
+ TStringBuilder finalFormattedQuery;
+ for (size_t i = 1; i < statements.size(); ++i) {
+ TStringBuilder currentQueryBuilder;
+ for (auto it = statements[i - 1]; it != statements[i]; ++it) {
+ currentQueryBuilder << it->Content;
+ }
+
+ TString currentQuery = currentQueryBuilder;
+ currentQuery = StripStringLeft(currentQuery);
+ bool isBlank = true;
+ for (auto c : currentQuery) {
+ if (c != ';') {
+ isBlank = false;
+ break;
+ }
+ };
+
+ if (isBlank) {
+ continue;
+ }
+
+ TVector<NSQLTranslation::TParsedToken> comments;
+ TParsedTokenList stmtTokens;
+ auto onNextRawToken = [&](NSQLTranslation::TParsedToken&& token) {
+ stmtTokens.push_back(token);
+ if (token.Name == "COMMENT") {
+ comments.emplace_back(std::move(token));
+ }
+ };
+
+ if (!lexer->Tokenize(currentQuery, "Query", onNextRawToken, issues, NSQLTranslation::SQL_MAX_PARSER_ERRORS)) {
+ return false;
+ }
+
+ NYql::TIssues parserIssues;
+ auto message = NSQLTranslationV1::SqlAST(currentQuery, "Query", parserIssues, NSQLTranslation::SQL_MAX_PARSER_ERRORS, parsedSettings.AnsiLexer, parsedSettings.Arena);
+ if (!message) {
+ finalFormattedQuery << currentQuery;
+ continue;
+ }
+
+ TVisitor visitor(comments);
+ auto currentFormattedQuery = visitor.Process(*message);
+ TParsedTokenList stmtFormattedTokens;
+ auto onNextFormattedToken = [&](NSQLTranslation::TParsedToken&& token) {
+ stmtFormattedTokens.push_back(token);
+ };
+
+ if (!lexer->Tokenize(currentFormattedQuery, "Query", onNextFormattedToken, issues, NSQLTranslation::SQL_MAX_PARSER_ERRORS)) {
+ return false;
+ }
+
+ if (!Validate(stmtFormattedTokens, stmtTokens)) {
+ issues.AddIssue(NYql::TIssue({}, TStringBuilder() << "Validation failed: " << currentQuery.Quote() << " != " << currentFormattedQuery.Quote()));
+ return false;
+ }
+
+ finalFormattedQuery << currentFormattedQuery;
+ if (!currentFormattedQuery.EndsWith(";\n")) {
+ finalFormattedQuery << ";\n";
+ }
+ }
+
+ formattedQuery = finalFormattedQuery;
+ return true;
+ }
+
+private:
+ const NSQLTranslation::TTranslationSettings Settings;
+};
+
+}
+
+ISqlFormatter::TPtr MakeSqlFormatter(const NSQLTranslation::TTranslationSettings& settings) {
+ return ISqlFormatter::TPtr(new TSqlFormatter(settings));
+}
+
+TString MutateQuery(const TString& query, const NSQLTranslation::TTranslationSettings& settings) {
+ auto parsedSettings = settings;
+ NYql::TIssues issues;
+ if (!NSQLTranslation::ParseTranslationSettings(query, parsedSettings, issues)) {
+ throw yexception() << issues.ToString();
+ }
+
+ auto lexer = NSQLTranslationV1::MakeLexer(parsedSettings.AnsiLexer);
+ TVector<NSQLTranslation::TParsedToken> allTokens;
+ auto onNextToken = [&](NSQLTranslation::TParsedToken&& token) {
+ if (token.Name != "EOF") {
+ allTokens.push_back(token);
+ }
+ };
+
+ if (!lexer->Tokenize(query, "Query", onNextToken, issues, NSQLTranslation::SQL_MAX_PARSER_ERRORS)) {
+ throw yexception() << issues.ToString();
+ }
+
+ TStringBuilder newQueryBuilder;
+ ui32 index = 0;
+ for (const auto& x : allTokens) {
+ newQueryBuilder << " /*" << index++ << "*/ ";
+ newQueryBuilder << x.Content;
+ }
+
+ newQueryBuilder << " /*" << index++ << "*/ ";
+ return newQueryBuilder;
+}
+
+bool SqlFormatSimple(const TString& query, TString& formattedQuery, TString& error) {
+ try {
+ google::protobuf::Arena arena;
+ NSQLTranslation::TTranslationSettings settings;
+ settings.Arena = &arena;
+
+ auto formatter = MakeSqlFormatter(settings);
+ NYql::TIssues issues;
+ const bool result = formatter->Format(query, formattedQuery, issues);
+ if (!result) {
+ error = issues.ToString();
+ }
+ return result;
+ } catch (const std::exception& e) {
+ error = e.what();
+ return false;
+ }
+}
+
+} // namespace NSQLFormat
diff --git a/ydb/library/yql/sql/v1/format/sql_format.h b/ydb/library/yql/sql/v1/format/sql_format.h
new file mode 100644
index 00000000000..80fc78fcddb
--- /dev/null
+++ b/ydb/library/yql/sql/v1/format/sql_format.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <ydb/library/yql/public/issue/yql_issue.h>
+#include <ydb/library/yql/sql/settings/translation_settings.h>
+
+#include <util/generic/string.h>
+
+namespace NSQLFormat {
+
+constexpr ui32 OneIndent = 4;
+
+class ISqlFormatter {
+public:
+ using TPtr = THolder<ISqlFormatter>;
+
+ struct TSettings {
+ bool TabToSpacesOnly = false;
+ size_t TabSize = OneIndent;
+ };
+
+ virtual bool Format(const TString& query, TString& formattedQuery, NYql::TIssues& issues) = 0;
+ virtual ~ISqlFormatter() = default;
+};
+
+ISqlFormatter::TPtr MakeSqlFormatter(const NSQLTranslation::TTranslationSettings& settings = {});
+
+// insert spaces and comments between each tokens
+TString MutateQuery(const TString& query, const NSQLTranslation::TTranslationSettings& settings = {});
+
+bool SqlFormatSimple(const TString& query, TString& formattedQuery, TString& error);
+
+}
diff --git a/ydb/library/yql/sql/v1/format/sql_format_ut.cpp b/ydb/library/yql/sql/v1/format/sql_format_ut.cpp
new file mode 100644
index 00000000000..bace25784be
--- /dev/null
+++ b/ydb/library/yql/sql/v1/format/sql_format_ut.cpp
@@ -0,0 +1,707 @@
+#include <library/cpp/testing/unittest/registar.h>
+
+#include "sql_format.h"
+
+#include <google/protobuf/arena.h>
+#include <util/string/subst.h>
+#include <util/string/join.h>
+
+namespace {
+
+using TCases = TVector<std::pair<TString, TString>>;
+
+struct TSetup {
+ TSetup() {
+ NSQLTranslation::TTranslationSettings settings;
+ settings.Arena = &Arena;
+ Formatter = NSQLFormat::MakeSqlFormatter(settings);
+ }
+
+ void Run(const TCases& cases) {
+ for (const auto& c : cases) {
+ NYql::TIssues issues;
+ TString formatted;
+ auto res = Formatter->Format(c.first, formatted, issues);
+ UNIT_ASSERT_C(res, issues.ToString());
+ auto expected = c.second;
+ SubstGlobal(expected, "\t", TString(NSQLFormat::OneIndent, ' '));
+ UNIT_ASSERT_NO_DIFF(formatted, expected);
+ auto mutatedQuery = NSQLFormat::MutateQuery(c.first);
+ auto res2 = Formatter->Format(mutatedQuery, formatted, issues);
+ UNIT_ASSERT_C(res2, issues.ToString());
+ }
+ }
+
+ google::protobuf::Arena Arena;
+ NSQLFormat::ISqlFormatter::TPtr Formatter;
+};
+
+}
+
+Y_UNIT_TEST_SUITE(CheckSqlFormatter) {
+ Y_UNIT_TEST(Pragma) {
+ TCases cases = {
+ {"pragma user = user;","PRAGMA user = user;\n"},
+ {"pragma user = default;","PRAGMA user = default;\n"},
+ {"pragma user.user = user;","PRAGMA user.user = user;\n"},
+ {"pragma user.user(user);","PRAGMA user.user(user);\n"},
+ {"pragma user.user(user, user);","PRAGMA user.user(user, user);\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(DotAfterDigits) {
+ TCases cases = {
+ {"select a.1 .b from plato.foo;","SELECT\n\ta.1 .b\n\tFROM plato.foo;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(DropRole) {
+ TCases cases = {
+ {"use plato;drop user user,user,user;","USE plato;\nDROP USER user, user, user;\n"},
+ {"use plato;drop group if exists user;","USE plato;\nDROP GROUP IF EXISTS user;\n"},
+ {"use plato;drop group user,;","USE plato;\nDROP GROUP user,;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(CreateUser) {
+ TCases cases = {
+ {"use plato;create user user;","USE plato;\nCREATE USER user;\n"},
+ {"use plato;create user user encrypted password 'foo';","USE plato;\nCREATE USER user ENCRYPTED PASSWORD 'foo';\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(CreateGroup) {
+ TCases cases = {
+ {"use plato;create group user;","USE plato;\nCREATE GROUP user;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(AlterUser) {
+ TCases cases = {
+ {"use plato;alter user user rename to user;","USE plato;\nALTER USER user RENAME TO user;\n"},
+ {"use plato;alter user user encrypted password 'foo';","USE plato;\nALTER USER user ENCRYPTED PASSWORD 'foo';\n"},
+ {"use plato;alter user user with encrypted password 'foo';","USE plato;\nALTER USER user WITH ENCRYPTED PASSWORD 'foo';\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(AlterGroup) {
+ TCases cases = {
+ {"use plato;alter group user add user user;","USE plato;\nALTER GROUP user ADD USER user;\n"},
+ {"use plato;alter group user drop user user;","USE plato;\nALTER GROUP user DROP USER user;\n"},
+ {"use plato;alter group user add user user, user,;","USE plato;\nALTER GROUP user ADD USER user, user,;\n"},
+ {"use plato;alter group user rename to user;","USE plato;\nALTER GROUP user RENAME TO user;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(Use) {
+ TCases cases = {
+ {"use user;","USE user;\n"},
+ {"use user:user;","USE user: user;\n"},
+ {"use user:*;","USE user: *;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(Commit) {
+ TCases cases = {
+ {"commit;","COMMIT;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(Rollback) {
+ TCases cases = {
+ {"rollback;","ROLLBACK;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(Export) {
+ TCases cases = {
+ {"export $foo;","EXPORT $foo;\n"},
+ {"export $foo, $bar;","EXPORT $foo, $bar;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(Import) {
+ TCases cases = {
+ {"import user symbols $foo;","IMPORT user SYMBOLS $foo;\n"},
+ {"import user symbols $foo,$bar;","IMPORT user SYMBOLS $foo, $bar;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(Values) {
+ TCases cases = {
+ {"values (1);","VALUES\n\t(1);\n"},
+ {"values (1,2),(3,4);","VALUES\n\t(1, 2),\n\t(3, 4);\n"},
+ {"values ('a\nb');","VALUES\n\t('a\nb');\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(Declare) {
+ TCases cases = {
+ {"declare $foo as int32;","DECLARE $foo AS int32;\n"},
+ {"declare $foo as bool ?","DECLARE $foo AS bool?;\n"},
+ {"declare $foo as bool ? ?","DECLARE $foo AS bool??;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(NamedNode) {
+ TCases cases = {
+ {"$x=1","$x = 1;\n"},
+ {"$x,$y=(2,3)","$x, $y = (2, 3);\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(DropTable) {
+ TCases cases = {
+ {"drop table user","DROP TABLE user;\n"},
+ {"drop table if exists user","DROP TABLE IF EXISTS user;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(CreateTable) {
+ TCases cases = {
+ {"create table user(user int32)","CREATE TABLE user (\n\tuser int32\n);\n"},
+ {"create table user(user int32,user bool ?)","CREATE TABLE user (\n\tuser int32,\n\tuser bool?\n);\n"},
+ {"create table user(user int32) with (user=user)","CREATE TABLE user (\n\tuser int32\n)\nWITH (user = user);\n"},
+ {"create table user(primary key (user))","CREATE TABLE user (\n\tPRIMARY KEY (user)\n);\n"},
+ {"create table user(primary key (user,user))","CREATE TABLE user (\n\tPRIMARY KEY (user, user)\n);\n"},
+ {"create table user(partition by (user))","CREATE TABLE user (\n\tPARTITION BY (user)\n);\n"},
+ {"create table user(partition by (user,user))","CREATE TABLE user (\n\tPARTITION BY (user, user)\n);\n"},
+ {"create table user(order by (user asc))","CREATE TABLE user (\n\tORDER BY (user ASC)\n);\n"},
+ {"create table user(order by (user desc,user))","CREATE TABLE user (\n\tORDER BY (user DESC, user)\n);\n"},
+ {"create table user(index user global unique sync with (user=user,user=user) on (user,user))",
+ "CREATE TABLE user (\n\tINDEX user GLOBAL UNIQUE SYNC WITH (user = user, user = user) ON (user, user)\n);\n"},
+ {"create table user(index user global async with (user=user,) on (user))",
+ "CREATE TABLE user (\n\tINDEX user GLOBAL ASYNC WITH (user = user,) ON (user)\n);\n"},
+ {"create table user(index user local on (user) cover (user))",
+ "CREATE TABLE user (\n\tINDEX user LOCAL ON (user) COVER (user)\n);\n"},
+ {"create table user(index user local on (user) cover (user,user))",
+ "CREATE TABLE user (\n\tINDEX user LOCAL ON (user) COVER (user, user)\n);\n"},
+ {"create table user(family user (user='foo'))",
+ "CREATE TABLE user (\n\tFAMILY user (user = 'foo')\n);\n"},
+ {"create table user(family user (user='foo',user='bar'))",
+ "CREATE TABLE user (\n\tFAMILY user (user = 'foo', user = 'bar')\n);\n"},
+ {"create table user(changefeed user with (user='foo'))",
+ "CREATE TABLE user (\n\tCHANGEFEED user WITH (user = 'foo')\n);\n"},
+ {"create table user(changefeed user with (user='foo',user='bar'))",
+ "CREATE TABLE user (\n\tCHANGEFEED user WITH (user = 'foo', user = 'bar')\n);\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(AlterTable) {
+ TCases cases = {
+ {"alter table user add user int32",
+ "ALTER TABLE user\n\tADD user int32;\n"},
+ {"alter table user add user int32, add user bool ?",
+ "ALTER TABLE user\n\tADD user int32,\n\tADD user bool?;\n"},
+ {"alter table user add column user int32",
+ "ALTER TABLE user\n\tADD COLUMN user int32;\n"},
+ {"alter table user drop user",
+ "ALTER TABLE user\n\tDROP user;\n"},
+ {"alter table user drop column user",
+ "ALTER TABLE user\n\tDROP COLUMN user;\n"},
+ {"alter table user alter column user set family user",
+ "ALTER TABLE user\n\tALTER COLUMN user SET FAMILY user;\n"},
+ {"alter table user add family user(user='foo')",
+ "ALTER TABLE user\n\tADD FAMILY user (user = 'foo');\n"},
+ {"alter table user alter family user set user 'foo'",
+ "ALTER TABLE user\n\tALTER FAMILY user SET user 'foo';\n"},
+ {"alter table user set user user",
+ "ALTER TABLE user\n\tSET user user;\n"},
+ {"alter table user set (user=user)",
+ "ALTER TABLE user\n\tSET (user = user);\n"},
+ {"alter table user set (user=user,user=user)",
+ "ALTER TABLE user\n\tSET (user = user, user = user);\n"},
+ {"alter table user reset(user)",
+ "ALTER TABLE user\n\tRESET (user);\n"},
+ {"alter table user reset(user, user)",
+ "ALTER TABLE user\n\tRESET (user, user);\n"},
+ {"alter table user add index user local on (user)",
+ "ALTER TABLE user\n\tADD INDEX user LOCAL ON (user);\n"},
+ {"alter table user drop index user",
+ "ALTER TABLE user\n\tDROP INDEX user;\n"},
+ {"alter table user rename to user",
+ "ALTER TABLE user\n\tRENAME TO user;\n"},
+ {"alter table user add changefeed user with (user = 'foo')",
+ "ALTER TABLE user\n\tADD CHANGEFEED user WITH (user = 'foo');\n"},
+ {"alter table user alter changefeed user disable",
+ "ALTER TABLE user\n\tALTER CHANGEFEED user DISABLE;\n"},
+ {"alter table user alter changefeed user set(user='foo')",
+ "ALTER TABLE user\n\tALTER CHANGEFEED user SET (user = 'foo');\n"},
+ {"alter table user drop changefeed user",
+ "ALTER TABLE user\n\tDROP CHANGEFEED user;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(Do) {
+ TCases cases = {
+ {"do $a(1,2,3)",
+ "DO $a(1, 2, 3);\n"},
+ {"do begin values(1); end do;",
+ "DO BEGIN\n\tVALUES\n\t\t(1);\nEND DO;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(DefineActionOrSubquery) {
+ TCases cases = {
+ {"define action $a() as "
+ "define action $b() as "
+ "values(1); "
+ "end define; "
+ "define subquery $c() as "
+ "select 1; "
+ "end define; "
+ "do $b(); "
+ "process $c(); "
+ "end define",
+ "DEFINE ACTION $a() AS\n\tDEFINE ACTION $b() AS\n\t\t"
+ "VALUES\n\t\t\t(1);\n\tEND DEFINE;\n\t"
+ "DEFINE SUBQUERY $c() AS\n\t\tSELECT\n\t\t\t1;\n\t"
+ "END DEFINE;\n\tDO $b();\n\tPROCESS $c();\nEND DEFINE;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(If) {
+ TCases cases = {
+ {"evaluate if 1=1 do $a()",
+ "EVALUATE IF 1 = 1\n\tDO $a();\n"},
+ {"evaluate if 1=1 do $a() else do $b()",
+ "EVALUATE IF 1 = 1\n\tDO $a()\nELSE\n\tDO $b();\n"},
+ {"evaluate if 1=1 do begin select 1; end do",
+ "EVALUATE IF 1 = 1\n\tDO BEGIN\n\t\tSELECT\n\t\t\t1;\n\tEND DO;\n"},
+ {"evaluate if 1=1 do begin select 1; end do else do begin select 2; end do",
+ "EVALUATE IF 1 = 1\n\tDO BEGIN\n\t\tSELECT\n\t\t\t1;\n\tEND DO\n"
+ "ELSE\n\tDO BEGIN\n\t\tSELECT\n\t\t\t2;\n\tEND DO;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(For) {
+ TCases cases = {
+ {"evaluate for $x in [] do $a($x)",
+ "EVALUATE FOR $x IN []\n\tDO $a($x);\n"},
+ {"evaluate for $x in [] do $a($x) else do $b()",
+ "EVALUATE FOR $x IN []\n\tDO $a($x)\nELSE\n\tDO $b();\n"},
+ {"evaluate for $x in [] do begin select $x; end do",
+ "EVALUATE FOR $x IN []\n\tDO BEGIN\n\t\tSELECT\n\t\t\t$x;\n\tEND DO;\n"},
+ {"evaluate for $x in [] do begin select $x; end do else do begin select 2; end do",
+ "EVALUATE FOR $x IN []\n\tDO BEGIN\n\t\tSELECT\n\t\t\t$x;\n\tEND DO\nELSE\n\tDO BEGIN\n\t\tSELECT\n\t\t\t2;\n\tEND DO;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(Update) {
+ TCases cases = {
+ {"update user on default values",
+ "UPDATE user\n\tON DEFAULT VALUES;\n"},
+ {"update user on values (1),(2)",
+ "UPDATE user\n\tON\n\tVALUES\n\t\t(1),\n\t\t(2);\n"},
+ {"update user on select 1 as x, 2 as y",
+ "UPDATE user\n\tON\n\tSELECT\n\t\t1 AS x,\n\t\t2 AS y;\n"},
+ {"update user on (x) values (1),(2),(3)",
+ "UPDATE user\n\tON (\n\t\tx\n\t)\n\tVALUES\n\t\t(1),\n\t\t(2),\n\t\t(3);\n"},
+ {"update user on (x,y) values (1,2),(2,3),(3,4)",
+ "UPDATE user\n\tON (\n\t\tx,\n\t\ty\n\t)\n\tVALUES\n\t\t(1, 2),\n\t\t(2, 3),\n\t\t(3, 4);\n"},
+ {"update user on (x) select 1",
+ "UPDATE user\n\tON (\n\t\tx\n\t)\n\tSELECT\n\t\t1;\n"},
+ {"update user on (x,y) select 1,2",
+ "UPDATE user\n\tON (\n\t\tx,\n\t\ty\n\t)\n\tSELECT\n\t\t1,\n\t\t2;\n"},
+ {"update user set x=1",
+ "UPDATE user\n\tSET\n\t\tx = 1;\n"},
+ {"update user set (x)=(1)",
+ "UPDATE user\n\tSET\n\t\t(\n\t\t\tx\n\t\t) = (\n\t\t\t1\n\t\t);\n"},
+ {"update user set (x,y)=(1,2)",
+ "UPDATE user\n\tSET\n\t\t(\n\t\t\tx,\n\t\t\ty\n\t\t) = (\n\t\t\t1,\n\t\t\t2\n\t\t);\n"},
+ {"update user set (x,y)=(select 1,2)",
+ "UPDATE user\n\tSET\n\t\t(\n\t\t\tx,\n\t\t\ty\n\t\t) = (\n\t\t\tSELECT\n\t\t\t\t1,\n\t\t\t\t2\n\t\t);\n"},
+ {"update user set x=1,y=2 where z=3",
+ "UPDATE user\n\tSET\n\t\tx = 1,\n\t\ty = 2\n\tWHERE z = 3;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(Delete) {
+ TCases cases = {
+ {"delete from user",
+ "DELETE FROM user;\n"},
+ {"delete from user where 1=1",
+ "DELETE FROM user\n\tWHERE 1 = 1;\n"},
+ {"delete from user on select 1 as x, 2 as y",
+ "DELETE FROM user\n\tON\n\tSELECT\n\t\t1 AS x,\n\t\t2 AS y;\n"},
+ {"delete from user on (x) values (1)",
+ "DELETE FROM user\n\tON (\n\t\tx\n\t)\n\tVALUES\n\t\t(1);\n"},
+ {"delete from user on (x,y) values (1,2), (3,4)",
+ "DELETE FROM user\n\tON (\n\t\tx,\n\t\ty\n\t)\n\tVALUES\n\t\t(1, 2),\n\t\t(3, 4);\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(Into) {
+ TCases cases = {
+ {"insert into user select 1 as x",
+ "INSERT INTO user\nSELECT\n\t1 AS x;\n"},
+ {"insert or abort into user select 1 as x",
+ "INSERT OR ABORT INTO user\nSELECT\n\t1 AS x;\n"},
+ {"insert or revert into user select 1 as x",
+ "INSERT OR REVERT INTO user\nSELECT\n\t1 AS x;\n"},
+ {"insert or ignore into user select 1 as x",
+ "INSERT OR IGNORE INTO user\nSELECT\n\t1 AS x;\n"},
+ {"upsert into user select 1 as x",
+ "UPSERT INTO user\nSELECT\n\t1 AS x;\n"},
+ {"replace into user select 1 as x",
+ "REPLACE INTO user\nSELECT\n\t1 AS x;\n"},
+ {"insert into user(x) values (1)",
+ "INSERT INTO user (\n\tx\n)\nVALUES\n\t(1);\n"},
+ {"insert into user(x,y) values (1,2)",
+ "INSERT INTO user (\n\tx,\n\ty\n)\nVALUES\n\t(1, 2);\n"},
+ {"insert into plato.user select 1 as x",
+ "INSERT INTO plato.user\nSELECT\n\t1 AS x;\n"},
+ {"insert into @user select 1 as x",
+ "INSERT INTO @user\nSELECT\n\t1 AS x;\n"},
+ {"insert into $user select 1 as x",
+ "INSERT INTO $user\nSELECT\n\t1 AS x;\n"},
+ {"insert into @$user select 1 as x",
+ "INSERT INTO @$user\nSELECT\n\t1 AS x;\n"},
+ {"upsert into user erase by (x,y) values (1)",
+ "UPSERT INTO user\n\tERASE BY (\n\t\tx,\n\t\ty\n\t)\nVALUES\n\t(1);\n"},
+ {"insert into user with truncate select 1 as x",
+ "INSERT INTO user\n\tWITH truncate\nSELECT\n\t1 AS x;\n"},
+ {"insert into user with (truncate,inferscheme='1') select 1 as x",
+ "INSERT INTO user\n\tWITH (truncate, inferscheme = '1')\nSELECT\n\t1 AS x;\n"},
+ {"insert into user with schema Struct<user:int32> select 1 as user",
+ "INSERT INTO user\n\tWITH SCHEMA Struct<user: int32>\nSELECT\n\t1 AS user;\n"},
+ {"insert into user with schema (int32 as user) select 1 as user",
+ "INSERT INTO user\n\tWITH SCHEMA (int32 AS user)\nSELECT\n\t1 AS user;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(Process) {
+ TCases cases = {
+ {"process user",
+ "PROCESS user;\n"},
+ {"process user using $f() as user",
+ "PROCESS user\n\tUSING $f() AS user;\n"},
+ {"process user,user using $f()",
+ "PROCESS user, user\n\tUSING $f();\n"},
+ {"process user using $f() where 1=1 having 1=1 assume order by user",
+ "PROCESS user\n\tUSING $f()\n\tWHERE 1 = 1\n\tHAVING 1 = 1\n\tASSUME ORDER BY\n\t\tuser;\n"},
+ {"process user using $f() union all process user using $f()",
+ "PROCESS user\n\tUSING $f()\nUNION ALL\nPROCESS user\n\tUSING $f();\n"},
+ {"process user using $f() with foo=bar",
+ "PROCESS user\n\tUSING $f()\n\tWITH foo = bar;\n"},
+ {"discard process user using $f()",
+ "DISCARD PROCESS user\n\tUSING $f();\n"},
+ {"process user using $f() into result user",
+ "PROCESS user\n\tUSING $f()\n\tINTO RESULT user;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(Reduce) {
+ TCases cases = {
+ {"reduce user on user using $f()",
+ "REDUCE user\n\tON\n\t\tuser\n\tUSING $f();\n"},
+ {"reduce user on user, using $f()",
+ "REDUCE user\n\tON\n\t\tuser,\n\tUSING $f();\n"},
+ {"discard reduce user on user using $f();",
+ "DISCARD REDUCE user\n\tON\n\t\tuser\n\tUSING $f();\n"},
+ {"reduce user on user using $f() into result user",
+ "REDUCE user\n\tON\n\t\tuser\n\tUSING $f()\n\tINTO RESULT user;\n"},
+ {"reduce user on user using all $f()",
+ "REDUCE user\n\tON\n\t\tuser\n\tUSING ALL $f();\n"},
+ {"reduce user on user using $f() as user",
+ "REDUCE user\n\tON\n\t\tuser\n\tUSING $f() AS user;\n"},
+ {"reduce user,user on user using $f()",
+ "REDUCE user, user\n\tON\n\t\tuser\n\tUSING $f();\n"},
+ {"reduce user on user,user using $f()",
+ "REDUCE user\n\tON\n\t\tuser,\n\t\tuser\n\tUSING $f();\n"},
+ {"reduce user on user using $f() where 1=1 having 1=1 assume order by user",
+ "REDUCE user\n\tON\n\t\tuser\n\tUSING $f()\n\tWHERE 1 = 1\n\tHAVING 1 = 1\n\tASSUME ORDER BY\n\t\tuser;\n"},
+ {"reduce user presort user,user on user using $f();",
+ "REDUCE user\n\tPRESORT\n\t\tuser,\n\t\tuser\n\tON\n\t\tuser\n\tUSING $f();\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(Select) {
+ TCases cases = {
+ {"select 1",
+ "SELECT\n\t1;\n"},
+ {"select 1,",
+ "SELECT\n\t1,;\n"},
+ {"select 1 as x",
+ "SELECT\n\t1 AS x;\n"},
+ {"select *",
+ "SELECT\n\t*;\n"},
+ {"select a.*",
+ "SELECT\n\ta.*;\n"},
+ {"select * without a",
+ "SELECT\n\t*\n\tWITHOUT\n\t\ta;\n"},
+ {"select * without a,b",
+ "SELECT\n\t*\n\tWITHOUT\n\t\ta,\n\t\tb;\n"},
+ {"select * without a,",
+ "SELECT\n\t*\n\tWITHOUT\n\t\ta,;\n"},
+ {"select 1 from user",
+ "SELECT\n\t1\n\tFROM user;\n"},
+ {"select 1 from plato.user",
+ "SELECT\n\t1\n\tFROM plato.user;\n"},
+ {"select 1 from $user",
+ "SELECT\n\t1\n\tFROM $user;\n"},
+ {"select 1 from @user",
+ "SELECT\n\t1\n\tFROM @user;\n"},
+ {"select 1 from @$user",
+ "SELECT\n\t1\n\tFROM @$user;\n"},
+ {"select 1 from user view user",
+ "SELECT\n\t1\n\tFROM user\n\t\tVIEW user;\n"},
+ {"select 1 from user as user",
+ "SELECT\n\t1\n\tFROM user\n\t\tAS user;\n"},
+ {"select 1 from user as user(user)",
+ "SELECT\n\t1\n\tFROM user\n\t\tAS user (\n\t\t\tuser\n\t\t);\n"},
+ {"select 1 from user as user(user, user)",
+ "SELECT\n\t1\n\tFROM user\n\t\tAS user (\n\t\t\tuser,\n\t\t\tuser\n\t\t);\n"},
+ {"select 1 from user with user=user",
+ "SELECT\n\t1\n\tFROM user\n\t\tWITH user = user;\n"},
+ {"select 1 from user with (user=user, user=user)",
+ "SELECT\n\t1\n\tFROM user\n\t\tWITH (user = user, user = user);\n"},
+ {"select 1 from user sample 0.1",
+ "SELECT\n\t1\n\tFROM user\n\t\tSAMPLE 0.1;\n"},
+ {"select 1 from user tablesample system(0.1)",
+ "SELECT\n\t1\n\tFROM user\n\t\tTABLESAMPLE SYSTEM (0.1);\n"},
+ {"select 1 from user tablesample bernoulli(0.1) repeatable(10)",
+ "SELECT\n\t1\n\tFROM user\n\t\tTABLESAMPLE BERNOULLI (0.1) REPEATABLE (10);\n"},
+ {"select 1 from user flatten columns",
+ "SELECT\n\t1\n\tFROM user\n\t\tFLATTEN COLUMNS;\n"},
+ {"select 1 from user flatten list by user",
+ "SELECT\n\t1\n\tFROM user\n\t\tFLATTEN LIST BY\n\t\t\tuser;\n"},
+ {"select 1 from user flatten list by (user,user)",
+ "SELECT\n\t1\n\tFROM user\n\t\tFLATTEN LIST BY (\n\t\t\tuser,\n\t\t\tuser\n\t\t);\n"},
+ {"select 1 from $user(1,2)",
+ "SELECT\n\t1\n\tFROM $user(1, 2);\n"},
+ {"select 1 from $user(1,2) view user",
+ "SELECT\n\t1\n\tFROM $user(1, 2)\n\t\tVIEW user;\n"},
+ {"select 1 from range('a','b')",
+ "SELECT\n\t1\n\tFROM range('a', 'b');\n"},
+ {"from user select 1",
+ "FROM user\nSELECT\n\t1;\n"},
+ {"select * from user as a join user as b on a.x=b.y",
+ "SELECT\n\t*\n\tFROM user\n\t\tAS a\n\tJOIN user\n\t\tAS b\n\tON a.x = b.y;\n"},
+ {"select * from user as a join user as b using(x)",
+ "SELECT\n\t*\n\tFROM user\n\t\tAS a\n\tJOIN user\n\t\tAS b\n\tUSING (x);\n"},
+ {"select * from any user as a full join user as b on a.x=b.y",
+ "SELECT\n\t*\n\tFROM ANY user\n\t\tAS a\n\tFULL JOIN user\n\t\tAS b\n\tON a.x = b.y;\n"},
+ {"select * from user as a left join any user as b on a.x=b.y",
+ "SELECT\n\t*\n\tFROM user\n\t\tAS a\n\tLEFT JOIN ANY user\n\t\tAS b\n\tON a.x = b.y;\n"},
+ {"select * from any user as a right join any user as b on a.x=b.y",
+ "SELECT\n\t*\n\tFROM ANY user\n\t\tAS a\n\tRIGHT JOIN ANY user\n\t\tAS b\n\tON a.x = b.y;\n"},
+ {"select * from user as a cross join user as b",
+ "SELECT\n\t*\n\tFROM user\n\t\tAS a\n\tCROSS JOIN user\n\t\tAS b;\n"},
+ {"select 1 from user where key = 1",
+ "SELECT\n\t1\n\tFROM user\n\tWHERE key = 1;\n"},
+ {"select 1 from user having count(*) = 1",
+ "SELECT\n\t1\n\tFROM user\n\tHAVING count(*) = 1;\n"},
+ {"select 1 from user group by key",
+ "SELECT\n\t1\n\tFROM user\n\tGROUP BY\n\t\tkey;\n"},
+ {"select 1 from user group compact by key, value as v",
+ "SELECT\n\t1\n\tFROM user\n\tGROUP COMPACT BY\n\t\tkey,\n\t\tvalue AS v;\n"},
+ {"select 1 from user order by key asc",
+ "SELECT\n\t1\n\tFROM user\nORDER BY\n\tkey ASC;\n"},
+ {"select 1 from user order by key, value desc",
+ "SELECT\n\t1\n\tFROM user\nORDER BY\n\tkey,\n\tvalue DESC;\n"},
+ {"select 1 from user assume order by key",
+ "SELECT\n\t1\n\tFROM user\nASSUME ORDER BY\n\tkey;\n"},
+ {"select 1 from user window w1 as (), w2 as ()",
+ "SELECT\n\t1\n\tFROM user\n\tWINDOW\n\t\tw1 AS (\n\t\t),\n\t\tw2 AS (\n\t\t);\n"},
+ {"select 1 from user window w1 as (user)",
+ "SELECT\n\t1\n\tFROM user\n\tWINDOW\n\t\tw1 AS (\n\t\t\tuser\n\t\t);\n"},
+ {"select 1 from user window w1 as (partition by user)",
+ "SELECT\n\t1\n\tFROM user\n\tWINDOW\n\t\tw1 AS (\n\t\t\tPARTITION BY\n\t\t\t\tuser\n\t\t);\n"},
+ {"select 1 from user window w1 as (partition by user, user)",
+ "SELECT\n\t1\n\tFROM user\n\tWINDOW\n\t\tw1 AS (\n\t\t\tPARTITION BY\n\t\t\t\tuser,\n\t\t\t\tuser\n\t\t);\n"},
+ {"select 1 from user window w1 as (order by user asc)",
+ "SELECT\n\t1\n\tFROM user\n\tWINDOW\n\t\tw1 AS (\n\t\t\tORDER BY\n\t\t\t\tuser ASC\n\t\t);\n"},
+ {"select 1 from user window w1 as (order by user, user desc)",
+ "SELECT\n\t1\n\tFROM user\n\tWINDOW\n\t\tw1 AS (\n\t\t\tORDER BY\n\t\t\t\tuser,\n\t\t\t\tuser DESC\n\t\t);\n"},
+ {"select 1 from user window w1 as (rows between 1 preceding and 1 following)",
+ "SELECT\n\t1\n\tFROM user\n\tWINDOW\n\t\tw1 AS (\n\t\t\tROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING\n\t\t);\n"},
+ {"select 1 limit 10",
+ "SELECT\n\t1\nLIMIT 10;\n"},
+ {"select 1 limit 10 offset 5",
+ "SELECT\n\t1\nLIMIT 10 OFFSET 5;\n"},
+ { "select 1 union all select 2",
+ "SELECT\n\t1\nUNION ALL\nSELECT\n\t2;\n" },
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(CompositeTypesAndQuestions) {
+ TCases cases = {
+ {"declare $_x AS list<int32>??;declare $_y AS int32 ? ? ;select 1<>2, 1??2,"
+ "formattype(list<int32>), formattype(resource<user>),formattype(tuple<>), formattype(tuple< >), formattype(int32 ? ? )",
+ "DECLARE $_x AS list<int32>??;\nDECLARE $_y AS int32??;\nSELECT\n\t1 <> 2,\n\t1 ?? 2,\n\tformattype(list<int32>),"
+ "\n\tformattype(resource<user>),\n\tformattype(tuple<>),\n\tformattype(tuple< >),\n\tformattype(int32??" ");\n"
+ },
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(Lambda) {
+ TCases cases = {
+ {"$f=($a,$b)->{$x=$a+$b;return $a*$x};$g=($a,$b?)->($a+$b??0);select $f(10,4),$g(1,2);",
+ "$f = ($a, $b) -> {\n\t$x = $a + $b;\n\tRETURN $a * $x\n};\n"
+ "$g = ($a, $b?) -> ($a + $b ?? 0);\n"
+ "SELECT\n\t$f(10, 4),\n\t$g(1, 2);\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(NestedSelect) {
+ TCases cases = {
+ {"$x=select 1",
+ "$x =\n\tSELECT\n\t\t1;\n"},
+ {"$x=(select 1)",
+ "$x = (\n\tSELECT\n\t\t1\n);\n"},
+ {"select 1 in (select 1)",
+ "SELECT\n\t1 IN (\n\t\tSELECT\n\t\t\t1\n\t);\n"},
+ {"select 1 in ((select 1))",
+ "SELECT\n\t1 IN (\n\t\t(\n\t\t\tSELECT\n\t\t\t\t1\n\t\t)\n\t);\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(Cast) {
+ TCases cases = {
+ {"select cast(1 as string)","SELECT\n\tCAST(1 AS string);\n"},
+ {"select bitcast(1 as int32)","SELECT\n\tBITCAST(1 AS int32);\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(StructLiteral) {
+ TCases cases = {
+ {"select <||>","SELECT\n\t<||>;\n"},
+ {"select <|a:1|>","SELECT\n\t<|a: 1|>;\n"},
+ {"select <|a:1,b:2|>","SELECT\n\t<|a: 1, b: 2|>;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(TableHints) {
+ TCases cases = {
+ {"select * from plato.T with schema(foo int32, bar list<string>) where key is not null",
+ "SELECT\n\t*\n\tFROM plato.T\n\t\tWITH SCHEMA (foo int32, bar list<string>)\n\tWHERE key IS NOT NULL;\n"},
+ {"select * from plato.T with schema struct<foo:integer, Bar:list<string?>> where key<0",
+ "SELECT\n\t*\n\tFROM plato.T\n\t\tWITH SCHEMA struct<foo: integer, Bar: list<string?>>\n\tWHERE key < 0;\n"},
+ {"select * from plato.T with (foo=bar, x=$y, a=(a, b, c), u='aaa', schema (foo int32, bar list<string>))",
+ "SELECT\n\t*\n\tFROM plato.T\n\t\tWITH (foo = bar, x = $y, a = (a, b, c), u = 'aaa', SCHEMA (foo int32, bar list<string>));\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(BoolAsVariableName) {
+ TCases cases = {
+ {"$ False = True; select $ False;",
+ "$False = TRUE;\nSELECT\n\t$False;\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+
+ Y_UNIT_TEST(WithSchemaEquals) {
+ TCases cases = {
+ {"select * from plato.T with (format= csv_with_names, schema=(year int32, month String, day String, a Utf8, b Uint16));",
+ "SELECT\n\t*\n\tFROM plato.T\n\t\tWITH (format = csv_with_names, SCHEMA = (year int32, month String, day String, a Utf8, b Uint16));\n"},
+ };
+
+ TSetup setup;
+ setup.Run(cases);
+ }
+}