diff --git a/.github/workflows/ExtensionTemplate.yml b/.github/workflows/ExtensionTemplate.yml deleted file mode 100644 index 42265dd..0000000 --- a/.github/workflows/ExtensionTemplate.yml +++ /dev/null @@ -1,177 +0,0 @@ -# -# NOTE: this workflow is for testing the extension template itself, -# this workflow will be removed when scripts/bootstrap-template.py is run -# -name: Extension Template -on: [push, pull_request,repository_dispatch] -concurrency: - group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref || '' }}-${{ github.base_ref || '' }}-${{ github.ref != 'refs/heads/main' || github.sha }} - cancel-in-progress: true - -jobs: - linux: - name: Linux - if: ${{ vars.RUN_RENAME_TEST == 'true' }} - runs-on: ubuntu-latest - container: ubuntu:18.04 - strategy: - matrix: - # Add commits/tags to build against other DuckDB versions - duckdb_version: [ '' ] - env: - VCPKG_TOOLCHAIN_PATH: ${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake - VCPKG_TARGET_TRIPLET: 'x64-linux' - GEN: ninja - defaults: - run: - shell: bash - - steps: - - name: Install required ubuntu packages - run: | - apt-get update -y -qq - apt-get install -y -qq software-properties-common - add-apt-repository ppa:git-core/ppa - apt-get update -y -qq - apt-get install -y -qq ninja-build make gcc-multilib g++-multilib libssl-dev wget openjdk-8-jdk zip maven unixodbc-dev libc6-dev-i386 lib32readline6-dev libssl-dev libcurl4-gnutls-dev libexpat1-dev gettext unzip build-essential checkinstall libffi-dev curl libz-dev openssh-client - - - name: Install Git 2.18.5 - run: | - wget https://github.com/git/git/archive/refs/tags/v2.18.5.tar.gz - tar xvf v2.18.5.tar.gz - cd git-2.18.5 - make - make prefix=/usr install - git --version - - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - submodules: 'true' - - - name: Checkout DuckDB to version - if: ${{ matrix.duckdb_version != ''}} - run: | - cd duckdb - git checkout ${{ matrix.duckdb_version }} - - - uses: ./duckdb/.github/actions/ubuntu_18_setup - - - name: Setup vcpkg - uses: lukka/run-vcpkg@v11.1 - with: - vcpkgGitCommitId: a42af01b72c28a8e1d7b48107b33e4f286a55ef6 - - - name: Rename extension - run: | - python3 scripts/bootstrap-template.py ext_1_a_123b_b11 - - - name: Build - run: | - make - - - name: Test - run: | - make test - - macos: - name: MacOS - if: ${{ vars.RUN_RENAME_TEST == 'true' }} - runs-on: macos-latest - strategy: - matrix: - # Add commits/tags to build against other DuckDB versions - duckdb_version: [ ''] - env: - VCPKG_TOOLCHAIN_PATH: ${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake - VCPKG_TARGET_TRIPLET: 'x64-osx' - OSX_BUILD_ARCH: 'x86_64' - GEN: ninja - defaults: - run: - shell: bash - - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - submodules: 'true' - - - name: Install Ninja - run: brew install ninja - - - uses: actions/setup-python@v2 - with: - python-version: '3.7' - - - name: Checkout DuckDB to version - if: ${{ matrix.duckdb_version != ''}} - run: | - cd duckdb - git checkout ${{ matrix.duckdb_version }} - - - name: Setup vcpkg - uses: lukka/run-vcpkg@v11.1 - with: - vcpkgGitCommitId: a42af01b72c28a8e1d7b48107b33e4f286a55ef6 - - - name: Rename extension - run: | - python scripts/bootstrap-template.py ext_1_a_123b_b11 - - - name: Build - run: | - make - - - name: Test - run: | - make test - - windows: - name: Windows - if: ${{ vars.RUN_RENAME_TEST == 'true' }} - runs-on: windows-latest - strategy: - matrix: - # Add commits/tags to build against other DuckDB versions - duckdb_version: [ '' ] - env: - VCPKG_TOOLCHAIN_PATH: ${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake - VCPKG_TARGET_TRIPLET: 'x64-windows-static-md' - defaults: - run: - shell: bash - - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - submodules: 'true' - - - uses: actions/setup-python@v2 - with: - python-version: '3.7' - - - name: Checkout DuckDB to version - # Add commits/tags to build against other DuckDB versions - if: ${{ matrix.duckdb_version != ''}} - run: | - cd duckdb - git checkout ${{ matrix.duckdb_version }} - - - name: Setup vcpkg - uses: lukka/run-vcpkg@v11.1 - with: - vcpkgGitCommitId: a42af01b72c28a8e1d7b48107b33e4f286a55ef6 - - - name: Rename extension - run: | - python scripts/bootstrap-template.py ext_1_a_123b_b11 - - - name: Build - run: | - make - - - name: Test extension - run: | - build/release/test/Release/unittest.exe \ No newline at end of file diff --git a/.github/workflows/MainDistributionPipeline.yml b/.github/workflows/MainDistributionPipeline.yml index 19e81d9..80717b0 100644 --- a/.github/workflows/MainDistributionPipeline.yml +++ b/.github/workflows/MainDistributionPipeline.yml @@ -18,7 +18,7 @@ jobs: with: vcpkg_commit: a42af01b72c28a8e1d7b48107b33e4f286a55ef6 duckdb_version: v0.9.2 - extension_name: quack + extension_name: rfuns duckdb-stable-deploy: name: Deploy extension binaries @@ -27,6 +27,6 @@ jobs: secrets: inherit with: duckdb_version: v0.9.2 - extension_name: quack + extension_name: rfuns 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 f5886a8..3b844d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 2.8.12) # Set extension name here -set(TARGET_NAME quack) +set(TARGET_NAME rfuns) # 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/quack_extension.cpp) +set(EXTENSION_SOURCES src/rfuns_extension.cpp) build_static_extension(${TARGET_NAME} ${EXTENSION_SOURCES}) build_loadable_extension(${TARGET_NAME} " " ${EXTENSION_SOURCES}) diff --git a/Makefile b/Makefile index 637f634..63d4690 100644 --- a/Makefile +++ b/Makefile @@ -31,12 +31,12 @@ ifeq ($(GEN),ninja) GENERATOR=-G "Ninja" -DFORCE_COLORED_OUTPUT=1 endif -EXT_NAME=quack +EXT_NAME=rfuns #### Configuration for this extension -EXTENSION_NAME=QUACK +EXTENSION_NAME=RFUNS EXTENSION_FLAGS=\ --DDUCKDB_EXTENSION_NAMES="quack" \ +-DDUCKDB_EXTENSION_NAMES="rfuns" \ -DDUCKDB_EXTENSION_${EXTENSION_NAME}_PATH="$(PROJ_DIR)" \ -DDUCKDB_EXTENSION_${EXTENSION_NAME}_LOAD_TESTS=1 \ -DDUCKDB_EXTENSION_${EXTENSION_NAME}_INCLUDE_PATH="$(PROJ_DIR)src/include" \ @@ -49,7 +49,7 @@ BUILD_FLAGS=-DEXTENSION_STATIC_BUILD=1 $(EXTENSION_FLAGS) ${EXTRA_EXTENSIONS_FLA CLIENT_FLAGS:= #### Main build -# For regular CLI build, we link the quack extension directly into the DuckDB executable +# For regular CLI build, we link the rfuns extension directly into the DuckDB executable CLIENT_FLAGS=-DDUCKDB_EXTENSION_${EXTENSION_NAME}_SHOULD_LINK=1 debug: @@ -83,8 +83,8 @@ test_debug: debug ./build/debug/$(TEST_PATH) "$(PROJ_DIR)test/*" #### Client tests -DEBUG_EXT_PATH='$(PROJ_DIR)build/debug/extension/quack/quack.duckdb_extension' -RELEASE_EXT_PATH='$(PROJ_DIR)build/release/extension/quack/quack.duckdb_extension' +DEBUG_EXT_PATH='$(PROJ_DIR)build/debug/extension/rfuns/rfuns.duckdb_extension' +RELEASE_EXT_PATH='$(PROJ_DIR)build/release/extension/rfuns/rfuns.duckdb_extension' test_js: test_debug_js test_debug_js: debug_js cd duckdb/tools/nodejs && ${EXTENSION_NAME}_EXTENSION_BINARY_PATH=$(DEBUG_EXT_PATH) npm run test-path -- "../../../test/nodejs/**/*.js" diff --git a/README.md b/README.md new file mode 100644 index 0000000..17341f1 --- /dev/null +++ b/README.md @@ -0,0 +1,86 @@ +# Quack + +This repository is based on https://github.com/duckdb/extension-template, check it out if you want to build and ship your own DuckDB extension. + +--- + +This extension, Quack, allow you to ... . + + +## Building +### Managing dependencies +DuckDB extensions uses VCPKG for dependency management. Enabling VCPKG is very simple: follow the [installation instructions](https://vcpkg.io/en/getting-started) or just run the following: +```shell +git clone https://github.com/Microsoft/vcpkg.git +./vcpkg/bootstrap-vcpkg.sh +export VCPKG_TOOLCHAIN_PATH=`pwd`/vcpkg/scripts/buildsystems/vcpkg.cmake +``` +Note: VCPKG is only required for extensions that want to rely on it for dependency management. If you want to develop an extension without dependencies, or want to do your own dependency management, just skip this step. Note that the example extension uses VCPKG to build with a dependency for instructive purposes, so when skipping this step the build may not work without removing the dependency. + +### Build steps +Now to build the extension, run: +```sh +make +``` +The main binaries that will be built are: +```sh +./build/release/duckdb +./build/release/test/unittest +./build/release/extension/rfuns/rfuns.duckdb_extension +``` +- `duckdb` is the binary for the duckdb shell with the extension code automatically loaded. +- `unittest` is the test runner of duckdb. Again, the extension is already linked into the binary. +- `rfuns.duckdb_extension` is the loadable binary as it would be distributed. + +## Running the extension +To run the extension code, simply start the shell with `./build/release/duckdb`. + +Now we can use the features from the extension directly in DuckDB. The template contains a single scalar function `rfuns()` that takes a string arguments and returns a string: +``` +D select rfuns('Jane') as result; +┌───────────────┐ +│ result │ +│ varchar │ +├───────────────┤ +│ Quack Jane 🐥 │ +└───────────────┘ +``` + +## Running the tests +Different tests can be created for DuckDB extensions. The primary way of testing DuckDB extensions should be the SQL tests in `./test/sql`. These SQL tests can be run using: +```sh +make test +``` + +### Installing the deployed binaries +To install your extension binaries from S3, you will need to do two things. Firstly, DuckDB should be launched with the +`allow_unsigned_extensions` option set to true. How to set this will depend on the client you're using. Some examples: + +CLI: +```shell +duckdb -unsigned +``` + +Python: +```python +con = duckdb.connect(':memory:', config={'allow_unsigned_extensions' : 'true'}) +``` + +NodeJS: +```js +db = new duckdb.Database(':memory:', {"allow_unsigned_extensions": "true"}); +``` + +Secondly, you will need to set the repository endpoint in DuckDB to the HTTP url of your bucket + version of the extension +you want to install. To do this run the following SQL query in DuckDB: +```sql +SET custom_extension_repository='bucket.s3.eu-west-1.amazonaws.com//latest'; +``` +Note that the `/latest` path will allow you to install the latest extension version available for your current version of +DuckDB. To specify a specific version, you can pass the version instead. + +After running these steps, you can install and load your extension using the regular INSTALL/LOAD commands in DuckDB: +```sql +INSTALL rfuns +LOAD rfuns +``` diff --git a/src/include/quack_extension.hpp b/src/include/rfuns_extension.hpp similarity index 78% rename from src/include/quack_extension.hpp rename to src/include/rfuns_extension.hpp index ac9f504..7bace71 100644 --- a/src/include/quack_extension.hpp +++ b/src/include/rfuns_extension.hpp @@ -4,7 +4,7 @@ namespace duckdb { -class QuackExtension : public Extension { +class RfunsExtension : public Extension { public: void Load(DuckDB &db) override; std::string Name() override; diff --git a/src/quack_extension.cpp b/src/rfuns_extension.cpp similarity index 59% rename from src/quack_extension.cpp rename to src/rfuns_extension.cpp index 4fdad78..76acafb 100644 --- a/src/quack_extension.cpp +++ b/src/rfuns_extension.cpp @@ -1,6 +1,6 @@ #define DUCKDB_EXTENSION_MAIN -#include "quack_extension.hpp" +#include "rfuns_extension.hpp" #include "duckdb.hpp" #include "duckdb/common/exception.hpp" #include "duckdb/common/string_util.hpp" @@ -13,21 +13,21 @@ namespace duckdb { -inline void QuackScalarFun(DataChunk &args, ExpressionState &state, Vector &result) { +inline void RfunsScalarFun(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, "Quack "+name.GetString()+" 🐥");; + return StringVector::AddString(result, "Rfuns "+name.GetString()+" 🐥");; }); } -inline void QuackOpenSSLVersionScalarFun(DataChunk &args, ExpressionState &state, Vector &result) { +inline void RfunsOpenSSLVersionScalarFun(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, "Quack " + name.GetString() + + return StringVector::AddString(result, "Rfuns " + name.GetString() + ", my linked OpenSSL version is " + OPENSSL_VERSION_TEXT );; }); @@ -35,32 +35,32 @@ inline void QuackOpenSSLVersionScalarFun(DataChunk &args, ExpressionState &state 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); + auto rfuns_scalar_function = ScalarFunction("rfuns", {LogicalType::VARCHAR}, LogicalType::VARCHAR, RfunsScalarFun); + ExtensionUtil::RegisterFunction(instance, rfuns_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); + auto rfuns_openssl_version_scalar_function = ScalarFunction("rfuns_openssl_version", {LogicalType::VARCHAR}, + LogicalType::VARCHAR, RfunsOpenSSLVersionScalarFun); + ExtensionUtil::RegisterFunction(instance, rfuns_openssl_version_scalar_function); } -void QuackExtension::Load(DuckDB &db) { +void RfunsExtension::Load(DuckDB &db) { LoadInternal(*db.instance); } -std::string QuackExtension::Name() { - return "quack"; +std::string RfunsExtension::Name() { + return "rfuns"; } } // namespace duckdb extern "C" { -DUCKDB_EXTENSION_API void quack_init(duckdb::DatabaseInstance &db) { +DUCKDB_EXTENSION_API void rfuns_init(duckdb::DatabaseInstance &db) { duckdb::DuckDB db_wrapper(db); - db_wrapper.LoadExtension(); + db_wrapper.LoadExtension(); } -DUCKDB_EXTENSION_API const char *quack_version() { +DUCKDB_EXTENSION_API const char *rfuns_version() { return duckdb::DuckDB::LibraryVersion(); } } diff --git a/test/nodejs/quack_test.js b/test/nodejs/rfuns_test.js similarity index 55% rename from test/nodejs/quack_test.js rename to test/nodejs/rfuns_test.js index b6b09c9..43994e4 100644 --- a/test/nodejs/quack_test.js +++ b/test/nodejs/rfuns_test.js @@ -1,30 +1,30 @@ var duckdb = require('../../duckdb/tools/nodejs'); var assert = require('assert'); -describe(`quack extension`, () => { +describe(`rfuns extension`, () => { let db; let conn; before((done) => { db = new duckdb.Database(':memory:', {"allow_unsigned_extensions":"true"}); conn = new duckdb.Connection(db); - conn.exec(`LOAD '${process.env.QUACK_EXTENSION_BINARY_PATH}';`, function (err) { + conn.exec(`LOAD '${process.env.RFUNS_EXTENSION_BINARY_PATH}';`, function (err) { if (err) throw err; done(); }); }); - it('quack function should return expected string', function (done) { - db.all("SELECT quack('Sam') as value;", function (err, res) { + it('rfuns function should return expected string', function (done) { + db.all("SELECT rfuns('Sam') as value;", function (err, res) { if (err) throw err; - assert.deepEqual(res, [{value: "Quack Sam 🐥"}]); + assert.deepEqual(res, [{value: "Rfuns Sam 🐥"}]); done(); }); }); - it('quack_openssl_version function should return expected string', function (done) { - db.all("SELECT quack_openssl_version('Michael') as value;", function (err, res) { + it('rfuns_openssl_version function should return expected string', function (done) { + db.all("SELECT rfuns_openssl_version('Michael') as value;", function (err, res) { if (err) throw err; - assert(res[0].value.startsWith('Quack Michael, my linked OpenSSL version is OpenSSL')); + assert(res[0].value.startsWith('Rfuns Michael, my linked OpenSSL version is OpenSSL')); done(); }); }); diff --git a/test/python/quack_test.py b/test/python/quack_test.py deleted file mode 100644 index e2be276..0000000 --- a/test/python/quack_test.py +++ /dev/null @@ -1,23 +0,0 @@ -import duckdb -import os -import pytest - -# Get a fresh connection to DuckDB with the quack extension binary loaded -@pytest.fixture -def duckdb_conn(): - extension_binary = os.getenv('QUACK_EXTENSION_BINARY_PATH') - if (extension_binary == ''): - raise Exception('Please make sure the `QUACK_EXTENSION_BINARY_PATH` is set to run the python tests') - conn = duckdb.connect('', config={'allow_unsigned_extensions': 'true'}) - conn.execute(f"load '{extension_binary}'") - return conn - -def test_quack(duckdb_conn): - duckdb_conn.execute("SELECT quack('Sam') as value;"); - res = duckdb_conn.fetchall() - assert res[0][0] == "Quack Sam 🐥" - -def test_quack_openssl_version_test(duckdb_conn): - duckdb_conn.execute("SELECT quack_openssl_version('Michael');"); - res = duckdb_conn.fetchall() - assert res[0][0][0:51] == "Quack Michael, my linked OpenSSL version is OpenSSL" diff --git a/test/python/rfuns_test.py b/test/python/rfuns_test.py new file mode 100644 index 0000000..96fc8f3 --- /dev/null +++ b/test/python/rfuns_test.py @@ -0,0 +1,23 @@ +import duckdb +import os +import pytest + +# Get a fresh connection to DuckDB with the rfuns extension binary loaded +@pytest.fixture +def duckdb_conn(): + extension_binary = os.getenv('RFUNS_EXTENSION_BINARY_PATH') + if (extension_binary == ''): + raise Exception('Please make sure the `RFUNS_EXTENSION_BINARY_PATH` is set to run the python tests') + conn = duckdb.connect('', config={'allow_unsigned_extensions': 'true'}) + conn.execute(f"load '{extension_binary}'") + return conn + +def test_rfuns(duckdb_conn): + duckdb_conn.execute("SELECT rfuns('Sam') as value;"); + res = duckdb_conn.fetchall() + assert res[0][0] == "Rfuns Sam 🐥" + +def test_rfuns_openssl_version_test(duckdb_conn): + duckdb_conn.execute("SELECT rfuns_openssl_version('Michael');"); + res = duckdb_conn.fetchall() + assert res[0][0][0:51] == "Rfuns Michael, my linked OpenSSL version is OpenSSL" diff --git a/test/sql/quack.test b/test/sql/quack.test deleted file mode 100644 index d6e3468..0000000 --- a/test/sql/quack.test +++ /dev/null @@ -1,23 +0,0 @@ -# name: test/sql/quack.test -# description: test quack extension -# group: [quack] - -# Before we load the extension, this will fail -statement error -SELECT quack('Sam'); ----- -Catalog Error: Scalar Function with name quack does not exist! - -# Require statement will ensure this test is run with this extension loaded -require quack - -# Confirm the extension works -query I -SELECT quack('Sam'); ----- -Quack Sam 🐥 - -query I -SELECT quack_openssl_version('Michael'); ----- -:Quack Michael, my linked OpenSSL version is OpenSSL.* \ No newline at end of file diff --git a/test/sql/rfuns.test b/test/sql/rfuns.test new file mode 100644 index 0000000..3c37515 --- /dev/null +++ b/test/sql/rfuns.test @@ -0,0 +1,23 @@ +# name: test/sql/rfuns.test +# description: test rfuns extension +# group: [rfuns] + +# Before we load the extension, this will fail +statement error +SELECT rfuns('Sam'); +---- +Catalog Error: Scalar Function with name rfuns does not exist! + +# Require statement will ensure this test is run with this extension loaded +require rfuns + +# Confirm the extension works +query I +SELECT rfuns('Sam'); +---- +Rfuns Sam 🐥 + +query I +SELECT rfuns_openssl_version('Michael'); +---- +:Rfuns Michael, my linked OpenSSL version is OpenSSL.* \ No newline at end of file