Use regrole for job owner

Instead of using a user name to register the owner of a job, we use
regrole. This allows renames to work properly since the underlying OID
does not change when the owner name changes.

We add a check when calling `DROP ROLE` that there is no job with that
owner and generate an error if there is.
This commit is contained in:
Mats Kindahl 2023-04-13 13:28:25 +02:00 committed by Mats Kindahl
parent 20db884bd7
commit 9a64385f34
23 changed files with 334 additions and 38 deletions

View File

@ -39,6 +39,7 @@ accidentally triggering the load of a previous DB version.**
* #5433 Fix join rte in CAggs with joins
* #5543 Copy scheduled_jobs list before sorting it
* #5570 Improve interpolate error message on datatype mismatch
* #5558 Use regrole for job owner
**Thanks**
* @nikolaps for reporting an issue with the COPY fetcher

View File

@ -65,7 +65,7 @@ VALUES
INTERVAL '1h',
'_timescaledb_internal',
'policy_job_error_retention',
CURRENT_ROLE,
current_role::regrole,
true,
'{"drop_after":"1 month"}',
'_timescaledb_internal',

View File

@ -280,7 +280,7 @@ CREATE TABLE _timescaledb_config.bgw_job (
retry_period interval NOT NULL,
proc_schema name NOT NULL,
proc_name name NOT NULL,
owner name NOT NULL DEFAULT CURRENT_ROLE,
owner regrole NOT NULL DEFAULT current_role::regrole,
scheduled bool NOT NULL DEFAULT TRUE,
fixed_schedule bool not null default true,
initial_start timestamptz,

View File

@ -57,3 +57,74 @@ ALTER FUNCTION _timescaledb_internal.compressed_data_recv(internal) SET SCHEMA _
ALTER FUNCTION _timescaledb_internal.rxid_in(cstring) SET SCHEMA _timescaledb_functions;
ALTER FUNCTION _timescaledb_internal.rxid_out(@extschema@.rxid) SET SCHEMA _timescaledb_functions;
-- drop dependent views
DROP VIEW IF EXISTS timescaledb_information.job_errors;
DROP VIEW IF EXISTS timescaledb_information.job_stats;
DROP VIEW IF EXISTS timescaledb_information.jobs;
DROP VIEW IF EXISTS timescaledb_experimental.policies;
ALTER TABLE _timescaledb_config.bgw_job
ALTER COLUMN owner DROP DEFAULT,
ALTER COLUMN owner TYPE regrole USING owner::regrole,
ALTER COLUMN owner SET DEFAULT current_role::regrole;
CREATE TABLE _timescaledb_config.bgw_job_tmp AS SELECT * FROM _timescaledb_config.bgw_job;
ALTER EXTENSION timescaledb DROP TABLE _timescaledb_config.bgw_job;
ALTER EXTENSION timescaledb DROP SEQUENCE _timescaledb_config.bgw_job_id_seq;
ALTER TABLE _timescaledb_internal.bgw_job_stat
DROP CONSTRAINT IF EXISTS bgw_job_stat_job_id_fkey;
ALTER TABLE _timescaledb_internal.bgw_policy_chunk_stats
DROP CONSTRAINT IF EXISTS bgw_policy_chunk_stats_job_id_fkey;
CREATE TABLE _timescaledb_internal.tmp_bgw_job_seq_value AS
SELECT last_value, is_called FROM _timescaledb_config.bgw_job_id_seq;
DROP TABLE _timescaledb_config.bgw_job;
CREATE SEQUENCE _timescaledb_config.bgw_job_id_seq MINVALUE 1000;
SELECT pg_catalog.pg_extension_config_dump('_timescaledb_config.bgw_job_id_seq', '');
SELECT pg_catalog.setval('_timescaledb_config.bgw_job_id_seq', last_value, is_called)
FROM _timescaledb_internal.tmp_bgw_job_seq_value;
DROP TABLE _timescaledb_internal.tmp_bgw_job_seq_value;
CREATE TABLE _timescaledb_config.bgw_job (
id integer NOT NULL DEFAULT nextval('_timescaledb_config.bgw_job_id_seq'),
application_name name NOT NULL,
schedule_interval interval NOT NULL,
max_runtime interval NOT NULL,
max_retries integer NOT NULL,
retry_period interval NOT NULL,
proc_schema name NOT NULL,
proc_name name NOT NULL,
owner regrole NOT NULL DEFAULT current_role::regrole,
scheduled bool NOT NULL DEFAULT TRUE,
fixed_schedule bool not null default true,
initial_start timestamptz,
hypertable_id integer,
config jsonb,
check_schema name,
check_name name,
timezone text,
CONSTRAINT bgw_job_pkey PRIMARY KEY (id),
CONSTRAINT bgw_job_hypertable_id_fkey FOREIGN KEY (hypertable_id) REFERENCES _timescaledb_catalog.hypertable (id) ON DELETE CASCADE
);
ALTER SEQUENCE _timescaledb_config.bgw_job_id_seq OWNED BY _timescaledb_config.bgw_job.id;
CREATE INDEX bgw_job_proc_hypertable_id_idx
ON _timescaledb_config.bgw_job(proc_schema,proc_name,hypertable_id);
INSERT INTO _timescaledb_config.bgw_job
SELECT * FROM _timescaledb_config.bgw_job_tmp ORDER BY id;
DROP TABLE _timescaledb_config.bgw_job_tmp;
ALTER TABLE _timescaledb_internal.bgw_job_stat
ADD CONSTRAINT bgw_job_stat_job_id_fkey
FOREIGN KEY(job_id)
REFERENCES _timescaledb_config.bgw_job(id)
ON DELETE CASCADE;
ALTER TABLE _timescaledb_internal.bgw_policy_chunk_stats
ADD CONSTRAINT bgw_policy_chunk_stats_job_id_fkey
FOREIGN KEY(job_id)
REFERENCES _timescaledb_config.bgw_job(id)
ON DELETE CASCADE;
SELECT pg_catalog.pg_extension_config_dump('_timescaledb_config.bgw_job', 'WHERE id >= 1000');
GRANT SELECT ON _timescaledb_config.bgw_job TO PUBLIC;
GRANT SELECT ON _timescaledb_config.bgw_job_id_seq TO PUBLIC;

View File

@ -202,3 +202,73 @@ BEGIN
END
$BODY$ SET search_path TO pg_catalog, pg_temp;
-- drop dependent views
DROP VIEW IF EXISTS timescaledb_information.job_errors;
DROP VIEW IF EXISTS timescaledb_information.job_stats;
DROP VIEW IF EXISTS timescaledb_information.jobs;
DROP VIEW IF EXISTS timescaledb_experimental.policies;
ALTER TABLE _timescaledb_config.bgw_job
ALTER COLUMN owner DROP DEFAULT,
ALTER COLUMN owner TYPE name USING owner::name,
ALTER COLUMN owner SET DEFAULT current_role;
CREATE TABLE _timescaledb_config.bgw_job_tmp AS SELECT * FROM _timescaledb_config.bgw_job;
ALTER EXTENSION timescaledb DROP TABLE _timescaledb_config.bgw_job;
ALTER EXTENSION timescaledb DROP SEQUENCE _timescaledb_config.bgw_job_id_seq;
ALTER TABLE _timescaledb_internal.bgw_job_stat
DROP CONSTRAINT IF EXISTS bgw_job_stat_job_id_fkey;
ALTER TABLE _timescaledb_internal.bgw_policy_chunk_stats
DROP CONSTRAINT IF EXISTS bgw_policy_chunk_stats_job_id_fkey;
CREATE TABLE _timescaledb_internal.tmp_bgw_job_seq_value AS
SELECT last_value, is_called FROM _timescaledb_config.bgw_job_id_seq;
DROP TABLE _timescaledb_config.bgw_job;
CREATE SEQUENCE _timescaledb_config.bgw_job_id_seq MINVALUE 1000;
SELECT pg_catalog.pg_extension_config_dump('_timescaledb_config.bgw_job_id_seq', '');
SELECT pg_catalog.setval('_timescaledb_config.bgw_job_id_seq', last_value, is_called)
FROM _timescaledb_internal.tmp_bgw_job_seq_value;
DROP TABLE _timescaledb_internal.tmp_bgw_job_seq_value;
CREATE TABLE _timescaledb_config.bgw_job (
id integer NOT NULL DEFAULT nextval('_timescaledb_config.bgw_job_id_seq'),
application_name name NOT NULL,
schedule_interval interval NOT NULL,
max_runtime interval NOT NULL,
max_retries integer NOT NULL,
retry_period interval NOT NULL,
proc_schema name NOT NULL,
proc_name name NOT NULL,
owner name NOT NULL DEFAULT current_role,
scheduled bool NOT NULL DEFAULT TRUE,
fixed_schedule bool not null default true,
initial_start timestamptz,
hypertable_id integer,
config jsonb,
check_schema name,
check_name name,
timezone text,
CONSTRAINT bgw_job_pkey PRIMARY KEY (id),
CONSTRAINT bgw_job_hypertable_id_fkey FOREIGN KEY (hypertable_id) REFERENCES _timescaledb_catalog.hypertable (id) ON DELETE CASCADE
);
ALTER SEQUENCE _timescaledb_config.bgw_job_id_seq OWNED BY _timescaledb_config.bgw_job.id;
CREATE INDEX bgw_job_proc_hypertable_id_idx
ON _timescaledb_config.bgw_job(proc_schema,proc_name,hypertable_id);
INSERT INTO _timescaledb_config.bgw_job
SELECT * FROM _timescaledb_config.bgw_job_tmp ORDER BY id;
DROP TABLE _timescaledb_config.bgw_job_tmp;
ALTER TABLE _timescaledb_internal.bgw_job_stat
ADD CONSTRAINT bgw_job_stat_job_id_fkey
FOREIGN KEY(job_id)
REFERENCES _timescaledb_config.bgw_job(id)
ON DELETE CASCADE;
ALTER TABLE _timescaledb_internal.bgw_policy_chunk_stats
ADD CONSTRAINT bgw_policy_chunk_stats_job_id_fkey
FOREIGN KEY(job_id)
REFERENCES _timescaledb_config.bgw_job(id)
ON DELETE CASCADE;
SELECT pg_catalog.pg_extension_config_dump('_timescaledb_config.bgw_job', 'WHERE id >= 1000');
GRANT SELECT ON _timescaledb_config.bgw_job TO PUBLIC;
GRANT SELECT ON _timescaledb_config.bgw_job_id_seq TO PUBLIC;

View File

@ -7,5 +7,5 @@ CREATE OR REPLACE FUNCTION @extschema@.get_telemetry_report() RETURNS jsonb
LANGUAGE C STABLE PARALLEL SAFE;
INSERT INTO _timescaledb_config.bgw_job (id, application_name, schedule_interval, max_runtime, max_retries, retry_period, proc_schema, proc_name, owner, scheduled, fixed_schedule) VALUES
(1, 'Telemetry Reporter [1]', INTERVAL '24h', INTERVAL '100s', -1, INTERVAL '1h', '_timescaledb_internal', 'policy_telemetry', CURRENT_ROLE, true, false)
(1, 'Telemetry Reporter [1]', INTERVAL '24h', INTERVAL '100s', -1, INTERVAL '1h', '_timescaledb_internal', 'policy_telemetry', current_role::regrole, true, false)
ON CONFLICT (id) DO NOTHING;

View File

@ -291,8 +291,7 @@ bgw_job_from_tupleinfo(TupleInfo *ti, size_t alloc_size)
NameStr(
*DatumGetName(values[AttrNumberGetAttrOffset(Anum_bgw_job_check_name)])));
if (!nulls[AttrNumberGetAttrOffset(Anum_bgw_job_owner)])
namestrcpy(&job->fd.owner,
NameStr(*DatumGetName(values[AttrNumberGetAttrOffset(Anum_bgw_job_owner)])));
job->fd.owner = DatumGetObjectId(values[AttrNumberGetAttrOffset(Anum_bgw_job_owner)]);
if (!nulls[AttrNumberGetAttrOffset(Anum_bgw_job_scheduled)])
job->fd.scheduled = DatumGetBool(values[AttrNumberGetAttrOffset(Anum_bgw_job_scheduled)]);
@ -977,9 +976,7 @@ ts_bgw_job_check_max_retries(BgwJob *job)
void
ts_bgw_job_permission_check(BgwJob *job)
{
Oid owner_oid = get_role_oid(NameStr(job->fd.owner), false);
if (!has_privs_of_role(GetUserId(), owner_oid))
if (!has_privs_of_role(GetUserId(), job->fd.owner))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("insufficient permissions to alter job %d", job->fd.id)));
@ -1329,7 +1326,7 @@ int
ts_bgw_job_insert_relation(Name application_name, Interval *schedule_interval,
Interval *max_runtime, int32 max_retries, Interval *retry_period,
Name proc_schema, Name proc_name, Name check_schema, Name check_name,
Name owner, bool scheduled, bool fixed_schedule, int32 hypertable_id,
Oid owner, bool scheduled, bool fixed_schedule, int32 hypertable_id,
Jsonb *config, TimestampTz initial_start, const char *timezone)
{
Catalog *catalog = ts_catalog_get();
@ -1363,7 +1360,7 @@ ts_bgw_job_insert_relation(Name application_name, Interval *schedule_interval,
else
nulls[AttrNumberGetAttrOffset(Anum_bgw_job_check_name)] = true;
values[AttrNumberGetAttrOffset(Anum_bgw_job_owner)] = NameGetDatum(owner);
values[AttrNumberGetAttrOffset(Anum_bgw_job_owner)] = ObjectIdGetDatum(owner);
values[AttrNumberGetAttrOffset(Anum_bgw_job_scheduled)] = BoolGetDatum(scheduled);
values[AttrNumberGetAttrOffset(Anum_bgw_job_fixed_schedule)] = BoolGetDatum(fixed_schedule);
/* initial_start must have a value if the schedule is fixed */
@ -1407,6 +1404,13 @@ ts_bgw_job_insert_relation(Name application_name, Interval *schedule_interval,
ts_catalog_insert_values(rel, desc, values, nulls);
ts_catalog_restore_user(&sec_ctx);
/* This is where we would add a call to recordDependencyOnOwner, but it
* cannot support dependencies on anything but built-in classes since
* getObjectClass() have a lot of hard-coded checks in place.
*
* Instead we have a check in process_utility.c that prevents dropping the
* user if there is a dependent job. */
table_close(rel, NoLock);
return values[AttrNumberGetAttrOffset(Anum_bgw_job_id)];
}

View File

@ -45,7 +45,7 @@ extern TSDLLEXPORT bool ts_bgw_job_update_by_id(int32 job_id, BgwJob *job);
extern TSDLLEXPORT int32 ts_bgw_job_insert_relation(
Name application_name, Interval *schedule_interval, Interval *max_runtime, int32 max_retries,
Interval *retry_period, Name proc_schema, Name proc_name, Name check_schema, Name check_name,
Name owner, bool scheduled, bool fixed_schedule, int32 hypertable_id, Jsonb *config,
Oid owner, bool scheduled, bool fixed_schedule, int32 hypertable_id, Jsonb *config,
TimestampTz initial_start, const char *timezone);
extern TSDLLEXPORT void ts_bgw_job_permission_check(BgwJob *job);

View File

@ -251,7 +251,6 @@ scheduled_bgw_job_transition_state_to(ScheduledBgwJob *sjob, JobState new_state)
#endif
BgwJobStat *job_stat;
Oid owner_uid;
switch (new_state)
{
@ -314,7 +313,6 @@ scheduled_bgw_job_transition_state_to(ScheduledBgwJob *sjob, JobState new_state)
else
sjob->timeout_at = DT_NOEND;
owner_uid = get_role_oid(NameStr(sjob->job.fd.owner), false);
CommitTransactionCommand();
MemoryContextSwitchTo(scratch_mctx);
@ -323,7 +321,7 @@ scheduled_bgw_job_transition_state_to(ScheduledBgwJob *sjob, JobState new_state)
sjob->job.fd.id,
NameStr(sjob->job.fd.application_name));
sjob->handle = ts_bgw_job_start(&sjob->job, owner_uid);
sjob->handle = ts_bgw_job_start(&sjob->job, sjob->job.fd.owner);
if (sjob->handle == NULL)
{
elog(WARNING,

View File

@ -13,6 +13,7 @@
#include <catalog/index.h>
#include <catalog/objectaddress.h>
#include <catalog/pg_trigger.h>
#include <catalog/pg_authid.h>
#include <commands/copy.h>
#include <commands/vacuum.h>
#include <commands/defrem.h>
@ -71,6 +72,7 @@
#include "compression_with_clause.h"
#include "partitioning.h"
#include "debug_point.h"
#include "debug_assert.h"
#ifdef USE_TELEMETRY
#include "telemetry/functions.h"
@ -1698,6 +1700,62 @@ process_drop_continuous_aggregates(ProcessUtilityArgs *args, DropStmt *stmt)
errhint("Drop continuous aggregates and other objects in separate statements.")));
}
static bool
fetch_role_info(RoleSpec *rolespec, Oid *roleid)
{
/* Special role specifiers should not be present when dropping a role,
* but if they are, we just ignore them */
if (rolespec->roletype != ROLESPEC_CSTRING)
return false;
/* Fetch the heap tuple from system table. If heaptuple is not valid it
* means we did not find a role. We ignore it since the real execution
* will handle this. */
HeapTuple tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(rolespec->rolename));
if (!HeapTupleIsValid(tuple))
return false;
Form_pg_authid roleform = (Form_pg_authid) GETSTRUCT(tuple);
*roleid = roleform->oid;
ReleaseSysCache(tuple);
return true;
}
static DDLResult
process_drop_role(ProcessUtilityArgs *args)
{
DropRoleStmt *stmt = (DropRoleStmt *) args->parsetree;
ListCell *cell;
foreach (cell, stmt->roles)
{
RoleSpec *rolespec = lfirst(cell);
Oid roleid;
if (!fetch_role_info(rolespec, &roleid))
continue;
ScanIterator iterator =
ts_scan_iterator_create(BGW_JOB, AccessShareLock, CurrentMemoryContext);
ts_scanner_foreach(&iterator)
{
bool isnull;
TupleInfo *ti = ts_scan_iterator_tuple_info(&iterator);
Datum value = slot_getattr(ti->slot, Anum_bgw_job_owner, &isnull);
if (!isnull && DatumGetObjectId(value) == roleid)
{
Datum value = slot_getattr(ti->slot, Anum_bgw_job_id, &isnull);
Ensure(!isnull, "job id was null");
ereport(ERROR,
(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
errmsg("role \"%s\" cannot be dropped because some objects depend on it",
rolespec->rolename),
errdetail("owner of job %d", DatumGetInt32(value))));
}
}
}
return DDL_CONTINUE;
}
static DDLResult
process_drop_start(ProcessUtilityArgs *args)
{
@ -4098,6 +4156,9 @@ process_ddl_command_start(ProcessUtilityArgs *args)
*/
handler = process_drop_start;
break;
case T_DropRoleStmt:
handler = process_drop_role;
break;
case T_DropTableSpaceStmt:
handler = process_drop_tablespace;
break;

View File

@ -742,7 +742,7 @@ typedef struct FormData_bgw_job
Interval retry_period;
NameData proc_schema;
NameData proc_name;
NameData owner;
Oid owner;
bool scheduled;
bool fixed_schedule;
TimestampTz initial_start;

View File

@ -557,16 +557,16 @@ EXECUTE record_from_prepared;
DEALLOCATE record_from_prepared;
SELECT get_telemetry_report()->'functions_used';
?column?
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
{"pg_catalog.count()": 1, "pg_catalog.sum(bigint)": 4, "pg_catalog.abs(integer)": 1, "pg_catalog.max(integer)": 2, "pg_catalog.min(integer)": 1, "pg_catalog.sum(integer)": 1, "pg_catalog.int8(numeric)": 4, "pg_catalog.sum(interval)": 2, "pg_catalog.current_database()": 1, "public.get_telemetry_report()": 1, "pg_catalog.text(pg_catalog.name)": 1, "pg_catalog.int4eq(integer,integer)": 3, "pg_catalog.int4mi(integer,integer)": 11, "pg_catalog.int4pl(integer,integer)": 3, "pg_catalog.concat(pg_catalog.\"any\")": 3, "pg_catalog.pg_get_userbyid(pg_catalog.oid)": 1, "pg_catalog.nameeq(pg_catalog.name,pg_catalog.name)": 2, "pg_catalog.texteq(pg_catalog.text,pg_catalog.text)": 1, "pg_catalog.nameregexeq(pg_catalog.name,pg_catalog.text)": 1, "pg_catalog.textregexeq(pg_catalog.text,pg_catalog.text)": 1, "pg_catalog.jsonb_object_agg(pg_catalog.\"any\",pg_catalog.\"any\")": 1, "pg_catalog.jsonb_object_field(pg_catalog.jsonb,pg_catalog.text)": 1, "pg_catalog.jsonb_object_field_text(pg_catalog.jsonb,pg_catalog.text)": 15, "pg_catalog.pg_has_role(pg_catalog.name,pg_catalog.name,pg_catalog.text)": 2}
?column?
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
{"pg_catalog.count()": 1, "pg_catalog.sum(bigint)": 4, "pg_catalog.abs(integer)": 1, "pg_catalog.max(integer)": 2, "pg_catalog.min(integer)": 1, "pg_catalog.sum(integer)": 1, "pg_catalog.int8(numeric)": 4, "pg_catalog.sum(interval)": 2, "pg_catalog.current_database()": 1, "public.get_telemetry_report()": 1, "pg_catalog.text(pg_catalog.name)": 1, "pg_catalog.int4eq(integer,integer)": 3, "pg_catalog.int4mi(integer,integer)": 11, "pg_catalog.int4pl(integer,integer)": 3, "pg_catalog.concat(pg_catalog.\"any\")": 3, "pg_catalog.pg_get_userbyid(pg_catalog.oid)": 1, "pg_catalog.nameeq(pg_catalog.name,pg_catalog.name)": 2, "pg_catalog.texteq(pg_catalog.text,pg_catalog.text)": 1, "pg_catalog.nameregexeq(pg_catalog.name,pg_catalog.text)": 1, "pg_catalog.textregexeq(pg_catalog.text,pg_catalog.text)": 1, "pg_catalog.jsonb_object_agg(pg_catalog.\"any\",pg_catalog.\"any\")": 1, "pg_catalog.jsonb_object_field(pg_catalog.jsonb,pg_catalog.text)": 1, "pg_catalog.jsonb_object_field_text(pg_catalog.jsonb,pg_catalog.text)": 15, "pg_catalog.pg_has_role(pg_catalog.name,pg_catalog.oid,pg_catalog.text)": 1, "pg_catalog.pg_has_role(pg_catalog.name,pg_catalog.name,pg_catalog.text)": 1}
(1 row)
-- check the report again to see if resetting works
SELECT get_telemetry_report()->'functions_used';
?column?
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
{"pg_catalog.count()": 1, "pg_catalog.sum(bigint)": 4, "pg_catalog.max(integer)": 2, "pg_catalog.int8(numeric)": 4, "pg_catalog.sum(interval)": 2, "pg_catalog.current_database()": 1, "public.get_telemetry_report()": 1, "pg_catalog.text(pg_catalog.name)": 1, "pg_catalog.int4eq(integer,integer)": 2, "pg_catalog.concat(pg_catalog.\"any\")": 3, "pg_catalog.pg_get_userbyid(pg_catalog.oid)": 1, "pg_catalog.nameeq(pg_catalog.name,pg_catalog.name)": 2, "pg_catalog.texteq(pg_catalog.text,pg_catalog.text)": 1, "pg_catalog.nameregexeq(pg_catalog.name,pg_catalog.text)": 1, "pg_catalog.textregexeq(pg_catalog.text,pg_catalog.text)": 1, "pg_catalog.jsonb_object_agg(pg_catalog.\"any\",pg_catalog.\"any\")": 1, "pg_catalog.jsonb_object_field(pg_catalog.jsonb,pg_catalog.text)": 1, "pg_catalog.jsonb_object_field_text(pg_catalog.jsonb,pg_catalog.text)": 15, "pg_catalog.pg_has_role(pg_catalog.name,pg_catalog.name,pg_catalog.text)": 2}
?column?
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
{"pg_catalog.count()": 1, "pg_catalog.sum(bigint)": 4, "pg_catalog.max(integer)": 2, "pg_catalog.int8(numeric)": 4, "pg_catalog.sum(interval)": 2, "pg_catalog.current_database()": 1, "public.get_telemetry_report()": 1, "pg_catalog.text(pg_catalog.name)": 1, "pg_catalog.int4eq(integer,integer)": 2, "pg_catalog.concat(pg_catalog.\"any\")": 3, "pg_catalog.pg_get_userbyid(pg_catalog.oid)": 1, "pg_catalog.nameeq(pg_catalog.name,pg_catalog.name)": 2, "pg_catalog.texteq(pg_catalog.text,pg_catalog.text)": 1, "pg_catalog.nameregexeq(pg_catalog.name,pg_catalog.text)": 1, "pg_catalog.textregexeq(pg_catalog.text,pg_catalog.text)": 1, "pg_catalog.jsonb_object_agg(pg_catalog.\"any\",pg_catalog.\"any\")": 1, "pg_catalog.jsonb_object_field(pg_catalog.jsonb,pg_catalog.text)": 1, "pg_catalog.jsonb_object_field_text(pg_catalog.jsonb,pg_catalog.text)": 15, "pg_catalog.pg_has_role(pg_catalog.name,pg_catalog.oid,pg_catalog.text)": 1, "pg_catalog.pg_has_role(pg_catalog.name,pg_catalog.name,pg_catalog.text)": 1}
(1 row)
\c :TEST_DBNAME :ROLE_SUPERUSER

View File

@ -320,7 +320,7 @@ policy_compression_add_internal(Oid user_rel_oid, Datum compress_after_datum,
&proc_name,
&check_schema,
&check_name,
&owner,
owner_id,
true,
fixed_schedule,
hypertable->fd.id,

View File

@ -633,7 +633,7 @@ policy_refresh_cagg_add_internal(Oid cagg_oid, Oid start_offset_type, NullableDa
&proc_name,
&check_schema,
&check_name,
&owner,
owner_id,
true,
fixed_schedule,
cagg->data.mat_hypertable_id,

View File

@ -80,7 +80,6 @@ job_add(PG_FUNCTION_ARGS)
NameData application_name;
NameData proc_name;
NameData proc_schema;
NameData owner_name;
NameData check_name = { .data = { 0 } };
NameData check_schema = { .data = { 0 } };
Interval max_runtime = { .time = DEFAULT_MAX_RUNTIME };
@ -167,7 +166,6 @@ job_add(PG_FUNCTION_ARGS)
namestrcpy(&application_name, "User-Defined Action");
namestrcpy(&proc_schema, get_namespace_name(get_func_namespace(proc)));
namestrcpy(&proc_name, func_name);
namestrcpy(&owner_name, GetUserNameFromId(owner, false));
/* The check exists but may not have the expected signature: (config jsonb) */
if (OidIsValid(check))
@ -184,7 +182,7 @@ job_add(PG_FUNCTION_ARGS)
&proc_name,
&check_schema,
&check_name,
&owner_name,
owner,
scheduled,
fixed_schedule,
0,
@ -240,18 +238,16 @@ job_delete(PG_FUNCTION_ARGS)
{
int32 job_id = PG_GETARG_INT32(0);
BgwJob *job;
Oid owner;
TS_PREVENT_FUNC_IF_READ_ONLY();
job = find_job(job_id, PG_ARGISNULL(0), false);
owner = get_role_oid(NameStr(job->fd.owner), false);
if (!has_privs_of_role(GetUserId(), owner))
if (!has_privs_of_role(GetUserId(), job->fd.owner))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("insufficient permissions to delete job for user \"%s\"",
NameStr(job->fd.owner))));
GetUserNameFromId(job->fd.owner, false))));
ts_bgw_job_delete_by_id(job_id);

