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 2023-04-19 18:41:36
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/pg_cron (Old)
 and      /work/SRC/openSUSE:Factory/.pg_cron.new.2023 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "pg_cron"

Wed Apr 19 18:41:36 2023 rev:12 rq:1080337 version:1.5.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/pg_cron/pg_cron.changes  2023-01-02 
15:02:04.861321496 +0100
+++ /work/SRC/openSUSE:Factory/.pg_cron.new.2023/pg_cron.changes        
2023-04-19 18:41:37.596644501 +0200
@@ -1,0 +2,39 @@
+Wed Apr 19 13:43:45 UTC 2023 - Reinhard Max <m...@suse.com>
+
+- PostgreSQL 10 is EOL. 
+
+-------------------------------------------------------------------
+Sat Apr  8 21:47:36 UTC 2023 - Marcus Rueckert <mrueck...@suse.de>
+
+- update to 1.5.2
+  - Fixes a bug that caused crashes after upgrading binaries to
+    1.5, by Polina Bungina
+
+-------------------------------------------------------------------
+Fri Feb 10 14:46:31 UTC 2023 - Marcus Rueckert <mrueck...@suse.de>
+
+- update to 1.5.1
+  - Fixes a bug that caused incorrect parsing of some crons schedules
+
+-------------------------------------------------------------------
+Tue Feb  7 12:00:24 UTC 2023 - Marcus Rueckert <mrueck...@suse.de>
+
+- update to 1.5.0
+  - Adds the possibility of scheduling a job with a 1-59 second
+    interval
+  - Adds a cron.timezone setting to configure the timezone of cron
+    schedules
+  - Removes pg_stat_activity reporting of internal pg_cron metadata
+    queries
+  - Fixes a bug that caused issues with long job names
+  - Fixes a bug that caused inactive @reboot jobs to still run
+  - Fixes a bug that could limit concurrency for background workers
+  - Fixes a bug that prevented compiling on ARM
+  - Fixes regression tests for PostgreSQL <= 12
+
+-------------------------------------------------------------------
+Tue Jan 31 15:04:18 UTC 2023 - Marcus Rueckert <mrueck...@suse.de>
+
+- enable pg 15
+
+-------------------------------------------------------------------

Old:
----
  pg_cron-1.4.2.tar.gz

New:
----
  pg_cron-1.5.2.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ pg_cron.spec ++++++
--- /var/tmp/diff_new_pack.PfMJRd/_old  2023-04-19 18:41:38.124647574 +0200
+++ /var/tmp/diff_new_pack.PfMJRd/_new  2023-04-19 18:41:38.132647621 +0200
@@ -21,7 +21,7 @@
 %{pg_version_from_name}
 
 Name:           %{pg_name}-%{ext_name}
-Version:        1.4.2
+Version:        1.5.2
 Release:        0
 Summary:        PostgreSQL module for simple job schedule
 License:        PostgreSQL

++++++ _multibuild ++++++
--- /var/tmp/diff_new_pack.PfMJRd/_old  2023-04-19 18:41:38.168647830 +0200
+++ /var/tmp/diff_new_pack.PfMJRd/_new  2023-04-19 18:41:38.172647854 +0200
@@ -1,8 +1,8 @@
 <multibuild>
-  <package>postgresql10</package>
   <package>postgresql11</package>
   <package>postgresql12</package>
   <package>postgresql13</package>
   <package>postgresql14</package>
+  <package>postgresql15</package>
 </multibuild>
 

++++++ pg_cron-1.4.2.tar.gz -> pg_cron-1.5.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pg_cron-1.4.2/CHANGELOG.md 
new/pg_cron-1.5.2/CHANGELOG.md
--- old/pg_cron-1.4.2/CHANGELOG.md      2022-07-15 00:14:59.000000000 +0200
+++ new/pg_cron-1.5.2/CHANGELOG.md      2023-04-06 13:56:10.000000000 +0200
@@ -1,7 +1,26 @@
+### pg_cron v1.5.2 (April 9, 2023) ###
+
+* Fixes a bug that caused crashes after upgrading binaries to 1.5, by Polina 
Bungina
+
+### pg_cron v1.5.1 (February 9, 2023) ###
+
+* Fixes a bug that caused incorrect parsing of some crons schedules
+
+### pg_cron v1.5.0 (February 7, 2023) ###
+
+* Adds the possibility of scheduling a job with a 1-59 second interval
+* Adds a cron.timezone setting to configure the timezone of cron schedules
+* Removes pg_stat_activity reporting of internal pg_cron metadata queries
+* Fixes a bug that caused issues with long job names
+* Fixes a bug that caused inactive @reboot jobs to still run
+* Fixes a bug that could limit concurrency for background workers
+* Fixes a bug that prevented compiling on ARM
+* Fixes regression tests for PostgreSQL <= 12
+
 ### pg_cron v1.4.2 (July 15, 2022) ###
 
 * Fixes a bug that could lead to privilege escalation if users can trigger 
CREATE EXTENSION
-* Add compatibility for PostgreSQL 15 beta
+* Add compatibility for PostgreSQL 15
 * Fixes a bug that could cause unschedule to crash
 * Ensures that cron.max_running_jobs is not higher than possible connection 
