diff --git a/HISTORY.md b/HISTORY.md index 985b923..f96bf99 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,10 @@ # count_nulls history +stable +------ + +No user-facing changes yet. + 1.0.0 ----- diff --git a/Makefile b/Makefile index e57011a..3f08c82 100644 --- a/Makefile +++ b/Makefile @@ -2,3 +2,12 @@ include pgxntool/base.mk # Temporary hack testdeps: $(wildcard test/*/*.sql) $(wildcard test/*.sql) # Be careful not to include directories in this + +# Install the outgoing "current" version's full install script now that +# default_version is 'stable' (see count_nulls.control and RELEASE.md), so +# CREATE EXTENSION count_nulls VERSION '1.0.0' keeps working. Not covered by +# base.mk's DATA wildcard, which only picks up upgrade scripts +# (sql/*--*--*.sql) and the CURRENT version file (now +# sql/count_nulls--stable.sql) - a pgxntool bug, filed as +# Postgres-Extensions/pgxntool#48. +DATA += sql/count_nulls--1.0.0.sql diff --git a/RELEASE.md b/RELEASE.md index 78cbac9..bc73c65 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -134,3 +134,6 @@ it's worth a direct look before relying on it. is always available) for the next cycle. Leaving master stamped at the real version means the next source edit regenerates and corrupts the released version's install file. +- [ ] Keep this PR's description small — something like "Reset version back to + `stable` after release." is enough. It's a mechanical, low-risk step; it + doesn't need the detailed rationale a real content change would. diff --git a/count_nulls.control b/count_nulls.control index c5c5c28..bf8f662 100644 --- a/count_nulls.control +++ b/count_nulls.control @@ -1,4 +1,4 @@ # count_nulls extension comment = 'Count the number of null arguments' -default_version = '1.0.0' +default_version = 'stable' relocatable = false diff --git a/sql/count_nulls--1.0.0--stable.sql b/sql/count_nulls--1.0.0--stable.sql new file mode 100644 index 0000000..dc73d7b --- /dev/null +++ b/sql/count_nulls--1.0.0--stable.sql @@ -0,0 +1 @@ +-- No actual SQL changes to this version bump diff --git a/sql/count_nulls--stable.sql b/sql/count_nulls--stable.sql new file mode 100644 index 0000000..ef7c3b5 --- /dev/null +++ b/sql/count_nulls--stable.sql @@ -0,0 +1,87 @@ +/* DO NOT EDIT - AUTO-GENERATED FILE */ +CREATE OR REPLACE FUNCTION null_count( + VARIADIC argument anyarray +) RETURNS int LANGUAGE sql IMMUTABLE AS $body$ + SELECT sum( CASE WHEN a IS NULL THEN 1 ELSE 0 END )::int + FROM unnest( $1 ) a +$body$; + +CREATE OR REPLACE FUNCTION null_count( + argument jsonb +) RETURNS int LANGUAGE sql IMMUTABLE AS $body$ +SELECT count(*)::int + FROM jsonb_each_text( $1 ) a + WHERE value IS NULL +$body$; +CREATE OR REPLACE FUNCTION null_count( + argument json +) RETURNS int LANGUAGE sql IMMUTABLE AS 'SELECT @extschema@.null_count($1::jsonb)'; + +CREATE OR REPLACE FUNCTION null_count_trigger( +) RETURNS trigger LANGUAGE plpgsql IMMUTABLE AS $body$ +DECLARE + c_msg CONSTANT text := coalesce( + nullif( TG_ARGV[1], 'null' ) + , format( '%s must contain %s NULL fields', TG_RELID::regclass, TG_ARGV[0] ) + ); +BEGIN + IF TG_NARGS NOT BETWEEN 1 AND 2 THEN + RAISE '% usage: number of NULL columns[, error message]', TG_NAME; + END IF; + -- Casing intentional for cut/paste/replace/ + IF nullif(TG_ARGV[0], 'null') IS null THEN + RAISE '%: first argument must not be null', TG_NAME; + END IF; + + IF @extschema@.null_count( row_to_json(NEW) ) <> TG_ARGV[0]::int THEN + RAISE '%', c_msg; + END IF; + + RETURN NEW; +END +$body$; + + +CREATE OR REPLACE FUNCTION not_null_count( + VARIADIC argument anyarray +) RETURNS int LANGUAGE sql IMMUTABLE AS $body$ + SELECT sum( CASE WHEN a IS NOT NULL THEN 1 ELSE 0 END )::int + FROM unnest( $1 ) a +$body$; + +CREATE OR REPLACE FUNCTION not_null_count( + argument jsonb +) RETURNS int LANGUAGE sql IMMUTABLE AS $body$ +SELECT count(*)::int + FROM jsonb_each_text( $1 ) a + WHERE value IS NOT NULL +$body$; +CREATE OR REPLACE FUNCTION not_null_count( + argument json +) RETURNS int LANGUAGE sql IMMUTABLE AS 'SELECT @extschema@.not_null_count($1::jsonb)'; + +CREATE OR REPLACE FUNCTION not_null_count_trigger( +) RETURNS trigger LANGUAGE plpgsql IMMUTABLE AS $body$ +DECLARE + c_msg CONSTANT text := coalesce( + nullif( TG_ARGV[1], 'null' ) + , format( '%s must contain %s NOT NULL fields', TG_RELID::regclass, TG_ARGV[0] ) + ); +BEGIN + IF TG_NARGS NOT BETWEEN 1 AND 2 THEN + RAISE '% usage: number of NOT NULL columns[, error message]', TG_NAME; + END IF; + -- Casing intentional for cut/paste/replace/ + IF nullif(TG_ARGV[0], 'null') IS null THEN + RAISE '%: first argument must not be null', TG_NAME; + END IF; + + IF @extschema@.not_null_count( row_to_json(NEW) ) <> TG_ARGV[0]::int THEN + RAISE '%', c_msg; + END IF; + + RETURN NEW; +END +$body$; + +-- vi: expandtab sw=2 ts=2