Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make r.default and non-existence errors not rely on exceptions #7144

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
46 changes: 35 additions & 11 deletions src/rdb_protocol/datum.cc
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,6 @@ datum_t::data_wrapper_t &datum_t::data_wrapper_t::operator=(
return *this;
}

datum_t::data_wrapper_t::data_wrapper_t() :
internal_type(internal_type_t::UNINITIALIZED) { }

datum_t::data_wrapper_t::data_wrapper_t(datum_t::construct_minval_t) :
internal_type(internal_type_t::MINVAL) { }

Expand Down Expand Up @@ -298,8 +295,6 @@ void datum_t::data_wrapper_t::assign_move(datum_t::data_wrapper_t &&movee) noexc
#endif
}

datum_t::datum_t() : data() { }

datum_t::datum_t(type_t type, shared_buf_ref_t<char> &&buf_ref)
: data(type, std::move(buf_ref)) { }

Expand Down Expand Up @@ -1458,6 +1453,18 @@ datum_t datum_t::get(size_t index, throw_bool_t throw_bool) const {
}
}

datum_t datum_t::get_with_err(eval_error *err_out, size_t index) const {
// Calling `arr_size()` here also makes sure this this is actually an R_ARRAY.
const size_t array_size = arr_size();
if (index < array_size) {
return unchecked_get(index);
} else {
err_out->datum_exc = make_scoped<datum_exc_t>(base_exc_t::NON_EXISTENCE,
strprintf("Index out of bounds: %zu", index));
return datum_t();
}
}

datum_t datum_t::unchecked_get(size_t index) const {
if (data.get_internal_type() == internal_type_t::BUF_R_ARRAY) {
const size_t offset = datum_get_element_offset(data.buf_ref, index);
Expand Down Expand Up @@ -1494,38 +1501,55 @@ std::pair<datum_string_t, datum_t> datum_t::unchecked_get_pair(size_t index) con
}
}