count
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pg_cron-1.4.2/Makefile new/pg_cron-1.5.2/Makefile
--- old/pg_cron-1.4.2/Makefile  2022-07-15 00:14:59.000000000 +0200
+++ new/pg_cron-1.5.2/Makefile  2023-04-06 13:56:10.000000000 +0200
@@ -12,7 +12,7 @@
 MODULE_big = $(EXTENSION)
 OBJS = $(patsubst %.c,%.o,$(wildcard src/*.c))
 ifeq ($(CC),gcc)
-    PG_CPPFLAGS = -std=c99 -Wall -Wextra -Werror -Wno-unused-parameter 
-Wno-maybe-uninitialized -Wno-implicit-fallthrough -Iinclude -I$(libpq_srcdir)
+    PG_CPPFLAGS = -std=c99 -Wall -Wextra -Werror -Wno-unused-parameter 
-Wno-uninitialized -Wno-implicit-fallthrough -Iinclude -I$(libpq_srcdir)
 else
     PG_CPPFLAGS = -std=c99 -Wall -Wextra -Werror -Wno-unused-parameter 
-Wno-implicit-fallthrough -Iinclude -I$(libpq_srcdir)
 endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pg_cron-1.4.2/README.md new/pg_cron-1.5.2/README.md
--- old/pg_cron-1.4.2/README.md 2022-07-15 00:14:59.000000000 +0200
+++ new/pg_cron-1.5.2/README.md 2023-04-06 13:56:10.000000000 +0200
@@ -1,10 +1,10 @@
 [![Citus Banner](/github-banner.png)](https://www.citusdata.com/)
 
-[![Slack 
Status](http://slack.citusdata.com/badge.svg)](https://slack.citusdata.com)
+[![Slack 
Status](https://citus-slack.herokuapp.com/badge.svg)](https://citus-public.slack.com/)
 
 ## What is pg_cron?
 
-pg_cron is a simple cron-based job scheduler for PostgreSQL (10 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:
+pg_cron is a simple cron-based job scheduler for PostgreSQL (10 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. You can also use '[1-59] seconds' to schedule a job based on an 
interval.
 
 ```sql
 -- Delete old data on Saturday at 3:30am (GMT)
@@ -30,12 +30,20 @@
  unschedule 
 ------------
  t
-(1 row)
 
 SELECT cron.unschedule(42);
  unschedule
 ------------
           t
+
+-- Vacuum every Sunday at 4:00am (GMT) in a database other than the one 
pg_cron is installed in
+SELECT cron.schedule_in_database('weekly-vacuum', '0 4 * * 0', 'VACUUM', 
'some_other_database');
+ schedule
+----------
+       44
+
+-- Call a stored procedure every 5 seconds
+SELECT cron.schedule('process-updates', '5 seconds', 'CALL 
process_updates()'); 
 ```
 
 pg_cron can run multiple jobs in parallel, but it runs at most one instance of 
a job at a time. If a second run is supposed to start before the first one 
finishes, then the second run is queued and started as soon as the first run 
completes.
@@ -56,22 +64,22 @@
 
 An easy way to create a cron schedule is: [crontab.guru](http://crontab.guru/).
 
-The code in pg_cron that handles parsing and scheduling comes directly from 
the cron source code by Paul Vixie, hence the same options are supported. Be 
aware that pg_cron always uses GMT!
+The code in pg_cron that handles parsing and scheduling comes directly from 
the cron source code by Paul Vixie, hence the same options are supported.
 
 ## Installing pg_cron
 
-Install on Red Hat, CentOS, Fedora, Amazon Linux with PostgreSQL 12 using 
[PGDG](https://yum.postgresql.org/repopackages/):
+Install on Red Hat, CentOS, Fedora, Amazon Linux with PostgreSQL 15 using 
[PGDG](https://yum.postgresql.org/repopackages/):
 
 ```bash
 # Install the pg_cron extension
-sudo yum install -y pg_cron_12
+sudo yum install -y pg_cron_15
 ```
 
-Install on Debian, Ubuntu with PostgreSQL 12 using 
[apt.postgresql.org](https://wiki.postgresql.org/wiki/Apt):
+Install on Debian, Ubuntu with PostgreSQL 15 using 
[apt.postgresql.org](https://wiki.postgresql.org/wiki/Apt):
 
 ```bash
 # Install the pg_cron extension
-sudo apt-get -y install postgresql-12-cron
+sudo apt-get -y install postgresql-15-cron
 ```
 
 You can also install pg_cron by building it from source:
@@ -80,7 +88,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-12/bin:$PATH
+export PATH=/usr/pgsql-15/bin:$PATH
 make && sudo PATH=$PATH make install
 ```
 
@@ -99,9 +107,18 @@
 ```
 # add to postgresql.conf
 
-# optionally, specify the database in which the pg_cron background worker 
should run (defaults to postgres) 
+# optionally, specify the database in which the pg_cron background worker 
should run (defaults to postgres)
 cron.database_name = 'postgres'
 ```
+`pg_cron` may only be installed to one database in a cluster. If you need to 
run jobs in multiple databases, use `cron.schedule_in_database()`.
+
+Previously pg_cron could only use GMT time, but now you can adapt your time by 
setting `cron.timezone` in postgresql.conf.
+```
+# add to postgresql.conf
+
+# optionally, specify the timezone in which the pg_cron background worker 
should run (defaults to GMT). E.g:
+cron.timezone = 'PRC'
+```
 
 After restarting PostgreSQL, you can create the pg_cron functions and metadata 
tables using `CREATE EXTENSION pg_cron`.
 
@@ -113,9 +130,17 @@
 GRANT USAGE ON SCHEMA cron TO marco;
 ```
 
+### Ensuring pg_cron can start jobs
+
 **Important**: By default, pg_cron uses libpq to open a new connection to the 
local database, which needs to be allowed by 
[pg_hba.conf](https://www.postgresql.org/docs/current/static/auth-pg-hba-conf.html).
 
 It may be necessary to enable `trust` authentication for connections coming 
from localhost in  for the user running the cron job, or you can add the 
password to a [.pgpass 
file](https://www.postgresql.org/docs/current/static/libpq-pgpass.html), which 
libpq will use when opening a connection. 
 
+You can also use a unix domain socket directory as the hostname and enable 
`trust` authentication for local connections in 
[pg_hba.conf](https://www.postgresql.org/docs/current/static/auth-pg-hba-conf.html),
 which is normally safe:
+```
+# Connect via a unix domain socket
+cron.host = '/tmp'
+```
+
 Alternatively, pg_cron can be configured to use background workers. In that 
case, the number of concurrent jobs is limited by the `max_worker_processes` 
setting, so you may need to raise that.
 
 ```
@@ -125,13 +150,36 @@
 max_worker_processes = 20
 ```
 
-You can also use a unix domain socket directory as the hostname and enable 
`trust` authentication for local connections in 
[pg_hba.conf](https://www.postgresql.org/docs/current/static/auth-pg-hba-conf.html),
 which is normally safe:
+For security, jobs are executed in the database in which the `cron.schedule` 
function is called with the same permissions as the current user. In addition, 
users are only able to see their own jobs in the `cron.job` table.
+
+## Viewing job run details
+
+You can view the status of running and recently completed job runs in the 
`cron.job_run_details`:
+
+```sql
+select * from cron.job_run_details order by start_time desc limit 5;
+┌───────┬───────┬─────────┬──────────┬──────────┬───────────────────┬───────────┬──────────────────┬───────────────────────────────┬───────────────────────────────┐
+│ jobid │ runid │ job_pid │ database │ username │      command     
 │  status   │  return_message  │          start_time           │       
    end_time            │
+├───────┼───────┼─────────┼──────────┼──────────┼───────────────────┼───────────┼──────────────────┼───────────────────────────────┼───────────────────────────────┤
+│    10 │  4328 │    2610 │ postgres │ marco    │ select process() 
 │ succeeded │ SELECT 1         │ 2023-02-07 09:30:00.098164+01 │ 
2023-02-07 09:30:00.130729+01 │
+│    10 │  4327 │    2609 │ postgres │ marco    │ select process() 
 │ succeeded │ SELECT 1         │ 2023-02-07 09:29:00.015168+01 │ 
2023-02-07 09:29:00.832308+01 │
+│    10 │  4321 │    2603 │ postgres │ marco    │ select process() 
 │ succeeded │ SELECT 1         │ 2023-02-07 09:28:00.011965+01 │ 
2023-02-07 09:28:01.420901+01 │
+│    10 │  4320 │    2602 │ postgres │ marco    │ select process() 
 │ failed    │ server restarted │ 2023-02-07 09:27:00.011833+01 │ 
2023-02-07 09:27:00.72121+01  │
+│     9 │  4320 │    2602 │ postgres │ marco    │ select 
do_stuff() │ failed    │ job canceled     │ 2023-02-07 09:26:00.011833+01 
│ 2023-02-07 09:26:00.22121+01  │
+└───────┴───────┴─────────┴──────────┴──────────┴───────────────────┴───────────┴──────────────────┴───────────────────────────────┴───────────────────────────────┘
+(10 rows)
 ```
-# Connect via a unix domain socket
-cron.host = '/tmp'
+ 
+The records in `cron.job_run_details` are not cleaned automatically, but every 
user that can schedule cron jobs also has permission to delete their own 
`cron.job_run_details` records. 
+
+Especially when you have jobs that run every few seconds, it can be a good 
idea to clean up regularly, which can easily be done using pg_cron itself:
+
+```sql
+-- Delete old cron.job_run_details records of the current user every day at 
noon
+SELECT  cron.schedule('delete-job-run-details', '0 12 * * *', $$DELETE FROM 
cron.job_run_details WHERE end_time < now() - interval '7 days'$$);
 ```
 
-For security, jobs are executed in the database in which the `cron.schedule` 
function is called with the same permissions as the current user. In addition, 
users are only able to see their own jobs in the `cron.job` table.
+If you do not want to use `cron.job_run_details` at all, then you can add 
`cron.log_run = off` to `postgresql.conf`.
 
 ## Example use cases
 
@@ -161,3 +209,8 @@
 | [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:  |
+
+
+## Code of Conduct
+
+This project has adopted the [Microsoft Open Source Code of 
Conduct](https://opensource.microsoft.com/codeofconduct/). For more information 
see the [Code of Conduct 
FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact 
[openc...@microsoft.com](mailto:openc...@microsoft.com) with any additional 
questions or comments.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pg_cron-1.4.2/expected/pg_cron-test.out 
new/pg_cron-1.5.2/expected/pg_cron-test.out
--- old/pg_cron-1.4.2/expected/pg_cron-test.out 2022-07-15 00:14:59.000000000 
+0200
+++ new/pg_cron-1.5.2/expected/pg_cron-test.out 2023-04-06 13:56:10.000000000 
+0200
@@ -29,9 +29,35 @@
 -- Invalid input: input too long
 SELECT cron.schedule(repeat('a', 1000), '');
 ERROR:  invalid schedule: 
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
 aaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+HINT:  Use cron format (e.g. 5 4 * * *), or interval format '[1-59] seconds'
+-- Invalid input: missing parts
+SELECT cron.schedule('* * * *', 'SELECT 1'); 
+ERROR:  invalid schedule: * * * *
+HINT:  Use cron format (e.g. 5 4 * * *), or interval format '[1-59] seconds'
+-- Invalid input: trailing characters
+SELECT cron.schedule('5 secondc', 'SELECT 1'); 
+ERROR:  invalid schedule: 5 secondc
+HINT:  Use cron format (e.g. 5 4 * * *), or interval format '[1-59] seconds'
+SELECT cron.schedule('50 seconds c', 'SELECT 1'); 
+ERROR:  invalid schedule: 50 seconds c
+HINT:  Use cron format (e.g. 5 4 * * *), or interval format '[1-59] seconds'
+-- Invalid input: seconds out of range
+SELECT cron.schedule('-1 seconds', 'SELECT 1'); 
+ERROR:  invalid schedule: -1 seconds
+HINT:  Use cron format (e.g. 5 4 * * *), or interval format '[1-59] seconds'
+SELECT cron.schedule('0 seconds', 'SELECT 1'); 
+ERROR:  invalid schedule: 0 seconds
+HINT:  Use cron format (e.g. 5 4 * * *), or interval format '[1-59] seconds'
+SELECT cron.schedule('60 seconds', 'SELECT 1'); 
+ERROR:  invalid schedule: 60 seconds
+HINT:  Use cron format (e.g. 5 4 * * *), or interval format '[1-59] seconds'
+SELECT cron.schedule('10000000000 seconds', 'SELECT 1'); 
+ERROR:  invalid schedule: 10000000000 seconds
+HINT:  Use cron format (e.g. 5 4 * * *), or interval format '[1-59] seconds'
 -- Try to update pg_cron on restart
 SELECT cron.schedule('@restar', 'ALTER EXTENSION pg_cron UPDATE');
 ERROR:  invalid schedule: @restar
+HINT:  Use cron format (e.g. 5 4 * * *), or interval format '[1-59] seconds'
 SELECT cron.schedule('@restart', 'ALTER EXTENSION pg_cron UPDATE');
  schedule 
 ----------
@@ -196,16 +222,50 @@
 NOTICE:  type "current_setting" does not exist, skipping
 CREATE TYPE current_setting AS ENUM ('cron.database_name');
 CREATE OR REPLACE FUNCTION public.func1(text, current_setting) RETURNS text
-    LANGUAGE sql volatile AS 'INSERT INTO test(data) VALUES (current_user); 
SELECT current_database();';
+    LANGUAGE sql volatile AS 'INSERT INTO test(data) VALUES (current_user); 
SELECT current_database()::text;';
 CREATE OR REPLACE FUNCTION public.func1(current_setting) RETURNS text
-    LANGUAGE sql volatile AS 'INSERT INTO test(data) VALUES (current_user); 
SELECT current_database();';
+    LANGUAGE sql volatile AS 'INSERT INTO test(data) VALUES (current_user); 
SELECT current_database()::text;';
 CREATE CAST (current_setting AS text) WITH FUNCTION 
public.func1(current_setting) AS IMPLICIT;
-CREATE EXTENSION pg_cron VERSION '1.4';
+CREATE EXTENSION pg_cron;
 select * from public.test;
  data 
 ------
 (0 rows)
 
+-- valid interval jobs
+SELECT cron.schedule('1 second', 'SELECT 1'); 
+ schedule 
+----------
+        1
+(1 row)
+
+SELECT cron.schedule(' 30 sEcOnDs ', 'SELECT 1'); 
+ schedule 
+----------
+        2
+(1 row)
+
+SELECT cron.schedule('59 seconds', 'SELECT 1'); 
+ schedule 
+----------
+        3
+(1 row)
+
+SELECT cron.schedule('17  seconds ', 'SELECT 1'); 
+ schedule 
+----------
+        4
+(1 row)
+
+SELECT jobid, jobname, schedule, command FROM cron.job ORDER BY jobid;
+ jobid | jobname |   schedule   | command  
+-------+---------+--------------+----------
+     1 |         | 1 second     | SELECT 1
+     2 |         |  30 sEcOnDs  | SELECT 1
+     3 |         | 59 seconds   | SELECT 1
+     4 |         | 17  seconds  | SELECT 1
+(4 rows)
+
 -- cleaning
 DROP EXTENSION pg_cron;
 drop user pgcron_cront;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pg_cron-1.4.2/include/cron.h 
new/pg_cron-1.5.2/include/cron.h
--- old/pg_cron-1.4.2/include/cron.h    2022-07-15 00:14:59.000000000 +0200
+++ new/pg_cron-1.5.2/include/cron.h    2023-04-06 13:56:10.000000000 +0200
@@ -159,7 +159,7 @@
        uid_t           uid;    
        gid_t           gid;
        char            **envp;
-       char            *cmd;
+       int         secondsInterval;
        bitstr_t        bit_decl(minute, MINUTE_COUNT);
        bitstr_t        bit_decl(hour,   HOUR_COUNT);
        bitstr_t        bit_decl(dom,    DOM_COUNT);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pg_cron-1.4.2/include/cron_job.h 
new/pg_cron-1.5.2/include/cron_job.h
--- old/pg_cron-1.4.2/include/cron_job.h        2022-07-15 00:14:59.000000000 
+0200
+++ new/pg_cron-1.5.2/include/cron_job.h        2023-04-06 13:56:10.000000000 
+0200
@@ -27,7 +27,7 @@
        text database;
        text userName;
        bool active;
-       Name jobName;
+       text jobName;
 #endif
 } FormData_cron_job;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pg_cron-1.4.2/include/job_metadata.h 
new/pg_cron-1.5.2/include/job_metadata.h
--- old/pg_cron-1.4.2/include/job_metadata.h    2022-07-15 00:14:59.000000000 
+0200
+++ new/pg_cron-1.5.2/include/job_metadata.h    2023-04-06 13:56:10.000000000 
+0200
@@ -39,7 +39,7 @@
        char *database;
        char *userName;
        bool active;
-       Name jobName;
+       char *jobName;
 } CronJob;
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pg_cron-1.4.2/include/task_states.h 
new/pg_cron-1.5.2/include/task_states.h
--- old/pg_cron-1.4.2/include/task_states.h     2022-07-15 00:14:59.000000000 
+0200
+++ new/pg_cron-1.5.2/include/task_states.h     2023-04-06 13:56:10.000000000 
+0200
@@ -16,6 +16,7 @@
 #include "libpq-fe.h"
 #include "postmaster/bgworker.h"
 #include "storage/dsm.h"
+#include "storage/shm_mq.h"
 #include "utils/timestamp.h"
 
 
@@ -48,10 +49,13 @@
        PGconn *connection;
        PostgresPollingStatusType pollingStatus;
        TimestampTz startDeadline;
+       TimestampTz lastStartTime;
+       uint32 secondsInterval;
        bool isSocketReady;
        bool isActive;
        char *errorMessage;
        bool freeErrorMessage;
+       shm_mq_handle *sharedMemoryQueue;
        dsm_segment *seg;
        BackgroundWorkerHandle handle;
 } CronTask;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pg_cron-1.4.2/pg_cron--1.2--1.3.sql 
new/pg_cron-1.5.2/pg_cron--1.2--1.3.sql
--- old/pg_cron-1.4.2/pg_cron--1.2--1.3.sql     2022-07-15 00:14:59.000000000 
+0200
+++ new/pg_cron-1.5.2/pg_cron--1.2--1.3.sql     2023-04-06 13:56:10.000000000 
+0200
@@ -17,7 +17,7 @@
 GRANT SELECT ON cron.job_run_details TO public;
 GRANT DELETE ON cron.job_run_details TO public;
 ALTER TABLE cron.job_run_details ENABLE ROW LEVEL SECURITY;
-CREATE POLICY cron_job_run_details_policy ON cron.job_run_details USING 
(username = current_user);
+CREATE POLICY cron_job_run_details_policy ON cron.job_run_details USING 
(username OPERATOR(pg_catalog.=) current_user);
 
 SELECT pg_catalog.pg_extension_config_dump('cron.job_run_details', '');
 SELECT pg_catalog.pg_extension_config_dump('cron.runid_seq', '');
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pg_cron-1.4.2/pg_cron--1.4-1--1.5.sql 
new/pg_cron-1.5.2/pg_cron--1.4-1--1.5.sql
--- old/pg_cron-1.4.2/pg_cron--1.4-1--1.5.sql   1970-01-01 01:00:00.000000000 
+0100
+++ new/pg_cron-1.5.2/pg_cron--1.4-1--1.5.sql   2023-04-06 13:56:10.000000000 
+0200
@@ -0,0 +1,9 @@
+ALTER TABLE cron.job ALTER COLUMN jobname TYPE text;
+
+DROP FUNCTION cron.unschedule(name);
+CREATE FUNCTION cron.unschedule(job_name text)
+    RETURNS bool
+    LANGUAGE C STRICT
+    AS 'MODULE_PATHNAME', $$cron_unschedule_named$$;
+COMMENT ON FUNCTION cron.unschedule(text)
+    IS 'unschedule a pg_cron job';
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pg_cron-1.4.2/pg_cron.control 
new/pg_cron-1.5.2/pg_cron.control
--- old/pg_cron-1.4.2/pg_cron.control   2022-07-15 00:14:59.000000000 +0200
+++ new/pg_cron-1.5.2/pg_cron.control   2023-04-06 13:56:10.000000000 +0200
@@ -1,4 +1,5 @@
 comment = 'Job scheduler for PostgreSQL'
-default_version = '1.4-1'
+default_version = '1.5'
 module_pathname = '$libdir/pg_cron'
 relocatable = false
+schema = pg_catalog
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pg_cron-1.4.2/sql/pg_cron-test.sql 
new/pg_cron-1.5.2/sql/pg_cron-test.sql
--- old/pg_cron-1.4.2/sql/pg_cron-test.sql      2022-07-15 00:14:59.000000000 
+0200
+++ new/pg_cron-1.5.2/sql/pg_cron-test.sql      2023-04-06 13:56:10.000000000 
+0200
@@ -13,6 +13,19 @@
 -- Invalid input: input too long
 SELECT cron.schedule(repeat('a', 1000), '');
 
+-- Invalid input: missing parts
+SELECT cron.schedule('* * * *', 'SELECT 1'); 
+
+-- Invalid input: trailing characters
+SELECT cron.schedule('5 secondc', 'SELECT 1'); 
+SELECT cron.schedule('50 seconds c', 'SELECT 1'); 
+
+-- Invalid input: seconds out of range
+SELECT cron.schedule('-1 seconds', 'SELECT 1'); 
+SELECT cron.schedule('0 seconds', 'SELECT 1'); 
+SELECT cron.schedule('60 seconds', 'SELECT 1'); 
+SELECT cron.schedule('10000000000 seconds', 'SELECT 1'); 
+
 -- Try to update pg_cron on restart
 SELECT cron.schedule('@restar', 'ALTER EXTENSION pg_cron UPDATE');
 SELECT cron.schedule('@restart', 'ALTER EXTENSION pg_cron UPDATE');
@@ -110,16 +123,23 @@
 CREATE TYPE current_setting AS ENUM ('cron.database_name');
 
 CREATE OR REPLACE FUNCTION public.func1(text, current_setting) RETURNS text
-    LANGUAGE sql volatile AS 'INSERT INTO test(data) VALUES (current_user); 
SELECT current_database();';
+    LANGUAGE sql volatile AS 'INSERT INTO test(data) VALUES (current_user); 
SELECT current_database()::text;';
 
 CREATE OR REPLACE FUNCTION public.func1(current_setting) RETURNS text
-    LANGUAGE sql volatile AS 'INSERT INTO test(data) VALUES (current_user); 
SELECT current_database();';
+    LANGUAGE sql volatile AS 'INSERT INTO test(data) VALUES (current_user); 
SELECT current_database()::text;';
 
 CREATE CAST (current_setting AS text) WITH FUNCTION 
public.func1(current_setting) AS IMPLICIT;
 
-CREATE EXTENSION pg_cron VERSION '1.4';
+CREATE EXTENSION pg_cron;
 select * from public.test;
 
+-- valid interval jobs
+SELECT cron.schedule('1 second', 'SELECT 1'); 
+SELECT cron.schedule(' 30 sEcOnDs ', 'SELECT 1'); 
+SELECT cron.schedule('59 seconds', 'SELECT 1'); 
+SELECT cron.schedule('17  seconds ', 'SELECT 1'); 
+SELECT jobid, jobname, schedule, command FROM cron.job ORDER BY jobid;
+
 -- cleaning
 DROP EXTENSION pg_cron;
 drop user pgcron_cront;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pg_cron-1.4.2/src/entry.c 
new/pg_cron-1.5.2/src/entry.c
--- old/pg_cron-1.4.2/src/entry.c       2022-07-15 00:14:59.000000000 +0200
+++ new/pg_cron-1.5.2/src/entry.c       2023-04-06 13:56:10.000000000 +0200
@@ -35,7 +35,7 @@
        e_cmd, e_timespec, e_username, e_cmd_len
 } ecode_e;
 
-static char    get_list(bitstr_t *, int, int, char *[], int, FILE *),
+static int     get_list(bitstr_t *, int, int, char *[], int, FILE *),
                get_range(bitstr_t *, int, int, char *[], int, FILE *),
                get_number(int *, int, char *[], int, FILE *);
 static int     set_element(bitstr_t *, int, int, int);
@@ -44,8 +44,6 @@
 void
 free_entry(entry *e)
 {
-       if (e->cmd)
-               free(e->cmd);
        free(e);
 }
 
@@ -74,7 +72,7 @@
         *      minutes hours doms months dows USERNAME cmd\n
         */
 
-       ecode_e ecode = e_none;
+       ecode_e ecode = e_none;
        entry *e = (entry *) calloc(sizeof(entry), sizeof(char));
        int     ch = 0;
        char cmd[MAX_COMMAND];
@@ -233,7 +231,7 @@
        return e;
 
  eof:
-       elog(LOG, "failed to parse entry %d", ecode);
+       elog(DEBUG1, "failed to parse entry %d", ecode);
        free_entry(e);
        while (ch != EOF && ch != '\n')
                ch = get_char(file);
@@ -241,7 +239,7 @@
 }
 
 
-static char
+static int
 get_list(bits, low, high, names, ch, file)
        bitstr_t        *bits;          /* one bit per flag, default=FALSE */
        int             low, high;      /* bounds, impl. offset for bitstr */
@@ -288,7 +286,7 @@
 }
 
 
