Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package pg_cron for openSUSE:Factory checked in at 2021-12-14 22:01:51 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/pg_cron (Old) and /work/SRC/openSUSE:Factory/.pg_cron.new.2520 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "pg_cron" Tue Dec 14 22:01:51 2021 rev:8 rq:940416 version:1.4.1 Changes: -------- --- /work/SRC/openSUSE:Factory/pg_cron/pg_cron.changes 2021-07-02 13:27:37.644685950 +0200 +++ /work/SRC/openSUSE:Factory/.pg_cron.new.2520/pg_cron.changes 2021-12-14 22:02:02.827142828 +0100 @@ -1,0 +2,36 @@ +Thu Dec 2 11:47:08 UTC 2021 - Marcus Rueckert <mrueck...@suse.de> + +- fix postgresql_has_llvm usage + +------------------------------------------------------------------- +Tue Nov 30 17:24:01 UTC 2021 - Marcus Rueckert <mrueck...@suse.de> + +- switch to %{pg_name}-llvmjit-devel + +------------------------------------------------------------------- +Sun Nov 14 02:05:33 UTC 2021 - Marcus Rueckert <mrueck...@suse.de> + +- port to postgresql macros + +------------------------------------------------------------------- +Sun Sep 26 08:00:49 UTC 2021 - Andrey Karepin <egdf...@opensuse.org> + +- update to 1.4.1 + * Fixes PostgreSQL 11- support + +------------------------------------------------------------------- +Sat Sep 18 17:42:49 UTC 2021 - Andrey Karepin <egdf...@opensuse.org> + +- update to 1.4.0 + * Adds a cron.alter_job function to change job properties, by + Bertrand Drouvot + * Adds a cron.log_min_messages setting to control log_min_messages + in pg_cron launcher, by Bertrand Drouvot + * Adds a cron.enable_superuser_jobs setting to disallow superuser + jobs + * Fixes a bug that could cause jobs to hang when using + cron.use_background_workers, by Bertrand Drouvot + * Fixes a small memory allocation bug, by @mrdrivingduck + * PostgreSQL 14 is supported (no changes were needed) + +------------------------------------------------------------------- Old: ---- pg_cron-1.3.1.tar.gz New: ---- pg_cron-1.4.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ pg_cron.spec ++++++ --- /var/tmp/diff_new_pack.tL3x5N/_old 2021-12-14 22:02:03.295143095 +0100 +++ /var/tmp/diff_new_pack.tL3x5N/_new 2021-12-14 22:02:03.299143098 +0100 @@ -16,82 +16,54 @@ # -%define pgversion @BUILD_FLAVOR@ -%define priority %{pgversion} -%define sname pg_cron -%define pg_libdir %(pg_config --pkglibdir) -%define pg_share %(pg_config --sharedir) -%if 0%{?is_opensuse} && ("%{pgversion}" == "postgresql11" || "%{pgversion}" == "postgresql12" || "%{pgversion}" == "postgresql13") && 0%{?suse_version} >= 1500 -%bcond_without llvm -%else -%bcond_with llvm -%endif -Name: %{pgversion}-%{sname} -Version: 1.3.1 +%define pg_name @BUILD_FLAVOR@%{nil} +%define ext_name pg_cron +%{pg_version_from_name} + +Name: %{pg_name}-%{ext_name} +Version: 1.4.1 Release: 0 Summary: PostgreSQL module for simple job schedule License: PostgreSQL Group: Productivity/Databases/Servers URL: https://github.com/citusdata/pg_cron -Source: %{sname}-%{version}.tar.gz -BuildRequires: %{pgversion}-server -BuildRequires: %{pgversion}-server-devel -%requires_eq %{pgversion}-server -%if "%{pgversion}" == "" -Name: %{sname} -ExclusiveArch: do_not_build -%endif -%if ("%{pgversion}" == "postgresql95" || "%{pgversion}" == "postgresql96") && 0%{?suse_version} >= 1550 +Source: https://github.com/citusdata/pg_cron/archive/refs/tags/v%{version}.tar.gz#/%{ext_name}-%{version}.tar.gz + +BuildRequires: %{pg_name}-llvmjit-devel +%pg_server_requires +%if "%{pg_name}" == "" +Name: %{ext_name} ExclusiveArch: do_not_build %endif %description -%{sname} is a simple cron-based job scheduler for PostgreSQL (9.5 or higher) +%{ext_name} is a simple cron-based job scheduler for PostgreSQL (9.5 or higher) that runs inside the database as an extension. It uses the same syntax as regular cron, but it allows you to schedule PostgreSQL commands directly from the database. -%if %{with llvm} -%package llvmjit -Summary: Just-in-time compilation support for PostgreSQL %{sname} extension -Group: Productivity/Databases/Servers -Requires: %{pgversion}-%{sname} = %{version}-%{release} -Requires: %{pgversion}-llvmjit -Requires: %{pgversion}-server -Supplements: packageand(%{pgversion}-llvmjit:%{name}) - -%description llvmjit -This package contains support for just-in-time compiling parts of -PostgreSQL queries. Using LLVM it compiles e.g. expressions and tuple -deforming into native code, with the goal of accelerating analytics -queries. -%endif - %prep -%setup -q -n %{sname}-%{version} +%autosetup -p1 -n %{ext_name}-%{version} %build -export PATH="$PATH:%{_libexecdir}/%{pgname}/bin" -make %{?_smp_mflags} +%make_pgxs %install %make_install %post -%{_datadir}/postgresql/install-alternatives %{priority} +%{_datadir}/postgresql/install-alternatives %{pg_version} %postun -%{_datadir}/postgresql/install-alternatives %{priority} +%{_datadir}/postgresql/install-alternatives %{pg_version} %files %license LICENSE %doc CHANGELOG.md README.md -%{_datadir}/%{pgversion}/extension/%{sname}* -%{pg_libdir}/%{sname}.so - -%if %{with llvm} -%files llvmjit -%{pg_libdir}/bitcode/* +%{pg_config_sharedir}/extension/%{ext_name}* +%{pg_config_pkglibdir}/%{ext_name}.so +%if %{postgresql_has_llvm} +%{pg_config_pkglibdir}/bitcode/* %endif %changelog ++++++ _multibuild ++++++ --- /var/tmp/diff_new_pack.tL3x5N/_old 2021-12-14 22:02:03.327143113 +0100 +++ /var/tmp/diff_new_pack.tL3x5N/_new 2021-12-14 22:02:03.327143113 +0100 @@ -1,9 +1,8 @@ <multibuild> - <package>postgresql95</package> - <package>postgresql96</package> <package>postgresql10</package> <package>postgresql11</package> <package>postgresql12</package> <package>postgresql13</package> + <package>postgresql14</package> </multibuild> ++++++ pg_cron-1.3.1.tar.gz -> pg_cron-1.4.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pg_cron-1.3.1/CHANGELOG.md new/pg_cron-1.4.1/CHANGELOG.md --- old/pg_cron-1.3.1/CHANGELOG.md 2021-03-29 12:17:31.000000000 +0200 +++ new/pg_cron-1.4.1/CHANGELOG.md 2021-09-25 18:55:16.000000000 +0200 @@ -1,3 +1,17 @@ +### pg_cron v1.4.1 (September 25, 2021) ### + +* Fixes PostgreSQL 11- support + +### pg_cron v1.4.0 (September 16, 2021) ### + +* Adds a cron.alter_job function to change job properties, by Bertrand Drouvot +* Adds a cron.schedule_in_database function to schedule in a custom database, by Bertrand Drouvot +* Adds a cron.log_min_messages setting to control log_min_messages in pg_cron launcher, by Bertrand Drouvot +* Adds a cron.enable_superuser_jobs setting to disallow superuser jobs +* Fixes a bug that could cause jobs to hang when using cron.use_background_workers, by Bertrand Drouvot +* Fixes a small memory allocation bug, by @mrdrivingduck +* PostgreSQL 14 is supported (no changes were needed) + ### pg_cron v1.3.1 (March 29, 2021) ### * Fixes a memory leak diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pg_cron-1.3.1/Makefile new/pg_cron-1.4.1/Makefile --- old/pg_cron-1.3.1/Makefile 2021-03-29 12:17:31.000000000 +0200 +++ new/pg_cron-1.4.1/Makefile 2021-09-25 18:55:16.000000000 +0200 @@ -18,7 +18,7 @@ SHLIB_LINK = $(libpq) EXTRA_CLEAN += $(addprefix src/,*.gcno *.gcda) # clean up after profiling runs -PG_CONFIG = pg_config +PG_CONFIG ?= pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pg_cron-1.3.1/README.md new/pg_cron-1.4.1/README.md --- old/pg_cron-1.3.1/README.md 2021-03-29 12:17:31.000000000 +0200 +++ new/pg_cron-1.4.1/README.md 2021-09-25 18:55:16.000000000 +0200 @@ -14,13 +14,25 @@ 42 -- Vacuum every day at 10:00am (GMT) -SELECT cron.schedule('0 10 * * *', 'VACUUM'); +SELECT cron.schedule('nightly-vacuum', '0 10 * * *', 'VACUUM'); schedule ---------- 43 --- Stop scheduling a job -SELECT cron.unschedule(43); +-- Change to vacuum at 3:00am (GMT) +SELECT cron.schedule('nightly-vacuum', '0 3 * * *', 'VACUUM'); + schedule +---------- + 43 + +-- Stop scheduling jobs +SELECT cron.unschedule('nightly-vacuum' ); + unschedule +------------ + t +(1 row) + +SELECT cron.unschedule(42); unschedule ------------ t @@ -48,21 +60,18 @@ ## Installing pg_cron -Install on Red Hat, CentOS, Fedora, Amazon Linux with PostgreSQL 11: +Install on Red Hat, CentOS, Fedora, Amazon Linux with PostgreSQL 12 using [PGDG](https://yum.postgresql.org/repopackages/): ```bash -# Add Citus Data package repository -curl https://install.citusdata.com/community/rpm.sh | sudo bash - # Install the pg_cron extension -sudo yum install -y pg_cron_11 +sudo yum install -y pg_cron_12 ``` -Install on Debian, Ubuntu with PostgreSQL 11 using [apt.postgresql.org](https://wiki.postgresql.org/wiki/Apt): +Install on Debian, Ubuntu with PostgreSQL 12 using [apt.postgresql.org](https://wiki.postgresql.org/wiki/Apt): ```bash # Install the pg_cron extension -sudo apt-get -y install postgresql-11-cron +sudo apt-get -y install postgresql-12-cron ``` You can also install pg_cron by building it from source: @@ -71,7 +80,7 @@ git clone https://github.com/citusdata/pg_cron.git cd pg_cron # Ensure pg_config is in your path, e.g. -export PATH=/usr/pgsql-11/bin:$PATH +export PATH=/usr/pgsql-12/bin:$PATH make && sudo PATH=$PATH make install ``` @@ -117,11 +126,15 @@ | Service | Supported | | ------------- |:-------------:| +| [Aiven](https://aiven.io/postgresql) | :heavy_check_mark: | | [Alibaba Cloud](https://www.alibabacloud.com/help/doc-detail/150355.htm) | :heavy_check_mark: | -| [Amazon RDS](https://aws.amazon.com/rds/postgresql/) | :x: | | +| [Amazon RDS](https://aws.amazon.com/rds/postgresql/) | :heavy_check_mark: | | | [Azure](https://azure.microsoft.com/en-us/services/postgresql/) | :heavy_check_mark: | | [Citus Cloud](https://www.citusdata.com/product/cloud) | :heavy_check_mark: | | [Crunchy Bridge](https://www.crunchydata.com/products/crunchy-bridge/?ref=producthunt) | :heavy_check_mark: | | [DigitalOcean](https://www.digitalocean.com/products/managed-databases/) | :heavy_check_mark: | | [Google Cloud](https://cloud.google.com/sql/docs/postgres/) | :x: | | [Heroku](https://elements.heroku.com/addons/heroku-postgresql) | :x: | | +| [ScaleGrid](https://scalegrid.io/postgresql.html) | :heavy_check_mark: | +| [Scaleway](https://www.scaleway.com/en/database/) | :heavy_check_mark: | +| [Supabase](https://supabase.io/docs/guides/database) | :heavy_check_mark: | diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pg_cron-1.3.1/expected/pg_cron-test.out new/pg_cron-1.4.1/expected/pg_cron-test.out --- old/pg_cron-1.3.1/expected/pg_cron-test.out 2021-03-29 12:17:31.000000000 +0200 +++ new/pg_cron-1.4.1/expected/pg_cron-test.out 2021-09-25 18:55:16.000000000 +0200 @@ -5,13 +5,14 @@ 1.0 (1 row) -ALTER EXTENSION pg_cron UPDATE TO '1.3'; +ALTER EXTENSION pg_cron UPDATE TO '1.4'; SELECT extversion FROM pg_extension WHERE extname='pg_cron'; extversion ------------ - 1.3 + 1.4 (1 row) +SET cron.enable_superuser_jobs TO on; -- Vacuum every day at 10:00am (GMT) SELECT cron.schedule('0 10 * * *', 'VACUUM'); schedule @@ -93,4 +94,125 @@ 2 | | @restart | ALTER EXTENSION pg_cron UPDATE (1 row) +-- Testing version >= 1.4 new APIs +-- First as superuser +-- Update a job without one job attribute to change +SELECT cron.alter_job(2); +ERROR: no updates specified +HINT: You must specify at least one job attribute to change when calling alter_job +-- Update to a non existing database +select cron.alter_job(job_id:=2,database:='hopedoesnotexist'); +ERROR: database "hopedoesnotexist" does not exist +-- Create a database that does not allow connection +create database pgcron_dbno; +revoke connect on database pgcron_dbno from public; +-- create a test user +create user pgcron_cront with password 'pwd'; +GRANT USAGE ON SCHEMA cron TO pgcron_cront; +-- Schedule a job for this user on the database that does not accept connections +SELECT cron.schedule_in_database(job_name:='can not connect', schedule:='0 11 * * *', command:='VACUUM',database:='pgcron_dbno',username:='pgcron_cront'); +ERROR: User pgcron_cront does not have CONNECT privilege on pgcron_dbno +-- Create a database that does allow connections +create database pgcron_dbyes; +-- Schedule a job on the database that does accept connections for a non existing user +SELECT cron.schedule_in_database(job_name:='user does not exist', schedule:='0 11 * * *', command:='VACUUM',database:='pgcron_dbyes',username:='pgcron_useraqwxszedc'); +ERROR: role "pgcron_useraqwxszedc" does not exist +-- Alter an existing job on a database that does not accept connections +SELECT cron.alter_job(job_id:=2,database:='pgcron_dbno',username:='pgcron_cront'); +ERROR: User pgcron_cront does not have CONNECT privilege on pgcron_dbno +-- Make sure pgcron_cront can execute alter_job +GRANT EXECUTE ON FUNCTION cron.alter_job(bigint,text,text,text,text,boolean) TO public; +-- Second as non superuser +SET SESSION AUTHORIZATION pgcron_cront; +-- Create a job +SELECT cron.schedule('My vacuum', '0 11 * * *', 'VACUUM'); + schedule +---------- + 6 +(1 row) + +-- Create a job for another user +SELECT cron.schedule_in_database(job_name:='his vacuum', schedule:='0 11 * * *', command:='VACUUM',database:=current_database(),username:='anotheruser'); +ERROR: permission denied for function schedule_in_database +-- Change the username of an existing job that the user own +select cron.alter_job(job_id:=6,username:='anotheruser'); +ERROR: must be superuser to alter username +-- Update a job that the user does not own +select cron.alter_job(job_id:=2,database:='pgcron_dbyes'); +ERROR: Job 2 does not exist or you don't own it +-- change the database for a job that the user own and can connect to +select cron.alter_job(job_id:=6,database:='pgcron_dbyes'); + alter_job +----------- + +(1 row) + +SELECT database FROM cron.job; + database +-------------- + pgcron_dbyes +(1 row) + +-- change the database for a job that the user own but can not connect to +select cron.alter_job(job_id:=6,database:='pgcron_dbno'); +ERROR: User pgcron_cront does not have CONNECT privilege on pgcron_dbno +SELECT database FROM cron.job; + database +-------------- + pgcron_dbyes +(1 row) + +-- back to superuser +RESET SESSION AUTHORIZATION; +-- Change the username of an existing job +select cron.alter_job(job_id:=2,username:='pgcron_cront'); + alter_job +----------- + +(1 row) + +SELECT username FROM cron.job where jobid=2; + username +-------------- + pgcron_cront +(1 row) + +-- Create a job for another user +SELECT cron.schedule_in_database(job_name:='his vacuum', schedule:='0 11 * * *', command:='VACUUM',database:=current_database(), username:='pgcron_cront'); + schedule_in_database +---------------------- + 7 +(1 row) + +SELECT username FROM cron.job where jobid=7; + username +-------------- + pgcron_cront +(1 row) + +-- Try to schedule a job as superuser when it is not allowed +SET cron.enable_superuser_jobs TO off; +SELECT cron.schedule(job_name:='disallowed-superuser', schedule:='* * * * *', command:='drop database pg_crondbno'); +ERROR: cannot schedule jobs as superuser +DETAIL: Scheduling jobs as superuser is disallowed when cron.enable_superuser_jobs is set to off. +SELECT cron.alter_job(7, username := current_user); +ERROR: cannot schedule jobs as superuser +DETAIL: Scheduling jobs as superuser is disallowed when cron.enable_superuser_jobs is set to off. +-- Scheduling as other users is allowed as superuser +SELECT cron.schedule_in_database(job_name:='more vacuum', schedule:='0 12 * * *', command:='VACUUM', database:=current_database(), username:='pgcron_cront'); + schedule_in_database +---------------------- + 8 +(1 row) + +SELECT cron.alter_job(7, username := 'pgcron_cront'); + alter_job +----------- + +(1 row) + +-- cleaning DROP EXTENSION pg_cron; +drop user pgcron_cront; +drop database pgcron_dbno; +drop database pgcron_dbyes; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pg_cron-1.3.1/include/job_metadata.h new/pg_cron-1.4.1/include/job_metadata.h --- old/pg_cron-1.3.1/include/job_metadata.h 2021-03-29 12:17:31.000000000 +0200 +++ new/pg_cron-1.4.1/include/job_metadata.h 2021-09-25 18:55:16.000000000 +0200 @@ -46,6 +46,7 @@ /* global settings */ extern char *CronHost; extern bool CronJobCacheValid; +extern bool EnableSuperuserJobs; /* functions for retrieving job metadata */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pg_cron-1.3.1/pg_cron--1.3--1.4.sql new/pg_cron-1.4.1/pg_cron--1.3--1.4.sql --- old/pg_cron-1.3.1/pg_cron--1.3--1.4.sql 1970-01-01 01:00:00.000000000 +0100 +++ new/pg_cron-1.4.1/pg_cron--1.3--1.4.sql 2021-09-25 18:55:16.000000000 +0200 @@ -0,0 +1,44 @@ +/* pg_cron--1.3--1.4.sql */ + +/* cron_schedule_named expects job name to be text */ +DROP FUNCTION cron.schedule(name,text,text); +CREATE FUNCTION cron.schedule(job_name text, + schedule text, + command text) +RETURNS bigint +LANGUAGE C +AS 'MODULE_PATHNAME', $$cron_schedule_named$$; +COMMENT ON FUNCTION cron.schedule(text,text,text) +IS 'schedule a pg_cron job'; + +CREATE FUNCTION cron.alter_job(job_id bigint, + schedule text default null, + command text default null, + database text default null, + username text default null, + active boolean default null) +RETURNS void +LANGUAGE C +AS 'MODULE_PATHNAME', $$cron_alter_job$$; + +COMMENT ON FUNCTION cron.alter_job(bigint,text,text,text,text,boolean) +IS 'Alter the job identified by job_id. Any option left as NULL will not be modified.'; + +/* admin should decide whether alter_job is safe by explicitly granting execute */ +REVOKE ALL ON FUNCTION cron.alter_job(bigint,text,text,text,text,boolean) FROM public; + +CREATE FUNCTION cron.schedule_in_database(job_name text, + schedule text, + command text, + database text, + username text default null, + active boolean default 'true') +RETURNS bigint +LANGUAGE C +AS 'MODULE_PATHNAME', $$cron_schedule_named$$; + +COMMENT ON FUNCTION cron.schedule_in_database(text,text,text,text,text,boolean) +IS 'schedule a pg_cron job'; + +/* admin should decide whether cron.schedule_in_database is safe by explicitly granting execute */ +REVOKE ALL ON FUNCTION cron.schedule_in_database(text,text,text,text,text,boolean) FROM public; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pg_cron-1.3.1/pg_cron.control new/pg_cron-1.4.1/pg_cron.control --- old/pg_cron-1.3.1/pg_cron.control 2021-03-29 12:17:31.000000000 +0200 +++ new/pg_cron-1.4.1/pg_cron.control 2021-09-25 18:55:16.000000000 +0200 @@ -1,4 +1,4 @@ comment = 'Job scheduler for PostgreSQL' -default_version = '1.3' +default_version = '1.4' module_pathname = '$libdir/pg_cron' relocatable = false diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pg_cron-1.3.1/sql/pg_cron-test.sql new/pg_cron-1.4.1/sql/pg_cron-test.sql --- old/pg_cron-1.3.1/sql/pg_cron-test.sql 2021-03-29 12:17:31.000000000 +0200 +++ new/pg_cron-1.4.1/sql/pg_cron-test.sql 2021-09-25 18:55:16.000000000 +0200 @@ -1,8 +1,10 @@ CREATE EXTENSION pg_cron VERSION '1.0'; SELECT extversion FROM pg_extension WHERE extname='pg_cron'; -ALTER EXTENSION pg_cron UPDATE TO '1.3'; +ALTER EXTENSION pg_cron UPDATE TO '1.4'; SELECT extversion FROM pg_extension WHERE extname='pg_cron'; +SET cron.enable_superuser_jobs TO on; + -- Vacuum every day at 10:00am (GMT) SELECT cron.schedule('0 10 * * *', 'VACUUM'); @@ -37,4 +39,84 @@ SELECT jobid, jobname, schedule, command FROM cron.job ORDER BY jobid; +-- Testing version >= 1.4 new APIs +-- First as superuser + +-- Update a job without one job attribute to change +SELECT cron.alter_job(2); + +-- Update to a non existing database +select cron.alter_job(job_id:=2,database:='hopedoesnotexist'); + +-- Create a database that does not allow connection +create database pgcron_dbno; +revoke connect on database pgcron_dbno from public; + +-- create a test user +create user pgcron_cront with password 'pwd'; +GRANT USAGE ON SCHEMA cron TO pgcron_cront; + +-- Schedule a job for this user on the database that does not accept connections +SELECT cron.schedule_in_database(job_name:='can not connect', schedule:='0 11 * * *', command:='VACUUM',database:='pgcron_dbno',username:='pgcron_cront'); + +-- Create a database that does allow connections +create database pgcron_dbyes; + +-- Schedule a job on the database that does accept connections for a non existing user +SELECT cron.schedule_in_database(job_name:='user does not exist', schedule:='0 11 * * *', command:='VACUUM',database:='pgcron_dbyes',username:='pgcron_useraqwxszedc'); + +-- Alter an existing job on a database that does not accept connections +SELECT cron.alter_job(job_id:=2,database:='pgcron_dbno',username:='pgcron_cront'); + +-- Make sure pgcron_cront can execute alter_job +GRANT EXECUTE ON FUNCTION cron.alter_job(bigint,text,text,text,text,boolean) TO public; + +-- Second as non superuser +SET SESSION AUTHORIZATION pgcron_cront; + +-- Create a job +SELECT cron.schedule('My vacuum', '0 11 * * *', 'VACUUM'); + +-- Create a job for another user +SELECT cron.schedule_in_database(job_name:='his vacuum', schedule:='0 11 * * *', command:='VACUUM',database:=current_database(),username:='anotheruser'); + +-- Change the username of an existing job that the user own +select cron.alter_job(job_id:=6,username:='anotheruser'); + +-- Update a job that the user does not own +select cron.alter_job(job_id:=2,database:='pgcron_dbyes'); + +-- change the database for a job that the user own and can connect to +select cron.alter_job(job_id:=6,database:='pgcron_dbyes'); +SELECT database FROM cron.job; + +-- change the database for a job that the user own but can not connect to +select cron.alter_job(job_id:=6,database:='pgcron_dbno'); +SELECT database FROM cron.job; + +-- back to superuser +RESET SESSION AUTHORIZATION; + +-- Change the username of an existing job +select cron.alter_job(job_id:=2,username:='pgcron_cront'); +SELECT username FROM cron.job where jobid=2; + +-- Create a job for another user +SELECT cron.schedule_in_database(job_name:='his vacuum', schedule:='0 11 * * *', command:='VACUUM',database:=current_database(), username:='pgcron_cront'); +SELECT username FROM cron.job where jobid=7; + +-- Try to schedule a job as superuser when it is not allowed +SET cron.enable_superuser_jobs TO off; + +SELECT cron.schedule(job_name:='disallowed-superuser', schedule:='* * * * *', command:='drop database pg_crondbno'); +SELECT cron.alter_job(7, username := current_user); + +-- Scheduling as other users is allowed as superuser +SELECT cron.schedule_in_database(job_name:='more vacuum', schedule:='0 12 * * *', command:='VACUUM', database:=current_database(), username:='pgcron_cront'); +SELECT cron.alter_job(7, username := 'pgcron_cront'); + +-- cleaning DROP EXTENSION pg_cron; +drop user pgcron_cront; +drop database pgcron_dbno; +drop database pgcron_dbyes; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pg_cron-1.3.1/src/entry.c new/pg_cron-1.4.1/src/entry.c --- old/pg_cron-1.3.1/src/entry.c 2021-03-29 12:17:31.000000000 +0200 +++ new/pg_cron-1.4.1/src/entry.c 2021-09-25 18:55:16.000000000 +0200 @@ -381,7 +381,7 @@ /* range. set all elements from num1 to num2, stepping * by num3. (the step is a downward-compatible extension * proposed conceptually by bob@acornrc, syntactically - * designed then implmented by paul vixie). + * designed then implemented by paul vixie). */ for (i = num1; i <= num2; i += num3) if (EOF == set_element(bits, low, high, i)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pg_cron-1.3.1/src/job_metadata.c new/pg_cron-1.4.1/src/job_metadata.c --- old/pg_cron-1.3.1/src/job_metadata.c 2021-03-29 12:17:31.000000000 +0200 +++ new/pg_cron-1.4.1/src/job_metadata.c 2021-09-25 18:55:16.000000000 +0200 @@ -50,6 +50,8 @@ #include "executor/spi.h" #include "catalog/pg_type.h" +#include "commands/dbcommands.h" +#include "catalog/pg_authid.h" #if (PG_VERSION_NUM < 120000) #define table_open(r, l) heap_open(r, l) @@ -68,7 +70,9 @@ /* forward declarations */ static HTAB * CreateCronJobHash(void); -static int64 ScheduleCronJob(Name jobName, char *schedule, char *command); +static int64 ScheduleCronJob(text *scheduleText, text *commandText, + text *databaseText, text *usernameText, + bool active, text *jobnameText); static Oid CronExtensionOwner(void); static void EnsureDeletePermission(Relation cronJobsTable, HeapTuple heapTuple); static void InvalidateJobCacheCallback(Datum argument, Oid relationId); @@ -78,7 +82,12 @@ static CronJob * TupleToCronJob(TupleDesc tupleDescriptor, HeapTuple heapTuple); static bool PgCronHasBeenLoaded(void); static bool JobRunDetailsTableExists(void); +static bool JobTableExists(void); +static void AlterJob(int64 jobId, text *scheduleText, text *commandText, + text *databaseText, text *usernameText, bool *active); + +static Oid GetRoleOidIfCanLogin(char *username); /* SQL-callable functions */ PG_FUNCTION_INFO_V1(cron_schedule); @@ -86,6 +95,7 @@ PG_FUNCTION_INFO_V1(cron_unschedule); PG_FUNCTION_INFO_V1(cron_unschedule_named); PG_FUNCTION_INFO_V1(cron_job_cache_invalidate); +PG_FUNCTION_INFO_V1(cron_alter_job); /* global variables */ @@ -94,6 +104,7 @@ static Oid CachedCronJobRelationId = InvalidOid; bool CronJobCacheValid = false; char *CronHost = "localhost"; +bool EnableSuperuserJobs = true; /* @@ -167,59 +178,28 @@ return job; } - -/* - * cron_schedule schedules an unnamed cron job. - */ -Datum -cron_schedule(PG_FUNCTION_ARGS) -{ - text *scheduleText = PG_GETARG_TEXT_P(0); - text *commandText = PG_GETARG_TEXT_P(1); - - Name jobName = NULL; - char *schedule = text_to_cstring(scheduleText); - char *command = text_to_cstring(commandText); - - int64 jobId = ScheduleCronJob(jobName, schedule, command); - - PG_RETURN_INT64(jobId); -} - - -/* - * cron_schedule_named schedules a named cron job - */ -Datum -cron_schedule_named(PG_FUNCTION_ARGS) -{ - Name jobName = PG_GETARG_NAME(0); - text *scheduleText = PG_GETARG_TEXT_P(1); - text *commandText = PG_GETARG_TEXT_P(2); - - char *schedule = text_to_cstring(scheduleText); - char *command = text_to_cstring(commandText); - - int64 jobId = ScheduleCronJob(jobName, schedule, command); - - PG_RETURN_INT64(jobId); -} - - /* * ScheduleCronJob schedules a cron job with the given name. */ static int64 -ScheduleCronJob(Name jobName, char *schedule, char *command) +ScheduleCronJob(text *scheduleText, text *commandText, text *databaseText, + text *usernameText, bool active, text *jobnameText) { entry *parsedSchedule = NULL; + char *schedule; + char *command; + char *database_name; + char *jobName; + char *username; + AclResult aclresult; + Oid userIdcheckacl; int64 jobId = 0; Datum jobIdDatum = 0; StringInfoData querybuf; - Oid argTypes[7]; - Datum argValues[7]; + Oid argTypes[8]; + Datum argValues[8]; int argCount = 0; Oid savedUserId = InvalidOid; @@ -230,9 +210,13 @@ bool returnedJobIdIsNull = false; Oid userId = GetUserId(); - char *userName = GetUserNameFromId(userId, false); + userIdcheckacl = GetUserId(); + username = GetUserNameFromId(userId, false); + /* check schedule is valid */ + schedule = text_to_cstring(scheduleText); parsedSchedule = parse_cron_entry(schedule); + if (parsedSchedule == NULL) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -244,19 +228,19 @@ initStringInfo(&querybuf); appendStringInfo(&querybuf, - "insert into %s (schedule, command, nodename, nodeport, database, username", + "insert into %s (schedule, command, nodename, nodeport, database, username, active", quote_qualified_identifier(CRON_SCHEMA_NAME, JOBS_TABLE_NAME)); - if (jobName != NULL) + if (jobnameText != NULL) { appendStringInfo(&querybuf, ", jobname"); } - appendStringInfo(&querybuf, ") values ($1, $2, $3, $4, $5, $6"); + appendStringInfo(&querybuf, ") values ($1, $2, $3, $4, $5, $6, $7"); - if (jobName != NULL) + if (jobnameText != NULL) { - appendStringInfo(&querybuf, ", $7) "); + appendStringInfo(&querybuf, ", $8) "); appendStringInfo(&querybuf, "on conflict on constraint jobname_username_uniq "); appendStringInfo(&querybuf, "do update set "); appendStringInfo(&querybuf, "schedule = EXCLUDED.schedule, "); @@ -274,6 +258,7 @@ argCount++; argTypes[1] = TEXTOID; + command = text_to_cstring(commandText); argValues[1] = CStringGetTextDatum(command); argCount++; @@ -285,18 +270,57 @@ argValues[3] = Int32GetDatum(PostPortNumber); argCount++; + /* username has been provided */ + if (usernameText != NULL) + { + if (!superuser()) + elog(ERROR, "must be superuser to create a job for another role"); + + username = text_to_cstring(usernameText); + userIdcheckacl = GetRoleOidIfCanLogin(username); + } + + /* database has been provided */ + if (databaseText != NULL) + database_name = text_to_cstring(databaseText); + else + /* use the GUC */ + database_name = CronTableDatabaseName; + + /* first do a crude check to see whether superuser jobs are allowed */ + if (!EnableSuperuserJobs && superuser_arg(userIdcheckacl)) + { + ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("cannot schedule jobs as superuser"), + errdetail("Scheduling jobs as superuser is disallowed when " + "cron.enable_superuser_jobs is set to off."))); + } + + /* ensure the user that is used in the job can connect to the database */ + aclresult = pg_database_aclcheck(get_database_oid(database_name, false), + userIdcheckacl, ACL_CONNECT); + + if (aclresult != ACLCHECK_OK) + elog(ERROR, "User %s does not have CONNECT privilege on %s", + GetUserNameFromId(userIdcheckacl, false), database_name); + argTypes[4] = TEXTOID; - argValues[4] = CStringGetTextDatum(CronTableDatabaseName); + argValues[4] = CStringGetTextDatum(database_name); argCount++; argTypes[5] = TEXTOID; - argValues[5] = CStringGetTextDatum(userName); + argValues[5] = CStringGetTextDatum(username); + argCount++; + + argTypes[6] = BOOLOID; + argValues[6] = BoolGetDatum(active); argCount++; - if (jobName != NULL) + if (jobnameText != NULL) { - argTypes[6] = NAMEOID; - argValues[6] = NameGetDatum(jobName); + argTypes[7] = TEXTOID; + jobName = text_to_cstring(jobnameText); + argValues[7] = CStringGetTextDatum(jobName); argCount++; } @@ -338,8 +362,154 @@ return jobId; } +/* + * GetRoleOidIfCanLogin + * Checks user exist and can log in + */ +static Oid +GetRoleOidIfCanLogin(char *username) +{ + HeapTuple roletup; + Form_pg_authid rform; + Oid roleOid = InvalidOid; + + roletup = SearchSysCache1(AUTHNAME, PointerGetDatum(username)); + if (!HeapTupleIsValid(roletup)) + ereport(ERROR, + (errmsg("role \"%s\" does not exist", + username))); + + rform = (Form_pg_authid) GETSTRUCT(roletup); + + if (!rform->rolcanlogin) + ereport(ERROR, + (errmsg("role \"%s\" can not log in", + username), + errdetail("Jobs may only be run by roles that have the LOGIN attribute."))); + +#if (PG_VERSION_NUM < 120000) + roleOid = HeapTupleGetOid(roletup); +#else + roleOid = rform->oid; +#endif + + ReleaseSysCache(roletup); + return roleOid; +} + +/* + * cron_alter_job alter a job + */ +Datum +cron_alter_job(PG_FUNCTION_ARGS) +{ + int64 jobId; + text *scheduleText = NULL; + text *commandText = NULL; + text *databaseText = NULL; + text *usernameText = NULL; + bool active; + + if (PG_ARGISNULL(0)) + ereport(ERROR, (errmsg("job_id can not be NULL"))); + else + jobId = PG_GETARG_INT64(0); + + if (!PG_ARGISNULL(1)) + scheduleText = PG_GETARG_TEXT_P(1); + + if (!PG_ARGISNULL(2)) + commandText = PG_GETARG_TEXT_P(2); + + if (!PG_ARGISNULL(3)) + databaseText = PG_GETARG_TEXT_P(3); + + if (!PG_ARGISNULL(4)) + usernameText = PG_GETARG_TEXT_P(4); + + if (!PG_ARGISNULL(5)) + active = PG_GETARG_BOOL(5); + + AlterJob(jobId, scheduleText, commandText, databaseText, usernameText, + PG_ARGISNULL(5) ? NULL : &active); + + PG_RETURN_VOID(); +} + /* + * cron_schedule schedule a job + */ +Datum +cron_schedule(PG_FUNCTION_ARGS) +{ + text *scheduleText = NULL; + text *commandText = NULL; + int64 jobId; + + if (PG_ARGISNULL(0)) + ereport(ERROR, (errmsg("schedule can not be NULL"))); + else + scheduleText = PG_GETARG_TEXT_P(0); + + if (PG_ARGISNULL(1)) + ereport(ERROR, (errmsg("command can not be NULL"))); + else + commandText = PG_GETARG_TEXT_P(1); + + jobId = ScheduleCronJob(scheduleText, commandText, NULL, + NULL, true, NULL); + + PG_RETURN_INT64(jobId); +} + +/* + * cron_schedule schedule a named job + */ +Datum +cron_schedule_named(PG_FUNCTION_ARGS) +{ + text *scheduleText = NULL; + text *commandText = NULL; + text *databaseText = NULL; + text *usernameText = NULL; + bool active = true; + text *jobnameText = NULL; + int64 jobId; + + if (PG_ARGISNULL(0)) + ereport(ERROR, (errmsg("job_name can not be NULL"))); + else + jobnameText = PG_GETARG_TEXT_P(0); + + if (PG_ARGISNULL(1)) + ereport(ERROR, (errmsg("schedule can not be NULL"))); + else + scheduleText = PG_GETARG_TEXT_P(1); + + if (PG_ARGISNULL(2)) + ereport(ERROR, (errmsg("command can not be NULL"))); + else + commandText = PG_GETARG_TEXT_P(2); + + if (PG_NARGS() > 3) + { + if (!PG_ARGISNULL(3)) + databaseText = PG_GETARG_TEXT_P(3); + + if (!PG_ARGISNULL(4)) + usernameText = PG_GETARG_TEXT_P(4); + + if (!PG_ARGISNULL(5)) + active = PG_GETARG_BOOL(5); + } + + jobId = ScheduleCronJob(scheduleText, commandText, databaseText, + usernameText, active, jobnameText); + + PG_RETURN_INT64(jobId); +} +/* * NextRunId draws a new run ID from cron.runid_seq. */ int64 @@ -692,11 +862,29 @@ { MemoryContext oldContext = NULL; CronJob *job = NULL; + Oid jobOwnerId = InvalidOid; oldContext = MemoryContextSwitchTo(CronJobContext); job = TupleToCronJob(tupleDescriptor, heapTuple); - jobList = lappend(jobList, job); + + jobOwnerId = get_role_oid(job->userName, false); + if (!EnableSuperuserJobs && superuser_arg(jobOwnerId)) + { + /* + * Someone inserted a superuser into the metadata. Skip over the + * job when cron.enable_superuser_jobs is disabled. The memory + * will be cleaned up when CronJobContext is reset. + */ + ereport(WARNING, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("skipping job " INT64_FORMAT " since superuser jobs " + "are currently disallowed", + job->jobId))); + } + else + { + jobList = lappend(jobList, job); + } MemoryContextSwitchTo(oldContext); @@ -1013,6 +1201,171 @@ pgstat_report_activity(STATE_IDLE, NULL); } + +static void +AlterJob(int64 jobId, text *scheduleText, text *commandText, text *databaseText, text *usernameText, bool *active) +{ + StringInfoData querybuf; + Oid argTypes[7]; + Datum argValues[7]; + int i; + AclResult aclresult; + Oid userId; + Oid userIdcheckacl; + Oid savedUserId; + int savedSecurityContext; + char *database_name; + char *schedule; + char *command; + char *username; + char *currentuser; + entry *parsedSchedule = NULL; + + userId = GetUserId(); + userIdcheckacl = GetUserId(); + + currentuser = GetUserNameFromId(userId, false); + savedUserId = InvalidOid; + savedSecurityContext = 0; + + if (!PgCronHasBeenLoaded() || RecoveryInProgress() || !JobTableExists()) + { + return; + } + + initStringInfo(&querybuf); + i = 0; + + appendStringInfo(&querybuf, + "update %s.%s set", CRON_SCHEMA_NAME, JOBS_TABLE_NAME); + + /* username has been provided */ + if (usernameText != NULL) + { + if (!superuser()) + elog(ERROR, "must be superuser to alter username"); + + username = text_to_cstring(usernameText); + userIdcheckacl = GetRoleOidIfCanLogin(username); + } + else + { + username = currentuser; + } + + if (!EnableSuperuserJobs && superuser_arg(userIdcheckacl)) + { + ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("cannot schedule jobs as superuser"), + errdetail("Scheduling jobs as superuser is disallowed when " + "cron.enable_superuser_jobs is set to off."))); + } + + /* add the fields to be updated */ + /* database has been provided */ + if (databaseText != NULL) + { + database_name = text_to_cstring(databaseText); + /* ensure the user that is used in the job can connect to the database */ + aclresult = pg_database_aclcheck(get_database_oid(database_name, false), userIdcheckacl, ACL_CONNECT); + + if (aclresult != ACLCHECK_OK) + elog(ERROR, "User %s does not have CONNECT privilege on %s", GetUserNameFromId(userIdcheckacl, false), database_name); + + argTypes[i] = TEXTOID; + argValues[i] = CStringGetTextDatum(database_name); + i++; + appendStringInfo(&querybuf, " database = $%d,", i); + } + + /* ensure schedule is valid */ + if (scheduleText != NULL) + { + schedule = text_to_cstring(scheduleText); + parsedSchedule = parse_cron_entry(schedule); + + if (parsedSchedule == NULL) + { + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid schedule: %s", schedule))); + } + + free_entry(parsedSchedule); + + argTypes[i] = TEXTOID; + argValues[i] = CStringGetTextDatum(schedule); + i++; + appendStringInfo(&querybuf, " schedule = $%d,", i); + } + + if (commandText != NULL) + { + argTypes[i] = TEXTOID; + command = text_to_cstring(commandText); + argValues[i] = CStringGetTextDatum(command); + i++; + appendStringInfo(&querybuf, " command = $%d,", i); + } + + if (usernameText != NULL) + { + argTypes[i] = TEXTOID; + argValues[i] = CStringGetTextDatum(username); + i++; + appendStringInfo(&querybuf, " username = $%d,", i); + } + + if (active != NULL) + { + argTypes[i] = BOOLOID; + argValues[i] = BoolGetDatum(*active); + i++; + appendStringInfo(&querybuf, " active = $%d,", i); + } + + /* remove the last comma */ + querybuf.len--; + querybuf.data[querybuf.len] = '\0'; + + /* and add the where clause */ + argTypes[i] = INT8OID; + argValues[i] = Int64GetDatum(jobId); + i++; + + appendStringInfo(&querybuf, " where jobid = $%d", i); + + /* ensure the caller owns the row */ + argTypes[i] = TEXTOID; + argValues[i] = CStringGetTextDatum(currentuser); + i++; + + if (!superuser()) + appendStringInfo(&querybuf, " and username = $%d", i); + + if (i <= 2) + ereport(ERROR, (errmsg("no updates specified"), + errhint("You must specify at least one job attribute to change when calling alter_job"))); + + GetUserIdAndSecContext(&savedUserId, &savedSecurityContext); + SetUserIdAndSecContext(CronExtensionOwner(), SECURITY_LOCAL_USERID_CHANGE); + + /* Open SPI context. */ + if (SPI_connect() != SPI_OK_CONNECT) + elog(ERROR, "SPI_connect failed"); + if(SPI_execute_with_args(querybuf.data, + i, argTypes, argValues, NULL, false, 1) != SPI_OK_UPDATE) + elog(ERROR, "SPI_exec failed: %s", querybuf.data); + + pfree(querybuf.data); + + if (SPI_processed <= 0) + elog(ERROR, "Job %ld does not exist or you don't own it", jobId); + + SPI_finish(); + SetUserIdAndSecContext(savedUserId, savedSecurityContext); + InvalidateJobCache(); +} + void MarkPendingRunsAsFailed(void) { @@ -1101,3 +1454,16 @@ return jobRunDetailsTableOid != InvalidOid; } + +/* + * JobTableExists returns whether the job table exists. + */ +static bool +JobTableExists(void) +{ + Oid cronSchemaId = get_namespace_oid(CRON_SCHEMA_NAME, false); + Oid jobTableOid = get_relname_relid(JOBS_TABLE_NAME, + cronSchemaId); + + return jobTableOid != InvalidOid; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pg_cron-1.3.1/src/pg_cron.c new/pg_cron-1.4.1/src/pg_cron.c --- old/pg_cron-1.3.1/src/pg_cron.c 2021-03-29 12:17:31.000000000 +0200 +++ new/pg_cron-1.4.1/src/pg_cron.c 2021-09-25 18:55:16.000000000 +0200 @@ -36,9 +36,14 @@ #include "task_states.h" #include "job_metadata.h" -#include "poll.h" + +#ifdef HAVE_POLL_H +#include <poll.h> +#elif defined(HAVE_SYS_POLL_H) +#include <sys/poll.h> +#endif + #include "sys/time.h" -#include "sys/poll.h" #include "time.h" #include "access/genam.h" @@ -133,7 +138,7 @@ static void ManageCronTask(CronTask *task, TimestampTz currentTime); static void ExecuteSqlString(const char *sql); static void GetTaskFeedback(PGresult *result, CronTask *task); -static void GetBgwTaskFeedback(shm_mq_handle *responseq, CronTask *task); +static void GetBgwTaskFeedback(shm_mq_handle *responseq, CronTask *task, bool running); static bool jobCanceled(CronTask *task); static bool jobStartupTimeout(CronTask *task, TimestampTz currentTime); @@ -144,6 +149,7 @@ char *CronTableDatabaseName = "postgres"; static bool CronLogStatement = true; static bool CronLogRun = true; +static bool CronReloadConfig = false; /* flags set by signal handlers */ static volatile sig_atomic_t got_sigterm = false; @@ -154,8 +160,27 @@ static bool RebootJobsScheduled = false; static int RunningTaskCount = 0; static int MaxRunningTasks = 0; +static int CronLogMinMessages = WARNING; static bool UseBackgroundWorkers = false; +static const struct config_enum_entry cron_message_level_options[] = { + {"debug5", DEBUG5, false}, + {"debug4", DEBUG4, false}, + {"debug3", DEBUG3, false}, + {"debug2", DEBUG2, false}, + {"debug1", DEBUG1, false}, + {"debug", DEBUG2, true}, + {"info", INFO, false}, + {"notice", NOTICE, false}, + {"warning", WARNING, false}, + {"error", ERROR, false}, + {"log", LOG, false}, + {"fatal", FATAL, false}, + {"panic", PANIC, false}, + {NULL, 0, false} +}; + +static const char *cron_error_severity(int elevel); /* * _PG_init gets called when the extension is loaded. @@ -207,6 +232,16 @@ GUC_SUPERUSER_ONLY, NULL, NULL, NULL); + DefineCustomBoolVariable( + "cron.enable_superuser_jobs", + gettext_noop("Allow jobs to be scheduled as superuser"), + NULL, + &EnableSuperuserJobs, + true, + PGC_USERSET, + GUC_SUPERUSER_ONLY, + NULL, NULL, NULL); + DefineCustomStringVariable( "cron.host", gettext_noop("Hostname to connect to postgres."), @@ -252,6 +287,17 @@ GUC_SUPERUSER_ONLY, NULL, NULL, NULL); + DefineCustomEnumVariable( + "cron.log_min_messages", + gettext_noop("log_min_messages for the launcher bgworker."), + NULL, + &CronLogMinMessages, + WARNING, + cron_message_level_options, + PGC_SIGHUP, + GUC_SUPERUSER_ONLY, + NULL, NULL, NULL); + /* set up common data for all our workers */ worker.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION; worker.bgw_start_time = BgWorkerStart_RecoveryFinished; @@ -297,6 +343,7 @@ pg_cron_sighup(SIGNAL_ARGS) { CronJobCacheValid = false; + CronReloadConfig = true; if (MyProc != NULL) { @@ -362,6 +409,60 @@ } /* + * cron_error_severity --- get string representing elevel + */ +static const char * +cron_error_severity(int elevel) +{ + const char *elevel_char; + + switch (elevel) + { + case DEBUG1: + elevel_char = "DEBUG1"; + break; + case DEBUG2: + elevel_char = "DEBUG2"; + break; + case DEBUG3: + elevel_char = "DEBUG3"; + break; + case DEBUG4: + elevel_char = "DEBUG4"; + break; + case DEBUG5: + elevel_char = "DEBUG5"; + break; + case LOG: + elevel_char = "LOG"; + break; + case INFO: + elevel_char = "INFO"; + break; + case NOTICE: + elevel_char = "NOTICE"; + break; + case WARNING: + elevel_char = "WARNING"; + break; + case ERROR: + elevel_char = "ERROR"; + break; + case FATAL: + elevel_char = "FATAL"; + break; + case PANIC: + elevel_char = "PANIC"; + break; + default: + elevel_char = "???"; + break; + } + + return elevel_char; +} + +/* * bgw_generate_returned_message - * generates the message to be inserted into the job_run_details table * first part is comming from error_severity (elog.c) @@ -516,6 +617,10 @@ ereport(LOG, (errmsg("pg_cron scheduler started"))); + /* set the desired log_min_messages */ + SetConfigOption("log_min_messages", cron_error_severity(CronLogMinMessages), + PGC_POSTMASTER, PGC_S_OVERRIDE); + MemoryContextSwitchTo(CronLoopContext); while (!got_sigterm) @@ -530,6 +635,15 @@ RefreshTaskHash(); } + if (CronReloadConfig) + { + /* set the desired log_min_messages */ + ProcessConfigFile(PGC_SIGHUP); + SetConfigOption("log_min_messages", cron_error_severity(CronLogMinMessages), + PGC_POSTMASTER, PGC_S_OVERRIDE); + CronReloadConfig = false; + } + taskList = CurrentTaskList(); currentTime = GetCurrentTimestamp(); @@ -913,7 +1027,7 @@ int activeTaskCount = 0; ListCell *taskCell = NULL; - polledTasks = (CronTask **) palloc0(taskCount * sizeof(CronTask)); + polledTasks = (CronTask **) palloc0(taskCount * sizeof(CronTask *)); pollFDs = (struct pollfd *) palloc0(taskCount * sizeof(struct pollfd)); currentTime = GetCurrentTimestamp(); @@ -1560,10 +1674,6 @@ break; } - /* still waiting for job to complete */ - if (GetBackgroundWorkerPid(&task->handle, &pid) != BGWH_STOPPED) - break; - toc = shm_toc_attach(PG_CRON_MAGIC, dsm_segment_address(task->seg)); #if PG_VERSION_NUM < 100000 mq = shm_toc_lookup(toc, PG_CRON_KEY_QUEUE); @@ -1571,7 +1681,16 @@ mq = shm_toc_lookup(toc, PG_CRON_KEY_QUEUE, false); #endif responseq = shm_mq_attach(mq, task->seg, NULL); - GetBgwTaskFeedback(responseq, task); + + /* still waiting for job to complete */ + if (GetBackgroundWorkerPid(&task->handle, &pid) != BGWH_STOPPED) + { + GetBgwTaskFeedback(responseq, task, true); + shm_mq_detach(responseq); + break; + } + + GetBgwTaskFeedback(responseq, task, false); task->state = CRON_TASK_DONE; dsm_detach(task->seg); @@ -1736,7 +1855,7 @@ } static void -GetBgwTaskFeedback(shm_mq_handle *responseq, CronTask *task) +GetBgwTaskFeedback(shm_mq_handle *responseq, CronTask *task, bool running) { TimestampTz end_time; @@ -1783,9 +1902,10 @@ if (edata.elevel >= ERROR) UpdateJobRunDetail(task->runId, NULL, GetCronStatus(CRON_STATUS_FAILED), display_msg.data, NULL, &end_time); + else if (running) + UpdateJobRunDetail(task->runId, NULL, NULL, display_msg.data, NULL, NULL); else UpdateJobRunDetail(task->runId, NULL, GetCronStatus(CRON_STATUS_SUCCEEDED), display_msg.data, NULL, &end_time); - } ereport(LOG, (errmsg("cron job " INT64_FORMAT ": %s",