feat: add direct PostgreSQL and ACK deployment support
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
-- Compatibility snapshot for existing Supabase deployments.
|
||||
-- New PostgreSQL/RDS deployments must use `npm run db:migrate`; do not use this
|
||||
-- file as an unversioned migration source.
|
||||
|
||||
create table if not exists assets (
|
||||
id text primary key,
|
||||
owner_id text not null,
|
||||
@@ -89,6 +93,8 @@ alter table usage_events add column if not exists account_display_name text;
|
||||
alter table usage_events add column if not exists tenant_id text;
|
||||
alter table usage_events add column if not exists organization_id text;
|
||||
alter table usage_events add column if not exists organization_name text;
|
||||
alter table usage_events add column if not exists quantity integer not null default 1;
|
||||
alter table usage_events add column if not exists estimated_unit text not null default 'job';
|
||||
|
||||
update usage_events as usage
|
||||
set source = case
|
||||
@@ -113,11 +119,6 @@ alter table usage_events alter column source set not null;
|
||||
alter table usage_events alter column estimated_unit set default 'job';
|
||||
alter table usage_events drop constraint if exists usage_events_job_id_fkey;
|
||||
|
||||
delete from usage_events as later
|
||||
using usage_events as earlier
|
||||
where later.job_id = earlier.job_id
|
||||
and (later.created_at, later.id) > (earlier.created_at, earlier.id);
|
||||
|
||||
create table if not exists projects (
|
||||
id text primary key,
|
||||
owner_id text not null,
|
||||
@@ -151,6 +152,20 @@ create unique index if not exists generation_jobs_idempotency_idx
|
||||
on generation_jobs(owner_id, external_client_id, idempotency_key)
|
||||
where external_client_id is not null and idempotency_key is not null;
|
||||
create index if not exists usage_events_owner_created_idx on usage_events(owner_id, created_at desc);
|
||||
do $$
|
||||
begin
|
||||
if exists (
|
||||
select 1
|
||||
from usage_events
|
||||
group by job_id
|
||||
having count(*) > 1
|
||||
) then
|
||||
raise exception using
|
||||
errcode = '23505',
|
||||
message = 'USAGE_EVENTS_DUPLICATE_JOB_ID: back up and clean duplicate usage_events.job_id rows before retrying this migration';
|
||||
end if;
|
||||
end;
|
||||
$$;
|
||||
create unique index if not exists usage_events_job_id_idx on usage_events(job_id);
|
||||
create index if not exists usage_events_source_created_idx on usage_events(source, created_at desc);
|
||||
create index if not exists usage_events_organization_created_idx on usage_events(organization_id, created_at desc);
|
||||
@@ -163,6 +178,7 @@ create or replace function claim_generation_jobs(
|
||||
)
|
||||
returns setof generation_jobs
|
||||
language plpgsql
|
||||
set search_path = public, pg_temp
|
||||
as $$
|
||||
declare
|
||||
v_now timestamptz := now();
|
||||
@@ -194,6 +210,8 @@ begin
|
||||
end;
|
||||
$$;
|
||||
|
||||
revoke all on function claim_generation_jobs(text, integer, integer) from public;
|
||||
|
||||
create table if not exists platform_organizations (
|
||||
id text primary key,
|
||||
name text not null unique,
|
||||
@@ -288,12 +306,61 @@ create table if not exists billing_ledger (
|
||||
delta_fen bigint not null check (delta_fen <> 0),
|
||||
balance_after_fen bigint not null check (balance_after_fen >= 0),
|
||||
currency text not null default 'CNY' check (currency = 'CNY'),
|
||||
idempotency_key text not null unique,
|
||||
idempotency_key text not null,
|
||||
description text not null,
|
||||
metadata jsonb not null default '{}'::jsonb,
|
||||
created_at timestamptz not null default now()
|
||||
);
|
||||
|
||||
do $$
|
||||
declare
|
||||
v_constraint record;
|
||||
v_index record;
|
||||
begin
|
||||
for v_constraint in
|
||||
select constraint_schema, constraint_name
|
||||
from information_schema.table_constraints
|
||||
where table_schema = current_schema()
|
||||
and table_name = 'billing_ledger'
|
||||
and constraint_type = 'UNIQUE'
|
||||
and array(
|
||||
select key_column_usage.column_name::text
|
||||
from information_schema.key_column_usage
|
||||
where key_column_usage.constraint_schema = table_constraints.constraint_schema
|
||||
and key_column_usage.constraint_name = table_constraints.constraint_name
|
||||
and key_column_usage.table_name = table_constraints.table_name
|
||||
order by key_column_usage.ordinal_position
|
||||
) = array['idempotency_key']::text[]
|
||||
loop
|
||||
execute format('alter table %I.%I drop constraint %I', current_schema(), 'billing_ledger', v_constraint.constraint_name);
|
||||
end loop;
|
||||
|
||||
for v_index in
|
||||
select indexes.schemaname, indexes.indexname
|
||||
from pg_indexes as indexes
|
||||
join pg_class as index_class on index_class.relname = indexes.indexname
|
||||
join pg_namespace as index_namespace
|
||||
on index_namespace.oid = index_class.relnamespace
|
||||
and index_namespace.nspname = indexes.schemaname
|
||||
join pg_index as index_metadata on index_metadata.indexrelid = index_class.oid
|
||||
join pg_attribute as indexed_column
|
||||
on indexed_column.attrelid = index_metadata.indrelid
|
||||
and indexed_column.attnum = index_metadata.indkey[0]
|
||||
left join pg_constraint as backing_constraint on backing_constraint.conindid = index_class.oid
|
||||
where indexes.schemaname = current_schema()
|
||||
and indexes.tablename = 'billing_ledger'
|
||||
and index_metadata.indisunique
|
||||
and index_metadata.indnkeyatts = 1
|
||||
and indexed_column.attname = 'idempotency_key'
|
||||
and backing_constraint.oid is null
|
||||
loop
|
||||
execute format('drop index %I.%I', v_index.schemaname, v_index.indexname);
|
||||
end loop;
|
||||
end;
|
||||
$$;
|
||||
|
||||
create unique index if not exists billing_ledger_organization_idempotency_idx
|
||||
on billing_ledger(organization_id, idempotency_key);
|
||||
create index if not exists billing_ledger_organization_created_idx on billing_ledger(organization_id, created_at desc);
|
||||
create index if not exists billing_ledger_account_created_idx on billing_ledger(account_id, created_at desc);
|
||||
create index if not exists billing_ledger_job_idx on billing_ledger(job_id);
|
||||
@@ -321,11 +388,13 @@ returns table (
|
||||
delta_fen bigint
|
||||
)
|
||||
language plpgsql
|
||||
set search_path = public, pg_temp
|
||||
as $$
|
||||
declare
|
||||
v_existing billing_ledger%rowtype;
|
||||
v_wallet billing_wallets%rowtype;
|
||||
v_entry billing_ledger%rowtype;
|
||||
v_account_id text;
|
||||
begin
|
||||
if p_currency <> 'CNY' then
|
||||
raise exception 'BILLING_UNSUPPORTED_CURRENCY';
|
||||
@@ -334,9 +403,29 @@ begin
|
||||
raise exception 'BILLING_ZERO_DELTA';
|
||||
end if;
|
||||
|
||||
select * into v_existing from billing_ledger where idempotency_key = p_idempotency_key;
|
||||
v_account_id := case when p_kind in ('recharge', 'adjustment') then null else p_account_id end;
|
||||
|
||||
perform pg_advisory_xact_lock(
|
||||
hashtextextended(jsonb_build_array(p_organization_id, p_idempotency_key)::text, 0)
|
||||
);
|
||||
|
||||
select * into v_existing
|
||||
from billing_ledger
|
||||
where organization_id = p_organization_id
|
||||
and idempotency_key = p_idempotency_key;
|
||||
if found then
|
||||
select * into v_wallet from billing_wallets where organization_id = v_existing.organization_id;
|
||||
if v_existing.account_id is distinct from v_account_id
|
||||
or v_existing.job_id is distinct from p_job_id
|
||||
or v_existing.kind is distinct from p_kind
|
||||
or v_existing.delta_fen is distinct from p_delta_fen
|
||||
or v_existing.currency is distinct from p_currency
|
||||
then
|
||||
raise exception using
|
||||
errcode = 'P0001',
|
||||
message = 'BILLING_IDEMPOTENCY_PAYLOAD_MISMATCH';
|
||||
end if;
|
||||
|
||||
select * into v_wallet from billing_wallets where organization_id = p_organization_id;
|
||||
return query select v_existing.id, v_existing.balance_after_fen, v_wallet.balance_fen,
|
||||
v_wallet.total_recharged_fen, v_wallet.total_charged_fen, v_existing.created_at,
|
||||
v_wallet.updated_at, v_existing.delta_fen;
|
||||
@@ -368,7 +457,7 @@ begin
|
||||
balance_after_fen, currency, idempotency_key, description, metadata
|
||||
) values (
|
||||
p_ledger_id, p_organization_id,
|
||||
case when p_kind in ('recharge', 'adjustment') then null else p_account_id end,
|
||||
v_account_id,
|
||||
p_job_id, p_kind, p_delta_fen,
|
||||
v_wallet.balance_fen, p_currency, p_idempotency_key, p_description, coalesce(p_metadata, '{}'::jsonb)
|
||||
) returning * into v_entry;
|
||||
@@ -378,3 +467,5 @@ begin
|
||||
v_wallet.updated_at, v_entry.delta_fen;
|
||||
end;
|
||||
$$;
|
||||
|
||||
revoke all on function billing_post_wallet_entry(text, text, text, text, text, bigint, text, text, text, jsonb) from public;
|
||||
|
||||
Reference in New Issue
Block a user