datum_t datum_t::get_field(const datum_string_t &key, throw_bool_t throw_bool) const {
datum_t datum_t::get_field_nothrow(const datum_string_t &key) const {
// Use binary search on top of unchecked_get_pair()
size_t range_beg = 0;
// The obj_size() also makes sure that this has the right type (R_OBJECT)
size_t range_end = obj_size();
while (range_beg < range_end) {
const size_t center = range_beg + ((range_end - range_beg) / 2);
auto center_pair = unchecked_get_pair(center);
std::pair<datum_string_t, datum_t> center_pair = unchecked_get_pair(center);
const int cmp_res = key.compare(center_pair.first);
if (cmp_res == 0) {
// Found it
return center_pair.second;
return std::move(center_pair.second);
} else if (cmp_res < 0) {
range_end = center;
} else {
range_beg = center + 1;
}
rassert(range_beg <= range_end);
}
return datum_t();
}

// Didn't find it
if (throw_bool == THROW) {
datum_t datum_t::get_field(const datum_string_t &key, throw_bool_t throw_bool) const {
datum_t field = get_field_nothrow(key);
if (throw_bool == THROW && !field.has()) {
rfail(base_exc_t::NON_EXISTENCE,
"No attribute `%s` in object:\n%s", key.to_std().c_str(), print().c_str());
}
return datum_t();
return field;
}

datum_t datum_t::get_field_with_err(eval_error *err_out, const datum_string_t &key) const {
datum_t field = get_field_nothrow(key);
if (!field.has()) {
err_out->datum_exc = make_scoped<datum_exc_t>(
base_exc_t::NON_EXISTENCE,
strprintf("No attribute `%s` in object:\n%s", key.to_std().c_str(), print().c_str()));
}
return field;
}

datum_t datum_t::get_field(const char *key, throw_bool_t throw_bool) const {
return get_field(datum_string_t(key), throw_bool);
}

datum_t datum_t::get_field_with_err(eval_error *err_out, const char *key) const {
return get_field_with_err(err_out, datum_string_t(key));
}

template <class json_writer_t>
void write_json_unchecked_stack(const datum_t &datum, json_writer_t *writer) {
switch (datum.get_type()) {
Expand Down
9 changes: 7 additions & 2 deletions src/rdb_protocol/datum.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ class datum_t {

// Construct an uninitialized datum_t. This is to ease the transition from
// counted_t<const datum_t>
datum_t();
datum_t() : data() { }

// Construct a datum_t from a shared buffer.
// type can be one of R_BINARY, R_STR, R_OBJECT or R_ARRAY. _buf_ref must point
Expand Down Expand Up @@ -271,17 +271,21 @@ class datum_t {
size_t arr_size() const;
// Access an element of an array.
datum_t get(size_t index, throw_bool_t throw_bool = THROW) const;
datum_t get_with_err(eval_error *err_out, size_t index) const;

// Object interface
size_t obj_size() const;
// Access an element of an object.
// get_pair does not perform boundary checking. Its primary use is for
// iterating over the object in combination with num_pairs().
std::pair<datum_string_t, datum_t> get_pair(size_t index) const;
datum_t get_field_nothrow(const datum_string_t &key) const; // Still throws if not an object.
datum_t get_field(const datum_string_t &key,
throw_bool_t throw_bool = THROW) const;
datum_t get_field_with_err(eval_error *err_out, const datum_string_t &key) const;
datum_t get_field(const char *key,
throw_bool_t throw_bool = THROW) const;
datum_t get_field_with_err(eval_error *err_out, const char *key) const;
datum_t merge(const datum_t &rhs) const;
// "Consumer defined" merge resolutions; these take limits unlike
// the other merge because the merge resolution can and does (in
Expand Down Expand Up @@ -416,7 +420,8 @@ class datum_t {
data_wrapper_t(data_wrapper_t &&movee) noexcept;

// Mirror the same constructors of datum_t
data_wrapper_t();
data_wrapper_t() :
internal_type(internal_type_t::UNINITIALIZED) { }
explicit data_wrapper_t(construct_minval_t);
explicit data_wrapper_t(construct_maxval_t);
explicit data_wrapper_t(construct_null_t);
Expand Down
19 changes: 17 additions & 2 deletions src/rdb_protocol/error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ base_exc_t::type_t exc_type(const scoped_ptr_t<val_t> &v);
#endif // NDEBUG

// A RQL exception.
class exc_t : public base_exc_t {
class exc_t final : public base_exc_t {
public:
// We have a default constructor because these are serialized.
exc_t() : base_exc_t(base_exc_t::LOGIC), message("UNINITIALIZED") { }
Expand Down Expand Up @@ -276,7 +276,7 @@ class exc_t : public base_exc_t {
// correspond to part of the source tree. It's usually thrown from inside
// datum.{hpp,cc} and must be caught by the enclosing term/stream/whatever and
// turned into a normal `exc_t`.
class datum_exc_t : public base_exc_t {
class datum_exc_t final : public base_exc_t {
public:
datum_exc_t() : base_exc_t(base_exc_t::LOGIC), message("UNINITIALIZED") { }
explicit datum_exc_t(base_exc_t::type_t _type, std::string &&_message)
Expand All @@ -292,6 +292,21 @@ class datum_exc_t : public base_exc_t {
std::string message;
};

struct eval_error {
bool has() const { return exc.has() || datum_exc.has(); }
bool throw_exc() const {
if (exc.has()) {
throw *exc;
} else {
rassert(datum_exc.has());
throw *datum_exc;
}
}

scoped_ptr_t<exc_t> exc;
scoped_ptr_t<datum_exc_t> datum_exc;
};

} // namespace ql

#endif // RDB_PROTOCOL_ERROR_HPP_
10 changes: 8 additions & 2 deletions src/rdb_protocol/func.cc
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,12 @@ scoped_ptr_t<val_t> reql_func_t::call(env_t *env,
: captured_scope.with_func_arg_list(arg_names, args);

scope_env_t scope_env(env, std::move(new_scope));
return body->eval(&scope_env, eval_flags);
eval_error err;
scoped_ptr_t<val_t> ret = body->eval(&err, &scope_env, eval_flags);
if (err.has()) {
err.throw_exc();
}
return ret;
} catch (const datum_exc_t &e) {
rfail(e.get_type(), "%s", e.what());
unreachable();
Expand Down Expand Up @@ -223,7 +228,8 @@ void func_term_t::accumulate_captures(var_captures_t *captures) const {
captures->implicit_is_captured |= external_captures.implicit_is_captured;
}

scoped_ptr_t<val_t> func_term_t::term_eval(scope_env_t *env,
scoped_ptr_t<val_t> func_term_t::term_eval(UNUSED eval_error *err_out,
scope_env_t *env,
UNUSED eval_flags_t flags) const {
return new_val(eval_to_func(env->scope));
}
Expand Down
2 changes: 1 addition & 1 deletion src/rdb_protocol/func.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ class func_term_t : public term_t {
private:
virtual void accumulate_captures(var_captures_t *captures) const;
virtual deterministic_t is_deterministic() const;
virtual scoped_ptr_t<val_t> term_eval(scope_env_t *env, eval_flags_t flags) const;
scoped_ptr_t<val_t> term_eval(eval_error *err_out, scope_env_t *env, eval_flags_t flags) const override;
virtual const char *name() const { return "func"; }

std::vector<sym_t> arg_names;
Expand Down
50 changes: 31 additions & 19 deletions src/rdb_protocol/op.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class faux_term_t : public runtime_term_t {
deterministic_t is_deterministic() const final { return deterministic; }
const char *name() const final { return "<EXPANDED FROM r.args>"; }
private:
scoped_ptr_t<val_t> term_eval(scope_env_t *, eval_flags_t) const final {
scoped_ptr_t<val_t> term_eval(eval_error *, scope_env_t *, eval_flags_t) const final {
return new_val(d);
}
datum_t d;
Expand Down Expand Up @@ -111,7 +111,11 @@ argvec_t arg_terms_t::start_eval(scope_env_t *env, eval_flags_t flags) const {
for (const auto &arg : original_args) {
if (arg->get_src().type() == Term::ARGS) {
deterministic_t det = arg->is_deterministic();
scoped_ptr_t<val_t> v = arg->eval(env, new_flags);
eval_error err;
scoped_ptr_t<val_t> v = arg->eval(&err, env, new_flags);
if (err.has()) {
err.throw_exc();
}
datum_t d = v->as_datum();
for (size_t i = 0; i < d.arr_size(); ++i) {
// This is a little hacky because the determinism flag is for
Expand Down Expand Up @@ -153,18 +157,18 @@ size_t args_t::num_args() const {
deterministic_t args_t::arg_is_deterministic(size_t i) const {
return argv.is_deterministic(i);
}
scoped_ptr_t<val_t> args_t::arg(scope_env_t *env, size_t i, eval_flags_t flags) {
scoped_ptr_t<val_t> args_t::arg(eval_error *err_out, scope_env_t *env, size_t i, eval_flags_t flags) {
if (i == 0 && arg0.has()) {
scoped_ptr_t<val_t> v = std::move(arg0);
arg0.reset();
return v;
} else {
return argv.remove(i)->eval(env, flags);
return argv.remove(i)->eval(err_out, env, flags);
}
}

scoped_ptr_t<val_t> args_t::optarg(scope_env_t *env, const std::string &key) const {
return op_term->optarg(env, key);
scoped_ptr_t<val_t> args_t::optarg(eval_error *err_out, scope_env_t *env, const std::string &key) const {
return op_term->optarg(err_out, env, key);
}

args_t::args_t(const op_term_t *_op_term, argvec_t _argv)
Expand Down Expand Up @@ -208,7 +212,8 @@ op_term_t::~op_term_t() {
}, MIN_TERM_DESTRUCT_STACK_SPACE);
}

scoped_ptr_t<val_t> op_term_t::term_eval(scope_env_t *env,
scoped_ptr_t<val_t> op_term_t::term_eval(eval_error *err_out,
scope_env_t *env,
eval_flags_t eval_flags) const {
argvec_t argv = arg_terms->start_eval(env, eval_flags);
if (can_be_grouped()) {
Expand All @@ -224,27 +229,29 @@ scoped_ptr_t<val_t> op_term_t::term_eval(scope_env_t *env,
for (auto kv = gd->begin(); kv != gd->end(); ++kv) {
arg_terms->start_eval(env, eval_flags);
args_t args(this, argv, make_scoped<val_t>(kv->second, backtrace()));
(*out)[kv->first] = eval_impl(env, &args, eval_flags)->as_datum();
scoped_ptr_t<val_t> val = eval_impl(err_out, env, &args, eval_flags);
if (err_out->has()) { return noval(); }
(*out)[kv->first] = val->as_datum();
}
return make_scoped<val_t>(out, backtrace());
} else {
args_t args(this, std::move(argv), std::move(arg0));
return eval_impl(env, &args, eval_flags);
return eval_impl(err_out, env, &args, eval_flags);
}
} else {
args_t args(this, std::move(argv));
return eval_impl(env, &args, eval_flags);
return eval_impl(err_out, env, &args, eval_flags);
}
}

bool op_term_t::can_be_grouped() const { return true; }
bool op_term_t::is_grouped_seq_op() const { return false; }

scoped_ptr_t<val_t> op_term_t::optarg(scope_env_t *env, const std::string &key) const {
scoped_ptr_t<val_t> op_term_t::optarg(eval_error *err_out, scope_env_t *env, const std::string &key) const {
std::map<std::string, counted_t<const term_t> >::const_iterator it
= optargs.find(key);
if (it != optargs.end()) {
return it->second->eval(env);
return it->second->eval(err_out, env);
}
// returns scoped_ptr_t<val_t>() if the key isn't found
return env->env->get_optarg(env->env, key);
Expand Down Expand Up @@ -305,7 +312,11 @@ void op_term_t::maybe_grouped_data(scope_env_t *env,
grouped_data_out->reset();
arg0_out->reset();
} else {
scoped_ptr_t<val_t> arg0 = argv->remove(0)->eval(env, flags);
eval_error err;
scoped_ptr_t<val_t> arg0 = argv->remove(0)->eval(&err, env, flags);
if (err.has()) {
err.throw_exc();
}

counted_t<grouped_data_t> gd = is_grouped_seq_op()
? arg0->maybe_as_grouped_data()
Expand All @@ -326,17 +337,18 @@ bounded_op_term_t::bounded_op_term_t(compile_env_t *env, const raw_term_t &term,
: op_term_t(env, term, argspec,
optargspec.with({"left_bound", "right_bound"})) { }

bool bounded_op_term_t::is_left_open(scope_env_t *env, args_t *args) const {
return open_bool(env, args, "left_bound", false);
bool bounded_op_term_t::is_left_open(eval_error *err_out, scope_env_t *env, args_t *args) const {
return open_bool(err_out, env, args, "left_bound", false);
}

bool bounded_op_term_t::is_right_open(scope_env_t *env, args_t *args) const {
return open_bool(env, args, "right_bound", true);
bool bounded_op_term_t::is_right_open(eval_error *err_out, scope_env_t *env, args_t *args) const {
return open_bool(err_out, env, args, "right_bound", true);
}

bool bounded_op_term_t::open_bool(
scope_env_t *env, args_t *args, const std::string &key, bool def/*ault*/) const {
scoped_ptr_t<val_t> v = args->optarg(env, key);
eval_error *err_out, scope_env_t *env, args_t *args, const std::string &key, bool def/*ault*/) const {
scoped_ptr_t<val_t> v = args->optarg(err_out, env, key);
if (err_out->has()) { return false; }
if (!v.has()) {
return def;
}
Expand Down
20 changes: 11 additions & 9 deletions src/rdb_protocol/op.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,10 @@ class args_t {
// number of arguments
size_t num_args() const;
// Returns argument `i`.
scoped_ptr_t<val_t> arg(scope_env_t *env, size_t i, eval_flags_t flags = NO_FLAGS);
scoped_ptr_t<val_t> arg(eval_error *err_out, scope_env_t *env, size_t i, eval_flags_t flags = NO_FLAGS);
deterministic_t arg_is_deterministic(size_t i) const;
// Tries to get an optional argument, returns `scoped_ptr_t<val_t>()` if not found.
scoped_ptr_t<val_t> optarg(scope_env_t *env, const std::string &key) const;
scoped_ptr_t<val_t> optarg(eval_error *err_out, scope_env_t *env, const std::string &key) const;

args_t(const op_term_t *op_term, argvec_t argv);
args_t(const op_term_t *op_term, argvec_t argv, scoped_ptr_t<val_t> arg0);
Expand Down Expand Up @@ -188,7 +188,7 @@ class op_term_t : public term_t {
// Union term is a friend so we can steal arguments from an array.
friend class union_term_t;
// Tries to get an optional argument, returns `scoped_ptr_t<val_t>()` if not found.
scoped_ptr_t<val_t> optarg(scope_env_t *env, const std::string &key) const;
scoped_ptr_t<val_t> optarg(eval_error *err_out, scope_env_t *env, const std::string &key) const;

// Evaluates args[0] and sets *grouped_data_out to non-nil if it's grouped.
// (Sets *arg0_out to non-nil if it's not grouped. Both outputs are set to nil
Expand All @@ -199,9 +199,11 @@ class op_term_t : public term_t {
counted_t<grouped_data_t> *grouped_data_out,
scoped_ptr_t<val_t> *arg0_out) const;

virtual scoped_ptr_t<val_t> term_eval(scope_env_t *env,
eval_flags_t eval_flags) const;
virtual scoped_ptr_t<val_t> eval_impl(scope_env_t *env,
scoped_ptr_t<val_t> term_eval(eval_error *err_out,
scope_env_t *env,
eval_flags_t eval_flags) const override;
virtual scoped_ptr_t<val_t> eval_impl(eval_error *err_out,
scope_env_t *env,
args_t *args,
eval_flags_t eval_flags) const = 0;
virtual bool can_be_grouped() const;
Expand Down Expand Up @@ -229,11 +231,11 @@ class bounded_op_term_t : public op_term_t {
virtual ~bounded_op_term_t() { }

protected:
bool is_left_open(scope_env_t *env, args_t *args) const;
bool is_right_open(scope_env_t *env, args_t *args) const;
bool is_left_open(eval_error *err_out, scope_env_t *env, args_t *args) const;
bool is_right_open(eval_error *err_out, scope_env_t *env, args_t *args) const;

private:
bool open_bool(scope_env_t *env, args_t *args, const std::string &key,
bool open_bool(eval_error *err_out, scope_env_t *env, args_t *args, const std::string &key,
bool def/*ault*/) const;
};

Expand Down