diff --git a/.github/workflows/MainDistributionPipeline.yml b/.github/workflows/MainDistributionPipeline.yml index 97e5ac8..2a95f31 100644 --- a/.github/workflows/MainDistributionPipeline.yml +++ b/.github/workflows/MainDistributionPipeline.yml @@ -3,7 +3,8 @@ # name: Main Extension Distribution Pipeline on: - push: + release: + types: [published] pull_request: workflow_dispatch: @@ -17,7 +18,7 @@ jobs: uses: duckdb/extension-ci-tools/.github/workflows/_extension_distribution.yml@main with: duckdb_version: v1.0.0 - extension_name: dynamic_sql_clickhouse + extension_name: chsql duckdb-stable-deploy: name: Deploy extension binaries @@ -26,6 +27,6 @@ jobs: secrets: inherit with: duckdb_version: v1.0.0 - extension_name: dynamic_sql_clickhouse + extension_name: chsql deploy_latest: ${{ startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' }} deploy_versioned: ${{ startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b4a41d..26ca360 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.5) # Set extension name here -set(TARGET_NAME dynamic_sql_clickhouse) +set(TARGET_NAME chsql) # DuckDB's extension distribution supports vcpkg. As such, dependencies can be added in ./vcpkg.json and then # used in cmake with find_package. Feel free to remove or replace with other dependencies. @@ -14,7 +14,7 @@ set(LOADABLE_EXTENSION_NAME ${TARGET_NAME}_loadable_extension) project(${TARGET_NAME}) include_directories(src/include) -set(EXTENSION_SOURCES src/dynamic_sql_clickhouse_extension.cpp src/default_table_functions.cpp) +set(EXTENSION_SOURCES src/chsql_extension.cpp src/default_table_functions.cpp) build_static_extension(${TARGET_NAME} ${EXTENSION_SOURCES}) build_loadable_extension(${TARGET_NAME} " " ${EXTENSION_SOURCES}) diff --git a/Makefile b/Makefile index fccfcf0..d1b6c95 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ PROJ_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) # Configuration of extension -EXT_NAME=dynamic_sql_clickhouse +EXT_NAME=chsql EXT_CONFIG=${PROJ_DIR}extension_config.cmake # Include the Makefile from extension-ci-tools diff --git a/aliases.sql b/aliases.sql new file mode 100644 index 0000000..d07d178 --- /dev/null +++ b/aliases.sql @@ -0,0 +1,47 @@ +-- Type conversion macros +CREATE OR REPLACE MACRO toString(expr) AS CAST(expr AS VARCHAR); +CREATE OR REPLACE MACRO toInt8(expr) AS CAST(expr AS INT8); +CREATE OR REPLACE MACRO toInt16(expr) AS CAST(expr AS INT16); +CREATE OR REPLACE MACRO toInt32(expr) AS CAST(expr AS INT32); +CREATE OR REPLACE MACRO toInt64(expr) AS CAST(expr AS INT64); +CREATE OR REPLACE MACRO toInt128(expr) AS CAST(expr AS INT128); +CREATE OR REPLACE MACRO toInt256(expr) AS CAST(expr AS HUGEINT); +-- Type conversion with default values +CREATE OR REPLACE MACRO toInt8OrZero(expr) AS COALESCE(TRY_CAST(expr AS INT8), 0); +CREATE OR REPLACE MACRO toInt16OrZero(expr) AS COALESCE(TRY_CAST(expr AS INT16), 0); +CREATE OR REPLACE MACRO toInt32OrZero(expr) AS COALESCE(TRY_CAST(expr AS INT32), 0); +CREATE OR REPLACE MACRO toInt64OrZero(expr) AS COALESCE(TRY_CAST(expr AS INT64), 0); +CREATE OR REPLACE MACRO toInt128OrZero(expr) AS COALESCE(TRY_CAST(expr AS INT128), 0); +CREATE OR REPLACE MACRO toInt256OrZero(expr) AS COALESCE(TRY_CAST(expr AS HUGEINT), 0); +CREATE OR REPLACE MACRO toInt8OrNull(expr) AS TRY_CAST(expr AS INT8); +CREATE OR REPLACE MACRO toInt16OrNull(expr) AS TRY_CAST(expr AS INT16); +CREATE OR REPLACE MACRO toInt32OrNull(expr) AS TRY_CAST(expr AS INT32); +CREATE OR REPLACE MACRO toInt64OrNull(expr) AS TRY_CAST(expr AS INT64); +CREATE OR REPLACE MACRO toInt128OrNull(expr) AS TRY_CAST(expr AS INT128); +CREATE OR REPLACE MACRO toInt256OrNull(expr) AS TRY_CAST(expr AS HUGEINT); +-- Unsigned integer conversion macros +CREATE OR REPLACE MACRO toUInt8(expr) AS CAST(expr AS UTINYINT); +CREATE OR REPLACE MACRO toUInt16(expr) AS CAST(expr AS USMALLINT); +CREATE OR REPLACE MACRO toUInt32(expr) AS CAST(expr AS UINTEGER); +CREATE OR REPLACE MACRO toUInt64(expr) AS CAST(expr AS UBIGINT); +-- Unsigned integer conversion with default values +CREATE OR REPLACE MACRO toUInt8OrZero(expr) AS COALESCE(TRY_CAST(expr AS UTINYINT), 0); +CREATE OR REPLACE MACRO toUInt16OrZero(expr) AS COALESCE(TRY_CAST(expr AS USMALLINT), 0); +CREATE OR REPLACE MACRO toUInt32OrZero(expr) AS COALESCE(TRY_CAST(expr AS UINTEGER), 0); +CREATE OR REPLACE MACRO toUInt64OrZero(expr) AS COALESCE(TRY_CAST(expr AS UBIGINT), 0); +CREATE OR REPLACE MACRO toUInt8OrNull(expr) AS TRY_CAST(expr AS UTINYINT); +CREATE OR REPLACE MACRO toUInt16OrNull(expr) AS TRY_CAST(expr AS USMALLINT); +CREATE OR REPLACE MACRO toUInt32OrNull(expr) AS TRY_CAST(expr AS UINTEGER); +CREATE OR REPLACE MACRO toUInt64OrNull(expr) AS TRY_CAST(expr AS UBIGINT); +-- Floating-point conversion macros +CREATE OR REPLACE MACRO toFloat(expr) AS CAST(expr AS DOUBLE); +CREATE OR REPLACE MACRO toFloatOrNull(expr) AS TRY_CAST(expr AS DOUBLE); +CREATE OR REPLACE MACRO toFloatOrZero(expr) AS COALESCE(TRY_CAST(expr AS DOUBLE), 0); +-- Arithmetic macros +CREATE OR REPLACE MACRO intDiv(a, b) AS (a / b); +CREATE OR REPLACE MACRO intDivOrZero(a, b) AS COALESCE((a / b), 0); +-- String matching macro +CREATE OR REPLACE MACRO match(string, token) AS (string LIKE token); +-- Array macros +CREATE OR REPLACE MACRO arrayExists(needle, haystack) AS (haystack @> ARRAY[needle]); +CREATE OR REPLACE MACRO arrayMap(e, arr) AS (array_transform(arr, e -> (e * e))); diff --git a/description.yml b/description.yml new file mode 100644 index 0000000..c726a5a --- /dev/null +++ b/description.yml @@ -0,0 +1,20 @@ +extension: + name: chsql + description: Clickhouse SQL Macros for DuckDB + version: 1.0.0 + language: C++ + build: cmake + license: MIT + maintainers: + - lmangani + +repo: + github: lmangani/duckdb-extension-clickhouse-sql + ref: e3024a76df741864696010130f72c79cbad2274d + +docs: + hello_world: | + SELECT toString('world') as hello; + extended_description: | + This extension provides a growing number of Clickhouse SQL Macros for DuckDB. + diff --git a/extension_config.cmake b/extension_config.cmake index 3319545..85d494c 100644 --- a/extension_config.cmake +++ b/extension_config.cmake @@ -1,7 +1,7 @@ # This file is included by DuckDB's build system. It specifies which extension to load # Extension from this repo -duckdb_extension_load(dynamic_sql_clickhouse +duckdb_extension_load(chsql SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR} LOAD_TESTS ) diff --git a/src/dynamic_sql_clickhouse_extension.cpp b/src/chsql_extension.cpp similarity index 77% rename from src/dynamic_sql_clickhouse_extension.cpp rename to src/chsql_extension.cpp index 37707ad..e310942 100644 --- a/src/dynamic_sql_clickhouse_extension.cpp +++ b/src/chsql_extension.cpp @@ -1,6 +1,6 @@ #define DUCKDB_EXTENSION_MAIN -#include "dynamic_sql_clickhouse_extension.hpp" +#include "chsql_extension.hpp" #include "duckdb.hpp" #include "duckdb/common/exception.hpp" #include "duckdb/common/string_util.hpp" @@ -28,8 +28,9 @@ namespace duckdb { // Add the text of your SQL macro as a raw string with the format R"( select 42 )" -static DefaultMacro dynamic_sql_clickhouse_macros[] = { +static DefaultMacro chsql_macros[] = { {DEFAULT_SCHEMA, "times_two", {"x", nullptr}, R"(x*2)"}, + // -- Type conversion macros {DEFAULT_SCHEMA, "toString", {"x", nullptr}, R"(CAST(x AS VARCHAR))"}, {DEFAULT_SCHEMA, "toInt8", {"x", nullptr}, R"(CAST(x AS INT8))"}, {DEFAULT_SCHEMA, "toInt16", {"x", nullptr}, R"(CAST(x AS INT16))"}, @@ -49,6 +50,7 @@ static DefaultMacro dynamic_sql_clickhouse_macros[] = { {DEFAULT_SCHEMA, "toInt64OrNull", {"x", nullptr}, R"(TRY_CAST(x AS INT64))"}, {DEFAULT_SCHEMA, "toInt128OrNull", {"x", nullptr}, R"(TRY_CAST(x AS INT128))"}, {DEFAULT_SCHEMA, "toInt256OrNull", {"x", nullptr}, R"(TRY_CAST(x AS HUGEINT))"}, + // -- Unsigned integer conversion macros {DEFAULT_SCHEMA, "toUInt8", {"x", nullptr}, R"(CAST(x AS UTINYINT))"}, {DEFAULT_SCHEMA, "toUInt16", {"x", nullptr}, R"(CAST(x AS USMALLINT))"}, {DEFAULT_SCHEMA, "toUInt32", {"x", nullptr}, R"(CAST(x AS UINTEGER))"}, @@ -61,16 +63,22 @@ static DefaultMacro dynamic_sql_clickhouse_macros[] = { {DEFAULT_SCHEMA, "toUInt16OrNull", {"x", nullptr}, R"(TRY_CAST(x AS USMALLINT))"}, // And here {DEFAULT_SCHEMA, "toUInt32OrNull", {"x", nullptr}, R"(TRY_CAST(x AS UINTEGER))"}, // Also here {DEFAULT_SCHEMA, "toUInt64OrNull", {"x", nullptr}, R"(TRY_CAST(x AS UBIGINT))"}, // And here + // -- Floating-point conversion macros {DEFAULT_SCHEMA, "toFloat", {"x", nullptr}, R"(CAST(x AS DOUBLE))"}, {DEFAULT_SCHEMA, "toFloatOrNull", {"x", nullptr}, R"(TRY_CAST(x AS DOUBLE))"}, {DEFAULT_SCHEMA, "toFloatOrZero", {"x", nullptr}, R"(CASE WHEN TRY_CAST(x AS DOUBLE) IS NOT NULL THEN CAST(x AS DOUBLE) ELSE 0 END)"}, + // -- Arithmetic macros {DEFAULT_SCHEMA, "intDiv", {"a", "b"}, R"((CAST(a AS BIGINT) / CAST(b AS BIGINT)))"}, + // -- String matching macros {DEFAULT_SCHEMA, "match", {"string", "token"}, R"(string LIKE token)"}, + // -- Array macros + {DEFAULT_SCHEMA, "arrayExists", {"needle", "haystack"}, R"(haystack @> ARRAY[needle])"}, + {DEFAULT_SCHEMA, "arrayMap", {"e", "arr"}, R"(array_transform(arr, e -> (e * e)))"}, {nullptr, nullptr, {nullptr}, nullptr}}; // To add a new table 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. +// 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}) // Keep the DEFAULT_SCHEMA (no change needed) @@ -80,30 +88,30 @@ static DefaultMacro dynamic_sql_clickhouse_macros[] = { // If your function has parameters with default values, add their names and values in quotes inside of {}'s inside of the {}. // 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; )" +// Add the text of your SQL macro as a raw string with the format R"( select 42; )" // clang-format off -static const DefaultTableMacro dynamic_sql_clickhouse_table_macros[] = { +static const DefaultTableMacro chsql_table_macros[] = { {DEFAULT_SCHEMA, "times_two_table", {"x", nullptr}, {{"two", "2"}, {nullptr, nullptr}}, R"(SELECT x * two as output_column;)"}, {nullptr, nullptr, {nullptr}, {{nullptr, nullptr}}, nullptr} }; // clang-format on -inline void DynamicSqlClickhouseScalarFun(DataChunk &args, ExpressionState &state, Vector &result) { +inline void ChSqlScalarFun(DataChunk &args, ExpressionState &state, Vector &result) { auto &name_vector = args.data[0]; UnaryExecutor::Execute( name_vector, result, args.size(), [&](string_t name) { - return StringVector::AddString(result, "DynamicSqlClickhouse "+name.GetString()+" 🐥");; + return StringVector::AddString(result, "ChSql "+name.GetString()+" 🐥");; }); } -inline void DynamicSqlClickhouseOpenSSLVersionScalarFun(DataChunk &args, ExpressionState &state, Vector &result) { +inline void ChSqlOpenSSLVersionScalarFun(DataChunk &args, ExpressionState &state, Vector &result) { auto &name_vector = args.data[0]; UnaryExecutor::Execute( name_vector, result, args.size(), [&](string_t name) { - return StringVector::AddString(result, "DynamicSqlClickhouse " + name.GetString() + + return StringVector::AddString(result, "ChSql " + name.GetString() + ", my linked OpenSSL version is " + OPENSSL_VERSION_TEXT );; }); @@ -111,36 +119,36 @@ inline void DynamicSqlClickhouseOpenSSLVersionScalarFun(DataChunk &args, Express static void LoadInternal(DatabaseInstance &instance) { // Register a scalar function - auto dynamic_sql_clickhouse_scalar_function = ScalarFunction("dynamic_sql_clickhouse", {LogicalType::VARCHAR}, LogicalType::VARCHAR, DynamicSqlClickhouseScalarFun); - ExtensionUtil::RegisterFunction(instance, dynamic_sql_clickhouse_scalar_function); + auto chsql_scalar_function = ScalarFunction("chsql", {LogicalType::VARCHAR}, LogicalType::VARCHAR, ChSqlScalarFun); + ExtensionUtil::RegisterFunction(instance, chsql_scalar_function); // Register another scalar function - auto dynamic_sql_clickhouse_openssl_version_scalar_function = ScalarFunction("dynamic_sql_clickhouse_openssl_version", {LogicalType::VARCHAR}, - LogicalType::VARCHAR, DynamicSqlClickhouseOpenSSLVersionScalarFun); - ExtensionUtil::RegisterFunction(instance, dynamic_sql_clickhouse_openssl_version_scalar_function); + auto chsql_openssl_version_scalar_function = ScalarFunction("chsql_openssl_version", {LogicalType::VARCHAR}, + LogicalType::VARCHAR, ChSqlOpenSSLVersionScalarFun); + ExtensionUtil::RegisterFunction(instance, chsql_openssl_version_scalar_function); // Macros - for (idx_t index = 0; dynamic_sql_clickhouse_macros[index].name != nullptr; index++) { - auto info = DefaultFunctionGenerator::CreateInternalMacroInfo(dynamic_sql_clickhouse_macros[index]); + for (idx_t index = 0; chsql_macros[index].name != nullptr; index++) { + auto info = DefaultFunctionGenerator::CreateInternalMacroInfo(chsql_macros[index]); ExtensionUtil::RegisterFunction(instance, *info); } // Table Macros - for (idx_t index = 0; dynamic_sql_clickhouse_table_macros[index].name != nullptr; index++) { - auto table_info = DefaultTableFunctionGenerator::CreateTableMacroInfo(dynamic_sql_clickhouse_table_macros[index]); + for (idx_t index = 0; chsql_table_macros[index].name != nullptr; index++) { + auto table_info = DefaultTableFunctionGenerator::CreateTableMacroInfo(chsql_table_macros[index]); ExtensionUtil::RegisterFunction(instance, *table_info); } } -void DynamicSqlClickhouseExtension::Load(DuckDB &db) { +void ChsqlExtension::Load(DuckDB &db) { LoadInternal(*db.instance); } -std::string DynamicSqlClickhouseExtension::Name() { - return "dynamic_sql_clickhouse"; +std::string ChsqlExtension::Name() { + return "chsql"; } -std::string DynamicSqlClickhouseExtension::Version() const { -#ifdef EXT_VERSION_DYNAMIC_SQL_CLICKHOUSE - return EXT_VERSION_DYNAMIC_SQL_CLICKHOUSE; +std::string ChsqlExtension::Version() const { +#ifdef EXT_VERSION_CHSQL + return EXT_VERSION_CHSQL; #else return ""; #endif @@ -150,12 +158,12 @@ std::string DynamicSqlClickhouseExtension::Version() const { extern "C" { -DUCKDB_EXTENSION_API void dynamic_sql_clickhouse_init(duckdb::DatabaseInstance &db) { +DUCKDB_EXTENSION_API void chsql_init(duckdb::DatabaseInstance &db) { duckdb::DuckDB db_wrapper(db); - db_wrapper.LoadExtension(); + db_wrapper.LoadExtension(); } -DUCKDB_EXTENSION_API const char *dynamic_sql_clickhouse_version() { +DUCKDB_EXTENSION_API const char *chsql_version() { return duckdb::DuckDB::LibraryVersion(); } } diff --git a/src/include/dynamic_sql_clickhouse_extension.hpp b/src/include/chsql_extension.hpp similarity index 77% rename from src/include/dynamic_sql_clickhouse_extension.hpp rename to src/include/chsql_extension.hpp index dc0d8db..57abf17 100644 --- a/src/include/dynamic_sql_clickhouse_extension.hpp +++ b/src/include/chsql_extension.hpp @@ -4,7 +4,7 @@ namespace duckdb { -class DynamicSqlClickhouseExtension : public Extension { +class ChsqlExtension : public Extension { public: void Load(DuckDB &db) override; std::string Name() override; diff --git a/test/sql/dynamic_sql_clickhouse.test b/test/sql/chsql.test similarity index 78% rename from test/sql/dynamic_sql_clickhouse.test rename to test/sql/chsql.test index cc20d57..80c80fd 100644 --- a/test/sql/dynamic_sql_clickhouse.test +++ b/test/sql/chsql.test @@ -1,26 +1,26 @@ -# name: test/sql/dynamic_sql_clickhouse.test -# description: test dynamic_sql_clickhouse extension -# group: [dynamic_sql_clickhouse] +# name: test/sql/chsql.test +# description: test chsql extension +# group: [chsql] # Before we load the extension, this will fail statement error -SELECT dynamic_sql_clickhouse('Sam'); +SELECT chsql('Sam'); ---- -Catalog Error: Scalar Function with name dynamic_sql_clickhouse does not exist! +Catalog Error: Scalar Function with name chsql does not exist! # Require statement will ensure this test is run with this extension loaded -require dynamic_sql_clickhouse +require chsql # Confirm the extension works query I -SELECT dynamic_sql_clickhouse('Works'); +SELECT chsql('Works'); ---- -DynamicSqlClickhouse Works 🐥 +ChSql Works 🐥 query I -SELECT dynamic_sql_clickhouse_openssl_version('Hello'); +SELECT chsql_openssl_version('Hello'); ---- -:.*DynamicSqlClickhouse Hello, my linked OpenSSL version is OpenSSL.* +:.*ChSql Hello, my linked OpenSSL version is OpenSSL.* query I SELECT toString('123')