#include "catch.hpp"
#include "duckdb/common/file_system.hpp"
#include "duckdb/common/enums/joinref_type.hpp"
#include "duckdb/parser/expression/columnref_expression.hpp"
#include "duckdb/parser/expression/constant_expression.hpp"
#include "duckdb/main/relation/value_relation.hpp"
#include "iostream"
#include "test_helpers.hpp"
#include "duckdb/main/relation/materialized_relation.hpp"

using namespace duckdb;
using namespace std;

TEST_CASE("Test simple relation API", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con(db);
	con.EnableQueryVerification();
	duckdb::unique_ptr<QueryResult> result;
	duckdb::shared_ptr<Relation> tbl, filter, proj, proj2, v1, v2, v3;

	// create some tables
	REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i INTEGER)"));
	REQUIRE_NO_FAIL(con.Query("INSERT INTO integers VALUES (1), (2), (3), (1), (2), (3)"));

	// simple projection
	REQUIRE_NOTHROW(tbl = con.Table("integers"));
	REQUIRE_NOTHROW(result = tbl->Project("i + 1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {2, 3, 4}));

	REQUIRE_NOTHROW(result = tbl->Project(duckdb::vector<string> {"i + 1", "i + 2"})->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {2, 3, 4}));
	REQUIRE(CHECK_COLUMN(result, 1, {3, 4, 5}));

	REQUIRE_NOTHROW(result = tbl->Project(duckdb::vector<string> {"i + 1"}, {"i"})->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {2, 3, 4}));

	// we support * expressions
	REQUIRE_NOTHROW(result = tbl->Project("*")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));

	// we can also read the table directly
	REQUIRE_NOTHROW(result = tbl->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));

	// we can stack projections
	REQUIRE_NOTHROW(
	    result =
	        tbl->Project("i + 1 AS i")->Project("i + 1 AS i")->Project("i + 1 AS i")->Project("i + 1 AS i")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {5, 6, 7}));

	// we can execute the same projection multiple times
	REQUIRE_NOTHROW(proj = tbl->Project("i + 1"));
	for (idx_t i = 0; i < 10; i++) {
		REQUIRE_NOTHROW(result = proj->Execute());
		REQUIRE(CHECK_COLUMN(result, 0, {2, 3, 4}));
	}

	result = con.Query("SELECT i+1 FROM (SELECT * FROM integers WHERE i <> 2) relation");
	REQUIRE(CHECK_COLUMN(result, 0, {2, 4}));
	// filter and projection
	REQUIRE_NOTHROW(filter = tbl->Filter("i <> 2"));
	REQUIRE_NOTHROW(proj = filter->Project("i + 1"));
	REQUIRE_NOTHROW(result = proj->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {2, 4}));

	// multi filter
	REQUIRE_NOTHROW(result = tbl->Filter(duckdb::vector<string> {"i <> 2", "i <> 3"})->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1}));

	// we can reuse the same filter again and perform a different projection
	REQUIRE_NOTHROW(proj = filter->Project("i * 10"));
	REQUIRE_NOTHROW(result = proj->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {10, 30}));

	// add a limit
	REQUIRE_NOTHROW(result = proj->Limit(1)->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {10}));

	// and an offset
	REQUIRE_NOTHROW(result = proj->Limit(1, 1)->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {30}));

	// lets add some aliases
	REQUIRE_NOTHROW(proj = filter->Project("i + 1 AS a"));
	REQUIRE_NOTHROW(result = proj->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {2, 4}));
	// we can check the column names
	REQUIRE(proj->Columns()[0].Name() == "a");
	REQUIRE(proj->Columns()[0].Type() == LogicalType::INTEGER);

	// we can also alias like this
	REQUIRE_NOTHROW(proj = filter->Project("i + 1", "a"));
	REQUIRE_NOTHROW(result = proj->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {2, 4}));
	// we can check the column names
	REQUIRE(proj->Columns()[0].Name() == "a");
	REQUIRE(proj->Columns()[0].Type() == LogicalType::INTEGER);

	// now we can use that column to perform additional projections
	REQUIRE_NOTHROW(result = proj->Project("a + 1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {3, 5}));

	// we can also filter on this again
	REQUIRE_NOTHROW(result = proj->Filter("a=2")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {2}));

	// filters can also contain conjunctions
	REQUIRE_NOTHROW(result = proj->Filter("a=2 OR a=4")->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {2, 2, 4, 4}));

	// alias
	REQUIRE_NOTHROW(result = proj->Project("a + 1")->Alias("bla")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {3, 5}));

	// now test ordering
	REQUIRE_NOTHROW(result = proj->Order("a DESC")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {4, 4, 2, 2}));
	REQUIRE_NOTHROW(result = proj->Order(duckdb::vector<string> {"a DESC", "a ASC"})->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {4, 4, 2, 2}));

	// top n
	REQUIRE_NOTHROW(result = proj->Order("a")->Limit(1)->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {2}));
	REQUIRE_NOTHROW(result = proj->Order("a DESC")->Limit(1)->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {4}));

	// test set operations
	REQUIRE_NOTHROW(result = tbl->Union(tbl)->Order("i")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3}));
	REQUIRE_NOTHROW(result = tbl->Except(tbl)->Order("i")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {}));
	REQUIRE_NOTHROW(result = tbl->Intersect(tbl)->Order("i")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 1, 2, 2, 3, 3}));
	REQUIRE_NOTHROW(result = tbl->Except(tbl->Filter("i=2"))->Order("i")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 1, 3, 3}));
	REQUIRE_NOTHROW(result = tbl->Intersect(tbl->Filter("i=2"))->Order("i")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {2, 2}));

	// set operations with projections
	REQUIRE_NOTHROW(proj = tbl->Project("i::TINYINT AS i, i::SMALLINT, i::BIGINT, i::VARCHAR"));
	REQUIRE_NOTHROW(proj2 = tbl->Project("(i+10)::TINYINT, (i+10)::SMALLINT, (i+10)::BIGINT, (i+10)::VARCHAR"));
	REQUIRE_NOTHROW(result = proj->Union(proj2)->Order("i")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 1, 2, 2, 3, 3, 11, 11, 12, 12, 13, 13}));
	REQUIRE(CHECK_COLUMN(result, 1, {1, 1, 2, 2, 3, 3, 11, 11, 12, 12, 13, 13}));
	REQUIRE(CHECK_COLUMN(result, 2, {1, 1, 2, 2, 3, 3, 11, 11, 12, 12, 13, 13}));
	REQUIRE(CHECK_COLUMN(result, 3, {"1", "1", "2", "2", "3", "3", "11", "11", "12", "12", "13", "13"}));

	// distinct
	REQUIRE_NOTHROW(result = tbl->Union(tbl)->Union(tbl)->Distinct()->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));

	// join
	REQUIRE_NOTHROW(v1 = con.Values({{1, 10}, {2, 5}, {3, 4}}, {"id", "j"}, "v1"));
	REQUIRE_NOTHROW(v2 = con.Values({{1, 27}, {2, 8}, {3, 20}}, {"id", "k"}, "v2"));
	REQUIRE_NOTHROW(v3 = con.Values({{1, 2}, {2, 6}, {3, 10}}, {"id", "k"}, "v3"));
	REQUIRE_NOTHROW(result = v1->Join(v2, "v1.id=v2.id")->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 5, 4}));
	REQUIRE(CHECK_COLUMN(result, 2, {1, 2, 3}));
	REQUIRE(CHECK_COLUMN(result, 3, {27, 8, 20}));

	// asof join
	REQUIRE_NOTHROW(v1 = con.Values({{1, 10}, {6, 5}, {8, 4}, {10, 23}, {12, 12}, {15, 14}}, {"id", "j"}, "v1"));
	REQUIRE_NOTHROW(v2 = con.Values({{4, 27}, {8, 8}, {14, 20}}, {"id", "k"}, "v2"));
	REQUIRE_NOTHROW(result = v1->Join(v2, "v1.id>=v2.id", JoinType::INNER, JoinRefType::ASOF)->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {6, 8, 10, 12, 15}));
	REQUIRE(CHECK_COLUMN(result, 1, {5, 4, 23, 12, 14}));
	REQUIRE(CHECK_COLUMN(result, 2, {4, 8, 8, 8, 14}));
	REQUIRE(CHECK_COLUMN(result, 3, {27, 8, 8, 8, 20}));

	REQUIRE_NOTHROW(v1 = con.Values({{1, 10}, {2, 5}, {3, 4}}, {"id", "j"}, "v1"));
	REQUIRE_NOTHROW(v2 = con.Values({{1, 27}, {2, 8}, {3, 20}}, {"id", "k"}, "v2"));
	REQUIRE_NOTHROW(v3 = con.Values({{1, 2}, {2, 6}, {3, 10}}, {"id", "k"}, "v3"));

	// projection after a join
	REQUIRE_NOTHROW(result = v1->Join(v2, "v1.id=v2.id")->Project("v1.id+v2.id, j+k")->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {2, 4, 6}));
	REQUIRE(CHECK_COLUMN(result, 1, {37, 13, 24}));

	// chain multiple joins
	auto multi_join = v1->Join(v2, "v1.id=v2.id")->Join(v3, "v1.id=v3.id");
	REQUIRE_NOTHROW(result = multi_join->Project("v1.id+v2.id+v3.id")->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {3, 6, 9}));

	// multiple joins followed by a filter and a projection
	REQUIRE_NOTHROW(result = multi_join->Filter("v1.id=1")->Project("v1.id+v2.id+v3.id")->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {3}));
	// multiple joins followed by multiple filters
	REQUIRE_NOTHROW(result = multi_join->Filter("v1.id>0")
	                             ->Filter("v2.id < 3")
	                             ->Filter("v3.id=2")
	                             ->Project("v1.id+v2.id+v3.id")
	                             ->Order("1")
	                             ->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {6}));

	// test explain
	REQUIRE_NO_FAIL(multi_join->Explain());

	// incorrect API usage
	REQUIRE_THROWS(tbl->Project(duckdb::vector<string> {})->Execute());
	REQUIRE_THROWS(tbl->Project(duckdb::vector<string> {"1, 2, 3"})->Execute());
	REQUIRE_THROWS(tbl->Project(duckdb::vector<string> {""})->Execute());
	REQUIRE_THROWS(tbl->Filter("i=1, i=2")->Execute());
	REQUIRE_THROWS(tbl->Filter("")->Execute());
	REQUIRE_THROWS(tbl->Filter(duckdb::vector<string> {})->Execute());
	REQUIRE_THROWS(tbl->Filter(duckdb::vector<string> {"1, 2, 3"})->Execute());
	REQUIRE_THROWS(tbl->Filter(duckdb::vector<string> {""})->Execute());
	REQUIRE_THROWS(tbl->Order(duckdb::vector<string> {})->Execute());
	REQUIRE_THROWS(tbl->Order(duckdb::vector<string> {"1, 2, 3"})->Execute());
	REQUIRE_THROWS(tbl->Order("1 LIMIT 3")->Execute());
	REQUIRE_THROWS(tbl->Order("1; SELECT 42")->Execute());
	REQUIRE_THROWS(tbl->Join(tbl, "")->Execute());
	REQUIRE_THROWS(tbl->Join(tbl, "a, a+1")->Execute());
	REQUIRE_THROWS(tbl->Join(tbl, "a, bla.bla")->Execute());
}

