url_query.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. #include "url_query.h"
  2. #include <yql/essentials/public/udf/udf_type_printer.h>
  3. #include <util/string/split.h>
  4. #include <library/cpp/string_utils/quote/quote.h>
  5. namespace NUrlUdf {
  6. void TQueryStringParse::MakeSignature(IFunctionTypeInfoBuilder& builder,
  7. const TType* retType)
  8. {
  9. builder.Returns(retType).OptionalArgs(4);
  10. auto args = builder.Args();
  11. args->Add<TAutoMap<TQueryStr>>();
  12. args->Add<TKeepBlankValuesNArg>();
  13. args->Add<TStrictNArg>();
  14. args->Add<TMaxFieldsNArg>();
  15. args->Add<TSeparatorNArg>().Done();
  16. }
  17. std::vector<std::pair<TString, TString>>
  18. TQueryStringParse::RunImpl(const TUnboxedValuePod* args) const {
  19. const std::string_view query(args[0].AsStringRef());
  20. if (query.empty())
  21. return {};
  22. const bool keepBlankValues = args[1].GetOrDefault(false);
  23. const bool strict = args[2].GetOrDefault(true);
  24. const ui32 maxFieldCnt = args[3].GetOrDefault(Max<ui32>());
  25. const std::string_view sep(args[4] ? args[4].AsStringRef() : "&");
  26. std::vector<TStringBuf> parts;
  27. StringSplitter(query).SplitByString(sep).Collect(&parts);
  28. if (parts.size() > maxFieldCnt) {
  29. UdfTerminate((TStringBuilder() << Pos_ << "Max number of fields (" << maxFieldCnt
  30. << ") exceeded: got " << parts.size()).data());
  31. }
  32. std::vector<std::pair<TString, TString>> pairs;
  33. for (const TStringBuf& part: parts) {
  34. if (part.empty() && !strict) {
  35. continue;
  36. }
  37. TVector<TString> nvPair = StringSplitter(part).Split('=').Limit(2);
  38. if (nvPair.size() != 2) {
  39. if (strict) {
  40. UdfTerminate((TStringBuilder() << Pos_ << "Bad query field: \""
  41. << nvPair[0] << "\"").data());
  42. }
  43. if (keepBlankValues) {
  44. nvPair.emplace_back("");
  45. } else {
  46. continue;
  47. }
  48. }
  49. if (!nvPair[1].empty() || keepBlankValues) {
  50. CGIUnescape(nvPair[0]);
  51. CGIUnescape(nvPair[1]);
  52. pairs.emplace_back(nvPair[0], nvPair[1]);
  53. }
  54. }
  55. return pairs;
  56. }
  57. bool TQueryStringToList::DeclareSignature(const TStringRef& name,
  58. TType*,
  59. IFunctionTypeInfoBuilder& builder,
  60. bool typesOnly) {
  61. if (Name() == name) {
  62. MakeSignature(builder, GetListType(builder));
  63. if (!typesOnly) {
  64. builder.Implementation(new TQueryStringToList(builder.GetSourcePosition()));
  65. }
  66. return true;
  67. }
  68. return false;
  69. }
  70. TUnboxedValue TQueryStringToList::Run(const IValueBuilder* valueBuilder,
  71. const TUnboxedValuePod* args) const {
  72. const auto pairs = RunImpl(args);
  73. std::vector<TUnboxedValue> ret;
  74. for (const auto& nvPair : pairs) {
  75. TUnboxedValue* pair = nullptr;
  76. auto item = valueBuilder->NewArray(2U, pair);
  77. pair[0] = valueBuilder->NewString(nvPair.first);
  78. pair[1] = valueBuilder->NewString(nvPair.second);
  79. ret.push_back(item);
  80. }
  81. return valueBuilder->NewList(ret.data(), ret.size());
  82. }
  83. bool TQueryStringToDict::DeclareSignature(const TStringRef& name,
  84. TType*,
  85. IFunctionTypeInfoBuilder& builder,
  86. bool typesOnly) {
  87. if (Name() == name) {
  88. auto dictType = GetDictType(builder);
  89. MakeSignature(builder, dictType);
  90. if (!typesOnly) {
  91. builder.Implementation(new TQueryStringToDict(dictType,
  92. builder.GetSourcePosition()));
  93. }
  94. return true;
  95. }
  96. return false;
  97. }
  98. TUnboxedValue TQueryStringToDict::Run(const IValueBuilder* valueBuilder,
  99. const TUnboxedValuePod* args) const {
  100. const auto pairs = RunImpl(args);
  101. auto ret = valueBuilder->NewDict(DictType_, TDictFlags::Hashed | TDictFlags::Multi);
  102. for (const auto& nvPair : pairs) {
  103. ret->Add(valueBuilder->NewString(nvPair.first),
  104. valueBuilder->NewString(nvPair.second));
  105. }
  106. return ret->Build();
  107. }
  108. TUnboxedValue TBuildQueryString::Run(const IValueBuilder* valueBuilder,
  109. const TUnboxedValuePod* args) const {
  110. const std::string_view sep(args[1] ? args[1].AsStringRef() : "&");
  111. TStringBuilder ret;
  112. switch(FirstArgTypeId_) {
  113. case EFirstArgTypeId::Dict: {
  114. TUnboxedValue key, value;
  115. const auto dictIt = args[0].GetDictIterator();
  116. ui64 wasItem = 0;
  117. while (dictIt.NextPair(key, value)) {
  118. TString keyEscaped = CGIEscapeRet(key.AsStringRef());
  119. const auto listIt = value.GetListIterator();
  120. TUnboxedValue item;
  121. while (listIt.Next(item)) {
  122. if (wasItem++)
  123. ret << sep;
  124. if (item) {
  125. ret << keyEscaped << '=' << CGIEscapeRet(item.AsStringRef());
  126. } else {
  127. ret << keyEscaped << '=';
  128. }
  129. }
  130. }
  131. break;
  132. }
  133. case EFirstArgTypeId::FlattenDict: {
  134. TUnboxedValue key, value;
  135. const auto dictIt = args[0].GetDictIterator();
  136. ui64 wasKey = 0;
  137. while (dictIt.NextPair(key, value)) {
  138. if (wasKey++)
  139. ret << sep;
  140. if (value) {
  141. ret << CGIEscapeRet(key.AsStringRef()) << '='
  142. << CGIEscapeRet(value.AsStringRef());
  143. } else {
  144. ret << CGIEscapeRet(key.AsStringRef()) << '=';
  145. }
  146. }
  147. break;
  148. }
  149. case EFirstArgTypeId::List: {
  150. ui64 wasItem = 0;
  151. TUnboxedValue item;
  152. const auto listIt = args[0].GetListIterator();
  153. while (listIt.Next(item)) {
  154. if (wasItem++)
  155. ret << sep;
  156. TUnboxedValue key = item.GetElement(0), val = item.GetElement(1);
  157. if (val) {
  158. ret << CGIEscapeRet(key.AsStringRef()) << '='
  159. << CGIEscapeRet(val.AsStringRef());
  160. } else {
  161. ret << CGIEscapeRet(key.AsStringRef()) << '=';
  162. }
  163. }
  164. break;
  165. }
  166. default:
  167. Y_ABORT("Current first parameter type is not yet implemented");
  168. }
  169. return valueBuilder->NewString(ret);
  170. }
  171. bool TBuildQueryString::DeclareSignature(const TStringRef& name,
  172. TType* userType,
  173. IFunctionTypeInfoBuilder& builder,
  174. bool typesOnly) {
  175. if (Name() == name) {
  176. if (!userType) {
  177. builder.SetError("Missing user type");
  178. return true;
  179. }
  180. builder.UserType(userType);
  181. const auto typeHelper = builder.TypeInfoHelper();
  182. const auto userTypeInspector = TTupleTypeInspector(*typeHelper, userType);
  183. if (!userTypeInspector || !userTypeInspector.GetElementsCount()) {
  184. builder.SetError("User type is not tuple");
  185. return true;
  186. }
  187. const auto argsTypeInspector = TTupleTypeInspector(*typeHelper,
  188. userTypeInspector.GetElementType(0));
  189. if (!argsTypeInspector || !argsTypeInspector.GetElementsCount()) {
  190. builder.SetError("Please provide at least one argument");
  191. return true;
  192. }
  193. const auto firstArgType = argsTypeInspector.GetElementType(0);
  194. EFirstArgTypeId firstArgTypeId = EFirstArgTypeId::None;
  195. if (typeHelper->IsSameType(GetDictType(builder), firstArgType) ||
  196. typeHelper->IsSameType(GetDictType(builder, true), firstArgType)) {
  197. firstArgTypeId = EFirstArgTypeId::Dict;
  198. } else if (typeHelper->IsSameType(GetListType(builder), firstArgType) ||
  199. typeHelper->IsSameType(GetListType(builder, true), firstArgType) ||
  200. typeHelper->GetTypeKind(firstArgType) == ETypeKind::EmptyList)
  201. {
  202. firstArgTypeId = EFirstArgTypeId::List;
  203. } else if (typeHelper->IsSameType(GetFlattenDictType(builder), firstArgType) ||
  204. typeHelper->IsSameType(GetFlattenDictType(builder, true), firstArgType) ||
  205. typeHelper->GetTypeKind(firstArgType) == ETypeKind::EmptyDict)
  206. {
  207. firstArgTypeId = EFirstArgTypeId::FlattenDict;
  208. }
  209. if (firstArgTypeId != EFirstArgTypeId::None) {
  210. builder.Returns<TQueryStr>().OptionalArgs(1);
  211. auto args = builder.Args();
  212. args->Add(firstArgType).Flags(ICallablePayload::TArgumentFlags::AutoMap);
  213. args->Add<TSeparatorNArg>().Done();
  214. if (!typesOnly) {
  215. builder.Implementation(new TBuildQueryString(builder.GetSourcePosition(),
  216. firstArgTypeId));
  217. }
  218. } else {
  219. TStringBuilder sb;
  220. sb << "Unsupported first argument type: ";
  221. TTypePrinter(*typeHelper, firstArgType).Out(sb.Out);
  222. builder.SetError(sb);
  223. }
  224. return true;
  225. }
  226. return false;
  227. }
  228. }