summaryrefslogtreecommitdiffstats
path: root/yql/essentials/sql/v1/context.cpp
diff options
context:
space:
mode:
authorvvvv <[email protected]>2024-11-07 12:29:36 +0300
committervvvv <[email protected]>2024-11-07 13:49:47 +0300
commitd4c258e9431675bab6745c8638df6e3dfd4dca6b (patch)
treeb5efcfa11351152a4c872fccaea35749141c0b11 /yql/essentials/sql/v1/context.cpp
parent13a4f274caef5cfdaf0263b24e4d6bdd5521472b (diff)
Moved other yql/essentials libs YQL-19206
init commit_hash:7d4c435602078407bbf20dd3c32f9c90d2bbcbc0
Diffstat (limited to 'yql/essentials/sql/v1/context.cpp')
-rw-r--r--yql/essentials/sql/v1/context.cpp656
1 files changed, 656 insertions, 0 deletions
diff --git a/yql/essentials/sql/v1/context.cpp b/yql/essentials/sql/v1/context.cpp
new file mode 100644
index 00000000000..4637a3be9e9
--- /dev/null
+++ b/yql/essentials/sql/v1/context.cpp
@@ -0,0 +1,656 @@
+#include "context.h"
+
+#include <yql/essentials/providers/common/provider/yql_provider_names.h>
+#include <yql/essentials/utils/yql_panic.h>
+#include <yql/essentials/utils/yql_paths.h>
+
+#include <util/folder/pathsplit.h>
+#include <util/string/join.h>
+#include <util/stream/null.h>
+
+#ifdef GetMessage
+#undef GetMessage
+#endif
+
+using namespace NYql;
+
+namespace NSQLTranslationV1 {
+
+namespace {
+
+TNodePtr AddTablePathPrefix(TContext& ctx, TStringBuf prefixPath, const TDeferredAtom& path) {
+ if (prefixPath.empty()) {
+ return path.Build();
+ }
+
+ if (path.GetLiteral()) {
+ return BuildQuotedAtom(path.Build()->GetPos(), BuildTablePath(prefixPath, *path.GetLiteral()));
+ }
+
+ auto pathNode = path.Build();
+ pathNode = new TCallNodeImpl(pathNode->GetPos(), "String", { pathNode });
+ auto prefixNode = BuildLiteralRawString(pathNode->GetPos(), TString(prefixPath));
+
+ TNodePtr buildPathNode = new TCallNodeImpl(pathNode->GetPos(), "BuildTablePath", { prefixNode, pathNode });
+
+ TDeferredAtom result;
+ MakeTableFromExpression(ctx.Pos(), ctx, buildPathNode, result);
+ return result.Build();
+}
+
+typedef bool TContext::*TPragmaField;
+
+THashMap<TStringBuf, TPragmaField> CTX_PRAGMA_FIELDS = {
+ {"AnsiOptionalAs", &TContext::AnsiOptionalAs},
+ {"WarnOnAnsiAliasShadowing", &TContext::WarnOnAnsiAliasShadowing},
+ {"PullUpFlatMapOverJoin", &TContext::PragmaPullUpFlatMapOverJoin},
+ {"FilterPushdownOverJoinOptionalSide", &TContext::FilterPushdownOverJoinOptionalSide},
+ {"RotateJoinTree", &TContext::RotateJoinTree},
+ {"DqEngineEnable", &TContext::DqEngineEnable},
+ {"DqEngineForce", &TContext::DqEngineForce},
+ {"RegexUseRe2", &TContext::PragmaRegexUseRe2},
+ {"OrderedColumns", &TContext::OrderedColumns},
+ {"BogousStarInGroupByOverJoin", &TContext::BogousStarInGroupByOverJoin},
+ {"CoalesceJoinKeysOnQualifiedAll", &TContext::CoalesceJoinKeysOnQualifiedAll},
+ {"UnorderedSubqueries", &TContext::UnorderedSubqueries},
+ {"FlexibleTypes", &TContext::FlexibleTypes},
+ {"AnsiCurrentRow", &TContext::AnsiCurrentRow},
+ {"EmitStartsWith", &TContext::EmitStartsWith},
+ {"AnsiLike", &TContext::AnsiLike},
+ {"UseBlocks", &TContext::UseBlocks},
+ {"BlockEngineEnable", &TContext::BlockEngineEnable},
+ {"BlockEngineForce", &TContext::BlockEngineForce},
+ {"UnorderedResult", &TContext::UnorderedResult},
+ {"CompactNamedExprs", &TContext::CompactNamedExprs},
+ {"ValidateUnusedExprs", &TContext::ValidateUnusedExprs},
+ {"AnsiImplicitCrossJoin", &TContext::AnsiImplicitCrossJoin},
+ {"DistinctOverWindow", &TContext::DistinctOverWindow},
+};
+
+typedef TMaybe<bool> TContext::*TPragmaMaybeField;
+
+THashMap<TStringBuf, TPragmaMaybeField> CTX_PRAGMA_MAYBE_FIELDS = {
+ {"AnsiRankForNullableKeys", &TContext::AnsiRankForNullableKeys},
+ {"AnsiInForEmptyOrNullableItemsCollections", &TContext::AnsiInForEmptyOrNullableItemsCollections},
+ {"EmitAggApply", &TContext::EmitAggApply},
+ {"CompactGroupBy", &TContext::CompactGroupBy},
+};
+
+} // namespace
+
+TContext::TContext(const NSQLTranslation::TTranslationSettings& settings,
+ const NSQLTranslation::TSQLHints& hints,
+ TIssues& issues)
+ : ClusterMapping(settings.ClusterMapping)
+ , PathPrefix(settings.PathPrefix)
+ , ClusterPathPrefixes(settings.ClusterPathPrefixes)
+ , SQLHints(hints)
+ , Settings(settings)
+ , Pool(new TMemoryPool(4096))
+ , Issues(issues)
+ , IncrementMonCounterFunction(settings.IncrementCounter)
+ , HasPendingErrors(false)
+ , DqEngineEnable(Settings.DqDefaultAuto->Allow())
+ , AnsiQuotedIdentifiers(settings.AnsiLexer)
+ , BlockEngineEnable(Settings.BlockDefaultAuto->Allow())
+{
+ for (auto lib : settings.Libraries) {
+ Libraries.emplace(lib, TLibraryStuff());
+ }
+
+ Scoped = MakeIntrusive<TScopedState>();
+ AllScopes.push_back(Scoped);
+ Scoped->UnicodeLiterals = settings.UnicodeLiterals;
+ if (settings.DefaultCluster) {
+ Scoped->CurrCluster = TDeferredAtom({}, settings.DefaultCluster);
+ auto provider = GetClusterProvider(settings.DefaultCluster);
+ YQL_ENSURE(provider);
+ Scoped->CurrService = *provider;
+ }
+
+ Position.File = settings.File;
+
+ for (auto& flag: settings.Flags) {
+ bool value = true;
+ TStringBuf key = flag;
+ auto ptr = CTX_PRAGMA_FIELDS.FindPtr(key);
+ auto ptrMaybe = CTX_PRAGMA_MAYBE_FIELDS.FindPtr(key);
+ if (!ptr && !ptrMaybe && key.SkipPrefix("Disable")) {
+ value = false;
+ ptr = CTX_PRAGMA_FIELDS.FindPtr(key);
+ ptrMaybe = CTX_PRAGMA_MAYBE_FIELDS.FindPtr(key);
+ }
+ if (ptr) {
+ this->*(*ptr) = value;
+ } else if (ptrMaybe) {
+ this->*(*ptrMaybe) = value;
+ }
+ }
+ DiscoveryMode = (NSQLTranslation::ESqlMode::DISCOVERY == Settings.Mode);
+}
+
+TContext::~TContext()
+{
+ for (auto& x: AllScopes) {
+ x->Clear();
+ }
+}
+
+const NYql::TPosition& TContext::Pos() const {
+ return Position;
+}
+
+TString TContext::MakeName(const TString& name) {
+ auto iter = GenIndexes.find(name);
+ if (iter == GenIndexes.end()) {
+ iter = GenIndexes.emplace(name, 0).first;
+ }
+ TStringBuilder str;
+ str << name << iter->second;
+ ++iter->second;
+ return str;
+}
+
+void TContext::PushCurrentBlocks(TBlocks* blocks) {
+ YQL_ENSURE(blocks);
+ CurrentBlocks.push_back(blocks);
+}
+
+void TContext::PopCurrentBlocks() {
+ YQL_ENSURE(!CurrentBlocks.empty());
+ CurrentBlocks.pop_back();
+}
+
+TBlocks& TContext::GetCurrentBlocks() const {
+ YQL_ENSURE(!CurrentBlocks.empty());
+ return *CurrentBlocks.back();
+}
+
+IOutputStream& TContext::Error(NYql::TIssueCode code) {
+ return Error(Pos(), code);
+}
+
+IOutputStream& TContext::Error(NYql::TPosition pos, NYql::TIssueCode code) {
+ HasPendingErrors = true;
+ return MakeIssue(TSeverityIds::S_ERROR, code, pos);
+}
+
+IOutputStream& TContext::Warning(NYql::TPosition pos, NYql::TIssueCode code) {
+ return MakeIssue(TSeverityIds::S_WARNING, code, pos);
+}
+
+IOutputStream& TContext::Info(NYql::TPosition pos) {
+ return MakeIssue(TSeverityIds::S_INFO, TIssuesIds::INFO, pos);
+}
+
+void TContext::SetWarningPolicyFor(NYql::TIssueCode code, NYql::EWarningAction action) {
+ TString codePattern = ToString(code);
+ TString actionString = ToString(action);
+
+ TWarningRule rule;
+ TString parseError;
+ auto parseResult = TWarningRule::ParseFrom(codePattern, actionString, rule, parseError);
+ YQL_ENSURE(parseResult == TWarningRule::EParseResult::PARSE_OK);
+ WarningPolicy.AddRule(rule);
+}
+
+TVector<NSQLTranslation::TSQLHint> TContext::PullHintForToken(NYql::TPosition tokenPos) {
+ TVector<NSQLTranslation::TSQLHint> result;
+ auto it = SQLHints.find(tokenPos);
+ if (it == SQLHints.end()) {
+ return result;
+ }
+ result = std::move(it->second);
+ SQLHints.erase(it);
+ return result;
+}
+
+void TContext::WarnUnusedHints() {
+ if (!SQLHints.empty()) {
+ // warn about first unused hint
+ auto firstUnused = SQLHints.begin();
+ YQL_ENSURE(!firstUnused->second.empty());
+ const NSQLTranslation::TSQLHint& hint = firstUnused->second.front();
+ Warning(hint.Pos, TIssuesIds::YQL_UNUSED_HINT) << "Hint " << hint.Name << " will not be used";
+ }
+}
+
+IOutputStream& TContext::MakeIssue(ESeverity severity, TIssueCode code, NYql::TPosition pos) {
+ if (severity == TSeverityIds::S_WARNING) {
+ auto action = WarningPolicy.GetAction(code);
+ if (action == EWarningAction::ERROR) {
+ severity = TSeverityIds::S_ERROR;
+ HasPendingErrors = true;
+ } else if (action == EWarningAction::DISABLE) {
+ return Cnull;
+ }
+ }
+
+ // we have the last cell for issue, let's fill it with our internal error
+ if (severity >= TSeverityIds::S_WARNING) {
+ const bool aboveHalf = Issues.Size() > Settings.MaxErrors / 2;
+ if (aboveHalf) {
+ return Cnull;
+ }
+ } else {
+ if (Settings.MaxErrors == Issues.Size() + 1) {
+ Issues.AddIssue(TIssue(NYql::TPosition(), TString(TStringBuf("Too many issues"))));
+ Issues.back().SetCode(UNEXPECTED_ERROR, TSeverityIds::S_ERROR);
+ }
+
+ if (Settings.MaxErrors <= Issues.Size()) {
+ ythrow NProtoAST::TTooManyErrors() << "Too many issues";
+ }
+ }
+
+ Issues.AddIssue(TIssue(pos, TString()));
+ auto& curIssue = Issues.back();
+ curIssue.Severity = severity;
+ curIssue.IssueCode = code;
+ IssueMsgHolder.Reset(new TStringOutput(*Issues.back().MutableMessage()));
+ return *IssueMsgHolder;
+}
+
+bool TContext::IsDynamicCluster(const TDeferredAtom& cluster) const {
+ const TString* clusterPtr = cluster.GetLiteral();
+ if (!clusterPtr) {
+ return false;
+ }
+ TString unused;
+ if (ClusterMapping.GetClusterProvider(*clusterPtr, unused)) {
+ return false;
+ }
+ if (Settings.AssumeYdbOnClusterWithSlash && clusterPtr->StartsWith('/')) {
+ return false;
+ }
+ return !Settings.DynamicClusterProvider.empty();
+}
+
+bool TContext::SetPathPrefix(const TString& value, TMaybe<TString> arg) {
+ if (arg.Defined()) {
+ if (*arg == YtProviderName
+ || *arg == KikimrProviderName
+ || *arg == RtmrProviderName
+ )
+ {
+ ProviderPathPrefixes[*arg] = value;
+ return true;
+ }
+
+ TString normalizedClusterName;
+ if (!GetClusterProvider(*arg, normalizedClusterName)) {
+ Error() << "Unknown cluster or provider: " << *arg;
+ IncrementMonCounter("sql_errors", "BadPragmaValue");
+ return false;
+ }
+
+ ClusterPathPrefixes[normalizedClusterName] = value;
+ } else {
+ PathPrefix = value;
+ }
+
+ return true;
+}
+
+TNodePtr TContext::GetPrefixedPath(const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& path) {
+ TStringBuf prefixPath = GetPrefixPath(service, cluster);
+ if (prefixPath) {
+ return AddTablePathPrefix(*this, prefixPath, path);
+ }
+ return path.Build();
+}
+
+TStringBuf TContext::GetPrefixPath(const TString& service, const TDeferredAtom& cluster) const {
+ if (IsDynamicCluster(cluster)) {
+ return {};
+ }
+ auto* clusterPrefix = cluster.GetLiteral()
+ ? ClusterPathPrefixes.FindPtr(*cluster.GetLiteral())
+ : nullptr;
+ if (clusterPrefix && !clusterPrefix->empty()) {
+ return *clusterPrefix;
+ } else {
+ auto* providerPrefix = ProviderPathPrefixes.FindPtr(service);
+ if (providerPrefix && !providerPrefix->empty()) {
+ return *providerPrefix;
+ } else if (!PathPrefix.empty()) {
+ return PathPrefix;
+ }
+ return {};
+ }
+}
+
+TNodePtr TContext::UniversalAlias(const TString& baseName, TNodePtr&& node) {
+ auto alias = MakeName(baseName);
+ UniversalAliases.emplace(alias, node);
+ return BuildAtom(node->GetPos(), alias, TNodeFlags::Default);
+}
+
+bool TContext::IsAlreadyDeclared(const TString& varName) const {
+ return Variables.find(varName) != Variables.end() && !WeakVariables.contains(varName);
+}
+
+void TContext::DeclareVariable(const TString& varName, const TPosition& pos, const TNodePtr& typeNode, bool isWeak) {
+ if (isWeak) {
+ auto inserted = Variables.emplace(varName, std::make_pair(pos, typeNode));
+ YQL_ENSURE(inserted.second);
+ WeakVariables.insert(varName);
+ } else {
+ WeakVariables.erase(WeakVariables.find(varName));
+ Variables[varName] = std::make_pair(pos, typeNode);
+ }
+}
+
+bool TContext::AddExport(TPosition pos, const TString& name) {
+ if (IsAnonymousName(name)) {
+ Error(pos) << "Can not export anonymous name " << name;
+ return false;
+ }
+ if (Exports.contains(name)) {
+ Error(pos) << "Duplicate export symbol: " << name;
+ return false;
+ }
+ if (!Scoped->LookupNode(name)) {
+ Error(pos) << "Unable to export unknown symbol: " << name;
+ return false;
+ }
+ Exports.emplace(name);
+ return true;
+}
+
+TString TContext::AddImport(const TVector<TString>& modulePath) {
+ YQL_ENSURE(!modulePath.empty());
+ TString path = JoinRange("/", modulePath.cbegin(), modulePath.cend());
+ if (!path.StartsWith('/')) {
+ path = Settings.FileAliasPrefix + path;
+ }
+
+ auto iter = ImportModuleAliases.find(path);
+ if (iter == ImportModuleAliases.end()) {
+ const TString alias = MakeName(TStringBuilder() << modulePath.back() << "_module");
+ iter = ImportModuleAliases.emplace(path, alias).first;
+ }
+ return iter->second;
+}
+
+TString TContext::AddSimpleUdf(const TString& udf) {
+ auto& name = SimpleUdfs[udf];
+ if (name.empty()) {
+ name = TStringBuilder() << "Udf" << SimpleUdfs.size();
+ }
+
+ return name;
+}
+
+void TContext::SetPackageVersion(const TString& packageName, ui32 version) {
+ PackageVersions[packageName] = version;
+}
+
+void TScopedState::UseCluster(const TString& service, const TDeferredAtom& cluster) {
+ YQL_ENSURE(!cluster.Empty());
+ if (cluster.GetLiteral()) {
+ if (!Local.UsedPlainClusters.insert(*cluster.GetLiteral()).second) {
+ return;
+ }
+ } else {
+ if (!Local.UsedExprClusters.insert(cluster.Build().Get()).second) {
+ return;
+ }
+ }
+ Local.UsedClusters.push_back({service, cluster});
+}
+
+void TScopedState::AddExprCluster(TNodePtr expr, TContext& ctx) {
+ auto node = expr.Get();
+ if (Local.ExprClustersMap.count(node)) {
+ return;
+ }
+ auto name = ctx.MakeName("cluster");
+ auto wrappedNode = expr->Y("EvaluateAtom", expr);
+ Local.ExprClustersMap.insert({node, {name, wrappedNode}});
+ Local.ExprClusters.push_back(expr);
+}
+
+const TVector<std::pair<TString, TDeferredAtom>>& TScopedState::GetUsedClusters() {
+ return Local.UsedClusters;
+}
+
+TNodePtr TScopedState::WrapCluster(const TDeferredAtom& cluster, TContext& ctx) {
+ auto node = cluster.Build();
+ if (!cluster.GetLiteral()) {
+ if (ctx.CompactNamedExprs) {
+ return node->Y("EvaluateAtom", node);
+ }
+ AddExprCluster(node, ctx);
+ auto exprIt = Local.ExprClustersMap.find(node.Get());
+ YQL_ENSURE(exprIt != Local.ExprClustersMap.end());
+ return node->AstNode(exprIt->second.first);
+ }
+
+ return node;
+}
+
+void TScopedState::Clear() {
+ *this = TScopedState();
+}
+
+TNodePtr TScopedState::LookupNode(const TString& name) {
+ auto mapIt = NamedNodes.find(name);
+ if (mapIt == NamedNodes.end()) {
+ return nullptr;
+ }
+ Y_DEBUG_ABORT_UNLESS(!mapIt->second.empty());
+ mapIt->second.front()->IsUsed = true;
+ return mapIt->second.front()->Node->Clone();
+}
+
+bool TContext::HasNonYtProvider(const ISource& source) const {
+ TTableList tableList;
+ source.GetInputTables(tableList);
+
+ TSet<TString> clusters;
+ for (auto& it: tableList) {
+ if (it.Service != YtProviderName) {
+ return true;
+ }
+ }
+
+ for (auto& cl: Scoped->Local.UsedClusters) {
+ if (cl.first != YtProviderName) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool TContext::UseUnordered(const ISource& source) const {
+ return !HasNonYtProvider(source);
+}
+
+bool TContext::UseUnordered(const TTableRef& table) const {
+ return YtProviderName == table.Service;
+}
+
+
+TMaybe<EColumnRefState> GetFunctionArgColumnStatus(TContext& ctx, const TString& module, const TString& func, size_t argIndex) {
+ static const TSet<TStringBuf> denyForAllArgs = {
+ "datatype",
+ "optionaltype",
+ "listtype",
+ "streamtype",
+ "dicttype",
+ "tupletype",
+ "resourcetype",
+ "taggedtype",
+ "varianttype",
+ "callabletype",
+ "optionalitemtype",
+ "listitemtype",
+ "streamitemtype",
+ "dictkeytype",
+ "dictpayloadtype",
+ "tupleelementtype",
+ "structmembertype",
+ "callableresulttype",
+ "callableargumenttype",
+ "variantunderlyingtype",
+ };
+ static const TMap<std::pair<TStringBuf, size_t>, EColumnRefState> positionalArgsCustomStatus = {
+ { {"frombytes", 1}, EColumnRefState::Deny },
+ { {"enum", 0}, EColumnRefState::Deny },
+ { {"asenum", 0}, EColumnRefState::Deny },
+ { {"variant", 1}, EColumnRefState::Deny },
+ { {"variant", 2}, EColumnRefState::Deny },
+ { {"asvariant", 1}, EColumnRefState::Deny },
+ { {"astagged", 1}, EColumnRefState::Deny },
+ { {"ensuretype", 1}, EColumnRefState::Deny },
+ { {"ensuretype", 2}, EColumnRefState::Deny },
+ { {"ensureconvertibleto", 1}, EColumnRefState::Deny },
+ { {"ensureconvertibleto", 2}, EColumnRefState::Deny },
+
+ { {"nothing", 0}, EColumnRefState::Deny },
+ { {"formattype", 0}, EColumnRefState::Deny },
+ { {"instanceof", 0}, EColumnRefState::Deny },
+ { {"pgtype", 0}, EColumnRefState::AsPgType },
+ { {"pgconst", 0}, EColumnRefState::Deny },
+ { {"pgconst", 1}, EColumnRefState::AsPgType },
+ { {"pgcast", 1}, EColumnRefState::AsPgType },
+
+ { {"unpickle", 0}, EColumnRefState::Deny },
+ { {"typehandle", 0}, EColumnRefState::Deny },
+
+ { {"listcreate", 0}, EColumnRefState::Deny },
+ { {"setcreate", 0}, EColumnRefState::Deny },
+ { {"dictcreate", 0}, EColumnRefState::Deny },
+ { {"dictcreate", 1}, EColumnRefState::Deny },
+ { {"weakfield", 1}, EColumnRefState::Deny },
+
+ { {"Yson::ConvertTo", 1}, EColumnRefState::Deny },
+ };
+
+ TString normalized;
+ if (module.empty()) {
+ normalized = to_lower(func);
+ } else if (to_upper(module) == "YQL") {
+ normalized = "YQL::" + func;
+ } else {
+ normalized = module + "::" + func;
+ }
+
+ if (normalized == "typeof" && argIndex == 0) {
+ // TODO: more such cases?
+ return ctx.GetTopLevelColumnReferenceState();
+ }
+
+ if (denyForAllArgs.contains(normalized)) {
+ return EColumnRefState::Deny;
+ }
+
+ auto it = positionalArgsCustomStatus.find(std::make_pair(normalized, argIndex));
+ if (it != positionalArgsCustomStatus.end()) {
+ return it->second;
+ }
+ return {};
+}
+
+TTranslation::TTranslation(TContext& ctx)
+ : Ctx(ctx)
+{
+}
+
+TContext& TTranslation::Context() {
+ return Ctx;
+}
+
+IOutputStream& TTranslation::Error() {
+ return Ctx.Error();
+}
+
+TNodePtr TTranslation::GetNamedNode(const TString& name) {
+ if (name == "$_") {
+ Ctx.Error() << "Unable to reference anonymous name " << name;
+ return nullptr;
+ }
+ auto res = Ctx.Scoped->LookupNode(name);
+ if (!res) {
+ Ctx.Error() << "Unknown name: " << name;
+ }
+ return SafeClone(res);
+}
+
+TString TTranslation::PushNamedNode(TPosition namePos, const TString& name, const TNodeBuilderByName& builder) {
+ TString resultName = name;
+ if (IsAnonymousName(name)) {
+ resultName = "$_yql_anonymous_name_" + ToString(Ctx.AnonymousNameIndex++);
+ YQL_ENSURE(Ctx.Scoped->NamedNodes.find(resultName) == Ctx.Scoped->NamedNodes.end());
+ }
+ auto node = builder(resultName);
+ Y_DEBUG_ABORT_UNLESS(node);
+ auto mapIt = Ctx.Scoped->NamedNodes.find(resultName);
+ if (mapIt == Ctx.Scoped->NamedNodes.end()) {
+ auto result = Ctx.Scoped->NamedNodes.insert(std::make_pair(resultName, TDeque<TNodeWithUsageInfoPtr>()));
+ Y_DEBUG_ABORT_UNLESS(result.second);
+ mapIt = result.first;
+ }
+
+ mapIt->second.push_front(MakeIntrusive<TNodeWithUsageInfo>(node, namePos, Ctx.ScopeLevel));
+ return resultName;
+}
+
+TString TTranslation::PushNamedNode(NYql::TPosition namePos, const TString &name, NSQLTranslationV1::TNodePtr node) {
+ return PushNamedNode(namePos, name, [node](const TString&) { return node; });
+}
+
+TString TTranslation::PushNamedAtom(TPosition namePos, const TString& name) {
+ auto buildAtom = [namePos](const TString& resultName) {
+ return BuildAtom(namePos, resultName);
+ };
+ return PushNamedNode(namePos, name, buildAtom);
+}
+
+void TTranslation::PopNamedNode(const TString& name) {
+ auto mapIt = Ctx.Scoped->NamedNodes.find(name);
+ Y_DEBUG_ABORT_UNLESS(mapIt != Ctx.Scoped->NamedNodes.end());
+ Y_DEBUG_ABORT_UNLESS(mapIt->second.size() > 0);
+ auto& top = mapIt->second.front();
+ if (!top->IsUsed && !Ctx.HasPendingErrors && !name.StartsWith("$_")) {
+ Ctx.Warning(top->NamePos, TIssuesIds::YQL_UNUSED_SYMBOL) << "Symbol " << name << " is not used";
+ }
+ mapIt->second.pop_front();
+ if (mapIt->second.empty()) {
+ Ctx.Scoped->NamedNodes.erase(mapIt);
+ }
+}
+
+void TTranslation::WarnUnusedNodes() const {
+ if (Ctx.HasPendingErrors) {
+ // result is not reliable in this case
+ return;
+ }
+ for (const auto& [name, items]: Ctx.Scoped->NamedNodes) {
+ if (name.StartsWith("$_")) {
+ continue;
+ }
+ for (const auto& item : items) {
+ if (!item->IsUsed && item->Level == Ctx.ScopeLevel) {
+ Ctx.Warning(item->NamePos, TIssuesIds::YQL_UNUSED_SYMBOL) << "Symbol " << name << " is not used";
+ }
+ }
+ }
+}
+
+TString GetDescription(const google::protobuf::Message& node, const google::protobuf::FieldDescriptor* d) {
+ const auto& field = node.GetReflection()->GetMessage(node, d);
+ return field.GetReflection()->GetString(field, d->message_type()->FindFieldByName("Descr"));
+}
+
+TString TTranslation::AltDescription(const google::protobuf::Message& node, ui32 altCase, const google::protobuf::Descriptor* descr) const {
+ return GetDescription(node, descr->FindFieldByNumber(altCase));
+}
+
+void TTranslation::AltNotImplemented(const TString& ruleName, ui32 altCase, const google::protobuf::Message& node, const google::protobuf::Descriptor* descr) {
+ Error() << ruleName << ": alternative is not implemented yet: " << AltDescription(node, altCase, descr);
+}
+
+} // namespace NSQLTranslationV1