context.cpp 22 KB

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