-static char
+static int
 get_range(bits, low, high, names, ch, file)
        bitstr_t        *bits;          /* one bit per flag, default=FALSE */
        int             low, high;      /* bounds, impl. offset for bitstr */
@@ -391,7 +389,7 @@
 }
 
 
-static char
+static int
 get_number(numptr, low, names, ch, file)
        int     *numptr;        /* where does the result go? */
        int     low;            /* offset applied to result if symbolic enum 
used */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pg_cron-1.4.2/src/job_metadata.c 
new/pg_cron-1.5.2/src/job_metadata.c
--- old/pg_cron-1.4.2/src/job_metadata.c        2022-07-15 00:14:59.000000000 
+0200
+++ new/pg_cron-1.5.2/src/job_metadata.c        2023-04-06 13:56:10.000000000 
+0200
@@ -26,6 +26,9 @@
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "catalog/pg_extension.h"
+#if (PG_VERSION_NUM >= 160000)
+#include "catalog/pg_database.h"
+#endif
 #include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "commands/extension.h"
@@ -37,6 +40,7 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
+#include "utils/formatting.h"
 #include "utils/inval.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
@@ -88,6 +92,9 @@
                                                text *databaseText, text 
*usernameText, bool *active);
 
 static Oid GetRoleOidIfCanLogin(char *username);
