@@ -162,7 +162,7 @@ var crdbInternal = virtualSchema{
162
162
catconstants .CrdbInternalIndexSpansTableID : crdbInternalIndexSpansTable ,
163
163
catconstants .CrdbInternalIndexUsageStatisticsTableID : crdbInternalIndexUsageStatistics ,
164
164
catconstants .CrdbInternalInflightTraceSpanTableID : crdbInternalInflightTraceSpanTable ,
165
- catconstants .CrdbInternalJobsTableID : crdbInternalJobsTable ,
165
+ catconstants .CrdbInternalJobsTableID : crdbInternalJobsView ,
166
166
catconstants .CrdbInternalSystemJobsTableID : crdbInternalSystemJobsTable ,
167
167
catconstants .CrdbInternalKVNodeStatusTableID : crdbInternalKVNodeStatusTable ,
168
168
catconstants .CrdbInternalKVStoreStatusTableID : crdbInternalKVStoreStatusTable ,
@@ -1134,142 +1134,82 @@ func wrapPayloadUnMarshalError(err error, jobID tree.Datum) error {
1134
1134
" consider deleting this job from system.jobs" , jobID )
1135
1135
}
1136
1136
1137
- const (
1138
- jobIDFilter = ` WHERE j.id = $1`
1139
- jobsStatusFilter = ` WHERE j.status = $1`
1140
- jobsTypeFilter = ` WHERE j.job_type = $1`
1141
- )
1142
-
1143
- // TODO(tbg): prefix with kv_.
1144
- var crdbInternalJobsTable = virtualSchemaTable {
1145
- schema : `
1146
- CREATE TABLE crdb_internal.jobs (
1147
- job_id INT,
1148
- job_type STRING,
1149
- description STRING,
1150
- statement STRING,
1151
- user_name STRING,
1152
- status STRING,
1153
- running_status STRING,
1154
- created TIMESTAMPTZ,
1155
- finished TIMESTAMPTZ,
1156
- modified TIMESTAMPTZ,
1157
- fraction_completed FLOAT,
1158
- high_water_timestamp DECIMAL,
1159
- error STRING,
1160
- coordinator_id INT,
1161
- INDEX(job_id),
1162
- INDEX(status),
1163
- INDEX(job_type)
1137
+ var crdbInternalJobsView = virtualSchemaView {
1138
+ // TODO(dt): the left-outer joins here in theory mean that if there are more
1139
+ // than one row per job in status or progress the job row would need to be
1140
+ // repeated for each. While this is never the case in practice, it does mean
1141
+ // the optimizer cannot elide the joins even if the progress or status is not
1142
+ // actually being read from a query on the view. We could change the joins to
1143
+ // be `LATERAL (... WHERE job_id = id LiMIT 1) ON true` which would promise to
1144
+ // the optimizer that the join cannot change the number of rows, allowing it
1145
+ // to elide it entirely if the joined columns are not used, however SHOW JOBS
1146
+ // and other callers typically _are_ looking for progress and status so we
1147
+ // likely will need the joins anyway, queries experts expressed some concern
1148
+ // that it could be possible "lateral join prevents some other optimizations".
1149
+ // Given we typically expect to need the joined columns anyway, these are left
1150
+ // as left joins for now. NB: the `union`, needs to be `union all` to avoid
1151
+ // materializing all of the columns even when they're not used, since a
1152
+ // non-`all` union has to be prepared to compare them all to eliminate
1153
+ // duplicates, even when in the vast majority of calls we expect the session
1154
+ // jobs generator to be empty.
1155
+ schema : `CREATE VIEW crdb_internal.jobs (
1156
+ job_id,
1157
+ job_type,
1158
+ description,
1159
+ statement,
1160
+ user_name,
1161
+ status,
1162
+ running_status,
1163
+ created,
1164
+ finished,
1165
+ modified,
1166
+ fraction_completed,
1167
+ high_water_timestamp,
1168
+ error,
1169
+ coordinator_id
1170
+ ) AS (
1171
+ SELECT
1172
+ j.id,
1173
+ j.job_type,
1174
+ coalesce(j.description, '') as description,
1175
+ coalesce(j.description, '') as statement,
1176
+ coalesce(j.owner, '') as user_name,
1177
+ j.status as status,
1178
+ s.status as running_status,
1179
+ j.created::timestamptz,
1180
+ j.finished,
1181
+ greatest(j.created, j.finished, p.written, s.written)::timestamptz AS modified,
1182
+ p.fraction as fraction_completed,
1183
+ p.resolved as high_water_timestamp,
1184
+ coalesce(j.error_msg, '') as error,
1185
+ j.claim_instance_id as coordinator_id
1186
+ FROM system.public.jobs AS j
1187
+ LEFT OUTER JOIN system.public.job_progress AS p ON j.id = p.job_id
1188
+ LEFT OUTER JOIN system.public.job_status AS s ON j.id = s.job_id
1189
+ WHERE crdb_internal.can_view_job(j.owner)
1190
+ UNION ALL
1191
+ (SELECT job_id, job_type, description, description, user_name, 'pending',
1192
+ NULL, now(), NULL, now(), NULL, NULL, NULL, NULL
1193
+ FROM crdb_internal.session_pending_jobs()
1194
+ )
1164
1195
)` ,
1165
- comment : `decoded job metadata from crdb_internal.system_jobs (KV scan)` ,
1166
- indexes : []virtualIndex {{
1167
- populate : func (ctx context.Context , unwrappedConstraint tree.Datum , p * planner , _ catalog.DatabaseDescriptor , addRow func (... tree.Datum ) error ) (matched bool , err error ) {
1168
- targetID := tree .MustBeDInt (unwrappedConstraint )
1169
- return makeJobsTableRows (ctx , p , addRow , jobIDFilter , targetID )
1170
- },
1171
- }, {
1172
- populate : func (ctx context.Context , unwrappedConstraint tree.Datum , p * planner , _ catalog.DatabaseDescriptor , addRow func (... tree.Datum ) error ) (matched bool , err error ) {
1173
- targetStatus := tree .MustBeDString (unwrappedConstraint )
1174
- return makeJobsTableRows (ctx , p , addRow , jobsStatusFilter , targetStatus )
1175
- },
1176
- }, {
1177
- populate : func (ctx context.Context , unwrappedConstraint tree.Datum , p * planner , _ catalog.DatabaseDescriptor , addRow func (... tree.Datum ) error ) (matched bool , err error ) {
1178
- targetType := tree .MustBeDString (unwrappedConstraint )
1179
- return makeJobsTableRows (ctx , p , addRow , jobsTypeFilter , targetType )
1180
- },
1181
- }},
1182
- populate : func (ctx context.Context , p * planner , db catalog.DatabaseDescriptor , addRow func (... tree.Datum ) error ) error {
1183
- _ , err := makeJobsTableRows (ctx , p , addRow , "" )
1184
- return err
1196
+ resultColumns : colinfo.ResultColumns {
1197
+ {Name : "job_id" , Typ : types .Int },
1198
+ {Name : "job_type" , Typ : types .String },
1199
+ {Name : "description" , Typ : types .String },
1200
+ {Name : "statement" , Typ : types .String },
1201
+ {Name : "user_name" , Typ : types .String },
1202
+ {Name : "status" , Typ : types .String },
1203
+ {Name : "running_status" , Typ : types .String },
1204
+ {Name : "created" , Typ : types .TimestampTZ },
1205
+ {Name : "finished" , Typ : types .TimestampTZ },
1206
+ {Name : "modified" , Typ : types .TimestampTZ },
1207
+ {Name : "fraction_completed" , Typ : types .Float },
1208
+ {Name : "high_water_timestamp" , Typ : types .Decimal },
1209
+ {Name : "error" , Typ : types .String },
1210
+ {Name : "coordinator_id" , Typ : types .Int },
1185
1211
},
1186
- }
1187
-
1188
- func makeJobsTableRows (
1189
- ctx context.Context ,
1190
- p * planner ,
1191
- addRow func (... tree.Datum ) error ,
1192
- whereClause string ,
1193
- params ... interface {},
1194
- ) (emitted bool , retErr error ) {
1195
- globalPrivileges , err := jobsauth .GetGlobalJobPrivileges (ctx , p )
1196
- if err != nil {
1197
- return false , err
1198
- }
1199
-
1200
- query := `SELECT
1201
- j.id, j.job_type, coalesce(j.description, ''), coalesce(j.owner, ''), j.status as state,
1202
- s.status, j.created::timestamptz, j.finished, greatest(j.created, j.finished, p.written, s.written)::timestamptz AS last_modified,
1203
- p.fraction,
1204
- p.resolved,
1205
- j.error_msg,
1206
- j.claim_instance_id
1207
- FROM system.public.jobs AS j
1208
- LEFT OUTER JOIN system.public.job_progress AS p ON j.id = p.job_id
1209
- LEFT OUTER JOIN system.public.job_status AS s ON j.id = s.job_id
1210
- ` + whereClause + `UNION
1211
- (SELECT job_id, job_type, description, user_name, 'pending',
1212
- NULL, now(), NULL, now(), NULL, NULL, NULL, NULL
1213
- FROM crdb_internal.session_pending_jobs())`
1214
-
1215
- it , err := p .InternalSQLTxn ().QueryIteratorEx (
1216
- ctx , "system-jobs-join" , p .txn , sessiondata .NodeUserSessionDataOverride , query , params ... )
1217
- if err != nil {
1218
- return emitted , err
1219
- }
1220
- defer func () {
1221
- if err := it .Close (); err != nil {
1222
- retErr = errors .CombineErrors (retErr , err )
1223
- }
1224
- }()
1225
-
1226
- // Loop while we need to skip a row.
1227
- for {
1228
- ok , err := it .Next (ctx )
1229
- if err != nil || ! ok {
1230
- return emitted , err
1231
- }
1232
- r := it .Cur ()
1233
- id , typStr , desc , ownerStr , state , status , created , finished , modified , fraction , resolved , errorMsg , instanceID :=
1234
- r [0 ], r [1 ], r [2 ], r [3 ], r [4 ], r [5 ], r [6 ], r [7 ], r [8 ], r [9 ], r [10 ], r [11 ], r [12 ]
1235
-
1236
- owner := username .MakeSQLUsernameFromPreNormalizedString (string (tree .MustBeDString (ownerStr )))
1237
- jobID := jobspb .JobID (tree .MustBeDInt (id ))
1238
-
1239
- if errorMsg == tree .DNull {
1240
- errorMsg = emptyString
1241
- }
1242
-
1243
- if err := jobsauth .Authorize (
1244
- ctx , p , jobID , owner , jobsauth .ViewAccess , globalPrivileges ,
1245
- ); err != nil {
1246
- // Filter out jobs which the user is not allowed to see.
1247
- if IsInsufficientPrivilegeError (err ) {
1248
- continue
1249
- }
1250
- return emitted , err
1251
- }
1252
-
1253
- if err = addRow (
1254
- id ,
1255
- typStr ,
1256
- desc ,
1257
- desc ,
1258
- ownerStr ,
1259
- state ,
1260
- status ,
1261
- created ,
1262
- finished ,
1263
- modified ,
1264
- fraction ,
1265
- resolved ,
1266
- errorMsg ,
1267
- instanceID ,
1268
- ); err != nil {
1269
- return emitted , err
1270
- }
1271
- emitted = true
1272
- }
1212
+ comment : `decoded job metadata from various jobs tables` ,
1273
1213
}
1274
1214
1275
1215
const crdbInternalKVProtectedTSTableQuery = `
@@ -9355,7 +9295,7 @@ var crdbInternalClusterReplicationResolvedView = virtualSchemaView{
9355
9295
SELECT
9356
9296
j.id AS job_id, jsonb_array_elements(crdb_internal.pb_to_json('progress', i.value)->'streamIngest'->'checkpoint'->'resolvedSpans') AS s
9357
9297
FROM system.jobs j LEFT JOIN system.job_info i ON j.id = i.job_id AND i.info_key = 'legacy_progress'
9358
- WHERE j.job_type = 'REPLICATION STREAM INGESTION'
9298
+ WHERE j.job_type = 'REPLICATION STREAM INGESTION' AND pg_has_role(current_user, 'admin', 'member')
9359
9299
) SELECT
9360
9300
job_id,
9361
9301
crdb_internal.pretty_key(decode(s->'span'->>'key', 'base64'), 0) AS start_key,
@@ -9378,7 +9318,7 @@ var crdbInternalLogicalReplicationResolvedView = virtualSchemaView{
9378
9318
SELECT
9379
9319
j.id AS job_id, jsonb_array_elements(crdb_internal.pb_to_json('progress', i.value)->'LogicalReplication'->'checkpoint'->'resolvedSpans') AS s
9380
9320
FROM system.jobs j LEFT JOIN system.job_info i ON j.id = i.job_id AND i.info_key = 'legacy_progress'
9381
- WHERE j.job_type = 'LOGICAL REPLICATION'
9321
+ WHERE j.job_type = 'LOGICAL REPLICATION' AND pg_has_role(current_user, 'admin', 'member')
9382
9322
) SELECT
9383
9323
job_id,
9384
9324
crdb_internal.pretty_key(decode(s->'span'->>'key', 'base64'), 0) AS start_key,
0 commit comments