@opengeni/db 1.0.1 → 3.1.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attached-browser-devices.d.ts +7 -0
- package/dist/automations.d.ts +154 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-integrations.d.ts +13 -0
- package/dist/child-lifecycle-notices.d.ts +68 -0
- package/dist/{chunk-LTG3S6ZY.js → chunk-EULAN2NP.js} +2 -2
- package/dist/{chunk-CUPM3FGB.js → chunk-NJVZPWXY.js} +3 -3
- package/dist/{chunk-4SR4HKKA.js → chunk-NMUBV7Z5.js} +19 -11
- package/dist/chunk-NMUBV7Z5.js.map +1 -0
- package/dist/chunk-OF5OBB76.js +4457 -0
- package/dist/chunk-OF5OBB76.js.map +1 -0
- package/dist/{chunk-6ZYKCRFO.js → chunk-PR2WMLOQ.js} +7580 -4417
- package/dist/chunk-PR2WMLOQ.js.map +1 -0
- package/dist/{chunk-ZWREYEWA.js → chunk-SCPUJS53.js} +41 -4
- package/dist/chunk-SCPUJS53.js.map +1 -0
- package/dist/{chunk-SK7YYHBI.js → chunk-SIBYBA2D.js} +46 -1
- package/dist/chunk-SIBYBA2D.js.map +1 -0
- package/dist/chunk-TIKF3KTI.js +383 -0
- package/dist/chunk-TIKF3KTI.js.map +1 -0
- package/dist/chunk-ZZ5LJEFX.js +516 -0
- package/dist/chunk-ZZ5LJEFX.js.map +1 -0
- package/dist/codemode-operations.d.ts +1 -6
- package/dist/company-brain-context-selection-schema.d.ts +705 -0
- package/dist/company-brain-context-selection.d.ts +94 -0
- package/dist/company-brain-governed-writes.d.ts +24 -0
- package/dist/company-brain.d.ts +55 -0
- package/dist/company-profile-agent-admin.d.ts +15 -0
- package/dist/company-profile-schema.d.ts +619 -2
- package/dist/company-profile.d.ts +9 -4
- package/dist/connection-authority.d.ts +95 -0
- package/dist/connection-scopes.d.ts +8 -0
- package/dist/connection-tenancy-backfill.d.ts +41 -0
- package/dist/connection-token-resolver.d.ts +30 -1
- package/dist/database.d.ts +19 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.d.ts +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/governed-learning-activation-schema.d.ts +984 -0
- package/dist/governed-learning-activation.d.ts +98 -0
- package/dist/governed-learning-evaluator-schema.d.ts +633 -0
- package/dist/governed-learning-evaluator.d.ts +28 -0
- package/dist/index.d.ts +1595 -81
- package/dist/index.js +21571 -8126
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +18 -0
- package/dist/integration-bindings.d.ts +1 -0
- package/dist/integration-facets.d.ts +30 -0
- package/dist/interaction-placement-loss.d.ts +22 -0
- package/dist/managed-human-provisioning.d.ts +25 -0
- package/dist/memory-domain.d.ts +3 -3
- package/dist/migrate.d.ts +11 -1
- package/dist/migrate.js +1 -1
- package/dist/new-session-drafts.d.ts +43 -5
- package/dist/organization-membership-backfill.d.ts +174 -0
- package/dist/organization-membership-lifecycle.d.ts +162 -0
- package/dist/pr-review.d.ts +107 -0
- package/dist/preference-registry-schema.d.ts +2 -0
- package/dist/preference-registry.d.ts +2 -0
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +22 -8
- package/dist/schema.d.ts +24914 -13703
- package/dist/schema.js +99 -1
- package/dist/scoped-knowledge-schema.d.ts +52 -1
- package/dist/session-background-commands.d.ts +125 -0
- package/dist/session-background-commands.js +34 -0
- package/dist/session-background-commands.js.map +1 -0
- package/dist/session-control.d.ts +156 -3
- package/dist/session-goal-pacing.d.ts +68 -0
- package/dist/session-queue-commands.d.ts +41 -4
- package/dist/session-realtime-ledger.d.ts +2 -0
- package/dist/session-tenancy.d.ts +71 -1
- package/dist/session-tenancy.js +25 -3
- package/dist/session-tool-call-settlement.d.ts +9 -4
- package/dist/slack-routing-personal-workspace.d.ts +23 -0
- package/dist/slack-routing.d.ts +153 -0
- package/dist/slack-task-policy-schema.d.ts +34 -0
- package/dist/slack-task-policy.d.ts +8 -0
- package/dist/slack-user-link-access.d.ts +1 -0
- package/dist/task-notes-schema.d.ts +594 -0
- package/dist/task-notes.d.ts +39 -0
- package/dist/turn-initiator.d.ts +2 -0
- package/dist/user-resource-authority.d.ts +109 -0
- package/dist/video-generation.js +3 -3
- package/dist/workspace-authority.d.ts +81 -0
- package/dist/workspace-instruction-policies-schema.d.ts +279 -0
- package/dist/workspace-instruction-policies.d.ts +62 -0
- package/dist/workspace-learning-policy.d.ts +1 -0
- package/dist/workspace-membership-permissions.d.ts +2 -0
- package/drizzle/0240_enrollment_connection_authority.sql +39 -0
- package/drizzle/0241_atomic_personal_resource_delegation.sql +1000 -0
- package/drizzle/0241_enrollment_agent_runtime.sql +70 -0
- package/drizzle/0244_slack_app_home_refresh_queue.sql +120 -0
- package/drizzle/0245_model_context_contribution_facts.sql +88 -0
- package/drizzle/0246_integration_personal_instance_authority.sql +70 -0
- package/drizzle/0247_terraform_stacks_provenance_repair.sql +428 -0
- package/drizzle/0248_terraform_stacks_component_resolution_fence.sql +83 -0
- package/drizzle/0249_personal_resource_delegation_authority_correction.sql +662 -0
- package/drizzle/0250_direct_retained_process_owner_liveness.sql +199 -0
- package/drizzle/0251_connected_machine_operation_policy.sql +45 -0
- package/drizzle/0252_scheduled_personal_resource_delegation.sql +1867 -0
- package/drizzle/0253_common_user_resource_authority_lifecycle.sql +1555 -0
- package/drizzle/0254_scoped_variable_set_authority.sql +1255 -0
- package/drizzle/0255_company_brain_governed_write_proposals.sql +524 -0
- package/drizzle/0256_connection_authority_delegation.sql +703 -0
- package/drizzle/0257_goal_revision_decisions_and_root_constraints.sql +436 -0
- package/drizzle/0258_three_scope_document_knowledge_authority.sql +1273 -0
- package/drizzle/0259_company_brain_context_selection_receipts.sql +869 -0
- package/drizzle/0260_task_note_knowledge_promotion.sql +820 -0
- package/drizzle/0261_preference_knowledge_proposal_actor_binding.sql +471 -0
- package/drizzle/0262_scoped_connected_machines_and_rigs.sql +1700 -0
- package/drizzle/0263_organization_membership_lifecycle.sql +3284 -0
- package/drizzle/0264_connection_authority_runtime_activation.sql +1084 -0
- package/drizzle/0266_company_brain_context_receipt_inspection.sql +156 -0
- package/drizzle/0268_governed_learning_decision_receipts.sql +825 -0
- package/drizzle/0269_governed_learning_activation_controller.sql +1874 -0
- package/drizzle/0270_governed_learning_history_inspection.sql +170 -0
- package/drizzle/0271_company_brain_retrieval_only_default.sql +524 -0
- package/drizzle/0272_human_confirmed_learning_activation.sql +591 -0
- package/drizzle/0273_scheduled_variable_set_materialization.sql +268 -0
- package/drizzle/0274_human_confirmed_knowledge_review.sql +310 -0
- package/drizzle/0275_scheduled_connection_authority.sql +5527 -0
- package/drizzle/0276_onboarding_proposal_initiating_human_guc.sql +198 -0
- package/drizzle/0277_workspace_writer_authority_attribution.sql +393 -0
- package/drizzle/0278_workspace_membership_removal_fencing.sql +1174 -0
- package/drizzle/0279_workspace_connection_use_lane.sql +343 -0
- package/drizzle/0280_connection_and_variable_set_audit_attribution.sql +773 -0
- package/drizzle/0281_viewer_holder_authority_claims.sql +52 -0
- package/drizzle/0282_variable_set_session_attach_attribution.sql +178 -0
- package/drizzle/0283_editable_spreadsheet_authored_state.sql +82 -0
- package/drizzle/0284_truthful_human_confirmed_review_reason.sql +930 -0
- package/drizzle/0285_organization_tenancy_inventory.sql +329 -0
- package/drizzle/0286_widen_task_note_expiry_ceiling.sql +391 -0
- package/drizzle/0287_open_suffix_pending_tool_calls.sql +18 -0
- package/drizzle/0288_attached_browser_reenrollment.sql +64 -0
- package/drizzle/0289_session_composer_policy_authority.sql +597 -0
- package/drizzle/0290_organization_membership_backfill.sql +268 -0
- package/drizzle/0291_resource_authority_classification_assertion.sql +371 -0
- package/drizzle/0292_truthful_tenancy_inventory_counters.sql +291 -0
- package/drizzle/0293_confirm_time_rule_rebaseline.sql +615 -0
- package/drizzle/0294_preference_activation_authority.sql +237 -0
- package/drizzle/0295_retire_legacy_standing_memory_mode.sql +476 -0
- package/drizzle/0296_force_rls_backfill_noop_repair.sql +215 -0
- package/drizzle/0297_session_ownership_classification_and_backfill.sql +752 -0
- package/drizzle/0298_organization_tenancy_parity.sql +609 -0
- package/drizzle/0299_organization_membership_lock_order.sql +291 -0
- package/drizzle/0300_tenancy_backfill_ledger.sql +459 -0
- package/drizzle/0301_session_snapshot_and_pin_visibility.sql +181 -0
- package/drizzle/0302_personal_workspace_session_ownership.sql +303 -0
- package/drizzle/0303_session_tenancy_product_activation.sql +919 -0
- package/drizzle/0304_personal_workspace_private_session_reads.sql +97 -0
- package/drizzle/0305_personal_resource_grant_management.sql +584 -0
- package/drizzle/0306_atomic_personal_resource_attachments.sql +1374 -0
- package/drizzle/0307_session_attention_state.sql +51 -0
- package/drizzle/0308_session_archives.sql +35 -0
- package/drizzle/0309_channel_project_pins.sql +6 -0
- package/drizzle/0310_channel_project_order.sql +27 -0
- package/drizzle/0311_company_scope_and_private_session_create.sql +656 -0
- package/drizzle/0312_quiescent_session_tree_deletion.sql +39 -0
- package/drizzle/0313_private_child_session_authority.sql +365 -0
- package/drizzle/0314_unregistered_organization_invitations.sql +606 -0
- package/drizzle/0315_personal_github_repository_selection.sql +624 -0
- package/drizzle/0316_human_confirmed_activation_resumed_generation.sql +869 -0
- package/drizzle/0317_session_goal_continuation_hold.sql +64 -0
- package/drizzle/0318_session_turn_startup_milestones.sql +115 -0
- package/drizzle/0319_event_triggered_automations.sql +254 -0
- package/drizzle/0320_pr_review_pack.sql +113 -0
- package/drizzle/0321_slack_bot_environment_display_name.sql +15 -0
- package/drizzle/0322_session_turns_unclaimed_prompt_trigger_index.sql +5 -0
- package/drizzle/0323_organization_private_session_enablement.sql +489 -0
- package/drizzle/0324_human_confirmed_company_profile_agent_admin.sql +856 -0
- package/drizzle/0325_child_lifecycle_notices.sql +44 -0
- package/drizzle/0326_interaction_operation_error_codes.sql +62 -0
- package/drizzle/0327_slack_first_task_hint.sql +29 -0
- package/drizzle/0328_session_background_commands.sql +590 -0
- package/drizzle/0329_slack_orchestration_delivery_events.sql +91 -0
- package/drizzle/0330_api_key_descriptions.sql +20 -0
- package/drizzle/0331_managed_organization_creation.sql +163 -0
- package/drizzle/0332_organization_shared_workspace_control_plane.sql +539 -0
- package/drizzle/0333_session_turn_prompt_routing.sql +45 -0
- package/drizzle/0334_connected_machine_workspace_root.sql +99 -0
- package/drizzle/0335_slack_workspace_routing.sql +460 -0
- package/drizzle/0336_atomic_session_fork_visibility.sql +667 -0
- package/drizzle/0337_slack_routed_action_handles.sql +189 -0
- package/drizzle/0338_atomic_connected_machine_attachments.sql +410 -0
- package/drizzle/0339_document_authority_reclassification.sql +1153 -0
- package/drizzle/0340_tenancy_backfill_activation_evidence.sql +1347 -0
- package/drizzle/0341_slack_routing_probe_organization_fence.sql +109 -0
- package/package.json +10 -6
- package/src/attached-browser-devices.ts +233 -5
- package/src/automations.ts +911 -0
- package/src/browser-sessions.ts +10 -0
- package/src/capability-integrations.ts +175 -1
- package/src/child-lifecycle-notices.ts +241 -0
- package/src/codemode-operations.ts +1 -35
- package/src/company-brain-context-selection-schema.ts +100 -0
- package/src/company-brain-context-selection.ts +359 -0
- package/src/company-brain-governed-writes.ts +1170 -0
- package/src/company-brain.ts +309 -0
- package/src/company-profile-agent-admin.ts +225 -0
- package/src/company-profile-schema.ts +121 -1
- package/src/company-profile.ts +21 -0
- package/src/computer-sessions.ts +10 -1
- package/src/connection-authority.ts +290 -0
- package/src/connection-scopes.ts +20 -0
- package/src/connection-tenancy-backfill.ts +81 -0
- package/src/connection-token-resolver.ts +263 -31
- package/src/database.ts +75 -2
- package/src/editable-artifacts-schema.ts +10 -5
- package/src/editable-artifacts.ts +21 -11
- package/src/governed-learning-activation-schema.ts +121 -0
- package/src/governed-learning-activation.ts +588 -0
- package/src/governed-learning-evaluator-schema.ts +72 -0
- package/src/governed-learning-evaluator.ts +217 -0
- package/src/index.ts +11651 -1548
- package/src/insights.ts +107 -0
- package/src/integration-bindings.ts +88 -6
- package/src/integration-facets.ts +287 -84
- package/src/interaction-placement-loss.ts +234 -0
- package/src/managed-human-provisioning.ts +116 -0
- package/src/memory-domain.ts +27 -32
- package/src/migrate.ts +77 -1
- package/src/new-session-drafts.ts +229 -35
- package/src/organization-membership-backfill.ts +735 -0
- package/src/organization-membership-lifecycle.ts +985 -0
- package/src/pr-review.ts +890 -0
- package/src/preference-registry.ts +31 -1
- package/src/provision-roles.ts +877 -6
- package/src/runtime-posture-cli.ts +2 -0
- package/src/runtime-posture.ts +1291 -76
- package/src/schema.ts +3303 -428
- package/src/scoped-knowledge-schema.ts +16 -8
- package/src/scoped-knowledge.ts +1 -1
- package/src/session-background-commands.ts +759 -0
- package/src/session-control.ts +640 -74
- package/src/session-goal-pacing.ts +126 -0
- package/src/session-queue-commands.ts +437 -84
- package/src/session-realtime-context.ts +3 -3
- package/src/session-realtime-ledger.ts +22 -11
- package/src/session-realtime-mirror.ts +1 -1
- package/src/session-tenancy.ts +403 -49
- package/src/session-tool-call-settlement.ts +38 -29
- package/src/slack-routing-personal-workspace.ts +67 -0
- package/src/slack-routing.ts +469 -0
- package/src/slack-task-policy-schema.ts +5 -0
- package/src/slack-task-policy.ts +18 -0
- package/src/slack-user-link-access.ts +17 -1
- package/src/task-notes-schema.ts +74 -0
- package/src/task-notes.ts +124 -2
- package/src/turn-initiator.ts +8 -0
- package/src/user-resource-authority.ts +377 -0
- package/src/workspace-authority.ts +160 -0
- package/src/workspace-instruction-policies-schema.ts +53 -1
- package/src/workspace-instruction-policies.ts +528 -77
- package/src/workspace-learning-policy.ts +21 -5
- package/src/workspace-membership-permissions.ts +9 -0
- package/dist/chunk-4SR4HKKA.js.map +0 -1
- package/dist/chunk-6ZYKCRFO.js.map +0 -1
- package/dist/chunk-NXAFLCQX.js +0 -2468
- package/dist/chunk-NXAFLCQX.js.map +0 -1
- package/dist/chunk-SK7YYHBI.js.map +0 -1
- package/dist/chunk-WD5ROSLL.js +0 -117
- package/dist/chunk-WD5ROSLL.js.map +0 -1
- package/dist/chunk-ZWREYEWA.js.map +0 -1
- /package/dist/{chunk-LTG3S6ZY.js.map → chunk-EULAN2NP.js.map} +0 -0
- /package/dist/{chunk-CUPM3FGB.js.map → chunk-NJVZPWXY.js.map} +0 -0
|
@@ -0,0 +1,1867 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Freeze personal Variable Set/Rig authority at scheduled-task write, admit the
|
|
3
|
+
-- exact immutable task snapshot for each occurrence, and bind that occurrence
|
|
4
|
+
-- to the causal scheduled turn before direct attempt admission. This migration
|
|
5
|
+
-- stores identifiers and authority generations only; it does not materialize
|
|
6
|
+
-- secrets, mutate Variable Set/Rig CRUD, or load worker credentials.
|
|
7
|
+
|
|
8
|
+
SET LOCAL lock_timeout = '5s';
|
|
9
|
+
SET LOCAL statement_timeout = '10min';
|
|
10
|
+
|
|
11
|
+
ALTER TABLE "scheduled_tasks"
|
|
12
|
+
ADD COLUMN "authority_revision" bigint NOT NULL DEFAULT 1;
|
|
13
|
+
|
|
14
|
+
ALTER TABLE "scheduled_tasks"
|
|
15
|
+
ADD COLUMN "execution_digest" text;
|
|
16
|
+
|
|
17
|
+
ALTER TABLE "scheduled_tasks"
|
|
18
|
+
ADD CONSTRAINT "scheduled_tasks_authority_revision_chk"
|
|
19
|
+
CHECK ("authority_revision" > 0),
|
|
20
|
+
ADD CONSTRAINT "scheduled_tasks_execution_digest_chk"
|
|
21
|
+
CHECK ("execution_digest" ~ '^[0-9a-f]{64}$') NOT VALID;
|
|
22
|
+
|
|
23
|
+
ALTER TABLE "scheduled_task_runs"
|
|
24
|
+
ADD COLUMN "task_authority_revision" bigint,
|
|
25
|
+
ADD COLUMN "task_execution_digest" text,
|
|
26
|
+
ADD CONSTRAINT "scheduled_task_runs_execution_binding_chk" CHECK (
|
|
27
|
+
("task_authority_revision" IS NULL AND "task_execution_digest" IS NULL)
|
|
28
|
+
OR (
|
|
29
|
+
"task_authority_revision" > 0
|
|
30
|
+
AND "task_execution_digest" ~ '^[0-9a-f]{64}$'
|
|
31
|
+
)
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
ALTER TABLE "session_system_updates"
|
|
35
|
+
ADD COLUMN "scheduled_task_run_id" uuid;
|
|
36
|
+
|
|
37
|
+
CREATE TABLE opengeni_private.scheduled_personal_resource_capabilities (
|
|
38
|
+
"backend_pid" integer NOT NULL,
|
|
39
|
+
"transaction_id" xid8 NOT NULL,
|
|
40
|
+
"capability_kind" text NOT NULL,
|
|
41
|
+
"created_at" timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
42
|
+
CONSTRAINT "scheduled_personal_resource_capabilities_kind_chk" CHECK (
|
|
43
|
+
"capability_kind" IN ('task_write', 'run_admit', 'attempt_match')
|
|
44
|
+
),
|
|
45
|
+
CONSTRAINT "scheduled_personal_resource_capabilities_pk" PRIMARY KEY (
|
|
46
|
+
"backend_pid", "transaction_id", "capability_kind"
|
|
47
|
+
)
|
|
48
|
+
);
|
|
49
|
+
REVOKE ALL ON TABLE opengeni_private.scheduled_personal_resource_capabilities FROM PUBLIC;
|
|
50
|
+
|
|
51
|
+
DO $scheduled_capability_revoke$
|
|
52
|
+
BEGIN
|
|
53
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
54
|
+
REVOKE ALL ON TABLE opengeni_private.scheduled_personal_resource_capabilities
|
|
55
|
+
FROM opengeni_app;
|
|
56
|
+
END IF;
|
|
57
|
+
END
|
|
58
|
+
$scheduled_capability_revoke$;
|
|
59
|
+
|
|
60
|
+
CREATE OR REPLACE FUNCTION opengeni_private.scheduled_personal_resource_capability_active(
|
|
61
|
+
p_capability_kind text DEFAULT NULL
|
|
62
|
+
)
|
|
63
|
+
RETURNS boolean
|
|
64
|
+
LANGUAGE sql
|
|
65
|
+
STABLE
|
|
66
|
+
SECURITY DEFINER
|
|
67
|
+
SET search_path = pg_catalog
|
|
68
|
+
AS $scheduled_personal_resource_capability_active$
|
|
69
|
+
SELECT EXISTS (
|
|
70
|
+
SELECT 1
|
|
71
|
+
FROM opengeni_private.scheduled_personal_resource_capabilities capability
|
|
72
|
+
WHERE capability.backend_pid = pg_catalog.pg_backend_pid()
|
|
73
|
+
AND capability.transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
|
|
74
|
+
AND (
|
|
75
|
+
p_capability_kind IS NULL
|
|
76
|
+
OR capability.capability_kind = p_capability_kind
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
$scheduled_personal_resource_capability_active$;
|
|
80
|
+
|
|
81
|
+
REVOKE ALL ON FUNCTION
|
|
82
|
+
opengeni_private.scheduled_personal_resource_capability_active(text)
|
|
83
|
+
FROM PUBLIC;
|
|
84
|
+
|
|
85
|
+
CREATE OR REPLACE FUNCTION scheduled_task_execution_state(
|
|
86
|
+
p_task scheduled_tasks
|
|
87
|
+
)
|
|
88
|
+
RETURNS jsonb
|
|
89
|
+
LANGUAGE sql
|
|
90
|
+
IMMUTABLE
|
|
91
|
+
SET search_path = pg_catalog, pg_temp
|
|
92
|
+
AS $scheduled_task_execution_state$
|
|
93
|
+
-- Administrative presentation/lifecycle fields and the binding bookkeeping
|
|
94
|
+
-- itself are the only exclusions. Every current and future task column is
|
|
95
|
+
-- execution-affecting by default until a migration explicitly proves and
|
|
96
|
+
-- adds a narrow administrative exception here.
|
|
97
|
+
SELECT pg_catalog.to_jsonb(p_task) - ARRAY[
|
|
98
|
+
'name',
|
|
99
|
+
'status',
|
|
100
|
+
'updated_at',
|
|
101
|
+
'authority_revision',
|
|
102
|
+
'execution_digest'
|
|
103
|
+
]::text[]
|
|
104
|
+
$scheduled_task_execution_state$;
|
|
105
|
+
|
|
106
|
+
CREATE OR REPLACE FUNCTION scheduled_task_execution_digest(
|
|
107
|
+
p_task scheduled_tasks
|
|
108
|
+
)
|
|
109
|
+
RETURNS text
|
|
110
|
+
LANGUAGE sql
|
|
111
|
+
IMMUTABLE
|
|
112
|
+
SET search_path = pg_catalog, pg_temp
|
|
113
|
+
AS $scheduled_task_execution_digest$
|
|
114
|
+
SELECT pg_catalog.encode(
|
|
115
|
+
pg_catalog.sha256(pg_catalog.convert_to(
|
|
116
|
+
(
|
|
117
|
+
pg_catalog.to_jsonb(p_task) - ARRAY[
|
|
118
|
+
'name',
|
|
119
|
+
'status',
|
|
120
|
+
'updated_at',
|
|
121
|
+
'authority_revision',
|
|
122
|
+
'execution_digest'
|
|
123
|
+
]::text[]
|
|
124
|
+
)::text,
|
|
125
|
+
'UTF8'
|
|
126
|
+
)),
|
|
127
|
+
'hex'
|
|
128
|
+
)
|
|
129
|
+
$scheduled_task_execution_digest$;
|
|
130
|
+
|
|
131
|
+
CREATE OR REPLACE FUNCTION set_scheduled_task_execution_digest()
|
|
132
|
+
RETURNS trigger
|
|
133
|
+
LANGUAGE plpgsql
|
|
134
|
+
SECURITY DEFINER
|
|
135
|
+
SET search_path = pg_catalog, pg_temp
|
|
136
|
+
AS $set_scheduled_task_execution_digest$
|
|
137
|
+
BEGIN
|
|
138
|
+
NEW.execution_digest := pg_catalog.encode(
|
|
139
|
+
pg_catalog.sha256(pg_catalog.convert_to(
|
|
140
|
+
(
|
|
141
|
+
pg_catalog.to_jsonb(NEW) - ARRAY[
|
|
142
|
+
'name',
|
|
143
|
+
'status',
|
|
144
|
+
'updated_at',
|
|
145
|
+
'authority_revision',
|
|
146
|
+
'execution_digest'
|
|
147
|
+
]::text[]
|
|
148
|
+
)::text,
|
|
149
|
+
'UTF8'
|
|
150
|
+
)),
|
|
151
|
+
'hex'
|
|
152
|
+
);
|
|
153
|
+
RETURN NEW;
|
|
154
|
+
END
|
|
155
|
+
$set_scheduled_task_execution_digest$;
|
|
156
|
+
|
|
157
|
+
UPDATE scheduled_tasks task
|
|
158
|
+
SET execution_digest = scheduled_task_execution_digest(task);
|
|
159
|
+
|
|
160
|
+
ALTER TABLE "scheduled_tasks"
|
|
161
|
+
ALTER COLUMN "execution_digest" SET NOT NULL,
|
|
162
|
+
VALIDATE CONSTRAINT "scheduled_tasks_execution_digest_chk";
|
|
163
|
+
|
|
164
|
+
CREATE TABLE "scheduled_task_personal_resource_authorities" (
|
|
165
|
+
"task_id" uuid NOT NULL REFERENCES "scheduled_tasks"("id") ON DELETE CASCADE,
|
|
166
|
+
"task_authority_revision" bigint NOT NULL,
|
|
167
|
+
"account_id" uuid NOT NULL,
|
|
168
|
+
"workspace_id" uuid NOT NULL,
|
|
169
|
+
"initiating_human_subject_id" text NOT NULL,
|
|
170
|
+
"owner_organization_membership_id" uuid NOT NULL,
|
|
171
|
+
"membership_authorization_revision" bigint NOT NULL,
|
|
172
|
+
"target_session_id" uuid,
|
|
173
|
+
"session_visibility" text NOT NULL,
|
|
174
|
+
"session_authority_epoch" integer,
|
|
175
|
+
"execution_digest" text NOT NULL,
|
|
176
|
+
"resource_count" integer NOT NULL,
|
|
177
|
+
"created_at" timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
178
|
+
CONSTRAINT "scheduled_task_personal_resource_authorities_pk" PRIMARY KEY (
|
|
179
|
+
"task_id", "task_authority_revision"
|
|
180
|
+
),
|
|
181
|
+
CONSTRAINT "scheduled_task_personal_resource_authorities_owner_fk"
|
|
182
|
+
FOREIGN KEY ("owner_organization_membership_id", "account_id")
|
|
183
|
+
REFERENCES "organization_memberships"("id", "account_id") ON DELETE RESTRICT,
|
|
184
|
+
CONSTRAINT "scheduled_task_personal_resource_authorities_revision_chk" CHECK (
|
|
185
|
+
"task_authority_revision" > 0
|
|
186
|
+
AND "membership_authorization_revision" > 0
|
|
187
|
+
AND "resource_count" > 0
|
|
188
|
+
),
|
|
189
|
+
CONSTRAINT "scheduled_task_personal_resource_authorities_visibility_chk" CHECK (
|
|
190
|
+
"session_visibility" IN ('user_private', 'workspace_shared')
|
|
191
|
+
),
|
|
192
|
+
CONSTRAINT "scheduled_task_personal_resource_authorities_session_chk" CHECK (
|
|
193
|
+
("target_session_id" IS NULL AND "session_authority_epoch" IS NULL)
|
|
194
|
+
OR ("target_session_id" IS NOT NULL AND "session_authority_epoch" > 0)
|
|
195
|
+
),
|
|
196
|
+
CONSTRAINT "scheduled_task_personal_resource_authorities_subject_chk" CHECK (
|
|
197
|
+
length(btrim("initiating_human_subject_id")) BETWEEN 1 AND 1024
|
|
198
|
+
),
|
|
199
|
+
CONSTRAINT "scheduled_task_personal_resource_authorities_digest_chk" CHECK (
|
|
200
|
+
"execution_digest" ~ '^[0-9a-f]{64}$'
|
|
201
|
+
)
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
CREATE TABLE "scheduled_task_personal_resource_snapshots" (
|
|
205
|
+
"task_id" uuid NOT NULL,
|
|
206
|
+
"task_authority_revision" bigint NOT NULL,
|
|
207
|
+
"account_id" uuid NOT NULL,
|
|
208
|
+
"workspace_id" uuid NOT NULL,
|
|
209
|
+
"resource_kind" text NOT NULL,
|
|
210
|
+
"resource_id" uuid NOT NULL,
|
|
211
|
+
"resource_version_id" uuid,
|
|
212
|
+
"selection_sources" text[] NOT NULL,
|
|
213
|
+
"action" text NOT NULL,
|
|
214
|
+
"origin_workspace_id" uuid NOT NULL,
|
|
215
|
+
"owner_organization_membership_id" uuid NOT NULL,
|
|
216
|
+
"membership_authorization_revision" bigint NOT NULL,
|
|
217
|
+
"authority_id" uuid NOT NULL,
|
|
218
|
+
"authority_generation" bigint NOT NULL,
|
|
219
|
+
"target_workspace_id" uuid NOT NULL,
|
|
220
|
+
"session_visibility" text NOT NULL,
|
|
221
|
+
"session_authority_epoch" integer,
|
|
222
|
+
"grant_id" uuid NOT NULL,
|
|
223
|
+
"grant_generation" bigint NOT NULL,
|
|
224
|
+
"grant_mode" text NOT NULL,
|
|
225
|
+
"grant_context" text NOT NULL,
|
|
226
|
+
"grant_session_id" uuid,
|
|
227
|
+
"grant_authority_epoch" integer,
|
|
228
|
+
"created_at" timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
229
|
+
CONSTRAINT "scheduled_task_personal_resource_snapshots_pk" PRIMARY KEY (
|
|
230
|
+
"task_id", "task_authority_revision", "resource_kind", "resource_id"
|
|
231
|
+
),
|
|
232
|
+
CONSTRAINT "scheduled_task_personal_resource_snapshots_authority_header_fk"
|
|
233
|
+
FOREIGN KEY ("task_id", "task_authority_revision")
|
|
234
|
+
REFERENCES "scheduled_task_personal_resource_authorities"(
|
|
235
|
+
"task_id", "task_authority_revision"
|
|
236
|
+
) ON DELETE CASCADE,
|
|
237
|
+
CONSTRAINT "scheduled_task_personal_resource_snapshots_authority_fk"
|
|
238
|
+
FOREIGN KEY ("authority_id", "account_id", "owner_organization_membership_id")
|
|
239
|
+
REFERENCES "organization_user_resource_authorities"(
|
|
240
|
+
"id", "account_id", "organization_membership_id"
|
|
241
|
+
) ON DELETE RESTRICT,
|
|
242
|
+
CONSTRAINT "scheduled_task_personal_resource_snapshots_grant_fk"
|
|
243
|
+
FOREIGN KEY ("grant_id", "account_id")
|
|
244
|
+
REFERENCES "organization_user_resource_grants"("id", "account_id") ON DELETE RESTRICT,
|
|
245
|
+
CONSTRAINT "scheduled_task_personal_resource_snapshots_kind_chk" CHECK (
|
|
246
|
+
("resource_kind" = 'variable_set' AND "resource_version_id" IS NULL)
|
|
247
|
+
OR ("resource_kind" = 'rig' AND "resource_version_id" IS NOT NULL)
|
|
248
|
+
),
|
|
249
|
+
CONSTRAINT "scheduled_task_personal_resource_snapshots_action_chk" CHECK (
|
|
250
|
+
("resource_kind" = 'variable_set' AND "action" = 'variable_set.use')
|
|
251
|
+
OR ("resource_kind" = 'rig' AND "action" = 'rig.use')
|
|
252
|
+
),
|
|
253
|
+
CONSTRAINT "scheduled_task_personal_resource_snapshots_generation_chk" CHECK (
|
|
254
|
+
"task_authority_revision" > 0
|
|
255
|
+
AND "membership_authorization_revision" > 0
|
|
256
|
+
AND "authority_generation" > 0
|
|
257
|
+
AND "grant_generation" > 0
|
|
258
|
+
),
|
|
259
|
+
CONSTRAINT "scheduled_task_personal_resource_snapshots_sources_chk" CHECK (
|
|
260
|
+
cardinality("selection_sources") > 0
|
|
261
|
+
),
|
|
262
|
+
CONSTRAINT "scheduled_task_personal_resource_snapshots_grant_fence_chk" CHECK (
|
|
263
|
+
(
|
|
264
|
+
"grant_mode" = 'always'
|
|
265
|
+
AND "grant_session_id" IS NULL
|
|
266
|
+
AND "grant_authority_epoch" IS NULL
|
|
267
|
+
AND "session_authority_epoch" IS NULL
|
|
268
|
+
) OR (
|
|
269
|
+
"grant_mode" IN ('once', 'session')
|
|
270
|
+
AND "grant_session_id" IS NOT NULL
|
|
271
|
+
AND "grant_authority_epoch" = "session_authority_epoch"
|
|
272
|
+
)
|
|
273
|
+
)
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
CREATE TABLE "scheduled_task_run_personal_resource_admissions" (
|
|
277
|
+
"run_id" uuid PRIMARY KEY REFERENCES "scheduled_task_runs"("id") ON DELETE CASCADE,
|
|
278
|
+
"task_id" uuid NOT NULL,
|
|
279
|
+
"task_authority_revision" bigint NOT NULL,
|
|
280
|
+
"account_id" uuid NOT NULL,
|
|
281
|
+
"workspace_id" uuid NOT NULL,
|
|
282
|
+
"initiating_human_subject_id" text NOT NULL,
|
|
283
|
+
"owner_organization_membership_id" uuid NOT NULL,
|
|
284
|
+
"membership_authorization_revision" bigint NOT NULL,
|
|
285
|
+
"target_session_id" uuid,
|
|
286
|
+
"session_visibility" text NOT NULL,
|
|
287
|
+
"session_authority_epoch" integer,
|
|
288
|
+
"execution_digest" text NOT NULL,
|
|
289
|
+
"resource_count" integer NOT NULL,
|
|
290
|
+
"admitted_at" timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
291
|
+
CONSTRAINT "scheduled_task_run_personal_resource_admissions_task_fk"
|
|
292
|
+
FOREIGN KEY ("task_id", "task_authority_revision")
|
|
293
|
+
REFERENCES "scheduled_task_personal_resource_authorities"(
|
|
294
|
+
"task_id", "task_authority_revision"
|
|
295
|
+
) ON DELETE RESTRICT,
|
|
296
|
+
CONSTRAINT "scheduled_task_run_personal_resource_admissions_owner_fk"
|
|
297
|
+
FOREIGN KEY ("owner_organization_membership_id", "account_id")
|
|
298
|
+
REFERENCES "organization_memberships"("id", "account_id") ON DELETE RESTRICT,
|
|
299
|
+
CONSTRAINT "scheduled_task_run_personal_resource_admissions_revision_chk" CHECK (
|
|
300
|
+
"task_authority_revision" > 0
|
|
301
|
+
AND "membership_authorization_revision" > 0
|
|
302
|
+
AND "resource_count" > 0
|
|
303
|
+
),
|
|
304
|
+
CONSTRAINT "scheduled_task_run_personal_resource_admissions_digest_chk" CHECK (
|
|
305
|
+
"execution_digest" ~ '^[0-9a-f]{64}$'
|
|
306
|
+
)
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
CREATE TABLE "scheduled_task_run_personal_resource_snapshots" (
|
|
310
|
+
"run_id" uuid NOT NULL,
|
|
311
|
+
"task_id" uuid NOT NULL,
|
|
312
|
+
"task_authority_revision" bigint NOT NULL,
|
|
313
|
+
"account_id" uuid NOT NULL,
|
|
314
|
+
"workspace_id" uuid NOT NULL,
|
|
315
|
+
"resource_kind" text NOT NULL,
|
|
316
|
+
"resource_id" uuid NOT NULL,
|
|
317
|
+
"resource_version_id" uuid,
|
|
318
|
+
"selection_sources" text[] NOT NULL,
|
|
319
|
+
"action" text NOT NULL,
|
|
320
|
+
"origin_workspace_id" uuid NOT NULL,
|
|
321
|
+
"owner_organization_membership_id" uuid NOT NULL,
|
|
322
|
+
"membership_authorization_revision" bigint NOT NULL,
|
|
323
|
+
"authority_id" uuid NOT NULL,
|
|
324
|
+
"authority_generation" bigint NOT NULL,
|
|
325
|
+
"target_workspace_id" uuid NOT NULL,
|
|
326
|
+
"session_visibility" text NOT NULL,
|
|
327
|
+
"session_authority_epoch" integer,
|
|
328
|
+
"grant_id" uuid NOT NULL,
|
|
329
|
+
"grant_generation" bigint NOT NULL,
|
|
330
|
+
"grant_mode" text NOT NULL,
|
|
331
|
+
"grant_context" text NOT NULL,
|
|
332
|
+
"grant_session_id" uuid,
|
|
333
|
+
"grant_authority_epoch" integer,
|
|
334
|
+
"created_at" timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
335
|
+
CONSTRAINT "scheduled_task_run_personal_resource_snapshots_pk" PRIMARY KEY (
|
|
336
|
+
"run_id", "resource_kind", "resource_id"
|
|
337
|
+
),
|
|
338
|
+
CONSTRAINT "scheduled_task_run_personal_resource_snapshots_admission_fk"
|
|
339
|
+
FOREIGN KEY ("run_id")
|
|
340
|
+
REFERENCES "scheduled_task_run_personal_resource_admissions"("run_id") ON DELETE CASCADE,
|
|
341
|
+
CONSTRAINT "scheduled_task_run_personal_resource_snapshots_task_fk"
|
|
342
|
+
FOREIGN KEY (
|
|
343
|
+
"task_id", "task_authority_revision", "resource_kind", "resource_id"
|
|
344
|
+
) REFERENCES "scheduled_task_personal_resource_snapshots"(
|
|
345
|
+
"task_id", "task_authority_revision", "resource_kind", "resource_id"
|
|
346
|
+
) ON DELETE RESTRICT
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
CREATE TABLE "scheduled_task_run_personal_resource_once_receipts" (
|
|
350
|
+
"grant_id" uuid PRIMARY KEY,
|
|
351
|
+
"run_id" uuid NOT NULL,
|
|
352
|
+
"account_id" uuid NOT NULL,
|
|
353
|
+
"workspace_id" uuid NOT NULL,
|
|
354
|
+
"authority_id" uuid NOT NULL,
|
|
355
|
+
"authority_generation" bigint NOT NULL,
|
|
356
|
+
"grant_generation" bigint NOT NULL,
|
|
357
|
+
"consumed_at" timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
358
|
+
CONSTRAINT "scheduled_task_run_personal_resource_once_receipts_run_fk"
|
|
359
|
+
FOREIGN KEY ("run_id")
|
|
360
|
+
REFERENCES "scheduled_task_run_personal_resource_admissions"("run_id") ON DELETE CASCADE,
|
|
361
|
+
CONSTRAINT "scheduled_task_run_personal_resource_once_receipts_grant_fk"
|
|
362
|
+
FOREIGN KEY ("grant_id", "account_id")
|
|
363
|
+
REFERENCES "organization_user_resource_grants"("id", "account_id") ON DELETE RESTRICT,
|
|
364
|
+
CONSTRAINT "scheduled_run_once_receipts_generation_chk" CHECK (
|
|
365
|
+
"authority_generation" > 0 AND "grant_generation" > 0
|
|
366
|
+
)
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
-- Existing tasks were accepted by writers that could not create this migration's
|
|
370
|
+
-- authority ledger. Pause every such personal-resource task deterministically;
|
|
371
|
+
-- a current writer's explicit resume creates a new, fully frozen revision.
|
|
372
|
+
UPDATE "scheduled_tasks" task
|
|
373
|
+
SET "status" = 'paused', "updated_at" = clock_timestamp()
|
|
374
|
+
WHERE task."status" = 'active'
|
|
375
|
+
AND NOT EXISTS (
|
|
376
|
+
SELECT 1
|
|
377
|
+
FROM "scheduled_task_personal_resource_authorities" authority
|
|
378
|
+
WHERE authority."task_id" = task."id"
|
|
379
|
+
AND authority."task_authority_revision" = task."authority_revision"
|
|
380
|
+
)
|
|
381
|
+
AND (
|
|
382
|
+
EXISTS (
|
|
383
|
+
SELECT 1
|
|
384
|
+
FROM "workspace_variable_sets" variable_set
|
|
385
|
+
WHERE variable_set."id" = task."variable_set_id"
|
|
386
|
+
AND variable_set."account_id" = task."account_id"
|
|
387
|
+
AND variable_set."authority_scope" = 'user'
|
|
388
|
+
)
|
|
389
|
+
OR EXISTS (
|
|
390
|
+
SELECT 1
|
|
391
|
+
FROM "rigs" rig
|
|
392
|
+
WHERE rig."id" = task."rig_id"
|
|
393
|
+
AND rig."account_id" = task."account_id"
|
|
394
|
+
AND rig."authority_scope" = 'user'
|
|
395
|
+
)
|
|
396
|
+
OR EXISTS (
|
|
397
|
+
SELECT 1
|
|
398
|
+
FROM "rigs" rig
|
|
399
|
+
JOIN "rig_versions" rig_version
|
|
400
|
+
ON rig_version."rig_id" = rig."id"
|
|
401
|
+
AND rig_version."account_id" = rig."account_id"
|
|
402
|
+
AND rig_version."active"
|
|
403
|
+
CROSS JOIN LATERAL jsonb_array_elements_text(
|
|
404
|
+
rig_version."default_variable_set_ids"
|
|
405
|
+
) default_id(value)
|
|
406
|
+
JOIN "workspace_variable_sets" default_variable_set
|
|
407
|
+
ON default_variable_set."id" = default_id.value::uuid
|
|
408
|
+
AND default_variable_set."account_id" = task."account_id"
|
|
409
|
+
AND default_variable_set."authority_scope" = 'user'
|
|
410
|
+
WHERE rig."id" = task."rig_id"
|
|
411
|
+
AND rig."account_id" = task."account_id"
|
|
412
|
+
)
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
CREATE INDEX "scheduled_task_personal_resource_snapshots_authority_idx"
|
|
416
|
+
ON "scheduled_task_personal_resource_snapshots"(
|
|
417
|
+
"account_id", "authority_id", "authority_generation"
|
|
418
|
+
);
|
|
419
|
+
CREATE INDEX "scheduled_task_personal_resource_snapshots_grant_idx"
|
|
420
|
+
ON "scheduled_task_personal_resource_snapshots"(
|
|
421
|
+
"account_id", "grant_id", "grant_generation"
|
|
422
|
+
);
|
|
423
|
+
CREATE INDEX "scheduled_task_run_personal_resource_snapshots_authority_idx"
|
|
424
|
+
ON "scheduled_task_run_personal_resource_snapshots"(
|
|
425
|
+
"account_id", "authority_id", "authority_generation"
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
ALTER TABLE "scheduled_task_personal_resource_authorities" ENABLE ROW LEVEL SECURITY;
|
|
429
|
+
ALTER TABLE "scheduled_task_personal_resource_authorities" FORCE ROW LEVEL SECURITY;
|
|
430
|
+
ALTER TABLE "scheduled_task_personal_resource_snapshots" ENABLE ROW LEVEL SECURITY;
|
|
431
|
+
ALTER TABLE "scheduled_task_personal_resource_snapshots" FORCE ROW LEVEL SECURITY;
|
|
432
|
+
ALTER TABLE "scheduled_task_run_personal_resource_admissions" ENABLE ROW LEVEL SECURITY;
|
|
433
|
+
ALTER TABLE "scheduled_task_run_personal_resource_admissions" FORCE ROW LEVEL SECURITY;
|
|
434
|
+
ALTER TABLE "scheduled_task_run_personal_resource_snapshots" ENABLE ROW LEVEL SECURITY;
|
|
435
|
+
ALTER TABLE "scheduled_task_run_personal_resource_snapshots" FORCE ROW LEVEL SECURITY;
|
|
436
|
+
ALTER TABLE "scheduled_task_run_personal_resource_once_receipts" ENABLE ROW LEVEL SECURITY;
|
|
437
|
+
ALTER TABLE "scheduled_task_run_personal_resource_once_receipts" FORCE ROW LEVEL SECURITY;
|
|
438
|
+
|
|
439
|
+
DO $scheduled_personal_resource_policies$
|
|
440
|
+
DECLARE
|
|
441
|
+
data_schema text := current_schema();
|
|
442
|
+
migration_owner text := current_user;
|
|
443
|
+
table_name text;
|
|
444
|
+
BEGIN
|
|
445
|
+
FOREACH table_name IN ARRAY ARRAY[
|
|
446
|
+
'scheduled_task_personal_resource_authorities',
|
|
447
|
+
'scheduled_task_personal_resource_snapshots',
|
|
448
|
+
'scheduled_task_run_personal_resource_admissions',
|
|
449
|
+
'scheduled_task_run_personal_resource_snapshots',
|
|
450
|
+
'scheduled_task_run_personal_resource_once_receipts'
|
|
451
|
+
] LOOP
|
|
452
|
+
EXECUTE format(
|
|
453
|
+
'CREATE POLICY scheduled_personal_resource_capability_read ON %I.%I '
|
|
454
|
+
|| 'FOR SELECT USING (current_user = %L AND '
|
|
455
|
+
|| 'opengeni_private.scheduled_personal_resource_capability_active())',
|
|
456
|
+
data_schema, table_name, migration_owner
|
|
457
|
+
);
|
|
458
|
+
EXECUTE format(
|
|
459
|
+
'CREATE POLICY scheduled_personal_resource_capability_insert ON %I.%I '
|
|
460
|
+
|| 'FOR INSERT WITH CHECK (current_user = %L AND '
|
|
461
|
+
|| 'opengeni_private.scheduled_personal_resource_capability_active())',
|
|
462
|
+
data_schema, table_name, migration_owner
|
|
463
|
+
);
|
|
464
|
+
END LOOP;
|
|
465
|
+
|
|
466
|
+
FOREACH table_name IN ARRAY ARRAY[
|
|
467
|
+
'scheduled_task_run_personal_resource_admissions',
|
|
468
|
+
'scheduled_task_run_personal_resource_snapshots'
|
|
469
|
+
] LOOP
|
|
470
|
+
EXECUTE format(
|
|
471
|
+
'CREATE POLICY scheduled_personal_resource_run_rebind_update ON %I.%I '
|
|
472
|
+
|| 'FOR UPDATE USING (current_user = %L AND '
|
|
473
|
+
|| 'opengeni_private.scheduled_personal_resource_capability_active(''run_admit'')) '
|
|
474
|
+
|| 'WITH CHECK (current_user = %L AND '
|
|
475
|
+
|| 'opengeni_private.scheduled_personal_resource_capability_active(''run_admit''))',
|
|
476
|
+
data_schema, table_name, migration_owner, migration_owner
|
|
477
|
+
);
|
|
478
|
+
END LOOP;
|
|
479
|
+
|
|
480
|
+
FOREACH table_name IN ARRAY ARRAY[
|
|
481
|
+
'organization_memberships',
|
|
482
|
+
'organization_user_resource_authorities',
|
|
483
|
+
'organization_user_resource_grants',
|
|
484
|
+
'workspace_variable_sets',
|
|
485
|
+
'rigs',
|
|
486
|
+
'rig_versions'
|
|
487
|
+
] LOOP
|
|
488
|
+
EXECUTE format(
|
|
489
|
+
'CREATE POLICY scheduled_personal_resource_read ON %I.%I '
|
|
490
|
+
|| 'FOR SELECT USING (current_user = %L AND '
|
|
491
|
+
|| 'opengeni_private.scheduled_personal_resource_capability_active())',
|
|
492
|
+
data_schema, table_name, migration_owner
|
|
493
|
+
);
|
|
494
|
+
END LOOP;
|
|
495
|
+
|
|
496
|
+
EXECUTE format(
|
|
497
|
+
'CREATE POLICY scheduled_personal_resource_grant_update '
|
|
498
|
+
|| 'ON %I.organization_user_resource_grants FOR UPDATE USING ('
|
|
499
|
+
|| 'current_user = %L AND '
|
|
500
|
+
|| 'opengeni_private.scheduled_personal_resource_capability_active()) '
|
|
501
|
+
|| 'WITH CHECK (current_user = %L AND '
|
|
502
|
+
|| 'opengeni_private.scheduled_personal_resource_capability_active())',
|
|
503
|
+
data_schema, migration_owner, migration_owner
|
|
504
|
+
);
|
|
505
|
+
EXECUTE format(
|
|
506
|
+
'CREATE POLICY scheduled_personal_resource_attempt_receipt_read '
|
|
507
|
+
|| 'ON %I.personal_resource_once_consumption_receipts FOR SELECT USING ('
|
|
508
|
+
|| 'current_user = %L AND '
|
|
509
|
+
|| 'opengeni_private.scheduled_personal_resource_capability_active(''attempt_match''))',
|
|
510
|
+
data_schema, migration_owner
|
|
511
|
+
);
|
|
512
|
+
EXECUTE format(
|
|
513
|
+
'CREATE POLICY scheduled_personal_resource_attempt_receipt_delete '
|
|
514
|
+
|| 'ON %I.personal_resource_once_consumption_receipts FOR DELETE USING ('
|
|
515
|
+
|| 'current_user = %L AND '
|
|
516
|
+
|| 'opengeni_private.scheduled_personal_resource_capability_active(''attempt_match''))',
|
|
517
|
+
data_schema, migration_owner
|
|
518
|
+
);
|
|
519
|
+
END
|
|
520
|
+
$scheduled_personal_resource_policies$;
|
|
521
|
+
|
|
522
|
+
CREATE OR REPLACE FUNCTION freeze_scheduled_task_personal_resources(
|
|
523
|
+
p_account_id uuid,
|
|
524
|
+
p_workspace_id uuid,
|
|
525
|
+
p_task_id uuid,
|
|
526
|
+
p_task_authority_revision bigint
|
|
527
|
+
)
|
|
528
|
+
RETURNS integer
|
|
529
|
+
LANGUAGE plpgsql
|
|
530
|
+
SECURITY DEFINER
|
|
531
|
+
SET search_path = pg_catalog, pg_temp
|
|
532
|
+
AS $freeze_scheduled_task_personal_resources$
|
|
533
|
+
DECLARE
|
|
534
|
+
task_row record;
|
|
535
|
+
member_row record;
|
|
536
|
+
session_row record;
|
|
537
|
+
resource_row record;
|
|
538
|
+
grant_row record;
|
|
539
|
+
initiating_subject text := coalesce(
|
|
540
|
+
nullif(btrim(current_setting('opengeni.initiating_human_subject_id', true)), ''),
|
|
541
|
+
nullif(btrim(current_setting('opengeni.subject_id', true)), '')
|
|
542
|
+
);
|
|
543
|
+
target_session uuid;
|
|
544
|
+
target_visibility text := 'workspace_shared';
|
|
545
|
+
target_epoch integer;
|
|
546
|
+
resource_total integer := 0;
|
|
547
|
+
BEGIN
|
|
548
|
+
IF p_account_id IS DISTINCT FROM nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
549
|
+
OR p_workspace_id IS DISTINCT FROM nullif(
|
|
550
|
+
current_setting('opengeni.workspace_id', true), ''
|
|
551
|
+
)::uuid
|
|
552
|
+
THEN
|
|
553
|
+
RAISE EXCEPTION 'scheduled personal-resource task scope mismatch' USING ERRCODE = '42501';
|
|
554
|
+
END IF;
|
|
555
|
+
|
|
556
|
+
INSERT INTO opengeni_private.scheduled_personal_resource_capabilities (
|
|
557
|
+
backend_pid, transaction_id, capability_kind
|
|
558
|
+
) VALUES (pg_backend_pid(), pg_current_xact_id(), 'task_write')
|
|
559
|
+
ON CONFLICT DO NOTHING;
|
|
560
|
+
|
|
561
|
+
SELECT task.* INTO STRICT task_row
|
|
562
|
+
FROM scheduled_tasks task
|
|
563
|
+
WHERE task.id = p_task_id
|
|
564
|
+
AND task.account_id = p_account_id
|
|
565
|
+
AND task.workspace_id = p_workspace_id
|
|
566
|
+
AND task.authority_revision = p_task_authority_revision
|
|
567
|
+
FOR UPDATE;
|
|
568
|
+
|
|
569
|
+
SELECT count(*)::integer INTO resource_total
|
|
570
|
+
FROM (
|
|
571
|
+
SELECT variable_set.id
|
|
572
|
+
FROM workspace_variable_sets variable_set
|
|
573
|
+
WHERE variable_set.id = task_row.variable_set_id
|
|
574
|
+
AND variable_set.account_id = p_account_id
|
|
575
|
+
AND variable_set.authority_scope = 'user'
|
|
576
|
+
UNION
|
|
577
|
+
SELECT rig.id
|
|
578
|
+
FROM rigs rig
|
|
579
|
+
WHERE rig.id = task_row.rig_id
|
|
580
|
+
AND rig.account_id = p_account_id
|
|
581
|
+
AND rig.authority_scope = 'user'
|
|
582
|
+
UNION
|
|
583
|
+
SELECT default_variable_set.id
|
|
584
|
+
FROM rigs rig
|
|
585
|
+
JOIN rig_versions rig_version
|
|
586
|
+
ON rig_version.rig_id = rig.id
|
|
587
|
+
AND rig_version.account_id = rig.account_id
|
|
588
|
+
AND rig_version.active
|
|
589
|
+
CROSS JOIN LATERAL jsonb_array_elements_text(
|
|
590
|
+
rig_version.default_variable_set_ids
|
|
591
|
+
) default_id(value)
|
|
592
|
+
JOIN workspace_variable_sets default_variable_set
|
|
593
|
+
ON default_variable_set.id = default_id.value::uuid
|
|
594
|
+
AND default_variable_set.account_id = p_account_id
|
|
595
|
+
AND default_variable_set.authority_scope = 'user'
|
|
596
|
+
WHERE rig.id = task_row.rig_id
|
|
597
|
+
AND rig.account_id = p_account_id
|
|
598
|
+
) selected;
|
|
599
|
+
|
|
600
|
+
IF resource_total = 0 THEN
|
|
601
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
602
|
+
WHERE backend_pid = pg_backend_pid()
|
|
603
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
604
|
+
AND capability_kind = 'task_write';
|
|
605
|
+
RETURN 0;
|
|
606
|
+
END IF;
|
|
607
|
+
|
|
608
|
+
IF initiating_subject IS NULL THEN
|
|
609
|
+
RAISE EXCEPTION 'scheduled personal resources require a causal human'
|
|
610
|
+
USING ERRCODE = '42501';
|
|
611
|
+
END IF;
|
|
612
|
+
|
|
613
|
+
SELECT membership.* INTO STRICT member_row
|
|
614
|
+
FROM organization_memberships membership
|
|
615
|
+
WHERE membership.account_id = p_account_id
|
|
616
|
+
AND membership.subject_id = initiating_subject
|
|
617
|
+
AND membership.status = 'active'
|
|
618
|
+
AND membership.revoked_at IS NULL
|
|
619
|
+
AND membership.personal_workspace_id IS NOT NULL
|
|
620
|
+
FOR SHARE;
|
|
621
|
+
|
|
622
|
+
IF member_row.personal_workspace_id IS DISTINCT FROM p_workspace_id THEN
|
|
623
|
+
PERFORM 1 FROM workspace_memberships workspace_membership
|
|
624
|
+
WHERE workspace_membership.account_id = p_account_id
|
|
625
|
+
AND workspace_membership.workspace_id = p_workspace_id
|
|
626
|
+
AND workspace_membership.subject_id = initiating_subject
|
|
627
|
+
FOR KEY SHARE;
|
|
628
|
+
IF NOT FOUND THEN
|
|
629
|
+
RAISE EXCEPTION 'scheduled personal-resource owner lacks workspace access'
|
|
630
|
+
USING ERRCODE = '42501';
|
|
631
|
+
END IF;
|
|
632
|
+
END IF;
|
|
633
|
+
|
|
634
|
+
IF task_row.run_mode = 'existing_session'
|
|
635
|
+
OR (task_row.run_mode = 'reusable_session' AND task_row.reusable_session_id IS NOT NULL)
|
|
636
|
+
THEN
|
|
637
|
+
target_session := task_row.reusable_session_id;
|
|
638
|
+
SELECT session_value.* INTO STRICT session_row
|
|
639
|
+
FROM sessions session_value
|
|
640
|
+
WHERE session_value.id = target_session
|
|
641
|
+
AND session_value.account_id = p_account_id
|
|
642
|
+
AND session_value.workspace_id = p_workspace_id
|
|
643
|
+
AND session_value.status <> 'cancelled'
|
|
644
|
+
FOR SHARE;
|
|
645
|
+
target_visibility := session_row.visibility;
|
|
646
|
+
target_epoch := session_row.authority_epoch;
|
|
647
|
+
END IF;
|
|
648
|
+
|
|
649
|
+
INSERT INTO scheduled_task_personal_resource_authorities (
|
|
650
|
+
task_id, task_authority_revision, account_id, workspace_id,
|
|
651
|
+
initiating_human_subject_id, owner_organization_membership_id,
|
|
652
|
+
membership_authorization_revision, target_session_id, session_visibility,
|
|
653
|
+
session_authority_epoch, execution_digest, resource_count
|
|
654
|
+
) VALUES (
|
|
655
|
+
p_task_id, p_task_authority_revision, p_account_id, p_workspace_id,
|
|
656
|
+
initiating_subject, member_row.id, member_row.authorization_revision,
|
|
657
|
+
target_session, target_visibility, target_epoch, task_row.execution_digest,
|
|
658
|
+
resource_total
|
|
659
|
+
);
|
|
660
|
+
|
|
661
|
+
FOR resource_row IN
|
|
662
|
+
WITH selected AS (
|
|
663
|
+
SELECT 'variable_set'::text AS resource_kind, variable_set.id AS resource_id,
|
|
664
|
+
NULL::uuid AS resource_version_id, 'variable_set.use'::text AS action,
|
|
665
|
+
'session_variable_set'::text AS selection_source,
|
|
666
|
+
variable_set.workspace_id AS resource_workspace_id, variable_set.authority_id,
|
|
667
|
+
variable_set.owner_organization_membership_id, variable_set.origin_workspace_id
|
|
668
|
+
FROM workspace_variable_sets variable_set
|
|
669
|
+
WHERE variable_set.id = task_row.variable_set_id
|
|
670
|
+
AND variable_set.account_id = p_account_id
|
|
671
|
+
AND variable_set.authority_scope = 'user'
|
|
672
|
+
UNION ALL
|
|
673
|
+
SELECT 'rig'::text, rig.id, rig_version.id, 'rig.use'::text,
|
|
674
|
+
'session_rig'::text, rig.workspace_id, rig.authority_id,
|
|
675
|
+
rig.owner_organization_membership_id, rig.origin_workspace_id
|
|
676
|
+
FROM rigs rig
|
|
677
|
+
JOIN rig_versions rig_version
|
|
678
|
+
ON rig_version.rig_id = rig.id
|
|
679
|
+
AND rig_version.account_id = rig.account_id
|
|
680
|
+
AND rig_version.active
|
|
681
|
+
WHERE rig.id = task_row.rig_id
|
|
682
|
+
AND rig.account_id = p_account_id
|
|
683
|
+
AND rig.authority_scope = 'user'
|
|
684
|
+
UNION ALL
|
|
685
|
+
SELECT 'variable_set'::text, default_variable_set.id, NULL::uuid,
|
|
686
|
+
'variable_set.use'::text,
|
|
687
|
+
('rig_default_variable_set:' || default_id.ordinality::text)::text,
|
|
688
|
+
default_variable_set.workspace_id, default_variable_set.authority_id,
|
|
689
|
+
default_variable_set.owner_organization_membership_id,
|
|
690
|
+
default_variable_set.origin_workspace_id
|
|
691
|
+
FROM rigs rig
|
|
692
|
+
JOIN rig_versions rig_version
|
|
693
|
+
ON rig_version.rig_id = rig.id
|
|
694
|
+
AND rig_version.account_id = rig.account_id
|
|
695
|
+
AND rig_version.active
|
|
696
|
+
CROSS JOIN LATERAL jsonb_array_elements_text(
|
|
697
|
+
rig_version.default_variable_set_ids
|
|
698
|
+
) WITH ORDINALITY default_id(value, ordinality)
|
|
699
|
+
JOIN workspace_variable_sets default_variable_set
|
|
700
|
+
ON default_variable_set.id = default_id.value::uuid
|
|
701
|
+
AND default_variable_set.account_id = p_account_id
|
|
702
|
+
AND default_variable_set.authority_scope = 'user'
|
|
703
|
+
WHERE rig.id = task_row.rig_id
|
|
704
|
+
AND rig.account_id = p_account_id
|
|
705
|
+
)
|
|
706
|
+
SELECT resource_kind, resource_id, min(resource_version_id::text)::uuid resource_version_id,
|
|
707
|
+
action, array_agg(selection_source ORDER BY selection_source) selection_sources,
|
|
708
|
+
min(resource_workspace_id::text)::uuid resource_workspace_id,
|
|
709
|
+
min(authority_id::text)::uuid authority_id,
|
|
710
|
+
min(owner_organization_membership_id::text)::uuid owner_organization_membership_id,
|
|
711
|
+
min(origin_workspace_id::text)::uuid origin_workspace_id
|
|
712
|
+
FROM selected
|
|
713
|
+
GROUP BY resource_kind, resource_id, action
|
|
714
|
+
ORDER BY resource_kind, resource_id
|
|
715
|
+
LOOP
|
|
716
|
+
IF resource_row.owner_organization_membership_id IS DISTINCT FROM member_row.id
|
|
717
|
+
OR resource_row.resource_workspace_id IS DISTINCT FROM member_row.personal_workspace_id
|
|
718
|
+
OR resource_row.origin_workspace_id IS DISTINCT FROM member_row.personal_workspace_id
|
|
719
|
+
THEN
|
|
720
|
+
RAISE EXCEPTION 'scheduled personal resource belongs to another human or organization'
|
|
721
|
+
USING ERRCODE = '42501';
|
|
722
|
+
END IF;
|
|
723
|
+
|
|
724
|
+
SELECT grant_value.* INTO grant_row
|
|
725
|
+
FROM organization_user_resource_grants grant_value
|
|
726
|
+
JOIN organization_user_resource_authorities authority
|
|
727
|
+
ON authority.id = grant_value.authority_id
|
|
728
|
+
AND authority.account_id = grant_value.account_id
|
|
729
|
+
WHERE authority.id = resource_row.authority_id
|
|
730
|
+
AND authority.account_id = p_account_id
|
|
731
|
+
AND authority.organization_membership_id = member_row.id
|
|
732
|
+
AND authority.resource_kind = resource_row.resource_kind
|
|
733
|
+
AND authority.resource_id = resource_row.resource_id
|
|
734
|
+
AND authority.origin_workspace_id = member_row.personal_workspace_id
|
|
735
|
+
AND authority.status = 'active'
|
|
736
|
+
AND authority.revoked_at IS NULL
|
|
737
|
+
AND grant_value.owner_organization_membership_id = member_row.id
|
|
738
|
+
AND grant_value.workspace_id = p_workspace_id
|
|
739
|
+
AND grant_value.action = resource_row.action
|
|
740
|
+
AND grant_value.context = target_visibility
|
|
741
|
+
AND grant_value.status = 'active'
|
|
742
|
+
AND (grant_value.expires_at IS NULL OR grant_value.expires_at > clock_timestamp())
|
|
743
|
+
AND (
|
|
744
|
+
(target_session IS NULL AND grant_value.mode = 'always'
|
|
745
|
+
AND grant_value.session_id IS NULL AND grant_value.authority_epoch IS NULL)
|
|
746
|
+
OR (target_session IS NOT NULL AND (
|
|
747
|
+
(grant_value.mode IN ('once', 'session')
|
|
748
|
+
AND grant_value.session_id = target_session
|
|
749
|
+
AND grant_value.authority_epoch = target_epoch)
|
|
750
|
+
OR (grant_value.mode = 'always'
|
|
751
|
+
AND grant_value.session_id IS NULL AND grant_value.authority_epoch IS NULL)
|
|
752
|
+
))
|
|
753
|
+
)
|
|
754
|
+
ORDER BY CASE grant_value.mode WHEN 'once' THEN 1 WHEN 'session' THEN 2 ELSE 3 END,
|
|
755
|
+
grant_value.generation DESC, grant_value.id
|
|
756
|
+
LIMIT 1
|
|
757
|
+
FOR SHARE OF authority, grant_value;
|
|
758
|
+
IF NOT FOUND THEN
|
|
759
|
+
RAISE EXCEPTION 'matching scheduled personal-resource grant required'
|
|
760
|
+
USING ERRCODE = '42501';
|
|
761
|
+
END IF;
|
|
762
|
+
|
|
763
|
+
INSERT INTO scheduled_task_personal_resource_snapshots (
|
|
764
|
+
task_id, task_authority_revision, account_id, workspace_id,
|
|
765
|
+
resource_kind, resource_id, resource_version_id, selection_sources, action,
|
|
766
|
+
origin_workspace_id, owner_organization_membership_id,
|
|
767
|
+
membership_authorization_revision, authority_id, authority_generation,
|
|
768
|
+
target_workspace_id, session_visibility, session_authority_epoch,
|
|
769
|
+
grant_id, grant_generation, grant_mode, grant_context,
|
|
770
|
+
grant_session_id, grant_authority_epoch
|
|
771
|
+
)
|
|
772
|
+
SELECT p_task_id, p_task_authority_revision, p_account_id, p_workspace_id,
|
|
773
|
+
resource_row.resource_kind, resource_row.resource_id,
|
|
774
|
+
resource_row.resource_version_id, resource_row.selection_sources, resource_row.action,
|
|
775
|
+
member_row.personal_workspace_id, member_row.id, member_row.authorization_revision,
|
|
776
|
+
authority.id, authority.generation, p_workspace_id, target_visibility, target_epoch,
|
|
777
|
+
grant_row.id, grant_row.generation, grant_row.mode, grant_row.context,
|
|
778
|
+
grant_row.session_id, grant_row.authority_epoch
|
|
779
|
+
FROM organization_user_resource_authorities authority
|
|
780
|
+
WHERE authority.id = resource_row.authority_id
|
|
781
|
+
AND authority.account_id = p_account_id;
|
|
782
|
+
END LOOP;
|
|
783
|
+
|
|
784
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
785
|
+
WHERE backend_pid = pg_backend_pid()
|
|
786
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
787
|
+
AND capability_kind = 'task_write';
|
|
788
|
+
RETURN resource_total;
|
|
789
|
+
EXCEPTION WHEN OTHERS THEN
|
|
790
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
791
|
+
WHERE backend_pid = pg_backend_pid()
|
|
792
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
793
|
+
AND capability_kind = 'task_write';
|
|
794
|
+
RAISE;
|
|
795
|
+
END
|
|
796
|
+
$freeze_scheduled_task_personal_resources$;
|
|
797
|
+
|
|
798
|
+
CREATE OR REPLACE FUNCTION clone_scheduled_task_personal_resource_authority(
|
|
799
|
+
p_account_id uuid,
|
|
800
|
+
p_workspace_id uuid,
|
|
801
|
+
p_task_id uuid,
|
|
802
|
+
p_source_task_authority_revision bigint,
|
|
803
|
+
p_target_task_authority_revision bigint
|
|
804
|
+
)
|
|
805
|
+
RETURNS integer
|
|
806
|
+
LANGUAGE plpgsql
|
|
807
|
+
SECURITY DEFINER
|
|
808
|
+
SET search_path = pg_catalog, pg_temp
|
|
809
|
+
AS $clone_scheduled_task_personal_resource_authority$
|
|
810
|
+
DECLARE
|
|
811
|
+
source_authority record;
|
|
812
|
+
target_execution_digest text;
|
|
813
|
+
copied_count integer;
|
|
814
|
+
BEGIN
|
|
815
|
+
IF p_account_id IS DISTINCT FROM nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
816
|
+
OR p_workspace_id IS DISTINCT FROM nullif(
|
|
817
|
+
current_setting('opengeni.workspace_id', true), ''
|
|
818
|
+
)::uuid
|
|
819
|
+
THEN
|
|
820
|
+
RAISE EXCEPTION 'scheduled personal-resource clone scope mismatch'
|
|
821
|
+
USING ERRCODE = '42501';
|
|
822
|
+
END IF;
|
|
823
|
+
|
|
824
|
+
IF p_source_task_authority_revision <= 0
|
|
825
|
+
OR p_target_task_authority_revision <= p_source_task_authority_revision
|
|
826
|
+
THEN
|
|
827
|
+
RAISE EXCEPTION 'scheduled personal-resource authority clone revision is invalid'
|
|
828
|
+
USING ERRCODE = '22023';
|
|
829
|
+
END IF;
|
|
830
|
+
|
|
831
|
+
INSERT INTO opengeni_private.scheduled_personal_resource_capabilities (
|
|
832
|
+
backend_pid, transaction_id, capability_kind
|
|
833
|
+
) VALUES (pg_backend_pid(), pg_current_xact_id(), 'task_write')
|
|
834
|
+
ON CONFLICT DO NOTHING;
|
|
835
|
+
|
|
836
|
+
SELECT task.execution_digest INTO target_execution_digest
|
|
837
|
+
FROM scheduled_tasks task
|
|
838
|
+
WHERE task.id = p_task_id
|
|
839
|
+
AND task.account_id = p_account_id
|
|
840
|
+
AND task.workspace_id = p_workspace_id
|
|
841
|
+
AND task.authority_revision = p_target_task_authority_revision
|
|
842
|
+
FOR UPDATE;
|
|
843
|
+
IF NOT FOUND THEN
|
|
844
|
+
RAISE EXCEPTION 'scheduled personal-resource clone target is not current'
|
|
845
|
+
USING ERRCODE = '40001';
|
|
846
|
+
END IF;
|
|
847
|
+
|
|
848
|
+
SELECT authority.* INTO source_authority
|
|
849
|
+
FROM scheduled_task_personal_resource_authorities authority
|
|
850
|
+
WHERE authority.task_id = p_task_id
|
|
851
|
+
AND authority.task_authority_revision = p_source_task_authority_revision
|
|
852
|
+
AND authority.account_id = p_account_id
|
|
853
|
+
AND authority.workspace_id = p_workspace_id;
|
|
854
|
+
IF NOT FOUND THEN
|
|
855
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
856
|
+
WHERE backend_pid = pg_backend_pid()
|
|
857
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
858
|
+
AND capability_kind = 'task_write';
|
|
859
|
+
RETURN 0;
|
|
860
|
+
END IF;
|
|
861
|
+
|
|
862
|
+
INSERT INTO scheduled_task_personal_resource_authorities (
|
|
863
|
+
task_id, task_authority_revision, account_id, workspace_id,
|
|
864
|
+
initiating_human_subject_id, owner_organization_membership_id,
|
|
865
|
+
membership_authorization_revision, target_session_id, session_visibility,
|
|
866
|
+
session_authority_epoch, execution_digest, resource_count
|
|
867
|
+
) VALUES (
|
|
868
|
+
source_authority.task_id, p_target_task_authority_revision,
|
|
869
|
+
source_authority.account_id, source_authority.workspace_id,
|
|
870
|
+
source_authority.initiating_human_subject_id,
|
|
871
|
+
source_authority.owner_organization_membership_id,
|
|
872
|
+
source_authority.membership_authorization_revision,
|
|
873
|
+
source_authority.target_session_id, source_authority.session_visibility,
|
|
874
|
+
source_authority.session_authority_epoch, target_execution_digest,
|
|
875
|
+
source_authority.resource_count
|
|
876
|
+
);
|
|
877
|
+
|
|
878
|
+
INSERT INTO scheduled_task_personal_resource_snapshots (
|
|
879
|
+
task_id, task_authority_revision, account_id, workspace_id,
|
|
880
|
+
resource_kind, resource_id, resource_version_id, selection_sources, action,
|
|
881
|
+
origin_workspace_id, owner_organization_membership_id,
|
|
882
|
+
membership_authorization_revision, authority_id, authority_generation,
|
|
883
|
+
target_workspace_id, session_visibility, session_authority_epoch,
|
|
884
|
+
grant_id, grant_generation, grant_mode, grant_context,
|
|
885
|
+
grant_session_id, grant_authority_epoch
|
|
886
|
+
)
|
|
887
|
+
SELECT snapshot.task_id, p_target_task_authority_revision,
|
|
888
|
+
snapshot.account_id, snapshot.workspace_id, snapshot.resource_kind,
|
|
889
|
+
snapshot.resource_id, snapshot.resource_version_id, snapshot.selection_sources,
|
|
890
|
+
snapshot.action, snapshot.origin_workspace_id,
|
|
891
|
+
snapshot.owner_organization_membership_id,
|
|
892
|
+
snapshot.membership_authorization_revision, snapshot.authority_id,
|
|
893
|
+
snapshot.authority_generation, snapshot.target_workspace_id,
|
|
894
|
+
snapshot.session_visibility, snapshot.session_authority_epoch,
|
|
895
|
+
snapshot.grant_id, snapshot.grant_generation, snapshot.grant_mode,
|
|
896
|
+
snapshot.grant_context, snapshot.grant_session_id, snapshot.grant_authority_epoch
|
|
897
|
+
FROM scheduled_task_personal_resource_snapshots snapshot
|
|
898
|
+
WHERE snapshot.task_id = p_task_id
|
|
899
|
+
AND snapshot.task_authority_revision = p_source_task_authority_revision;
|
|
900
|
+
GET DIAGNOSTICS copied_count = ROW_COUNT;
|
|
901
|
+
IF copied_count <> source_authority.resource_count THEN
|
|
902
|
+
RAISE EXCEPTION 'scheduled personal-resource authority clone is incomplete'
|
|
903
|
+
USING ERRCODE = '42501';
|
|
904
|
+
END IF;
|
|
905
|
+
|
|
906
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
907
|
+
WHERE backend_pid = pg_backend_pid()
|
|
908
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
909
|
+
AND capability_kind = 'task_write';
|
|
910
|
+
RETURN copied_count;
|
|
911
|
+
EXCEPTION WHEN OTHERS THEN
|
|
912
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
913
|
+
WHERE backend_pid = pg_backend_pid()
|
|
914
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
915
|
+
AND capability_kind = 'task_write';
|
|
916
|
+
RAISE;
|
|
917
|
+
END
|
|
918
|
+
$clone_scheduled_task_personal_resource_authority$;
|
|
919
|
+
|
|
920
|
+
CREATE OR REPLACE FUNCTION materialize_scheduled_task_reusable_session_from_run(
|
|
921
|
+
p_account_id uuid,
|
|
922
|
+
p_workspace_id uuid,
|
|
923
|
+
p_task_id uuid,
|
|
924
|
+
p_run_id uuid,
|
|
925
|
+
p_session_id uuid,
|
|
926
|
+
p_source_task_authority_revision bigint,
|
|
927
|
+
p_source_execution_digest text
|
|
928
|
+
)
|
|
929
|
+
RETURNS bigint
|
|
930
|
+
LANGUAGE plpgsql
|
|
931
|
+
SECURITY DEFINER
|
|
932
|
+
SET search_path = pg_catalog, pg_temp
|
|
933
|
+
AS $materialize_scheduled_task_reusable_session_from_run$
|
|
934
|
+
DECLARE
|
|
935
|
+
task_row record;
|
|
936
|
+
admission_row record;
|
|
937
|
+
target_revision bigint;
|
|
938
|
+
target_execution_digest text;
|
|
939
|
+
copied_count integer;
|
|
940
|
+
affected integer;
|
|
941
|
+
BEGIN
|
|
942
|
+
IF p_account_id IS DISTINCT FROM nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
943
|
+
OR p_workspace_id IS DISTINCT FROM nullif(
|
|
944
|
+
current_setting('opengeni.workspace_id', true), ''
|
|
945
|
+
)::uuid
|
|
946
|
+
THEN
|
|
947
|
+
RAISE EXCEPTION 'scheduled reusable-session materialization scope mismatch'
|
|
948
|
+
USING ERRCODE = '42501';
|
|
949
|
+
END IF;
|
|
950
|
+
IF p_source_task_authority_revision <= 0
|
|
951
|
+
OR p_source_execution_digest !~ '^[0-9a-f]{64}$'
|
|
952
|
+
THEN
|
|
953
|
+
RAISE EXCEPTION 'scheduled reusable-session source binding is invalid'
|
|
954
|
+
USING ERRCODE = '22023';
|
|
955
|
+
END IF;
|
|
956
|
+
|
|
957
|
+
INSERT INTO opengeni_private.scheduled_personal_resource_capabilities (
|
|
958
|
+
backend_pid, transaction_id, capability_kind
|
|
959
|
+
) VALUES (pg_backend_pid(), pg_current_xact_id(), 'run_admit')
|
|
960
|
+
ON CONFLICT DO NOTHING;
|
|
961
|
+
|
|
962
|
+
SELECT admission.* INTO admission_row
|
|
963
|
+
FROM scheduled_task_run_personal_resource_admissions admission
|
|
964
|
+
WHERE admission.run_id = p_run_id
|
|
965
|
+
AND admission.task_id = p_task_id
|
|
966
|
+
AND admission.account_id = p_account_id
|
|
967
|
+
AND admission.workspace_id = p_workspace_id
|
|
968
|
+
AND admission.task_authority_revision = p_source_task_authority_revision
|
|
969
|
+
AND admission.execution_digest = p_source_execution_digest
|
|
970
|
+
FOR UPDATE;
|
|
971
|
+
IF NOT FOUND THEN
|
|
972
|
+
RAISE EXCEPTION 'scheduled reusable-session admission binding changed'
|
|
973
|
+
USING ERRCODE = '40001';
|
|
974
|
+
END IF;
|
|
975
|
+
|
|
976
|
+
PERFORM 1
|
|
977
|
+
FROM scheduled_task_runs run
|
|
978
|
+
WHERE run.id = p_run_id
|
|
979
|
+
AND run.task_id = p_task_id
|
|
980
|
+
AND run.account_id = p_account_id
|
|
981
|
+
AND run.workspace_id = p_workspace_id
|
|
982
|
+
AND run.status = 'queued'
|
|
983
|
+
AND run.task_authority_revision = p_source_task_authority_revision
|
|
984
|
+
AND run.task_execution_digest = p_source_execution_digest
|
|
985
|
+
FOR UPDATE;
|
|
986
|
+
IF NOT FOUND THEN
|
|
987
|
+
RAISE EXCEPTION 'scheduled reusable-session run binding changed'
|
|
988
|
+
USING ERRCODE = '40001';
|
|
989
|
+
END IF;
|
|
990
|
+
|
|
991
|
+
PERFORM 1
|
|
992
|
+
FROM sessions session_value
|
|
993
|
+
WHERE session_value.id = p_session_id
|
|
994
|
+
AND session_value.account_id = p_account_id
|
|
995
|
+
AND session_value.workspace_id = p_workspace_id
|
|
996
|
+
AND session_value.status <> 'cancelled'
|
|
997
|
+
FOR SHARE;
|
|
998
|
+
IF NOT FOUND THEN
|
|
999
|
+
RAISE EXCEPTION 'scheduled reusable-session target is unavailable'
|
|
1000
|
+
USING ERRCODE = '42501';
|
|
1001
|
+
END IF;
|
|
1002
|
+
|
|
1003
|
+
SELECT task.* INTO task_row
|
|
1004
|
+
FROM scheduled_tasks task
|
|
1005
|
+
WHERE task.id = p_task_id
|
|
1006
|
+
AND task.account_id = p_account_id
|
|
1007
|
+
AND task.workspace_id = p_workspace_id
|
|
1008
|
+
FOR UPDATE;
|
|
1009
|
+
IF NOT FOUND THEN
|
|
1010
|
+
RAISE EXCEPTION 'scheduled reusable-session task is unavailable'
|
|
1011
|
+
USING ERRCODE = '40001';
|
|
1012
|
+
END IF;
|
|
1013
|
+
IF task_row.status <> 'active'
|
|
1014
|
+
OR task_row.run_mode <> 'reusable_session'
|
|
1015
|
+
OR task_row.reusable_session_id IS NOT NULL
|
|
1016
|
+
OR task_row.authority_revision IS DISTINCT FROM p_source_task_authority_revision
|
|
1017
|
+
OR task_row.execution_digest IS DISTINCT FROM p_source_execution_digest
|
|
1018
|
+
THEN
|
|
1019
|
+
RAISE EXCEPTION 'scheduled reusable-session task changed before materialization'
|
|
1020
|
+
USING ERRCODE = '40001';
|
|
1021
|
+
END IF;
|
|
1022
|
+
|
|
1023
|
+
UPDATE scheduled_tasks task
|
|
1024
|
+
SET reusable_session_id = p_session_id,
|
|
1025
|
+
authority_revision = task.authority_revision + 1,
|
|
1026
|
+
updated_at = clock_timestamp()
|
|
1027
|
+
WHERE task.id = p_task_id
|
|
1028
|
+
AND task.account_id = p_account_id
|
|
1029
|
+
AND task.workspace_id = p_workspace_id
|
|
1030
|
+
RETURNING task.authority_revision, task.execution_digest
|
|
1031
|
+
INTO STRICT target_revision, target_execution_digest;
|
|
1032
|
+
|
|
1033
|
+
SELECT clone_scheduled_task_personal_resource_authority(
|
|
1034
|
+
p_account_id,
|
|
1035
|
+
p_workspace_id,
|
|
1036
|
+
p_task_id,
|
|
1037
|
+
p_source_task_authority_revision,
|
|
1038
|
+
target_revision
|
|
1039
|
+
) INTO copied_count;
|
|
1040
|
+
IF copied_count <> admission_row.resource_count THEN
|
|
1041
|
+
RAISE EXCEPTION 'scheduled reusable-session authority clone is incomplete'
|
|
1042
|
+
USING ERRCODE = '42501';
|
|
1043
|
+
END IF;
|
|
1044
|
+
|
|
1045
|
+
UPDATE scheduled_task_run_personal_resource_snapshots snapshot
|
|
1046
|
+
SET task_authority_revision = target_revision
|
|
1047
|
+
WHERE snapshot.run_id = p_run_id
|
|
1048
|
+
AND snapshot.task_id = p_task_id
|
|
1049
|
+
AND snapshot.task_authority_revision = p_source_task_authority_revision;
|
|
1050
|
+
GET DIAGNOSTICS affected = ROW_COUNT;
|
|
1051
|
+
IF affected <> admission_row.resource_count THEN
|
|
1052
|
+
RAISE EXCEPTION 'scheduled reusable-session run snapshot rebind is incomplete'
|
|
1053
|
+
USING ERRCODE = '42501';
|
|
1054
|
+
END IF;
|
|
1055
|
+
|
|
1056
|
+
UPDATE scheduled_task_run_personal_resource_admissions admission
|
|
1057
|
+
SET task_authority_revision = target_revision,
|
|
1058
|
+
execution_digest = target_execution_digest
|
|
1059
|
+
WHERE admission.run_id = p_run_id
|
|
1060
|
+
AND admission.task_authority_revision = p_source_task_authority_revision
|
|
1061
|
+
AND admission.execution_digest = p_source_execution_digest;
|
|
1062
|
+
GET DIAGNOSTICS affected = ROW_COUNT;
|
|
1063
|
+
IF affected <> 1 THEN
|
|
1064
|
+
RAISE EXCEPTION 'scheduled reusable-session run admission rebind failed'
|
|
1065
|
+
USING ERRCODE = '42501';
|
|
1066
|
+
END IF;
|
|
1067
|
+
|
|
1068
|
+
UPDATE scheduled_task_runs run
|
|
1069
|
+
SET task_authority_revision = target_revision,
|
|
1070
|
+
task_execution_digest = target_execution_digest,
|
|
1071
|
+
updated_at = clock_timestamp()
|
|
1072
|
+
WHERE run.id = p_run_id
|
|
1073
|
+
AND run.task_authority_revision = p_source_task_authority_revision
|
|
1074
|
+
AND run.task_execution_digest = p_source_execution_digest;
|
|
1075
|
+
GET DIAGNOSTICS affected = ROW_COUNT;
|
|
1076
|
+
IF affected <> 1 THEN
|
|
1077
|
+
RAISE EXCEPTION 'scheduled reusable-session run row rebind failed'
|
|
1078
|
+
USING ERRCODE = '42501';
|
|
1079
|
+
END IF;
|
|
1080
|
+
|
|
1081
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
1082
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1083
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1084
|
+
AND capability_kind = 'run_admit';
|
|
1085
|
+
RETURN target_revision;
|
|
1086
|
+
EXCEPTION WHEN OTHERS THEN
|
|
1087
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
1088
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1089
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1090
|
+
AND capability_kind = 'run_admit';
|
|
1091
|
+
RAISE;
|
|
1092
|
+
END
|
|
1093
|
+
$materialize_scheduled_task_reusable_session_from_run$;
|
|
1094
|
+
|
|
1095
|
+
CREATE OR REPLACE FUNCTION fence_scheduled_task_personal_resource_execution_update()
|
|
1096
|
+
RETURNS trigger
|
|
1097
|
+
LANGUAGE plpgsql
|
|
1098
|
+
SECURITY DEFINER
|
|
1099
|
+
SET search_path = pg_catalog, pg_temp
|
|
1100
|
+
AS $fence_scheduled_task_personal_resource_execution_update$
|
|
1101
|
+
DECLARE
|
|
1102
|
+
execution_changed boolean;
|
|
1103
|
+
has_personal_authority boolean := false;
|
|
1104
|
+
capability_inserted boolean := false;
|
|
1105
|
+
affected integer;
|
|
1106
|
+
BEGIN
|
|
1107
|
+
IF NEW.authority_revision IS DISTINCT FROM OLD.authority_revision
|
|
1108
|
+
AND NEW.authority_revision <> OLD.authority_revision + 1
|
|
1109
|
+
THEN
|
|
1110
|
+
RAISE EXCEPTION 'scheduled personal-resource authority revision update is invalid'
|
|
1111
|
+
USING ERRCODE = '22023';
|
|
1112
|
+
END IF;
|
|
1113
|
+
|
|
1114
|
+
execution_changed := (
|
|
1115
|
+
pg_catalog.to_jsonb(NEW) - ARRAY[
|
|
1116
|
+
'name',
|
|
1117
|
+
'status',
|
|
1118
|
+
'updated_at',
|
|
1119
|
+
'authority_revision',
|
|
1120
|
+
'execution_digest'
|
|
1121
|
+
]::text[]
|
|
1122
|
+
) IS DISTINCT FROM (
|
|
1123
|
+
pg_catalog.to_jsonb(OLD) - ARRAY[
|
|
1124
|
+
'name',
|
|
1125
|
+
'status',
|
|
1126
|
+
'updated_at',
|
|
1127
|
+
'authority_revision',
|
|
1128
|
+
'execution_digest'
|
|
1129
|
+
]::text[]
|
|
1130
|
+
);
|
|
1131
|
+
|
|
1132
|
+
IF NOT execution_changed THEN
|
|
1133
|
+
RETURN NEW;
|
|
1134
|
+
END IF;
|
|
1135
|
+
|
|
1136
|
+
INSERT INTO opengeni_private.scheduled_personal_resource_capabilities (
|
|
1137
|
+
backend_pid, transaction_id, capability_kind
|
|
1138
|
+
) VALUES (pg_backend_pid(), pg_current_xact_id(), 'task_write')
|
|
1139
|
+
ON CONFLICT DO NOTHING;
|
|
1140
|
+
GET DIAGNOSTICS affected = ROW_COUNT;
|
|
1141
|
+
capability_inserted := affected = 1;
|
|
1142
|
+
|
|
1143
|
+
SELECT EXISTS (
|
|
1144
|
+
SELECT 1
|
|
1145
|
+
FROM scheduled_task_personal_resource_authorities authority
|
|
1146
|
+
WHERE authority.task_id = OLD.id
|
|
1147
|
+
) INTO has_personal_authority;
|
|
1148
|
+
|
|
1149
|
+
IF capability_inserted THEN
|
|
1150
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
1151
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1152
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1153
|
+
AND capability_kind = 'task_write';
|
|
1154
|
+
capability_inserted := false;
|
|
1155
|
+
END IF;
|
|
1156
|
+
|
|
1157
|
+
IF has_personal_authority THEN
|
|
1158
|
+
-- A current writer already supplies OLD + 1 and freezes/clones that exact
|
|
1159
|
+
-- revision in the same transaction. A rolling legacy writer supplies no
|
|
1160
|
+
-- revision, so advance it here and leave no matching snapshot: run
|
|
1161
|
+
-- admission then fails closed instead of reusing stale personal authority.
|
|
1162
|
+
NEW.authority_revision := OLD.authority_revision + 1;
|
|
1163
|
+
END IF;
|
|
1164
|
+
RETURN NEW;
|
|
1165
|
+
EXCEPTION WHEN OTHERS THEN
|
|
1166
|
+
IF capability_inserted THEN
|
|
1167
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
1168
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1169
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1170
|
+
AND capability_kind = 'task_write';
|
|
1171
|
+
END IF;
|
|
1172
|
+
RAISE;
|
|
1173
|
+
END
|
|
1174
|
+
$fence_scheduled_task_personal_resource_execution_update$;
|
|
1175
|
+
|
|
1176
|
+
CREATE OR REPLACE FUNCTION admit_scheduled_task_run_personal_resources()
|
|
1177
|
+
RETURNS trigger
|
|
1178
|
+
LANGUAGE plpgsql
|
|
1179
|
+
SECURITY DEFINER
|
|
1180
|
+
SET search_path = pg_catalog, pg_temp
|
|
1181
|
+
AS $admit_scheduled_task_run_personal_resources$
|
|
1182
|
+
DECLARE
|
|
1183
|
+
task_row record;
|
|
1184
|
+
authority_row record;
|
|
1185
|
+
once_snapshot record;
|
|
1186
|
+
selected_personal_count integer;
|
|
1187
|
+
has_authority boolean := false;
|
|
1188
|
+
invalid_count integer;
|
|
1189
|
+
affected integer;
|
|
1190
|
+
BEGIN
|
|
1191
|
+
SELECT task.* INTO STRICT task_row
|
|
1192
|
+
FROM scheduled_tasks task
|
|
1193
|
+
WHERE task.id = NEW.task_id
|
|
1194
|
+
AND task.account_id = NEW.account_id
|
|
1195
|
+
AND task.workspace_id = NEW.workspace_id
|
|
1196
|
+
FOR UPDATE;
|
|
1197
|
+
|
|
1198
|
+
IF (NEW.task_authority_revision IS NULL) <> (NEW.task_execution_digest IS NULL) THEN
|
|
1199
|
+
RAISE EXCEPTION 'scheduled task run execution binding is incomplete'
|
|
1200
|
+
USING ERRCODE = '22023';
|
|
1201
|
+
END IF;
|
|
1202
|
+
IF NEW.task_authority_revision IS NOT NULL AND (
|
|
1203
|
+
NEW.task_authority_revision IS DISTINCT FROM task_row.authority_revision
|
|
1204
|
+
OR NEW.task_execution_digest IS DISTINCT FROM task_row.execution_digest
|
|
1205
|
+
) THEN
|
|
1206
|
+
RAISE EXCEPTION 'scheduled task changed after worker read'
|
|
1207
|
+
USING ERRCODE = '40001';
|
|
1208
|
+
END IF;
|
|
1209
|
+
|
|
1210
|
+
INSERT INTO opengeni_private.scheduled_personal_resource_capabilities (
|
|
1211
|
+
backend_pid, transaction_id, capability_kind
|
|
1212
|
+
) VALUES (pg_backend_pid(), pg_current_xact_id(), 'run_admit')
|
|
1213
|
+
ON CONFLICT DO NOTHING;
|
|
1214
|
+
|
|
1215
|
+
SELECT authority.* INTO authority_row
|
|
1216
|
+
FROM scheduled_task_personal_resource_authorities authority
|
|
1217
|
+
WHERE authority.task_id = NEW.task_id
|
|
1218
|
+
AND authority.task_authority_revision = task_row.authority_revision;
|
|
1219
|
+
has_authority := FOUND;
|
|
1220
|
+
IF NOT has_authority THEN
|
|
1221
|
+
SELECT count(*)::integer INTO selected_personal_count
|
|
1222
|
+
FROM (
|
|
1223
|
+
SELECT variable_set.id
|
|
1224
|
+
FROM workspace_variable_sets variable_set
|
|
1225
|
+
WHERE variable_set.id = task_row.variable_set_id
|
|
1226
|
+
AND variable_set.account_id = task_row.account_id
|
|
1227
|
+
AND variable_set.authority_scope = 'user'
|
|
1228
|
+
UNION
|
|
1229
|
+
SELECT rig.id
|
|
1230
|
+
FROM rigs rig
|
|
1231
|
+
WHERE rig.id = task_row.rig_id
|
|
1232
|
+
AND rig.account_id = task_row.account_id
|
|
1233
|
+
AND rig.authority_scope = 'user'
|
|
1234
|
+
UNION
|
|
1235
|
+
SELECT default_variable_set.id
|
|
1236
|
+
FROM rigs rig
|
|
1237
|
+
JOIN rig_versions rig_version
|
|
1238
|
+
ON rig_version.rig_id = rig.id
|
|
1239
|
+
AND rig_version.account_id = rig.account_id
|
|
1240
|
+
AND rig_version.active
|
|
1241
|
+
CROSS JOIN LATERAL jsonb_array_elements_text(
|
|
1242
|
+
rig_version.default_variable_set_ids
|
|
1243
|
+
) default_id(value)
|
|
1244
|
+
JOIN workspace_variable_sets default_variable_set
|
|
1245
|
+
ON default_variable_set.id = default_id.value::uuid
|
|
1246
|
+
AND default_variable_set.account_id = task_row.account_id
|
|
1247
|
+
AND default_variable_set.authority_scope = 'user'
|
|
1248
|
+
WHERE rig.id = task_row.rig_id
|
|
1249
|
+
AND rig.account_id = task_row.account_id
|
|
1250
|
+
) selected;
|
|
1251
|
+
IF selected_personal_count = 0 THEN
|
|
1252
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
1253
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1254
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1255
|
+
AND capability_kind = 'run_admit';
|
|
1256
|
+
RETURN NEW;
|
|
1257
|
+
END IF;
|
|
1258
|
+
END IF;
|
|
1259
|
+
|
|
1260
|
+
-- Ordinary run-history rows remain writable for paused tasks. The active
|
|
1261
|
+
-- fence applies only when this insert would admit personal-resource
|
|
1262
|
+
-- authority, and is checked while holding the task row lock before any
|
|
1263
|
+
-- admission/snapshot/once-consumption write.
|
|
1264
|
+
IF task_row.status <> 'active' THEN
|
|
1265
|
+
RAISE EXCEPTION 'scheduled task is not active' USING ERRCODE = '55000';
|
|
1266
|
+
END IF;
|
|
1267
|
+
|
|
1268
|
+
IF NOT has_authority THEN
|
|
1269
|
+
RAISE EXCEPTION 'scheduled personal-resource task has no authority snapshot'
|
|
1270
|
+
USING ERRCODE = '42501';
|
|
1271
|
+
END IF;
|
|
1272
|
+
|
|
1273
|
+
IF NEW.task_authority_revision IS NULL THEN
|
|
1274
|
+
RAISE EXCEPTION 'scheduled personal-resource run requires an execution binding'
|
|
1275
|
+
USING ERRCODE = '42501';
|
|
1276
|
+
END IF;
|
|
1277
|
+
IF authority_row.execution_digest IS DISTINCT FROM task_row.execution_digest
|
|
1278
|
+
OR authority_row.task_authority_revision IS DISTINCT FROM NEW.task_authority_revision
|
|
1279
|
+
OR authority_row.execution_digest IS DISTINCT FROM NEW.task_execution_digest
|
|
1280
|
+
THEN
|
|
1281
|
+
RAISE EXCEPTION 'scheduled personal-resource execution binding mismatch'
|
|
1282
|
+
USING ERRCODE = '42501';
|
|
1283
|
+
END IF;
|
|
1284
|
+
|
|
1285
|
+
SELECT count(*)::integer INTO invalid_count
|
|
1286
|
+
FROM scheduled_task_personal_resource_snapshots snapshot
|
|
1287
|
+
WHERE snapshot.task_id = authority_row.task_id
|
|
1288
|
+
AND snapshot.task_authority_revision = authority_row.task_authority_revision
|
|
1289
|
+
AND NOT (
|
|
1290
|
+
EXISTS (
|
|
1291
|
+
SELECT 1 FROM organization_memberships membership
|
|
1292
|
+
WHERE membership.id = snapshot.owner_organization_membership_id
|
|
1293
|
+
AND membership.account_id = snapshot.account_id
|
|
1294
|
+
AND membership.subject_id = authority_row.initiating_human_subject_id
|
|
1295
|
+
AND membership.status = 'active'
|
|
1296
|
+
AND membership.revoked_at IS NULL
|
|
1297
|
+
AND membership.personal_workspace_id = snapshot.origin_workspace_id
|
|
1298
|
+
AND membership.authorization_revision = snapshot.membership_authorization_revision
|
|
1299
|
+
AND (membership.personal_workspace_id = snapshot.workspace_id OR EXISTS (
|
|
1300
|
+
SELECT 1 FROM workspace_memberships workspace_membership
|
|
1301
|
+
WHERE workspace_membership.account_id = membership.account_id
|
|
1302
|
+
AND workspace_membership.workspace_id = snapshot.workspace_id
|
|
1303
|
+
AND workspace_membership.subject_id = membership.subject_id
|
|
1304
|
+
))
|
|
1305
|
+
)
|
|
1306
|
+
AND EXISTS (
|
|
1307
|
+
SELECT 1 FROM organization_user_resource_authorities authority
|
|
1308
|
+
WHERE authority.id = snapshot.authority_id
|
|
1309
|
+
AND authority.account_id = snapshot.account_id
|
|
1310
|
+
AND authority.organization_membership_id = snapshot.owner_organization_membership_id
|
|
1311
|
+
AND authority.resource_kind = snapshot.resource_kind
|
|
1312
|
+
AND authority.resource_id = snapshot.resource_id
|
|
1313
|
+
AND authority.origin_workspace_id = snapshot.origin_workspace_id
|
|
1314
|
+
AND authority.generation = snapshot.authority_generation
|
|
1315
|
+
AND authority.status = 'active'
|
|
1316
|
+
AND authority.revoked_at IS NULL
|
|
1317
|
+
)
|
|
1318
|
+
AND (
|
|
1319
|
+
(snapshot.resource_kind = 'variable_set' AND EXISTS (
|
|
1320
|
+
SELECT 1 FROM workspace_variable_sets variable_set
|
|
1321
|
+
WHERE variable_set.id = snapshot.resource_id
|
|
1322
|
+
AND variable_set.account_id = snapshot.account_id
|
|
1323
|
+
AND variable_set.workspace_id = snapshot.origin_workspace_id
|
|
1324
|
+
AND variable_set.authority_scope = 'user'
|
|
1325
|
+
AND variable_set.authority_id = snapshot.authority_id
|
|
1326
|
+
AND variable_set.owner_organization_membership_id =
|
|
1327
|
+
snapshot.owner_organization_membership_id
|
|
1328
|
+
AND variable_set.origin_workspace_id = snapshot.origin_workspace_id
|
|
1329
|
+
)) OR (snapshot.resource_kind = 'rig' AND EXISTS (
|
|
1330
|
+
SELECT 1 FROM rigs rig
|
|
1331
|
+
JOIN rig_versions rig_version
|
|
1332
|
+
ON rig_version.id = snapshot.resource_version_id
|
|
1333
|
+
AND rig_version.rig_id = rig.id
|
|
1334
|
+
AND rig_version.account_id = rig.account_id
|
|
1335
|
+
AND rig_version.workspace_id = snapshot.origin_workspace_id
|
|
1336
|
+
WHERE rig.id = snapshot.resource_id
|
|
1337
|
+
AND rig.account_id = snapshot.account_id
|
|
1338
|
+
AND rig.workspace_id = snapshot.origin_workspace_id
|
|
1339
|
+
AND rig.authority_scope = 'user'
|
|
1340
|
+
AND rig.authority_id = snapshot.authority_id
|
|
1341
|
+
AND rig.owner_organization_membership_id =
|
|
1342
|
+
snapshot.owner_organization_membership_id
|
|
1343
|
+
AND rig.origin_workspace_id = snapshot.origin_workspace_id
|
|
1344
|
+
))
|
|
1345
|
+
)
|
|
1346
|
+
AND EXISTS (
|
|
1347
|
+
SELECT 1 FROM organization_user_resource_grants grant_value
|
|
1348
|
+
WHERE grant_value.id = snapshot.grant_id
|
|
1349
|
+
AND grant_value.account_id = snapshot.account_id
|
|
1350
|
+
AND grant_value.authority_id = snapshot.authority_id
|
|
1351
|
+
AND grant_value.owner_organization_membership_id =
|
|
1352
|
+
snapshot.owner_organization_membership_id
|
|
1353
|
+
AND grant_value.workspace_id = snapshot.target_workspace_id
|
|
1354
|
+
AND grant_value.action = snapshot.action
|
|
1355
|
+
AND grant_value.mode = snapshot.grant_mode
|
|
1356
|
+
AND grant_value.context = snapshot.grant_context
|
|
1357
|
+
AND grant_value.generation = snapshot.grant_generation
|
|
1358
|
+
AND grant_value.session_id IS NOT DISTINCT FROM snapshot.grant_session_id
|
|
1359
|
+
AND grant_value.authority_epoch IS NOT DISTINCT FROM snapshot.grant_authority_epoch
|
|
1360
|
+
AND grant_value.status = 'active'
|
|
1361
|
+
AND (grant_value.expires_at IS NULL OR grant_value.expires_at > clock_timestamp())
|
|
1362
|
+
)
|
|
1363
|
+
AND (
|
|
1364
|
+
authority_row.target_session_id IS NULL
|
|
1365
|
+
OR EXISTS (
|
|
1366
|
+
SELECT 1 FROM sessions session_value
|
|
1367
|
+
WHERE session_value.id = authority_row.target_session_id
|
|
1368
|
+
AND session_value.account_id = authority_row.account_id
|
|
1369
|
+
AND session_value.workspace_id = authority_row.workspace_id
|
|
1370
|
+
AND session_value.status <> 'cancelled'
|
|
1371
|
+
AND session_value.visibility = authority_row.session_visibility
|
|
1372
|
+
AND session_value.authority_epoch = authority_row.session_authority_epoch
|
|
1373
|
+
)
|
|
1374
|
+
)
|
|
1375
|
+
);
|
|
1376
|
+
IF invalid_count <> 0 THEN
|
|
1377
|
+
RAISE EXCEPTION 'scheduled personal-resource authority snapshot is no longer live'
|
|
1378
|
+
USING ERRCODE = '42501';
|
|
1379
|
+
END IF;
|
|
1380
|
+
|
|
1381
|
+
INSERT INTO scheduled_task_run_personal_resource_admissions (
|
|
1382
|
+
run_id, task_id, task_authority_revision, account_id, workspace_id,
|
|
1383
|
+
initiating_human_subject_id, owner_organization_membership_id,
|
|
1384
|
+
membership_authorization_revision, target_session_id, session_visibility,
|
|
1385
|
+
session_authority_epoch, execution_digest, resource_count
|
|
1386
|
+
) VALUES (
|
|
1387
|
+
NEW.id, authority_row.task_id, authority_row.task_authority_revision,
|
|
1388
|
+
authority_row.account_id, authority_row.workspace_id,
|
|
1389
|
+
authority_row.initiating_human_subject_id,
|
|
1390
|
+
authority_row.owner_organization_membership_id,
|
|
1391
|
+
authority_row.membership_authorization_revision, authority_row.target_session_id,
|
|
1392
|
+
authority_row.session_visibility, authority_row.session_authority_epoch,
|
|
1393
|
+
authority_row.execution_digest, authority_row.resource_count
|
|
1394
|
+
);
|
|
1395
|
+
|
|
1396
|
+
INSERT INTO scheduled_task_run_personal_resource_snapshots (
|
|
1397
|
+
run_id, task_id, task_authority_revision, account_id, workspace_id,
|
|
1398
|
+
resource_kind, resource_id, resource_version_id, selection_sources, action,
|
|
1399
|
+
origin_workspace_id, owner_organization_membership_id,
|
|
1400
|
+
membership_authorization_revision, authority_id, authority_generation,
|
|
1401
|
+
target_workspace_id, session_visibility, session_authority_epoch,
|
|
1402
|
+
grant_id, grant_generation, grant_mode, grant_context,
|
|
1403
|
+
grant_session_id, grant_authority_epoch
|
|
1404
|
+
)
|
|
1405
|
+
SELECT NEW.id, snapshot.task_id, snapshot.task_authority_revision,
|
|
1406
|
+
snapshot.account_id, snapshot.workspace_id, snapshot.resource_kind,
|
|
1407
|
+
snapshot.resource_id, snapshot.resource_version_id, snapshot.selection_sources,
|
|
1408
|
+
snapshot.action, snapshot.origin_workspace_id,
|
|
1409
|
+
snapshot.owner_organization_membership_id,
|
|
1410
|
+
snapshot.membership_authorization_revision, snapshot.authority_id,
|
|
1411
|
+
snapshot.authority_generation, snapshot.target_workspace_id,
|
|
1412
|
+
snapshot.session_visibility, snapshot.session_authority_epoch,
|
|
1413
|
+
snapshot.grant_id, snapshot.grant_generation, snapshot.grant_mode,
|
|
1414
|
+
snapshot.grant_context, snapshot.grant_session_id, snapshot.grant_authority_epoch
|
|
1415
|
+
FROM scheduled_task_personal_resource_snapshots snapshot
|
|
1416
|
+
WHERE snapshot.task_id = authority_row.task_id
|
|
1417
|
+
AND snapshot.task_authority_revision = authority_row.task_authority_revision;
|
|
1418
|
+
|
|
1419
|
+
FOR once_snapshot IN
|
|
1420
|
+
SELECT snapshot.*
|
|
1421
|
+
FROM scheduled_task_run_personal_resource_snapshots snapshot
|
|
1422
|
+
WHERE snapshot.run_id = NEW.id
|
|
1423
|
+
AND snapshot.grant_mode = 'once'
|
|
1424
|
+
ORDER BY snapshot.grant_id
|
|
1425
|
+
LOOP
|
|
1426
|
+
UPDATE organization_user_resource_grants grant_value
|
|
1427
|
+
SET status = 'consumed', updated_at = clock_timestamp()
|
|
1428
|
+
WHERE grant_value.id = once_snapshot.grant_id
|
|
1429
|
+
AND grant_value.account_id = once_snapshot.account_id
|
|
1430
|
+
AND grant_value.authority_id = once_snapshot.authority_id
|
|
1431
|
+
AND grant_value.generation = once_snapshot.grant_generation
|
|
1432
|
+
AND grant_value.status = 'active';
|
|
1433
|
+
GET DIAGNOSTICS affected = ROW_COUNT;
|
|
1434
|
+
IF affected <> 1 THEN
|
|
1435
|
+
RAISE EXCEPTION 'scheduled occurrence once grant lost its first-use race'
|
|
1436
|
+
USING ERRCODE = '40001';
|
|
1437
|
+
END IF;
|
|
1438
|
+
INSERT INTO scheduled_task_run_personal_resource_once_receipts (
|
|
1439
|
+
grant_id, run_id, account_id, workspace_id, authority_id,
|
|
1440
|
+
authority_generation, grant_generation
|
|
1441
|
+
) VALUES (
|
|
1442
|
+
once_snapshot.grant_id, NEW.id, once_snapshot.account_id,
|
|
1443
|
+
once_snapshot.workspace_id, once_snapshot.authority_id,
|
|
1444
|
+
once_snapshot.authority_generation, once_snapshot.grant_generation
|
|
1445
|
+
);
|
|
1446
|
+
END LOOP;
|
|
1447
|
+
|
|
1448
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
1449
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1450
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1451
|
+
AND capability_kind = 'run_admit';
|
|
1452
|
+
RETURN NEW;
|
|
1453
|
+
EXCEPTION WHEN OTHERS THEN
|
|
1454
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
1455
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1456
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1457
|
+
AND capability_kind = 'run_admit';
|
|
1458
|
+
RAISE;
|
|
1459
|
+
END
|
|
1460
|
+
$admit_scheduled_task_run_personal_resources$;
|
|
1461
|
+
|
|
1462
|
+
CREATE OR REPLACE FUNCTION prepare_scheduled_task_attempt_once_grants()
|
|
1463
|
+
RETURNS trigger
|
|
1464
|
+
LANGUAGE plpgsql
|
|
1465
|
+
SECURITY DEFINER
|
|
1466
|
+
SET search_path = pg_catalog, pg_temp
|
|
1467
|
+
AS $prepare_scheduled_task_attempt_once_grants$
|
|
1468
|
+
DECLARE
|
|
1469
|
+
scheduled_run_id uuid;
|
|
1470
|
+
run_count integer;
|
|
1471
|
+
receipt_row record;
|
|
1472
|
+
prior_attempt_run_id uuid;
|
|
1473
|
+
affected integer;
|
|
1474
|
+
BEGIN
|
|
1475
|
+
SELECT count(DISTINCT update_row.scheduled_task_run_id)::integer,
|
|
1476
|
+
min(update_row.scheduled_task_run_id::text)::uuid
|
|
1477
|
+
INTO run_count, scheduled_run_id
|
|
1478
|
+
FROM session_system_updates update_row
|
|
1479
|
+
WHERE update_row.workspace_id = NEW.workspace_id
|
|
1480
|
+
AND update_row.session_id = NEW.session_id
|
|
1481
|
+
AND update_row.delivered_turn_id = NEW.turn_id
|
|
1482
|
+
AND update_row.kind = 'scheduled_occurrence'
|
|
1483
|
+
AND update_row.scheduled_task_run_id IS NOT NULL;
|
|
1484
|
+
IF run_count = 0 THEN RETURN NEW; END IF;
|
|
1485
|
+
IF run_count <> 1 THEN
|
|
1486
|
+
RAISE EXCEPTION 'scheduled once-grant attempt requires one occurrence snapshot'
|
|
1487
|
+
USING ERRCODE = '42501';
|
|
1488
|
+
END IF;
|
|
1489
|
+
|
|
1490
|
+
INSERT INTO opengeni_private.scheduled_personal_resource_capabilities (
|
|
1491
|
+
backend_pid, transaction_id, capability_kind
|
|
1492
|
+
) VALUES (pg_backend_pid(), pg_current_xact_id(), 'attempt_match')
|
|
1493
|
+
ON CONFLICT DO NOTHING;
|
|
1494
|
+
|
|
1495
|
+
FOR receipt_row IN
|
|
1496
|
+
SELECT receipt.*
|
|
1497
|
+
FROM scheduled_task_run_personal_resource_once_receipts receipt
|
|
1498
|
+
WHERE receipt.run_id = scheduled_run_id
|
|
1499
|
+
ORDER BY receipt.grant_id
|
|
1500
|
+
FOR UPDATE
|
|
1501
|
+
LOOP
|
|
1502
|
+
SELECT update_row.scheduled_task_run_id INTO prior_attempt_run_id
|
|
1503
|
+
FROM personal_resource_once_consumption_receipts attempt_receipt
|
|
1504
|
+
JOIN session_system_updates update_row
|
|
1505
|
+
ON update_row.workspace_id = attempt_receipt.workspace_id
|
|
1506
|
+
AND update_row.session_id = attempt_receipt.session_id
|
|
1507
|
+
AND update_row.delivered_turn_id = attempt_receipt.turn_id
|
|
1508
|
+
AND update_row.kind = 'scheduled_occurrence'
|
|
1509
|
+
AND update_row.scheduled_task_run_id IS NOT NULL
|
|
1510
|
+
WHERE attempt_receipt.grant_id = receipt_row.grant_id
|
|
1511
|
+
AND attempt_receipt.account_id = receipt_row.account_id
|
|
1512
|
+
LIMIT 1;
|
|
1513
|
+
IF FOUND AND prior_attempt_run_id IS DISTINCT FROM scheduled_run_id THEN
|
|
1514
|
+
RAISE EXCEPTION 'once grant is owned by another scheduled occurrence'
|
|
1515
|
+
USING ERRCODE = '42501';
|
|
1516
|
+
END IF;
|
|
1517
|
+
IF FOUND THEN
|
|
1518
|
+
DELETE FROM personal_resource_once_consumption_receipts
|
|
1519
|
+
WHERE grant_id = receipt_row.grant_id
|
|
1520
|
+
AND account_id = receipt_row.account_id;
|
|
1521
|
+
END IF;
|
|
1522
|
+
|
|
1523
|
+
UPDATE organization_user_resource_grants grant_value
|
|
1524
|
+
SET status = 'active', updated_at = clock_timestamp()
|
|
1525
|
+
WHERE grant_value.id = receipt_row.grant_id
|
|
1526
|
+
AND grant_value.account_id = receipt_row.account_id
|
|
1527
|
+
AND grant_value.authority_id = receipt_row.authority_id
|
|
1528
|
+
AND grant_value.generation = receipt_row.grant_generation
|
|
1529
|
+
AND grant_value.status = 'consumed';
|
|
1530
|
+
GET DIAGNOSTICS affected = ROW_COUNT;
|
|
1531
|
+
IF affected <> 1 THEN
|
|
1532
|
+
RAISE EXCEPTION 'scheduled occurrence once grant is no longer owned'
|
|
1533
|
+
USING ERRCODE = '42501';
|
|
1534
|
+
END IF;
|
|
1535
|
+
END LOOP;
|
|
1536
|
+
|
|
1537
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
1538
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1539
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1540
|
+
AND capability_kind = 'attempt_match';
|
|
1541
|
+
RETURN NEW;
|
|
1542
|
+
EXCEPTION WHEN OTHERS THEN
|
|
1543
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
1544
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1545
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1546
|
+
AND capability_kind = 'attempt_match';
|
|
1547
|
+
RAISE;
|
|
1548
|
+
END
|
|
1549
|
+
$prepare_scheduled_task_attempt_once_grants$;
|
|
1550
|
+
|
|
1551
|
+
CREATE OR REPLACE FUNCTION validate_scheduled_task_attempt_personal_resources()
|
|
1552
|
+
RETURNS trigger
|
|
1553
|
+
LANGUAGE plpgsql
|
|
1554
|
+
SECURITY DEFINER
|
|
1555
|
+
SET search_path = pg_catalog, pg_temp
|
|
1556
|
+
AS $validate_scheduled_task_attempt_personal_resources$
|
|
1557
|
+
DECLARE
|
|
1558
|
+
scheduled_run_id uuid;
|
|
1559
|
+
run_count integer;
|
|
1560
|
+
admission_row record;
|
|
1561
|
+
turn_subject text;
|
|
1562
|
+
mismatch_count integer;
|
|
1563
|
+
BEGIN
|
|
1564
|
+
SELECT count(DISTINCT update_row.scheduled_task_run_id)::integer,
|
|
1565
|
+
min(update_row.scheduled_task_run_id::text)::uuid
|
|
1566
|
+
INTO run_count, scheduled_run_id
|
|
1567
|
+
FROM session_system_updates update_row
|
|
1568
|
+
WHERE update_row.workspace_id = NEW.workspace_id
|
|
1569
|
+
AND update_row.session_id = NEW.session_id
|
|
1570
|
+
AND update_row.delivered_turn_id = NEW.turn_id
|
|
1571
|
+
AND update_row.kind = 'scheduled_occurrence'
|
|
1572
|
+
AND update_row.scheduled_task_run_id IS NOT NULL;
|
|
1573
|
+
IF run_count = 0 THEN RETURN NEW; END IF;
|
|
1574
|
+
IF run_count <> 1 THEN
|
|
1575
|
+
RAISE EXCEPTION 'scheduled personal-resource attempt requires one occurrence snapshot'
|
|
1576
|
+
USING ERRCODE = '42501';
|
|
1577
|
+
END IF;
|
|
1578
|
+
|
|
1579
|
+
INSERT INTO opengeni_private.scheduled_personal_resource_capabilities (
|
|
1580
|
+
backend_pid, transaction_id, capability_kind
|
|
1581
|
+
) VALUES (pg_backend_pid(), pg_current_xact_id(), 'attempt_match')
|
|
1582
|
+
ON CONFLICT DO NOTHING;
|
|
1583
|
+
|
|
1584
|
+
SELECT admission.* INTO admission_row
|
|
1585
|
+
FROM scheduled_task_run_personal_resource_admissions admission
|
|
1586
|
+
WHERE admission.run_id = scheduled_run_id;
|
|
1587
|
+
IF NOT FOUND THEN
|
|
1588
|
+
IF EXISTS (
|
|
1589
|
+
SELECT 1 FROM session_attempt_personal_resource_snapshots snapshot
|
|
1590
|
+
WHERE snapshot.attempt_id = NEW.id
|
|
1591
|
+
) THEN
|
|
1592
|
+
RAISE EXCEPTION 'scheduled occurrence has no admitted personal-resource snapshot'
|
|
1593
|
+
USING ERRCODE = '42501';
|
|
1594
|
+
END IF;
|
|
1595
|
+
RETURN NEW;
|
|
1596
|
+
END IF;
|
|
1597
|
+
|
|
1598
|
+
SELECT coalesce(
|
|
1599
|
+
nullif(btrim(turn_value.initiating_human_subject_id), ''),
|
|
1600
|
+
CASE WHEN turn_value.initiator_kind = 'subject'
|
|
1601
|
+
THEN nullif(btrim(turn_value.initiator_subject_id), '') END
|
|
1602
|
+
) INTO turn_subject
|
|
1603
|
+
FROM session_turns turn_value
|
|
1604
|
+
WHERE turn_value.id = NEW.turn_id
|
|
1605
|
+
AND turn_value.account_id = NEW.account_id
|
|
1606
|
+
AND turn_value.workspace_id = NEW.workspace_id
|
|
1607
|
+
AND turn_value.session_id = NEW.session_id;
|
|
1608
|
+
IF turn_subject IS DISTINCT FROM admission_row.initiating_human_subject_id THEN
|
|
1609
|
+
RAISE EXCEPTION 'scheduled occurrence causal human mismatch' USING ERRCODE = '42501';
|
|
1610
|
+
END IF;
|
|
1611
|
+
|
|
1612
|
+
PERFORM 1 FROM scheduled_tasks task
|
|
1613
|
+
WHERE task.id = admission_row.task_id
|
|
1614
|
+
AND task.account_id = admission_row.account_id
|
|
1615
|
+
AND task.workspace_id = admission_row.workspace_id
|
|
1616
|
+
AND task.status = 'active'
|
|
1617
|
+
AND task.authority_revision = admission_row.task_authority_revision
|
|
1618
|
+
AND task.execution_digest = admission_row.execution_digest
|
|
1619
|
+
FOR SHARE;
|
|
1620
|
+
IF NOT FOUND THEN
|
|
1621
|
+
RAISE EXCEPTION 'scheduled task authority revision changed before attempt admission'
|
|
1622
|
+
USING ERRCODE = '42501';
|
|
1623
|
+
END IF;
|
|
1624
|
+
|
|
1625
|
+
SELECT count(*)::integer INTO mismatch_count
|
|
1626
|
+
FROM scheduled_task_run_personal_resource_snapshots run_snapshot
|
|
1627
|
+
FULL JOIN session_attempt_personal_resource_snapshots attempt_snapshot
|
|
1628
|
+
ON attempt_snapshot.attempt_id = NEW.id
|
|
1629
|
+
AND attempt_snapshot.resource_kind = run_snapshot.resource_kind
|
|
1630
|
+
AND attempt_snapshot.resource_id = run_snapshot.resource_id
|
|
1631
|
+
WHERE run_snapshot.run_id = scheduled_run_id
|
|
1632
|
+
AND (
|
|
1633
|
+
attempt_snapshot.attempt_id IS NULL
|
|
1634
|
+
OR attempt_snapshot.resource_version_id IS DISTINCT FROM run_snapshot.resource_version_id
|
|
1635
|
+
OR attempt_snapshot.selection_sources IS DISTINCT FROM run_snapshot.selection_sources
|
|
1636
|
+
OR attempt_snapshot.authority_id IS DISTINCT FROM run_snapshot.authority_id
|
|
1637
|
+
OR attempt_snapshot.authority_generation IS DISTINCT FROM run_snapshot.authority_generation
|
|
1638
|
+
OR attempt_snapshot.grant_id IS DISTINCT FROM run_snapshot.grant_id
|
|
1639
|
+
OR attempt_snapshot.grant_generation IS DISTINCT FROM run_snapshot.grant_generation
|
|
1640
|
+
OR attempt_snapshot.grant_mode IS DISTINCT FROM run_snapshot.grant_mode
|
|
1641
|
+
);
|
|
1642
|
+
IF mismatch_count <> 0 OR (
|
|
1643
|
+
SELECT count(*) FROM session_attempt_personal_resource_snapshots snapshot
|
|
1644
|
+
WHERE snapshot.attempt_id = NEW.id
|
|
1645
|
+
) <> admission_row.resource_count THEN
|
|
1646
|
+
RAISE EXCEPTION 'scheduled occurrence personal-resource snapshot widened or changed'
|
|
1647
|
+
USING ERRCODE = '42501';
|
|
1648
|
+
END IF;
|
|
1649
|
+
|
|
1650
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
1651
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1652
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1653
|
+
AND capability_kind = 'attempt_match';
|
|
1654
|
+
RETURN NEW;
|
|
1655
|
+
EXCEPTION WHEN OTHERS THEN
|
|
1656
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
1657
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1658
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1659
|
+
AND capability_kind = 'attempt_match';
|
|
1660
|
+
RAISE;
|
|
1661
|
+
END
|
|
1662
|
+
$validate_scheduled_task_attempt_personal_resources$;
|
|
1663
|
+
|
|
1664
|
+
CREATE TRIGGER scheduled_task_personal_resource_execution_revision
|
|
1665
|
+
BEFORE UPDATE ON "scheduled_tasks"
|
|
1666
|
+
FOR EACH ROW EXECUTE FUNCTION fence_scheduled_task_personal_resource_execution_update();
|
|
1667
|
+
|
|
1668
|
+
CREATE TRIGGER zz_scheduled_task_execution_digest
|
|
1669
|
+
BEFORE INSERT OR UPDATE ON "scheduled_tasks"
|
|
1670
|
+
FOR EACH ROW EXECUTE FUNCTION set_scheduled_task_execution_digest();
|
|
1671
|
+
|
|
1672
|
+
CREATE TRIGGER scheduled_task_run_personal_resource_admission
|
|
1673
|
+
AFTER INSERT ON "scheduled_task_runs"
|
|
1674
|
+
FOR EACH ROW EXECUTE FUNCTION admit_scheduled_task_run_personal_resources();
|
|
1675
|
+
|
|
1676
|
+
CREATE TRIGGER scheduled_task_attempt_once_retry_prepare
|
|
1677
|
+
BEFORE INSERT ON "session_turn_attempts"
|
|
1678
|
+
FOR EACH ROW EXECUTE FUNCTION prepare_scheduled_task_attempt_once_grants();
|
|
1679
|
+
|
|
1680
|
+
CREATE TRIGGER zz_scheduled_task_attempt_personal_resource_match
|
|
1681
|
+
AFTER INSERT ON "session_turn_attempts"
|
|
1682
|
+
FOR EACH ROW EXECUTE FUNCTION validate_scheduled_task_attempt_personal_resources();
|
|
1683
|
+
|
|
1684
|
+
CREATE OR REPLACE FUNCTION scheduled_task_run_personal_resource_authority(
|
|
1685
|
+
p_account_id uuid,
|
|
1686
|
+
p_workspace_id uuid,
|
|
1687
|
+
p_run_id uuid
|
|
1688
|
+
)
|
|
1689
|
+
RETURNS TABLE (
|
|
1690
|
+
task_id uuid,
|
|
1691
|
+
task_authority_revision bigint,
|
|
1692
|
+
execution_digest text,
|
|
1693
|
+
initiating_human_subject_id text,
|
|
1694
|
+
resource_kind text,
|
|
1695
|
+
resource_id uuid,
|
|
1696
|
+
resource_version_id uuid,
|
|
1697
|
+
selection_sources text[],
|
|
1698
|
+
authority_id uuid,
|
|
1699
|
+
authority_generation bigint,
|
|
1700
|
+
grant_id uuid,
|
|
1701
|
+
grant_generation bigint,
|
|
1702
|
+
grant_mode text
|
|
1703
|
+
)
|
|
1704
|
+
LANGUAGE plpgsql
|
|
1705
|
+
SECURITY DEFINER
|
|
1706
|
+
SET search_path = pg_catalog, pg_temp
|
|
1707
|
+
AS $scheduled_task_run_personal_resource_authority$
|
|
1708
|
+
BEGIN
|
|
1709
|
+
IF p_account_id IS DISTINCT FROM nullif(current_setting('opengeni.account_id', true), '')::uuid
|
|
1710
|
+
OR p_workspace_id IS DISTINCT FROM nullif(
|
|
1711
|
+
current_setting('opengeni.workspace_id', true), ''
|
|
1712
|
+
)::uuid
|
|
1713
|
+
THEN
|
|
1714
|
+
RAISE EXCEPTION 'scheduled run personal-resource scope mismatch' USING ERRCODE = '42501';
|
|
1715
|
+
END IF;
|
|
1716
|
+
INSERT INTO opengeni_private.scheduled_personal_resource_capabilities (
|
|
1717
|
+
backend_pid, transaction_id, capability_kind
|
|
1718
|
+
) VALUES (pg_backend_pid(), pg_current_xact_id(), 'run_admit')
|
|
1719
|
+
ON CONFLICT DO NOTHING;
|
|
1720
|
+
RETURN QUERY
|
|
1721
|
+
SELECT admission.task_id, admission.task_authority_revision,
|
|
1722
|
+
admission.execution_digest, admission.initiating_human_subject_id, snapshot.resource_kind,
|
|
1723
|
+
snapshot.resource_id, snapshot.resource_version_id, snapshot.selection_sources,
|
|
1724
|
+
snapshot.authority_id, snapshot.authority_generation, snapshot.grant_id,
|
|
1725
|
+
snapshot.grant_generation, snapshot.grant_mode
|
|
1726
|
+
FROM scheduled_task_run_personal_resource_admissions admission
|
|
1727
|
+
JOIN scheduled_task_run_personal_resource_snapshots snapshot
|
|
1728
|
+
ON snapshot.run_id = admission.run_id
|
|
1729
|
+
WHERE admission.run_id = p_run_id
|
|
1730
|
+
AND admission.account_id = p_account_id
|
|
1731
|
+
AND admission.workspace_id = p_workspace_id
|
|
1732
|
+
ORDER BY snapshot.resource_kind, snapshot.resource_id;
|
|
1733
|
+
DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
|
|
1734
|
+
WHERE backend_pid = pg_backend_pid()
|
|
1735
|
+
AND transaction_id = pg_current_xact_id_if_assigned()
|
|
1736
|
+
AND capability_kind = 'run_admit';
|
|
1737
|
+
END
|
|
1738
|
+
$scheduled_task_run_personal_resource_authority$;
|
|
1739
|
+
|
|
1740
|
+
-- The migration target may be public or a dedicated embed schema. Bind that
|
|
1741
|
+
-- trusted schema explicitly into every privileged routine and put pg_temp last
|
|
1742
|
+
-- so caller-created temporary relations can never shadow durable authority.
|
|
1743
|
+
DO $scheduled_personal_resource_search_path$
|
|
1744
|
+
DECLARE
|
|
1745
|
+
data_schema text := current_schema();
|
|
1746
|
+
BEGIN
|
|
1747
|
+
EXECUTE format(
|
|
1748
|
+
'ALTER FUNCTION %1$I.scheduled_task_execution_state(%1$I.scheduled_tasks) '
|
|
1749
|
+
|| 'SET search_path = pg_catalog, %1$I, pg_temp',
|
|
1750
|
+
data_schema
|
|
1751
|
+
);
|
|
1752
|
+
EXECUTE format(
|
|
1753
|
+
'ALTER FUNCTION %1$I.scheduled_task_execution_digest(%1$I.scheduled_tasks) '
|
|
1754
|
+
|| 'SET search_path = pg_catalog, %1$I, pg_temp',
|
|
1755
|
+
data_schema
|
|
1756
|
+
);
|
|
1757
|
+
EXECUTE format(
|
|
1758
|
+
'ALTER FUNCTION %1$I.set_scheduled_task_execution_digest() '
|
|
1759
|
+
|| 'SET search_path = pg_catalog, %1$I, pg_temp',
|
|
1760
|
+
data_schema
|
|
1761
|
+
);
|
|
1762
|
+
EXECUTE format(
|
|
1763
|
+
'ALTER FUNCTION %1$I.freeze_scheduled_task_personal_resources(uuid, uuid, uuid, bigint) '
|
|
1764
|
+
|| 'SET search_path = pg_catalog, %1$I, pg_temp',
|
|
1765
|
+
data_schema
|
|
1766
|
+
);
|
|
1767
|
+
EXECUTE format(
|
|
1768
|
+
'ALTER FUNCTION %1$I.clone_scheduled_task_personal_resource_authority('
|
|
1769
|
+
|| 'uuid, uuid, uuid, bigint, bigint) SET search_path = pg_catalog, %1$I, pg_temp',
|
|
1770
|
+
data_schema
|
|
1771
|
+
);
|
|
1772
|
+
EXECUTE format(
|
|
1773
|
+
'ALTER FUNCTION %1$I.materialize_scheduled_task_reusable_session_from_run('
|
|
1774
|
+
|| 'uuid, uuid, uuid, uuid, uuid, bigint, text) '
|
|
1775
|
+
|| 'SET search_path = pg_catalog, %1$I, pg_temp',
|
|
1776
|
+
data_schema
|
|
1777
|
+
);
|
|
1778
|
+
EXECUTE format(
|
|
1779
|
+
'ALTER FUNCTION %1$I.fence_scheduled_task_personal_resource_execution_update() '
|
|
1780
|
+
|| 'SET search_path = pg_catalog, %1$I, pg_temp',
|
|
1781
|
+
data_schema
|
|
1782
|
+
);
|
|
1783
|
+
EXECUTE format(
|
|
1784
|
+
'ALTER FUNCTION %1$I.admit_scheduled_task_run_personal_resources() '
|
|
1785
|
+
|| 'SET search_path = pg_catalog, %1$I, pg_temp',
|
|
1786
|
+
data_schema
|
|
1787
|
+
);
|
|
1788
|
+
EXECUTE format(
|
|
1789
|
+
'ALTER FUNCTION %1$I.prepare_scheduled_task_attempt_once_grants() '
|
|
1790
|
+
|| 'SET search_path = pg_catalog, %1$I, pg_temp',
|
|
1791
|
+
data_schema
|
|
1792
|
+
);
|
|
1793
|
+
EXECUTE format(
|
|
1794
|
+
'ALTER FUNCTION %1$I.validate_scheduled_task_attempt_personal_resources() '
|
|
1795
|
+
|| 'SET search_path = pg_catalog, %1$I, pg_temp',
|
|
1796
|
+
data_schema
|
|
1797
|
+
);
|
|
1798
|
+
EXECUTE format(
|
|
1799
|
+
'ALTER FUNCTION %1$I.scheduled_task_run_personal_resource_authority(uuid, uuid, uuid) '
|
|
1800
|
+
|| 'SET search_path = pg_catalog, %1$I, pg_temp',
|
|
1801
|
+
data_schema
|
|
1802
|
+
);
|
|
1803
|
+
END
|
|
1804
|
+
$scheduled_personal_resource_search_path$;
|
|
1805
|
+
|
|
1806
|
+
REVOKE ALL ON FUNCTION scheduled_task_execution_state(scheduled_tasks) FROM PUBLIC;
|
|
1807
|
+
REVOKE ALL ON FUNCTION scheduled_task_execution_digest(scheduled_tasks) FROM PUBLIC;
|
|
1808
|
+
REVOKE ALL ON FUNCTION set_scheduled_task_execution_digest() FROM PUBLIC;
|
|
1809
|
+
REVOKE ALL ON FUNCTION freeze_scheduled_task_personal_resources(uuid, uuid, uuid, bigint)
|
|
1810
|
+
FROM PUBLIC;
|
|
1811
|
+
REVOKE ALL ON FUNCTION clone_scheduled_task_personal_resource_authority(
|
|
1812
|
+
uuid, uuid, uuid, bigint, bigint
|
|
1813
|
+
) FROM PUBLIC;
|
|
1814
|
+
REVOKE ALL ON FUNCTION materialize_scheduled_task_reusable_session_from_run(
|
|
1815
|
+
uuid, uuid, uuid, uuid, uuid, bigint, text
|
|
1816
|
+
) FROM PUBLIC;
|
|
1817
|
+
REVOKE ALL ON FUNCTION fence_scheduled_task_personal_resource_execution_update() FROM PUBLIC;
|
|
1818
|
+
REVOKE ALL ON FUNCTION scheduled_task_run_personal_resource_authority(uuid, uuid, uuid)
|
|
1819
|
+
FROM PUBLIC;
|
|
1820
|
+
REVOKE ALL ON FUNCTION admit_scheduled_task_run_personal_resources() FROM PUBLIC;
|
|
1821
|
+
REVOKE ALL ON FUNCTION prepare_scheduled_task_attempt_once_grants() FROM PUBLIC;
|
|
1822
|
+
REVOKE ALL ON FUNCTION validate_scheduled_task_attempt_personal_resources() FROM PUBLIC;
|
|
1823
|
+
|
|
1824
|
+
DO $scheduled_personal_resource_runtime_grants$
|
|
1825
|
+
DECLARE
|
|
1826
|
+
table_name text;
|
|
1827
|
+
BEGIN
|
|
1828
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
1829
|
+
GRANT EXECUTE ON FUNCTION
|
|
1830
|
+
opengeni_private.scheduled_personal_resource_capability_active(text)
|
|
1831
|
+
TO opengeni_app;
|
|
1832
|
+
GRANT EXECUTE ON FUNCTION
|
|
1833
|
+
freeze_scheduled_task_personal_resources(uuid, uuid, uuid, bigint)
|
|
1834
|
+
TO opengeni_app;
|
|
1835
|
+
GRANT EXECUTE ON FUNCTION
|
|
1836
|
+
clone_scheduled_task_personal_resource_authority(uuid, uuid, uuid, bigint, bigint)
|
|
1837
|
+
TO opengeni_app;
|
|
1838
|
+
GRANT EXECUTE ON FUNCTION
|
|
1839
|
+
materialize_scheduled_task_reusable_session_from_run(
|
|
1840
|
+
uuid, uuid, uuid, uuid, uuid, bigint, text
|
|
1841
|
+
) TO opengeni_app;
|
|
1842
|
+
GRANT EXECUTE ON FUNCTION
|
|
1843
|
+
scheduled_task_run_personal_resource_authority(uuid, uuid, uuid)
|
|
1844
|
+
TO opengeni_app;
|
|
1845
|
+
FOREACH table_name IN ARRAY ARRAY[
|
|
1846
|
+
'scheduled_task_personal_resource_authorities',
|
|
1847
|
+
'scheduled_task_personal_resource_snapshots',
|
|
1848
|
+
'scheduled_task_run_personal_resource_admissions',
|
|
1849
|
+
'scheduled_task_run_personal_resource_snapshots',
|
|
1850
|
+
'scheduled_task_run_personal_resource_once_receipts'
|
|
1851
|
+
] LOOP
|
|
1852
|
+
EXECUTE format('REVOKE ALL ON TABLE %I FROM opengeni_app', table_name);
|
|
1853
|
+
END LOOP;
|
|
1854
|
+
END IF;
|
|
1855
|
+
END
|
|
1856
|
+
$scheduled_personal_resource_runtime_grants$;
|
|
1857
|
+
|
|
1858
|
+
COMMENT ON TABLE "scheduled_task_personal_resource_authorities" IS
|
|
1859
|
+
'Immutable causal-human and membership header for one scheduled-task authority revision.';
|
|
1860
|
+
COMMENT ON TABLE "scheduled_task_personal_resource_snapshots" IS
|
|
1861
|
+
'Server-derived exact personal Variable Set/Rig authority frozen at scheduled-task write.';
|
|
1862
|
+
COMMENT ON TABLE "scheduled_task_run_personal_resource_admissions" IS
|
|
1863
|
+
'Accepted occurrence binding to one immutable task authority revision before dispatch.';
|
|
1864
|
+
COMMENT ON TABLE "scheduled_task_run_personal_resource_snapshots" IS
|
|
1865
|
+
'Exact occurrence copy used to fence queued/recovering scheduled work against widening.';
|
|
1866
|
+
COMMENT ON TABLE "scheduled_task_run_personal_resource_once_receipts" IS
|
|
1867
|
+
'Unique once-grant ownership for one admitted scheduled occurrence across attempt retries.';
|