{"id":20749685,"url":"https://github.com/splendiddata/pg_cron_helper","last_synced_at":"2026-03-07T21:32:20.749Z","repository":{"id":168248083,"uuid":"643766841","full_name":"splendiddata/pg_cron_helper","owner":"splendiddata","description":null,"archived":false,"fork":false,"pushed_at":"2024-02-29T10:01:37.000Z","size":233,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-11T14:18:05.729Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PLpgSQL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/splendiddata.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-05-22T05:47:07.000Z","updated_at":"2023-05-22T12:26:36.000Z","dependencies_parsed_at":"2024-11-17T23:02:00.172Z","dependency_job_id":null,"html_url":"https://github.com/splendiddata/pg_cron_helper","commit_stats":null,"previous_names":["splendiddata/pg_cron_helper"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/splendiddata/pg_cron_helper","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/splendiddata%2Fpg_cron_helper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/splendiddata%2Fpg_cron_helper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/splendiddata%2Fpg_cron_helper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/splendiddata%2Fpg_cron_helper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/splendiddata","download_url":"https://codeload.github.com/splendiddata/pg_cron_helper/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/splendiddata%2Fpg_cron_helper/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30231634,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T19:01:10.287Z","status":"ssl_error","status_checked_at":"2026-03-07T18:59:58.103Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-17T08:24:09.636Z","updated_at":"2026-03-07T21:32:20.718Z","avatar_url":"https://github.com/splendiddata.png","language":"PLpgSQL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pg_cron_helper\nThe pg_cron_helper database extension is supposed to help running \"jobs\" on a simple time-based schedule. Of course you are far better of using an external scheduler, but if you must schedule jobs inside a Postgres database then this extension may be of some help.\n \nPostgres extension pg_cron_helper is a wrapper around the [pg_cron](https://github.com/citusdata/pg_cron) extension. It also depends on contrib extensions postgres_fdw and dblink. Pg_cron_helper \"helps\" making a job scheduling interface available in more that one database in the Postgres cluster, and it supports a repeat interval that is roughly based on Oracle's repeat_interval definition in dbms_scheduler (altough it doesn't even come anywhere near the possibilities of Oracle's dbms_scheduler package).\n\n## Installation\nFirst make the pg_cron_helper extension available in the Postgres installation directory either using the pgxs infrastructure:\u003cbr\u003e\nMake sure pg_config is on the PATH and execute '`sudo make install`'\u003cbr\u003e\nOr:\u003cbr\u003e\nexecute \"`select setting from pg_config() where name = 'SHAREDIR';`\" in the database to find the *\u0026lt;SHAREDIR\u0026gt;* directory and copy the pg_cron_helper.control and pg_cron_helper--0.1.sql file into the *\u0026lt;SHAREDIR\u0026gt;*/extension directory.\n\nAlso make sure that the pg_cron, postgres_fdw and dblink extensions are available in Postgres.\n\nThe pg_cron extension needs setting \"`shared_preload_libraries = 'pg_cron'`\" to be present in the postgresql.conf file (findable via \"`select setting from pg_settings where name = 'config_file';`\"). Postgres needs to be restarted after adding that setting. See [pg_cron description](https://github.com/citusdata/pg_cron#setting-up-pg_cron).\n\nThen the pg_cron extension must be installed on the cron server database ('postgres' by default -- see the installation instructions of the pg_cron extension).\u003cbr\u003e\nAfter installing pg_cron, the pg_cron_helper must be installed in the same cron server database using \"`create extension pg_cron_helper cascade;`\" (\"cascade\" makes sure extensions postgres_fdw and dblink are installed as well).\n\nIf you want to make pg_cron functionality available in more than one database in the Postgres cluster, then you can install the pg_cron_helper extension in these databases as well. Just execute \"`create extension pg_cron_helper cascade;`\"\n\n![img](pictures/deployment.png)\n\n## Interface\n\n### create_job\n\n```\nprocedure cron.create_job\n    ( job_name             varchar(128)\n    , job_action           text\n    , start_date           timestamp with time zone  default current_timestamp::timestamp(0) with time zone\n    , repeat_interval      varchar                   default ''\n    , end_date             timestamp with time zone  default null\n    , enabled              boolean                   default false\n    , auto_drop            boolean                   default true\n    , comments             text                      default ''\n    , user_name            name                      default current_user\n    )\n```\n\n**Defines a job.**\n\n**Arguments:**\n\n\u003cstyle\u003e\ntable {\n    border-collapse: collapse;\n}\ntd {\n    border: 1px solid;\n    vertical-align: top;\n}\n\u003c/style\u003e\n\u003ctable\u003e\n\u003ctr\u003e\u003ctd\u003ejob_name\u003c/td\u003e\u003ctd\u003eName of the job to be created. It must be unique (within the user_name).\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003ejob_action\u003c/td\u003e\u003ctd\u003eThe sql script that is to be executed on behalf of the job. It can consist of one or more sql statements.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003estart_date\u003c/td\u003e\u003ctd\u003eNormally the creation timestamp of the job. Will be used as base timestamp for intervals. Can be set in the future to have the job started at a later moment.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003erepeat_interval\u003c/td\u003e\u003ctd\u003eCan be a cron pattern, as described in the \u003ca href=\"https://github.com/citusdata/pg_cron#what-is-pg_cron\"\u003epg_cron\u003c/a\u003e extension.\u003cbr\u003eOr an Oracle-ish repeat interval - see below.\u003cbr\u003eOr it can be left empty, in which case the job can only be run using the cron.run_job() procedure.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eend_date\u003c/td\u003e\u003ctd\u003eTimestamp after which the job will be disabled.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eenabled\u003c/td\u003e\u003ctd\u003eDirectly schedule the job.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eauto_drop\u003c/td\u003e\u003ctd\u003eNot implemented yet.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003ecomments\u003c/td\u003e\u003ctd\u003eJust comment.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003euser_name\u003c/td\u003e\u003ctd\u003eThe role under which the job should run. the user_name is part of the unique identification of the job.\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n**repeat_interval:**\n\n\u003ctable\u003e\n\u003ctr\u003e\u003ctd\u003eFREQ\u003c/td\u003e\u003ctd\u003eDefines the interval unit. Can be one of YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY or SECONDLY.\u003cbr\u003e\nThe FREQ clause must be defined as first in the repeat-interval definition. If it is SECONDLY, then an INTERVAL clause can be defined, but no others.\u003cbr\u003e\u003cbr\u003eExample:\u003cbr\u003e\nFREQ=DAILY\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eINTERVAL\u003c/td\u003e\u003ctd\u003eDefines how many minutes, hours, days ... depending on FREQ must be (at least) between the starts of job runs.\u003cbr\u003e\nSo, for example 'FREQ=SECONDLY;INTERVAL=45' means: every 45 seconds\u003cbr\u003e\nand 'FREQ=HOURLY;INTERVAL=3' means: every third our.\u003cbr\u003e\nDefaults to 1.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eBYDAY\u003c/td\u003e\u003ctd\u003eComma separated list of weekdays on which the job is allowed to run. Allowed values : SUN, SUNDAY, 0, MON, MONDAY, 1, TUE, TUESDAY, 2, WED, WEDNESDAY, 3, THU, THURSDAY, 4, FRI, FRIDAY, 5, SAT, SATURDAY, 6.\u003cbr\u003e\nWhen the start time of a job + the interval is not in one of the allowed days, then the next run will be postponed to the next allowed day.\u003cbr\u003e\u003cbr\u003e\nFor example:\n'FREQ=HOURLY;BYDAY=MON,TUE,WED,THU,FRI'\u003cbr\u003eschedules the job to run every hour on working days\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eBYHOUR\u003c/td\u003e\u003ctd\u003eComma separated list of hours (24 hours per day) on which the job is allowed to run.\u003cbr\u003e\u003cbr\u003e\nFor example:\n'FREQ=DAILY;INTERVAL=5,BYDAY=MON,TUE,WED,THU,FRI;BYHOUR=18,19,20'\u003cbr\u003e\nmeans: the job is scheduled every fifth day, on working days, somewhere between 18:00 and 20:59.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eBYMINUTE\u003c/td\u003e\u003ctd\u003ecomma separated list of minutes (0 - 59) within the hour on which a job is allowed to start.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eBYMONTHDAY\u003c/td\u003e\u003ctd\u003eComma separated list of days of month on which a job is allowed to start.\u003cbr\u003eBe cautious with nunbers \u003e 28 as they may not appear in every month.\u003cbr\u003e\u003cbr\u003e\nSo a schedule like\u003cbr\u003e\n'FREQ=MONTHLY;BYMONTHDAY=29\u003cbr\u003e\nwill cause the job to run every 29'th of the month - so in February only once every four years.\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n**Examples:**\n\n```\ncall cron.create_job( job_name =\u003e 'cleanup job'\n                    , job_action =\u003e $$truncate table my_schema.tbl_a;\n                                      truncate table my_schema.tbl_b cascade;\n                                      insert into my_schema.admin_log (msg) values ('ran cleanup job.');$$\n                    );\n                    \n```\n\nThat creates a job called 'cleanup job', which is not scheduled. So it will only be executed when you execute:\u003cbr\u003e\ncall cron.run_job( job_name =\u003e 'cleanup job' );\n\n```\ncall cron.create_job( job_name =\u003e 'every ten minutes on week days'\n                    , job_action =\u003e 'call my_schema.some_procedure();\n                    , repeat_interval =\u003e 'FREQ=MINUTELY;INTERVAL=15;BYDAY=MON,TUE,WED,THU,FRI'\n                    , enabled =\u003e true\n                    , comments =\u003e 'This is a great job'\n                    );\n```\n\nCreates a job that will run every 15 minutes on weekdays.\n\n### enable_job\n\n```\nprocedure cron.enable_job\n    ( job_name             varchar(128)\n    , user_name            name default current_user\n    )\n```\n**Schedules the job if a repeat_interval is present**\n\nOnly a job that has a repeat_interval can be enabled.\n\n**Arguments:**\n\n\u003ctable\u003e\n\u003ctr\u003e\u003ctd\u003ejob_name\u003c/td\u003e\u003ctd\u003eName of the job to schedule.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003euser_name\u003c/td\u003e\u003ctd\u003eThe user name of the job if that is not the current user.\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n**Example:**\n\n```\ncall cron.enable_job(job_name =\u003e 'my beautiful job');\n```\n\n### disable_job\n\n```\nprocedure cron.disable_job\n    ( job_name             varchar(128)\n    , user_name            name default current_user\n    )\n```\n**Unschedules the job**\n\nAfter running the disable_job procedure, the job is no longer scheduled. A running instance of the job will not be affected - will be allowed to finish normally. But it will not start again automatically. \n\n**Arguments:**\n\n\u003ctable\u003e\n\u003ctr\u003e\u003ctd\u003ejob_name\u003c/td\u003e\u003ctd\u003eName of the job to unschedule.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003euser_name\u003c/td\u003e\u003ctd\u003eThe user name of the job if that is not the current user.\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n**Example:**\n\n```\ncall cron.disable_job(job_name =\u003e 'my beautiful job');\n```\n\n### run_job\n\n```\nprocedure cron.run_job\n    ( job_name             varchar(128)\n    , user_name            name default current_user\n    )\n```\n**Runs the job \"now\"**\n\nNo, it doesn't. In fact it schedules the job to run in five seconds. The reason for that is the fact that pg_cron can only schedule jobs. The five seconds are a safety margin. As soon as the job starts running, it will be unscheduled - or rescheduled according to its defined schedule.\n\nA job can be run at any time, regardless if it is enabled or not. \n\n**Arguments:**\n\n\u003ctable\u003e\n\u003ctr\u003e\u003ctd\u003ejob_name\u003c/td\u003e\u003ctd\u003eName of the job to run.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003euser_name\u003c/td\u003e\u003ctd\u003eThe user name of the job if that is not the current user.\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n**Example:**\n\n```\ncall cron.run_job(job_name =\u003e 'my beautiful job');\n```\n\n### stop_job\n\n```\nprocedure cron.stop_job\n    ( job_name             varchar(128)\n    , force                boolean      default false\n    , user_name            name         default current_user\n    )\n```\n**Aborts a running job**\n\nIf the job is not running, nothing will happen \n\n**Arguments:**\n\n\u003ctable\u003e\n\u003ctr\u003e\u003ctd\u003ejob_name\u003c/td\u003e\u003ctd\u003eName of the job to stop.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eforce\u003c/td\u003e\u003ctd\u003eIf false (default), the job will be stopped using pg_cancel_backend(). If true then  pg_terminate_backend() will be used. See the \u003ca href=\"https://www.postgresql.org/docs/current/functions-admin.html#FUNCTIONS-ADMIN-SIGNAL\"\u003ePostgres manual\u003c/a\u003e.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003euser_name\u003c/td\u003e\u003ctd\u003eThe user name of the job if that is not the current user.\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n**Example:**\n\n```\ncall cron.stop_job(job_name =\u003e 'my beautiful job', force =\u003e true);\n```\n\n### drop_job\n\n```\nprocedure cron.drop_job\n    ( job_name             varchar(128)\n    , force                boolean      default false\n    , user_name            name         default current_user\n    )\n```\n**Deletes a job definition**\n\n**Arguments:**\n\n\u003ctable\u003e\n\u003ctr\u003e\u003ctd\u003ejob_name\u003c/td\u003e\u003ctd\u003eName of the job definition to delete.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eforce\u003c/td\u003e\u003ctd\u003eIf true, then stop_job() will be invoked. Else a running instance of the job will be allowed to finish normally.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003euser_name\u003c/td\u003e\u003ctd\u003eThe user name of the job if that is not the current user.\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n**Example:**\n\n```\ncall cron.drop_job(job_name =\u003e 'his job', user_name =\u003e 'someone_else');\n```\n\n### list_jobs\n\n```\nfunction cron.list_jobs()\n    returns setof cron.job_record\n```\n\nwhereby cron.job_record is defined as:\n\n```\ntype cron.job_record as\n    ( job_name             varchar(128) \n    , job_action           text\n    , start_date           timestamp with time zone\n    , repeat_interval      text\n    , end_date             timestamp with time zone\n    , enabled              boolean\n    , auto_drop            boolean\n    , user_name            name\n    , comments             text\n    )\n```\n\n**Just lists all jobs known for this database**\n\n**Example:**\n\n```\nselect * from  cron.list_jobs();\n```\n\n### get_job_state\n\n```\nfunction cron.get_job_state\n    ( job_name             varchar(128)\n    , user_name            name default current_user\n    ) returns text\n```\n**Returns the current state of a job**\n\n**Arguments:**\n\n\u003ctable\u003e\n\u003ctr\u003e\u003ctd\u003ejob_name\u003c/td\u003e\u003ctd\u003eName of the job for which the state is requested.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003euser_name\u003c/td\u003e\u003ctd\u003eThe user name of the job if that is not the current user.\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n**Possible return values:**\n\n\u003ctable\u003e\n\u003ctr\u003e\u003ctd\u003e'defined'\u003c/td\u003e\u003ctd\u003eThe job is known, but not currently scheduled. No information about previous runs is available.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e'scheduled'\u003c/td\u003e\u003ctd\u003eThe job is scheduled to run.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e'running'\u003c/td\u003e\u003ctd\u003eThe job is currently executing.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e'succeeded'\u003c/td\u003e\u003ctd\u003eThe last run was succesful.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e'failed'\u003c/td\u003e\u003ctd\u003eThe last run ended unsuccesful.\u003c/td\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003e'unknown'\u003c/td\u003e\u003ctd\u003eThe job definition is not found.\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\n**Example:**\n\n```\nselect cron.get_job_state(job_name =\u003e 'my_job');\n```\n\n### Other functions and procedures\nFunctions and procedures that start with \\_cron\\_ or \\_srvr\\_ are intended for internal use. Please don't invoke.\n\n## Todo:\nThe pg_cron_helper extension is still a bit premature. Next things to implement are:\n\n- validations for the create_job procedure\n- implement auto_drop\n- enhance logging and logging maintenance \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsplendiddata%2Fpg_cron_helper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsplendiddata%2Fpg_cron_helper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsplendiddata%2Fpg_cron_helper/lists"}