1#include "duckdb/parser/expression/comparison_expression.hpp"
2#include "duckdb/planner/expression/bound_cast_expression.hpp"
3#include "duckdb/planner/expression/bound_comparison_expression.hpp"
4#include "duckdb/planner/expression/bound_function_expression.hpp"
5#include "duckdb/planner/expression_binder.hpp"
6#include "duckdb/catalog/catalog_entry/collate_catalog_entry.hpp"
7
8#include "duckdb/function/scalar/string_functions.hpp"
9
10#include "duckdb/main/client_context.hpp"
11#include "duckdb/main/database.hpp"
12
13using namespace duckdb;
14using namespace std;
15
16unique_ptr<Expression> ExpressionBinder::PushCollation(ClientContext &context, unique_ptr<Expression> source,
17 string collation, bool equality_only) {
18 // replace default collation with system collation
19 if (collation.empty()) {
20 collation = context.db.collation;
21 }
22 // bind the collation
23 if (collation.empty() || collation == "binary" || collation == "c" || collation == "posix") {
24 // binary collation: just skip
25 return move(source);
26 }
27 auto &catalog = Catalog::GetCatalog(context);
28 auto splits = StringUtil::Split(StringUtil::Lower(collation), ".");
29 vector<CollateCatalogEntry *> entries;
30 for (auto &collation_argument : splits) {
31 auto collation_entry = catalog.GetEntry<CollateCatalogEntry>(context, DEFAULT_SCHEMA, collation_argument);
32 if (collation_entry->combinable) {
33 entries.insert(entries.begin(), collation_entry);
34 } else {
35 if (entries.size() > 0 && !entries.back()->combinable) {
36 throw BinderException("Cannot combine collation types \"%s\" and \"%s\"", entries.back()->name.c_str(),
37 collation_entry->name.c_str());
38 }
39 entries.push_back(collation_entry);
40 }
41 }
42 for (auto &collation_entry : entries) {
43 if (equality_only && collation_entry->not_required_for_equality) {
44 continue;
45 }
46 auto function = make_unique<BoundFunctionExpression>(TypeId::VARCHAR, collation_entry->function);
47 function->children.push_back(move(source));
48 function->arguments.push_back({SQLType::VARCHAR});
49 function->sql_return_type = SQLType::VARCHAR;
50 if (collation_entry->function.bind) {
51 function->bind_info = collation_entry->function.bind(*function, context);
52 }
53 source = move(function);
54 }
55 return source;
56}
57
58BindResult ExpressionBinder::BindExpression(ComparisonExpression &expr, idx_t depth) {
59 // first try to bind the children of the case expression
60 string error;
61 BindChild(expr.left, depth, error);
62 BindChild(expr.right, depth, error);
63 if (!error.empty()) {
64 return BindResult(error);
65 }
66 // the children have been successfully resolved
67 auto &left = (BoundExpression &)*expr.left;
68 auto &right = (BoundExpression &)*expr.right;
69 // cast the input types to the same type
70 // now obtain the result type of the input types
71 auto input_type = MaxSQLType(left.sql_type, right.sql_type);
72 if (input_type.id == SQLTypeId::VARCHAR) {
73 // for comparison with strings, we prefer to bind to the numeric types
74 if (left.sql_type.IsNumeric()) {
75 input_type = left.sql_type;
76 } else if (right.sql_type.IsNumeric()) {
77 input_type = right.sql_type;
78 } else {
79 // else: check if collations are compatible
80 if (!left.sql_type.collation.empty() && !right.sql_type.collation.empty() &&
81 left.sql_type.collation != right.sql_type.collation) {
82 throw BinderException("Cannot combine types with different collation!");
83 }
84 }
85 }
86 if (input_type.id == SQLTypeId::UNKNOWN) {
87 throw BinderException("Could not determine type of parameters: try adding explicit type casts");
88 }
89 // add casts (if necessary)
90 left.expr = BoundCastExpression::AddCastToType(move(left.expr), left.sql_type, input_type);
91 right.expr = BoundCastExpression::AddCastToType(move(right.expr), right.sql_type, input_type);
92 if (input_type.id == SQLTypeId::VARCHAR) {
93 // handle collation
94 left.expr = PushCollation(context, move(left.expr), input_type.collation, expr.type == ExpressionType::COMPARE_EQUAL);
95 right.expr = PushCollation(context, move(right.expr), input_type.collation, expr.type == ExpressionType::COMPARE_EQUAL);
96 }
97 // now create the bound comparison expression
98 return BindResult(make_unique<BoundComparisonExpression>(expr.type, move(left.expr), move(right.expr)),
99 SQLType(SQLTypeId::BOOLEAN));
100}
101