+static entry * ParseSchedule(char *scheduleText);
+static bool TryParseInterval(char *scheduleText, uint32 *secondsInterval);
+
 
 /* SQL-callable functions */
 PG_FUNCTION_INFO_V1(cron_schedule);
@@ -215,12 +222,14 @@
 
        /* check schedule is valid */
        schedule = text_to_cstring(scheduleText);
-       parsedSchedule = parse_cron_entry(schedule);
+       parsedSchedule = ParseSchedule(schedule);
 
        if (parsedSchedule == NULL)
        {
                ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                               errmsg("invalid schedule: %s", 
schedule)));
+                                               errmsg("invalid schedule: %s", 
schedule),
+                                               errhint("Use cron format (e.g. 
5 4 * * *), or interval "
+                                                               "format '[1-59] 
seconds'")));
        }
 
        free_entry(parsedSchedule);
@@ -297,8 +306,14 @@
        }
 
        /* ensure the user that is used in the job can connect to the database 
*/
+#if (PG_VERSION_NUM >= 160000)
+       aclresult = object_aclcheck(DatabaseRelationId,
+                                                               
get_database_oid(database_name, false),
+                                                               userIdcheckacl, 
ACL_CONNECT);
+#else
        aclresult = pg_database_aclcheck(get_database_oid(database_name, false),
                                                                                
userIdcheckacl, ACL_CONNECT);
+#endif
 
        if (aclresult != ACLCHECK_OK)
                elog(ERROR, "User %s does not have CONNECT privilege on %s",
@@ -522,8 +537,8 @@
        Datum sequenceIdDatum = InvalidOid;
        Oid savedUserId = InvalidOid;
        int savedSecurityContext = 0;
-       Datum jobIdDatum = 0;
-       int64 jobId = 0;
+       Datum runIdDatum = 0;
+       int64 runId = 0;
        bool failOK = true;
        MemoryContext originalContext = CurrentMemoryContext;
 
@@ -551,17 +566,17 @@
        SetUserIdAndSecContext(CronExtensionOwner(), 
SECURITY_LOCAL_USERID_CHANGE);
 
        /* generate new and unique colocation id from sequence */
-       jobIdDatum = DirectFunctionCall1(nextval_oid, sequenceIdDatum);
+       runIdDatum = DirectFunctionCall1(nextval_oid, sequenceIdDatum);
 
        SetUserIdAndSecContext(savedUserId, savedSecurityContext);
 
-       jobId = DatumGetInt64(jobIdDatum);
+       runId = DatumGetInt64(runIdDatum);
 
        PopActiveSnapshot();
        CommitTransactionCommand();
        MemoryContextSwitchTo(originalContext);
 
-       return jobId;
+       return runId;
 }
 
 /*
@@ -662,8 +677,8 @@
 Datum
 cron_unschedule_named(PG_FUNCTION_ARGS)
 {
-       Datum jobNameDatum = PG_GETARG_DATUM(0);
-       Name jobName = DatumGetName(jobNameDatum);
+       Datum jobNameDatum = 0;
+       char *jobName = NULL;
 
        Oid userId = GetUserId();
        char *userName = GetUserNameFromId(userId, false);
@@ -676,10 +691,18 @@
        bool indexOK = false;
        HeapTuple heapTuple = NULL;
 
+       if (PG_ARGISNULL(0))
+       {
+               ereport(ERROR, (errmsg("job_name can not be NULL")));
+       }
+
+       jobNameDatum = PG_GETARG_DATUM(0);
+       jobName = TextDatumGetCString(jobNameDatum);
+
        cronJobsTable = table_open(CronJobRelationId(), RowExclusiveLock);
 
        ScanKeyInit(&scanKey[0], Anum_cron_job_jobname,
-                               BTEqualStrategyNumber, F_NAMEEQ, jobNameDatum);
+                               BTEqualStrategyNumber, F_TEXTEQ, jobNameDatum);
        ScanKeyInit(&scanKey[1], Anum_cron_job_username,
                                BTEqualStrategyNumber, F_TEXTEQ, userNameDatum);
 
@@ -690,7 +713,7 @@
        if (!HeapTupleIsValid(heapTuple))
        {
                ereport(ERROR, (errmsg("could not find valid entry for job 
'%s'",
-                                                          NameStr(*jobName))));
+                                                          jobName)));
        }
 
        EnsureDeletePermission(cronJobsTable, heapTuple);
@@ -844,7 +867,6 @@
                PopActiveSnapshot();
                CommitTransactionCommand();
                MemoryContextSwitchTo(originalContext);
-               pgstat_report_activity(STATE_IDLE, NULL);
 
                return NIL;
        }
@@ -897,7 +919,6 @@
        PopActiveSnapshot();
        CommitTransactionCommand();
        MemoryContextSwitchTo(originalContext);
-       pgstat_report_activity(STATE_IDLE, NULL);
 
        return jobList;
 }
@@ -961,7 +982,15 @@
                                                                         
tupleDescriptor, &isJobNameNull);
                if (!isJobNameNull)
                {
-                       job->jobName = DatumGetName(jobName);
+                       /* Handle the column type change introduced in 1.5 */
+                       if (TupleDescAttr(tupleDescriptor, 
Anum_cron_job_jobname - 1)->atttypid == NAMEOID)
+                       {
+                               job->jobName = 
pstrdup(NameStr(*DatumGetName(jobName)));
+                       }
+                       else
+                       {
+                               job->jobName = TextDatumGetCString(jobName);
+                       }
                }
                else
                {
@@ -969,7 +998,7 @@
                }
        }
 