TEST_CASE("Test combinations of set operations", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con(db);
	con.EnableQueryVerification();
	duckdb::unique_ptr<QueryResult> result;
	duckdb::shared_ptr<Relation> values, v1, v2, v3;

	REQUIRE_NOTHROW(values = con.Values({{1, 10}, {2, 5}, {3, 4}}, {"i", "j"}));

	// union between values
	auto vunion = values->Union(values);
	REQUIRE_NOTHROW(result = vunion->Order("i")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 1, 2, 2, 3, 3}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 10, 5, 5, 4, 4}));

	// different ops after a union
	// order and limit
	REQUIRE_NOTHROW(result = vunion->Order("i")->Limit(1)->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1}));
	REQUIRE(CHECK_COLUMN(result, 1, {10}));
	// multiple orders and limits
	REQUIRE_NOTHROW(result = vunion->Order("i")->Limit(4)->Order("j")->Limit(2)->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {2, 2}));
	REQUIRE(CHECK_COLUMN(result, 1, {5, 5}));
	// filter
	REQUIRE_NOTHROW(result = vunion->Filter("i=1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 1}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 10}));
	// multiple filters
	REQUIRE_NOTHROW(result = vunion->Filter("i<3")->Filter("j=5")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {2, 2}));
	REQUIRE(CHECK_COLUMN(result, 1, {5, 5}));
	// distinct
	REQUIRE_NOTHROW(result = vunion->Distinct()->Order("j DESC")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 5, 4}));
	// multiple distincts followed by a top-n
	REQUIRE_NOTHROW(result = vunion->Distinct()->Distinct()->Distinct()->Order("j DESC")->Limit(2)->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 5}));
	// top-n followed by multiple distincts
	REQUIRE_NOTHROW(result = vunion->Order("j DESC")->Limit(2)->Distinct()->Distinct()->Distinct()->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1}));
	REQUIRE(CHECK_COLUMN(result, 1, {10}));

	// multiple set ops
	REQUIRE_NOTHROW(result = vunion->Union(vunion)->Distinct()->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 5, 4}));
	REQUIRE_NOTHROW(result = vunion->Intersect(vunion)->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 1, 2, 2, 3, 3}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 10, 5, 5, 4, 4}));
	REQUIRE_NOTHROW(result = vunion->Except(vunion)->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {}));
	REQUIRE(CHECK_COLUMN(result, 1, {}));

	// setops require the same amount of columns on both sides
	REQUIRE_NOTHROW(v1 = con.Values("(1, 2), (3, 4)"));
	REQUIRE_NOTHROW(v2 = con.Values("(1)"));
	REQUIRE_THROWS(v1->Union(v2)->Execute());

	// setops require the same types on both sides
	REQUIRE_NOTHROW(v1 = con.Values("(DATE '1992-01-01', 2)"));
	REQUIRE_NOTHROW(v2 = con.Values("(3.0, 'hello')"));
	REQUIRE_FAIL(v1->Union(v2)->Execute());
}

