Type inference for function calls is a bit complicated: - a function argument can be inferred from the signature: a call to my_func(:arg) will infer :arg's type from the function signature - a function signature can be inferred from its argument types: a call to max(my_column) will select the correct max() signature (as max is generic) from my_column's type Currently, functions::get() implements this by invoking dynamic_cast<selector*> on the argument. If the caller of functions::get() is the SELECT clause preparation, then the cast will succeed and we'll be able to find the type. If not, we fail (and fall back to inferring the argument types from a non-generic function signature). Since we're about to move selectors to expressions, the dynamic_cast will fail, so we must replace it with a less fragile approach. The fix is to augment assignment_testable (the interface representing a function argument) with an intentionally-awkwardly-named assignment_testable_type_opt(), that sees whether we happen to know the type for the argument in order to implement signature-from-argument inference. A note about assignment_testable: this is a bridge interface that is the least common denominator of anything that calls functions. Since we're moving towards expressions, there are fewer implementations of the interface as the code evolves.
70 lines
2.0 KiB
C++
70 lines
2.0 KiB
C++
/*
|
|
* Copyright (C) 2014-present ScyllaDB
|
|
*
|
|
* Modified by ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* SPDX-License-Identifier: (AGPL-3.0-or-later and Apache-2.0)
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "column_specification.hh"
|
|
#include "data_dictionary/data_dictionary.hh"
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
namespace cql3 {
|
|
|
|
class assignment_testable {
|
|
public:
|
|
virtual ~assignment_testable() {}
|
|
|
|
enum class test_result {
|
|
EXACT_MATCH,
|
|
WEAKLY_ASSIGNABLE,
|
|
NOT_ASSIGNABLE,
|
|
};
|
|
|
|
static bool is_assignable(test_result tr) {
|
|
return tr != test_result::NOT_ASSIGNABLE;
|
|
}
|
|
|
|
static bool is_exact_match(test_result tr) {
|
|
return tr != test_result::EXACT_MATCH;
|
|
}
|
|
|
|
/**
|
|
* @return whether this object can be assigned to the provided receiver. We distinguish
|
|
* between 3 values:
|
|
* - EXACT_MATCH if this object is exactly of the type expected by the receiver
|
|
* - WEAKLY_ASSIGNABLE if this object is not exactly the expected type but is assignable nonetheless
|
|
* - NOT_ASSIGNABLE if it's not assignable
|
|
* Most caller should just call the isAssignable() method on the result, though functions have a use for
|
|
* testing "strong" equality to decide the most precise overload to pick when multiple could match.
|
|
*/
|
|
virtual test_result test_assignment(data_dictionary::database db, const sstring& keyspace, const schema* schema_opt, const column_specification& receiver) const = 0;
|
|
|
|
virtual std::optional<data_type> assignment_testable_type_opt() const = 0;
|
|
|
|
// for error reporting
|
|
virtual sstring assignment_testable_source_context() const = 0;
|
|
};
|
|
|
|
inline bool is_assignable(assignment_testable::test_result tr) {
|
|
return assignment_testable::is_assignable(tr);
|
|
}
|
|
|
|
inline bool is_exact_match(assignment_testable::test_result tr) {
|
|
return assignment_testable::is_exact_match(tr);
|
|
}
|
|
|
|
inline
|
|
std::ostream&
|
|
operator<<(std::ostream& os, const assignment_testable& at) {
|
|
return os << at.assignment_testable_source_context();
|
|
}
|
|
|
|
}
|