-       parsedSchedule = parse_cron_entry(job->scheduleText);
+       parsedSchedule = ParseSchedule(job->scheduleText);
        if (parsedSchedule != NULL)
        {
                /* copy the schedule and free the allocated memory immediately 
*/
@@ -1082,8 +1111,6 @@
        argTypes[5] = TEXTOID;
        argValues[5] = CStringGetTextDatum(status);
 
-       pgstat_report_activity(STATE_RUNNING, querybuf.data);
-
        if(SPI_execute_with_args(querybuf.data,
                argCount, argTypes, argValues, NULL, false, 1) != SPI_OK_INSERT)
                elog(ERROR, "SPI_exec failed: %s", querybuf.data);
@@ -1094,7 +1121,6 @@
        PopActiveSnapshot();
        CommitTransactionCommand();
        MemoryContextSwitchTo(originalContext);
-       pgstat_report_activity(STATE_IDLE, NULL);
 }
 
 void
@@ -1186,8 +1212,6 @@
        /* and add the where clause */
        appendStringInfo(&querybuf, " where runid = $%d", i);
 
-       pgstat_report_activity(STATE_RUNNING, querybuf.data);
-
        if(SPI_execute_with_args(querybuf.data,
                i, argTypes, argValues, NULL, false, 1) != SPI_OK_UPDATE)
                elog(ERROR, "SPI_exec failed: %s", querybuf.data);