View File

@ -281,7 +281,7 @@ policy_reorder_add(PG_FUNCTION_ARGS)
&proc_name,
&check_schema,
&check_name,
&owner,
owner_id,
true,
fixed_schedule,
hypertable_id,

View File

@ -272,12 +272,11 @@ policy_retention_add_internal(Oid ht_oid, Oid window_type, Datum window_datum,
/* Next, insert a new job into jobs table */
namestrcpy(&application_name, "Retention Policy");
NameData proc_name, proc_schema, check_schema, check_name, owner;
NameData proc_name, proc_schema, check_schema, check_name;
namestrcpy(&proc_name, POLICY_RETENTION_PROC_NAME);
namestrcpy(&proc_schema, INTERNAL_SCHEMA_NAME);
namestrcpy(&check_name, POLICY_RETENTION_CHECK_NAME);
namestrcpy(&check_schema, INTERNAL_SCHEMA_NAME);
namestrcpy(&owner, GetUserNameFromId(owner_id, false));
job_id = ts_bgw_job_insert_relation(&application_name,
&default_schedule_interval,
@ -288,7 +287,7 @@ policy_retention_add_internal(Oid ht_oid, Oid window_type, Datum window_datum,
&proc_name,
&check_schema,
&check_name,
&owner,
owner_id,
true,
fixed_schedule,
hypertable->fd.id,

View File

@ -864,6 +864,8 @@ ERROR: permission denied for function "test_config_check_func_privileges"
\c :TEST_DBNAME :ROLE_SUPERUSER
DROP SCHEMA test_schema CASCADE;
NOTICE: drop cascades to function test_schema.test_config_check_func_privileges(jsonb)
-- Delete all jobs with that owner before we can drop the user.
DELETE FROM _timescaledb_config.bgw_job WHERE owner = 'user_noexec'::regrole;
DROP ROLE user_noexec;
-- test with aggregate check proc
create function jsonb_add (j1 jsonb, j2 jsonb) returns jsonb

View File

@ -16,7 +16,16 @@ CREATE OR REPLACE FUNCTION ts_bgw_params_destroy() RETURNS VOID AS :MODULE_PATHN
CREATE OR REPLACE FUNCTION ts_bgw_test_job_sleep(job_id INT, config JSONB) RETURNS VOID AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
CREATE OR REPLACE FUNCTION ts_bgw_params_reset_time(set_time BIGINT = 0, wait BOOLEAN = false) RETURNS VOID
AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
CREATE OR REPLACE FUNCTION insert_job(application_name NAME,job_type NAME, schedule_interval INTERVAL, max_runtime INTERVAL, retry_period INTERVAL, owner NAME DEFAULT CURRENT_ROLE, scheduled BOOL DEFAULT true, fixed_schedule BOOL DEFAULT false) RETURNS INT LANGUAGE SQL SECURITY DEFINER AS
CREATE OR REPLACE FUNCTION insert_job(
application_name NAME,
job_type NAME,
schedule_interval INTERVAL,
max_runtime INTERVAL,
retry_period INTERVAL,
owner regrole DEFAULT CURRENT_ROLE::regrole,
scheduled BOOL DEFAULT true,
fixed_schedule BOOL DEFAULT false
) RETURNS INT LANGUAGE SQL SECURITY DEFINER AS
$$
INSERT INTO _timescaledb_config.bgw_job(application_name,schedule_interval,max_runtime,max_retries,
retry_period,proc_name,proc_schema,owner,scheduled,fixed_schedule)
@ -1603,6 +1612,35 @@ consecutive_crashes | 0
flags | 0
\x off
-- Test renaming a user and see that the owner of the job changes.
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE USER another_user;
SET ROLE another_user;
SELECT insert_job('another_one', 'bgw_test_job_1', INTERVAL '100ms', INTERVAL '100s', INTERVAL '1s') AS job_id \gset
SELECT proc_name, owner FROM _timescaledb_config.bgw_job WHERE id = :job_id;
proc_name | owner
----------------+--------------
bgw_test_job_1 | another_user
(1 row)
RESET ROLE;
ALTER USER another_user RENAME TO renamed_user;
SELECT proc_name, owner FROM _timescaledb_config.bgw_job WHERE id = :job_id;
proc_name | owner
----------------+--------------
bgw_test_job_1 | renamed_user
(1 row)
-- This should fail since the job is dependent on the owner
\set VERBOSITY default
\set ON_ERROR_STOP 0
DROP USER renamed_user;
ERROR: role "renamed_user" cannot be dropped because some objects depend on it
DETAIL: owner of job 1026
\set ON_ERROR_STOP 1
DELETE FROM _timescaledb_config.bgw_job WHERE id = :job_id;
-- This should succeed
DROP USER renamed_user;
-- clean up jobs
SELECT _timescaledb_internal.stop_background_workers();
stop_background_workers

View File

@ -18,7 +18,16 @@ CREATE OR REPLACE FUNCTION ts_bgw_test_job_sleep(job_id INT, config JSONB) RETUR
CREATE OR REPLACE FUNCTION ts_bgw_params_reset_time(set_time BIGINT = 0, wait BOOLEAN = false) RETURNS VOID
AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
-- we use insert_job instead of add_job because we want to be able to set and use max_retries, max_runtime, retry_period which are not part of the add_job api
CREATE OR REPLACE FUNCTION insert_job(application_name NAME,job_type NAME, schedule_interval INTERVAL, max_runtime INTERVAL, retry_period INTERVAL, owner NAME DEFAULT CURRENT_ROLE, scheduled BOOL DEFAULT true, fixed_schedule BOOL DEFAULT true)
CREATE OR REPLACE FUNCTION insert_job(
application_name NAME,
job_type NAME,
schedule_interval INTERVAL,
max_runtime INTERVAL,
retry_period INTERVAL,
owner regrole DEFAULT current_role::regrole,
scheduled BOOL DEFAULT true,
fixed_schedule BOOL DEFAULT true
)
RETURNS INT LANGUAGE SQL SECURITY DEFINER AS
$$
INSERT INTO _timescaledb_config.bgw_job(application_name,schedule_interval,max_runtime,max_retries,

View File

@ -530,6 +530,9 @@ select * from alter_job(:job_id_owner, check_config => 'test_schema.test_config_
\c :TEST_DBNAME :ROLE_SUPERUSER
DROP SCHEMA test_schema CASCADE;
-- Delete all jobs with that owner before we can drop the user.
DELETE FROM _timescaledb_config.bgw_job WHERE owner = 'user_noexec'::regrole;
DROP ROLE user_noexec;
-- test with aggregate check proc

View File

@ -22,7 +22,16 @@ CREATE OR REPLACE FUNCTION ts_bgw_test_job_sleep(job_id INT, config JSONB) RETUR
CREATE OR REPLACE FUNCTION ts_bgw_params_reset_time(set_time BIGINT = 0, wait BOOLEAN = false) RETURNS VOID
AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
CREATE OR REPLACE FUNCTION insert_job(application_name NAME,job_type NAME, schedule_interval INTERVAL, max_runtime INTERVAL, retry_period INTERVAL, owner NAME DEFAULT CURRENT_ROLE, scheduled BOOL DEFAULT true, fixed_schedule BOOL DEFAULT false) RETURNS INT LANGUAGE SQL SECURITY DEFINER AS
CREATE OR REPLACE FUNCTION insert_job(
application_name NAME,
job_type NAME,
schedule_interval INTERVAL,
max_runtime INTERVAL,
retry_period INTERVAL,
owner regrole DEFAULT CURRENT_ROLE::regrole,
scheduled BOOL DEFAULT true,
fixed_schedule BOOL DEFAULT false
) RETURNS INT LANGUAGE SQL SECURITY DEFINER AS
$$
INSERT INTO _timescaledb_config.bgw_job(application_name,schedule_interval,max_runtime,max_retries,
retry_period,proc_name,proc_schema,owner,scheduled,fixed_schedule)
@ -656,6 +665,32 @@ SELECT * FROM sorted_bgw_log;
\x on
SELECT * FROM _timescaledb_internal.bgw_job_stat;
\x off
-- Test renaming a user and see that the owner of the job changes.
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE USER another_user;
SET ROLE another_user;
SELECT insert_job('another_one', 'bgw_test_job_1', INTERVAL '100ms', INTERVAL '100s', INTERVAL '1s') AS job_id \gset
SELECT proc_name, owner FROM _timescaledb_config.bgw_job WHERE id = :job_id;
RESET ROLE;
ALTER USER another_user RENAME TO renamed_user;
SELECT proc_name, owner FROM _timescaledb_config.bgw_job WHERE id = :job_id;
-- This should fail since the job is dependent on the owner
\set VERBOSITY default
\set ON_ERROR_STOP 0
DROP USER renamed_user;
\set ON_ERROR_STOP 1
DELETE FROM _timescaledb_config.bgw_job WHERE id = :job_id;
-- This should succeed
DROP USER renamed_user;
-- clean up jobs
SELECT _timescaledb_internal.stop_background_workers();

View File

@ -24,7 +24,16 @@ CREATE OR REPLACE FUNCTION ts_bgw_params_reset_time(set_time BIGINT = 0, wait BO
AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
-- we use insert_job instead of add_job because we want to be able to set and use max_retries, max_runtime, retry_period which are not part of the add_job api
CREATE OR REPLACE FUNCTION insert_job(application_name NAME,job_type NAME, schedule_interval INTERVAL, max_runtime INTERVAL, retry_period INTERVAL, owner NAME DEFAULT CURRENT_ROLE, scheduled BOOL DEFAULT true, fixed_schedule BOOL DEFAULT true)
CREATE OR REPLACE FUNCTION insert_job(
application_name NAME,
job_type NAME,
schedule_interval INTERVAL,
max_runtime INTERVAL,
retry_period INTERVAL,
owner regrole DEFAULT current_role::regrole,
scheduled BOOL DEFAULT true,
fixed_schedule BOOL DEFAULT true
)
RETURNS INT LANGUAGE SQL SECURITY DEFINER AS
$$
INSERT INTO _timescaledb_config.bgw_job(application_name,schedule_interval,max_runtime,max_retries,