TEST_CASE("Test combinations of joins", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con(db);
	con.EnableQueryVerification();
	duckdb::unique_ptr<QueryResult> result;
	duckdb::shared_ptr<Relation> values, vjoin;

	REQUIRE_NOTHROW(values = con.Values({{1, 10}, {2, 5}, {3, 4}}, {"i", "j"}));

	auto v1 = values->Alias("v1");
	auto v2 = values->Alias("v2");

	// join on explicit join condition
	vjoin = v1->Join(v2, "v1.i=v2.i");
	REQUIRE_NOTHROW(result = vjoin->Order("v1.i")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 5, 4}));
	REQUIRE(CHECK_COLUMN(result, 2, {1, 2, 3}));
	REQUIRE(CHECK_COLUMN(result, 3, {10, 5, 4}));

	// aggregate on EXPLICIT join
	REQUIRE_NOTHROW(result = vjoin->Aggregate("SUM(v1.i) + SUM(v2.i), SUM(v1.j) + SUM(v2.j)")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {12}));
	REQUIRE(CHECK_COLUMN(result, 1, {38}));

	// implicit join
	vjoin = v1->Join(v2, "i");
	REQUIRE_NOTHROW(result = vjoin->Order("i")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 5, 4}));
	REQUIRE(CHECK_COLUMN(result, 2, {10, 5, 4}));

	// implicit join on multiple columns
	vjoin = v1->Join(v2, "i, j");
	REQUIRE_NOTHROW(result = vjoin->Order("i")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 5, 4}));

	// aggregate on USING join
	REQUIRE_NOTHROW(result = vjoin->Aggregate("SUM(i), SUM(j)")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {6}));
	REQUIRE(CHECK_COLUMN(result, 1, {19}));

	// joining on a column that doesn't exist results in an error
	REQUIRE_THROWS(v1->Join(v2, "blabla"));
	// also with explicit join condition
	REQUIRE_THROWS(v1->Join(v2, "v1.i=v2.blabla"));

	// set ops involving joins
	REQUIRE_NOTHROW(result = vjoin->Union(vjoin)->Distinct()->Order("i")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 5, 4}));

	// do a bunch of joins in a loop
	auto v1tmp = v1;
	auto v2tmp = v2;
	for (idx_t i = 0; i < 4; i++) {
		REQUIRE_NOTHROW(v1tmp = v1tmp->Join(v2tmp->Alias(to_string(i)), "i, j"));
	}
	REQUIRE_NOTHROW(result = v1tmp->Order("i")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 5, 4}));

	// now add on some projections and such
	auto complex_join = v1tmp->Order("i")->Limit(2)->Order("i DESC")->Limit(1)->Project("i+1, j+1");
	for (idx_t i = 0; i < 3; i++) {
		REQUIRE_NOTHROW(result = complex_join->Execute());
		REQUIRE(CHECK_COLUMN(result, 0, {3}));
		REQUIRE(CHECK_COLUMN(result, 1, {6}));
	}

	// create and query a view
	REQUIRE_NOTHROW(complex_join->CreateView("test123"));
	result = con.Query("SELECT * FROM test123");
	REQUIRE(CHECK_COLUMN(result, 0, {3}));
	REQUIRE(CHECK_COLUMN(result, 1, {6}));

	// joins of tables that have output modifiers attached to them (limit, order by, distinct)
	auto v1_modified = v1->Limit(100)->Order("1")->Distinct()->Filter("i<1000");
	auto v2_modified = v2->Limit(100)->Order("1")->Distinct()->Filter("i<1000");
	vjoin = v1_modified->Join(v2_modified, "i, j");
	REQUIRE_NOTHROW(result = vjoin->Order("i")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 5, 4}));

	REQUIRE_NO_FAIL(con.Query("SELECT * FROM sqlite_master"));
}

TEST_CASE("Test crossproduct relation", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con(db);
	con.EnableQueryVerification();
	duckdb::unique_ptr<QueryResult> result;
	duckdb::shared_ptr<Relation> values, vcross;

	REQUIRE_NOTHROW(values = con.Values({{1, 10}, {2, 5}, {3, 4}}, {"i", "j"}), "v1");
	REQUIRE_NOTHROW(values = con.Values({{1, 10}, {2, 5}, {3, 4}}, {"i", "j"}), "v2");

	auto v1 = values->Alias("v1");
	auto v2 = values->Alias("v2");

	// run cross product
	vcross = v1->CrossProduct(v2);
	REQUIRE_NOTHROW(result = vcross->Order("v1.i, v2.i")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 1, 1, 2, 2, 2, 3, 3, 3}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 10, 10, 5, 5, 5, 4, 4, 4}));
	REQUIRE(CHECK_COLUMN(result, 2, {1, 2, 3, 1, 2, 3, 1, 2, 3}));
	REQUIRE(CHECK_COLUMN(result, 3, {10, 5, 4, 10, 5, 4, 10, 5, 4}));

	// run a positional cross product
	auto join_ref_type = JoinRefType::POSITIONAL;
	vcross = v1->CrossProduct(v2, join_ref_type);
	REQUIRE_NOTHROW(result = vcross->Order("v1.i")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 5, 4}));
	REQUIRE(CHECK_COLUMN(result, 2, {1, 2, 3}));
	REQUIRE(CHECK_COLUMN(result, 3, {10, 5, 4}));
}