@@ -1198,7 +1222,6 @@
        PopActiveSnapshot();
        CommitTransactionCommand();
        MemoryContextSwitchTo(originalContext);
-       pgstat_report_activity(STATE_IDLE, NULL);
 }
 
 
@@ -1267,7 +1290,14 @@
        {
                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 (PG_VERSION_NUM >= 160000)
+               aclresult = object_aclcheck(DatabaseRelationId,
+                                                                       
get_database_oid(database_name, false),
+                                                                       
userIdcheckacl, ACL_CONNECT);
+#else
+               aclresult = 
pg_database_aclcheck(get_database_oid(database_name, false),
+                                                                               
 userIdcheckacl, ACL_CONNECT);
+#endif
 
                if (aclresult != ACLCHECK_OK)
                        elog(ERROR, "User %s does not have CONNECT privilege on 
%s", GetUserNameFromId(userIdcheckacl, false), database_name);
@@ -1282,12 +1312,14 @@
        if (scheduleText != NULL)
        {
                schedule = text_to_cstring(scheduleText);
-               parsedSchedule = parse_cron_entry(schedule);
+               parsedSchedule = ParseSchedule(schedule);
 
                if (parsedSchedule == NULL)
                {
                        ereport(ERROR, 
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                       errmsg("invalid schedule: %s", 
schedule)));
+                                       errmsg("invalid schedule: %s", 
schedule),
+                                       errhint("Use cron format (e.g. 5 4 * * 
*), or interval "
+                                                       "format '[1-59] 
seconds'")));
                }
 
                free_entry(parsedSchedule);
@@ -1396,8 +1428,6 @@
                , CRON_SCHEMA_NAME, JOB_RUN_DETAILS_TABLE_NAME, 
GetCronStatus(CRON_STATUS_FAILED), GetCronStatus(CRON_STATUS_STARTING), 
GetCronStatus(CRON_STATUS_RUNNING));
 
 
-       pgstat_report_activity(STATE_RUNNING, querybuf.data);
-
        if (SPI_exec(querybuf.data, 0) != SPI_OK_UPDATE)
                elog(ERROR, "SPI_exec failed: %s", querybuf.data);
 
@@ -1407,7 +1437,6 @@
        PopActiveSnapshot();
        CommitTransactionCommand();
        MemoryContextSwitchTo(originalContext);
-       pgstat_report_activity(STATE_IDLE, NULL);
 }
 
 char *
