PageRenderTime 31ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/src/rdb_protocol/terms/obj_or_seq.cc

https://gitlab.com/freesoftware/rethinkdb
C++ | 384 lines | 332 code | 42 blank | 10 comment | 39 complexity | 27a4933985c8d81912fa537d0b5debdf MD5 | raw file
  1. // Copyright 2010-2015 RethinkDB, all rights reserved.
  2. #include "rdb_protocol/terms/terms.hpp"
  3. #include <string>
  4. #include <functional>
  5. #include "rdb_protocol/error.hpp"
  6. #include "rdb_protocol/func.hpp"
  7. #include "rdb_protocol/op.hpp"
  8. #include "rdb_protocol/pathspec.hpp"
  9. #include "rdb_protocol/pseudo_literal.hpp"
  10. #include "rdb_protocol/minidriver.hpp"
  11. #include "rdb_protocol/term_walker.hpp"
  12. #include "rdb_protocol/terms/arr.hpp"
  13. #include "rdb_protocol/terms/obj_or_seq.hpp"
  14. namespace ql {
  15. // Returns a new reql expression similar to term except that the first argument
  16. // is replaced by a new variable.
  17. // For example, foo.pluck('a') becomes varnum.pluck('a')
  18. raw_term_t make_obj_or_seq_func(const raw_term_t &term,
  19. poly_type_t poly_type) {
  20. minidriver_t r(term.bt());
  21. auto varnum = minidriver_t::dummy_var_t::OBJORSEQ_VARNUM;
  22. minidriver_t::reql_t body = r.var(varnum).call(term.type());
  23. body.copy_args_from_term(term, 1);
  24. body.add_arg(r.optarg("_NO_RECURSE_", r.boolean(true)));
  25. switch (poly_type) {
  26. case MAP: // fallthru
  27. case FILTER:
  28. return r.fun(varnum, body).root_term();
  29. case SKIP_MAP:
  30. return r.fun(varnum, r.array(body).default_(r.array())).root_term();
  31. default: unreachable();
  32. }
  33. }
  34. obj_or_seq_op_impl_t::obj_or_seq_op_impl_t(
  35. const term_t *_parent, poly_type_t _poly_type,
  36. std::set<std::string> &&_acceptable_ptypes)
  37. : parent(_parent), poly_type(_poly_type),
  38. func(make_obj_or_seq_func(parent->get_src(), poly_type)),
  39. acceptable_ptypes(std::move(_acceptable_ptypes)) { }
  40. scoped_ptr_t<val_t> obj_or_seq_op_impl_t::eval_impl_dereferenced(
  41. const term_t *target, scope_env_t *env, args_t *args,
  42. const scoped_ptr_t<val_t> &v0,
  43. std::function<scoped_ptr_t<val_t>()> helper) const {
  44. datum_t d;
  45. if (v0->get_type().is_convertible(val_t::type_t::DATUM)) {
  46. d = v0->as_datum();
  47. }
  48. if (d.has() && d.get_type() == datum_t::R_OBJECT) {
  49. if (d.is_ptype() &&
  50. acceptable_ptypes.find(d.get_reql_type()) == acceptable_ptypes.end()) {
  51. rfail_target(v0, base_exc_t::LOGIC,
  52. "Cannot call `%s` on objects of type `%s`.",
  53. parent->name(), d.get_type_name().c_str());
  54. }
  55. return helper();
  56. } else if ((d.has() && d.get_type() == datum_t::R_ARRAY) ||
  57. (!d.has()
  58. && v0->get_type().is_convertible(val_t::type_t::SEQUENCE))) {
  59. // The above if statement is complicated because it produces better
  60. // error messages on e.g. strings.
  61. if (scoped_ptr_t<val_t> no_recurse = args->optarg(env, "_NO_RECURSE_")) {
  62. rcheck_target(target,
  63. no_recurse->as_bool() == false,
  64. base_exc_t::LOGIC,
  65. strprintf("Cannot perform %s on a sequence of sequences.",
  66. target->name()));
  67. }
  68. compile_env_t compile_env(env->scope.compute_visibility());
  69. counted_t<func_term_t> func_term =
  70. make_counted<func_term_t>(&compile_env, func);
  71. counted_t<const func_t> f =
  72. func_term->eval_to_func(env->scope);
  73. counted_t<datum_stream_t> stream;
  74. counted_t<selection_t> sel; // may be empty
  75. if (poly_type == FILTER
  76. && v0->get_type().is_convertible(val_t::type_t::SELECTION)) {
  77. sel = v0->as_selection(env->env);
  78. stream = sel->seq;
  79. } else {
  80. stream = v0->as_seq(env->env);
  81. }
  82. switch (poly_type) {
  83. case MAP:
  84. stream->add_transformation(map_wire_func_t(f), target->backtrace());
  85. break;
  86. case FILTER:
  87. stream->add_transformation(filter_wire_func_t(f, r_nullopt),
  88. target->backtrace());
  89. break;
  90. case SKIP_MAP:
  91. stream->add_transformation(
  92. concatmap_wire_func_t(result_hint_t::AT_MOST_ONE, f),
  93. target->backtrace());
  94. break;
  95. default: unreachable();
  96. }
  97. if (sel) {
  98. return target->new_val(sel);
  99. } else {
  100. return target->new_val(env->env, stream);
  101. }
  102. }
  103. rfail_typed_target(
  104. v0, "Cannot perform %s on a non-object non-sequence `%s`.",
  105. target->name(), v0->trunc_print().c_str());
  106. }
  107. obj_or_seq_op_term_t::obj_or_seq_op_term_t(
  108. compile_env_t *env, const raw_term_t &term,
  109. poly_type_t _poly_type, argspec_t argspec)
  110. : grouped_seq_op_term_t(env, term, argspec, optargspec_t({"_NO_RECURSE_"})),
  111. impl(this, _poly_type, std::set<std::string>()) {
  112. }
  113. obj_or_seq_op_term_t::obj_or_seq_op_term_t(
  114. compile_env_t *env, const raw_term_t &term,
  115. poly_type_t _poly_type, argspec_t argspec, std::set<std::string> &&ptypes)
  116. : grouped_seq_op_term_t(env, term, argspec, optargspec_t({"_NO_RECURSE_"})),
  117. impl(this, _poly_type, std::move(ptypes)) {
  118. }
  119. scoped_ptr_t<val_t> obj_or_seq_op_term_t::eval_impl(scope_env_t *env, args_t *args,
  120. eval_flags_t) const {
  121. scoped_ptr_t<val_t> v0 = args->arg(env, 0);
  122. return impl.eval_impl_dereferenced(this, env, args, v0,
  123. [&]{ return this->obj_eval(env, args, v0); });
  124. }
  125. class pluck_term_t : public obj_or_seq_op_term_t {
  126. public:
  127. pluck_term_t(compile_env_t *env, const raw_term_t &term)
  128. : obj_or_seq_op_term_t(env, term, MAP, argspec_t(1, -1)) { }
  129. private:
  130. virtual scoped_ptr_t<val_t> obj_eval(
  131. scope_env_t *env, args_t *args, const scoped_ptr_t<val_t> &v0) const {
  132. datum_t obj = v0->as_datum();
  133. r_sanity_check(obj.get_type() == datum_t::R_OBJECT);
  134. const size_t n = args->num_args();
  135. std::vector<datum_t> paths;
  136. paths.reserve(n - 1);
  137. for (size_t i = 1; i < n; ++i) {
  138. paths.push_back(args->arg(env, i)->as_datum());
  139. }
  140. pathspec_t pathspec(datum_t(std::move(paths), env->env->limits()), this);
  141. return new_val(project(obj, pathspec, DONT_RECURSE, env->env->limits()));
  142. }
  143. virtual const char *name() const { return "pluck"; }
  144. };
  145. class without_term_t : public obj_or_seq_op_term_t {
  146. public:
  147. without_term_t(compile_env_t *env, const raw_term_t &term)
  148. : obj_or_seq_op_term_t(env, term, MAP, argspec_t(1, -1)) { }
  149. private:
  150. virtual scoped_ptr_t<val_t> obj_eval(
  151. scope_env_t *env, args_t *args, const scoped_ptr_t<val_t> &v0) const {
  152. datum_t obj = v0->as_datum();
  153. r_sanity_check(obj.get_type() == datum_t::R_OBJECT);
  154. std::vector<datum_t> paths;
  155. const size_t n = args->num_args();
  156. paths.reserve(n - 1);
  157. for (size_t i = 1; i < n; ++i) {
  158. paths.push_back(args->arg(env, i)->as_datum());
  159. }
  160. pathspec_t pathspec(datum_t(std::move(paths), env->env->limits()), this);
  161. return new_val(unproject(obj, pathspec, DONT_RECURSE, env->env->limits()));
  162. }
  163. virtual const char *name() const { return "without"; }
  164. };
  165. class literal_term_t : public op_term_t {
  166. public:
  167. literal_term_t(compile_env_t *env, const raw_term_t &term)
  168. : op_term_t(env, term, argspec_t(0, 1)) { }
  169. private:
  170. virtual scoped_ptr_t<val_t> eval_impl(
  171. scope_env_t *env, args_t *args, eval_flags_t flags) const {
  172. rcheck(flags & LITERAL_OK, base_exc_t::LOGIC,
  173. "Stray literal keyword found: literal is only legal inside of "
  174. "the object passed to merge or update and cannot nest inside "
  175. "other literals.");
  176. datum_object_builder_t res;
  177. bool clobber = res.add(datum_t::reql_type_string,
  178. datum_t(pseudo::literal_string));
  179. if (args->num_args() == 1) {
  180. clobber |= res.add(pseudo::value_key, args->arg(env, 0)->as_datum());
  181. }
  182. r_sanity_check(!clobber);
  183. std::set<std::string> permissible_ptypes { pseudo::literal_string };
  184. return new_val(std::move(res).to_datum(permissible_ptypes));
  185. }
  186. virtual const char *name() const { return "literal"; }
  187. virtual bool can_be_grouped() const { return false; }
  188. };
  189. class merge_term_t : public obj_or_seq_op_term_t {
  190. public:
  191. merge_term_t(compile_env_t *env, const raw_term_t &term)
  192. : obj_or_seq_op_term_t(env, term, MAP, argspec_t(1, -1, LITERAL_OK)) { }
  193. private:
  194. virtual scoped_ptr_t<val_t> obj_eval(
  195. scope_env_t *env, args_t *args, const scoped_ptr_t<val_t> &v0) const {
  196. datum_t d = v0->as_datum();
  197. for (size_t i = 1; i < args->num_args(); ++i) {
  198. scoped_ptr_t<val_t> v = args->arg(env, i, LITERAL_OK);
  199. // We branch here because compiling functions is expensive, and
  200. // `obj_eval` may be called many many times.
  201. if (v->get_type().is_convertible(val_t::type_t::DATUM)) {
  202. datum_t d0 = v->as_datum();
  203. if (d0.get_type() == datum_t::R_OBJECT) {
  204. rcheck_target(v,
  205. !d0.is_ptype() || d0.is_ptype("LITERAL"),
  206. base_exc_t::LOGIC,
  207. strprintf("Cannot merge objects of type `%s`.",
  208. d0.get_type_name().c_str()));
  209. }
  210. d = d.merge(d0);
  211. } else {
  212. auto f = v->as_func(CONSTANT_SHORTCUT);
  213. datum_t d0 = f->call(env->env, d, LITERAL_OK)->as_datum();
  214. if (d0.get_type() == datum_t::R_OBJECT) {
  215. rcheck_target(v,
  216. !d0.is_ptype() || d0.is_ptype("LITERAL"),
  217. base_exc_t::LOGIC,
  218. strprintf("Cannot merge objects of type `%s`.",
  219. d0.get_type_name().c_str()));
  220. }
  221. d = d.merge(d0);
  222. }
  223. }
  224. return new_val(d);
  225. }
  226. virtual const char *name() const { return "merge"; }
  227. };
  228. class has_fields_term_t : public obj_or_seq_op_term_t {
  229. public:
  230. has_fields_term_t(compile_env_t *env, const raw_term_t &term)
  231. : obj_or_seq_op_term_t(env, term, FILTER, argspec_t(1, -1)) { }
  232. private:
  233. virtual scoped_ptr_t<val_t> obj_eval(
  234. scope_env_t *env, args_t *args, const scoped_ptr_t<val_t> &v0) const {
  235. datum_t obj = v0->as_datum();
  236. r_sanity_check(obj.get_type() == datum_t::R_OBJECT);
  237. std::vector<datum_t> paths;
  238. const size_t n = args->num_args();
  239. paths.reserve(n - 1);
  240. for (size_t i = 1; i < n; ++i) {
  241. paths.push_back(args->arg(env, i)->as_datum());
  242. }
  243. pathspec_t pathspec(datum_t(std::move(paths), env->env->limits()), this);
  244. return new_val_bool(contains(obj, pathspec));
  245. }
  246. virtual const char *name() const { return "has_fields"; }
  247. };
  248. class get_field_term_t : public obj_or_seq_op_term_t {
  249. public:
  250. get_field_term_t(compile_env_t *env, const raw_term_t &term)
  251. : obj_or_seq_op_term_t(env, term, SKIP_MAP, argspec_t(2)) { }
  252. bool is_simple_selector() const final {
  253. return recursive_is_simple_selector();
  254. }
  255. private:
  256. virtual scoped_ptr_t<val_t> obj_eval(
  257. scope_env_t *env, args_t *args, const scoped_ptr_t<val_t> &v0) const {
  258. datum_t d = v0->as_datum();
  259. return new_val(d.get_field(args->arg(env, 1)->as_str()));
  260. }
  261. virtual const char *name() const { return "get_field"; }
  262. };
  263. class bracket_term_t : public grouped_seq_op_term_t {
  264. public:
  265. bracket_term_t(compile_env_t *env, const raw_term_t &term)
  266. : grouped_seq_op_term_t(env, term, argspec_t(2),
  267. optargspec_t({"_NO_RECURSE_"})),
  268. impl(this, SKIP_MAP, std::set<std::string>()) {}
  269. bool is_simple_selector() const final {
  270. return recursive_is_simple_selector();
  271. }
  272. private:
  273. scoped_ptr_t<val_t> obj_eval_dereferenced(
  274. const scoped_ptr_t<val_t> &v0, const scoped_ptr_t<val_t> &v1) const {
  275. datum_t d = v0->as_datum();
  276. return new_val(d.get_field(v1->as_str()));
  277. }
  278. virtual scoped_ptr_t<val_t> eval_impl(
  279. scope_env_t *env, args_t *args, eval_flags_t) const {
  280. scoped_ptr_t<val_t> v0 = args->arg(env, 0);
  281. scoped_ptr_t<val_t> v1 = args->arg(env, 1);
  282. datum_t d = v1->as_datum();
  283. r_sanity_check(d.has());
  284. switch (d.get_type()) {
  285. case datum_t::R_NUM: {
  286. return nth_term_impl(this, env, std::move(v0), v1);
  287. }
  288. case datum_t::R_STR:
  289. return impl.eval_impl_dereferenced(
  290. this, env, args, v0,
  291. [&]{ return this->obj_eval_dereferenced(v0, v1); });
  292. case datum_t::MINVAL:
  293. case datum_t::R_ARRAY:
  294. case datum_t::R_BINARY:
  295. case datum_t::R_BOOL:
  296. case datum_t::R_NULL:
  297. case datum_t::R_OBJECT:
  298. case datum_t::MAXVAL:
  299. case datum_t::UNINITIALIZED:
  300. default:
  301. d.type_error(
  302. strprintf(
  303. "Expected NUMBER or STRING as second argument to `%s` but found %s.",
  304. name(), d.get_type_name().c_str()));
  305. unreachable();
  306. }
  307. }
  308. virtual const char *name() const { return "bracket"; }
  309. // obj_or_seq_op_term_t already does this, but because nth_term wasn't grouped,
  310. // I reimplement it here for clarity.
  311. virtual bool is_grouped_seq_op() const { return true; }
  312. obj_or_seq_op_impl_t impl;
  313. };
  314. counted_t<term_t> make_get_field_term(
  315. compile_env_t *env, const raw_term_t &term) {
  316. return make_counted<get_field_term_t>(env, term);
  317. }
  318. counted_t<term_t> make_bracket_term(
  319. compile_env_t *env, const raw_term_t &term) {
  320. return make_counted<bracket_term_t>(env, term);
  321. }
  322. counted_t<term_t> make_has_fields_term(
  323. compile_env_t *env, const raw_term_t &term) {
  324. return make_counted<has_fields_term_t>(env, term);
  325. }
  326. counted_t<term_t> make_pluck_term(
  327. compile_env_t *env, const raw_term_t &term) {
  328. return make_counted<pluck_term_t>(env, term);
  329. }
  330. counted_t<term_t> make_without_term(
  331. compile_env_t *env, const raw_term_t &term) {
  332. return make_counted<without_term_t>(env, term);
  333. }
  334. counted_t<term_t> make_literal_term(
  335. compile_env_t *env, const raw_term_t &term) {
  336. return make_counted<literal_term_t>(env, term);
  337. }
  338. counted_t<term_t> make_merge_term(
  339. compile_env_t *env, const raw_term_t &term) {
  340. return make_counted<merge_term_t>(env, term);
  341. }
  342. } // namespace ql