TEST_CASE("Test view creation of relations", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con(db);
	con.EnableQueryVerification();
	duckdb::unique_ptr<QueryResult> result;
	duckdb::shared_ptr<Relation> tbl, filter, proj, proj2;

	// create some tables
	REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i INTEGER)"));
	REQUIRE_NOTHROW(con.Table("integers")->Insert({{Value::INTEGER(1)}, {Value::INTEGER(2)}, {Value::INTEGER(3)}}));

	// insertion failure because of primary key
	REQUIRE_NO_FAIL(con.Query("CREATE TABLE tbl_pk(i INTEGER PRIMARY KEY)"));
	REQUIRE_NOTHROW(con.Table("tbl_pk")->Insert({{Value::INTEGER(1)}, {Value::INTEGER(2)}, {Value::INTEGER(3)}}));
	REQUIRE_THROWS(con.Table("tbl_pk")->Insert({{Value::INTEGER(1)}, {Value::INTEGER(2)}, {Value::INTEGER(3)}}));

	// simple view creation
	REQUIRE_NOTHROW(tbl = con.Table("integers"));
	REQUIRE_NOTHROW(result = tbl->Query("test", "SELECT * FROM test"));
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));

	duckdb::vector<duckdb::unique_ptr<ParsedExpression>> expressions;
	expressions.push_back(duckdb::make_uniq<duckdb::ColumnRefExpression>("i"));
	duckdb::vector<duckdb::string> aliases;
	aliases.push_back("j");

	REQUIRE_NOTHROW(result = tbl->Project(std::move(expressions), aliases)->Query("test", "SELECT * FROM test"));
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));
	// add a projection
	REQUIRE_NOTHROW(result = tbl->Project("i + 1")->Query("test", "SELECT * FROM test"));
	REQUIRE(CHECK_COLUMN(result, 0, {2, 3, 4}));

	// multiple projections
	proj = tbl->Project("i + 1", "i");
	for (idx_t i = 0; i < 10; i++) {
		proj = proj->Project("i + 1", "i");
	}
	REQUIRE_NOTHROW(result = proj->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {12, 13, 14}));
	REQUIRE_NOTHROW(result = proj->Query("test", "SELECT * FROM test"));
	REQUIRE(CHECK_COLUMN(result, 0, {12, 13, 14}));

	// we can also use more complex SQL
	REQUIRE_NOTHROW(result = proj->Query("test", "SELECT SUM(t1.i) FROM test t1 JOIN test t2 ON t1.i=t2.i"));
	REQUIRE(CHECK_COLUMN(result, 0, {39}));

	// limit
	REQUIRE_NOTHROW(result = tbl->Limit(1)->Query("test", "SELECT * FROM test"));
	REQUIRE(CHECK_COLUMN(result, 0, {1}));
	// order
	REQUIRE_NOTHROW(result = tbl->Order("i DESC")->Limit(1)->Query("test", "SELECT * FROM test"));
	REQUIRE(CHECK_COLUMN(result, 0, {3}));
	// union
	auto node = tbl->Order("i DESC")->Limit(1);
	REQUIRE_NOTHROW(result = node->Union(node)->Query("test", "SELECT * FROM test"));
	REQUIRE(CHECK_COLUMN(result, 0, {3, 3}));
	// distinct
	REQUIRE_NOTHROW(result = node->Union(node)->Distinct()->Query("test", "SELECT * FROM test"));
	REQUIRE(CHECK_COLUMN(result, 0, {3}));

	// manually create views and query from them
	result = con.Query("SELECT i+1 FROM integers UNION SELECT i+10 FROM integers ORDER BY 1");
	REQUIRE(CHECK_COLUMN(result, 0, {2, 3, 4, 11, 12, 13}));

	REQUIRE_NOTHROW(tbl->Project("i + 1")->CreateView("test1"));
	REQUIRE_NOTHROW(tbl->Project("i + 10")->CreateView("test2"));
	result = con.Query("SELECT * FROM test1 UNION SELECT * FROM test2 ORDER BY 1");
	REQUIRE(CHECK_COLUMN(result, 0, {2, 3, 4, 11, 12, 13}));

	// project <> alias column count mismatch
	REQUIRE_THROWS(proj->Project("i + 1, i + 2", "i"));
	// view already exists
	REQUIRE_THROWS(tbl->Project("i + 10")->CreateView("test2", false));
	// table already exists
	REQUIRE_THROWS(tbl->Project("i + 10")->Create("test2"));
}

TEST_CASE("Test table creations using the relation API", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con(db);
	con.EnableQueryVerification();
	duckdb::unique_ptr<QueryResult> result;
	duckdb::shared_ptr<Relation> values;

	// create a table from a Values statement
	REQUIRE_NOTHROW(values = con.Values({{1, 10}, {2, 5}, {3, 4}}, {"i", "j"}));
	REQUIRE_NOTHROW(values->Create("integers"));

	result = con.Query("SELECT * FROM integers ORDER BY i");
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 5, 4}));

	// insert from a set of values
	REQUIRE_NOTHROW(con.Values({{4, 7}, {5, 8}})->Insert("integers"));

	result = con.Query("SELECT * FROM integers ORDER BY i");
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3, 4, 5}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 5, 4, 7, 8}));

	// create a table from a query
	REQUIRE_NOTHROW(
	    con.Table("integers")->Filter("i BETWEEN 3 AND 4")->Project("i + 1 AS k, 'hello' AS l")->Create("new_values"));

	result = con.Query("SELECT * FROM new_values ORDER BY k");
	REQUIRE(CHECK_COLUMN(result, 0, {4, 5}));
	REQUIRE(CHECK_COLUMN(result, 1, {"hello", "hello"}));

	// create a table in an attached db and insert values
	auto test_dir = TestDirectoryPath();
	string db_path = test_dir + "/my_db.db";
	REQUIRE_NO_FAIL(con.Query("ATTACH '" + db_path + "' AS my_db;"));
	REQUIRE_NOTHROW(values = con.Values({{1, 10}, {2, 5}, {3, 4}}, {"i", "j"}));
	REQUIRE_NOTHROW(values->Create(std::string("my_db"), std::string(), std::string("integers")));
	result = con.Query("SELECT * FROM my_db.integers ORDER BY i");
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 5, 4}));
}

TEST_CASE("Test table creations with on_create_conflict using the relation API", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con(db);
	con.EnableQueryVerification();
	duckdb::unique_ptr<QueryResult> result;
	duckdb::shared_ptr<Relation> values, values1, proj;

	// create a table from a Values statement
	REQUIRE_NOTHROW(values = con.Values({{1, 10}, {2, 5}, {3, 4}}, {"i", "j"}));
	REQUIRE_NOTHROW(values->Create("integers"));

	result = con.Query("SELECT * FROM integers ORDER BY i");
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 5, 4}));

	REQUIRE_NOTHROW(values1 = con.Values({{4, 14}, {5, 15}, {6, 16}}, {"i", "j"}));
	REQUIRE_THROWS(values1->Create("integers"));
	result = con.Query("SELECT * FROM integers ORDER BY i");
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 5, 4}));

	REQUIRE_THROWS(values1->Create("integers", false, OnCreateConflict::ERROR_ON_CONFLICT));
	result = con.Query("SELECT * FROM integers ORDER BY i");
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 5, 4}));

	REQUIRE_NOTHROW(values1->Create("integers", false, OnCreateConflict::IGNORE_ON_CONFLICT));
	result = con.Query("SELECT * FROM integers ORDER BY i");
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 5, 4}));

	REQUIRE_NOTHROW(values1->Create("integers", false, OnCreateConflict::REPLACE_ON_CONFLICT));
	result = con.Query("SELECT * FROM integers ORDER BY i");
	REQUIRE(CHECK_COLUMN(result, 0, {4, 5, 6}));
	REQUIRE(CHECK_COLUMN(result, 1, {14, 15, 16}));
}