@@ -1467,3 +1496,73 @@
 
        return jobTableOid != InvalidOid;
 }
+
+
+/*
+ * ParseSchedule attempts to parse a cron schedule or an interval in seconds.
+ * The returned pointer is allocated using malloc and should be freed by the
+ * caller.
+ */
+static entry *
+ParseSchedule(char *scheduleText)
+{
+       uint32 secondsInterval = 0;
+
+       /*
+        * First try to parse as a cron schedule.
+        */
+       entry *schedule = parse_cron_entry(scheduleText);
+       if (schedule != NULL)
+       {
+               /* valid cron schedule */
+               return schedule;
+       }
+
+       /*
+        * Parse as interval on seconds.
+        */
+       if (TryParseInterval(scheduleText, &secondsInterval))
+       {
+               entry *schedule = calloc(sizeof(entry), sizeof(char));
+               schedule->secondsInterval = secondsInterval;
+               return schedule;
+       }
+
+       elog(LOG, "failed to parse schedule: %s", scheduleText);
+       return NULL;
+}
+
+
+/*
+ * TryParseInterval returns whether scheduleText is of the form
+ * <positive number> second[s].
+ */
+static bool
+TryParseInterval(char *scheduleText, uint32 *secondsInterval)
+{
+       char lastChar = '\0';
+       char plural = '\0';
+       char extra = '\0';
+       char *lowercaseSchedule = asc_tolower(scheduleText, 
strlen(scheduleText));
+
+       int numParts = sscanf(lowercaseSchedule, " %u secon%c%c %c", 
secondsInterval,
+                                                 &lastChar, &plural, &extra);
+       if (lastChar != 'd')
+       {
+               /* value did not have a "second" suffix */
+               return false;
+       }
+
+       if (numParts == 2)
+       {
+               /* <number> second (allow "2 second") */
+               return 0 < *secondsInterval && *secondsInterval < 60;
+       }
+       else if (numParts == 3 && plural == 's')
+       {
+               /* <number> seconds (allow "1 seconds") */
+               return 0 < *secondsInterval && *secondsInterval < 60;
+       }
+
+       return false;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pg_cron-1.4.2/src/pg_cron.c 
new/pg_cron-1.5.2/src/pg_cron.c
--- old/pg_cron-1.4.2/src/pg_cron.c     2022-07-15 00:14:59.000000000 +0200
+++ new/pg_cron-1.5.2/src/pg_cron.c     2023-04-06 13:56:10.000000000 +0200
@@ -60,6 +60,11 @@
 #include "commands/extension.h"
 #include "commands/sequence.h"
 #include "commands/trigger.h"
+#if (PG_VERSION_NUM >= 160000)
+#include "utils/guc_hooks.h"
+#else
+#include "commands/variable.h"
+#endif
 #include "lib/stringinfo.h"
 #include "libpq-fe.h"
 #include "libpq/pqmq.h"
@@ -117,9 +122,8 @@
 void _PG_fini(void);
 static void pg_cron_sigterm(SIGNAL_ARGS);
 static void pg_cron_sighup(SIGNAL_ARGS);
-static void pg_cron_background_worker_sigterm(SIGNAL_ARGS);
-void PgCronLauncherMain(Datum arg);
-void CronBackgroundWorker(Datum arg);
+PGDLLEXPORT void PgCronLauncherMain(Datum arg);
+PGDLLEXPORT void CronBackgroundWorker(Datum arg);
 
 static void StartAllPendingRuns(List *taskList, TimestampTz currentTime);
 static void StartPendingRuns(CronTask *task, ClockProgress clockProgress,
@@ -138,7 +142,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, bool 
running);
+static void ProcessBgwTaskFeedback(CronTask *task, bool running);
 
 static bool jobCanceled(CronTask *task);
 static bool jobStartupTimeout(CronTask *task, TimestampTz currentTime);
@@ -163,6 +167,8 @@
 static int CronLogMinMessages = WARNING;
 static bool UseBackgroundWorkers = false;
 
+char  *cron_timezone = NULL;
+
 static const struct config_enum_entry cron_message_level_options[] = {
        {"debug5", DEBUG5, false},
        {"debug4", DEBUG4, false},
@@ -298,6 +304,16 @@
                GUC_SUPERUSER_ONLY,
                NULL, NULL, NULL);
 
+       DefineCustomStringVariable(
+               "cron.timezone",
+               gettext_noop("Specify timezone used for cron schedule."),
+               NULL,
+               &cron_timezone,
+               "GMT",
+               PGC_POSTMASTER,
+               GUC_SUPERUSER_ONLY,
+               check_timezone, 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;
@@ -521,29 +537,6 @@
                appendStringInfo(display_msg, "\nCONTEXT: %s", edata.context);
 }
 
-/*
- * Signal handler for SIGTERM for background workers
- *             When we receive a SIGTERM, we set InterruptPending and 
ProcDiePending
- *             just like a normal backend.  The next CHECK_FOR_INTERRUPTS() 
will do the
- *             right thing.
- */
-static void
-pg_cron_background_worker_sigterm(SIGNAL_ARGS)
-{
-       int save_errno = errno;
-
-       if (MyProc)
-               SetLatch(&MyProc->procLatch);
-
-       if (!proc_exit_inprogress)
-       {
-               InterruptPending = true;
-               ProcDiePending = true;
-       }
-
-       errno = save_errno;
-}
-
 
 /*
  * PgCronLauncherMain is the main entry-point for the background worker
@@ -684,7 +677,8 @@
                        CronJob *cronJob = GetCronJob(task->jobId);
                        entry *schedule = &cronJob->schedule;
 
-                       if (schedule->flags & WHEN_REBOOT)
+                       if (schedule->flags & WHEN_REBOOT &&
+                               task->isActive)
                        {
                                task->pendingRunCount += 1;
                        }
@@ -693,6 +687,27 @@
                RebootJobsScheduled = true;
        }
 
+       foreach(taskCell, taskList)
+       {
+               CronTask *task = (CronTask *) lfirst(taskCell);
+
+               if (task->secondsInterval > 0 && task->isActive)
+               {
+                       /*
+                        * For interval jobs, if a task takes longer than the 
interval,
+                        * we only queue up once. So if a task that is supposed 
to run
+                        * every 30 seconds takes 5 minutes, we start another 
run
+                        * immediately after 5 minutes, but then return to 
regular cadence.
+                        */
+                       if (task->pendingRunCount == 0 &&
+                               TimestampDifferenceExceeds(task->lastStartTime, 
currentTime,
+                                                                               
   task->secondsInterval * 1000))
+                       {
+                               task->pendingRunCount += 1;
+                       }
+               }
+       }
+
        if (lastMinute == 0)
        {
                lastMinute = TimestampMinuteStart(currentTime);
@@ -933,8 +948,8 @@
 ShouldRunTask(entry *schedule, TimestampTz currentTime, bool doWild,
                          bool doNonWild)
 {
-       time_t currentTime_t = timestamptz_to_time_t(currentTime);
-       struct tm *tm = gmtime(&currentTime_t);
+       pg_time_t currentTime_t = timestamptz_to_time_t(currentTime);
+       struct pg_tm* tm = pg_localtime(&currentTime_t, 
pg_tzset(cron_timezone));
 
        int minute = tm->tm_min -FIRST_MINUTE;
        int hour = tm->tm_hour -FIRST_HOUR;
@@ -1062,6 +1077,22 @@
 
                if (task->state == CRON_TASK_WAITING && task->pendingRunCount 
== 0)
                {
+                       /*
+                        * Make sure we do not wait past the next run time of 
an interval
+                        * job.
+                        */
+                       if (task->secondsInterval > 0)
+                       {
+                               TimestampTz nextRunTime =
+                                       
TimestampTzPlusMilliseconds(task->lastStartTime,
+                                                                               
                task->secondsInterval * 1000);
+
+                               if (TimestampDifferenceExceeds(nextRunTime, 
nextEventTime, 0))
+                               {
+                                       nextEventTime = nextRunTime;
+                               }
+                       }
+
                        /* don't poll idle tasks */
                        continue;
                }
@@ -1132,9 +1163,11 @@
        pollTimeout = waitSeconds * 1000 + waitMicros / 1000;
        if (pollTimeout <= 0)
        {
-               pfree(polledTasks);
-               pfree(pollFDs);
-               return;
+               /*
+                * Interval jobs might frequently be overdue, inject a small
+                * 1ms wait to avoid getting into a tight loop.
+                */
+               pollTimeout = 1;
        }
        else if (pollTimeout > MaxWait)
        {
@@ -1248,6 +1281,8 @@
                        else
                                task->state = CRON_TASK_START;
 
+                       task->lastStartTime = currentTime;
+
                        RunningTaskCount++;
 
                        /* Add new entry to audit table. */
@@ -1410,7 +1445,7 @@
                         * there trying to write the queue long after we've 
gone away.)
                         */
                        oldcontext = MemoryContextSwitchTo(TopMemoryContext);
-                       shm_mq_attach(mq, task->seg, NULL);
+                       task->sharedMemoryQueue = shm_mq_attach(mq, task->seg, 
NULL);
                        MemoryContextSwitchTo(oldcontext);
 
                        /*
@@ -1660,11 +1695,9 @@
                case CRON_TASK_BGW_RUNNING:
                {
                        pid_t pid;
-                       shm_mq_handle *responseq;
-                       shm_mq *mq;
-                       shm_toc *toc;
 
                        Assert(UseBackgroundWorkers);
+
                        /* check if job has been removed */
                        if (jobCanceled(task))
                        {
@@ -1676,28 +1709,28 @@
                                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);
-                       #else
-                               mq = shm_toc_lookup(toc, PG_CRON_KEY_QUEUE, 
false);
-                       #endif
-                       responseq = shm_mq_attach(mq, task->seg, NULL);
-
                        /* still waiting for job to complete */
                        if (GetBackgroundWorkerPid(&task->handle, &pid) != 
BGWH_STOPPED)
                        {
-                               GetBgwTaskFeedback(responseq, task, true);
-                               shm_mq_detach(responseq);
-                               break;
+                               bool isRunning = true;
+
+                               /* process notices and warnings */
+                               ProcessBgwTaskFeedback(task, isRunning);
                        }
+                       else
+                       {
+                               bool isRunning = false;
 
-                       GetBgwTaskFeedback(responseq, task, false);
+                               /* process remaining notices and final task 
result */
+                               ProcessBgwTaskFeedback(task, isRunning);
 
-                       task->state = CRON_TASK_DONE;
-                       dsm_detach(task->seg);
-                       task->seg = NULL;
-                       RunningTaskCount--;
+                               task->state = CRON_TASK_DONE;
+
+                               dsm_detach(task->seg);
+
+                               task->seg = NULL;
+                               RunningTaskCount--;
+                       }
 
                        break;
                }
@@ -1866,10 +1899,17 @@
        PQclear(result);
 }
 
