From a5f04f540f7e87fdad0aa6afd329d9383e4a2d3b Mon Sep 17 00:00:00 2001 From: Alex-Monahan Date: Mon, 16 Sep 2024 07:56:54 -0700 Subject: [PATCH 1/3] Add SQL scalar and table macros and tests --- src/quack_extension.cpp | 61 +++++++++++++++++++++++++++++++++++++++++ test/sql/quack.test | 38 +++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/src/quack_extension.cpp b/src/quack_extension.cpp index 468b2e2..383ac62 100644 --- a/src/quack_extension.cpp +++ b/src/quack_extension.cpp @@ -7,12 +7,62 @@ #include "duckdb/function/scalar_function.hpp" #include "duckdb/main/extension_util.hpp" #include +#include "duckdb/catalog/default/default_functions.hpp" +#include "duckdb/catalog/default/default_table_functions.hpp" // OpenSSL linked through vcpkg #include namespace duckdb { + +// To add a new scalar SQL macro, add a new macro to this array! +// Copy and paste the top item in the array into the +// second-to-last position and make some modifications. +// (essentially, leave the last entry in the array as {nullptr, nullptr, {nullptr}, {{nullptr, nullptr}}, nullptr} ) + +// Keep the DEFAULT_SCHEMA (no change needed) +// Replace "times_two" with a name for your macro +// If your function has parameters without default values, add their names in quotes inside of the {}, with a nullptr at the end +// If you do not have parameters, simplify to {nullptr} +// If your function has parameters with default values, add their names and values in double quotes inside of {}'s inside of the {}. +// If you have default values that are strings, wrap them in single quotes inside the double quotes +// (Ex: {{"my_string":"'my_default_value'"}, {nullptr, nullptr}} ) +// Be sure to keep {nullptr, nullptr} at the end +// If you do not have parameters with default values, simplify to {nullptr, nullptr} +// Add the text of your SQL macro as a raw string with the format R"( select 42 )" +// clang-format off +static DefaultMacro quack_macros[] = { + {DEFAULT_SCHEMA, "times_two", {"x", nullptr}, {{nullptr, nullptr}}, R"( x * 2 )"}, + {DEFAULT_SCHEMA, "default_to_times_two", {"x", nullptr}, {{"multiplier", "2"}, {nullptr, nullptr}}, R"( x * multiplier )"}, + {nullptr, nullptr, {nullptr}, {{nullptr, nullptr}}, nullptr} +}; +// clang-format on + +// To add a new SQL table macro, add a new macro to this array! +// Copy and paste the top item in the array into the +// second-to-last position and make some modifications. +// (essentially, leave the last entry in the array as {nullptr, nullptr, {nullptr}, {{nullptr, nullptr}}, nullptr} + +// Keep the DEFAULT_SCHEMA (no change needed) +// Replace "times_two_table" with a name for your macro +// If your function has parameters without default values, add their names in quotes inside of the {}, with a nullptr at the end +// If you do not have parameters, simplify to {nullptr} +// If your function has parameters with default values, add their names and values in double quotes inside of {}'s inside of the {}. +// If you have default values that are strings, wrap them in single quotes inside the double quotes +// (Ex: {{"my_string":"'my_default_value'"}, {nullptr, nullptr}} ) +// Be sure to keep {nullptr, nullptr} at the end +// If you do not have parameters with default values, simplify to {nullptr, nullptr} +// Add the text of your SQL macro as a raw string with the format R"( select 42; )" + +// clang-format off +static const DefaultTableMacro quack_table_macros[] = { + {DEFAULT_SCHEMA, "times_two_table", {"x", nullptr}, {{nullptr, nullptr}}, R"( SELECT x * 2 as output_column; )"}, + {DEFAULT_SCHEMA, "default_to_times_two_table", {"x", nullptr}, {{"multiplier", "2"}, {nullptr, nullptr}}, R"( SELECT x * multiplier as output_column; )"}, + {nullptr, nullptr, {nullptr}, {{nullptr, nullptr}}, nullptr} +}; +// clang-format on + inline void QuackScalarFun(DataChunk &args, ExpressionState &state, Vector &result) { auto &name_vector = args.data[0]; UnaryExecutor::Execute( @@ -34,6 +84,17 @@ inline void QuackOpenSSLVersionScalarFun(DataChunk &args, ExpressionState &state } static void LoadInternal(DatabaseInstance &instance) { + // Register Scalar Macros + for (idx_t index = 0; quack_macros[index].name != nullptr; index++) { + auto info = DefaultFunctionGenerator::CreateInternalMacroInfo(quack_macros[index]); + ExtensionUtil::RegisterFunction(instance, *info); + } + // Register Table Macros + for (idx_t index = 0; quack_table_macros[index].name != nullptr; index++) { + auto table_info = DefaultTableFunctionGenerator::CreateTableMacroInfo(quack_table_macros[index]); + ExtensionUtil::RegisterFunction(instance, *table_info); + } + // Register a scalar function auto quack_scalar_function = ScalarFunction("quack", {LogicalType::VARCHAR}, LogicalType::VARCHAR, QuackScalarFun); ExtensionUtil::RegisterFunction(instance, quack_scalar_function); diff --git a/test/sql/quack.test b/test/sql/quack.test index 519a354..2573269 100644 --- a/test/sql/quack.test +++ b/test/sql/quack.test @@ -12,11 +12,49 @@ Catalog Error: Scalar Function with name quack does not exist! require quack # Confirm the extension works +# Test scalar macro +query I +SELECT times_two(4); +---- +8 + +# Test table macro +query I +SELECT * FROM times_two_table(5); +---- +10 + +# Test scalar macro using a default +query I +SELECT default_to_times_two(4); +---- +8 + +# Test scalar macro overriding the default +query I +SELECT default_to_times_two(4, multiplier:=3); +---- +12 + +# Test table macro using a default +query I +SELECT * FROM default_to_times_two_table(5); +---- +10 + +# Test table macro overriding the default +query I +SELECT * FROM default_to_times_two_table(5, multiplier:=3); +---- +15 + +# Test scalar C++ function query I SELECT quack('Sam'); ---- Quack Sam 🐥 +# Test scalar C++ function using vcpkg and openssl query I SELECT quack_openssl_version('Michael') ILIKE 'Quack Michael, my linked OpenSSL version is OpenSSL%'; ---- From f21173a1fcd9435039f9f10edf84e3cd49dddc69 Mon Sep 17 00:00:00 2001 From: Alex-Monahan Date: Fri, 20 Sep 2024 07:13:58 -0700 Subject: [PATCH 2/3] Trim SQL Macro examples (remove comments and some tests, move to bottom) --- src/quack_extension.cpp | 81 ++++++++++++----------------------------- test/sql/quack.test | 37 ++++--------------- 2 files changed, 31 insertions(+), 87 deletions(-) diff --git a/src/quack_extension.cpp b/src/quack_extension.cpp index 383ac62..ac7e853 100644 --- a/src/quack_extension.cpp +++ b/src/quack_extension.cpp @@ -15,54 +15,6 @@ namespace duckdb { - -// To add a new scalar SQL macro, add a new macro to this array! -// Copy and paste the top item in the array into the -// second-to-last position and make some modifications. -// (essentially, leave the last entry in the array as {nullptr, nullptr, {nullptr}, {{nullptr, nullptr}}, nullptr} ) - -// Keep the DEFAULT_SCHEMA (no change needed) -// Replace "times_two" with a name for your macro -// If your function has parameters without default values, add their names in quotes inside of the {}, with a nullptr at the end -// If you do not have parameters, simplify to {nullptr} -// If your function has parameters with default values, add their names and values in double quotes inside of {}'s inside of the {}. -// If you have default values that are strings, wrap them in single quotes inside the double quotes -// (Ex: {{"my_string":"'my_default_value'"}, {nullptr, nullptr}} ) -// Be sure to keep {nullptr, nullptr} at the end -// If you do not have parameters with default values, simplify to {nullptr, nullptr} -// Add the text of your SQL macro as a raw string with the format R"( select 42 )" -// clang-format off -static DefaultMacro quack_macros[] = { - {DEFAULT_SCHEMA, "times_two", {"x", nullptr}, {{nullptr, nullptr}}, R"( x * 2 )"}, - {DEFAULT_SCHEMA, "default_to_times_two", {"x", nullptr}, {{"multiplier", "2"}, {nullptr, nullptr}}, R"( x * multiplier )"}, - {nullptr, nullptr, {nullptr}, {{nullptr, nullptr}}, nullptr} -}; -// clang-format on - -// To add a new SQL table macro, add a new macro to this array! -// Copy and paste the top item in the array into the -// second-to-last position and make some modifications. -// (essentially, leave the last entry in the array as {nullptr, nullptr, {nullptr}, {{nullptr, nullptr}}, nullptr} - -// Keep the DEFAULT_SCHEMA (no change needed) -// Replace "times_two_table" with a name for your macro -// If your function has parameters without default values, add their names in quotes inside of the {}, with a nullptr at the end -// If you do not have parameters, simplify to {nullptr} -// If your function has parameters with default values, add their names and values in double quotes inside of {}'s inside of the {}. -// If you have default values that are strings, wrap them in single quotes inside the double quotes -// (Ex: {{"my_string":"'my_default_value'"}, {nullptr, nullptr}} ) -// Be sure to keep {nullptr, nullptr} at the end -// If you do not have parameters with default values, simplify to {nullptr, nullptr} -// Add the text of your SQL macro as a raw string with the format R"( select 42; )" - -// clang-format off -static const DefaultTableMacro quack_table_macros[] = { - {DEFAULT_SCHEMA, "times_two_table", {"x", nullptr}, {{nullptr, nullptr}}, R"( SELECT x * 2 as output_column; )"}, - {DEFAULT_SCHEMA, "default_to_times_two_table", {"x", nullptr}, {{"multiplier", "2"}, {nullptr, nullptr}}, R"( SELECT x * multiplier as output_column; )"}, - {nullptr, nullptr, {nullptr}, {{nullptr, nullptr}}, nullptr} -}; -// clang-format on - inline void QuackScalarFun(DataChunk &args, ExpressionState &state, Vector &result) { auto &name_vector = args.data[0]; UnaryExecutor::Execute( @@ -83,7 +35,31 @@ inline void QuackOpenSSLVersionScalarFun(DataChunk &args, ExpressionState &state }); } +// clang-format off +// SQL Macros +static DefaultMacro quack_macros[] = { + {DEFAULT_SCHEMA, "default_to_times_two", {"x", nullptr}, {{"multiplier", "2"}, {nullptr, nullptr}}, R"( x * multiplier )"}, + {nullptr, nullptr, {nullptr}, {{nullptr, nullptr}}, nullptr} +}; + +// SQL Table Macros +static const DefaultTableMacro quack_table_macros[] = { + {DEFAULT_SCHEMA, "default_to_times_two_table", {"x", nullptr}, {{"multiplier", "2"}, {nullptr, nullptr}}, R"( SELECT x * multiplier as output_column; )"}, + {nullptr, nullptr, {nullptr}, {{nullptr, nullptr}}, nullptr} +}; +// clang-format on + static void LoadInternal(DatabaseInstance &instance) { + + // Register a scalar function + auto quack_scalar_function = ScalarFunction("quack", {LogicalType::VARCHAR}, LogicalType::VARCHAR, QuackScalarFun); + ExtensionUtil::RegisterFunction(instance, quack_scalar_function); + + // Register another scalar function + auto quack_openssl_version_scalar_function = ScalarFunction("quack_openssl_version", {LogicalType::VARCHAR}, + LogicalType::VARCHAR, QuackOpenSSLVersionScalarFun); + ExtensionUtil::RegisterFunction(instance, quack_openssl_version_scalar_function); + // Register Scalar Macros for (idx_t index = 0; quack_macros[index].name != nullptr; index++) { auto info = DefaultFunctionGenerator::CreateInternalMacroInfo(quack_macros[index]); @@ -94,15 +70,6 @@ static void LoadInternal(DatabaseInstance &instance) { auto table_info = DefaultTableFunctionGenerator::CreateTableMacroInfo(quack_table_macros[index]); ExtensionUtil::RegisterFunction(instance, *table_info); } - - // Register a scalar function - auto quack_scalar_function = ScalarFunction("quack", {LogicalType::VARCHAR}, LogicalType::VARCHAR, QuackScalarFun); - ExtensionUtil::RegisterFunction(instance, quack_scalar_function); - - // Register another scalar function - auto quack_openssl_version_scalar_function = ScalarFunction("quack_openssl_version", {LogicalType::VARCHAR}, - LogicalType::VARCHAR, QuackOpenSSLVersionScalarFun); - ExtensionUtil::RegisterFunction(instance, quack_openssl_version_scalar_function); } void QuackExtension::Load(DuckDB &db) { diff --git a/test/sql/quack.test b/test/sql/quack.test index 2573269..b88c533 100644 --- a/test/sql/quack.test +++ b/test/sql/quack.test @@ -12,23 +12,18 @@ Catalog Error: Scalar Function with name quack does not exist! require quack # Confirm the extension works -# Test scalar macro -query I -SELECT times_two(4); ----- -8 -# Test table macro +# Test scalar C++ function query I -SELECT * FROM times_two_table(5); +SELECT quack('Sam'); ---- -10 +Quack Sam 🐥 -# Test scalar macro using a default +# Test scalar C++ function using vcpkg and openssl query I -SELECT default_to_times_two(4); +SELECT quack_openssl_version('Michael') ILIKE 'Quack Michael, my linked OpenSSL version is OpenSSL%'; ---- -8 +true # Test scalar macro overriding the default query I @@ -36,26 +31,8 @@ SELECT default_to_times_two(4, multiplier:=3); ---- 12 -# Test table macro using a default -query I -SELECT * FROM default_to_times_two_table(5); ----- -10 - # Test table macro overriding the default query I SELECT * FROM default_to_times_two_table(5, multiplier:=3); ---- -15 - -# Test scalar C++ function -query I -SELECT quack('Sam'); ----- -Quack Sam 🐥 - -# Test scalar C++ function using vcpkg and openssl -query I -SELECT quack_openssl_version('Michael') ILIKE 'Quack Michael, my linked OpenSSL version is OpenSSL%'; ----- -true +15 \ No newline at end of file From 13a5f759852313b0ee717ebbc5ced7b61292f7a4 Mon Sep 17 00:00:00 2001 From: Alex-Monahan Date: Fri, 20 Sep 2024 07:18:52 -0700 Subject: [PATCH 3/3] Edit comments to be consistent --- src/quack_extension.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/quack_extension.cpp b/src/quack_extension.cpp index ac7e853..adf6bbd 100644 --- a/src/quack_extension.cpp +++ b/src/quack_extension.cpp @@ -36,13 +36,13 @@ inline void QuackOpenSSLVersionScalarFun(DataChunk &args, ExpressionState &state } // clang-format off -// SQL Macros +// SQL scalar macros static DefaultMacro quack_macros[] = { {DEFAULT_SCHEMA, "default_to_times_two", {"x", nullptr}, {{"multiplier", "2"}, {nullptr, nullptr}}, R"( x * multiplier )"}, {nullptr, nullptr, {nullptr}, {{nullptr, nullptr}}, nullptr} }; -// SQL Table Macros +// SQL table macros static const DefaultTableMacro quack_table_macros[] = { {DEFAULT_SCHEMA, "default_to_times_two_table", {"x", nullptr}, {{"multiplier", "2"}, {nullptr, nullptr}}, R"( SELECT x * multiplier as output_column; )"}, {nullptr, nullptr, {nullptr}, {{nullptr, nullptr}}, nullptr} @@ -60,12 +60,12 @@ static void LoadInternal(DatabaseInstance &instance) { LogicalType::VARCHAR, QuackOpenSSLVersionScalarFun); ExtensionUtil::RegisterFunction(instance, quack_openssl_version_scalar_function); - // Register Scalar Macros + // Register SQL scalar macros for (idx_t index = 0; quack_macros[index].name != nullptr; index++) { auto info = DefaultFunctionGenerator::CreateInternalMacroInfo(quack_macros[index]); ExtensionUtil::RegisterFunction(instance, *info); } - // Register Table Macros + // Register SQL table macros for (idx_t index = 0; quack_table_macros[index].name != nullptr; index++) { auto table_info = DefaultTableFunctionGenerator::CreateTableMacroInfo(quack_table_macros[index]); ExtensionUtil::RegisterFunction(instance, *table_info);