├── .cargo
└── config.toml
├── .dockerignore
├── .github
└── workflows
│ ├── coverage.yml
│ ├── docs.yml
│ ├── release.yml
│ ├── release_wasm_fdw.yml
│ ├── test_supabase_wrappers.yml
│ └── test_wrappers.yml
├── .gitignore
├── .python-version
├── .vscode
└── settings.json
├── CONTRIBUTING.md
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
├── docs
├── CNAME
├── assets
│ ├── airtable_credentials.png
│ ├── favicon.ico
│ ├── fdw-dark.png
│ ├── fdw-light.png
│ ├── google-sheet.png
│ ├── query-pushdown-dark.png
│ ├── wasm-build.png
│ ├── wrappers-github.jpg
│ ├── wrappers-icon_container-3x.png
│ ├── wrappers-icon_container.png
│ ├── wrappers-icon_container.svg
│ ├── wrappers-wordmark.svg
│ └── wrappers.svg
├── catalog
│ ├── airtable.md
│ ├── auth0.md
│ ├── bigquery.md
│ ├── cal.md
│ ├── calendly.md
│ ├── cfd1.md
│ ├── clerk.md
│ ├── clickhouse.md
│ ├── cognito.md
│ ├── firebase.md
│ ├── hubspot.md
│ ├── iceberg.md
│ ├── index.md
│ ├── logflare.md
│ ├── mssql.md
│ ├── notion.md
│ ├── orb.md
│ ├── paddle.md
│ ├── redis.md
│ ├── s3.md
│ ├── slack.md
│ ├── snowflake.md
│ ├── stripe.md
│ └── wasm
│ │ └── index.md
├── contributing
│ ├── core.md
│ ├── documentation.md
│ └── native.md
├── guides
│ ├── create-wasm-wrapper.md
│ ├── installation.md
│ ├── limitations.md
│ ├── native-wasm.md
│ ├── query-pushdown.md
│ ├── remote-subqueries.md
│ ├── removing-wrappers.md
│ ├── security.md
│ ├── updating-wrappers.md
│ ├── usage-statistics.md
│ └── wasm-advanced.md
├── index.md
├── requirements_docs.txt
├── stylesheets
│ └── extra.css
└── tags.md
├── mkdocs.yaml
├── supabase-wrappers-macros
├── Cargo.toml
├── LICENSE
├── README.md
├── rustfmt.toml
└── src
│ └── lib.rs
├── supabase-wrappers
├── Cargo.toml
├── LICENSE
├── README.md
├── rustfmt.toml
└── src
│ ├── import_foreign_schema.rs
│ ├── instance.rs
│ ├── interface.rs
│ ├── lib.rs
│ ├── limit.rs
│ ├── memctx.rs
│ ├── modify.rs
│ ├── options.rs
│ ├── polyfill.rs
│ ├── qual.rs
│ ├── scan.rs
│ ├── sort.rs
│ └── utils.rs
├── wasm-wrappers
├── .cargo
│ └── config.toml
├── fdw
│ ├── .gitignore
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── cal_fdw
│ │ ├── Cargo.lock
│ │ ├── Cargo.toml
│ │ ├── src
│ │ │ └── lib.rs
│ │ └── wit
│ │ │ └── world.wit
│ ├── calendly_fdw
│ │ ├── Cargo.lock
│ │ ├── Cargo.toml
│ │ ├── src
│ │ │ └── lib.rs
│ │ └── wit
│ │ │ └── world.wit
│ ├── cfd1_fdw
│ │ ├── Cargo.lock
│ │ ├── Cargo.toml
│ │ ├── src
│ │ │ └── lib.rs
│ │ └── wit
│ │ │ └── world.wit
│ ├── clerk_fdw
│ │ ├── Cargo.lock
│ │ ├── Cargo.toml
│ │ ├── src
│ │ │ └── lib.rs
│ │ └── wit
│ │ │ └── world.wit
│ ├── helloworld_fdw
│ │ ├── .cargo
│ │ │ └── config.toml
│ │ ├── .vscode
│ │ │ └── settings.json
│ │ ├── Cargo.lock
│ │ ├── Cargo.toml
│ │ ├── src
│ │ │ └── lib.rs
│ │ └── wit
│ │ │ └── world.wit
│ ├── hubspot_fdw
│ │ ├── Cargo.lock
│ │ ├── Cargo.toml
│ │ ├── src
│ │ │ └── lib.rs
│ │ └── wit
│ │ │ └── world.wit
│ ├── notion_fdw
│ │ ├── Cargo.lock
│ │ ├── Cargo.toml
│ │ ├── src
│ │ │ └── lib.rs
│ │ └── wit
│ │ │ └── world.wit
│ ├── orb_fdw
│ │ ├── Cargo.lock
│ │ ├── Cargo.toml
│ │ ├── src
│ │ │ └── lib.rs
│ │ └── wit
│ │ │ └── world.wit
│ ├── paddle_fdw
│ │ ├── Cargo.lock
│ │ ├── Cargo.toml
│ │ ├── src
│ │ │ └── lib.rs
│ │ └── wit
│ │ │ └── world.wit
│ ├── slack_fdw
│ │ ├── .cargo
│ │ │ └── config.toml
│ │ ├── Cargo.lock
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── api.rs
│ │ │ ├── lib.rs
│ │ │ └── models.rs
│ │ └── wit
│ │ │ └── world.wit
│ └── snowflake_fdw
│ │ ├── Cargo.lock
│ │ ├── Cargo.toml
│ │ ├── src
│ │ └── lib.rs
│ │ └── wit
│ │ └── world.wit
└── wit
│ ├── v1
│ ├── http.wit
│ ├── jwt.wit
│ ├── routines.wit
│ ├── stats.wit
│ ├── time.wit
│ ├── types.wit
│ ├── utils.wit
│ └── world.wit
│ └── v2
│ ├── http.wit
│ ├── jwt.wit
│ ├── routines.wit
│ ├── stats.wit
│ ├── time.wit
│ ├── types.wit
│ ├── utils.wit
│ └── world.wit
└── wrappers
├── .ci
├── docker-compose-native.yaml
├── docker-compose-wasm.yaml
└── docker-compose.yaml
├── Cargo.toml
├── README.md
├── dockerfiles
├── airtable
│ ├── Dockerfile
│ └── server.py
├── auth0
│ ├── Dockerfile
│ └── server.py
├── bigquery
│ ├── Dockerfile
│ └── data.yaml
├── cognito
│ └── .cognito
│ │ ├── config.json
│ │ └── db
│ │ ├── clients.json
│ │ └── local_6QNVVZIN.json
├── firebase
│ ├── README.md
│ ├── baseline-data
│ │ ├── auth_export
│ │ │ ├── accounts.json
│ │ │ └── config.json
│ │ ├── firebase-export-metadata.json
│ │ └── firestore_export
│ │ │ ├── all_namespaces
│ │ │ └── all_kinds
│ │ │ │ ├── all_namespaces_all_kinds.export_metadata
│ │ │ │ └── output-0
│ │ │ └── firestore_export.overall_export_metadata
│ ├── firebase.json
│ └── storage.rules
├── logflare
│ ├── Dockerfile
│ ├── data.json
│ ├── requirements.txt
│ └── server.py
├── notion
│ ├── Dockerfile
│ ├── data.json
│ ├── requirements.txt
│ └── server.py
├── s3
│ ├── Dockerfile
│ ├── iceberg_seed.py
│ └── test_data
│ │ ├── test_data.csv
│ │ ├── test_data.csv.gz
│ │ ├── test_data.jsonl
│ │ ├── test_data.jsonl.bz2
│ │ ├── test_data.parquet
│ │ └── test_data.parquet.gz
└── wasm
│ ├── Dockerfile
│ └── server.py
├── rustfmt.toml
├── sql
├── bootstrap.sql
└── finalize.sql
├── src
├── bin
│ └── pgrx_embed.rs
├── fdw
│ ├── airtable_fdw
│ │ ├── README.md
│ │ ├── airtable_fdw.rs
│ │ ├── mod.rs
│ │ ├── result.rs
│ │ └── tests.rs
│ ├── auth0_fdw
│ │ ├── README.md
│ │ ├── auth0_client
│ │ │ ├── mod.rs
│ │ │ ├── row.rs
│ │ │ └── rows_iterator.rs
│ │ ├── auth0_fdw.rs
│ │ ├── mod.rs
│ │ └── tests.rs
│ ├── bigquery_fdw
│ │ ├── README.md
│ │ ├── bigquery_fdw.rs
│ │ ├── mod.rs
│ │ └── tests.rs
│ ├── clickhouse_fdw
│ │ ├── README.md
│ │ ├── clickhouse_fdw.rs
│ │ ├── mod.rs
│ │ └── tests.rs
│ ├── cognito_fdw
│ │ ├── README.md
│ │ ├── cognito_client
│ │ │ ├── mod.rs
│ │ │ ├── row.rs
│ │ │ └── rows_iterator.rs
│ │ ├── cognito_fdw.rs
│ │ ├── mod.rs
│ │ └── tests.rs
│ ├── firebase_fdw
│ │ ├── README.md
│ │ ├── firebase_fdw.rs
│ │ ├── mod.rs
│ │ └── tests.rs
│ ├── helloworld_fdw
│ │ ├── README.md
│ │ ├── helloworld_fdw.rs
│ │ └── mod.rs
│ ├── iceberg_fdw
│ │ ├── README.md
│ │ ├── iceberg_fdw.rs
│ │ ├── mapper.rs
│ │ ├── mod.rs
│ │ ├── pushdown.rs
│ │ └── tests.rs
│ ├── logflare_fdw
│ │ ├── README.md
│ │ ├── logflare_fdw.rs
│ │ ├── mod.rs
│ │ └── tests.rs
│ ├── mod.rs
│ ├── mssql_fdw
│ │ ├── README.md
│ │ ├── mod.rs
│ │ ├── mssql_fdw.rs
│ │ └── tests.rs
│ ├── redis_fdw
│ │ ├── README.md
│ │ ├── mod.rs
│ │ ├── redis_fdw.rs
│ │ └── tests.rs
│ ├── s3_fdw
│ │ ├── README.md
│ │ ├── mod.rs
│ │ ├── parquet.rs
│ │ ├── s3_fdw.rs
│ │ └── tests.rs
│ ├── stripe_fdw
│ │ ├── README.md
│ │ ├── mod.rs
│ │ ├── stripe_fdw.rs
│ │ └── tests.rs
│ └── wasm_fdw
│ │ ├── README.md
│ │ ├── bindings
│ │ ├── mod.rs
│ │ ├── v1.rs
│ │ └── v2.rs
│ │ ├── host
│ │ ├── http.rs
│ │ ├── jwt.rs
│ │ ├── mod.rs
│ │ ├── stats.rs
│ │ ├── time.rs
│ │ └── utils.rs
│ │ ├── mod.rs
│ │ ├── tests.rs
│ │ └── wasm_fdw.rs
├── lib.rs
└── stats.rs
└── wrappers.control
/.cargo/config.toml:
--------------------------------------------------------------------------------
1 | [target.'cfg(target_os="macos")']
2 | # Postgres symbols won't be available until runtime
3 | rustflags = ["-Clink-arg=-Wl,-undefined,dynamic_lookup"]
4 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | .benchmarks
2 | .git/
3 | .github/
4 | .pytest_cache/
5 | dockerfiles/
6 | docs/
7 | pg_graphql.egg-info/
8 | target/
9 | wrappers/target/
10 | supabase-wrappers/target/
11 | supabase-wrappers-macros/target/
12 | nix/
13 | node_modules/
14 | results/
15 | venv/
16 | *.md
17 | *.py
18 | *.yaml
19 | *.yml
20 | *.graphql
21 | *.nix
22 | .python-version
23 | .gitignore
24 | .clang-format
25 |
--------------------------------------------------------------------------------
/.github/workflows/coverage.yml:
--------------------------------------------------------------------------------
1 | name: Code Coverage
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches:
7 | - main
8 |
9 | permissions:
10 | contents: write
11 |
12 | jobs:
13 | code-coverage:
14 | name: Code Coverage
15 | runs-on: [larger-runner-4cpu]
16 | steps:
17 | - name: Checkout code
18 | uses: actions/checkout@v4
19 |
20 | - name: Install Rust toolchain
21 | uses: actions-rs/toolchain@v1
22 | with:
23 | toolchain: 1.85.1
24 | components: llvm-tools-preview, rustfmt, clippy
25 | default: true
26 | override: true
27 |
28 | - name: Install cargo-llvm-cov
29 | uses: taiki-e/install-action@cargo-llvm-cov
30 |
31 | - run: |
32 | sudo apt remove -y postgres*
33 | sudo apt -y install curl ca-certificates build-essential pkg-config libssl-dev
34 | sudo install -d /usr/share/postgresql-common/pgdg
35 | sudo curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc
36 | . /etc/os-release
37 | sudo sh -c "echo 'deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $VERSION_CODENAME-pgdg main' > /etc/apt/sources.list.d/pgdg.list"
38 | sudo apt update -y -qq --fix-missing
39 | sudo apt -y install postgresql-client-15 postgresql-15 postgresql-server-dev-15
40 | sudo apt -y autoremove && sudo apt -y clean
41 | sudo chmod a+rwx `/usr/lib/postgresql/15/bin/pg_config --pkglibdir` `/usr/lib/postgresql/15/bin/pg_config --sharedir`/extension /var/run/postgresql/
42 |
43 | - run: cargo install cargo-pgrx --version 0.14.3
44 | - run: cargo pgrx init --pg15 /usr/lib/postgresql/15/bin/pg_config
45 |
46 | - name: Build docker images
47 | run: |
48 | docker compose -f wrappers/.ci/docker-compose-native.yaml up -d
49 |
50 | - name: Generate code coverage
51 | id: coverage
52 | run: |
53 | source <(cargo llvm-cov show-env --export-prefix --no-cfg-coverage)
54 | cargo llvm-cov clean --workspace
55 | cargo pgrx test --features "native_fdws" --manifest-path wrappers/Cargo.toml pg15
56 | cargo llvm-cov report --lcov --output-path lcov.info
57 |
58 | - name: Coveralls upload
59 | uses: coverallsapp/github-action@v2
60 | with:
61 | github-token: ${{ secrets.GITHUB_TOKEN }}
62 | path-to-lcov: lcov.info
63 | debug: true
64 |
--------------------------------------------------------------------------------
/.github/workflows/docs.yml:
--------------------------------------------------------------------------------
1 | name: Deploy docs
2 |
3 | on:
4 | push:
5 | tags:
6 | - docs_v*
7 |
8 | permissions: {}
9 |
10 | jobs:
11 | deploy-docs:
12 | runs-on: ubuntu-latest
13 | env:
14 | DOCS_REVALIDATION_KEY: ${{ secrets.DOCS_REVALIDATION_KEY }}
15 | steps:
16 | - name: Request docs revalidation
17 | run: |
18 | curl -X POST https://supabase.com/docs/api/revalidate \
19 | -H 'Content-Type: application/json' \
20 | -H 'Authorization: Bearer ${{ secrets.DOCS_REVALIDATION_KEY }}' \
21 | -d '{"tags": ["wrappers"]}'
22 |
--------------------------------------------------------------------------------
/.github/workflows/test_supabase_wrappers.yml:
--------------------------------------------------------------------------------
1 | name: Test Wrappers
2 | on:
3 | pull_request:
4 | push:
5 | branches:
6 | - main
7 |
8 | permissions:
9 | contents: read
10 |
11 | jobs:
12 | test:
13 | name: Run supabase_wrappers tests
14 | runs-on: ubuntu-24.04
15 |
16 | steps:
17 | - name: Checkout code
18 | uses: actions/checkout@v4
19 |
20 | - uses: actions-rs/toolchain@v1
21 | with:
22 | toolchain: 1.85.1
23 | default: true
24 | override: true
25 | components: rustfmt, clippy
26 |
27 | - run: |
28 | sudo apt remove -y postgres*
29 | sudo apt -y install curl ca-certificates build-essential pkg-config libssl-dev
30 | sudo install -d /usr/share/postgresql-common/pgdg
31 | sudo curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc
32 | . /etc/os-release
33 | sudo sh -c "echo 'deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $VERSION_CODENAME-pgdg main' > /etc/apt/sources.list.d/pgdg.list"
34 | sudo apt update -y -qq --fix-missing
35 | sudo apt -y install postgresql-client-15 postgresql-15 postgresql-server-dev-15
36 | sudo apt -y autoremove && sudo apt -y clean
37 | sudo chmod a+rwx `/usr/lib/postgresql/15/bin/pg_config --pkglibdir` `/usr/lib/postgresql/15/bin/pg_config --sharedir`/extension /var/run/postgresql/
38 |
39 | - run: cargo install cargo-pgrx --version 0.14.3
40 | - run: cargo pgrx init --pg15 /usr/lib/postgresql/15/bin/pg_config
41 |
42 | - name: Format code
43 | run: |
44 | cd supabase-wrappers && cargo fmt --check
45 |
46 | - name: Run clippy
47 | run: |
48 | cd supabase-wrappers && RUSTFLAGS="-D warnings" cargo clippy --all --tests --no-deps
49 |
50 | - run: cd supabase-wrappers && cargo test
51 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea/
3 | target/
4 | *.iml
5 | **/*.rs.bk
6 | *.o
7 | *.so
8 | *.a
9 | *.swp
10 | *.log
11 | site/
12 | .bash_history
13 | .config/
14 | cmake*/
15 | .direnv
16 | results/
17 | wrappers/results/
18 | wrappers/regression.*
19 | .venv/
20 |
--------------------------------------------------------------------------------
/.python-version:
--------------------------------------------------------------------------------
1 | 3.10.3
2 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "rust-analyzer.cargo.features": ["all_fdws,helloworld_fdw"]
3 | }
4 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Any contributions to wrappers are welcome, please visit https://fdw.dev/contributing/core/ for more details.
4 |
5 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = [
3 | "supabase-wrappers",
4 | "supabase-wrappers-macros",
5 | "wrappers",
6 | ]
7 | exclude = [
8 | "wasm-wrappers",
9 | ]
10 | resolver = "2"
11 |
12 | [workspace.package]
13 | edition = "2021"
14 | rust-version = "1.85.1"
15 | homepage = "https://github.com/supabase/wrappers"
16 | repository = "https://github.com/supabase/wrappers"
17 |
18 | [profile.dev]
19 | panic = "unwind"
20 | lto = "thin"
21 |
22 | [profile.release]
23 | panic = "unwind"
24 | opt-level = 3
25 | lto = "fat"
26 | codegen-units = 1
27 |
28 |
--------------------------------------------------------------------------------
/docs/CNAME:
--------------------------------------------------------------------------------
1 | fdw.dev
2 |
--------------------------------------------------------------------------------
/docs/assets/airtable_credentials.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supabase/wrappers/0da8b7131243e2a1696980703acd9b363c9c49d1/docs/assets/airtable_credentials.png
--------------------------------------------------------------------------------
/docs/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supabase/wrappers/0da8b7131243e2a1696980703acd9b363c9c49d1/docs/assets/favicon.ico
--------------------------------------------------------------------------------
/docs/assets/fdw-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supabase/wrappers/0da8b7131243e2a1696980703acd9b363c9c49d1/docs/assets/fdw-dark.png
--------------------------------------------------------------------------------
/docs/assets/fdw-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supabase/wrappers/0da8b7131243e2a1696980703acd9b363c9c49d1/docs/assets/fdw-light.png
--------------------------------------------------------------------------------
/docs/assets/google-sheet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supabase/wrappers/0da8b7131243e2a1696980703acd9b363c9c49d1/docs/assets/google-sheet.png
--------------------------------------------------------------------------------
/docs/assets/query-pushdown-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supabase/wrappers/0da8b7131243e2a1696980703acd9b363c9c49d1/docs/assets/query-pushdown-dark.png
--------------------------------------------------------------------------------
/docs/assets/wasm-build.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supabase/wrappers/0da8b7131243e2a1696980703acd9b363c9c49d1/docs/assets/wasm-build.png
--------------------------------------------------------------------------------
/docs/assets/wrappers-github.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supabase/wrappers/0da8b7131243e2a1696980703acd9b363c9c49d1/docs/assets/wrappers-github.jpg
--------------------------------------------------------------------------------
/docs/assets/wrappers-icon_container-3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supabase/wrappers/0da8b7131243e2a1696980703acd9b363c9c49d1/docs/assets/wrappers-icon_container-3x.png
--------------------------------------------------------------------------------
/docs/assets/wrappers-icon_container.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supabase/wrappers/0da8b7131243e2a1696980703acd9b363c9c49d1/docs/assets/wrappers-icon_container.png
--------------------------------------------------------------------------------
/docs/assets/wrappers-icon_container.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/docs/assets/wrappers.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/catalog/auth0.md:
--------------------------------------------------------------------------------
1 | ---
2 | source:
3 | documentation:
4 | author: supabase
5 | tags:
6 | - native
7 | - official
8 | ---
9 |
10 | # Auth0
11 |
12 | [Auth0](https://auth0.com/) is a flexible, drop-in solution to add authentication and authorization services to your applications
13 |
14 | The Auth0 Wrapper allows you to read data from your Auth0 tenant for use within your Postgres database.
15 |
16 | ## Preparation
17 |
18 | Before you can query Auth0, you need to enable the Wrappers extension and store your credentials in Postgres.
19 |
20 | ### Enable Wrappers
21 |
22 | Make sure the `wrappers` extension is installed on your database:
23 |
24 | ```sql
25 | create extension if not exists wrappers with schema extensions;
26 | ```
27 |
28 | ### Enable the Auth0 Wrapper
29 |
30 | Enable the `auth0_wrapper` FDW:
31 |
32 | ```sql
33 | create foreign data wrapper auth0_wrapper
34 | handler auth0_fdw_handler
35 | validator auth0_fdw_validator;
36 | ```
37 |
38 | ### Store your credentials (optional)
39 |
40 | By default, Postgres stores FDW credentials inside `pg_catalog.pg_foreign_server` in plain text. Anyone with access to this table will be able to view these credentials. Wrappers is designed to work with [Vault](https://supabase.com/docs/guides/database/vault), which provides an additional level of security for storing credentials. We recommend using Vault to store your credentials.
41 |
42 | ```sql
43 | -- Save your Auth0 API key in Vault and retrieve the created `key_id`
44 | select vault.create_secret(
45 | '', -- Auth0 API key or Personal Access Token (PAT)
46 | 'auth0',
47 | 'Auth0 API key for Wrappers'
48 | );
49 | ```
50 |
51 | ### Connecting to Auth0
52 |
53 | We need to provide Postgres with the credentials to connect to Auth0, and any additional options. We can do this using the `create server` command:
54 |
55 | === "With Vault"
56 |
57 | ```sql
58 | create server auth0_server
59 | foreign data wrapper auth0_wrapper
60 | options (
61 | url 'https://dev-.us.auth0.com/api/v2/users',
62 | api_key_id '' -- The Key ID from above.
63 | );
64 | ```
65 |
66 | === "Without Vault"
67 |
68 | ```sql
69 | -- create server and specify custom options
70 | create server auth0_server
71 | foreign data wrapper auth0_wrapper
72 | options (
73 | url 'https://dev-.us.auth0.com/api/v2/users',
74 | api_key ''
75 | );
76 | ```
77 |
78 | ### Create a schema
79 |
80 | We recommend creating a schema to hold all the foreign tables:
81 |
82 | ```sql
83 | create schema if not exists auth0;
84 | ```
85 |
86 | ## Entities
87 |
88 | The Auth0 Wrapper supports data reads from Auth0 API.
89 |
90 | ### Users
91 |
92 | The Auth0 Wrapper supports data reads from Auth0's [Management API List users endpoint](https://auth0.com/docs/api/management/v2/users/get-users) endpoint (_read only_).
93 |
94 | #### Operations
95 |
96 | | Object | Select | Insert | Update | Delete | Truncate |
97 | | ------ | :----: | :----: | :----: | :----: | :------: |
98 | | Users | ✅ | ❌ | ❌ | ❌ | ❌ |
99 |
100 | #### Usage
101 |
102 | ```sql
103 | create foreign table auth0.my_foreign_table (
104 | name text
105 | -- other fields
106 | )
107 | server auth0_server
108 | options (
109 | object 'users'
110 | );
111 | ```
112 |
113 | #### Notes
114 |
115 | - Currently only supports the `users` object
116 |
117 | ## Query Pushdown Support
118 |
119 | This FDW doesn't support query pushdown.
120 |
121 | ## Limitations
122 |
123 | This section describes important limitations and considerations when using this FDW:
124 |
125 | - No query pushdown support, all filtering must be done locally
126 | - Large result sets may experience slower performance due to full data transfer requirement
127 | - Only supports the `users` object from Auth0 Management API
128 | - Cannot modify Auth0 user properties via FDW
129 | - Materialized views using these foreign tables may fail during logical backups
130 |
131 | ## Examples
132 |
133 | ### Basic Auth0 Users Query
134 |
135 | This example demonstrates querying Auth0 users data.
136 |
137 | ```sql
138 | create foreign table auth0.auth0_table (
139 | created_at text,
140 | email text,
141 | email_verified bool,
142 | identities jsonb
143 | )
144 | server auth0_server
145 | options (
146 | object 'users'
147 | );
148 | ```
149 |
150 | You can now fetch your Auth0 data from within your Postgres database:
151 |
152 | ```sql
153 | select * from auth0.auth0_table;
154 | ```
155 |
--------------------------------------------------------------------------------
/docs/catalog/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | hide:
3 | - toc
4 | ---
5 |
6 | # Catalog
7 |
8 | Each FDW documentation includes a detailed "Limitations" section that describes important considerations and potential pitfalls when using that specific FDW.
9 |
10 | ## Official
11 |
12 | | Integration | Select | Insert | Update | Delete | Truncate | Push Down |
13 | | ------------- | :----: | :----: | :----: | :----: | :------: | :-------: |
14 | | Airtable | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
15 | | Auth0 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
16 | | AWS Cognito | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
17 | | BigQuery | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
18 | | Cal.com | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
19 | | Calendly | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
20 | | Clerk | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
21 | | ClickHouse | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
22 | | Cloudflare D1 | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
23 | | Firebase | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
24 | | HubSpot | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
25 | | Iceberg | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
26 | | Logflare | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
27 | | Notion | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
28 | | Orb | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
29 | | Paddle | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ |
30 | | Redis | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
31 | | S3 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
32 | | Snowflake | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
33 | | Stripe | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
34 | | SQL Server | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
35 |
36 | ## Community
37 |
38 | Wasm wrappers can be installed directly from GitHub or any external source.
39 |
40 | See [Developing a Wasm Wrapper](../guides/create-wasm-wrapper.md) for instructions on how to build and develop your own.
41 |
42 | | Integration | Developer | Docs | Source |
43 | | :-----------: | :------------------------------: | :------------------: | :------------------------------------------------------------------------------------: |
44 | | Cal.com | [Supabase](https://supabase.com) | [Link](cal.md) | [Link](https://github.com/supabase/wrappers/tree/main/wasm-wrappers/fdw/cal_fdw) |
45 | | Calendly | [Supabase](https://supabase.com) | [Link](calendly.md) | [Link](https://github.com/supabase/wrappers/tree/main/wasm-wrappers/fdw/calendly_fdw) |
46 | | Clerk | [Supabase](https://supabase.com) | [Link](clerk.md) | [Link](https://github.com/supabase/wrappers/tree/main/wasm-wrappers/fdw/clerk_fdw) |
47 | | Cloudflare D1 | [Supabase](https://supabase.com) | [Link](cfd1.md) | [Link](https://github.com/supabase/wrappers/tree/main/wasm-wrappers/fdw/cfd1_fdw) |
48 | | HubSpot | [Supabase](https://supabase.com) | [Link](hubspot.md) | [Link](https://github.com/supabase/wrappers/tree/main/wasm-wrappers/fdw/hubspot_fdw) |
49 | | Notion | [Supabase](https://supabase.com) | [Link](notion.md) | [Link](https://github.com/supabase/wrappers/tree/main/wasm-wrappers/fdw/notion_fdw) |
50 | | Orb | [Supabase](https://supabase.com) | [Link](orb.md) | [Link](https://github.com/supabase/wrappers/tree/main/wasm-wrappers/fdw/orb_fdw) |
51 | | Paddle | [Supabase](https://supabase.com) | [Link](paddle.md) | [Link](https://github.com/supabase/wrappers/tree/main/wasm-wrappers/fdw/paddle_fdw) |
52 | | Snowflake | [Supabase](https://supabase.com) | [Link](snowflake.md) | [Link](https://github.com/supabase/wrappers/tree/main/wasm-wrappers/fdw/snowflake_fdw) |
53 |
--------------------------------------------------------------------------------
/docs/contributing/core.md:
--------------------------------------------------------------------------------
1 | [`supabase/wrappers`](https://github.com/supabase/wrappers) is open source. PRs and issues are welcome.
2 |
3 | ## Development
4 |
5 | Requirements:
6 |
7 | - [Rust](https://www.rust-lang.org/)
8 | - [Cargo](https://doc.rust-lang.org/cargo/)
9 | - [Docker](https://www.docker.com/)
10 | - [pgrx](https://github.com/pgcentralfoundation/pgrx)
11 |
12 | ### Testing
13 |
14 | Tests are located in each FDW's `tests.rs` file. For example, Stripe FDW tests are in `wrappers/src/fdw/stripe_fdw/tests.rs` file.
15 |
16 | To run the tests locally, we need to start the mock containers first:
17 |
18 | ```bash
19 | cd wrappers
20 | docker-compose -f .ci/docker-compose.yaml up -d
21 | ```
22 |
23 | And then build all Wasm FDW packages:
24 |
25 | ```bash
26 | find ../wasm-wrappers/fdw/ -name "Cargo.toml" -exec cargo component build --release --target wasm32-unknown-unknown --manifest-path {} \;
27 | ```
28 |
29 | Now we can run all the tests:
30 |
31 | ```bash
32 | cargo pgrx test --features "all_fdws pg15"
33 | ```
34 |
35 | You can also run the native or Wasm FDW tests individually:
36 |
37 | ```base
38 | # run native FDW tests only
39 | cargo pgrx test --features "native_fdws pg15"
40 |
41 | # or run Wasm FDW tests only
42 | cargo pgrx test --features "wasm_fdw pg15"
43 | ```
44 |
45 | ### Interactive PSQL Development
46 |
47 | To reduce the iteration cycle, you may want to launch a psql prompt with `wrappers` installed to experiment a single FDW like below:
48 |
49 | ```bash
50 | cd wrappers
51 | cargo pgrx run pg15 --features clickhouse_fdw
52 | ```
53 |
54 | Try out the SQLs in the psql prompt with the `wrappers` extension installed like below:
55 |
56 | ```sql
57 | create extension if not exists wrappers;
58 |
59 | create foreign data wrapper wasm_wrapper
60 | handler wasm_fdw_handler
61 | validator wasm_fdw_validator;
62 | ```
63 |
64 | For debugging, you can make use of [`notice!` macros](https://docs.rs/pgrx/latest/pgrx/macro.notice.html) to print out statements while using your wrapper in `psql`.
65 |
--------------------------------------------------------------------------------
/docs/contributing/documentation.md:
--------------------------------------------------------------------------------
1 | Building documentation requires Python 3.6+.
2 |
3 | ### Install Dependencies
4 |
5 | Install mkdocs, themes, and extensions.
6 |
7 | ```shell
8 | pip install -r docs/requirements_docs.txt
9 | ```
10 |
11 | ### Serving
12 |
13 | To serve the documentation locally run
14 |
15 | ```shell
16 | mkdocs serve
17 | ```
18 |
19 | and visit the docs at [http://127.0.0.1:8000/wrappers/](http://127.0.0.1:8000/wrappers/)
20 |
21 | ### Deploying
22 |
23 | If you have write access to the repo, docs can be updated using
24 |
25 | ```
26 | mkdocs gh-deploy
27 | ```
28 |
--------------------------------------------------------------------------------
/docs/contributing/native.md:
--------------------------------------------------------------------------------
1 | # Developing a Native Wrapper
2 |
3 | !!! warning
4 |
5 | We are not accepting native community Wrappers this repo until the Wrappers API is stabilized. If you have an idea for a Wrapper, you can [vote your favorite Wrapper](https://github.com/orgs/supabase/discussions/26627). Once we release Wrappers 1.0, we will support native community Wrappers within the Wrappers repo.
6 |
7 | In the meantime you can develop a Wasm wrapper, which can be installed on any Postgres instance with `wrappers v0.4.0+`.
8 |
9 | To develop a FDW using `Wrappers`, you only need to implement the [ForeignDataWrapper](https://github.com/supabase/wrappers/blob/main/supabase-wrappers/src/interface.rs) trait.
10 |
11 | ```rust
12 | pub trait ForeignDataWrapper {
13 | // create a FDW instance
14 | fn new(...) -> Self;
15 |
16 | // functions for data scan, e.g. select
17 | fn begin_scan(...);
18 | fn iter_scan(...) -> Option;
19 | fn end_scan(...);
20 |
21 | // functions for data modify, e.g. insert, update and delete
22 | fn begin_modify(...);
23 | fn insert(...);
24 | fn update(...);
25 | fn delete(...);
26 | fn end_modify(...);
27 |
28 | // other optional functions
29 | ...
30 | }
31 | ```
32 |
33 | In a minimum FDW, which supports data scan only, `new()`, `begin_scan()`, `iter_scan()` and `end_scan()` are required, all the other functions are optional.
34 |
35 | To know more about FDW development, please visit the [Wrappers documentation](https://docs.rs/supabase-wrappers/latest/supabase_wrappers/).
36 |
37 | ## Basic usage
38 |
39 | These steps outline how to use the a demo FDW [HelloWorldFdw](https://github.com/supabase/wrappers/tree/main/wrappers/src/fdw/helloworld_fdw), which only outputs a single line of fake data:
40 |
41 | 1. Clone this repo
42 |
43 | ```bash
44 | git clone https://github.com/supabase/wrappers.git
45 | ```
46 |
47 | 2. Run it using pgrx with feature:
48 |
49 | ```bash
50 | cd wrappers/wrappers
51 | cargo pgrx run pg15 --features helloworld_fdw
52 | ```
53 |
54 | 3. Create the extension, foreign data wrapper and related objects:
55 |
56 | ```sql
57 | -- create extension
58 | create extension wrappers;
59 |
60 | -- create foreign data wrapper and enable 'HelloWorldFdw'
61 | create foreign data wrapper helloworld_wrapper
62 | handler hello_world_fdw_handler
63 | validator hello_world_fdw_validator;
64 |
65 | -- create server and specify custom options
66 | create server my_helloworld_server
67 | foreign data wrapper helloworld_wrapper
68 | options (
69 | foo 'bar'
70 | );
71 |
72 | -- create an example foreign table
73 | create foreign table hello (
74 | id bigint,
75 | col text
76 | )
77 | server my_helloworld_server
78 | options (
79 | foo 'bar'
80 | );
81 | ```
82 |
83 | 4. Run a query to check if it is working:
84 |
85 | ```sql
86 | wrappers=# select * from hello;
87 | id | col
88 | ----+-------------
89 | 0 | Hello world
90 | (1 row)
91 | ```
92 |
93 | ## Running tests
94 |
95 | In order to run tests in `wrappers`:
96 |
97 | ```bash
98 | docker-compose -f .ci/docker-compose.yaml up -d
99 | cargo pgrx test --features all_fdws,pg15
100 | ```
101 |
102 | ## Contribution
103 |
104 | All contributions, feature requests, bug report or ideas are welcomed.
105 |
--------------------------------------------------------------------------------
/docs/guides/installation.md:
--------------------------------------------------------------------------------
1 | First, install [pgrx](https://github.com/tcdi/pgrx)
2 |
3 | Then clone the repo and install using
4 |
5 | ```bash
6 | git clone https://github.com/supabase/wrappers.git
7 | cd wrappers/wrappers
8 | cargo pgrx install --no-default-features --features pg14,_fdw --release
9 | ```
10 |
11 | To enable the extension in PostgreSQL we must execute a `create extension` statement.
12 |
13 | ```psql
14 | create extension wrappers;
15 | ```
16 |
--------------------------------------------------------------------------------
/docs/guides/limitations.md:
--------------------------------------------------------------------------------
1 | ## Limitations
2 |
3 | - Windows is not supported, that limitation inherits from [pgrx](https://github.com/tcdi/pgrx).
4 | - Currently only supports PostgreSQL v14, v15 and v16.
5 | - Generated columns are not supported.
6 |
--------------------------------------------------------------------------------
/docs/guides/native-wasm.md:
--------------------------------------------------------------------------------
1 | # Native vs Wasm Wrappers
2 |
3 | ## Wasm Wrappers
4 |
5 | Since v0.4.0, Wrappers supports WebAssembly (Wasm) FDWs. Anyone can develop a Wasm Wrapper. Wasm foreign data wrappers are dynamically loaded on the first query and then cached locally, they can be installed directly from remotes like GitHub and S3.
6 |
7 | Check out [Developing a Wrapper](create-wasm-wrapper.md) to develop your own Wasm Wrapper.
8 |
9 | ## Native Wrappers
10 |
11 | Native Wrappers are developed and supported by Supabase. These have better performance than Wasm wrappers, but they must be pre-installed on the Postgres database before a developer can use it.
12 |
--------------------------------------------------------------------------------
/docs/guides/query-pushdown.md:
--------------------------------------------------------------------------------
1 | # Query Pushdown
2 |
3 | Query pushdown is a technique that enhances query performance by executing parts of the query directly on the data source. It reduces data transfer between the database and the application, enabling faster execution and improved performance.
4 |
5 | ### What is Query Pushdown?
6 |
7 | Query pushdown is a technique that enhances query performance by executing parts of the query directly on the data source. It reduces data transfer between the database and the application, enabling faster execution and improved performance.
8 |
9 | 
10 |
11 | ### Using Query Pushdown
12 |
13 | In Wrappers, the pushdown logic is integrated into each FDW. You don’t need to modify your queries to benefit from this feature. For example, the [Stripe FDW](https://supabase.com/docs/guides/database/extensions/wrappers/stripe) automatically applies query pushdown for `id` within the `customer` object:
14 |
15 | ```sql
16 | select *
17 | from stripe.customers
18 | where id = 'cus_N5WMk7pvQPkY3B';
19 | ```
20 |
21 | This approach contrasts with fetching and filtering all customers locally, which is less efficient. Query pushdown translates this into a single API call, significantly speeding up the process:
22 |
23 | ```bash
24 | https://api.stripe.com/v1/customers/cus_N5WMk7pvQPkY3B
25 | ```
26 |
27 | We can use push down criteria and other query parameters too. For example, [ClickHouse FDW](https://supabase.com/docs/guides/database/extensions/wrappers/clickhouse) supports `order by` and `limit` pushdown:
28 |
29 | ```sql
30 | select *
31 | from clickhouse.people
32 | order by name
33 | limit 20;
34 | ```
35 |
36 | This query executes `order by name limit 20` on ClickHouse before transferring the result to Postgres.
37 |
--------------------------------------------------------------------------------
/docs/guides/remote-subqueries.md:
--------------------------------------------------------------------------------
1 | # Remote Subqueries
2 |
3 | Remote subqueries enable the use of prepared data on a remote server, which is beneficial for complex queries or sensitive data protection.
4 |
5 | ### Static Subqueries
6 |
7 | In its most basic form, you can map a query on the remote server into a foreign table in Postgres. For instance:
8 |
9 | ```sql
10 | create foreign table clickhouse.people (
11 | id bigint,
12 | name text,
13 | age bigint
14 | )
15 | server clickhouse_server
16 | options (
17 | table '(select * from people where age < 25)'
18 | );
19 | ```
20 |
21 | In this example, the foreign table `clickhouse.people` data is read from the result of the subquery `select * from people where age < 25` which runs on ClickHouse server.
22 |
23 | ### Dynamic Subqueries
24 |
25 | What if the query is not fixed and needs to be dynamic? For example, ClickHouse provides [Parameterized Views](https://clickhouse.com/docs/en/sql-reference/statements/create/view#parameterized-view) which can accept parameters for a view. Wrappers supports this by defining a column for each parameter.
26 |
27 | Let's take a look at an example:
28 |
29 | ```sql
30 | create foreign table clickhouse.my_table (
31 | id bigint,
32 | col1 text,
33 | col2 bigint,
34 | _param1 text,
35 | _param2 bigint
36 | )
37 | server clickhouse_server
38 | options (
39 | table '(select * from my_view(column1=${_param1}, column2=${_param2}))'
40 | );
41 | ```
42 |
43 | You can then pass values to these parameters in your query:
44 |
45 | ```sql
46 | select id, col1, col2
47 | from clickhouse.my_table
48 | where _param1 = 'abc' and _param2 = 42;
49 | ```
50 |
51 | Currently, this feature is supported by [ClickHouse FDW](https://supabase.com/docs/guides/database/extensions/wrappers/clickhouse) and [BigQuery FDW](https://supabase.com/docs/guides/database/extensions/wrappers/bigquery), with plans to expand support in the future.
52 |
--------------------------------------------------------------------------------
/docs/guides/removing-wrappers.md:
--------------------------------------------------------------------------------
1 | # Removing Foreign Data Wrappers
2 |
3 | This guide explains how to fully remove all foreign data wrappers from your PostgreSQL database.
4 |
5 |
6 | ## Components to Remove
7 |
8 | When removing a foreign data wrapper, you need to remove several components in the correct order:
9 |
10 | 1. Foreign Tables
11 | 2. Foreign Servers
12 | 3. Wrappers Extension
13 |
14 | ## Step-by-Step Removal Process
15 |
16 | ### 1. List and Remove Foreign Tables
17 |
18 | First, list all foreign tables associated with your wrapper:
19 |
20 | ```sql
21 | select c.relname as foreign_table_name,
22 | n.nspname as schema_name
23 | from pg_catalog.pg_foreign_table ft
24 | join pg_catalog.pg_class c on c.oid = ft.ftrelid
25 | join pg_catalog.pg_namespace n on n.oid = c.relnamespace;
26 | ```
27 |
28 | Remove each foreign table:
29 |
30 | ```sql
31 | drop foreign table if exists schema_name.table_name;
32 | ```
33 |
34 | ### 2. Remove Foreign Servers
35 |
36 | List servers:
37 |
38 | ```sql
39 | select fs.srvname as server_name,
40 | fdw.fdwname as wrapper_name
41 | from pg_catalog.pg_foreign_server fs
42 | join pg_catalog.pg_foreign_data_wrapper fdw on fdw.oid = fs.srvfdw;
43 | ```
44 |
45 | Remove each server:
46 |
47 | ```sql
48 | drop server if exists server_name cascade;
49 | ```
50 |
51 | !!! note
52 |
53 | With `cascade` option, this will also drop all foreign tables using that foreign server.
54 |
55 | ### 3. Remove the Extension
56 |
57 | ```sql
58 | drop extension if exists wrappers cascade;
59 | ```
60 |
61 | !!! note
62 |
63 | With `cascade` option, this will also drop all foreign servers and foreign tables using Wrappers extension.
64 |
65 |
--------------------------------------------------------------------------------
/docs/guides/security.md:
--------------------------------------------------------------------------------
1 | # Security
2 |
3 | ## Remote Servers
4 |
5 | Your FDWs will inherit the security model of the credentials provided when you create the remote server. For example, take this Stripe Wrapper:
6 |
7 | ```sql
8 | create server stripe_server
9 | foreign data wrapper stripe_wrapper
10 | options (
11 | api_key '', -- Stripe API key, required
12 | api_url 'https://api.stripe.com/v1/',
13 | api_version '2024-06-20'
14 | );
15 | ```
16 |
17 | The Wrapper will be able to access any data that the `api_key` has permission to access.
18 |
19 | ## Row Level Security
20 |
21 | Foreign Data Wrappers do not provide Row Level Security. Wrappers should _always_ be stored in a private schema. For example, if you are connecting to your Stripe account, you should create a `stripe` schema to store all of your foreign tables inside. This schema should have a restrictive set of grants.
22 |
23 | ## Exposing foreign data
24 |
25 | If you want to expose any of the foreign table columns through a public API, we recommend using a [Postgres Function with `security definer`](https://supabase.com/docs/guides/database/functions#security-definer-vs-invoker). For better access control, the function should have appropriate filters on the foreign table to apply security rules based on your business needs.
26 |
27 | For example, if you wanted to expose all of your Stripe Products through an API:
28 |
29 | Create a Stripe Products foreign table:
30 |
31 | ```sql
32 | create foreign table stripe.stripe_products (
33 | id text,
34 | name text,
35 | active bool,
36 | default_price text,
37 | description text
38 | )
39 | server stripe_fdw_server
40 | options (
41 | object 'products',
42 | rowid_column 'id'
43 | );
44 | ```
45 |
46 | Create a `security definer` function that queries the foreign table and filters on the name prefix parameter:
47 |
48 | ```sql
49 | create function public.get_stripe_products(name_prefix text)
50 | returns table (
51 | id text,
52 | name text,
53 | active boolean,
54 | default_price text,
55 | description text
56 | )
57 | language plpgsql
58 | security definer set search_path = ''
59 | as $$
60 | begin
61 | return query
62 | select
63 | id,
64 | name,
65 | active,
66 | default_price,
67 | description
68 | from
69 | stripe.stripe_products
70 | where
71 | name like name_prefix || '%'
72 | ;
73 | end;
74 | $$;
75 | ```
76 |
77 | Restrict the function execution to a specific role only. For example, if you have an `authenticated` role in Postgres, revoke access to everything except that one role:
78 |
79 | ```sql
80 | -- revoke public execute permission
81 | revoke execute on function public.get_stripe_products from public;
82 | revoke execute on function public.get_stripe_products from anon;
83 |
84 | -- grant execute permission to a specific role only
85 | grant execute on function public.get_stripe_products to authenticated;
86 | ```
87 |
--------------------------------------------------------------------------------
/docs/guides/updating-wrappers.md:
--------------------------------------------------------------------------------
1 | # Updating Foreign Data Wrappers
2 |
3 | This guide explains how to update foreign data wrappers in your PostgreSQL database, with special focus on WebAssembly (WASM) wrappers.
4 |
5 | ## Types of Updates
6 |
7 | There are two main types of updates for foreign data wrappers:
8 |
9 | 1. **Native wrapper updates**: Updates to native Postgres extensions
10 | 2. **WASM wrapper updates**: Updates to WebAssembly-based wrappers
11 |
12 | ## Updating WASM Foreign Data Wrappers
13 |
14 | WASM wrappers can be updated without reinstalling the entire extension, by modifying the foreign server configuration.
15 |
16 | ### Step-by-Step Update Process
17 |
18 | #### 1. Update the Foreign Server
19 |
20 | To update a WASM wrapper, you need to update the URL, version, and checksum:
21 |
22 | ```sql
23 | alter server slack_server options (
24 | set fdw_package_url 'https://github.com/supabase/wrappers/releases/download/....',
25 | set fdw_package_version '0.0.1',
26 | set fdw_package_checksum 'new-checksum-here'
27 | );
28 | ```
29 |
30 | **Important options:**
31 |
32 | - `fdw_package_url`: The URL to the new version of the WASM package
33 | - `fdw_package_version`: The new version number
34 | - `fdw_package_checksum`: The checksum of the new version for verification
35 |
36 | #### 3. Verify the Update
37 |
38 | You can verify that the server has been updated by querying it again:
39 |
40 | ```sql
41 | select option_name, option_value
42 | from pg_options_to_table(
43 | (select srvoptions from pg_foreign_server where srvname = 'slack_server')
44 | );
45 | ```
46 |
47 | You can change `srvname` to the name of the FDW when you installed it.
48 |
49 | ## Updating Native Foreign Data Wrappers
50 |
51 | This is valid if your version of Postgres has multiple versions of the FDW installed.
52 |
53 | ```sql
54 | alter extension wrappers update;
55 | ```
56 |
57 | ## Backward Compatibility Considerations
58 |
59 | When updating wrappers, consider the following:
60 |
61 | 1. **Schema changes**: Some updates might change the schema of foreign tables, requiring you to recreate them
62 | 2. **API changes**: The wrapper might interact with a different version of an external API
63 | 3. **Connection parameters**: New parameters might be required or old ones deprecated
64 |
65 | Always check the release notes for any breaking changes before updating.
66 |
67 | ## Troubleshooting
68 |
69 | If you encounter issues after updating:
70 |
71 | 1. **Check server logs**: PostgreSQL logs may contain error messages
72 | 2. **Verify options**: Make sure all required options are set correctly
73 | 3. **Test with a simple query**: Start with basic queries to isolate problems
--------------------------------------------------------------------------------
/docs/guides/usage-statistics.md:
--------------------------------------------------------------------------------
1 | # FDW Usage Statistics
2 |
3 | Quantitative metrics are useful when working with Postgres FDWs because of their impact on performance optimization, monitoring, and query planning across distributed databases.
4 |
5 | You can use the FDW statistics to identify bottlenecks, latency issues, and inefficiencies in data retrieval.
6 |
7 | ## Querying FDW Statistics
8 |
9 | ```sql
10 | select *
11 | from extensions.wrappers_fdw_stats;
12 | ```
13 |
14 | ## Statistics Reference
15 |
16 | - `create_times` - number of times the FDW instance has been created
17 | - `rows_in` - number of rows transferred from source
18 | - `rows_out` - number of rows transferred to source
19 | - `bytes_in` - number of bytes transferred from source
20 | - `bytes_out` - number of bytes transferred to source
21 | - `metadata` - additional usage statistics specific to a FDW
22 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | hide:
3 | - navigation
4 | ---
5 |
6 | # Postgres Wrappers
7 |
8 | Wrappers is a Rust framework for developing PostgreSQL Foreign Data Wrappers.
9 |
10 | ## What is a Foreign Data Wrapper?
11 |
12 | Foreign Data Wrappers (FDW) are a core feature of Postgres that allow you to access and query data stored in external data sources as if they were native Postgres tables.
13 |
14 | Postgres includes several built-in foreign data wrappers, such as `postgres_fdw` for accessing other PostgreSQL databases, and `file_fdw` for reading data from files.
15 |
16 | ## The Wrappers Framework
17 |
18 | The Wrappers framework extends the Postgres FDW feature. You can use it to query other databases or any other external systems. For example, developers can use the Stripe wrapper to query Stripe data and join the data with customer data inside Postgres:
19 |
20 | ```sql
21 | select
22 | customer_id,
23 | name
24 | from
25 | stripe.customers;
26 | ```
27 |
28 | returns
29 |
30 | ```
31 | customer_id | name
32 | --------------------+-----------
33 | cus_NffrFeUfNV2Hib | Jenny Rosen
34 | (1 row)
35 | ```
36 |
37 | ## Concepts
38 |
39 | Postgres FDWs introduce the concept of a "remote server" and "foreign table":
40 |
41 | 
42 |
43 | ### Remote servers
44 |
45 | A Remote Server is an external database, API, or any system containing data that you want to query from your Postgres database. Examples include:
46 |
47 | - An external database, like Postgres or Firebase.
48 | - A remote data warehouse, like ClickHouse, BigQuery, or Snowflake.
49 | - An API, like Stripe or GitHub.
50 |
51 | It's possible to connect to multiple remote servers of the same type. For example, you can connect to two different Firebase projects within the same Postgres database.
52 |
53 | ### Foreign tables
54 |
55 | A table in your database which maps to some data inside a Remote Server.
56 |
57 | Examples:
58 |
59 | - An `analytics` table which maps to a table inside your data warehouse.
60 | - A `subscriptions` table which maps to your Stripe subscriptions.
61 | - A `collections` table which maps to a Firebase collection.
62 |
63 | Although a foreign table behaves like any other table, the data is not stored inside your database. The data remains inside the Remote Server.
64 |
65 | ## Supported platforms
66 |
67 | The following Postgres providers support Wrappers:
68 |
69 | - [supabase.com](https://supabase.com)
70 |
--------------------------------------------------------------------------------
/docs/requirements_docs.txt:
--------------------------------------------------------------------------------
1 | mkdocs
2 | mkdocs-material
3 |
--------------------------------------------------------------------------------
/docs/stylesheets/extra.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap');
2 |
3 | :root {
4 | --md-text-font: "IBM Plex Sans", "Roboto", sans-serif;
5 | /* color: red; */
6 | }
7 |
8 | [data-md-color-scheme="slate"] {
9 | --md-default-bg-color:#121212;
10 | --md-default-fg-color--light: white;
11 | --md-code-bg-color: #2a2929;
12 | --md-code-hl-keyword-color: #569cd6;
13 | }
14 |
15 | .md-header, .md-tabs {
16 | background-color: var(--md-default-bg-color);
17 | color: var(--md-default-fg-color--light);
18 | font-family: var(--md-text-font);
19 | }
--------------------------------------------------------------------------------
/docs/tags.md:
--------------------------------------------------------------------------------
1 | ---
2 | hide:
3 | - navigation
4 | ---
5 |
6 | # Catalog
7 |
8 | Following is a list of relevant tags:
9 |
10 |
11 |
--------------------------------------------------------------------------------
/mkdocs.yaml:
--------------------------------------------------------------------------------
1 | site_name: Wrappers
2 | site_url: https://supabase.github.io/wrappers
3 | site_description: A PostgreSQL extension for connecting to external data sources
4 | copyright: Copyright © Supabase
5 | repo_name: supabase/wrappers
6 | repo_url: https://github.com/supabase/wrappers
7 | edit_uri: edit/main/docs/
8 |
9 | not_in_nav: |
10 | tags.md
11 |
12 | nav:
13 | - Home:
14 | - "index.md"
15 | - Catalog:
16 | - catalog/index.md
17 | - Native:
18 | - Airtable: "catalog/airtable.md"
19 | - Auth0: "catalog/auth0.md"
20 | - AWS Cognito: "catalog/cognito.md"
21 | - BigQuery: "catalog/bigquery.md"
22 | - ClickHouse: "catalog/clickhouse.md"
23 | - Firebase: "catalog/firebase.md"
24 | - Iceberg : "catalog/iceberg.md"
25 | - Logflare: "catalog/logflare.md"
26 | - Redis: "catalog/redis.md"
27 | - S3 (CSV, JSON, Parquet): "catalog/s3.md"
28 | - Stripe: "catalog/stripe.md"
29 | - SQL Server: "catalog/mssql.md"
30 | - Wasm:
31 | - catalog/wasm/index.md
32 | - Cal.com: "catalog/cal.md"
33 | - Calendly: "catalog/calendly.md"
34 | - Clerk: "catalog/clerk.md"
35 | - Cloudflare D1: "catalog/cfd1.md"
36 | - HubSpot: "catalog/hubspot.md"
37 | - Notion: "catalog/notion.md"
38 | - Orb: "catalog/orb.md"
39 | - Paddle: "catalog/paddle.md"
40 | - Slack: "catalog/slack.md"
41 | - Snowflake: "catalog/snowflake.md"
42 | - Guides:
43 | - Native vs Wasm Wrappers: "guides/native-wasm.md"
44 | - Query Pushdown: "guides/query-pushdown.md"
45 | - Remote Subqueries: "guides/remote-subqueries.md"
46 | - Security: "guides/security.md"
47 | - FDW Statistics: "guides/usage-statistics.md"
48 | - Installing Wrappers in Postgres: "guides/installation.md"
49 | - Updating Foreign Data Wrappers: "guides/updating-wrappers.md"
50 | - Removing Foreign Data Wrappers: "guides/removing-wrappers.md"
51 | - Limitations: "guides/limitations.md"
52 | - Contributing:
53 | - Building the Docs: "contributing/documentation.md"
54 | - Core Development: "contributing/core.md"
55 | - Developing a Native Wrapper:
56 | - Quick Start: "contributing/native.md"
57 | - Developing a Wasm Wrapper:
58 | - Quick Start: "guides/create-wasm-wrapper.md"
59 | - Advanced guide: "guides/wasm-advanced.md"
60 |
61 | plugins:
62 | - tags:
63 | tags_file: tags.md
64 |
65 | theme:
66 | name: "material"
67 | favicon: "assets/favicon.ico"
68 | logo: "assets/wrappers.svg"
69 | homepage: https://supabase.github.io/wrappers
70 | features:
71 | - content.code.annotate
72 | - content.code.copy
73 | - navigation.expand
74 | - navigation.tabs
75 | - navigation.tabs.sticky
76 | - navigation.sections
77 | - navigation.indexes
78 | - navigation.footer
79 | palette:
80 | scheme: slate
81 | primary: green
82 | accent: green
83 |
84 | extra_css:
85 | - stylesheets/extra.css
86 |
87 | extra:
88 | social:
89 | - icon: fontawesome/brands/twitter
90 | link: https://x.com/supabase
91 | name: Supabase on Twitter
92 | - icon: fontawesome/brands/reddit
93 | link: https://reddit.com/r/supabase
94 | name: Supabase on Reddit
95 | - icon: fontawesome/brands/github
96 | link: https://github.com/supabase/wrappers
97 | name: Wrappers on GitHub
98 |
99 | markdown_extensions:
100 | - attr_list
101 | - md_in_html
102 | - pymdownx.highlight:
103 | linenums: true
104 | guess_lang: false
105 | use_pygments: true
106 | pygments_style: default
107 | - pymdownx.superfences
108 | - pymdownx.tabbed:
109 | alternate_style: true
110 | - pymdownx.snippets
111 | - pymdownx.tasklist
112 | - admonition
113 | - pymdownx.emoji:
114 | emoji_index: !!python/name:material.extensions.emoji.twemoji
115 | emoji_generator: !!python/name:material.extensions.emoji.to_svg
116 |
--------------------------------------------------------------------------------
/supabase-wrappers-macros/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "supabase-wrappers-macros"
3 | version = "0.1.18"
4 | authors = ["Supabase Inc. https://supabase.com/"]
5 | license = "Apache-2.0"
6 | description = "Postgres Foreign Data Wrapper development framework macros for supabase-wrappers"
7 | homepage = "https://github.com/supabase/wrappers/tree/main/supabase-wrappers-macros"
8 | repository = "https://github.com/supabase/wrappers/tree/main/supabase-wrappers-macros"
9 | categories = ["database"]
10 | keywords = ["database", "postgres", "postgresql", "extension"]
11 | edition = { workspace = true }
12 | rust-version = { workspace = true }
13 |
14 | [dependencies]
15 | syn = { version = "1.0", features = ["full"] }
16 | quote = "1.0"
17 | proc-macro2 = "1.0"
18 |
19 | [lib]
20 | proc-macro = true
21 |
--------------------------------------------------------------------------------
/supabase-wrappers-macros/README.md:
--------------------------------------------------------------------------------
1 | # supabase-wrappers-macros
2 |
3 | This crate is to provide helper macros for `Wrappers` and NOT supposed to be used directly, please use [supabase-wrappers](https://github.com/supabase/wrappers/tree/main/supabase-wrappers) instead.
4 |
--------------------------------------------------------------------------------
/supabase-wrappers-macros/rustfmt.toml:
--------------------------------------------------------------------------------
1 | max_width = 100
2 |
--------------------------------------------------------------------------------
/supabase-wrappers/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "supabase-wrappers"
3 | version = "0.1.22"
4 | authors = ["Supabase Inc. https://supabase.com/"]
5 | license = "Apache-2.0"
6 | description = "Postgres Foreign Data Wrapper development framework in Rust."
7 | homepage = "https://github.com/supabase/wrappers/tree/main/supabase-wrappers"
8 | repository = "https://github.com/supabase/wrappers/tree/main/supabase-wrappers"
9 | categories = ["database"]
10 | keywords = ["database", "postgres", "postgresql", "extension"]
11 | edition = { workspace = true }
12 | rust-version = { workspace = true }
13 |
14 | [lints.rust]
15 | unexpected_cfgs = { level = "warn", check-cfg = ['cfg(pgrx_embed)'] }
16 |
17 | [features]
18 | default = ["pg15"]
19 | pg13 = ["pgrx/pg13", "pgrx-tests/pg13"]
20 | pg14 = ["pgrx/pg14", "pgrx-tests/pg14"]
21 | pg15 = ["pgrx/pg15", "pgrx-tests/pg15"]
22 | pg16 = ["pgrx/pg16", "pgrx-tests/pg16"]
23 | pg17 = ["pgrx/pg17", "pgrx-tests/pg17"]
24 | pg_test = []
25 |
26 | [dependencies]
27 | pgrx = { version = "=0.14.3", default-features = false }
28 | thiserror = "1.0.63"
29 | tokio = { version = "1.43", features = ["rt", "net"] }
30 | uuid = { version = "1.10.0" }
31 | supabase-wrappers-macros = { version = "0.1", path = "../supabase-wrappers-macros" }
32 |
33 | [dev-dependencies]
34 | pgrx-tests = "=0.14.3"
35 |
36 | [package.metadata.docs.rs]
37 | features = ["pg15"]
38 | no-default-features = true
39 | # Enable `#[cfg(docsrs)]` (https://docs.rs/about/builds#cross-compiling)
40 | rustc-args = ["--cfg", "docsrs"]
41 |
--------------------------------------------------------------------------------
/supabase-wrappers/rustfmt.toml:
--------------------------------------------------------------------------------
1 | max_width = 100
2 |
--------------------------------------------------------------------------------
/supabase-wrappers/src/instance.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 | use std::ffi::CStr;
3 |
4 | use crate::prelude::*;
5 | use pgrx::pg_sys::panic::ErrorReport;
6 | use pgrx::prelude::*;
7 |
8 | #[derive(Debug, Clone, Default)]
9 | pub struct ForeignServer {
10 | pub server_name: String,
11 | pub server_type: Option,
12 | pub server_version: Option,
13 | pub options: HashMap,
14 | }
15 |
16 | // create a fdw instance from its id
17 | pub(super) unsafe fn create_fdw_instance_from_server_id<
18 | E: Into,
19 | W: ForeignDataWrapper,
20 | >(
21 | fserver_id: pg_sys::Oid,
22 | ) -> W {
23 | let to_string = |raw: *mut std::ffi::c_char| -> Option {
24 | if raw.is_null() {
25 | return None;
26 | }
27 | let c_str = CStr::from_ptr(raw);
28 | let value = c_str
29 | .to_str()
30 | .map_err(|_| {
31 | OptionsError::OptionValueIsInvalidUtf8(
32 | String::from_utf8_lossy(c_str.to_bytes()).to_string(),
33 | )
34 | })
35 | .report_unwrap()
36 | .to_string();
37 | Some(value)
38 | };
39 | let fserver = pg_sys::GetForeignServer(fserver_id);
40 | let server = ForeignServer {
41 | server_name: to_string((*fserver).servername).unwrap(),
42 | server_type: to_string((*fserver).servertype),
43 | server_version: to_string((*fserver).serverversion),
44 | options: options_to_hashmap((*fserver).options).report_unwrap(),
45 | };
46 | let wrapper = W::new(server);
47 | wrapper.report_unwrap()
48 | }
49 |
50 | // create a fdw instance from a foreign table id
51 | pub(super) unsafe fn create_fdw_instance_from_table_id<
52 | E: Into,
53 | W: ForeignDataWrapper,
54 | >(
55 | ftable_id: pg_sys::Oid,
56 | ) -> W {
57 | let ftable = pg_sys::GetForeignTable(ftable_id);
58 | create_fdw_instance_from_server_id((*ftable).serverid)
59 | }
60 |
--------------------------------------------------------------------------------
/supabase-wrappers/src/limit.rs:
--------------------------------------------------------------------------------
1 | use crate::interface::Limit;
2 | use pgrx::{is_a, pg_sys, FromDatum};
3 |
4 | // extract limit
5 | pub(crate) unsafe fn extract_limit(
6 | root: *mut pg_sys::PlannerInfo,
7 | _baserel: *mut pg_sys::RelOptInfo,
8 | _baserel_id: pg_sys::Oid,
9 | ) -> Option {
10 | let parse = (*root).parse;
11 |
12 | // don't push down LIMIT if the query has a GROUP BY clause or aggregates
13 | if !(*parse).groupClause.is_null() || (*parse).hasAggs {
14 | return None;
15 | }
16 |
17 | // only push down constant LIMITs that are not NULL
18 | let limit_count = (*parse).limitCount as *mut pg_sys::Const;
19 | if limit_count.is_null() || !is_a(limit_count as *mut pg_sys::Node, pg_sys::NodeTag::T_Const) {
20 | return None;
21 | }
22 |
23 | let mut limit = Limit::default();
24 |
25 | if let Some(count) = i64::from_polymorphic_datum(
26 | (*limit_count).constvalue,
27 | (*limit_count).constisnull,
28 | (*limit_count).consttype,
29 | ) {
30 | limit.count = count;
31 | } else {
32 | return None;
33 | }
34 |
35 | // only consider OFFSETS that are non-NULL constants
36 | let limit_offset = (*parse).limitOffset as *mut pg_sys::Const;
37 | if !limit_offset.is_null() && is_a(limit_offset as *mut pg_sys::Node, pg_sys::NodeTag::T_Const)
38 | {
39 | if let Some(offset) = i64::from_polymorphic_datum(
40 | (*limit_offset).constvalue,
41 | (*limit_offset).constisnull,
42 | (*limit_offset).consttype,
43 | ) {
44 | limit.offset = offset;
45 | }
46 | }
47 |
48 | Some(limit)
49 | }
50 |
--------------------------------------------------------------------------------
/supabase-wrappers/src/memctx.rs:
--------------------------------------------------------------------------------
1 | //! Helper functions for Wrappers Memory Context management
2 | //!
3 |
4 | use pgrx::{memcxt::PgMemoryContexts, pg_sys::AsPgCStr, prelude::*};
5 |
6 | // Wrappers root memory context name
7 | const ROOT_MEMCTX_NAME: &str = "WrappersRootMemCtx";
8 |
9 | // search memory context by name under specified MemoryContext
10 | unsafe fn find_memctx_under(name: &str, under: PgMemoryContexts) -> Option {
11 | let mut ctx = (*under.value()).firstchild;
12 | while !ctx.is_null() {
13 | if let Ok(ctx_name) = std::ffi::CStr::from_ptr((*ctx).name).to_str() {
14 | if ctx_name == name {
15 | return Some(PgMemoryContexts::For(ctx));
16 | }
17 | }
18 | ctx = (*ctx).nextchild;
19 | }
20 | None
21 | }
22 |
23 | // search for root memory context under CacheMemoryContext, create a new one if not exists
24 | unsafe fn ensure_root_wrappers_memctx() -> PgMemoryContexts {
25 | find_memctx_under(ROOT_MEMCTX_NAME, PgMemoryContexts::CacheMemoryContext).unwrap_or_else(|| {
26 | let name = PgMemoryContexts::CacheMemoryContext.pstrdup(ROOT_MEMCTX_NAME);
27 | let ctx = pg_sys::AllocSetContextCreateExtended(
28 | PgMemoryContexts::CacheMemoryContext.value(),
29 | name,
30 | pg_sys::ALLOCSET_DEFAULT_MINSIZE as usize,
31 | pg_sys::ALLOCSET_DEFAULT_INITSIZE as usize,
32 | pg_sys::ALLOCSET_DEFAULT_MAXSIZE as usize,
33 | );
34 | PgMemoryContexts::For(ctx)
35 | })
36 | }
37 |
38 | // search Wrappers memory context by name, reset it if exists otherwise create a new one
39 | pub(super) unsafe fn refresh_wrappers_memctx(name: &str) -> PgMemoryContexts {
40 | let mut root = ensure_root_wrappers_memctx();
41 | find_memctx_under(name, PgMemoryContexts::For(root.value()))
42 | .map(|mut ctx| {
43 | ctx.reset();
44 | ctx
45 | })
46 | .unwrap_or_else(|| {
47 | let name = root.switch_to(|_| name.as_pg_cstr());
48 | let ctx = pg_sys::AllocSetContextCreateExtended(
49 | root.value(),
50 | name,
51 | pg_sys::ALLOCSET_DEFAULT_MINSIZE as usize,
52 | pg_sys::ALLOCSET_DEFAULT_INITSIZE as usize,
53 | pg_sys::ALLOCSET_DEFAULT_MAXSIZE as usize,
54 | );
55 | PgMemoryContexts::For(ctx)
56 | })
57 | }
58 |
--------------------------------------------------------------------------------
/supabase-wrappers/src/polyfill.rs:
--------------------------------------------------------------------------------
1 | use pgrx::pg_sys::Datum;
2 | use pgrx::prelude::*;
3 | use std::os::raw::c_int;
4 | use std::slice;
5 |
6 | // ExecClearTuple
7 | pub(super) unsafe fn exec_clear_tuple(slot: *mut pg_sys::TupleTableSlot) {
8 | if let Some(clear) = (*(*slot).tts_ops).clear {
9 | clear(slot);
10 | }
11 | }
12 |
13 | // fetch one attribute of the slot's contents.
14 | pub(super) unsafe fn slot_getattr(
15 | slot: *mut pg_sys::TupleTableSlot,
16 | attnum: c_int,
17 | isnull: *mut bool,
18 | ) -> Datum {
19 | assert!(attnum > 0);
20 |
21 | if attnum > (*slot).tts_nvalid.into() {
22 | pg_sys::slot_getsomeattrs_int(slot, attnum);
23 | }
24 |
25 | let attnum = attnum as usize;
26 | let values = slice::from_raw_parts((*slot).tts_values, attnum);
27 | let nulls = slice::from_raw_parts((*slot).tts_isnull, attnum);
28 |
29 | *isnull = nulls[attnum - 1];
30 | values[attnum - 1]
31 | }
32 |
33 | #[cfg(not(feature = "pg13"))]
34 | #[inline]
35 | pub(super) unsafe fn outer_plan_state(node: *mut pg_sys::PlanState) -> *mut pg_sys::PlanState {
36 | (*node).lefttree
37 | }
38 |
--------------------------------------------------------------------------------
/supabase-wrappers/src/sort.rs:
--------------------------------------------------------------------------------
1 | use crate::interface::Sort;
2 | use pgrx::list::List;
3 | use pgrx::{is_a, pg_sys};
4 | use std::ffi::c_void;
5 | use std::ffi::CStr;
6 |
7 | pub(crate) unsafe fn create_sort(
8 | pathkey: *mut pg_sys::PathKey,
9 | var: *mut pg_sys::Var,
10 | baserel_id: pg_sys::Oid,
11 | ) -> Option {
12 | let attno = (*var).varattno;
13 | let attname = pg_sys::get_attname(baserel_id, attno, true);
14 | if !attname.is_null() {
15 | let sort = Sort {
16 | field: CStr::from_ptr(attname).to_str().unwrap().to_owned(),
17 | field_no: attno as usize,
18 | reversed: (*pathkey).pk_strategy as u32 == pg_sys::BTGreaterStrategyNumber,
19 | nulls_first: (*pathkey).pk_nulls_first,
20 | ..Default::default()
21 | };
22 | return Some(sort);
23 | }
24 | None
25 | }
26 |
27 | // extract sorts
28 | pub(crate) unsafe fn extract_sorts(
29 | root: *mut pg_sys::PlannerInfo,
30 | baserel: *mut pg_sys::RelOptInfo,
31 | baserel_id: pg_sys::Oid,
32 | ) -> Vec {
33 | pgrx::memcx::current_context(|mcx| {
34 | let mut ret = Vec::new();
35 |
36 | if let Some(pathkeys) =
37 | List::<*mut c_void>::downcast_ptr_in_memcx((*root).query_pathkeys, mcx)
38 | {
39 | for pathkey in pathkeys.iter() {
40 | let ec = (*(*pathkey as *mut pg_sys::PathKey)).pk_eclass;
41 |
42 | if (*ec).ec_has_volatile {
43 | continue;
44 | }
45 |
46 | if let Some(ems) = List::<*mut c_void>::downcast_ptr_in_memcx((*ec).ec_members, mcx)
47 | {
48 | ems.iter()
49 | .find(|em| {
50 | pg_sys::bms_equal(
51 | (*(**em as *mut pg_sys::EquivalenceMember)).em_relids,
52 | (*baserel).relids,
53 | )
54 | })
55 | .and_then(|em| {
56 | let expr = (*(*em as *mut pg_sys::EquivalenceMember)).em_expr
57 | as *mut pg_sys::Node;
58 |
59 | if is_a(expr, pg_sys::NodeTag::T_Var) {
60 | let var = expr as *mut pg_sys::Var;
61 | let sort = create_sort(*pathkey as _, var, baserel_id);
62 | if let Some(sort) = sort {
63 | ret.push(sort);
64 | }
65 | } else if is_a(expr, pg_sys::NodeTag::T_RelabelType) {
66 | // ORDER BY clauses having a COLLATE option will be RelabelType
67 | let expr = expr as *mut pg_sys::RelabelType;
68 | let var = (*expr).arg as *mut pg_sys::Var;
69 | if is_a(var as *mut pg_sys::Node, pg_sys::NodeTag::T_Var) {
70 | let sort = create_sort(*pathkey as _, var, baserel_id);
71 | if let Some(mut sort) = sort {
72 | let coll_id = (*expr).resultcollid;
73 | sort.collate = Some(
74 | CStr::from_ptr(pg_sys::get_collation_name(coll_id))
75 | .to_str()
76 | .unwrap()
77 | .to_owned(),
78 | );
79 | ret.push(sort);
80 | }
81 | }
82 | }
83 |
84 | None::<()>
85 | });
86 | }
87 | }
88 | }
89 |
90 | ret
91 | })
92 | }
93 |
--------------------------------------------------------------------------------
/wasm-wrappers/.cargo/config.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | target = "wasm32-unknown-unknown"
3 |
4 | [profile.release]
5 | strip = "debuginfo"
6 | lto = true
7 |
--------------------------------------------------------------------------------
/wasm-wrappers/fdw/.gitignore:
--------------------------------------------------------------------------------
1 | **/src/bindings.rs
2 |
--------------------------------------------------------------------------------
/wasm-wrappers/fdw/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = [
3 | "cal_fdw",
4 | "calendly_fdw",
5 | "cfd1_fdw",
6 | "clerk_fdw",
7 | "helloworld_fdw",
8 | "hubspot_fdw",
9 | "notion_fdw",
10 | "orb_fdw",
11 | "paddle_fdw",
12 | "slack_fdw",
13 | "snowflake_fdw",
14 | ]
15 | resolver = "2"
16 |
17 | [workspace.package]
18 | edition = "2021"
19 | rust-version = "1.85"
20 | homepage = "https://github.com/supabase/wrappers/tree/main/wasm-wrappers"
21 | repository = "https://github.com/supabase/wrappers/tree/main/wasm-wrappers"
22 |
--------------------------------------------------------------------------------
/wasm-wrappers/fdw/cal_fdw/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "cal_fdw"
3 | version = "0.2.0"
4 | edition = { workspace = true }
5 | homepage = { workspace = true }
6 | rust-version = { workspace = true }
7 |
8 | [lib]
9 | crate-type = ["cdylib"]
10 |
11 | [dependencies]
12 | wit-bindgen-rt = "0.41.0"
13 | serde_json = "1.0"
14 | chrono = "0.4.38"
15 |
16 | [package.metadata.component]
17 | package = "supabase:cal-fdw"
18 |
19 | [package.metadata.component.dependencies]
20 |
21 | [package.metadata.component.target]
22 | path = "wit"
23 |
24 | [package.metadata.component.target.dependencies]
25 | "supabase:wrappers" = { path = "../../wit/v2" }
26 |
--------------------------------------------------------------------------------
/wasm-wrappers/fdw/cal_fdw/wit/world.wit:
--------------------------------------------------------------------------------
1 | package supabase:cal-fdw@0.2.0;
2 |
3 | world cal {
4 | import supabase:wrappers/http@0.2.0;
5 | import supabase:wrappers/jwt@0.2.0;
6 | import supabase:wrappers/stats@0.2.0;
7 | import supabase:wrappers/time@0.2.0;
8 | import supabase:wrappers/utils@0.2.0;
9 | export supabase:wrappers/routines@0.2.0;
10 | }
11 |
--------------------------------------------------------------------------------
/wasm-wrappers/fdw/calendly_fdw/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "calendly_fdw"
3 | version = "0.2.0"
4 | edition = { workspace = true }
5 | homepage = { workspace = true }
6 | rust-version = { workspace = true }
7 |
8 | [lib]
9 | crate-type = ["cdylib"]
10 |
11 | [dependencies]
12 | wit-bindgen-rt = "0.41.0"
13 | serde_json = "1.0"
14 | chrono = "0.4.38"
15 |
16 | [package.metadata.component]
17 | package = "supabase:calendly-fdw"
18 |
19 | [package.metadata.component.dependencies]
20 |
21 | [package.metadata.component.target]
22 | path = "wit"
23 |
24 | [package.metadata.component.target.dependencies]
25 | "supabase:wrappers" = { path = "../../wit/v2" }
26 |
--------------------------------------------------------------------------------
/wasm-wrappers/fdw/calendly_fdw/wit/world.wit:
--------------------------------------------------------------------------------
1 | package supabase:calendly-fdw@0.2.0;
2 |
3 | world calendly {
4 | import supabase:wrappers/http@0.2.0;
5 | import supabase:wrappers/jwt@0.2.0;
6 | import supabase:wrappers/stats@0.2.0;
7 | import supabase:wrappers/time@0.2.0;
8 | import supabase:wrappers/utils@0.2.0;
9 | export supabase:wrappers/routines@0.2.0;
10 | }
11 |
--------------------------------------------------------------------------------
/wasm-wrappers/fdw/cfd1_fdw/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 4
4 |
5 | [[package]]
6 | name = "cfd1_fdw"
7 | version = "0.2.0"
8 | dependencies = [
9 | "serde_json",
10 | "wit-bindgen-rt",
11 | ]
12 |
13 | [[package]]
14 | name = "itoa"
15 | version = "1.0.14"
16 | source = "registry+https://github.com/rust-lang/crates.io-index"
17 | checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
18 |
19 | [[package]]
20 | name = "memchr"
21 | version = "2.7.4"
22 | source = "registry+https://github.com/rust-lang/crates.io-index"
23 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
24 |
25 | [[package]]
26 | name = "proc-macro2"
27 | version = "1.0.92"
28 | source = "registry+https://github.com/rust-lang/crates.io-index"
29 | checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
30 | dependencies = [
31 | "unicode-ident",
32 | ]
33 |
34 | [[package]]
35 | name = "quote"
36 | version = "1.0.37"
37 | source = "registry+https://github.com/rust-lang/crates.io-index"
38 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
39 | dependencies = [
40 | "proc-macro2",
41 | ]
42 |
43 | [[package]]
44 | name = "ryu"
45 | version = "1.0.18"
46 | source = "registry+https://github.com/rust-lang/crates.io-index"
47 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
48 |
49 | [[package]]
50 | name = "serde"
51 | version = "1.0.216"
52 | source = "registry+https://github.com/rust-lang/crates.io-index"
53 | checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
54 | dependencies = [
55 | "serde_derive",
56 | ]
57 |
58 | [[package]]
59 | name = "serde_derive"
60 | version = "1.0.216"
61 | source = "registry+https://github.com/rust-lang/crates.io-index"
62 | checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
63 | dependencies = [
64 | "proc-macro2",
65 | "quote",
66 | "syn",
67 | ]
68 |
69 | [[package]]
70 | name = "serde_json"
71 | version = "1.0.133"
72 | source = "registry+https://github.com/rust-lang/crates.io-index"
73 | checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
74 | dependencies = [
75 | "itoa",
76 | "memchr",
77 | "ryu",
78 | "serde",
79 | ]
80 |
81 | [[package]]
82 | name = "syn"
83 | version = "2.0.90"
84 | source = "registry+https://github.com/rust-lang/crates.io-index"
85 | checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
86 | dependencies = [
87 | "proc-macro2",
88 | "quote",
89 | "unicode-ident",
90 | ]
91 |
92 | [[package]]
93 | name = "unicode-ident"
94 | version = "1.0.14"
95 | source = "registry+https://github.com/rust-lang/crates.io-index"
96 | checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
97 |
98 | [[package]]
99 | name = "wit-bindgen-rt"
100 | version = "0.41.0"
101 | source = "registry+https://github.com/rust-lang/crates.io-index"
102 | checksum = "c4db52a11d4dfb0a59f194c064055794ee6564eb1ced88c25da2cf76e50c5621"
103 |
--------------------------------------------------------------------------------
/wasm-wrappers/fdw/cfd1_fdw/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "cfd1_fdw"
3 | version = "0.2.0"
4 | edition = { workspace = true }
5 | homepage = { workspace = true }
6 | rust-version = { workspace = true }
7 |
8 | [lib]
9 | crate-type = ["cdylib"]
10 |
11 | [dependencies]
12 | wit-bindgen-rt = "0.41.0"
13 | serde_json = "1.0"
14 |
15 | [package.metadata.component]
16 | package = "supabase:cfd1-fdw"
17 |
18 | [package.metadata.component.dependencies]
19 |
20 | [package.metadata.component.target]
21 | path = "wit"
22 |
23 | [package.metadata.component.target.dependencies]
24 | "supabase:wrappers" = { path = "../../wit/v2" }
25 |
--------------------------------------------------------------------------------
/wasm-wrappers/fdw/cfd1_fdw/wit/world.wit:
--------------------------------------------------------------------------------
1 | package supabase:cfd1-fdw@0.2.0;
2 |
3 | world cfd1 {
4 | import supabase:wrappers/http@0.2.0;
5 | import supabase:wrappers/jwt@0.2.0;
6 | import supabase:wrappers/stats@0.2.0;
7 | import supabase:wrappers/time@0.2.0;
8 | import supabase:wrappers/utils@0.2.0;
9 | export supabase:wrappers/routines@0.2.0;
10 | }
11 |
--------------------------------------------------------------------------------
/wasm-wrappers/fdw/clerk_fdw/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 4
4 |
5 | [[package]]
6 | name = "clerk_fdw"
7 | version = "0.2.0"
8 | dependencies = [
9 | "serde_json",
10 | "wit-bindgen-rt",
11 | ]
12 |
13 | [[package]]
14 | name = "itoa"
15 | version = "1.0.14"
16 | source = "registry+https://github.com/rust-lang/crates.io-index"
17 | checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
18 |
19 | [[package]]
20 | name = "memchr"
21 | version = "2.7.4"
22 | source = "registry+https://github.com/rust-lang/crates.io-index"
23 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
24 |
25 | [[package]]
26 | name = "proc-macro2"
27 | version = "1.0.93"
28 | source = "registry+https://github.com/rust-lang/crates.io-index"
29 | checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
30 | dependencies = [
31 | "unicode-ident",
32 | ]
33 |
34 | [[package]]
35 | name = "quote"
36 | version = "1.0.38"
37 | source = "registry+https://github.com/rust-lang/crates.io-index"
38 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
39 | dependencies = [
40 | "proc-macro2",
41 | ]
42 |
43 | [[package]]
44 | name = "ryu"
45 | version = "1.0.18"
46 | source = "registry+https://github.com/rust-lang/crates.io-index"
47 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
48 |
49 | [[package]]
50 | name = "serde"
51 | version = "1.0.217"
52 | source = "registry+https://github.com/rust-lang/crates.io-index"
53 | checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
54 | dependencies = [
55 | "serde_derive",
56 | ]
57 |
58 | [[package]]
59 | name = "serde_derive"
60 | version = "1.0.217"
61 | source = "registry+https://github.com/rust-lang/crates.io-index"
62 | checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
63 | dependencies = [
64 | "proc-macro2",
65 | "quote",
66 | "syn",
67 | ]
68 |
69 | [[package]]
70 | name = "serde_json"
71 | version = "1.0.137"
72 | source = "registry+https://github.com/rust-lang/crates.io-index"
73 | checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b"
74 | dependencies = [
75 | "itoa",
76 | "memchr",
77 | "ryu",
78 | "serde",
79 | ]
80 |
81 | [[package]]
82 | name = "syn"
83 | version = "2.0.96"
84 | source = "registry+https://github.com/rust-lang/crates.io-index"
85 | checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
86 | dependencies = [
87 | "proc-macro2",
88 | "quote",
89 | "unicode-ident",
90 | ]
91 |
92 | [[package]]
93 | name = "unicode-ident"
94 | version = "1.0.14"
95 | source = "registry+https://github.com/rust-lang/crates.io-index"
96 | checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
97 |
98 | [[package]]
99 | name = "wit-bindgen-rt"
100 | version = "0.41.0"
101 | source = "registry+https://github.com/rust-lang/crates.io-index"
102 | checksum = "c4db52a11d4dfb0a59f194c064055794ee6564eb1ced88c25da2cf76e50c5621"
103 |
--------------------------------------------------------------------------------
/wasm-wrappers/fdw/clerk_fdw/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "clerk_fdw"
3 | version = "0.2.0"
4 | edition = { workspace = true }
5 | homepage = { workspace = true }
6 | rust-version = { workspace = true }
7 |
8 | [lib]
9 | crate-type = ["cdylib"]
10 |
11 | [dependencies]
12 | wit-bindgen-rt = "0.41.0"
13 | serde_json = "1.0"
14 |
15 | [package.metadata.component]
16 | package = "supabase:clerk-fdw"
17 |
18 | [package.metadata.component.dependencies]
19 |
20 | [package.metadata.component.target]
21 | path = "wit"
22 |
23 | [package.metadata.component.target.dependencies]
24 | "supabase:wrappers" = { path = "../../wit/v2" }
25 |
--------------------------------------------------------------------------------
/wasm-wrappers/fdw/clerk_fdw/wit/world.wit:
--------------------------------------------------------------------------------
1 | package supabase:clerk-fdw@0.2.0;
2 |
3 | world clerk {
4 | import supabase:wrappers/http@0.2.0;
5 | import supabase:wrappers/jwt@0.2.0;
6 | import supabase:wrappers/stats@0.2.0;
7 | import supabase:wrappers/time@0.2.0;
8 | import supabase:wrappers/utils@0.2.0;
9 | export supabase:wrappers/routines@0.2.0;
10 | }
11 |
--------------------------------------------------------------------------------
/wasm-wrappers/fdw/helloworld_fdw/.cargo/config.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | target = "wasm32-unknown-unknown"
3 |
--------------------------------------------------------------------------------
/wasm-wrappers/fdw/helloworld_fdw/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "rust-analyzer.check.overrideCommand": [
3 | "cargo",
4 | "component",
5 | "check",
6 | "--workspace",
7 | "--all-targets",
8 | "--message-format=json"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/wasm-wrappers/fdw/helloworld_fdw/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 4
4 |
5 | [[package]]
6 | name = "helloworld_fdw"
7 | version = "0.2.0"
8 | dependencies = [
9 | "wit-bindgen-rt",
10 | ]
11 |
12 | [[package]]
13 | name = "wit-bindgen-rt"
14 | version = "0.41.0"
15 | source = "registry+https://github.com/rust-lang/crates.io-index"
16 | checksum = "c4db52a11d4dfb0a59f194c064055794ee6564eb1ced88c25da2cf76e50c5621"
17 |
--------------------------------------------------------------------------------
/wasm-wrappers/fdw/helloworld_fdw/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "helloworld_fdw"
3 | version = "0.2.0"
4 | edition = { workspace = true }
5 | homepage = { workspace = true }
6 | rust-version = { workspace = true }
7 |
8 | [lib]
9 | crate-type = ["cdylib"]
10 |
11 | [dependencies]
12 | wit-bindgen-rt = "0.41.0"
13 |
14 | [package.metadata.component]
15 | package = "supabase:helloworld-fdw"
16 |
17 | [package.metadata.component.dependencies]
18 |
19 | [package.metadata.component.target]
20 | path = "wit"
21 |
22 | [package.metadata.component.target.dependencies]
23 | "supabase:wrappers" = { path = "../../wit/v2" }
24 |
--------------------------------------------------------------------------------
/wasm-wrappers/fdw/helloworld_fdw/src/lib.rs:
--------------------------------------------------------------------------------
1 | #[allow(warnings)]
2 | mod bindings;
3 |
4 | use bindings::{
5 | exports::supabase::wrappers::routines::Guest,
6 | supabase::wrappers::types::{Cell, Context, FdwError, FdwResult, ImportForeignSchemaStmt, Row},
7 | };
8 |
9 | #[derive(Debug, Default)]
10 | struct HelloWorldFdw {
11 | // row counter
12 | row_cnt: i32,
13 | }
14 |
15 | static mut INSTANCE: *mut HelloWorldFdw = std::ptr::null_mut::();
16 |
17 | impl HelloWorldFdw {
18 | fn init() {
19 | let instance = Self::default();
20 | unsafe {
21 | INSTANCE = Box::leak(Box::new(instance));
22 | }
23 | }
24 |
25 | fn this_mut() -> &'static mut Self {
26 | unsafe { &mut (*INSTANCE) }
27 | }
28 | }
29 |
30 | impl Guest for HelloWorldFdw {
31 | fn host_version_requirement() -> String {
32 | // semver ref: https://docs.rs/semver/latest/semver/enum.Op.html
33 | "^0.1.0".to_string()
34 | }
35 |
36 | fn init(_ctx: &Context) -> FdwResult {
37 | Self::init();
38 | Ok(())
39 | }
40 |
41 | fn begin_scan(_ctx: &Context) -> FdwResult {
42 | let this = Self::this_mut();
43 |
44 | // reset row counter
45 | this.row_cnt = 0;
46 |
47 | Ok(())
48 | }
49 |
50 | fn iter_scan(ctx: &Context, row: &Row) -> Result