+
+/*
+ * ProcessBgwTaskFeedback reads messages from a shared memory queue associated
+ * with the background worker that is executing a given task. If the task is
+ * still running, the function does not block if the queue is empty. Otherwise,
+ * it reads until the end of the queue.
+ */
 static void
-GetBgwTaskFeedback(shm_mq_handle *responseq, CronTask *task, bool running)
+ProcessBgwTaskFeedback(CronTask *task, bool running)
 {
-
+       shm_mq_handle *responseq = task->sharedMemoryQueue;
        TimestampTz end_time;
 
        Size            nbytes;
@@ -1885,11 +1925,15 @@
         */
        for (;;)
        {
+               /* do not wait if the task is running */
+               bool nowait = running;
+
                /* Get next message. */
-               res = shm_mq_receive(responseq, &nbytes, &data, false);
+               res = shm_mq_receive(responseq, &nbytes, &data, nowait);
 
                if (res != SHM_MQ_SUCCESS)
                        break;
+
                initStringInfo(&msg);
                resetStringInfo(&msg);
                enlargeStringInfo(&msg, nbytes);
@@ -1978,7 +2022,8 @@
        shm_mq *mq;
        shm_mq_handle *responseq;
 
-       pqsignal(SIGTERM, pg_cron_background_worker_sigterm);
+       /* handle SIGTERM like regular backend */
+       pqsignal(SIGTERM, die);
        BackgroundWorkerUnblockSignals();
 
        /* Set up a memory context and resource owner. */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pg_cron-1.4.2/src/task_states.c 
new/pg_cron-1.5.2/src/task_states.c
--- old/pg_cron-1.4.2/src/task_states.c 2022-07-15 00:14:59.000000000 +0200
+++ new/pg_cron-1.5.2/src/task_states.c 2023-04-06 13:56:10.000000000 +0200
@@ -100,8 +100,9 @@
        {
                CronJob *job = (CronJob *) lfirst(jobCell);
 
-               CronTask *task = GetCronTask(job->jobId);
+               task = GetCronTask(job->jobId);
                task->isActive = job->active;
+               task->secondsInterval = job->schedule.secondsInterval;
        }
 
        CronJobCacheValid = true;
@@ -122,6 +123,13 @@
        if (!isPresent)
        {
                InitializeCronTask(task, jobId);
+
+               /*
+                * We only initialize last run when entering into the hash.
+                * The net effect is that the timer for the first run of an
+                * interval job starts when pg_cron first learns about the job.
+                */
+               task->lastStartTime = GetCurrentTimestamp();
        }
 
        return task;

Reply via email to