Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
inputs = {
# 2025-11-13
nixpkgs.url = "github:NixOS/nixpkgs/91c9a64ce2a84e648d0cf9671274bb9c2fb9ba60";

xpg = {
url = "github:supabase/xpg/v2.5.0";
url = "github:supabase/xpg/v2.6.0";
};
};

Expand Down
40 changes: 40 additions & 0 deletions nix/plpgsql-check.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
lib,
stdenv,
fetchFromGitHub,
postgresql,
}:

stdenv.mkDerivation rec {
pname = "plpgsql-check";
version = "2.10.15";

src = fetchFromGitHub {
owner = "okbob";
repo = "plpgsql_check";
rev = "v${version}";
hash = "sha256-war+GVHMDegfDXjxdIsUkrPPDntKeyb/bGxibHVbpDo=";
};

buildInputs = [ postgresql ];

installPhase = ''
install -D -t $out *${postgresql.dlSuffix}
install -D -t $out *.sql
install -D -t $out *.control

# Needed for postgres 18+ native implementation of extension_search_path
mkdir $out/extension
install -D -t $out/extension *.sql
install -D -t $out/extension *.control
'';

meta = with lib; {
description = "Linter tool for language PL/pgSQL";
homepage = "https://github.com/okbob/plpgsql_check";
changelog = "https://github.com/okbob/plpgsql_check/releases/tag/v${version}";
platforms = postgresql.meta.platforms;
license = licenses.mit;
maintainers = [ maintainers.marsam ];
};
}
25 changes: 25 additions & 0 deletions shell.nix
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,25 @@ in
cassert ? true,
}:
let
plpgsqlcheck14 = pkgs.callPackage ./nix/plpgsql-check.nix {
postgresql = xpgPkgs.postgresql_14;
};
plpgsqlcheck15 = pkgs.callPackage ./nix/plpgsql-check.nix {
postgresql = xpgPkgs.postgresql_15;
};
plpgsqlcheck16 = pkgs.callPackage ./nix/plpgsql-check.nix {
postgresql = xpgPkgs.postgresql_16;
};
plpgsqlcheck17 = pkgs.callPackage ./nix/plpgsql-check.nix {
postgresql = xpgPkgs.postgresql_17;
};
plpgsqlcheck18 = pkgs.callPackage ./nix/plpgsql-check.nix {
postgresql = xpgPkgs.postgresql_18;
};
plpgsqlcheck19 = pkgs.callPackage ./nix/plpgsql-check.nix {
postgresql = xpgPkgs.postgresql_19;
};