TEST_CASE("Test table create from query with on_create_conflict using the relation API", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con(db);
	con.EnableQueryVerification();
	duckdb::unique_ptr<QueryResult> result;
	duckdb::shared_ptr<Relation> values, values1, proj;

	REQUIRE_NOTHROW(values = con.Values({{1, 10}, {2, 20}, {3, 30}, {4, 40}}, {"i", "j"}));
	REQUIRE_NOTHROW(values->Create("integers"));

	result = con.Query("SELECT * FROM integers ORDER BY i");
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3, 4}));
	REQUIRE(CHECK_COLUMN(result, 1, {10, 20, 30, 40}));

	REQUIRE_NOTHROW(con.Table("integers")
	                    ->Filter("i BETWEEN 2 AND 3")
	                    ->Project("i + 100 AS k, 'hello' AS l")
	                    ->Create("new_values"));

	result = con.Query("SELECT * FROM new_values ORDER BY k");
	REQUIRE(CHECK_COLUMN(result, 0, {102, 103}));
	REQUIRE(CHECK_COLUMN(result, 1, {"hello", "hello"}));

	REQUIRE_NOTHROW(proj = con.Table("integers")->Filter("i BETWEEN 1 AND 2")->Project("i + 200 AS k, 'hi' AS l"));
	REQUIRE_THROWS(proj->Create("new_values"));
	REQUIRE_THROWS(proj->Create("new_values", false, OnCreateConflict::ERROR_ON_CONFLICT));
	REQUIRE_NOTHROW(proj->Create("new_values", false, OnCreateConflict::IGNORE_ON_CONFLICT));

	result = con.Query("SELECT * FROM new_values ORDER BY k");
	REQUIRE(CHECK_COLUMN(result, 0, {102, 103}));
	REQUIRE(CHECK_COLUMN(result, 1, {"hello", "hello"}));

	REQUIRE_NOTHROW(proj->Create("new_values", false, OnCreateConflict::REPLACE_ON_CONFLICT));
	result = con.Query("SELECT * FROM new_values ORDER BY k");
	REQUIRE(CHECK_COLUMN(result, 0, {201, 202}));
	REQUIRE(CHECK_COLUMN(result, 1, {"hi", "hi"}));
}

TEST_CASE("Test table deletions and updates", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con(db);
	con.EnableQueryVerification();
	duckdb::unique_ptr<QueryResult> result;

	REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i INTEGER)"));
	REQUIRE_NO_FAIL(con.Query("INSERT INTO integers VALUES (1), (2), (3)"));

	auto tbl = con.Table("integers");

	// update
	tbl->Update("i=i+10", "i=2");

	result = con.Query("SELECT * FROM integers ORDER BY 1");
	REQUIRE(CHECK_COLUMN(result, 0, {1, 3, 12}));

	// we can only have a single expression in the condition liset
	REQUIRE_THROWS(tbl->Update("i=1", "i=3,i<100"));

	tbl->Delete("i=3");

	result = con.Query("SELECT * FROM integers ORDER BY 1");
	REQUIRE(CHECK_COLUMN(result, 0, {1, 12}));

	// delete without condition
	tbl->Delete();

	result = con.Query("SELECT * FROM integers ORDER BY 1");
	REQUIRE(CHECK_COLUMN(result, 0, {}));

	// we cannot run update/delete on anything but base table relations
	REQUIRE_THROWS(tbl->Limit(1)->Delete());
	REQUIRE_THROWS(tbl->Limit(1)->Update("i=1"));
}

