Repository: duckdb/duckdb
File: src/planner/binder/expression/bind_operator_expression.cpp (C++)

Code before:
#include "duckdb/parser/expression/function_expression.hpp"
#include "duckdb/parser/expression/operator_expression.hpp"
#include "duckdb/planner/binder.hpp"
#include "duckdb/planner/expression/bound_case_expression.hpp"
#include "duckdb/planner/expression/bound_cast_expression.hpp"
#include "duckdb/planner/expression/bound_comparison_expression.hpp"
#include "duckdb/planner/expression/bound_constant_expression.hpp"
#include "duckdb/planner/expression/bound_operator_expression.hpp"
#include "duckdb/planner/expression/bound_parameter_expression.hpp"
#include "duckdb/planner/expression_binder.hpp"

namespace duckdb {

LogicalType ExpressionBinder::ResolveNotType(OperatorExpression &op, vector<unique_ptr<Expression>> &children) {
	// NOT expression, cast child to BOOLEAN
	if (children.size() != 1) {
		throw BinderException(
		    "Failed to bind the NOT operator, too many child expressions found (%d), expected only one",
		    children.size());
	}
	children[0] = BoundCastExpression::AddCastToType(context, std::move(children[0]), LogicalType::BOOLEAN);
	return LogicalType(LogicalTypeId::BOOLEAN);
}

LogicalType ExpressionBinder::ResolveCoalesceType(OperatorExpression &op, vector<unique_ptr<Expression>> &children) {
	if (children.empty()) {
		throw InternalException("IN requires at least a single child node");
	}
	// get the maximum type from the children
	LogicalType max_type = ExpressionBinder::GetExpressionReturnType(*children[0]);
	bool is_in_operator = (op.GetExpressionType() == ExpressionType::COMPARE_IN ||
	                       op.GetExpressionType() == ExpressionType::COMPARE_NOT_IN);
	for (idx_t i = 1; i < children.size(); i++) {
		auto child_return = ExpressionBinder::GetExpressionReturnType(*children[i]);
		if (is_in_operator) {
			// If it's IN/NOT_IN operator, adjust DECIMAL and VARCHAR returned type.
			if (!BoundComparisonExpression::TryBindComparison(context, max_type, child_return, max_type,
			                                                  op.GetExpressionType())) {
				throw BinderException(op,
				                      "Cannot mix values of type %s and %s in %s clause - an explicit cast is required",
				                      max_type.ToString(), child_return.ToString(),

Review comment (bug):
I think this is the wrong place to fix the error - NOT operators should not be generated with multiple expressions. I think we should be throwing this error when expanding the `*COLUMNS(*)` expression instead 

Code after:
#include "duckdb/parser/expression/function_expression.hpp"
#include "duckdb/parser/expression/operator_expression.hpp"
#include "duckdb/planner/binder.hpp"
#include "duckdb/planner/expression/bound_case_expression.hpp"
#include "duckdb/planner/expression/bound_cast_expression.hpp"
#include "duckdb/planner/expression/bound_comparison_expression.hpp"
#include "duckdb/planner/expression/bound_constant_expression.hpp"
#include "duckdb/planner/expression/bound_operator_expression.hpp"
#include "duckdb/planner/expression/bound_parameter_expression.hpp"
#include "duckdb/planner/expression_binder.hpp"

namespace duckdb {

LogicalType ExpressionBinder::ResolveNotType(OperatorExpression &op, vector<unique_ptr<Expression>> &children) {
	// NOT expression, cast child to BOOLEAN
	D_ASSERT(children.size() == 1);
	children[0] = BoundCastExpression::AddCastToType(context, std::move(children[0]), LogicalType::BOOLEAN);
	return LogicalType(LogicalTypeId::BOOLEAN);
}

LogicalType ExpressionBinder::ResolveCoalesceType(OperatorExpression &op, vector<unique_ptr<Expression>> &children) {
	if (children.empty()) {
		throw InternalException("IN requires at least a single child node");
	}
	// get the maximum type from the children
	LogicalType max_type = ExpressionBinder::GetExpressionReturnType(*children[0]);
	bool is_in_operator = (op.GetExpressionType() == ExpressionType::COMPARE_IN ||
	                       op.GetExpressionType() == ExpressionType::COMPARE_NOT_IN);
	for (idx_t i = 1; i < children.size(); i++) {
		auto child_return = ExpressionBinder::GetExpressionReturnType(*children[i]);
		if (is_in_operator) {
			// If it's IN/NOT_IN operator, adjust DECIMAL and VARCHAR returned type.
			if (!BoundComparisonExpression::TryBindComparison(context, max_type, child_return, max_type,
			                                                  op.GetExpressionType())) {
				throw BinderException(op,
				                      "Cannot mix values of type %s and %s in %s clause - an explicit cast is required",
				                      max_type.ToString(), child_return.ToString(),
				                      op.GetExpressionType() == ExpressionType::COMPARE_IN ? "IN" : "NOT IN");
			}
		} else {
			// If it's COALESCE operator, don't do extra adjustment.