style = pkgs.writeShellScriptBin "vault-style" ''
${pkgs.clang-tools}/bin/clang-format -i src/*
${pkgs.findutils}/bin/find . -name '*.nix' -exec ${pkgs.nixfmt}/bin/nixfmt {} \+
Expand All @@ -34,16 +53,22 @@ pkgs.mkShell {
(xpgPkgs.xpg.withExtensions {
extensions = {
"14" = [
plpgsqlcheck14
];
"15" = [
plpgsqlcheck15
];
"16" = [
plpgsqlcheck16
];
"17" = [
plpgsqlcheck17
];
"18" = [
plpgsqlcheck18
];
"19" = [
plpgsqlcheck19
];
};
})
Expand Down
32 changes: 31 additions & 1 deletion sql/supabase_vault--0.3.1--0.3.2.sql
Original file line number Diff line number Diff line change
@@ -1,3 +1,33 @@
-- Search pg_temp last so callers can't shadow types (e.g. text, bytea) with temp objects that run as the function owner
ALTER FUNCTION vault.create_secret(text, text, text, uuid) SET search_path = pg_catalog, pg_temp;
ALTER FUNCTION vault.update_secret(uuid, text, text, text, uuid) SET search_path = pg_catalog, pg_temp;

CREATE OR REPLACE FUNCTION vault.update_secret(
secret_id uuid,
new_secret text = NULL,
new_name text = NULL,
new_description text = NULL,
-- unused
new_key_id uuid = NULL
)
RETURNS void
SECURITY DEFINER
LANGUAGE plpgsql
SET search_path = 'pg_catalog, pg_temp'
AS $$
BEGIN
UPDATE vault.secrets s
SET
secret = CASE WHEN new_secret IS NULL THEN s.secret
ELSE encode(vault._crypto_aead_det_encrypt(
message := convert_to(new_secret, 'utf8'),
additional := convert_to(s.id::text, 'utf8'),
key_id := 0,
context := 'pgsodium'::bytea,
nonce := s.nonce
), 'base64') END,
name = coalesce(new_name, s.name),
description = coalesce(new_description, s.description),
updated_at = now()
WHERE s.id = secret_id;
END
$$;
31 changes: 31 additions & 0 deletions test/expected/test.out
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,34 @@ WITH security_definer_fn_settings AS (
-------------+---------------+-----------------
(0 rows)

-- below query is sourced from from pgsql_check's git repo, link below
-- https://github.com/okbob/plpgsql_check/blob/0ac96adc13b34d071c4be8ba0ec4f73e6e7520f5/README.md?plain=1#L310
-- check all plpgsql functions (functions or trigger functions with defined triggers)
SELECT
(pcf).functionid::regprocedure, (pcf).lineno, (pcf).statement,
(pcf).sqlstate, (pcf).message, (pcf).detail, (pcf).hint, (pcf).level,
(pcf)."position", (pcf).query, (pcf).context
FROM
(
SELECT
plpgsql_check_function_tb(pg_proc.oid, COALESCE(pg_trigger.tgrelid, 0),
oldtable=>pg_trigger.tgoldtable,
newtable=>pg_trigger.tgnewtable) AS pcf
FROM pg_proc
LEFT JOIN pg_trigger
ON (pg_trigger.tgfoid = pg_proc.oid)
WHERE
prolang = (SELECT lang.oid FROM pg_language lang WHERE lang.lanname = 'plpgsql') AND
pronamespace <> (SELECT nsp.oid FROM pg_namespace nsp WHERE nsp.nspname = 'pg_catalog') AND
-- ignore unused triggers
(pg_proc.prorettype <> (SELECT typ.oid FROM pg_type typ WHERE typ.typname = 'trigger') OR
pg_trigger.tgfoid IS NOT NULL)
OFFSET 0
) ss
ORDER BY (pcf).functionid::regprocedure::text, (pcf).lineno;
functionid | lineno | statement | sqlstate | message | detail | hint | level | position | query | context
-----------------------------------------------+--------+-----------+----------+-------------------------------+--------+------+---------------+----------+-------+---------
vault.create_secret(text,text,text,uuid) | | | 00000 | unused parameter "new_key_id" | | | warning extra | | |
vault.update_secret(uuid,text,text,text,uuid) | | | 00000 | unused parameter "new_key_id" | | | warning extra | | |
(2 rows)

1 change: 1 addition & 0 deletions test/init.sql
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
CREATE ROLE bob login password 'bob';

CREATE EXTENSION plpgsql_check;
CREATE EXTENSION supabase_vault CASCADE;

GRANT USAGE ON SCHEMA vault TO bob WITH GRANT OPTION;
Expand Down
26 changes: 26 additions & 0 deletions test/sql/test.sql
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,29 @@ WITH security_definer_fn_settings AS (
WHERE array_position(s.search_path_ary, 'pg_temp') < array_position(s.search_path_ary, 'pg_catalog')
AND array_position(s.search_path_ary, 'pg_temp') IS NOT NULL
AND array_position(s.search_path_ary, 'pg_catalog') IS NOT NULL;

-- below query is sourced from from pgsql_check's git repo, link below
-- https://github.com/okbob/plpgsql_check/blob/0ac96adc13b34d071c4be8ba0ec4f73e6e7520f5/README.md?plain=1#L310
-- check all plpgsql functions (functions or trigger functions with defined triggers)
SELECT
(pcf).functionid::regprocedure, (pcf).lineno, (pcf).statement,
(pcf).sqlstate, (pcf).message, (pcf).detail, (pcf).hint, (pcf).level,
(pcf)."position", (pcf).query, (pcf).context
FROM
(
SELECT
plpgsql_check_function_tb(pg_proc.oid, COALESCE(pg_trigger.tgrelid, 0),
oldtable=>pg_trigger.tgoldtable,
newtable=>pg_trigger.tgnewtable) AS pcf
FROM pg_proc
LEFT JOIN pg_trigger
ON (pg_trigger.tgfoid = pg_proc.oid)
WHERE
prolang = (SELECT lang.oid FROM pg_language lang WHERE lang.lanname = 'plpgsql') AND
pronamespace <> (SELECT nsp.oid FROM pg_namespace nsp WHERE nsp.nspname = 'pg_catalog') AND
-- ignore unused triggers
(pg_proc.prorettype <> (SELECT typ.oid FROM pg_type typ WHERE typ.typname = 'trigger') OR
pg_trigger.tgfoid IS NOT NULL)
OFFSET 0
) ss
ORDER BY (pcf).functionid::regprocedure::text, (pcf).lineno;
Loading