TEST_CASE("Test aggregates in relation API", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con(db);
	con.EnableQueryVerification();
	duckdb::unique_ptr<QueryResult> result;

	// create a table
	REQUIRE_NOTHROW(con.Values("(1, 5), (2, 6), (1, 7)", {"i", "j"})->Create("integers"));

	// perform some aggregates
	auto tbl = con.Table("integers");

	// ungrouped aggregate
	REQUIRE_NOTHROW(result = tbl->Aggregate("SUM(i), SUM(j)")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {4}));
	REQUIRE(CHECK_COLUMN(result, 1, {18}));

	REQUIRE_NOTHROW(result = tbl->Aggregate(duckdb::vector<string> {"SUM(i)", "SUM(j)"})->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {4}));
	REQUIRE(CHECK_COLUMN(result, 1, {18}));

	// we cannot put aggregates in a Project clause
	REQUIRE_THROWS(result = tbl->Project("SUM(i), SUM(j)")->Execute());
	REQUIRE_THROWS(result = tbl->Project("i, SUM(j)")->Execute());
	// implicitly grouped aggregate
	REQUIRE_NOTHROW(result = tbl->Aggregate("i, SUM(j)")->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2}));
	REQUIRE(CHECK_COLUMN(result, 1, {12, 6}));
	REQUIRE_NOTHROW(result = tbl->Aggregate("SUM(j), i")->Order("2")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {12, 6}));
	REQUIRE(CHECK_COLUMN(result, 1, {1, 2}));
	// explicitly grouped aggregate
	REQUIRE_NOTHROW(
	    result =
	        tbl->Aggregate(duckdb::vector<string> {"SUM(j)"}, duckdb::vector<string> {"i"})->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {6, 12}));

	// grouped aggregates can be expressions
	REQUIRE_NOTHROW(result = tbl->Aggregate("i+1 AS i, SUM(j)")->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {2, 3}));
	REQUIRE(CHECK_COLUMN(result, 1, {12, 6}));
	// they can also involve multiple columns
	REQUIRE_NOTHROW(result = tbl->Aggregate("i+i AS i, SUM(j)")->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {2, 4}));
	REQUIRE(CHECK_COLUMN(result, 1, {12, 6}));
	// we cannot combine non-aggregates with aggregates
	REQUIRE_THROWS(result = tbl->Aggregate("i + SUM(j) AS i")->Order("1")->Execute());
	REQUIRE_THROWS(result = tbl->Aggregate("i, i + SUM(j)")->Order("1")->Execute());
	// group by multiple columns
	REQUIRE_NOTHROW(result = tbl->Aggregate("i, j, SUM(i + j)")->Order("1, 2")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 1, 2}));
	REQUIRE(CHECK_COLUMN(result, 1, {5, 7, 6}));
	REQUIRE(CHECK_COLUMN(result, 2, {6, 8, 8}));
	// subqueries as groups
	REQUIRE_NOTHROW(result = tbl->Aggregate("(SELECT i), SUM(i + j)")->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2}));
	REQUIRE(CHECK_COLUMN(result, 1, {14, 8}));
	// subqueries as aggregates
	REQUIRE_NOTHROW(result = tbl->Aggregate("(SELECT i), (SELECT SUM(i + j))")->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2}));
	REQUIRE(CHECK_COLUMN(result, 1, {14, 8}));
	// constants without a grouping column
	REQUIRE_NOTHROW(result = tbl->Aggregate("'hello', SUM(i + j)")->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {"hello"}));
	REQUIRE(CHECK_COLUMN(result, 1, {22}));
	// constants with a grouping column
	REQUIRE_NOTHROW(result = tbl->Aggregate("i, 'hello', SUM(i + j)")->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2}));
	REQUIRE(CHECK_COLUMN(result, 1, {"hello", "hello"}));
	REQUIRE(CHECK_COLUMN(result, 2, {14, 8}));
	// aggregate with only non-aggregate columns becomes a distinct
	REQUIRE_NOTHROW(result = tbl->Aggregate("i")->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2}));
	REQUIRE_NOTHROW(result = tbl->Aggregate("i, j")->Order("1, 2")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 1, 2}));
	REQUIRE(CHECK_COLUMN(result, 1, {5, 7, 6}));

	// now test aggregates with explicit groups
	REQUIRE_NOTHROW(result = tbl->Aggregate("i", "i")->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2}));
	REQUIRE_NOTHROW(result = tbl->Aggregate("i, SUM(j)", "i")->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2}));
	REQUIRE(CHECK_COLUMN(result, 1, {12, 6}));
	// explicit groups can be combined with aggregates
	REQUIRE_NOTHROW(result = tbl->Aggregate("i, i+SUM(j)", "i")->Order("1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2}));
	REQUIRE(CHECK_COLUMN(result, 1, {13, 8}));
	// when using explicit groups, we cannot have non-explicit groups
	REQUIRE_THROWS(tbl->Aggregate("j, i+SUM(j)", "i")->Order("1")->Execute());

	// Coverage: Groups expressions can not create multiple statements
	REQUIRE_THROWS(tbl->Aggregate("i", "i; select 42")->Execute());

	// project -> aggregate -> project -> aggregate
	// SUM(j) = 18 -> 18 + 1 = 19 -> 19 * 2 = 38
	result = tbl->Aggregate("SUM(j) AS k")->Project("k+1 AS l")->Aggregate("SUM(l) AS m")->Project("m*2")->Execute();
	REQUIRE(CHECK_COLUMN(result, 0, {38}));

	// aggregate after output modifiers
	result = tbl->Order("i")->Limit(100)->Aggregate("SUM(j) AS k")->Execute();
	REQUIRE(CHECK_COLUMN(result, 0, {18}));
}

TEST_CASE("Test interaction of relations with transactions", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con1(db), con2(db);
	duckdb::unique_ptr<QueryResult> result;

	con1.BeginTransaction();
	con2.BeginTransaction();

	// create a table in con1
	REQUIRE_NOTHROW(con1.Values("(1), (2), (3)")->Create("integers"));

	// con1 can see it, but con2 can't see it yet
	REQUIRE_NOTHROW(con1.Table("integers"));
	REQUIRE_THROWS(con2.Table("integers"));

	// we can also rollback
	con1.Rollback();

	REQUIRE_THROWS(con1.Table("integers"));
	REQUIRE_THROWS(con2.Table("integers"));

	// recreate the table, this time in auto-commit mode
	REQUIRE_NOTHROW(con1.Values("(1), (2), (3)")->Create("integers"));

	// con2 still can't see it, because it is in its own transaction
	REQUIRE_NOTHROW(con1.Table("integers"));
	REQUIRE_THROWS(con2.Table("integers"));

	// after con2 commits, both can see the table
	con2.Commit();

	REQUIRE_NOTHROW(con1.Table("integers"));
	REQUIRE_NOTHROW(con2.Table("integers"));
}

TEST_CASE("Test interaction of relations with schema changes", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con(db);
	duckdb::unique_ptr<QueryResult> result;

	// create some tables
	REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i INTEGER)"));
	REQUIRE_NO_FAIL(con.Query("INSERT INTO integers VALUES (1), (2), (3)"));

	// create a table scan of the integers table
	auto tbl_scan = con.Table("integers")->Project("i+1")->Order("1");
	REQUIRE_NOTHROW(result = tbl_scan->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {2, 3, 4}));

	// now drop the table
	REQUIRE_NO_FAIL(con.Query("DROP TABLE integers"));

	// the scan now fails, because the table is dropped!
	REQUIRE_FAIL(tbl_scan->Execute());

	// if we recreate the table, it works again
	REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i INTEGER)"));
	REQUIRE_NO_FAIL(con.Query("INSERT INTO integers VALUES (1), (2), (3)"));

	REQUIRE_NOTHROW(result = tbl_scan->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {2, 3, 4}));

	// but what if we recreate an incompatible table?
	REQUIRE_NO_FAIL(con.Query("DROP TABLE integers"));
	REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i VARCHAR, j INTEGER)"));
	REQUIRE_NO_FAIL(con.Query("INSERT INTO integers VALUES ('hello', 3)"));

	// this results in a binding error!
	REQUIRE_FAIL(tbl_scan->Execute());

	// now what if we run a query that still binds successfully, but changes result?
	auto tbl = con.Table("integers");
	REQUIRE_NO_FAIL(tbl->Execute());

	// add extra columns
	REQUIRE_NO_FAIL(con.Query("DROP TABLE integers"));
	REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i VARCHAR, j VARCHAR)"));
	REQUIRE_FAIL(tbl->Execute());

	// change type of column
	REQUIRE_NO_FAIL(con.Query("DROP TABLE integers"));
	REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i DATE, j INTEGER)"));
	REQUIRE_FAIL(tbl->Execute());

	// different name also results in an error
	REQUIRE_NO_FAIL(con.Query("DROP TABLE integers"));
	REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(k VARCHAR, j INTEGER)"));
	REQUIRE_FAIL(tbl->Execute());

	// but once we go back to the original table it works again!
	REQUIRE_NO_FAIL(con.Query("DROP TABLE integers"));
	REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i VARCHAR, j INTEGER)"));
	REQUIRE_NO_FAIL(tbl->Execute());
}

TEST_CASE("Test junk SQL in expressions", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con(db);
	duckdb::unique_ptr<QueryResult> result;

	REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i INTEGER)"));
	REQUIRE_NO_FAIL(con.Query("INSERT INTO integers VALUES (1), (2), (3)"));

	auto tbl = con.Table("integers");
	REQUIRE_THROWS(tbl->Filter("1=1; DELETE FROM tables;"));
	REQUIRE_THROWS(tbl->Filter("1=1 UNION SELECT 42"));
	REQUIRE_THROWS(tbl->Order("1 DESC; DELETE FROM tables;"));
	REQUIRE_THROWS(tbl->Update("i=1; DELETE FROM TABLES"));
	REQUIRE_THROWS(con.Values("(1, 1); SELECT 42"));
	REQUIRE_THROWS(con.Values("(1, 1) UNION SELECT 42"));
}

