context.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. #include "context.h"
  2. #include <yql/essentials/providers/common/provider/yql_provider_names.h>
  3. #include <yql/essentials/utils/yql_panic.h>
  4. #include <yql/essentials/utils/yql_paths.h>
  5. #include <util/folder/pathsplit.h>
  6. #include <util/string/join.h>
  7. #include <util/stream/null.h>
  8. #ifdef GetMessage
  9. #undef GetMessage
  10. #endif
  11. using namespace NYql;
  12. namespace NSQLTranslationV1 {
  13. namespace {
  14. TNodePtr AddTablePathPrefix(TContext& ctx, TStringBuf prefixPath, const TDeferredAtom& path) {
  15. if (prefixPath.empty()) {
  16. return path.Build();
  17. }
  18. if (path.GetLiteral()) {
  19. return BuildQuotedAtom(path.Build()->GetPos(), BuildTablePath(prefixPath, *path.GetLiteral()));
  20. }
  21. auto pathNode = path.Build();
  22. pathNode = new TCallNodeImpl(pathNode->GetPos(), "String", { pathNode });
  23. auto prefixNode = BuildLiteralRawString(pathNode->GetPos(), TString(prefixPath));
  24. TNodePtr buildPathNode = new TCallNodeImpl(pathNode->GetPos(), "BuildTablePath", { prefixNode, pathNode });
  25. TDeferredAtom result;
  26. MakeTableFromExpression(ctx.Pos(), ctx, buildPathNode, result);
  27. return result.Build();
  28. }
  29. typedef bool TContext::*TPragmaField;
  30. THashMap<TStringBuf, TPragmaField> CTX_PRAGMA_FIELDS = {
  31. {"AnsiOptionalAs", &TContext::AnsiOptionalAs},
  32. {"WarnOnAnsiAliasShadowing", &TContext::WarnOnAnsiAliasShadowing},
  33. {"PullUpFlatMapOverJoin", &TContext::PragmaPullUpFlatMapOverJoin},
  34. {"FilterPushdownOverJoinOptionalSide", &TContext::FilterPushdownOverJoinOptionalSide},
  35. {"RotateJoinTree", &TContext::RotateJoinTree},
  36. {"DqEngineEnable", &TContext::DqEngineEnable},
  37. {"DqEngineForce", &TContext::DqEngineForce},
  38. {"RegexUseRe2", &TContext::PragmaRegexUseRe2},
  39. {"OrderedColumns", &TContext::OrderedColumns},
  40. {"BogousStarInGroupByOverJoin", &TContext::BogousStarInGroupByOverJoin},
  41. {"CoalesceJoinKeysOnQualifiedAll", &TContext::CoalesceJoinKeysOnQualifiedAll},
  42. {"UnorderedSubqueries", &TContext::UnorderedSubqueries},
  43. {"FlexibleTypes", &TContext::FlexibleTypes},
  44. {"AnsiCurrentRow", &TContext::AnsiCurrentRow},
  45. {"EmitStartsWith", &TContext::EmitStartsWith},
  46. {"AnsiLike", &TContext::AnsiLike},
  47. {"UseBlocks", &TContext::UseBlocks},
  48. {"EmitTableSource", &TContext::EmitTableSource},
  49. {"BlockEngineEnable", &TContext::BlockEngineEnable},
  50. {"BlockEngineForce", &TContext::BlockEngineForce},
  51. {"UnorderedResult", &TContext::UnorderedResult},
  52. {"CompactNamedExprs", &TContext::CompactNamedExprs},
  53. {"ValidateUnusedExprs", &TContext::ValidateUnusedExprs},
  54. {"AnsiImplicitCrossJoin", &TContext::AnsiImplicitCrossJoin},
  55. {"DistinctOverWindow", &TContext::DistinctOverWindow},
  56. {"EmitUnionMerge", &TContext::EmitUnionMerge},
  57. {"SeqMode", &TContext::SeqMode},
  58. };
  59. typedef TMaybe<bool> TContext::*TPragmaMaybeField;
  60. THashMap<TStringBuf, TPragmaMaybeField> CTX_PRAGMA_MAYBE_FIELDS = {
  61. {"AnsiRankForNullableKeys", &TContext::AnsiRankForNullableKeys},
  62. {"AnsiInForEmptyOrNullableItemsCollections", &TContext::AnsiInForEmptyOrNullableItemsCollections},
  63. {"EmitAggApply", &TContext::EmitAggApply},
  64. {"CompactGroupBy", &TContext::CompactGroupBy},
  65. };
  66. } // namespace
  67. TContext::TContext(const TLexers& lexers, const TParsers& parsers,
  68. const NSQLTranslation::TTranslationSettings& settings,
  69. const NSQLTranslation::TSQLHints& hints,
  70. TIssues& issues,
  71. const TString& query)
  72. : Lexers(lexers)
  73. , Parsers(parsers)
  74. , ClusterMapping(settings.ClusterMapping)
  75. , PathPrefix(settings.PathPrefix)
  76. , ClusterPathPrefixes(settings.ClusterPathPrefixes)
  77. , SQLHints(hints)
  78. , Settings(settings)
  79. , Query(query)
  80. , Pool(new TMemoryPool(4096))
  81. , Issues(issues)
  82. , IncrementMonCounterFunction(settings.IncrementCounter)
  83. , HasPendingErrors(false)
  84. , DqEngineEnable(Settings.DqDefaultAuto->Allow())
  85. , AnsiQuotedIdentifiers(settings.AnsiLexer)
  86. , BlockEngineEnable(Settings.BlockDefaultAuto->Allow())
  87. {
  88. for (auto lib : settings.Libraries) {
  89. Libraries.emplace(lib, TLibraryStuff());
  90. }
  91. Scoped = MakeIntrusive<TScopedState>();
  92. AllScopes.push_back(Scoped);
  93. Scoped->UnicodeLiterals = settings.UnicodeLiterals;
  94. if (settings.DefaultCluster) {
  95. Scoped->CurrCluster = TDeferredAtom({}, settings.DefaultCluster);
  96. auto provider = GetClusterProvider(settings.DefaultCluster);
  97. YQL_ENSURE(provider);
  98. Scoped->CurrService = *provider;
  99. }
  100. Position.File = settings.File;
  101. for (auto& flag: settings.Flags) {
  102. bool value = true;
  103. TStringBuf key = flag;
  104. auto ptr = CTX_PRAGMA_FIELDS.FindPtr(key);
  105. auto ptrMaybe = CTX_PRAGMA_MAYBE_FIELDS.FindPtr(key);
  106. if (!ptr && !ptrMaybe && key.SkipPrefix("Disable")) {
  107. value = false;
  108. ptr = CTX_PRAGMA_FIELDS.FindPtr(key);
  109. ptrMaybe = CTX_PRAGMA_MAYBE_FIELDS.FindPtr(key);
  110. }
  111. if (ptr) {
  112. this->*(*ptr) = value;
  113. } else if (ptrMaybe) {
  114. this->*(*ptrMaybe) = value;
  115. }
  116. }
  117. DiscoveryMode = (NSQLTranslation::ESqlMode::DISCOVERY == Settings.Mode);
  118. }
  119. TContext::~TContext()
  120. {
  121. for (auto& x: AllScopes) {
  122. x->Clear();
  123. }
  124. }
  125. const NYql::TPosition& TContext::Pos() const {
  126. return Position;
  127. }
  128. TString TContext::MakeName(const TString& name) {
  129. auto iter = GenIndexes.find(name);
  130. if (iter == GenIndexes.end()) {
  131. iter = GenIndexes.emplace(name, 0).first;
  132. }
  133. TStringBuilder str;
  134. str << name << iter->second;
  135. ++iter->second;
  136. return str;
  137. }
  138. void TContext::PushCurrentBlocks(TBlocks* blocks) {
  139. YQL_ENSURE(blocks);
  140. CurrentBlocks.push_back(blocks);
  141. }
  142. void TContext::PopCurrentBlocks() {
  143. YQL_ENSURE(!CurrentBlocks.empty());
  144. CurrentBlocks.pop_back();
  145. }
  146. TBlocks& TContext::GetCurrentBlocks() const {
  147. YQL_ENSURE(!CurrentBlocks.empty());
  148. return *CurrentBlocks.back();
  149. }
  150. IOutputStream& TContext::Error(NYql::TIssueCode code) {
  151. return Error(Pos(), code);
  152. }
  153. IOutputStream& TContext::Error(NYql::TPosition pos, NYql::TIssueCode code) {
  154. HasPendingErrors = true;
  155. return MakeIssue(TSeverityIds::S_ERROR, code, pos);
  156. }
  157. IOutputStream& TContext::Warning(NYql::TPosition pos, NYql::TIssueCode code) {
  158. return MakeIssue(TSeverityIds::S_WARNING, code, pos);
  159. }
  160. IOutputStream& TContext::Info(NYql::TPosition pos) {
  161. return MakeIssue(TSeverityIds::S_INFO, TIssuesIds::INFO, pos);
  162. }
  163. void TContext::SetWarningPolicyFor(NYql::TIssueCode code, NYql::EWarningAction action) {
  164. TString codePattern = ToString(code);
  165. TString actionString = ToString(action);
  166. TWarningRule rule;
  167. TString parseError;
  168. auto parseResult = TWarningRule::ParseFrom(codePattern, actionString, rule, parseError);
  169. YQL_ENSURE(parseResult == TWarningRule::EParseResult::PARSE_OK);
  170. WarningPolicy.AddRule(rule);
  171. }
  172. TVector<NSQLTranslation::TSQLHint> TContext::PullHintForToken(NYql::TPosition tokenPos) {
  173. TVector<NSQLTranslation::TSQLHint> result;
  174. auto it = SQLHints.find(tokenPos);
  175. if (it == SQLHints.end()) {
  176. return result;
  177. }
  178. result = std::move(it->second);
  179. SQLHints.erase(it);
  180. return result;
  181. }
  182. void TContext::WarnUnusedHints() {
  183. if (!SQLHints.empty()) {
  184. // warn about first unused hint
  185. auto firstUnused = SQLHints.begin();
  186. YQL_ENSURE(!firstUnused->second.empty());
  187. const NSQLTranslation::TSQLHint& hint = firstUnused->second.front();
  188. Warning(hint.Pos, TIssuesIds::YQL_UNUSED_HINT) << "Hint " << hint.Name << " will not be used";
  189. }
  190. }
  191. IOutputStream& TContext::MakeIssue(ESeverity severity, TIssueCode code, NYql::TPosition pos) {
  192. if (severity == TSeverityIds::S_WARNING) {
  193. auto action = WarningPolicy.GetAction(code);
  194. if (action == EWarningAction::ERROR) {
  195. severity = TSeverityIds::S_ERROR;
  196. HasPendingErrors = true;
  197. } else if (action == EWarningAction::DISABLE) {
  198. return Cnull;
  199. }
  200. }
  201. // we have the last cell for issue, let's fill it with our internal error
  202. if (severity >= TSeverityIds::S_WARNING) {
  203. const bool aboveHalf = Issues.Size() > Settings.MaxErrors / 2;
  204. if (aboveHalf) {
  205. return Cnull;
  206. }
  207. } else {
  208. if (Settings.MaxErrors == Issues.Size() + 1) {
  209. Issues.AddIssue(TIssue(NYql::TPosition(), TString(TStringBuf("Too many issues"))));
  210. Issues.back().SetCode(UNEXPECTED_ERROR, TSeverityIds::S_ERROR);
  211. }
  212. if (Settings.MaxErrors <= Issues.Size()) {
  213. ythrow NProtoAST::TTooManyErrors() << "Too many issues";
  214. }
  215. }
  216. Issues.AddIssue(TIssue(pos, TString()));
  217. auto& curIssue = Issues.back();
  218. curIssue.Severity = severity;
  219. curIssue.IssueCode = code;
  220. IssueMsgHolder.Reset(new TStringOutput(*Issues.back().MutableMessage()));
  221. return *IssueMsgHolder;
  222. }
  223. bool TContext::IsDynamicCluster(const TDeferredAtom& cluster) const {
  224. const TString* clusterPtr = cluster.GetLiteral();
  225. if (!clusterPtr) {
  226. return false;
  227. }
  228. TString unused;
  229. if (ClusterMapping.GetClusterProvider(*clusterPtr, unused)) {
  230. return false;
  231. }
  232. if (Settings.AssumeYdbOnClusterWithSlash && clusterPtr->StartsWith('/')) {
  233. return false;
  234. }
  235. return !Settings.DynamicClusterProvider.empty();
  236. }
  237. bool TContext::SetPathPrefix(const TString& value, TMaybe<TString> arg) {
  238. if (arg.Defined()) {
  239. if (*arg == YtProviderName
  240. || *arg == KikimrProviderName
  241. || *arg == RtmrProviderName
  242. )
  243. {
  244. ProviderPathPrefixes[*arg] = value;
  245. return true;
  246. }
  247. TString normalizedClusterName;
  248. if (!GetClusterProvider(*arg, normalizedClusterName)) {
  249. Error() << "Unknown cluster or provider: " << *arg;
  250. IncrementMonCounter("sql_errors", "BadPragmaValue");
  251. return false;
  252. }
  253. ClusterPathPrefixes[normalizedClusterName] = value;
  254. } else {
  255. PathPrefix = value;
  256. }
  257. return true;
  258. }
  259. TNodePtr TContext::GetPrefixedPath(const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& path) {
  260. TStringBuf prefixPath = GetPrefixPath(service, cluster);
  261. if (prefixPath) {
  262. return AddTablePathPrefix(*this, prefixPath, path);
  263. }
  264. return path.Build();
  265. }
  266. TStringBuf TContext::GetPrefixPath(const TString& service, const TDeferredAtom& cluster) const {
  267. if (IsDynamicCluster(cluster)) {
  268. return {};
  269. }
  270. auto* clusterPrefix = cluster.GetLiteral()
  271. ? ClusterPathPrefixes.FindPtr(*cluster.GetLiteral())
  272. : nullptr;
  273. if (clusterPrefix && !clusterPrefix->empty()) {
  274. return *clusterPrefix;
  275. } else {
  276. auto* providerPrefix = ProviderPathPrefixes.FindPtr(service);
  277. if (providerPrefix && !providerPrefix->empty()) {
  278. return *providerPrefix;
  279. } else if (!PathPrefix.empty()) {
  280. return PathPrefix;
  281. }
  282. return {};
  283. }
  284. }
  285. TNodePtr TContext::UniversalAlias(const TString& baseName, TNodePtr&& node) {
  286. auto alias = MakeName(baseName);
  287. UniversalAliases.emplace(alias, node);
  288. return BuildAtom(node->GetPos(), alias, TNodeFlags::Default);
  289. }
  290. bool TContext::IsAlreadyDeclared(const TString& varName) const {
  291. return Variables.find(varName) != Variables.end() && !WeakVariables.contains(varName);
  292. }
  293. void TContext::DeclareVariable(const TString& varName, const TPosition& pos, const TNodePtr& typeNode, bool isWeak) {
  294. if (isWeak) {
  295. auto inserted = Variables.emplace(varName, std::make_pair(pos, typeNode));
  296. YQL_ENSURE(inserted.second);
  297. WeakVariables.insert(varName);
  298. } else {
  299. WeakVariables.erase(WeakVariables.find(varName));
  300. Variables[varName] = std::make_pair(pos, typeNode);
  301. }
  302. }
  303. bool TContext::AddExport(TPosition pos, const TString& name) {
  304. if (IsAnonymousName(name)) {
  305. Error(pos) << "Can not export anonymous name " << name;
  306. return false;
  307. }
  308. if (!Scoped->LookupNode(name)) {
  309. Error(pos) << "Unable to export unknown symbol: " << name;
  310. return false;
  311. }
  312. Exports.emplace(name);
  313. return true;
  314. }
  315. TString TContext::AddImport(const TVector<TString>& modulePath) {
  316. YQL_ENSURE(!modulePath.empty());
  317. TString path = JoinRange("/", modulePath.cbegin(), modulePath.cend());
  318. if (!path.StartsWith('/')) {
  319. path = Settings.FileAliasPrefix + path;
  320. }
  321. auto iter = ImportModuleAliases.find(path);
  322. if (iter == ImportModuleAliases.end()) {
  323. const TString alias = MakeName(TStringBuilder() << modulePath.back() << "_module");
  324. iter = ImportModuleAliases.emplace(path, alias).first;
  325. }
  326. return iter->second;
  327. }
  328. TString TContext::AddSimpleUdf(const TString& udf) {
  329. auto& name = SimpleUdfs[udf];
  330. if (name.empty()) {
  331. name = TStringBuilder() << "Udf" << SimpleUdfs.size();
  332. }
  333. return name;
  334. }
  335. void TContext::SetPackageVersion(const TString& packageName, ui32 version) {
  336. PackageVersions[packageName] = version;
  337. }
  338. void TScopedState::UseCluster(const TString& service, const TDeferredAtom& cluster) {
  339. YQL_ENSURE(!cluster.Empty());
  340. if (cluster.GetLiteral()) {
  341. if (!Local.UsedPlainClusters.insert(*cluster.GetLiteral()).second) {
  342. return;
  343. }
  344. } else {
  345. if (!Local.UsedExprClusters.insert(cluster.Build().Get()).second) {
  346. return;
  347. }
  348. }
  349. Local.UsedClusters.push_back({service, cluster});
  350. }
  351. void TScopedState::AddExprCluster(TNodePtr expr, TContext& ctx) {
  352. auto node = expr.Get();
  353. if (Local.ExprClustersMap.count(node)) {
  354. return;
  355. }
  356. auto name = ctx.MakeName("cluster");
  357. auto wrappedNode = expr->Y("EvaluateAtom", expr);
  358. Local.ExprClustersMap.insert({node, {name, wrappedNode}});
  359. Local.ExprClusters.push_back(expr);
  360. }
  361. const TVector<std::pair<TString, TDeferredAtom>>& TScopedState::GetUsedClusters() {
  362. return Local.UsedClusters;
  363. }
  364. TNodePtr TScopedState::WrapCluster(const TDeferredAtom& cluster, TContext& ctx) {
  365. auto node = cluster.Build();
  366. if (!cluster.GetLiteral()) {
  367. if (ctx.CompactNamedExprs) {
  368. return node->Y("EvaluateAtom", node);
  369. }
  370. AddExprCluster(node, ctx);
  371. auto exprIt = Local.ExprClustersMap.find(node.Get());
  372. YQL_ENSURE(exprIt != Local.ExprClustersMap.end());
  373. return node->AstNode(exprIt->second.first);
  374. }
  375. return node;
  376. }
  377. void TScopedState::Clear() {
  378. *this = TScopedState();
  379. }
  380. TNodePtr TScopedState::LookupNode(const TString& name) {
  381. auto mapIt = NamedNodes.find(name);
  382. if (mapIt == NamedNodes.end()) {
  383. return nullptr;
  384. }
  385. Y_DEBUG_ABORT_UNLESS(!mapIt->second.empty());
  386. mapIt->second.front()->IsUsed = true;
  387. return mapIt->second.front()->Node->Clone();
  388. }
  389. bool TContext::HasNonYtProvider(const ISource& source) const {
  390. TTableList tableList;
  391. source.GetInputTables(tableList);
  392. TSet<TString> clusters;
  393. for (auto& it: tableList) {
  394. if (it.Service != YtProviderName) {
  395. return true;
  396. }
  397. }
  398. for (auto& cl: Scoped->Local.UsedClusters) {
  399. if (cl.first != YtProviderName) {
  400. return true;
  401. }
  402. }
  403. return false;
  404. }
  405. bool TContext::UseUnordered(const ISource& source) const {
  406. return !HasNonYtProvider(source);
  407. }
  408. bool TContext::UseUnordered(const TTableRef& table) const {
  409. return YtProviderName == table.Service;
  410. }
  411. TMaybe<EColumnRefState> GetFunctionArgColumnStatus(TContext& ctx, const TString& module, const TString& func, size_t argIndex) {
  412. static const TSet<TStringBuf> denyForAllArgs = {
  413. "datatype",
  414. "optionaltype",
  415. "listtype",
  416. "streamtype",
  417. "dicttype",
  418. "tupletype",
  419. "resourcetype",
  420. "taggedtype",
  421. "varianttype",
  422. "callabletype",
  423. "optionalitemtype",
  424. "listitemtype",
  425. "streamitemtype",
  426. "dictkeytype",
  427. "dictpayloadtype",
  428. "tupleelementtype",
  429. "structmembertype",
  430. "callableresulttype",
  431. "callableargumenttype",
  432. "variantunderlyingtype",
  433. };
  434. static const TMap<std::pair<TStringBuf, size_t>, EColumnRefState> positionalArgsCustomStatus = {
  435. { {"frombytes", 1}, EColumnRefState::Deny },
  436. { {"enum", 0}, EColumnRefState::Deny },
  437. { {"asenum", 0}, EColumnRefState::Deny },
  438. { {"variant", 1}, EColumnRefState::Deny },
  439. { {"variant", 2}, EColumnRefState::Deny },
  440. { {"asvariant", 1}, EColumnRefState::Deny },
  441. { {"astagged", 1}, EColumnRefState::Deny },
  442. { {"ensuretype", 1}, EColumnRefState::Deny },
  443. { {"ensuretype", 2}, EColumnRefState::Deny },
  444. { {"ensureconvertibleto", 1}, EColumnRefState::Deny },
  445. { {"ensureconvertibleto", 2}, EColumnRefState::Deny },
  446. { {"nothing", 0}, EColumnRefState::Deny },
  447. { {"formattype", 0}, EColumnRefState::Deny },
  448. { {"instanceof", 0}, EColumnRefState::Deny },
  449. { {"pgtype", 0}, EColumnRefState::AsPgType },
  450. { {"pgconst", 0}, EColumnRefState::Deny },
  451. { {"pgconst", 1}, EColumnRefState::AsPgType },
  452. { {"pgcast", 1}, EColumnRefState::AsPgType },
  453. { {"unpickle", 0}, EColumnRefState::Deny },
  454. { {"typehandle", 0}, EColumnRefState::Deny },
  455. { {"listcreate", 0}, EColumnRefState::Deny },
  456. { {"setcreate", 0}, EColumnRefState::Deny },
  457. { {"dictcreate", 0}, EColumnRefState::Deny },
  458. { {"dictcreate", 1}, EColumnRefState::Deny },
  459. { {"weakfield", 1}, EColumnRefState::Deny },
  460. { {"Yson::ConvertTo", 1}, EColumnRefState::Deny },
  461. };
  462. TString normalized;
  463. if (module.empty()) {
  464. normalized = to_lower(func);
  465. } else if (to_upper(module) == "YQL") {
  466. normalized = "YQL::" + func;
  467. } else {
  468. normalized = module + "::" + func;
  469. }
  470. if (normalized == "typeof" && argIndex == 0) {
  471. // TODO: more such cases?
  472. return ctx.GetTopLevelColumnReferenceState();
  473. }
  474. if (denyForAllArgs.contains(normalized)) {
  475. return EColumnRefState::Deny;
  476. }
  477. auto it = positionalArgsCustomStatus.find(std::make_pair(normalized, argIndex));
  478. if (it != positionalArgsCustomStatus.end()) {
  479. return it->second;
  480. }
  481. return {};
  482. }
  483. TTranslation::TTranslation(TContext& ctx)
  484. : Ctx(ctx)
  485. {
  486. }
  487. TContext& TTranslation::Context() {
  488. return Ctx;
  489. }
  490. IOutputStream& TTranslation::Error() {
  491. return Ctx.Error();
  492. }
  493. TNodePtr TTranslation::GetNamedNode(const TString& name) {
  494. if (name == "$_") {
  495. Ctx.Error() << "Unable to reference anonymous name " << name;
  496. return nullptr;
  497. }
  498. auto res = Ctx.Scoped->LookupNode(name);
  499. if (!res) {
  500. Ctx.Error() << "Unknown name: " << name;
  501. }
  502. return SafeClone(res);
  503. }
  504. TString TTranslation::PushNamedNode(TPosition namePos, const TString& name, const TNodeBuilderByName& builder) {
  505. TString resultName = name;
  506. if (IsAnonymousName(name)) {
  507. resultName = "$_yql_anonymous_name_" + ToString(Ctx.AnonymousNameIndex++);
  508. YQL_ENSURE(Ctx.Scoped->NamedNodes.find(resultName) == Ctx.Scoped->NamedNodes.end());
  509. }
  510. auto node = builder(resultName);
  511. Y_DEBUG_ABORT_UNLESS(node);
  512. auto mapIt = Ctx.Scoped->NamedNodes.find(resultName);
  513. if (mapIt == Ctx.Scoped->NamedNodes.end()) {
  514. auto result = Ctx.Scoped->NamedNodes.insert(std::make_pair(resultName, TDeque<TNodeWithUsageInfoPtr>()));
  515. Y_DEBUG_ABORT_UNLESS(result.second);
  516. mapIt = result.first;
  517. }
  518. mapIt->second.push_front(MakeIntrusive<TNodeWithUsageInfo>(node, namePos, Ctx.ScopeLevel));
  519. return resultName;
  520. }
  521. TString TTranslation::PushNamedNode(NYql::TPosition namePos, const TString &name, NSQLTranslationV1::TNodePtr node) {
  522. return PushNamedNode(namePos, name, [node](const TString&) { return node; });
  523. }
  524. TString TTranslation::PushNamedAtom(TPosition namePos, const TString& name) {
  525. auto buildAtom = [namePos](const TString& resultName) {
  526. return BuildAtom(namePos, resultName);
  527. };
  528. return PushNamedNode(namePos, name, buildAtom);
  529. }
  530. void TTranslation::PopNamedNode(const TString& name) {
  531. auto mapIt = Ctx.Scoped->NamedNodes.find(name);
  532. Y_DEBUG_ABORT_UNLESS(mapIt != Ctx.Scoped->NamedNodes.end());
  533. Y_DEBUG_ABORT_UNLESS(mapIt->second.size() > 0);
  534. auto& top = mapIt->second.front();
  535. if (!top->IsUsed && !Ctx.HasPendingErrors && !name.StartsWith("$_")) {
  536. Ctx.Warning(top->NamePos, TIssuesIds::YQL_UNUSED_SYMBOL) << "Symbol " << name << " is not used";
  537. }
  538. mapIt->second.pop_front();
  539. if (mapIt->second.empty()) {
  540. Ctx.Scoped->NamedNodes.erase(mapIt);
  541. }
  542. }
  543. void TTranslation::WarnUnusedNodes() const {
  544. if (Ctx.HasPendingErrors) {
  545. // result is not reliable in this case
  546. return;
  547. }
  548. for (const auto& [name, items]: Ctx.Scoped->NamedNodes) {
  549. if (name.StartsWith("$_")) {
  550. continue;
  551. }
  552. for (const auto& item : items) {
  553. if (!item->IsUsed && item->Level == Ctx.ScopeLevel) {
  554. Ctx.Warning(item->NamePos, TIssuesIds::YQL_UNUSED_SYMBOL) << "Symbol " << name << " is not used";
  555. }
  556. }
  557. }
  558. }
  559. TString GetDescription(const google::protobuf::Message& node, const google::protobuf::FieldDescriptor* d) {
  560. const auto& field = node.GetReflection()->GetMessage(node, d);
  561. return field.GetReflection()->GetString(field, d->message_type()->FindFieldByName("Descr"));
  562. }
  563. TString TTranslation::AltDescription(const google::protobuf::Message& node, ui32 altCase, const google::protobuf::Descriptor* descr) const {
  564. return GetDescription(node, descr->FindFieldByNumber(altCase));
  565. }
  566. void TTranslation::AltNotImplemented(const TString& ruleName, ui32 altCase, const google::protobuf::Message& node, const google::protobuf::Descriptor* descr) {
  567. Error() << ruleName << ": alternative is not implemented yet: " << AltDescription(node, altCase, descr);
  568. }
  569. } // namespace NSQLTranslationV1