From 6cce769f1dd9184c3ad47d73de10c890837a329b Mon Sep 17 00:00:00 2001 From: if-loop69420 Date: Mon, 22 Sep 2025 11:12:08 +0200 Subject: [PATCH 01/11] chore: Change the dependency for supabase-wrappers to their repos 'main' branch in order to use the latest pgrx version --- Cargo.toml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5313311..7350cf5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,18 +17,19 @@ pg14 = ["pgrx/pg14", "pgrx-tests/pg14", "supabase-wrappers/pg14"] pg15 = ["pgrx/pg15", "pgrx-tests/pg15", "supabase-wrappers/pg15"] pg16 = ["pgrx/pg16", "pgrx-tests/pg16", "supabase-wrappers/pg16"] pg17 = ["pgrx/pg17", "pgrx-tests/pg17", "supabase-wrappers/pg17"] +pg18 = ["pgrx/pg18", "pgrx-tests/pg18", "supabase-wrappers/pg18"] pg_test = [] [dependencies] etcd-client = "0.16" futures = "0.3.31" -pgrx = {version="=0.14.3"} -supabase-wrappers = {version="0.1.23", default-features = false} +pgrx = {version="=0.16.0"} +supabase-wrappers = {git="https://github.com/supabase/wrappers.git", branch="main",default-features = false} thiserror = "2.0.16" tokio = { version = "1.47.1", features = ["full"] } [dev-dependencies] -pgrx-tests = "=0.14.3" +pgrx-tests = "=0.16.0" [profile.dev] panic = "unwind" From cdbb4b9dba69b2eaf5929a0458f6ee746e77ff29 Mon Sep 17 00:00:00 2001 From: if-loop69420 Date: Mon, 22 Sep 2025 12:08:20 +0200 Subject: [PATCH 02/11] feat: Add testing and a test to create a test foreign table --- Cargo.toml | 4 ++++ src/lib.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 7350cf5..eec03d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,9 +27,13 @@ pgrx = {version="=0.16.0"} supabase-wrappers = {git="https://github.com/supabase/wrappers.git", branch="main",default-features = false} thiserror = "2.0.16" tokio = { version = "1.47.1", features = ["full"] } +testcontainers = { version = "0.25.0", features = ["blocking"] } +serde = { version = "1.0.226", features = ["derive"] } [dev-dependencies] pgrx-tests = "=0.16.0" +testcontainers = { version = "0.25.0", features = ["blocking"] } +serde = { version = "1.0.226", features = ["derive"] } [profile.dev] panic = "unwind" diff --git a/src/lib.rs b/src/lib.rs index 76625e8..d4a2301 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ use etcd_client::{Client, DeleteOptions, GetOptions, KeyValue, PutOptions}; use pgrx::pg_sys::panic::ErrorReport; use pgrx::PgSqlErrorCode; +use pgrx::*; use supabase_wrappers::prelude::*; use thiserror::Error; @@ -279,3 +280,72 @@ impl ForeignDataWrapper for EtcdFdw { Ok(()) } } + +#[cfg(test)] +pub mod pg_test { + + pub fn setup(_options: Vec<&str>) { + // perform one-off initialization when the pg_test framework starts + } + + pub fn postgresql_conf_options() -> Vec<&'static str> { + // return any postgresql.conf settings that are required for your tests + vec![] + } +} + +#[pg_schema] +#[cfg(any(test, feature = "pg_test"))] +mod tests { + use super::*; + use serde::{Deserialize, Serialize}; + use testcontainers::{ + core::{IntoContainerPort, WaitFor}, + runners::SyncRunner, + GenericImage, ImageExt, + }; + + #[derive(PostgresType, Serialize, Deserialize, Debug, Eq, PartialEq)] + struct KVStruct { + key: String, + value: String, + } + + #[pg_test] + fn test_create_table() { + let container = GenericImage::new("quay.io/coreos/etcd", "v3.6.4") + .with_exposed_port(2379.tcp()) + // .with_wait_for(WaitFor::message_on_stdout("ready to serve client requests")) + .with_wait_for(WaitFor::seconds(20)) + .with_network("bridge") + .start() + .expect("An etcd image was supposed to be started"); + + let host = container + .get_host() + .expect("Host-address should be available"); + + let port = container + .get_host_port_ipv4(2379.tcp()) + .expect("Exposed host port should be available"); + + let url = format!("{}:{}", host, port); + dbg!("Testing FDW on container at {}", &url); + + // Create our fdw + Spi::run("CREATE FOREIGN DATA WRAPPER etcd_fdw handler etcd_fdw_handler validator etcd_fdw_validator;").expect("FDW should have been created"); + + // Create a server + Spi::run( + format!( + "CREATE SERVER etcd_test_server FOREIGN DATA WRAPPER etcd_fdw options(connstr '{}')", + url + ) + .as_str(), + ) + .expect("Server should have been created"); + + // Create a foreign table + Spi::run("CREATE FOREIGN TABLE test (key text, value text) server etcd_test_server options (rowid_column 'key')").expect("Test table should have been created"); + } +} From 9692780eba34265bb1584b974a3f0e1a9ec6bcbf Mon Sep 17 00:00:00 2001 From: if-loop69420 Date: Tue, 23 Sep 2025 08:45:58 +0200 Subject: [PATCH 03/11] feat,fix: Fix testcontainer startup and listening. Add test for insertion into table and select --- src/lib.rs | 84 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 75 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d4a2301..44b4908 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -294,30 +294,83 @@ pub mod pg_test { } } +// use serde::{Deserialize, Serialize}; +// #[derive(PostgresType, Serialize, Deserialize, Debug, Eq, PartialEq)] +// struct KVStruct { +// pub key: String, +// pub value: String, +// } + #[pg_schema] #[cfg(any(test, feature = "pg_test"))] mod tests { + use std::time::Duration; + use super::*; - use serde::{Deserialize, Serialize}; use testcontainers::{ core::{IntoContainerPort, WaitFor}, runners::SyncRunner, GenericImage, ImageExt, }; - #[derive(PostgresType, Serialize, Deserialize, Debug, Eq, PartialEq)] - struct KVStruct { - key: String, - value: String, - } + const CMD: [&'static str; 5] = [ + "/usr/local/bin/etcd", + "--listen-client-urls", + "http://0.0.0.0:2379", + "--advertise-client-urls", + "http://0.0.0.0:2379", + ]; #[pg_test] fn test_create_table() { let container = GenericImage::new("quay.io/coreos/etcd", "v3.6.4") .with_exposed_port(2379.tcp()) - // .with_wait_for(WaitFor::message_on_stdout("ready to serve client requests")) - .with_wait_for(WaitFor::seconds(20)) - .with_network("bridge") + .with_wait_for(WaitFor::message_on_either_std( + "ready to serve client requests", + )) + .with_privileged(true) + .with_cmd(CMD) + .with_startup_timeout(Duration::from_secs(90)) + .start() + .expect("An etcd image was supposed to be started"); + + let host = container + .get_host() + .expect("Host-address should be available"); + + let port = container + .get_host_port_ipv4(2379.tcp()) + .expect("Exposed host port should be available"); + + let url = format!("{}:{}", host, port); + dbg!("Testing FDW on container at {}", &url); + + // Create our fdw + Spi::run("CREATE FOREIGN DATA WRAPPER etcd_fdw handler etcd_fdw_handler validator etcd_fdw_validator;").expect("FDW should have been created"); + + // Create a server + Spi::run( + format!( + "CREATE SERVER etcd_test_server FOREIGN DATA WRAPPER etcd_fdw options(connstr '{}')", + url + ) + .as_str(), + ) + .expect("Server should have been created"); + + // Create a foreign table + Spi::run("CREATE FOREIGN TABLE test (key text, value text) server etcd_test_server options (rowid_column 'key')").expect("Test table should have been created"); + } + #[pg_test] + fn test_insert_select() { + let container = GenericImage::new("quay.io/coreos/etcd", "v3.6.4") + .with_exposed_port(2379.tcp()) + .with_wait_for(WaitFor::message_on_either_std( + "ready to serve client requests", + )) + .with_cmd(CMD) + .with_privileged(true) + .with_startup_timeout(Duration::from_secs(90)) .start() .expect("An etcd image was supposed to be started"); @@ -347,5 +400,18 @@ mod tests { // Create a foreign table Spi::run("CREATE FOREIGN TABLE test (key text, value text) server etcd_test_server options (rowid_column 'key')").expect("Test table should have been created"); + + // Insert into the foreign table + Spi::run("INSERT INTO test (key, value) VALUES ('foo','bar'),('bar','baz')") + .expect("INSERT should work"); + + let query_result = Spi::get_two::("SELECT * FROM test WHERE key='foo'") + .expect("Select should work"); + + assert_eq!((Some(format!("foo")), Some(format!("bar"))), query_result); + let query_result = Spi::get_two::("SELECT * FROM test WHERE key='bar'") + .expect("Select should work"); + + assert_eq!((Some(format!("bar")), Some(format!("baz"))), query_result); } } From e1dffc5121b86420df53a4d96e2e8253fac638d1 Mon Sep 17 00:00:00 2001 From: if-loop69420 Date: Tue, 23 Sep 2025 08:54:04 +0200 Subject: [PATCH 04/11] fix: More code reuse for testing --- src/lib.rs | 55 ++++++++++++++---------------------------------------- 1 file changed, 14 insertions(+), 41 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 44b4908..8acd4e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -310,7 +310,7 @@ mod tests { use testcontainers::{ core::{IntoContainerPort, WaitFor}, runners::SyncRunner, - GenericImage, ImageExt, + Container, GenericImage, ImageExt, }; const CMD: [&'static str; 5] = [ @@ -321,8 +321,7 @@ mod tests { "http://0.0.0.0:2379", ]; - #[pg_test] - fn test_create_table() { + fn create_container() -> (Container, String) { let container = GenericImage::new("quay.io/coreos/etcd", "v3.6.4") .with_exposed_port(2379.tcp()) .with_wait_for(WaitFor::message_on_either_std( @@ -343,9 +342,10 @@ mod tests { .expect("Exposed host port should be available"); let url = format!("{}:{}", host, port); - dbg!("Testing FDW on container at {}", &url); + (container, url) + } - // Create our fdw + fn create_fdt(url: String) -> () { Spi::run("CREATE FOREIGN DATA WRAPPER etcd_fdw handler etcd_fdw_handler validator etcd_fdw_validator;").expect("FDW should have been created"); // Create a server @@ -361,45 +361,18 @@ mod tests { // Create a foreign table Spi::run("CREATE FOREIGN TABLE test (key text, value text) server etcd_test_server options (rowid_column 'key')").expect("Test table should have been created"); } - #[pg_test] - fn test_insert_select() { - let container = GenericImage::new("quay.io/coreos/etcd", "v3.6.4") - .with_exposed_port(2379.tcp()) - .with_wait_for(WaitFor::message_on_either_std( - "ready to serve client requests", - )) - .with_cmd(CMD) - .with_privileged(true) - .with_startup_timeout(Duration::from_secs(90)) - .start() - .expect("An etcd image was supposed to be started"); - - let host = container - .get_host() - .expect("Host-address should be available"); - - let port = container - .get_host_port_ipv4(2379.tcp()) - .expect("Exposed host port should be available"); - - let url = format!("{}:{}", host, port); - dbg!("Testing FDW on container at {}", &url); - // Create our fdw - Spi::run("CREATE FOREIGN DATA WRAPPER etcd_fdw handler etcd_fdw_handler validator etcd_fdw_validator;").expect("FDW should have been created"); + #[pg_test] + fn test_create_table() { + let (_container, url) = create_container(); - // Create a server - Spi::run( - format!( - "CREATE SERVER etcd_test_server FOREIGN DATA WRAPPER etcd_fdw options(connstr '{}')", - url - ) - .as_str(), - ) - .expect("Server should have been created"); + create_fdt(url); + } + #[pg_test] + fn test_insert_select() { + let (_container, url) = create_container(); - // Create a foreign table - Spi::run("CREATE FOREIGN TABLE test (key text, value text) server etcd_test_server options (rowid_column 'key')").expect("Test table should have been created"); + create_fdt(url); // Insert into the foreign table Spi::run("INSERT INTO test (key, value) VALUES ('foo','bar'),('bar','baz')") From 49347d0668c2d550e2a5a71008d5eefec686fb4f Mon Sep 17 00:00:00 2001 From: if-loop69420 Date: Tue, 23 Sep 2025 08:59:05 +0200 Subject: [PATCH 05/11] chore: Add github action for building and testing --- .github/workflows/build_test.yaml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/build_test.yaml diff --git a/.github/workflows/build_test.yaml b/.github/workflows/build_test.yaml new file mode 100644 index 0000000..fb3ae29 --- /dev/null +++ b/.github/workflows/build_test.yaml @@ -0,0 +1,21 @@ +name: Cargo Build & Test + +on: + push: + pull_request: + +env: + CARGO_TERM_COLOR: always + +jobs: + build_and_test: + name: Rust project - latest + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: rustup update stable && rustup default stable + - run: cargo install cargo-pgrx --version 0.16.0 + - run: cargo build --verbose + - run: cargo pgrx test --verbose + + From ea565ce90a4b57db6f6029fcb3b87dca1b9720fc Mon Sep 17 00:00:00 2001 From: if-loop69420 Date: Tue, 23 Sep 2025 13:12:43 +0200 Subject: [PATCH 06/11] fix: Fix test/build workflow. It doesn't do integration tests now --- .github/workflows/build_test.yaml | 40 +++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build_test.yaml b/.github/workflows/build_test.yaml index fb3ae29..f887954 100644 --- a/.github/workflows/build_test.yaml +++ b/.github/workflows/build_test.yaml @@ -9,13 +9,43 @@ env: jobs: build_and_test: - name: Rust project - latest + name: etcd_fdw Build and Test runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - run: rustup update stable && rustup default stable - - run: cargo install cargo-pgrx --version 0.16.0 - - run: cargo build --verbose - - run: cargo pgrx test --verbose + + - name: Cache build/deps + uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache pgrx + uses: actions/cache@v4 + with: + path: | + ~/.pgrx/ + key: pgrx-0.16.0 + + - name: Install latest stable toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + components: rustfmt, clippy + - run: apt update + - run: apt install build-essential bison flex clang protobuf-compiler -y + - name: Create user + run: | + useradd -m test_user + - run: sudo -u test_user cargo install cargo-pgrx --version 0.16.0 + - run: sudo -u test_user cargo pgrx init + - run: sudo -u test_user cargo build --verbose + - run: sudo -u test_user cargo test --verbose From 4f51cbef5c2adeced01c8310fc35dfda53c3833b Mon Sep 17 00:00:00 2001 From: if-loop69420 Date: Tue, 23 Sep 2025 13:42:52 +0200 Subject: [PATCH 07/11] feat: Add tests for all crud things currently supported --- src/lib.rs | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 8acd4e7..0555b59 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -383,8 +383,41 @@ mod tests { assert_eq!((Some(format!("foo")), Some(format!("bar"))), query_result); let query_result = Spi::get_two::("SELECT * FROM test WHERE key='bar'") - .expect("Select should work"); + .expect("SELECT should work"); assert_eq!((Some(format!("bar")), Some(format!("baz"))), query_result); } + + #[pg_test] + fn test_update() { + let (_container, url) = create_container(); + + create_fdt(url); + + Spi::run("INSERT INTO test (key, value) VALUES ('foo','bar'),('bar','baz')") + .expect("INSERT should work"); + + Spi::run("UPDATE test SET value='test_successful'").expect("UPDATE should work"); + + let query_result = + Spi::get_one::("SELECT value FROM test;").expect("SELECT should work"); + + assert_eq!(Some(format!("test_successful")), query_result); + } + + #[pg_test] + fn test_delete() { + let (_container, url) = create_container(); + + create_fdt(url); + + Spi::run("INSERT INTO test (key, value) VALUES ('foo','bar'),('bar','baz')") + .expect("INSERT should work"); + + Spi::run("DELETE FROM test").expect("DELETE should work"); + + let query_result = Spi::get_one::("SELECT value FROM test;"); + + assert_eq!(Err(spi::SpiError::InvalidPosition), query_result); + } } From 048a5c49ec8b79e7a3766a631b80927ca7ea8732 Mon Sep 17 00:00:00 2001 From: if-loop69420 Date: Tue, 23 Sep 2025 14:34:42 +0200 Subject: [PATCH 08/11] fix: Remove unused/commented out code. Remove apt update from workflow --- .github/workflows/build_test.yaml | 1 - src/lib.rs | 7 ------- 2 files changed, 8 deletions(-) diff --git a/.github/workflows/build_test.yaml b/.github/workflows/build_test.yaml index f887954..dd25670 100644 --- a/.github/workflows/build_test.yaml +++ b/.github/workflows/build_test.yaml @@ -38,7 +38,6 @@ jobs: toolchain: stable override: true components: rustfmt, clippy - - run: apt update - run: apt install build-essential bison flex clang protobuf-compiler -y - name: Create user run: | diff --git a/src/lib.rs b/src/lib.rs index 0555b59..883be5f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -294,13 +294,6 @@ pub mod pg_test { } } -// use serde::{Deserialize, Serialize}; -// #[derive(PostgresType, Serialize, Deserialize, Debug, Eq, PartialEq)] -// struct KVStruct { -// pub key: String, -// pub value: String, -// } - #[pg_schema] #[cfg(any(test, feature = "pg_test"))] mod tests { From ee89d221faea94cede731ee3aa19d4123f826753 Mon Sep 17 00:00:00 2001 From: if-loop69420 Date: Tue, 23 Sep 2025 14:38:15 +0200 Subject: [PATCH 09/11] fix: Run apt command as sudo --- .github/workflows/build_test.yaml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build_test.yaml b/.github/workflows/build_test.yaml index dd25670..fe477d7 100644 --- a/.github/workflows/build_test.yaml +++ b/.github/workflows/build_test.yaml @@ -38,13 +38,10 @@ jobs: toolchain: stable override: true components: rustfmt, clippy - - run: apt install build-essential bison flex clang protobuf-compiler -y - - name: Create user - run: | - useradd -m test_user - - run: sudo -u test_user cargo install cargo-pgrx --version 0.16.0 - - run: sudo -u test_user cargo pgrx init - - run: sudo -u test_user cargo build --verbose - - run: sudo -u test_user cargo test --verbose + - run: sudo apt install build-essential bison flex clang protobuf-compiler -y + - run: cargo install cargo-pgrx --version 0.16.0 + - run: cargo pgrx init + - run: cargo build --verbose + - run: cargo test --verbose From 3e8dda6ba25447b57ac6c2dfd7c055c7016c9b49 Mon Sep 17 00:00:00 2001 From: if-loop69420 Date: Tue, 23 Sep 2025 14:44:26 +0200 Subject: [PATCH 10/11] fix: Add readline to the packages for apt to install --- .github/workflows/build_test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_test.yaml b/.github/workflows/build_test.yaml index fe477d7..9ea0cf0 100644 --- a/.github/workflows/build_test.yaml +++ b/.github/workflows/build_test.yaml @@ -38,7 +38,7 @@ jobs: toolchain: stable override: true components: rustfmt, clippy - - run: sudo apt install build-essential bison flex clang protobuf-compiler -y + - run: sudo apt install build-essential bison flex clang protobuf-compiler libreadline8 libreadline-dev -y - run: cargo install cargo-pgrx --version 0.16.0 - run: cargo pgrx init - run: cargo build --verbose From d793f09a4f1e5331e0a9b1b6844689cb0e612993 Mon Sep 17 00:00:00 2001 From: if-loop69420 Date: Tue, 23 Sep 2025 14:55:46 +0200 Subject: [PATCH 11/11] feat: Add integration tests back in --- .github/workflows/build_test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build_test.yaml b/.github/workflows/build_test.yaml index 9ea0cf0..73689aa 100644 --- a/.github/workflows/build_test.yaml +++ b/.github/workflows/build_test.yaml @@ -43,5 +43,6 @@ jobs: - run: cargo pgrx init - run: cargo build --verbose - run: cargo test --verbose + - run: cargo pgrx test --verbose