TEST_CASE("We cannot mix statements from multiple databases", "[relation_api]") {
	DuckDB db(nullptr), db2(nullptr);
	Connection con(db), con2(db2);
	duckdb::unique_ptr<QueryResult> result;

	// create some tables
	REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i INTEGER)"));
	REQUIRE_NO_FAIL(con.Query("INSERT INTO integers VALUES (1), (2), (3)"));
	REQUIRE_NO_FAIL(con2.Query("CREATE TABLE integers(i INTEGER)"));
	REQUIRE_NO_FAIL(con2.Query("INSERT INTO integers VALUES (4)"));

	auto i1 = con.Table("integers");
	auto i2 = con2.Table("integers");

	// we cannot mix statements from different connections without a wrapper!
	REQUIRE_THROWS(i2->Union(i1));
	REQUIRE_THROWS(i2->Except(i1));
	REQUIRE_THROWS(i2->Intersect(i1));
	REQUIRE_THROWS(i2->Join(i1, "i"));
	REQUIRE_THROWS(i2->Join(i1, "i1.i=i2.i"));

	// FIXME: what about a wrapper to scan data from other databases/connections?
}

TEST_CASE("Test view relations", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con(db);
	con.EnableQueryVerification();
	duckdb::unique_ptr<QueryResult> result;

	REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i INTEGER)"));
	REQUIRE_NO_FAIL(con.Query("INSERT INTO integers VALUES (1), (2), (3)"));
	REQUIRE_NO_FAIL(con.Query("CREATE VIEW v1 AS SELECT i+1 AS i, i+2 FROM integers"));
	REQUIRE_NO_FAIL(con.Query("CREATE VIEW v2(a,b) AS SELECT i+1, i+2 FROM integers"));

	auto i1 = con.View("v1");
	result = i1->Execute();
	REQUIRE(CHECK_COLUMN(result, 0, {2, 3, 4}));
	REQUIRE(CHECK_COLUMN(result, 1, {3, 4, 5}));

	result = con.View("v2")->Project("a+b")->Execute();
	REQUIRE(CHECK_COLUMN(result, 0, {5, 7, 9}));

	// non-existant view
	REQUIRE_THROWS(con.View("blabla"));

	// combining views
	result = con.View("v1")->Join(con.View("v2"), "v1.i=v2.a")->Execute();
	REQUIRE(CHECK_COLUMN(result, 0, {2, 3, 4}));
	REQUIRE(CHECK_COLUMN(result, 1, {3, 4, 5}));
	REQUIRE(CHECK_COLUMN(result, 2, {2, 3, 4}));
	REQUIRE(CHECK_COLUMN(result, 3, {3, 4, 5}));
}

TEST_CASE("Test table function relations", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con(db);
	con.EnableQueryVerification();
	duckdb::unique_ptr<QueryResult> result;

	REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i INTEGER)"));

	auto i1 = con.TableFunction("duckdb_tables");
	result = i1->Execute();
	REQUIRE(CHECK_COLUMN(result, 2, {"main"}));

	// function with parameters
	auto i2 = con.TableFunction("pragma_table_info", {"integers"});
	result = i2->Execute();
	REQUIRE(CHECK_COLUMN(result, 0, {0}));
	REQUIRE(CHECK_COLUMN(result, 1, {"i"}));
	REQUIRE(CHECK_COLUMN(result, 2, {"INTEGER"}));

	// we can do ops on table functions
	result = i2->Filter("cid=0")->Project("concat(name, ' ', type), length(name), reverse(lower(type))")->Execute();
	REQUIRE(CHECK_COLUMN(result, 0, {"i INTEGER"}));
	REQUIRE(CHECK_COLUMN(result, 1, {1}));
	REQUIRE(CHECK_COLUMN(result, 2, {"regetni"}));

	// table function that takes a relation as input
	auto values = con.Values("(42)", {"i"});
	auto summary = values->TableFunction("summary", duckdb::vector<Value> {});
	result = summary->Execute();
	REQUIRE(CHECK_COLUMN(result, 0, {"[42]"}));
	REQUIRE(CHECK_COLUMN(result, 1, {"42"}));

	// non-existant table function
	REQUIRE_THROWS(con.TableFunction("blabla"));
}

TEST_CASE("Test CSV Relation with union by name", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con(db);
	con.EnableQueryVerification();
	named_parameter_map_t options;
	options["union_by_name"] = Value(true);
	duckdb::vector<string> paths {"data/csv/sample-0.csv", "data/csv/sample-0.csv"};
	auto csv_scan = con.ReadCSV(paths, std::move(options));
	auto result = csv_scan->Execute();
	REQUIRE(!result->HasError());
	REQUIRE(CHECK_COLUMN(result, 0, {Value::DATE(2024, 1, 2), Value::DATE(2024, 1, 2), Value::DATE(2024, 1, 2)}));

	options["union_by_name"] = Value(true);
	paths = {"data/csv/union-by-name/ubn1.csv", "data/csv/union-by-name/ubn2.csv"};
	csv_scan = con.ReadCSV(paths, std::move(options));
	result = csv_scan->Execute();
	REQUIRE(!result->HasError());
}

TEST_CASE("Test CSV reading/writing from relations", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con(db);
	con.EnableQueryVerification();
	duckdb::unique_ptr<QueryResult> result;

	// write a bunch of values to a CSV
	auto csv_file = TestCreatePath("relationtest.csv");

	case_insensitive_map_t<duckdb::vector<Value>> options;
	options["header"] = {duckdb::Value(0)};
	con.Values("(1), (2), (3)", {"i"})->WriteCSV(csv_file, options);
	REQUIRE_THROWS(con.Values("(1), (2), (3)", {"i"})->WriteCSV("//fef//gw/g/bla/bla", options));

	// now scan the CSV file
	auto csv_scan = con.ReadCSV(csv_file, {"i INTEGER"});
	result = csv_scan->Execute();
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));

	// with auto detect
	auto auto_csv_scan = con.ReadCSV(csv_file);
	result = auto_csv_scan->Execute();
	REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));

	REQUIRE_THROWS(con.ReadCSV(csv_file, {"i INTEGER); SELECT 42;--"}));
	REQUIRE_THROWS(con.ReadCSV(csv_file, {"i INTEGER, j INTEGER"}));
}

TEST_CASE("Test CSV reading from weather.csv", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con(db);
	con.EnableQueryVerification();
	duckdb::unique_ptr<QueryResult> result;

	auto auto_csv_scan = con.ReadCSV("data/csv/weather.csv")->Limit(1);
	result = auto_csv_scan->Execute();
	REQUIRE(CHECK_COLUMN(result, 0, {"2016-01-01"}));
}

