yql_library_compiler.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. #include "yql_library_compiler.h"
  2. #include "yql_expr_optimize.h"
  3. #include <yql/essentials/sql/sql.h>
  4. #include <util/system/file.h>
  5. #include <unordered_set>
  6. #include <unordered_map>
  7. namespace NYql {
  8. namespace {
  9. bool ReplaceNodes(TExprNode& node, const TNodeOnNodeOwnedMap& replaces, bool& hasChanges, TNodeSet& visited, TNodeSet& parents)
  10. {
  11. if (!node.ChildrenSize()) {
  12. return true;
  13. }
  14. const auto pair = parents.emplace(&node);
  15. if (!pair.second) {
  16. return false;
  17. }
  18. if (!visited.emplace(&node).second) {
  19. parents.erase(pair.first);
  20. return true;
  21. }
  22. for (ui32 i = 0U; i < node.ChildrenSize(); ++i) {
  23. auto& child = node.ChildRef(i);
  24. if (const auto it = replaces.find(node.Child(i)); replaces.cend() != it) {
  25. child = it->second;
  26. hasChanges = true;
  27. }
  28. if (!ReplaceNodes(*node.Child(i), replaces, hasChanges, visited, parents)) {
  29. child.Reset();
  30. return false;
  31. }
  32. }
  33. parents.erase(pair.first);
  34. return true;
  35. }
  36. bool ReplaceNodes(TExprNode& node, const TNodeOnNodeOwnedMap& replaces, bool& hasChanges) {
  37. TNodeSet visited;
  38. TNodeSet parents;
  39. return ReplaceNodes(node, replaces, hasChanges, visited, parents);
  40. }
  41. TString Load(const TString& path)
  42. {
  43. TFile file(path, EOpenModeFlag::RdOnly);
  44. if (file.GetLength() <= 0)
  45. return TString();
  46. std::vector<TString::value_type> buffer(file.GetLength());
  47. file.Load(buffer.data(), buffer.size());
  48. return TString(buffer.data(), buffer.size());
  49. }
  50. }
  51. bool OptimizeLibrary(TLibraryCohesion& cohesion, TExprContext& ctx) {
  52. TExprNode::TListType tupleItems;
  53. for (const auto& x : cohesion.Exports.Symbols()) {
  54. tupleItems.push_back(x.second);
  55. }
  56. auto root = ctx.NewList(TPositionHandle(), std::move(tupleItems));
  57. for (;;) {
  58. auto status = ExpandApply(root, root, ctx);
  59. if (status == IGraphTransformer::TStatus::Error) {
  60. return false;
  61. }
  62. if (status == IGraphTransformer::TStatus::Ok) {
  63. ctx.Step.Repeat(TExprStep::ExpandApplyForLambdas);
  64. break;
  65. }
  66. }
  67. ui32 index = 0;
  68. for (auto& x : cohesion.Exports.Symbols(ctx)) {
  69. x.second = root->ChildPtr(index++);
  70. }
  71. return true;
  72. }
  73. bool CompileLibrary(const NSQLTranslation::TTranslators& translators, const TString& alias,
  74. const TString& script, TExprContext& ctx, TLibraryCohesion& cohesion, bool optimize)
  75. {
  76. TAstParseResult res;
  77. if (alias.EndsWith(".sql")) {
  78. NSQLTranslation::TTranslationSettings translationSettings;
  79. translationSettings.SyntaxVersion = 1;
  80. translationSettings.Mode = NSQLTranslation::ESqlMode::LIBRARY;
  81. res = NSQLTranslation::SqlToYql(translators, script, translationSettings);
  82. } else {
  83. res = ParseAst(script, nullptr, alias);
  84. }
  85. if (!res.IsOk()) {
  86. for (const auto& originalError : res.Issues) {
  87. TIssue error(originalError);
  88. TStringBuilder message;
  89. message << error.GetMessage() << " (at " << alias << ")";
  90. error.SetMessage(message);
  91. ctx.AddError(error);
  92. }
  93. return false;
  94. }
  95. if (!CompileExpr(*res.Root, cohesion, ctx))
  96. return false;
  97. if (!optimize) {
  98. return true;
  99. }
  100. return OptimizeLibrary(cohesion, ctx);
  101. }
  102. bool LinkLibraries(THashMap<TString, TLibraryCohesion>& libs, TExprContext& ctx, TExprContext& ctxToClone, const TModulesTable* loadedModules) {
  103. std::function<const TExportTable*(const TString&)> f = [loadedModules](const TString& normalizedModuleName) -> const TExportTable* {
  104. if (!loadedModules) {
  105. return nullptr;
  106. }
  107. return loadedModules->FindPtr(normalizedModuleName);
  108. };
  109. return LinkLibraries(libs, ctx, ctxToClone, f);
  110. }
  111. bool LinkLibraries(THashMap<TString, TLibraryCohesion>& libs, TExprContext& ctx, TExprContext& ctxToClone, const std::function<const TExportTable*(const TString&)>& module2ExportTable)
  112. {
  113. TNodeOnNodeOwnedMap clones, replaces;
  114. for (const auto& lib : libs) {
  115. for (const auto& import : lib.second.Imports) {
  116. if (import.first->Dead()) {
  117. continue;
  118. }
  119. if (import.second.first == lib.first) {
  120. ctx.AddError(TIssue(ctxToClone.GetPosition(import.first->Pos()),
  121. TStringBuilder() << "Library '" << lib.first << "' tries to import itself."));
  122. return false;
  123. }
  124. const auto* exportTable = module2ExportTable(TModuleResolver::NormalizeModuleName(import.second.first));
  125. const bool externalModule = exportTable;
  126. if (!exportTable) {
  127. if (const auto it = libs.find(import.second.first); libs.cend() != it) {
  128. exportTable = &it->second.Exports;
  129. }
  130. }
  131. if (!exportTable) {
  132. ctx.AddError(TIssue(ctxToClone.GetPosition(import.first->Pos()),
  133. TStringBuilder() << "Library '" << lib.first << "' has unresolved dependency from '" << import.second.first << "'."));
  134. return false;
  135. }
  136. if (const auto ex = exportTable->Symbols().find(import.second.second); exportTable->Symbols().cend() != ex) {
  137. replaces[import.first] = externalModule ? ctxToClone.DeepCopy(*ex->second, exportTable->ExprCtx(), clones, true, false) : ex->second;
  138. } else {
  139. ctx.AddError(TIssue(ctxToClone.GetPosition(import.first->Pos()),
  140. TStringBuilder() << "Library '" << lib.first << "' has unresolved symbol '" << import.second.second << "' from '" << import.second.first << "'."));
  141. return false;
  142. }
  143. }
  144. }
  145. if (!replaces.empty()) {
  146. for (auto& lib : libs) {
  147. for (auto& expo : lib.second.Exports.Symbols(lib.second.Exports.ExprCtx())) {
  148. if (const auto find = replaces.find(expo.second.Get()); replaces.cend() != find)
  149. expo.second = find->second;
  150. }
  151. }
  152. }
  153. for (bool hasChanges = !replaces.empty(); hasChanges;) {
  154. hasChanges = false;
  155. for (const auto& lib : libs) {
  156. for (const auto& expo : lib.second.Exports.Symbols()) {
  157. if (!ReplaceNodes(*expo.second, replaces, hasChanges)) {
  158. ctx.AddError(TIssue(ctxToClone.GetPosition(expo.second->Pos()),
  159. TStringBuilder() << "Cross reference detected under '" << expo.first << "' in '" << lib.first << "'."));
  160. return false;
  161. }
  162. }
  163. }
  164. }
  165. return true;
  166. }
  167. bool CompileLibraries(const NSQLTranslation::TTranslators& translators, const TUserDataTable& userData,
  168. TExprContext& ctx, TModulesTable& modules, bool optimize)
  169. {
  170. THashMap<TString, TLibraryCohesion> libs;
  171. for (const auto& data : userData) {
  172. if (data.first.IsFile() && data.second.Usage.Test(EUserDataBlockUsage::Library)) {
  173. TString libraryData;
  174. const TString& alias = data.first.Alias();
  175. if (data.second.Type == EUserDataType::PATH) {
  176. libraryData = Load(data.second.Data);
  177. } else if (data.second.Type == EUserDataType::RAW_INLINE_DATA) {
  178. libraryData = data.second.Data;
  179. }
  180. if (!libraryData.empty()) {
  181. if (CompileLibrary(translators, alias, libraryData, ctx, libs[alias], optimize))
  182. modules[TModuleResolver::NormalizeModuleName(alias)] = libs[alias].Exports;
  183. else
  184. return false;
  185. }
  186. }
  187. }
  188. return LinkLibraries(libs, ctx, ctx);
  189. }
  190. bool CompileLibraries(const TUserDataTable& userData, TExprContext& ctx, TModulesTable& modules, bool optimize) {
  191. return CompileLibraries(NSQLTranslation::MakeAllTranslators(), userData, ctx, modules, optimize);
  192. }
  193. }