TEST_CASE("Test query relation", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con(db);
	con.EnableQueryVerification();
	duckdb::unique_ptr<QueryResult> result;
	duckdb::shared_ptr<Relation> tbl;

	// create some tables
	REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i INTEGER)"));
	REQUIRE_NO_FAIL(con.Query("INSERT INTO integers VALUES (1), (2), (3)"));

	REQUIRE_NOTHROW(tbl = con.RelationFromQuery("SELECT * FROM integers WHERE i >= 2"));
	REQUIRE_NOTHROW(result = tbl->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {2, 3}));
	REQUIRE_NOTHROW(result = tbl->Project("i + 1")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {3, 4}));

	REQUIRE_NOTHROW(result = tbl->Alias("q1")->Join(tbl->Alias("q2")->Filter("i=3"), "q1.i=q2.i")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {3}));

	REQUIRE_THROWS(tbl->Project("k+1"));
	// multiple queries
	REQUIRE_THROWS(con.RelationFromQuery("SELECT 42; SELECT 84"));
	// not a select statement
	REQUIRE_THROWS(con.RelationFromQuery("DELETE FROM tbl"));
}

TEST_CASE("Test TopK relation", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con(db);
	con.EnableQueryVerification();
	duckdb::unique_ptr<QueryResult> result;
	duckdb::shared_ptr<Relation> tbl;

	REQUIRE_NO_FAIL(con.Query("CREATE TABLE test (i integer,j VARCHAR, k varchar )"));
	REQUIRE_NO_FAIL(con.Query("insert into test values (10,'a','a'), (20,'a','b')"));

	REQUIRE_NOTHROW(tbl = con.Table("test"));
	REQUIRE_NOTHROW(tbl = tbl->Filter("k < 'f' and k is not null")
	                          ->Project("#3,#2,#1")
	                          ->Project("#2,#3")
	                          ->Order("(#2-10)::UTINYINT ASC")
	                          ->Limit(1));
}

TEST_CASE("Test Relation Pending Query API", "[relation_api]") {
	DuckDB db;
	Connection con(db);

	SECTION("Materialized result") {
		auto tbl = con.TableFunction("range", {Value(1000000)});
		auto aggr = tbl->Aggregate("SUM(range)");
		auto pending_query = con.context->PendingQuery(aggr, false);
		REQUIRE(!pending_query->HasError());
		auto result = pending_query->Execute();
		REQUIRE(CHECK_COLUMN(result, 0, {Value::BIGINT(499999500000)}));

		// cannot fetch twice from the same pending query
		REQUIRE_THROWS(pending_query->Execute());
		REQUIRE_THROWS(pending_query->Execute());

		// query the connection as normal after
		result = con.Query("SELECT 42");
		REQUIRE(CHECK_COLUMN(result, 0, {42}));
	}
	SECTION("Runtime error in pending query (materialized)") {
		auto tbl = con.TableFunction("range", {Value(1000000)});
		auto aggr = tbl->Aggregate("SUM(range) AS s")->Project("concat(s::varchar, 'hello')::int");
		// this succeeds initially
		auto pending_query = con.context->PendingQuery(aggr, false);
		REQUIRE(!pending_query->HasError());
		// we only encounter the failure later on as we are executing the query
		auto result = pending_query->Execute();
		REQUIRE_FAIL(result);

		// query the connection as normal after
		result = con.Query("SELECT 42");
		REQUIRE(CHECK_COLUMN(result, 0, {42}));
	}
}

TEST_CASE("Test Relation Query setting query", "[relation_api]") {
	DuckDB db;
	Connection con(db);

	auto query = con.RelationFromQuery("SELECT current_query()");
	auto result = query->Limit(1)->Execute();
	REQUIRE(!result->Fetch()->GetValue(0, 0).ToString().empty());
}

TEST_CASE("Construct ValueRelation with RelationContextWrapper and operate on it", "[relation_api][txn][wrapper]") {
	DuckDB db;
	Connection con(db);
	con.EnableQueryVerification();

	// Build expressions to force the "expressions" overload (not the constants path)
	duckdb::vector<duckdb::vector<duckdb::unique_ptr<duckdb::ParsedExpression>>> expressions;
	{
		duckdb::vector<duckdb::unique_ptr<duckdb::ParsedExpression>> row;

		{
			duckdb::ConstantExpression ce1(duckdb::Value::INTEGER(1));
			row.push_back(ce1.Copy());
		}
		{
			duckdb::ConstantExpression ce2(duckdb::Value::INTEGER(2));
			row.push_back(ce2.Copy());
		}
		expressions.push_back(std::move(row));
	}

	// Explicitly create a RelationContextWrapper from the client's context
	auto rcw = duckdb::make_shared_ptr<duckdb::RelationContextWrapper>(con.context);

	// Manually construct a ValueRelation using the RelationContextWrapper + expressions ctor
	duckdb::shared_ptr<duckdb::Relation> rel;
	REQUIRE_NOTHROW(rel = duckdb::make_shared_ptr<duckdb::ValueRelation>(rcw, std::move(expressions),
	                                                                     duckdb::vector<std::string> {}, "vr"));

	// Base relation should execute (1 row, 2 columns)
	duckdb::unique_ptr<QueryResult> result;
	REQUIRE_NOTHROW(result = rel->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1}));
	REQUIRE(CHECK_COLUMN(result, 1, {2}));

	// Project on top — should bind & execute under a valid transaction
	REQUIRE_NOTHROW(result = rel->Project("1+1, 2+2")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {2}));
	REQUIRE(CHECK_COLUMN(result, 1, {4}));

	// Aggregate on top — also must bind & execute cleanly
	REQUIRE_NOTHROW(result = rel->Aggregate("count(*)")->Execute());
	REQUIRE(CHECK_COLUMN(result, 0, {1}));
}

TEST_CASE("Test materialized relations", "[relation_api]") {
	auto db_path = TestCreatePath("relational_api_materialized_view.db");
	{
		DuckDB db(db_path);
		Connection con(db);
		con.EnableQueryVerification();

		REQUIRE_NO_FAIL(con.Query("create table tbl(a varchar);"));

		auto result = con.Query("insert into tbl values ('test') returning *");
		auto &materialized_result = result->Cast<MaterializedQueryResult>();
		auto materialized_relation = make_shared_ptr<MaterializedRelation>(
		    con.context, materialized_result.TakeCollection(), result->names, "vw");
		materialized_relation->CreateView("vw");
		materialized_relation.reset();

		result = con.Query("SELECT * FROM vw");
		REQUIRE(CHECK_COLUMN(result, 0, {"test"}));
	}
	// reset and query again
	{
		DuckDB db(db_path);
		Connection con(db);
		auto result = con.Query("SELECT * FROM vw");
		REQUIRE(CHECK_COLUMN(result, 0, {"test"}));
	}
}

TEST_CASE("Test create table with empty name", "[relation_api]") {
	DuckDB db(nullptr);
	Connection con(db);

	duckdb::unique_ptr<QueryResult> result = con.Query("CREATE TABLE '' AS SELECT 42;");
	REQUIRE_FAIL(result);

	auto values = con.Values("(42)");
	REQUIRE_THROWS_AS(values->Create(""), ParserException);
}
