xtablo-source/supabase/migrations/20251105074514_remote_schema.sql
2025-11-06 08:38:38 +01:00

7482 lines
115 KiB
PL/PgSQL

SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;
CREATE EXTENSION IF NOT EXISTS "pg_cron" WITH SCHEMA "pg_catalog";
CREATE SCHEMA IF NOT EXISTS "graphile_worker";
ALTER SCHEMA "graphile_worker" OWNER TO "postgres";
CREATE EXTENSION IF NOT EXISTS "pgsodium";
COMMENT ON SCHEMA "public" IS 'standard public schema';
CREATE SCHEMA IF NOT EXISTS "stripe";
ALTER SCHEMA "stripe" OWNER TO "postgres";
CREATE EXTENSION IF NOT EXISTS "pg_graphql" WITH SCHEMA "graphql";
CREATE EXTENSION IF NOT EXISTS "pg_stat_statements" WITH SCHEMA "extensions";
CREATE EXTENSION IF NOT EXISTS "pgcrypto" WITH SCHEMA "extensions";
CREATE EXTENSION IF NOT EXISTS "pgjwt" WITH SCHEMA "extensions";
CREATE EXTENSION IF NOT EXISTS "pgtap" WITH SCHEMA "extensions";
CREATE EXTENSION IF NOT EXISTS "supabase_vault" WITH SCHEMA "vault";
CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA "extensions";
CREATE EXTENSION IF NOT EXISTS "wrappers" WITH SCHEMA "extensions";
CREATE TYPE "graphile_worker"."job_spec" AS (
"identifier" "text",
"payload" "json",
"queue_name" "text",
"run_at" timestamp with time zone,
"max_attempts" smallint,
"job_key" "text",
"priority" smallint,
"flags" "text"[]
);
ALTER TYPE "graphile_worker"."job_spec" OWNER TO "postgres";
CREATE TYPE "public"."devis_status" AS ENUM (
'draft',
'sent',
'accepted',
'rejected',
'expired'
);
ALTER TYPE "public"."devis_status" OWNER TO "postgres";
CREATE TYPE "public"."subscription_plan" AS ENUM (
'none',
'trial',
'standard'
);
ALTER TYPE "public"."subscription_plan" OWNER TO "postgres";
CREATE TYPE "public"."time_range" AS (
"start_time" time with time zone,
"end_time" time with time zone
);
ALTER TYPE "public"."time_range" OWNER TO "postgres";
CREATE TYPE "stripe"."invoice_status" AS ENUM (
'draft',
'open',
'paid',
'uncollectible',
'void',
'deleted'
);
ALTER TYPE "stripe"."invoice_status" OWNER TO "postgres";
CREATE TYPE "stripe"."pricing_tiers" AS ENUM (
'graduated',
'volume'
);
ALTER TYPE "stripe"."pricing_tiers" OWNER TO "postgres";
CREATE TYPE "stripe"."pricing_type" AS ENUM (
'one_time',
'recurring'
);
ALTER TYPE "stripe"."pricing_type" OWNER TO "postgres";
CREATE TYPE "stripe"."subscription_schedule_status" AS ENUM (
'not_started',
'active',
'completed',
'released',
'canceled'
);
ALTER TYPE "stripe"."subscription_schedule_status" OWNER TO "postgres";
CREATE TYPE "stripe"."subscription_status" AS ENUM (
'trialing',
'active',
'canceled',
'incomplete',
'incomplete_expired',
'past_due',
'unpaid',
'paused'
);
ALTER TYPE "stripe"."subscription_status" OWNER TO "postgres";
SET default_tablespace = '';
SET default_table_access_method = "heap";
CREATE TABLE IF NOT EXISTS "graphile_worker"."_private_jobs" (
"id" bigint NOT NULL,
"job_queue_id" integer,
"task_id" integer NOT NULL,
"payload" "json" DEFAULT '{}'::"json" NOT NULL,
"priority" smallint DEFAULT 0 NOT NULL,
"run_at" timestamp with time zone DEFAULT "now"() NOT NULL,
"attempts" smallint DEFAULT 0 NOT NULL,
"max_attempts" smallint DEFAULT 25 NOT NULL,
"last_error" "text",
"created_at" timestamp with time zone DEFAULT "now"() NOT NULL,
"updated_at" timestamp with time zone DEFAULT "now"() NOT NULL,
"key" "text",
"locked_at" timestamp with time zone,
"locked_by" "text",
"revision" integer DEFAULT 0 NOT NULL,
"flags" "jsonb",
"is_available" boolean GENERATED ALWAYS AS ((("locked_at" IS NULL) AND ("attempts" < "max_attempts"))) STORED NOT NULL,
CONSTRAINT "jobs_key_check" CHECK ((("length"("key") > 0) AND ("length"("key") <= 512))),
CONSTRAINT "jobs_max_attempts_check" CHECK (("max_attempts" >= 1))
);
ALTER TABLE "graphile_worker"."_private_jobs" OWNER TO "postgres";
CREATE OR REPLACE FUNCTION "graphile_worker"."add_job"("identifier" "text", "payload" "json" DEFAULT NULL::"json", "queue_name" "text" DEFAULT NULL::"text", "run_at" timestamp with time zone DEFAULT NULL::timestamp with time zone, "max_attempts" integer DEFAULT NULL::integer, "job_key" "text" DEFAULT NULL::"text", "priority" integer DEFAULT NULL::integer, "flags" "text"[] DEFAULT NULL::"text"[], "job_key_mode" "text" DEFAULT 'replace'::"text") RETURNS "graphile_worker"."_private_jobs"
LANGUAGE "plpgsql"
AS $$
declare
v_job "graphile_worker"._private_jobs;
begin
if (job_key is null or job_key_mode is null or job_key_mode in ('replace', 'preserve_run_at')) then
select * into v_job
from "graphile_worker".add_jobs(
ARRAY[(
identifier,
payload,
queue_name,
run_at,
max_attempts::smallint,
job_key,
priority::smallint,
flags
)::"graphile_worker".job_spec],
(job_key_mode = 'preserve_run_at')
)
limit 1;
return v_job;
elsif job_key_mode = 'unsafe_dedupe' then
-- Ensure all the tasks exist
insert into "graphile_worker"._private_tasks as tasks (identifier)
values (add_job.identifier)
on conflict do nothing;
-- Ensure all the queues exist
if add_job.queue_name is not null then
insert into "graphile_worker"._private_job_queues as job_queues (queue_name)
values (add_job.queue_name)
on conflict do nothing;
end if;
-- Insert job, but if one already exists then do nothing, even if the
-- existing job has already started (and thus represents an out-of-date
-- world state). This is dangerous because it means that whatever state
-- change triggered this add_job may not be acted upon (since it happened
-- after the existing job started executing, but no further job is being
-- scheduled), but it is useful in very rare circumstances for
-- de-duplication. If in doubt, DO NOT USE THIS.
insert into "graphile_worker"._private_jobs as jobs (
job_queue_id,
task_id,
payload,
run_at,
max_attempts,
key,
priority,
flags
)
select
job_queues.id,
tasks.id,
coalesce(add_job.payload, '{}'::json),
coalesce(add_job.run_at, now()),
coalesce(add_job.max_attempts::smallint, 25::smallint),
add_job.job_key,
coalesce(add_job.priority::smallint, 0::smallint),
(
select jsonb_object_agg(flag, true)
from unnest(add_job.flags) as item(flag)
)
from "graphile_worker"._private_tasks as tasks
left join "graphile_worker"._private_job_queues as job_queues
on job_queues.queue_name = add_job.queue_name
where tasks.identifier = add_job.identifier
on conflict (key)
-- Bump the updated_at so that there's something to return
do update set
revision = jobs.revision + 1,
updated_at = now()
returning *
into v_job;
if v_job.revision = 0 then
perform pg_notify('jobs:insert', '{"r":' || random()::text || ',"count":1}');
end if;
return v_job;
else
raise exception 'Invalid job_key_mode value, expected ''replace'', ''preserve_run_at'' or ''unsafe_dedupe''.' using errcode = 'GWBKM';
end if;
end;
$$;
ALTER FUNCTION "graphile_worker"."add_job"("identifier" "text", "payload" "json", "queue_name" "text", "run_at" timestamp with time zone, "max_attempts" integer, "job_key" "text", "priority" integer, "flags" "text"[], "job_key_mode" "text") OWNER TO "postgres";
CREATE OR REPLACE FUNCTION "graphile_worker"."add_jobs"("specs" "graphile_worker"."job_spec"[], "job_key_preserve_run_at" boolean DEFAULT false) RETURNS SETOF "graphile_worker"."_private_jobs"
LANGUAGE "plpgsql"
AS $$
begin
-- Ensure all the tasks exist
insert into "graphile_worker"._private_tasks as tasks (identifier)
select distinct spec.identifier
from unnest(specs) spec
on conflict do nothing;
-- Ensure all the queues exist
insert into "graphile_worker"._private_job_queues as job_queues (queue_name)
select distinct spec.queue_name
from unnest(specs) spec
where spec.queue_name is not null
on conflict do nothing;
-- Ensure any locked jobs have their key cleared - in the case of locked
-- existing job create a new job instead as it must have already started
-- executing (i.e. it's world state is out of date, and the fact add_job
-- has been called again implies there's new information that needs to be
-- acted upon).
update "graphile_worker"._private_jobs as jobs
set
key = null,
attempts = jobs.max_attempts,
updated_at = now()
from unnest(specs) spec
where spec.job_key is not null
and jobs.key = spec.job_key
and is_available is not true;
-- WARNING: this count is not 100% accurate; 'on conflict' clause will cause it to be an overestimate
perform pg_notify('jobs:insert', '{"r":' || random()::text || ',"count":' || array_length(specs, 1)::text || '}');
-- TODO: is there a risk that a conflict could occur depending on the
-- isolation level?
return query insert into "graphile_worker"._private_jobs as jobs (
job_queue_id,
task_id,
payload,
run_at,
max_attempts,
key,
priority,
flags
)
select
job_queues.id,
tasks.id,
coalesce(spec.payload, '{}'::json),
coalesce(spec.run_at, now()),
coalesce(spec.max_attempts, 25),
spec.job_key,
coalesce(spec.priority, 0),
(
select jsonb_object_agg(flag, true)
from unnest(spec.flags) as item(flag)
)
from unnest(specs) spec
inner join "graphile_worker"._private_tasks as tasks
on tasks.identifier = spec.identifier
left join "graphile_worker"._private_job_queues as job_queues
on job_queues.queue_name = spec.queue_name
on conflict (key) do update set
job_queue_id = excluded.job_queue_id,
task_id = excluded.task_id,
payload =
case
when json_typeof(jobs.payload) = 'array' and json_typeof(excluded.payload) = 'array' then
(jobs.payload::jsonb || excluded.payload::jsonb)::json
else
excluded.payload
end,
max_attempts = excluded.max_attempts,
run_at = (case
when job_key_preserve_run_at is true and jobs.attempts = 0 then jobs.run_at
else excluded.run_at
end),
priority = excluded.priority,
revision = jobs.revision + 1,
flags = excluded.flags,
-- always reset error/retry state
attempts = 0,
last_error = null,
updated_at = now()
where jobs.locked_at is null
returning *;
end;
$$;
ALTER FUNCTION "graphile_worker"."add_jobs"("specs" "graphile_worker"."job_spec"[], "job_key_preserve_run_at" boolean) OWNER TO "postgres";
CREATE OR REPLACE FUNCTION "graphile_worker"."complete_jobs"("job_ids" bigint[]) RETURNS SETOF "graphile_worker"."_private_jobs"
LANGUAGE "sql"
AS $$
delete from "graphile_worker"._private_jobs as jobs
where id = any(job_ids)
and (
locked_at is null
or
locked_at < now() - interval '4 hours'
)
returning *;
$$;
ALTER FUNCTION "graphile_worker"."complete_jobs"("job_ids" bigint[]) OWNER TO "postgres";
CREATE OR REPLACE FUNCTION "graphile_worker"."force_unlock_workers"("worker_ids" "text"[]) RETURNS "void"
LANGUAGE "sql"
AS $$
update "graphile_worker"._private_jobs as jobs
set locked_at = null, locked_by = null
where locked_by = any(worker_ids);
update "graphile_worker"._private_job_queues as job_queues
set locked_at = null, locked_by = null
where locked_by = any(worker_ids);
$$;
ALTER FUNCTION "graphile_worker"."force_unlock_workers"("worker_ids" "text"[]) OWNER TO "postgres";
CREATE OR REPLACE FUNCTION "graphile_worker"."permanently_fail_jobs"("job_ids" bigint[], "error_message" "text" DEFAULT NULL::"text") RETURNS SETOF "graphile_worker"."_private_jobs"
LANGUAGE "sql"
AS $$
update "graphile_worker"._private_jobs as jobs
set
last_error = coalesce(error_message, 'Manually marked as failed'),
attempts = max_attempts,
updated_at = now()
where id = any(job_ids)
and (
locked_at is null
or
locked_at < NOW() - interval '4 hours'
)
returning *;
$$;
ALTER FUNCTION "graphile_worker"."permanently_fail_jobs"("job_ids" bigint[], "error_message" "text") OWNER TO "postgres";
CREATE OR REPLACE FUNCTION "graphile_worker"."remove_job"("job_key" "text") RETURNS "graphile_worker"."_private_jobs"
LANGUAGE "plpgsql" STRICT
AS $$
declare
v_job "graphile_worker"._private_jobs;
begin
-- Delete job if not locked
delete from "graphile_worker"._private_jobs as jobs
where key = job_key
and (
locked_at is null
or
locked_at < NOW() - interval '4 hours'
)
returning * into v_job;
if not (v_job is null) then
perform pg_notify('jobs:insert', '{"r":' || random()::text || ',"count":-1}');
return v_job;
end if;
-- Otherwise prevent job from retrying, and clear the key
update "graphile_worker"._private_jobs as jobs
set
key = null,
attempts = jobs.max_attempts,
updated_at = now()
where key = job_key
returning * into v_job;
return v_job;
end;
$$;
ALTER FUNCTION "graphile_worker"."remove_job"("job_key" "text") OWNER TO "postgres";
CREATE OR REPLACE FUNCTION "graphile_worker"."reschedule_jobs"("job_ids" bigint[], "run_at" timestamp with time zone DEFAULT NULL::timestamp with time zone, "priority" integer DEFAULT NULL::integer, "attempts" integer DEFAULT NULL::integer, "max_attempts" integer DEFAULT NULL::integer) RETURNS SETOF "graphile_worker"."_private_jobs"
LANGUAGE "sql"
AS $$
update "graphile_worker"._private_jobs as jobs
set
run_at = coalesce(reschedule_jobs.run_at, jobs.run_at),
priority = coalesce(reschedule_jobs.priority::smallint, jobs.priority),
attempts = coalesce(reschedule_jobs.attempts::smallint, jobs.attempts),
max_attempts = coalesce(reschedule_jobs.max_attempts::smallint, jobs.max_attempts),
updated_at = now()
where id = any(job_ids)
and (
locked_at is null
or
locked_at < NOW() - interval '4 hours'
)
returning *;
$$;
ALTER FUNCTION "graphile_worker"."reschedule_jobs"("job_ids" bigint[], "run_at" timestamp with time zone, "priority" integer, "attempts" integer, "max_attempts" integer) OWNER TO "postgres";
CREATE OR REPLACE FUNCTION "public"."create_last_signed_in_on_profiles"() RETURNS "trigger"
LANGUAGE "plpgsql" SECURITY DEFINER
AS $$
begin
IF (NEW.last_sign_in_at is null) THEN
RETURN NULL;
ELSE
UPDATE public.profiles
SET last_signed_in = NEW.last_sign_in_at
WHERE id = (NEW.id)::uuid;
RETURN NEW;
END IF;
END;
$$;
ALTER FUNCTION "public"."create_last_signed_in_on_profiles"() OWNER TO "postgres";
CREATE OR REPLACE FUNCTION "public"."create_tablo_access_for_owner"() RETURNS "trigger"
LANGUAGE "plpgsql" SECURITY DEFINER
AS $$
BEGIN
-- Insert a tablo_access record for the tablo owner
INSERT INTO tablo_access (
tablo_id,
user_id,
granted_by,
is_active,
is_admin
) VALUES (
NEW.id, -- tablo_id: the newly created tablo's id (now TEXT)
NEW.owner_id, -- user_id: the tablo owner gets access
NEW.owner_id, -- granted_by: self-granted by the owner
TRUE, -- is_active: access is active
TRUE -- is_admin: owner has admin privileges
);
RETURN NEW;
END;
$$;
ALTER FUNCTION "public"."create_tablo_access_for_owner"() OWNER TO "postgres";
CREATE OR REPLACE FUNCTION "public"."generate_random_string"("length" integer DEFAULT 24) RETURNS "text"
LANGUAGE "plpgsql"
AS $$
DECLARE
chars TEXT := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
result TEXT := '';
i INTEGER := 0;
BEGIN
FOR i IN 1..length LOOP
result := result || substr(chars, floor(random() * length(chars) + 1)::INTEGER, 1);
END LOOP;
RETURN result;
END;
$$;
ALTER FUNCTION "public"."generate_random_string"("length" integer) OWNER TO "postgres";
COMMENT ON FUNCTION "public"."generate_random_string"("length" integer) IS 'Generates a random alphanumeric string of specified length (default 24 characters)';
CREATE OR REPLACE FUNCTION "public"."get_my_active_subscription"() RETURNS TABLE("subscription_id" "text", "user_id" "uuid", "user_email" "text", "first_name" "text", "last_name" "text", "status" "text", "current_period_start" timestamp with time zone, "current_period_end" timestamp with time zone, "cancel_at_period_end" boolean, "product_name" "text", "currency" "text", "unit_amount" integer, "billing_interval" "text", "plan" "public"."subscription_plan")
LANGUAGE "plpgsql" SECURITY DEFINER
SET "search_path" TO 'public', 'stripe'
AS $$
begin
-- Only return data for the authenticated user
return query
select
s.id as subscription_id,
(c.metadata->>'user_id')::uuid as user_id,
p.email as user_email,
p.first_name,
p.last_name,
s.status::text,
to_timestamp(si.current_period_start) as current_period_start,
to_timestamp(si.current_period_end) as current_period_end,
s.cancel_at_period_end,
pr.name as product_name,
pc.currency,
pc.unit_amount,
pc.recurring->>'interval' as billing_interval,
p.plan
from stripe.subscriptions s
inner join stripe.customers c on c.id = s.customer
inner join stripe.subscription_items si on si.subscription = s.id
inner join public.profiles p on p.id = (c.metadata->>'user_id')::uuid
left join stripe.prices pc on pc.id = si.price
left join stripe.products pr on pr.id = pc.product
where (c.metadata->>'user_id')::uuid = auth.uid() -- Filter by authenticated user only!
and s.status::text in ('active', 'trialing')
and si.current_period_end is not null
and to_timestamp(si.current_period_end) > now()
order by si.current_period_end desc
limit 1;
end;
$$;
ALTER FUNCTION "public"."get_my_active_subscription"() OWNER TO "postgres";
COMMENT ON FUNCTION "public"."get_my_active_subscription"() IS 'Returns the current authenticated user''s active subscription (secure, RLS-compliant)';
CREATE OR REPLACE FUNCTION "public"."get_stripe_prices"() RETURNS TABLE("id" "text", "product" "text", "active" boolean, "currency" "text", "unit_amount" integer, "recurring" "jsonb", "created" integer, "metadata" "jsonb")
LANGUAGE "plpgsql" SECURITY DEFINER
AS $$
begin
return query
select
pr.id,
pr.product,
pr.active,
pr.currency,
pr.unit_amount,
pr.recurring,
pr.created,
pr.metadata
from stripe.prices pr
where pr.active = true;
end;
$$;
ALTER FUNCTION "public"."get_stripe_prices"() OWNER TO "postgres";
COMMENT ON FUNCTION "public"."get_stripe_prices"() IS 'Returns all active Stripe prices (public access)';
CREATE OR REPLACE FUNCTION "public"."get_stripe_products"() RETURNS TABLE("id" "text", "name" "text", "description" "text", "active" boolean, "created" integer, "metadata" "jsonb")
LANGUAGE "plpgsql" SECURITY DEFINER
AS $$
begin
return query
select
p.id,
p.name,
p.description,
p.active,
p.created,
p.metadata
from stripe.products p
where p.active = true;
end;
$$;
ALTER FUNCTION "public"."get_stripe_products"() OWNER TO "postgres";
COMMENT ON FUNCTION "public"."get_stripe_products"() IS 'Returns all active Stripe products (public access)';
CREATE OR REPLACE FUNCTION "public"."get_user_stripe_customer"() RETURNS TABLE("id" "text", "email" "text", "user_id" "uuid", "created" integer, "metadata" "jsonb")
LANGUAGE "plpgsql" SECURITY DEFINER
AS $$
begin
return query
select
c.id,
c.email,
(c.metadata->>'user_id')::uuid as user_id,
c.created,
c.metadata
from stripe.customers c
where (c.metadata->>'user_id')::uuid = auth.uid();
end;
$$;
ALTER FUNCTION "public"."get_user_stripe_customer"() OWNER TO "postgres";
COMMENT ON FUNCTION "public"."get_user_stripe_customer"() IS 'Returns current user''s customer record from Stripe';
CREATE OR REPLACE FUNCTION "public"."get_user_stripe_customer_id"("user_uuid" "uuid") RETURNS "text"
LANGUAGE "plpgsql" SECURITY DEFINER
AS $$
declare
customer_id text;
begin
select id into customer_id
from stripe.customers
where (metadata->>'user_id')::uuid = user_uuid
limit 1;
return customer_id;
end;
$$;
ALTER FUNCTION "public"."get_user_stripe_customer_id"("user_uuid" "uuid") OWNER TO "postgres";
COMMENT ON FUNCTION "public"."get_user_stripe_customer_id"("user_uuid" "uuid") IS 'Returns the Stripe customer ID for a user';
CREATE OR REPLACE FUNCTION "public"."get_user_stripe_subscriptions"() RETURNS TABLE("id" "text", "customer" "text", "user_id" "uuid", "status" "text", "cancel_at_period_end" boolean, "current_period_start" integer, "current_period_end" integer, "created" integer, "canceled_at" integer, "trial_start" "jsonb", "trial_end" "jsonb", "price_id" "text", "quantity" integer, "metadata" "jsonb")
LANGUAGE "plpgsql" SECURITY DEFINER
AS $$
begin
return query
select
s.id,
s.customer,
(c.metadata->>'user_id')::uuid as user_id,
s.status::text,
s.cancel_at_period_end,
si.current_period_start,
si.current_period_end,
s.created,
s.canceled_at,
s.trial_start,
s.trial_end,
si.price as price_id,
si.quantity,
s.metadata
from stripe.subscriptions s
inner join stripe.customers c on c.id = s.customer
left join stripe.subscription_items si on si.subscription = s.id
where (c.metadata->>'user_id')::uuid = auth.uid()
order by s.created desc;
end;
$$;
ALTER FUNCTION "public"."get_user_stripe_subscriptions"() OWNER TO "postgres";
CREATE OR REPLACE FUNCTION "public"."get_user_subscription_status"("user_uuid" "uuid") RETURNS TABLE("subscription_id" "text", "status" "text", "current_period_start" integer, "current_period_end" integer, "cancel_at_period_end" boolean, "price_id" "text", "product_name" "text", "plan" "public"."subscription_plan")
LANGUAGE "plpgsql" SECURITY DEFINER
AS $$
begin
return query
select
s.id,
s.status::text,
si.current_period_start,
si.current_period_end,
s.cancel_at_period_end,
si.price as price_id,
p.name as product_name,
case
when s.status::text = 'trialing' then 'trial'::subscription_plan
when s.status::text in ('active', 'past_due') then 'standard'::subscription_plan
else 'none'::subscription_plan
end as plan
from stripe.subscriptions s
inner join stripe.customers c on c.id = s.customer
inner join stripe.subscription_items si on si.subscription = s.id
left join stripe.prices pr on pr.id = si.price
left join stripe.products p on p.id = pr.product
where (c.metadata->>'user_id')::uuid = user_uuid
and s.status::text in ('active', 'trialing', 'past_due')
and si.current_period_end is not null
order by si.current_period_end desc
limit 1;
end;
$$;
ALTER FUNCTION "public"."get_user_subscription_status"("user_uuid" "uuid") OWNER TO "postgres";
COMMENT ON FUNCTION "public"."get_user_subscription_status"("user_uuid" "uuid") IS 'Returns current subscription details using subscription_items for accurate period dates';
CREATE OR REPLACE FUNCTION "public"."handle_event_types_standard_name"() RETURNS "trigger"
LANGUAGE "plpgsql"
AS $$
BEGIN
-- On INSERT: automatically set standard_name from config->>'name', sanitized
IF TG_OP = 'INSERT' THEN
-- Extract name from config and sanitize it (replace spaces with hyphens, lowercase)
NEW.standard_name = LOWER(REPLACE(TRIM(NEW.config->>'name'), ' ', '-'));
RETURN NEW;
END IF;
-- On UPDATE: prevent standard_name modification by authenticated users
IF TG_OP = 'UPDATE' THEN
-- Only allow system/service role to modify standard_name
-- If the current user is authenticated (not service_role), prevent standard_name changes
IF current_setting('role') != 'service_role' AND OLD.standard_name IS DISTINCT FROM NEW.standard_name THEN RAISE EXCEPTION 'standard_name column cannot be modified'; END IF;
-- If name in config changes, update standard_name accordingly (but only for non-authenticated users)
IF current_setting('role') = 'service_role' AND OLD.config->>'name' IS DISTINCT FROM NEW.config->>'name' THEN
NEW.standard_name = LOWER(REPLACE(TRIM(NEW.config->>'name'), ' ', '-'));
END IF;
END IF;
RETURN NEW;
END;
$$;
ALTER FUNCTION "public"."handle_event_types_standard_name"() OWNER TO "postgres";
CREATE OR REPLACE FUNCTION "public"."handle_new_user"() RETURNS "trigger"
LANGUAGE "plpgsql" SECURITY DEFINER
AS $$
DECLARE
name TEXT;
first_name TEXT;
last_name TEXT;
is_temp BOOLEAN;
email_prefix TEXT;
BEGIN
-- Extract first_name and last_name from metadata
first_name = new.raw_user_meta_data ->> 'first_name';
last_name = new.raw_user_meta_data ->> 'last_name';
-- If first_name is not provided, extract it from email (part before @)
IF first_name IS NULL OR first_name = '' THEN
email_prefix = SPLIT_PART(new.email, '@', 1);
first_name = email_prefix;
END IF;
-- Determine the full name
IF new.raw_user_meta_data ->> 'name' IS NOT NULL
THEN
name = new.raw_user_meta_data ->> 'name';
-- If name is provided but not first/last, try to split it
IF first_name IS NULL AND last_name IS NULL AND name IS NOT NULL THEN
first_name = SPLIT_PART(name, ' ', 1);
IF ARRAY_LENGTH(STRING_TO_ARRAY(name, ' '), 1) > 1 THEN
last_name = SUBSTRING(name FROM LENGTH(SPLIT_PART(name, ' ', 1)) + 2);
END IF;
END IF;
ELSE
name = CONCAT(first_name, ' ', last_name);
END IF;
-- Check if the role is 'invited_user' in app_metadata
IF COALESCE(new.raw_user_meta_data->>'role', '') = 'invited_user'
THEN
is_temp = TRUE;
ELSE
is_temp = FALSE;
END IF;
INSERT INTO public.profiles (id, name, email, avatar_url, first_name, last_name, is_temporary)
VALUES (new.id, name, new.email, new.raw_user_meta_data ->> 'avatar_url', first_name, last_name, is_temp);
RETURN new;
END;
$$;
ALTER FUNCTION "public"."handle_new_user"() OWNER TO "postgres";
COMMENT ON FUNCTION "public"."handle_new_user"() IS 'Trigger function that creates a profile when a new user is created. Sets is_temporary=true for users with app_metadata.role=invited_user. Extracts first_name from email prefix (before @) if not provided in metadata.';
CREATE OR REPLACE FUNCTION "public"."is_paying_user"("user_uuid" "uuid") RETURNS boolean
LANGUAGE "plpgsql" SECURITY DEFINER
AS $$
declare
has_active_subscription boolean;
begin
select exists(
select 1
from stripe.subscriptions s
inner join stripe.customers c on c.id = s.customer
inner join stripe.subscription_items si on si.subscription = s.id
where (c.metadata->>'user_id')::uuid = user_uuid
and s.status::text in ('active', 'trialing')
and si.current_period_end is not null
and to_timestamp(si.current_period_end) > now()
) into has_active_subscription;
return has_active_subscription;
end;
$$;
ALTER FUNCTION "public"."is_paying_user"("user_uuid" "uuid") OWNER TO "postgres";
COMMENT ON FUNCTION "public"."is_paying_user"("user_uuid" "uuid") IS 'Returns true if user has an active or trialing subscription';
CREATE OR REPLACE FUNCTION "public"."set_short_user_id"() RETURNS "trigger"
LANGUAGE "plpgsql"
AS $$
BEGIN
NEW.short_user_id = LEFT(NEW.id::TEXT, 6);
RETURN NEW;
END;
$$;
ALTER FUNCTION "public"."set_short_user_id"() OWNER TO "postgres";
CREATE OR REPLACE FUNCTION "public"."set_updated_at"() RETURNS "trigger"
LANGUAGE "plpgsql"
AS $$
begin
new.updated_at = now();
return NEW;
end;
$$;
ALTER FUNCTION "public"."set_updated_at"() OWNER TO "postgres";
CREATE OR REPLACE FUNCTION "public"."update_event_types_updated_at"() RETURNS "trigger"
LANGUAGE "plpgsql"
AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$;
ALTER FUNCTION "public"."update_event_types_updated_at"() OWNER TO "postgres";
CREATE OR REPLACE FUNCTION "public"."update_profile_subscription_status"() RETURNS "trigger"
LANGUAGE "plpgsql" SECURITY DEFINER
AS $$
declare
v_user_id uuid;
v_plan subscription_plan;
v_customer_id text;
begin
-- Get customer ID based on which table triggered this
if TG_TABLE_NAME = 'subscriptions' then
v_customer_id := new.customer;
elsif TG_TABLE_NAME = 'subscription_items' then
-- Get customer ID from the subscription
select customer into v_customer_id
from stripe.subscriptions
where id = new.subscription;
else
-- Unknown table, skip
return new;
end if;
-- Skip if no customer_id found
if v_customer_id is null then
return new;
end if;
-- Extract user_id from customer metadata
select (metadata->>'user_id')::uuid into v_user_id
from stripe.customers
where id = v_customer_id;
-- Skip if no user_id found
if v_user_id is null then
return new;
end if;
-- Determine the user's current plan
select
case
when exists(
select 1
from stripe.subscriptions s
inner join stripe.customers c on c.id = s.customer
inner join stripe.subscription_items si on si.subscription = s.id
where (c.metadata->>'user_id')::uuid = v_user_id
and s.status::text = 'trialing'
and si.current_period_end is not null
and to_timestamp(si.current_period_end) > now()
) then 'trial'::subscription_plan
when exists(
select 1
from stripe.subscriptions s
inner join stripe.customers c on c.id = s.customer
inner join stripe.subscription_items si on si.subscription = s.id
where (c.metadata->>'user_id')::uuid = v_user_id
and s.status::text in ('active', 'past_due')
and si.current_period_end is not null
and to_timestamp(si.current_period_end) > now()
) then 'standard'::subscription_plan
else 'none'::subscription_plan
end into v_plan;
-- Update the user's profile
update public.profiles
set plan = v_plan
where id = v_user_id;
return new;
end;
$$;
ALTER FUNCTION "public"."update_profile_subscription_status"() OWNER TO "postgres";
COMMENT ON FUNCTION "public"."update_profile_subscription_status"() IS 'Trigger function to update profile fields when subscription changes';
CREATE OR REPLACE FUNCTION "public"."update_tablo_invites_on_login"() RETURNS "trigger"
LANGUAGE "plpgsql" SECURITY DEFINER
AS $$
BEGIN
IF (NEW.last_sign_in_at IS NULL OR NEW.last_sign_in_at = OLD.last_sign_in_at) THEN
RETURN NULL;
ELSE
-- Check if the user is temporary and update pending invites
UPDATE public.tablo_invites
SET is_pending = FALSE
WHERE invited_email = NEW.email
AND is_pending = TRUE
AND EXISTS (
SELECT 1 FROM public.profiles
WHERE id = (NEW.id)::uuid
AND is_temporary = TRUE
);
RETURN NEW;
END IF;
END;
$$;
ALTER FUNCTION "public"."update_tablo_invites_on_login"() OWNER TO "postgres";
CREATE OR REPLACE FUNCTION "public"."update_tablos_updated_at"() RETURNS "trigger"
LANGUAGE "plpgsql"
AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$;
ALTER FUNCTION "public"."update_tablos_updated_at"() OWNER TO "postgres";
CREATE OR REPLACE FUNCTION "public"."update_updated_at_column"() RETURNS "trigger"
LANGUAGE "plpgsql"
AS $$
begin
new.updated_at = now();
return new;
end;
$$;
ALTER FUNCTION "public"."update_updated_at_column"() OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "graphile_worker"."_private_job_queues" (
"id" integer NOT NULL,
"queue_name" "text" NOT NULL,
"locked_at" timestamp with time zone,
"locked_by" "text",
"is_available" boolean GENERATED ALWAYS AS (("locked_at" IS NULL)) STORED NOT NULL,
CONSTRAINT "job_queues_queue_name_check" CHECK (("length"("queue_name") <= 128))
);
ALTER TABLE "graphile_worker"."_private_job_queues" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "graphile_worker"."_private_known_crontabs" (
"identifier" "text" NOT NULL,
"known_since" timestamp with time zone NOT NULL,
"last_execution" timestamp with time zone
);
ALTER TABLE "graphile_worker"."_private_known_crontabs" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "graphile_worker"."_private_tasks" (
"id" integer NOT NULL,
"identifier" "text" NOT NULL,
CONSTRAINT "tasks_identifier_check" CHECK (("length"("identifier") <= 128))
);
ALTER TABLE "graphile_worker"."_private_tasks" OWNER TO "postgres";
ALTER TABLE "graphile_worker"."_private_job_queues" ALTER COLUMN "id" ADD GENERATED ALWAYS AS IDENTITY (
SEQUENCE NAME "graphile_worker"."job_queues_id_seq"
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1
);
CREATE OR REPLACE VIEW "graphile_worker"."jobs" AS
SELECT "jobs"."id",
"job_queues"."queue_name",
"tasks"."identifier" AS "task_identifier",
"jobs"."priority",
"jobs"."run_at",
"jobs"."attempts",
"jobs"."max_attempts",
"jobs"."last_error",
"jobs"."created_at",
"jobs"."updated_at",
"jobs"."key",
"jobs"."locked_at",
"jobs"."locked_by",
"jobs"."revision",
"jobs"."flags"
FROM (("graphile_worker"."_private_jobs" "jobs"
JOIN "graphile_worker"."_private_tasks" "tasks" ON (("tasks"."id" = "jobs"."task_id")))
LEFT JOIN "graphile_worker"."_private_job_queues" "job_queues" ON (("job_queues"."id" = "jobs"."job_queue_id")));
ALTER TABLE "graphile_worker"."jobs" OWNER TO "postgres";
ALTER TABLE "graphile_worker"."_private_jobs" ALTER COLUMN "id" ADD GENERATED ALWAYS AS IDENTITY (
SEQUENCE NAME "graphile_worker"."jobs_id_seq1"
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1
);
CREATE TABLE IF NOT EXISTS "graphile_worker"."migrations" (
"id" integer NOT NULL,
"ts" timestamp with time zone DEFAULT "now"() NOT NULL,
"breaking" boolean DEFAULT false NOT NULL
);
ALTER TABLE "graphile_worker"."migrations" OWNER TO "postgres";
ALTER TABLE "graphile_worker"."_private_tasks" ALTER COLUMN "id" ADD GENERATED ALWAYS AS IDENTITY (
SEQUENCE NAME "graphile_worker"."tasks_id_seq"
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1
);
CREATE TABLE IF NOT EXISTS "public"."availabilities" (
"id" integer NOT NULL,
"user_id" "uuid" NOT NULL,
"availability_data" "jsonb" DEFAULT '{}'::"jsonb" NOT NULL,
"created_at" timestamp with time zone DEFAULT "now"() NOT NULL,
"updated_at" timestamp with time zone DEFAULT "now"() NOT NULL,
"exceptions" "jsonb" DEFAULT '[]'::"jsonb"
);
ALTER TABLE "public"."availabilities" OWNER TO "postgres";
COMMENT ON TABLE "public"."availabilities" IS 'User availability settings with Row Level Security';
COMMENT ON COLUMN "public"."availabilities"."id" IS 'Primary key: auto-incrementing integer';
COMMENT ON COLUMN "public"."availabilities"."user_id" IS 'Foreign key reference to auth.users(id)';
COMMENT ON COLUMN "public"."availabilities"."availability_data" IS 'JSONB object containing availability settings for each day (0-6, where 0 is Monday). Each day has enabled status and time ranges.';
COMMENT ON COLUMN "public"."availabilities"."exceptions" IS 'JSONB object containing date-specific availability exceptions that override regular availability settings. Keys are ISO date strings (YYYY-MM-DD), values contain exception type and optional time ranges.';
CREATE SEQUENCE IF NOT EXISTS "public"."availabilities_id_seq"
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE "public"."availabilities_id_seq" OWNER TO "postgres";
ALTER SEQUENCE "public"."availabilities_id_seq" OWNED BY "public"."availabilities"."id";
CREATE TABLE IF NOT EXISTS "public"."calendar_subscriptions" (
"id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL,
"tablo_id" "text" NOT NULL,
"token" "text" NOT NULL,
"created_at" timestamp with time zone DEFAULT "now"()
);
ALTER TABLE "public"."calendar_subscriptions" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "public"."devis" (
"id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL,
"user_id" "uuid" NOT NULL,
"client_email" "text" NOT NULL,
"number" character varying(50) NOT NULL,
"date" timestamp with time zone NOT NULL,
"due_date" timestamp with time zone NOT NULL,
"status" "public"."devis_status" DEFAULT 'draft'::"public"."devis_status" NOT NULL,
"items" "jsonb" DEFAULT '[]'::"jsonb" NOT NULL,
"subtotal" numeric(10,2) NOT NULL,
"tax" numeric(10,2) NOT NULL,
"total" numeric(10,2) NOT NULL,
"notes" "text",
"terms" "text",
"created_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
"updated_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
);
ALTER TABLE "public"."devis" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "public"."event_types" (
"id" "text" DEFAULT "public"."generate_random_string"(24) NOT NULL,
"user_id" "uuid" NOT NULL,
"config" "jsonb" DEFAULT '{}'::"jsonb" NOT NULL,
"is_active" boolean DEFAULT true NOT NULL,
"created_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
"updated_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
"deleted_at" timestamp with time zone,
"standard_name" "text",
CONSTRAINT "event_types_config_check" CHECK ((("config" ? 'name'::"text") AND (("config" ->> 'name'::"text") <> ''::"text") AND ("config" ? 'duration'::"text") AND ((("config" ->> 'duration'::"text"))::integer > 0)))
);
ALTER TABLE "public"."event_types" OWNER TO "postgres";
COMMENT ON TABLE "public"."event_types" IS 'Event type configurations stored as JSONB with Row Level Security';
COMMENT ON COLUMN "public"."event_types"."id" IS 'Primary key: random 24-character alphanumeric string';
COMMENT ON COLUMN "public"."event_types"."user_id" IS 'Foreign key reference to auth.users.id';
COMMENT ON COLUMN "public"."event_types"."config" IS 'JSONB configuration containing: name (required), description (optional), duration (required), bufferTime, maxBookingsPerDay, requiresApproval, price, location, minAdvanceBooking, etc.';
COMMENT ON COLUMN "public"."event_types"."is_active" IS 'Whether this event type is active and available for booking';
COMMENT ON COLUMN "public"."event_types"."created_at" IS 'Timestamp when the event type was created';
COMMENT ON COLUMN "public"."event_types"."updated_at" IS 'Timestamp when the event type was last updated (auto-updated by trigger)';
COMMENT ON COLUMN "public"."event_types"."deleted_at" IS 'Timestamp for soft deletion (NULL means not deleted)';
COMMENT ON COLUMN "public"."event_types"."standard_name" IS 'Standard name for the event type - not modifiable by authenticated users';
CREATE TABLE IF NOT EXISTS "public"."events" (
"id" "text" DEFAULT "public"."generate_random_string"(24) NOT NULL,
"tablo_id" "text" NOT NULL,
"title" character varying(255) NOT NULL,
"description" "text",
"start_date" "date" NOT NULL,
"start_time" time without time zone NOT NULL,
"end_time" time without time zone,
"created_by" "uuid" NOT NULL,
"created_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
"deleted_at" timestamp with time zone
);
ALTER TABLE "public"."events" OWNER TO "postgres";
COMMENT ON TABLE "public"."events" IS 'Calendar events linked to tablos with Row Level Security';
COMMENT ON COLUMN "public"."events"."id" IS 'Primary key: random 24-character alphanumeric string';
COMMENT ON COLUMN "public"."events"."tablo_id" IS 'Foreign key reference to tablos.id (24-character string)';
COMMENT ON COLUMN "public"."events"."start_date" IS 'Date of the event (YYYY-MM-DD format)';
COMMENT ON COLUMN "public"."events"."start_time" IS 'Start time of the event (HH:MM format)';
COMMENT ON COLUMN "public"."events"."end_time" IS 'End time of the event (HH:MM format), optional';
CREATE TABLE IF NOT EXISTS "public"."tablos" (
"owner_id" "uuid" NOT NULL,
"name" character varying(255) NOT NULL,
"image" "text",
"color" character varying(50),
"status" character varying(20) DEFAULT 'todo'::character varying NOT NULL,
"position" integer DEFAULT 0 NOT NULL,
"created_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
"deleted_at" timestamp with time zone,
"id" "text" DEFAULT "public"."generate_random_string"(24) NOT NULL,
"updated_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "tablos_status_check" CHECK ((("status")::"text" = ANY (ARRAY[('todo'::character varying)::"text", ('in_progress'::character varying)::"text", ('done'::character varying)::"text"])))
);
ALTER TABLE "public"."tablos" OWNER TO "postgres";
COMMENT ON COLUMN "public"."tablos"."id" IS 'Primary key: random 24-character alphanumeric string';
COMMENT ON COLUMN "public"."tablos"."updated_at" IS 'Timestamp when the tablo was last updated (auto-updated by trigger)';
CREATE OR REPLACE VIEW "public"."events_and_tablos" WITH ("security_invoker"='true') AS
SELECT DISTINCT "e"."id" AS "event_id",
"e"."title",
"e"."start_date",
"e"."start_time",
"e"."end_time",
"e"."description",
"t"."id" AS "tablo_id",
"t"."name" AS "tablo_name",
"t"."color" AS "tablo_color",
"t"."status" AS "tablo_status"
FROM ("public"."events" "e"
LEFT JOIN "public"."tablos" "t" ON (("e"."tablo_id" = "t"."id")))
WHERE (("e"."deleted_at" IS NULL) AND ("t"."deleted_at" IS NULL))
ORDER BY "e"."start_date", "e"."start_time";
ALTER TABLE "public"."events_and_tablos" OWNER TO "postgres";
COMMENT ON VIEW "public"."events_and_tablos" IS 'View that returns all events and their associated tablos parameters';
CREATE TABLE IF NOT EXISTS "public"."feedbacks" (
"id" integer NOT NULL,
"fd_type" character varying(20) NOT NULL,
"user_id" "uuid" NOT NULL,
"message" "text" NOT NULL,
"created_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "feedbacks_fd_type_check" CHECK ((("fd_type")::"text" = ANY (ARRAY[('bug'::character varying)::"text", ('feature'::character varying)::"text", ('improvement'::character varying)::"text", ('other'::character varying)::"text"])))
);
ALTER TABLE "public"."feedbacks" OWNER TO "postgres";
CREATE SEQUENCE IF NOT EXISTS "public"."feedbacks_id_seq"
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE "public"."feedbacks_id_seq" OWNER TO "postgres";
ALTER SEQUENCE "public"."feedbacks_id_seq" OWNED BY "public"."feedbacks"."id";
CREATE TABLE IF NOT EXISTS "public"."note_access" (
"id" integer NOT NULL,
"note_id" "text" NOT NULL,
"user_id" "uuid" NOT NULL,
"tablo_id" "text",
"is_active" boolean DEFAULT true,
"created_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
"updated_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE "public"."note_access" OWNER TO "postgres";
COMMENT ON TABLE "public"."note_access" IS 'Tracks which notes are shared with tablos. When tablo_id IS NULL and is_active = TRUE, the note is shared with all user tablos. Uses partial unique indexes to handle NULL values correctly.';
COMMENT ON COLUMN "public"."note_access"."tablo_id" IS 'Foreign key reference to tablos.id - NULL means shared with all user tablos. Partial unique indexes ensure only one NULL per (note_id, user_id) combination.';
COMMENT ON COLUMN "public"."note_access"."is_active" IS 'When TRUE, the sharing is active';
CREATE SEQUENCE IF NOT EXISTS "public"."note_access_id_seq"
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE "public"."note_access_id_seq" OWNER TO "postgres";
ALTER SEQUENCE "public"."note_access_id_seq" OWNED BY "public"."note_access"."id";
CREATE TABLE IF NOT EXISTS "public"."notes" (
"id" "text" DEFAULT "public"."generate_random_string"(24) NOT NULL,
"title" character varying(255) NOT NULL,
"content" "text",
"user_id" "uuid" NOT NULL,
"created_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
"updated_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
"deleted_at" timestamp with time zone
);
ALTER TABLE "public"."notes" OWNER TO "postgres";
COMMENT ON TABLE "public"."notes" IS 'User notes with Row Level Security to ensure users can only access their own notes';
COMMENT ON COLUMN "public"."notes"."id" IS 'Primary key: random 24-character alphanumeric string';
COMMENT ON COLUMN "public"."notes"."title" IS 'Title of the note';
COMMENT ON COLUMN "public"."notes"."content" IS 'Content of the note (can be plain text or formatted text)';
COMMENT ON COLUMN "public"."notes"."user_id" IS 'Foreign key reference to auth.users.id - owner of the note';
COMMENT ON COLUMN "public"."notes"."deleted_at" IS 'Soft delete timestamp - when not NULL, the note is considered deleted';
CREATE TABLE IF NOT EXISTS "public"."profiles" (
"id" "uuid" NOT NULL,
"name" "text",
"email" character varying,
"avatar_url" "text",
"short_user_id" "text" NOT NULL,
"is_temporary" boolean DEFAULT false NOT NULL,
"first_name" "text",
"last_name" "text",
"last_signed_in" timestamp with time zone,
"plan" "public"."subscription_plan" DEFAULT 'none'::"public"."subscription_plan"
);
ALTER TABLE "public"."profiles" OWNER TO "postgres";
COMMENT ON COLUMN "public"."profiles"."is_temporary" IS 'Indicates if the user account was created with a temporary password and needs to be changed on first login';
COMMENT ON COLUMN "public"."profiles"."first_name" IS 'User''s first name';
COMMENT ON COLUMN "public"."profiles"."last_name" IS 'User''s last name';
COMMENT ON COLUMN "public"."profiles"."last_signed_in" IS 'Timestamp when the user last signed in, updated from auth.users.last_sign_in_at';
COMMENT ON COLUMN "public"."profiles"."plan" IS 'User subscription plan: none (free), trial, or standard';
CREATE TABLE IF NOT EXISTS "public"."shared_notes" (
"note_id" "text" NOT NULL,
"user_id" "uuid" NOT NULL,
"is_public" boolean DEFAULT false,
"created_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
"updated_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE "public"."shared_notes" OWNER TO "postgres";
COMMENT ON TABLE "public"."shared_notes" IS 'Tracks which notes are shared publicly with Row Level Security';
COMMENT ON COLUMN "public"."shared_notes"."note_id" IS 'Foreign key reference to notes.id';
COMMENT ON COLUMN "public"."shared_notes"."user_id" IS 'Foreign key reference to auth.users.id - owner of the note';
COMMENT ON COLUMN "public"."shared_notes"."is_public" IS 'When TRUE, the note is publicly accessible via /notes/public/:noteId';
CREATE TABLE IF NOT EXISTS "public"."tablo_access" (
"id" integer NOT NULL,
"user_id" "uuid" NOT NULL,
"granted_by" "uuid" NOT NULL,
"is_active" boolean DEFAULT true,
"is_admin" boolean DEFAULT false,
"created_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
"tablo_id" "text" NOT NULL
);
ALTER TABLE "public"."tablo_access" OWNER TO "postgres";
COMMENT ON COLUMN "public"."tablo_access"."tablo_id" IS 'Foreign key reference to tablos.id (24-character string)';
CREATE SEQUENCE IF NOT EXISTS "public"."tablo_access_id_seq"
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE "public"."tablo_access_id_seq" OWNER TO "postgres";
ALTER SEQUENCE "public"."tablo_access_id_seq" OWNED BY "public"."tablo_access"."id";
CREATE TABLE IF NOT EXISTS "public"."tablo_invites" (
"id" integer NOT NULL,
"invited_email" character varying(255) NOT NULL,
"invited_by" "uuid" NOT NULL,
"invite_token" "text" NOT NULL,
"tablo_id" "text" NOT NULL,
"is_pending" boolean DEFAULT false NOT NULL,
"created_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
);
ALTER TABLE "public"."tablo_invites" OWNER TO "postgres";
COMMENT ON COLUMN "public"."tablo_invites"."tablo_id" IS 'Foreign key reference to tablos.id (24-character string)';
COMMENT ON COLUMN "public"."tablo_invites"."is_pending" IS 'When TRUE, the invite is pending acceptance. When FALSE, the invite has been accepted or rejected.';
COMMENT ON COLUMN "public"."tablo_invites"."created_at" IS 'Timestamp when the invite was created';
CREATE SEQUENCE IF NOT EXISTS "public"."tablo_invites_id_seq"
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE "public"."tablo_invites_id_seq" OWNER TO "postgres";
ALTER SEQUENCE "public"."tablo_invites_id_seq" OWNED BY "public"."tablo_invites"."id";
CREATE TABLE IF NOT EXISTS "public"."user_introductions" (
"user_id" "uuid" NOT NULL,
"created_at" timestamp with time zone DEFAULT "now"(),
"updated_at" timestamp with time zone DEFAULT "now"(),
"config" "jsonb" DEFAULT '{}'::"jsonb" NOT NULL
);
ALTER TABLE "public"."user_introductions" OWNER TO "postgres";
COMMENT ON TABLE "public"."user_introductions" IS 'Stores user introduction email templates';
COMMENT ON COLUMN "public"."user_introductions"."user_id" IS 'Reference to the user';
COMMENT ON COLUMN "public"."user_introductions"."config" IS 'User introduction configuration stored as JSON';
CREATE OR REPLACE VIEW "public"."user_tablos" WITH ("security_invoker"='true') AS
SELECT DISTINCT "t"."id",
"ta"."user_id",
"t"."name",
"t"."image",
"t"."color",
"t"."status",
"t"."position",
"t"."created_at",
"t"."deleted_at",
CASE
WHEN ("ta"."is_admin" = true) THEN 'admin'::"text"
ELSE 'member'::"text"
END AS "access_level",
"ta"."is_admin"
FROM ("public"."tablos" "t"
LEFT JOIN "public"."tablo_access" "ta" ON (("t"."id" = "ta"."tablo_id")))
WHERE (("ta"."is_active" = true) AND ("t"."deleted_at" IS NULL))
ORDER BY "t"."position", "t"."created_at" DESC;
ALTER TABLE "public"."user_tablos" OWNER TO "postgres";
COMMENT ON VIEW "public"."user_tablos" IS 'View that returns all tablos accessible to the current authenticated user, including owned tablos and shared tablos with active access';
CREATE TABLE IF NOT EXISTS "stripe"."active_entitlements" (
"id" "text" NOT NULL,
"object" "text",
"livemode" boolean,
"feature" "text",
"customer" "text",
"lookup_key" "text",
"updated_at" timestamp with time zone DEFAULT "timezone"('utc'::"text", "now"()) NOT NULL,
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."active_entitlements" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."charges" (
"id" "text" NOT NULL,
"object" "text",
"paid" boolean,
"order" "text",
"amount" bigint,
"review" "text",
"source" "jsonb",
"status" "text",
"created" integer,
"dispute" "text",
"invoice" "text",
"outcome" "jsonb",
"refunds" "jsonb",
"updated" integer,
"captured" boolean,
"currency" "text",
"customer" "text",
"livemode" boolean,
"metadata" "jsonb",
"refunded" boolean,
"shipping" "jsonb",
"application" "text",
"description" "text",
"destination" "text",
"failure_code" "text",
"on_behalf_of" "text",
"fraud_details" "jsonb",
"receipt_email" "text",
"payment_intent" "text",
"receipt_number" "text",
"transfer_group" "text",
"amount_refunded" bigint,
"application_fee" "text",
"failure_message" "text",
"source_transfer" "text",
"balance_transaction" "text",
"statement_descriptor" "text",
"payment_method_details" "jsonb",
"updated_at" timestamp with time zone DEFAULT "timezone"('utc'::"text", "now"()) NOT NULL,
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."charges" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."checkout_session_line_items" (
"id" "text" NOT NULL,
"object" "text",
"amount_discount" integer,
"amount_subtotal" integer,
"amount_tax" integer,
"amount_total" integer,
"currency" "text",
"description" "text",
"price" "text",
"quantity" integer,
"checkout_session" "text",
"updated_at" timestamp with time zone DEFAULT "timezone"('utc'::"text", "now"()) NOT NULL,
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."checkout_session_line_items" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."checkout_sessions" (
"id" "text" NOT NULL,
"object" "text",
"adaptive_pricing" "jsonb",
"after_expiration" "jsonb",
"allow_promotion_codes" boolean,
"amount_subtotal" integer,
"amount_total" integer,
"automatic_tax" "jsonb",
"billing_address_collection" "text",
"cancel_url" "text",
"client_reference_id" "text",
"client_secret" "text",
"collected_information" "jsonb",
"consent" "jsonb",
"consent_collection" "jsonb",
"created" integer,
"currency" "text",
"currency_conversion" "jsonb",
"custom_fields" "jsonb",
"custom_text" "jsonb",
"customer" "text",
"customer_creation" "text",
"customer_details" "jsonb",
"customer_email" "text",
"discounts" "jsonb",
"expires_at" integer,
"invoice" "text",
"invoice_creation" "jsonb",
"livemode" boolean,
"locale" "text",
"metadata" "jsonb",
"mode" "text",
"optional_items" "jsonb",
"payment_intent" "text",
"payment_link" "text",
"payment_method_collection" "text",
"payment_method_configuration_details" "jsonb",
"payment_method_options" "jsonb",
"payment_method_types" "jsonb",
"payment_status" "text",
"permissions" "jsonb",
"phone_number_collection" "jsonb",
"presentment_details" "jsonb",
"recovered_from" "text",
"redirect_on_completion" "text",
"return_url" "text",
"saved_payment_method_options" "jsonb",
"setup_intent" "text",
"shipping_address_collection" "jsonb",
"shipping_cost" "jsonb",
"shipping_details" "jsonb",
"shipping_options" "jsonb",
"status" "text",
"submit_type" "text",
"subscription" "text",
"success_url" "text",
"tax_id_collection" "jsonb",
"total_details" "jsonb",
"ui_mode" "text",
"url" "text",
"wallet_options" "jsonb",
"updated_at" timestamp with time zone DEFAULT "timezone"('utc'::"text", "now"()) NOT NULL,
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."checkout_sessions" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."coupons" (
"id" "text" NOT NULL,
"object" "text",
"name" "text",
"valid" boolean,
"created" integer,
"updated" integer,
"currency" "text",
"duration" "text",
"livemode" boolean,
"metadata" "jsonb",
"redeem_by" integer,
"amount_off" bigint,
"percent_off" double precision,
"times_redeemed" bigint,
"max_redemptions" bigint,
"duration_in_months" bigint,
"percent_off_precise" double precision,
"updated_at" timestamp with time zone DEFAULT "timezone"('utc'::"text", "now"()) NOT NULL,
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."coupons" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."credit_notes" (
"id" "text" NOT NULL,
"object" "text",
"amount" integer,
"amount_shipping" integer,
"created" integer,
"currency" "text",
"customer" "text",
"customer_balance_transaction" "text",
"discount_amount" integer,
"discount_amounts" "jsonb",
"invoice" "text",
"lines" "jsonb",
"livemode" boolean,
"memo" "text",
"metadata" "jsonb",
"number" "text",
"out_of_band_amount" integer,
"pdf" "text",
"reason" "text",
"refund" "text",
"shipping_cost" "jsonb",
"status" "text",
"subtotal" integer,
"subtotal_excluding_tax" integer,
"tax_amounts" "jsonb",
"total" integer,
"total_excluding_tax" integer,
"type" "text",
"voided_at" "text",
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."credit_notes" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."customers" (
"id" "text" NOT NULL,
"object" "text",
"address" "jsonb",
"description" "text",
"email" "text",
"metadata" "jsonb",
"name" "text",
"phone" "text",
"shipping" "jsonb",
"balance" integer,
"created" integer,
"currency" "text",
"default_source" "text",
"delinquent" boolean,
"discount" "jsonb",
"invoice_prefix" "text",
"invoice_settings" "jsonb",
"livemode" boolean,
"next_invoice_sequence" integer,
"preferred_locales" "jsonb",
"tax_exempt" "text",
"updated_at" timestamp with time zone DEFAULT "timezone"('utc'::"text", "now"()) NOT NULL,
"deleted" boolean DEFAULT false NOT NULL,
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."customers" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."disputes" (
"id" "text" NOT NULL,
"object" "text",
"amount" bigint,
"charge" "text",
"reason" "text",
"status" "text",
"created" integer,
"updated" integer,
"currency" "text",
"evidence" "jsonb",
"livemode" boolean,
"metadata" "jsonb",
"evidence_details" "jsonb",
"balance_transactions" "jsonb",
"is_charge_refundable" boolean,
"updated_at" timestamp with time zone DEFAULT "timezone"('utc'::"text", "now"()) NOT NULL,
"payment_intent" "text",
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."disputes" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."early_fraud_warnings" (
"id" "text" NOT NULL,
"object" "text",
"actionable" boolean,
"charge" "text",
"created" integer,
"fraud_type" "text",
"livemode" boolean,
"payment_intent" "text",
"updated_at" timestamp with time zone DEFAULT "timezone"('utc'::"text", "now"()) NOT NULL,
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."early_fraud_warnings" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."events" (
"id" "text" NOT NULL,
"object" "text",
"data" "jsonb",
"type" "text",
"created" integer,
"request" "text",
"updated" integer,
"livemode" boolean,
"api_version" "text",
"pending_webhooks" bigint,
"updated_at" timestamp with time zone DEFAULT "timezone"('utc'::"text", "now"()) NOT NULL,
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."events" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."features" (
"id" "text" NOT NULL,
"object" "text",
"livemode" boolean,
"name" "text",
"lookup_key" "text",
"active" boolean,
"metadata" "jsonb",
"updated_at" timestamp with time zone DEFAULT "timezone"('utc'::"text", "now"()) NOT NULL,
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."features" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."invoices" (
"id" "text" NOT NULL,
"object" "text",
"auto_advance" boolean,
"collection_method" "text",
"currency" "text",
"description" "text",
"hosted_invoice_url" "text",
"lines" "jsonb",
"metadata" "jsonb",
"period_end" integer,
"period_start" integer,
"status" "stripe"."invoice_status",
"total" bigint,
"account_country" "text",
"account_name" "text",
"account_tax_ids" "jsonb",
"amount_due" bigint,
"amount_paid" bigint,
"amount_remaining" bigint,
"application_fee_amount" bigint,
"attempt_count" integer,
"attempted" boolean,
"billing_reason" "text",
"created" integer,
"custom_fields" "jsonb",
"customer_address" "jsonb",
"customer_email" "text",
"customer_name" "text",
"customer_phone" "text",
"customer_shipping" "jsonb",
"customer_tax_exempt" "text",
"customer_tax_ids" "jsonb",
"default_tax_rates" "jsonb",
"discount" "jsonb",
"discounts" "jsonb",
"due_date" integer,
"ending_balance" integer,
"footer" "text",
"invoice_pdf" "text",
"last_finalization_error" "jsonb",
"livemode" boolean,
"next_payment_attempt" integer,
"number" "text",
"paid" boolean,
"payment_settings" "jsonb",
"post_payment_credit_notes_amount" integer,
"pre_payment_credit_notes_amount" integer,
"receipt_number" "text",
"starting_balance" integer,
"statement_descriptor" "text",
"status_transitions" "jsonb",
"subtotal" integer,
"tax" integer,
"total_discount_amounts" "jsonb",
"total_tax_amounts" "jsonb",
"transfer_data" "jsonb",
"webhooks_delivered_at" integer,
"customer" "text",
"subscription" "text",
"payment_intent" "text",
"default_payment_method" "text",
"default_source" "text",
"on_behalf_of" "text",
"charge" "text",
"updated_at" timestamp with time zone DEFAULT "timezone"('utc'::"text", "now"()) NOT NULL,
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."invoices" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."payment_intents" (
"id" "text" NOT NULL,
"object" "text",
"amount" integer,
"amount_capturable" integer,
"amount_details" "jsonb",
"amount_received" integer,
"application" "text",
"application_fee_amount" integer,
"automatic_payment_methods" "text",
"canceled_at" integer,
"cancellation_reason" "text",
"capture_method" "text",
"client_secret" "text",
"confirmation_method" "text",
"created" integer,
"currency" "text",
"customer" "text",
"description" "text",
"invoice" "text",
"last_payment_error" "text",
"livemode" boolean,
"metadata" "jsonb",
"next_action" "text",
"on_behalf_of" "text",
"payment_method" "text",
"payment_method_options" "jsonb",
"payment_method_types" "jsonb",
"processing" "text",
"receipt_email" "text",
"review" "text",
"setup_future_usage" "text",
"shipping" "jsonb",
"statement_descriptor" "text",
"statement_descriptor_suffix" "text",
"status" "text",
"transfer_data" "jsonb",
"transfer_group" "text",
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."payment_intents" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."payment_methods" (
"id" "text" NOT NULL,
"object" "text",
"created" integer,
"customer" "text",
"type" "text",
"billing_details" "jsonb",
"metadata" "jsonb",
"card" "jsonb",
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."payment_methods" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."payouts" (
"id" "text" NOT NULL,
"object" "text",
"date" "text",
"type" "text",
"amount" bigint,
"method" "text",
"status" "text",
"created" integer,
"updated" integer,
"currency" "text",
"livemode" boolean,
"metadata" "jsonb",
"automatic" boolean,
"recipient" "text",
"description" "text",
"destination" "text",
"source_type" "text",
"arrival_date" "text",
"bank_account" "jsonb",
"failure_code" "text",
"transfer_group" "text",
"amount_reversed" bigint,
"failure_message" "text",
"source_transaction" "text",
"balance_transaction" "text",
"statement_descriptor" "text",
"statement_description" "text",
"failure_balance_transaction" "text",
"updated_at" timestamp with time zone DEFAULT "timezone"('utc'::"text", "now"()) NOT NULL,
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."payouts" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."plans" (
"id" "text" NOT NULL,
"object" "text",
"active" boolean,
"amount" bigint,
"created" integer,
"product" "text",
"currency" "text",
"interval" "text",
"livemode" boolean,
"metadata" "jsonb",
"nickname" "text",
"tiers_mode" "text",
"usage_type" "text",
"billing_scheme" "text",
"interval_count" bigint,
"aggregate_usage" "text",
"transform_usage" "text",
"trial_period_days" bigint,
"updated_at" timestamp with time zone DEFAULT "timezone"('utc'::"text", "now"()) NOT NULL,
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."plans" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."prices" (
"id" "text" NOT NULL,
"object" "text",
"active" boolean,
"currency" "text",
"metadata" "jsonb",
"nickname" "text",
"recurring" "jsonb",
"type" "stripe"."pricing_type",
"unit_amount" integer,
"billing_scheme" "text",
"created" integer,
"livemode" boolean,
"lookup_key" "text",
"tiers_mode" "stripe"."pricing_tiers",
"transform_quantity" "jsonb",
"unit_amount_decimal" "text",
"product" "text",
"updated_at" timestamp with time zone DEFAULT "timezone"('utc'::"text", "now"()) NOT NULL,
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."prices" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."products" (
"id" "text" NOT NULL,
"object" "text",
"active" boolean,
"description" "text",
"metadata" "jsonb",
"name" "text",
"created" integer,
"images" "jsonb",
"livemode" boolean,
"package_dimensions" "jsonb",
"shippable" boolean,
"statement_descriptor" "text",
"unit_label" "text",
"updated" integer,
"url" "text",
"updated_at" timestamp with time zone DEFAULT "timezone"('utc'::"text", "now"()) NOT NULL,
"marketing_features" "jsonb",
"default_price" "text",
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."products" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."refunds" (
"id" "text" NOT NULL,
"object" "text",
"amount" integer,
"balance_transaction" "text",
"charge" "text",
"created" integer,
"currency" "text",
"destination_details" "jsonb",
"metadata" "jsonb",
"payment_intent" "text",
"reason" "text",
"receipt_number" "text",
"source_transfer_reversal" "text",
"status" "text",
"transfer_reversal" "text",
"updated_at" timestamp with time zone DEFAULT "timezone"('utc'::"text", "now"()) NOT NULL,
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."refunds" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."reviews" (
"id" "text" NOT NULL,
"object" "text",
"billing_zip" "text",
"charge" "text",
"created" integer,
"closed_reason" "text",
"livemode" boolean,
"ip_address" "text",
"ip_address_location" "jsonb",
"open" boolean,
"opened_reason" "text",
"payment_intent" "text",
"reason" "text",
"session" "text",
"updated_at" timestamp with time zone DEFAULT "timezone"('utc'::"text", "now"()) NOT NULL,
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."reviews" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."setup_intents" (
"id" "text" NOT NULL,
"object" "text",
"created" integer,
"customer" "text",
"description" "text",
"payment_method" "text",
"status" "text",
"usage" "text",
"cancellation_reason" "text",
"latest_attempt" "text",
"mandate" "text",
"single_use_mandate" "text",
"on_behalf_of" "text",
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."setup_intents" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."subscription_items" (
"id" "text" NOT NULL,
"object" "text",
"billing_thresholds" "jsonb",
"created" integer,
"deleted" boolean,
"metadata" "jsonb",
"quantity" integer,
"price" "text",
"subscription" "text",
"tax_rates" "jsonb",
"current_period_end" integer,
"current_period_start" integer,
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."subscription_items" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."subscription_schedules" (
"id" "text" NOT NULL,
"object" "text",
"application" "text",
"canceled_at" integer,
"completed_at" integer,
"created" integer NOT NULL,
"current_phase" "jsonb",
"customer" "text" NOT NULL,
"default_settings" "jsonb",
"end_behavior" "text",
"livemode" boolean NOT NULL,
"metadata" "jsonb" NOT NULL,
"phases" "jsonb" NOT NULL,
"released_at" integer,
"released_subscription" "text",
"status" "stripe"."subscription_schedule_status" NOT NULL,
"subscription" "text",
"test_clock" "text",
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."subscription_schedules" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."subscriptions" (
"id" "text" NOT NULL,
"object" "text",
"cancel_at_period_end" boolean,
"current_period_end" integer,
"current_period_start" integer,
"default_payment_method" "text",
"items" "jsonb",
"metadata" "jsonb",
"pending_setup_intent" "text",
"pending_update" "jsonb",
"status" "stripe"."subscription_status",
"application_fee_percent" double precision,
"billing_cycle_anchor" integer,
"billing_thresholds" "jsonb",
"cancel_at" integer,
"canceled_at" integer,
"collection_method" "text",
"created" integer,
"days_until_due" integer,
"default_source" "text",
"default_tax_rates" "jsonb",
"discount" "jsonb",
"ended_at" integer,
"livemode" boolean,
"next_pending_invoice_item_invoice" integer,
"pause_collection" "jsonb",
"pending_invoice_item_interval" "jsonb",
"start_date" integer,
"transfer_data" "jsonb",
"trial_end" "jsonb",
"trial_start" "jsonb",
"schedule" "text",
"customer" "text",
"latest_invoice" "text",
"plan" "text",
"updated_at" timestamp with time zone DEFAULT "timezone"('utc'::"text", "now"()) NOT NULL,
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."subscriptions" OWNER TO "postgres";
CREATE TABLE IF NOT EXISTS "stripe"."tax_ids" (
"id" "text" NOT NULL,
"object" "text",
"country" "text",
"customer" "text",
"type" "text",
"value" "text",
"created" integer NOT NULL,
"livemode" boolean,
"owner" "jsonb",
"last_synced_at" timestamp with time zone
);
ALTER TABLE "stripe"."tax_ids" OWNER TO "postgres";
ALTER TABLE ONLY "public"."availabilities" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."availabilities_id_seq"'::"regclass");
ALTER TABLE ONLY "public"."feedbacks" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."feedbacks_id_seq"'::"regclass");
ALTER TABLE ONLY "public"."note_access" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."note_access_id_seq"'::"regclass");
ALTER TABLE ONLY "public"."tablo_access" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."tablo_access_id_seq"'::"regclass");
ALTER TABLE ONLY "public"."tablo_invites" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."tablo_invites_id_seq"'::"regclass");
ALTER TABLE ONLY "graphile_worker"."_private_job_queues"
ADD CONSTRAINT "job_queues_pkey1" PRIMARY KEY ("id");
ALTER TABLE ONLY "graphile_worker"."_private_job_queues"
ADD CONSTRAINT "job_queues_queue_name_key" UNIQUE ("queue_name");
ALTER TABLE ONLY "graphile_worker"."_private_jobs"
ADD CONSTRAINT "jobs_key_key1" UNIQUE ("key");
ALTER TABLE ONLY "graphile_worker"."_private_jobs"
ADD CONSTRAINT "jobs_pkey1" PRIMARY KEY ("id");
ALTER TABLE ONLY "graphile_worker"."_private_known_crontabs"
ADD CONSTRAINT "known_crontabs_pkey" PRIMARY KEY ("identifier");
ALTER TABLE ONLY "graphile_worker"."migrations"
ADD CONSTRAINT "migrations_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "graphile_worker"."_private_tasks"
ADD CONSTRAINT "tasks_identifier_key" UNIQUE ("identifier");
ALTER TABLE ONLY "graphile_worker"."_private_tasks"
ADD CONSTRAINT "tasks_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "public"."availabilities"
ADD CONSTRAINT "availabilities_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "public"."calendar_subscriptions"
ADD CONSTRAINT "calendar_subscriptions_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "public"."calendar_subscriptions"
ADD CONSTRAINT "calendar_subscriptions_tablo_id_key" UNIQUE ("tablo_id");
ALTER TABLE ONLY "public"."calendar_subscriptions"
ADD CONSTRAINT "calendar_subscriptions_token_key" UNIQUE ("token");
ALTER TABLE ONLY "public"."devis"
ADD CONSTRAINT "devis_number_key" UNIQUE ("number");
ALTER TABLE ONLY "public"."devis"
ADD CONSTRAINT "devis_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "public"."event_types"
ADD CONSTRAINT "event_types_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "public"."events"
ADD CONSTRAINT "events_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "public"."feedbacks"
ADD CONSTRAINT "feedbacks_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "public"."note_access"
ADD CONSTRAINT "note_access_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "public"."notes"
ADD CONSTRAINT "notes_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "public"."profiles"
ADD CONSTRAINT "profiles_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "public"."shared_notes"
ADD CONSTRAINT "shared_notes_pkey" PRIMARY KEY ("note_id");
ALTER TABLE ONLY "public"."tablo_access"
ADD CONSTRAINT "tablo_access_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "public"."tablo_invites"
ADD CONSTRAINT "tablo_invites_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "public"."tablos"
ADD CONSTRAINT "tablos_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "public"."tablo_access"
ADD CONSTRAINT "unique_tablo_access" UNIQUE ("tablo_id", "user_id");
ALTER TABLE ONLY "public"."tablo_invites"
ADD CONSTRAINT "unique_tablo_invitation" UNIQUE ("tablo_id", "invited_email");
ALTER TABLE ONLY "public"."availabilities"
ADD CONSTRAINT "unique_user_availabilities" UNIQUE ("user_id");
ALTER TABLE ONLY "public"."user_introductions"
ADD CONSTRAINT "user_introductions_pkey" PRIMARY KEY ("user_id");
ALTER TABLE ONLY "stripe"."active_entitlements"
ADD CONSTRAINT "active_entitlements_lookup_key_key" UNIQUE ("lookup_key");
ALTER TABLE ONLY "stripe"."active_entitlements"
ADD CONSTRAINT "active_entitlements_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."charges"
ADD CONSTRAINT "charges_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."checkout_session_line_items"
ADD CONSTRAINT "checkout_session_line_items_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."checkout_sessions"
ADD CONSTRAINT "checkout_sessions_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."coupons"
ADD CONSTRAINT "coupons_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."credit_notes"
ADD CONSTRAINT "credit_notes_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."customers"
ADD CONSTRAINT "customers_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."disputes"
ADD CONSTRAINT "disputes_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."early_fraud_warnings"
ADD CONSTRAINT "early_fraud_warnings_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."events"
ADD CONSTRAINT "events_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."features"
ADD CONSTRAINT "features_lookup_key_key" UNIQUE ("lookup_key");
ALTER TABLE ONLY "stripe"."features"
ADD CONSTRAINT "features_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."invoices"
ADD CONSTRAINT "invoices_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."payment_intents"
ADD CONSTRAINT "payment_intents_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."payment_methods"
ADD CONSTRAINT "payment_methods_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."payouts"
ADD CONSTRAINT "payouts_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."plans"
ADD CONSTRAINT "plans_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."prices"
ADD CONSTRAINT "prices_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."products"
ADD CONSTRAINT "products_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."refunds"
ADD CONSTRAINT "refunds_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."reviews"
ADD CONSTRAINT "reviews_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."setup_intents"
ADD CONSTRAINT "setup_intents_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."subscription_items"
ADD CONSTRAINT "subscription_items_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."subscription_schedules"
ADD CONSTRAINT "subscription_schedules_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."subscriptions"
ADD CONSTRAINT "subscriptions_pkey" PRIMARY KEY ("id");
ALTER TABLE ONLY "stripe"."tax_ids"
ADD CONSTRAINT "tax_ids_pkey" PRIMARY KEY ("id");
CREATE INDEX "jobs_main_index" ON "graphile_worker"."_private_jobs" USING "btree" ("priority", "run_at") INCLUDE ("id", "task_id", "job_queue_id") WHERE ("is_available" = true);
CREATE INDEX "jobs_no_queue_index" ON "graphile_worker"."_private_jobs" USING "btree" ("priority", "run_at") INCLUDE ("id", "task_id") WHERE (("is_available" = true) AND ("job_queue_id" IS NULL));
CREATE INDEX "idx_availabilities_user_id" ON "public"."availabilities" USING "btree" ("user_id");
CREATE INDEX "idx_calendar_subscriptions_token" ON "public"."calendar_subscriptions" USING "btree" ("token");
CREATE INDEX "idx_devis_user_id" ON "public"."devis" USING "btree" ("user_id");
CREATE INDEX "idx_event_types_config_gin" ON "public"."event_types" USING "gin" ("config");
CREATE INDEX "idx_event_types_deleted_at" ON "public"."event_types" USING "btree" ("deleted_at");
CREATE INDEX "idx_event_types_is_active" ON "public"."event_types" USING "btree" ("is_active");
CREATE INDEX "idx_event_types_user_id" ON "public"."event_types" USING "btree" ("user_id");
CREATE INDEX "idx_events_created_by" ON "public"."events" USING "btree" ("created_by");
CREATE INDEX "idx_events_deleted_at" ON "public"."events" USING "btree" ("deleted_at");
CREATE INDEX "idx_events_start_date" ON "public"."events" USING "btree" ("start_date");
CREATE INDEX "idx_events_tablo_id" ON "public"."events" USING "btree" ("tablo_id");
CREATE INDEX "idx_note_access_is_active" ON "public"."note_access" USING "btree" ("is_active");
CREATE INDEX "idx_note_access_note_id" ON "public"."note_access" USING "btree" ("note_id");
CREATE INDEX "idx_note_access_tablo_id" ON "public"."note_access" USING "btree" ("tablo_id");
CREATE INDEX "idx_note_access_user_id" ON "public"."note_access" USING "btree" ("user_id");
CREATE INDEX "idx_notes_created_at" ON "public"."notes" USING "btree" ("created_at");
CREATE INDEX "idx_notes_deleted_at" ON "public"."notes" USING "btree" ("deleted_at");
CREATE INDEX "idx_notes_user_id" ON "public"."notes" USING "btree" ("user_id");
CREATE INDEX "idx_profiles_short_user_id" ON "public"."profiles" USING "btree" ("short_user_id");
CREATE INDEX "idx_shared_notes_is_public" ON "public"."shared_notes" USING "btree" ("is_public");
CREATE INDEX "idx_shared_notes_user_id" ON "public"."shared_notes" USING "btree" ("user_id");
CREATE INDEX "idx_tablo_access_user_id" ON "public"."tablo_access" USING "btree" ("user_id");
CREATE INDEX "idx_tablo_invites_created_at" ON "public"."tablo_invites" USING "btree" ("created_at");
CREATE INDEX "idx_tablo_invites_is_pending" ON "public"."tablo_invites" USING "btree" ("is_pending");
CREATE UNIQUE INDEX "unique_note_access_all_tablos" ON "public"."note_access" USING "btree" ("note_id", "user_id") WHERE ("tablo_id" IS NULL);
CREATE UNIQUE INDEX "unique_note_access_with_tablo" ON "public"."note_access" USING "btree" ("note_id", "user_id", "tablo_id") WHERE ("tablo_id" IS NOT NULL);
CREATE INDEX "stripe_active_entitlements_customer_idx" ON "stripe"."active_entitlements" USING "btree" ("customer");
CREATE INDEX "stripe_active_entitlements_feature_idx" ON "stripe"."active_entitlements" USING "btree" ("feature");
CREATE INDEX "stripe_checkout_session_line_items_price_idx" ON "stripe"."checkout_session_line_items" USING "btree" ("price");
CREATE INDEX "stripe_checkout_session_line_items_session_idx" ON "stripe"."checkout_session_line_items" USING "btree" ("checkout_session");
CREATE INDEX "stripe_checkout_sessions_customer_idx" ON "stripe"."checkout_sessions" USING "btree" ("customer");
CREATE INDEX "stripe_checkout_sessions_invoice_idx" ON "stripe"."checkout_sessions" USING "btree" ("invoice");
CREATE INDEX "stripe_checkout_sessions_payment_intent_idx" ON "stripe"."checkout_sessions" USING "btree" ("payment_intent");
CREATE INDEX "stripe_checkout_sessions_subscription_idx" ON "stripe"."checkout_sessions" USING "btree" ("subscription");
CREATE INDEX "stripe_credit_notes_customer_idx" ON "stripe"."credit_notes" USING "btree" ("customer");
CREATE INDEX "stripe_credit_notes_invoice_idx" ON "stripe"."credit_notes" USING "btree" ("invoice");
CREATE INDEX "stripe_dispute_created_idx" ON "stripe"."disputes" USING "btree" ("created");
CREATE INDEX "stripe_early_fraud_warnings_charge_idx" ON "stripe"."early_fraud_warnings" USING "btree" ("charge");
CREATE INDEX "stripe_early_fraud_warnings_payment_intent_idx" ON "stripe"."early_fraud_warnings" USING "btree" ("payment_intent");
CREATE INDEX "stripe_invoices_customer_idx" ON "stripe"."invoices" USING "btree" ("customer");
CREATE INDEX "stripe_invoices_subscription_idx" ON "stripe"."invoices" USING "btree" ("subscription");
CREATE INDEX "stripe_payment_intents_customer_idx" ON "stripe"."payment_intents" USING "btree" ("customer");
CREATE INDEX "stripe_payment_intents_invoice_idx" ON "stripe"."payment_intents" USING "btree" ("invoice");
CREATE INDEX "stripe_payment_methods_customer_idx" ON "stripe"."payment_methods" USING "btree" ("customer");
CREATE INDEX "stripe_refunds_charge_idx" ON "stripe"."refunds" USING "btree" ("charge");
CREATE INDEX "stripe_refunds_payment_intent_idx" ON "stripe"."refunds" USING "btree" ("payment_intent");
CREATE INDEX "stripe_reviews_charge_idx" ON "stripe"."reviews" USING "btree" ("charge");
CREATE INDEX "stripe_reviews_payment_intent_idx" ON "stripe"."reviews" USING "btree" ("payment_intent");
CREATE INDEX "stripe_setup_intents_customer_idx" ON "stripe"."setup_intents" USING "btree" ("customer");
CREATE INDEX "stripe_tax_ids_customer_idx" ON "stripe"."tax_ids" USING "btree" ("customer");
CREATE OR REPLACE TRIGGER "handle_event_types_standard_name_trigger" BEFORE INSERT OR UPDATE ON "public"."event_types" FOR EACH ROW EXECUTE FUNCTION "public"."handle_event_types_standard_name"();
CREATE OR REPLACE TRIGGER "trigger_create_tablo_access" AFTER INSERT ON "public"."tablos" FOR EACH ROW EXECUTE FUNCTION "public"."create_tablo_access_for_owner"();
CREATE OR REPLACE TRIGGER "trigger_set_short_user_id" BEFORE INSERT ON "public"."profiles" FOR EACH ROW EXECUTE FUNCTION "public"."set_short_user_id"();
CREATE OR REPLACE TRIGGER "update_availabilities_updated_at" BEFORE UPDATE ON "public"."availabilities" FOR EACH ROW EXECUTE FUNCTION "public"."update_updated_at_column"();
CREATE OR REPLACE TRIGGER "update_devis_updated_at" BEFORE UPDATE ON "public"."devis" FOR EACH ROW EXECUTE FUNCTION "public"."update_updated_at_column"();
CREATE OR REPLACE TRIGGER "update_event_types_updated_at" BEFORE UPDATE ON "public"."event_types" FOR EACH ROW EXECUTE FUNCTION "public"."update_event_types_updated_at"();
CREATE OR REPLACE TRIGGER "update_tablos_updated_at" BEFORE UPDATE ON "public"."tablos" FOR EACH ROW EXECUTE FUNCTION "public"."update_tablos_updated_at"();
CREATE OR REPLACE TRIGGER "handle_updated_at" BEFORE UPDATE ON "stripe"."active_entitlements" FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();
CREATE OR REPLACE TRIGGER "handle_updated_at" BEFORE UPDATE ON "stripe"."charges" FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();
CREATE OR REPLACE TRIGGER "handle_updated_at" BEFORE UPDATE ON "stripe"."checkout_session_line_items" FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();
CREATE OR REPLACE TRIGGER "handle_updated_at" BEFORE UPDATE ON "stripe"."checkout_sessions" FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();
CREATE OR REPLACE TRIGGER "handle_updated_at" BEFORE UPDATE ON "stripe"."coupons" FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();
CREATE OR REPLACE TRIGGER "handle_updated_at" BEFORE UPDATE ON "stripe"."customers" FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();
CREATE OR REPLACE TRIGGER "handle_updated_at" BEFORE UPDATE ON "stripe"."disputes" FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();
CREATE OR REPLACE TRIGGER "handle_updated_at" BEFORE UPDATE ON "stripe"."early_fraud_warnings" FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();
CREATE OR REPLACE TRIGGER "handle_updated_at" BEFORE UPDATE ON "stripe"."events" FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();
CREATE OR REPLACE TRIGGER "handle_updated_at" BEFORE UPDATE ON "stripe"."features" FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();
CREATE OR REPLACE TRIGGER "handle_updated_at" BEFORE UPDATE ON "stripe"."invoices" FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();
CREATE OR REPLACE TRIGGER "handle_updated_at" BEFORE UPDATE ON "stripe"."payouts" FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();
CREATE OR REPLACE TRIGGER "handle_updated_at" BEFORE UPDATE ON "stripe"."plans" FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();
CREATE OR REPLACE TRIGGER "handle_updated_at" BEFORE UPDATE ON "stripe"."prices" FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();
CREATE OR REPLACE TRIGGER "handle_updated_at" BEFORE UPDATE ON "stripe"."products" FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();
CREATE OR REPLACE TRIGGER "handle_updated_at" BEFORE UPDATE ON "stripe"."refunds" FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();
CREATE OR REPLACE TRIGGER "handle_updated_at" BEFORE UPDATE ON "stripe"."reviews" FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();
CREATE OR REPLACE TRIGGER "handle_updated_at" BEFORE UPDATE ON "stripe"."subscriptions" FOR EACH ROW EXECUTE FUNCTION "public"."set_updated_at"();
CREATE OR REPLACE TRIGGER "update_profile_on_subscription_change" AFTER INSERT OR UPDATE ON "stripe"."subscriptions" FOR EACH ROW EXECUTE FUNCTION "public"."update_profile_subscription_status"();
CREATE OR REPLACE TRIGGER "update_profile_on_subscription_item_change" AFTER INSERT OR UPDATE ON "stripe"."subscription_items" FOR EACH ROW EXECUTE FUNCTION "public"."update_profile_subscription_status"();
ALTER TABLE ONLY "public"."availabilities"
ADD CONSTRAINT "availabilities_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE;
ALTER TABLE ONLY "public"."calendar_subscriptions"
ADD CONSTRAINT "calendar_subscriptions_tablo_id_fkey" FOREIGN KEY ("tablo_id") REFERENCES "public"."tablos"("id") ON DELETE CASCADE;
ALTER TABLE ONLY "public"."devis"
ADD CONSTRAINT "devis_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id");
ALTER TABLE ONLY "public"."event_types"
ADD CONSTRAINT "fk_event_types_user_id" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE;
ALTER TABLE ONLY "public"."events"
ADD CONSTRAINT "fk_events_created_by" FOREIGN KEY ("created_by") REFERENCES "auth"."users"("id") ON DELETE CASCADE;
ALTER TABLE ONLY "public"."events"
ADD CONSTRAINT "fk_events_tablo_id" FOREIGN KEY ("tablo_id") REFERENCES "public"."tablos"("id") ON DELETE CASCADE;
ALTER TABLE ONLY "public"."note_access"
ADD CONSTRAINT "fk_note_access_note_id" FOREIGN KEY ("note_id") REFERENCES "public"."notes"("id") ON DELETE CASCADE;
ALTER TABLE ONLY "public"."note_access"
ADD CONSTRAINT "fk_note_access_tablo_id" FOREIGN KEY ("tablo_id") REFERENCES "public"."tablos"("id") ON DELETE CASCADE;
ALTER TABLE ONLY "public"."note_access"
ADD CONSTRAINT "fk_note_access_user_id" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE;
ALTER TABLE ONLY "public"."notes"
ADD CONSTRAINT "fk_notes_user_id" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE;
ALTER TABLE ONLY "public"."shared_notes"
ADD CONSTRAINT "fk_shared_notes_note_id" FOREIGN KEY ("note_id") REFERENCES "public"."notes"("id") ON DELETE CASCADE;
ALTER TABLE ONLY "public"."shared_notes"
ADD CONSTRAINT "fk_shared_notes_user_id" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE;
ALTER TABLE ONLY "public"."tablo_access"
ADD CONSTRAINT "fk_tablo_access_tablo_id" FOREIGN KEY ("tablo_id") REFERENCES "public"."tablos"("id") ON DELETE CASCADE;
ALTER TABLE ONLY "public"."tablo_access"
ADD CONSTRAINT "fk_tablo_access_user_id" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE;
ALTER TABLE ONLY "public"."tablo_access"
ADD CONSTRAINT "fk_tablo_access_user_id_from_profiles" FOREIGN KEY ("user_id") REFERENCES "public"."profiles"("id");
ALTER TABLE ONLY "public"."tablo_invites"
ADD CONSTRAINT "fk_tablo_invitations_tablo_id" FOREIGN KEY ("tablo_id") REFERENCES "public"."tablos"("id") ON DELETE CASCADE;
ALTER TABLE ONLY "public"."profiles"
ADD CONSTRAINT "profiles_id_fkey" FOREIGN KEY ("id") REFERENCES "auth"."users"("id") ON DELETE CASCADE;
ALTER TABLE ONLY "public"."user_introductions"
ADD CONSTRAINT "user_introductions_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE;
ALTER TABLE ONLY "stripe"."checkout_session_line_items"
ADD CONSTRAINT "checkout_session_line_items_checkout_session_fkey" FOREIGN KEY ("checkout_session") REFERENCES "stripe"."checkout_sessions"("id") ON DELETE CASCADE;
ALTER TABLE ONLY "stripe"."checkout_session_line_items"
ADD CONSTRAINT "checkout_session_line_items_price_fkey" FOREIGN KEY ("price") REFERENCES "stripe"."prices"("id") ON DELETE CASCADE;
ALTER TABLE "graphile_worker"."_private_job_queues" ENABLE ROW LEVEL SECURITY;
ALTER TABLE "graphile_worker"."_private_jobs" ENABLE ROW LEVEL SECURITY;
ALTER TABLE "graphile_worker"."_private_known_crontabs" ENABLE ROW LEVEL SECURITY;
ALTER TABLE "graphile_worker"."_private_tasks" ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Anyone can view public notes" ON "public"."shared_notes" FOR SELECT TO "authenticated", "anon" USING (("is_public" = true));
CREATE POLICY "Users can delete their own availabilities" ON "public"."availabilities" FOR DELETE TO "authenticated" USING (("user_id" = "auth"."uid"()));
CREATE POLICY "Users can delete their own devis" ON "public"."devis" FOR DELETE USING (("auth"."uid"() = "user_id"));
CREATE POLICY "Users can delete their own event types" ON "public"."event_types" FOR UPDATE TO "authenticated" USING ((("user_id" = "auth"."uid"()) AND ("deleted_at" IS NULL))) WITH CHECK (("user_id" = "auth"."uid"()));
CREATE POLICY "Users can delete their own introduction" ON "public"."user_introductions" FOR DELETE USING (("auth"."uid"() = "user_id"));
CREATE POLICY "Users can delete their own note access" ON "public"."note_access" FOR DELETE TO "authenticated" USING (("user_id" = ( SELECT "auth"."uid"() AS "uid")));
CREATE POLICY "Users can delete their own notes (soft)" ON "public"."notes" FOR UPDATE TO "authenticated" USING ((("user_id" = "auth"."uid"()) AND ("deleted_at" IS NULL))) WITH CHECK (("user_id" = "auth"."uid"()));
CREATE POLICY "Users can delete their own profile" ON "public"."profiles" FOR DELETE USING (("auth"."uid"() = "id"));
CREATE POLICY "Users can delete their own shared notes" ON "public"."shared_notes" FOR DELETE TO "authenticated" USING (("user_id" = ( SELECT "auth"."uid"() AS "uid")));
CREATE POLICY "Users can insert events into accessible tablos" ON "public"."events" FOR INSERT TO "authenticated" WITH CHECK (((( SELECT "auth"."uid"() AS "uid") = "created_by") AND (EXISTS ( SELECT 1
FROM "public"."user_tablos" "ut"
WHERE (("ut"."id" = "events"."tablo_id") AND ("ut"."deleted_at" IS NULL) AND ("ut"."user_id" = ( SELECT "auth"."uid"() AS "uid")))))));
CREATE POLICY "Users can insert feedback." ON "public"."feedbacks" FOR INSERT TO "authenticated" WITH CHECK ((( SELECT "auth"."uid"() AS "uid") = "user_id"));
CREATE POLICY "Users can insert own tablos" ON "public"."tablos" FOR INSERT TO "authenticated" WITH CHECK ((( SELECT "auth"."uid"() AS "uid") = "owner_id"));
CREATE POLICY "Users can insert their own availabilities" ON "public"."availabilities" FOR INSERT TO "authenticated" WITH CHECK (("user_id" = "auth"."uid"()));
CREATE POLICY "Users can insert their own devis" ON "public"."devis" FOR INSERT WITH CHECK (("auth"."uid"() = "user_id"));
CREATE POLICY "Users can insert their own event types" ON "public"."event_types" FOR INSERT TO "authenticated" WITH CHECK (("user_id" = "auth"."uid"()));
CREATE POLICY "Users can insert their own introduction" ON "public"."user_introductions" FOR INSERT WITH CHECK (("auth"."uid"() = "user_id"));
CREATE POLICY "Users can insert their own note access" ON "public"."note_access" FOR INSERT TO "authenticated" WITH CHECK (("user_id" = ( SELECT "auth"."uid"() AS "uid")));
CREATE POLICY "Users can insert their own notes" ON "public"."notes" FOR INSERT TO "authenticated" WITH CHECK (("user_id" = ( SELECT "auth"."uid"() AS "uid")));
CREATE POLICY "Users can insert their own shared notes" ON "public"."shared_notes" FOR INSERT TO "authenticated" WITH CHECK (("user_id" = ( SELECT "auth"."uid"() AS "uid")));
CREATE POLICY "Users can update own tablos" ON "public"."tablos" FOR UPDATE TO "authenticated" USING ((( SELECT "auth"."uid"() AS "uid") = "owner_id")) WITH CHECK ((( SELECT "auth"."uid"() AS "uid") = "owner_id"));
CREATE POLICY "Users can update their own availabilities" ON "public"."availabilities" FOR UPDATE TO "authenticated" USING (("user_id" = "auth"."uid"())) WITH CHECK (("user_id" = "auth"."uid"()));
CREATE POLICY "Users can update their own devis" ON "public"."devis" FOR UPDATE USING (("auth"."uid"() = "user_id"));
CREATE POLICY "Users can update their own event types" ON "public"."event_types" FOR UPDATE TO "authenticated" USING (("user_id" = "auth"."uid"())) WITH CHECK (("user_id" = "auth"."uid"()));
CREATE POLICY "Users can update their own events in accessible tablos" ON "public"."events" FOR UPDATE USING ((("created_by" = ( SELECT "auth"."uid"() AS "uid")) AND (EXISTS ( SELECT 1
FROM "public"."user_tablos" "ut"
WHERE (("ut"."id" = "events"."tablo_id") AND ("events"."deleted_at" IS NULL) AND ("ut"."user_id" = ( SELECT "auth"."uid"() AS "uid"))))))) WITH CHECK ((("created_by" = ( SELECT "auth"."uid"() AS "uid")) AND (EXISTS ( SELECT 1
FROM "public"."user_tablos" "ut"
WHERE (("ut"."id" = "events"."tablo_id") AND ("ut"."user_id" = ( SELECT "auth"."uid"() AS "uid")))))));
CREATE POLICY "Users can update their own introduction" ON "public"."user_introductions" FOR UPDATE USING (("auth"."uid"() = "user_id"));
CREATE POLICY "Users can update their own note access" ON "public"."note_access" FOR UPDATE TO "authenticated" USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))) WITH CHECK (("user_id" = ( SELECT "auth"."uid"() AS "uid")));
CREATE POLICY "Users can update their own notes" ON "public"."notes" FOR UPDATE TO "authenticated" USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))) WITH CHECK (("user_id" = ( SELECT "auth"."uid"() AS "uid")));
CREATE POLICY "Users can update their own profile" ON "public"."profiles" FOR UPDATE USING (("auth"."uid"() = "id"));
CREATE POLICY "Users can update their own shared notes" ON "public"."shared_notes" FOR UPDATE TO "authenticated" USING (("user_id" = ( SELECT "auth"."uid"() AS "uid"))) WITH CHECK (("user_id" = ( SELECT "auth"."uid"() AS "uid")));
CREATE POLICY "Users can view events from accessible tablos" ON "public"."events" FOR SELECT TO "authenticated" USING ((EXISTS ( SELECT 1
FROM "public"."user_tablos" "ut"
WHERE (("ut"."id" = "events"."tablo_id") AND ("ut"."deleted_at" IS NULL) AND ("ut"."user_id" = ( SELECT "auth"."uid"() AS "uid"))))));
CREATE POLICY "Users can view notes shared with their tablos" ON "public"."note_access" FOR SELECT TO "authenticated" USING ((("is_active" = true) AND (("tablo_id" IS NULL) OR (EXISTS ( SELECT 1
FROM "public"."tablo_access"
WHERE (("tablo_access"."tablo_id" = "note_access"."tablo_id") AND ("tablo_access"."user_id" = ( SELECT "auth"."uid"() AS "uid")) AND ("tablo_access"."is_active" = true)))))));
CREATE POLICY "Users can view tablos they have access to" ON "public"."tablos" FOR SELECT TO "authenticated" USING (((( SELECT "auth"."uid"() AS "uid") = "owner_id") OR (EXISTS ( SELECT 1
FROM "public"."tablo_access"
WHERE (("tablo_access"."tablo_id" = "tablos"."id") AND ("tablo_access"."user_id" = ( SELECT "auth"."uid"() AS "uid")))))));
CREATE POLICY "Users can view their own availabilities" ON "public"."availabilities" FOR SELECT TO "authenticated" USING (("user_id" = "auth"."uid"()));
CREATE POLICY "Users can view their own devis" ON "public"."devis" FOR SELECT USING (("auth"."uid"() = "user_id"));
CREATE POLICY "Users can view their own event types" ON "public"."event_types" FOR SELECT TO "authenticated" USING (("user_id" = "auth"."uid"()));
CREATE POLICY "Users can view their own introduction" ON "public"."user_introductions" FOR SELECT USING (("auth"."uid"() = "user_id"));
CREATE POLICY "Users can view their own note access" ON "public"."note_access" FOR SELECT TO "authenticated" USING (("user_id" = ( SELECT "auth"."uid"() AS "uid")));
CREATE POLICY "Users can view their own notes and public notes" ON "public"."notes" FOR SELECT TO "authenticated", "anon" USING ((("user_id" = ( SELECT "auth"."uid"() AS "uid")) OR (EXISTS ( SELECT 1
FROM "public"."shared_notes"
WHERE (("shared_notes"."note_id" = "notes"."id") AND ("shared_notes"."is_public" = true))))));
CREATE POLICY "Users can view their own pending invites" ON "public"."tablo_invites" FOR SELECT USING ((("invited_by" = "auth"."uid"()) AND ("is_pending" = true)));
CREATE POLICY "Users can view their own profile" ON "public"."profiles" FOR SELECT USING (("auth"."uid"() = "id"));
CREATE POLICY "Users can view their own shared notes" ON "public"."shared_notes" FOR SELECT TO "authenticated" USING (("user_id" = ( SELECT "auth"."uid"() AS "uid")));
CREATE POLICY "Users can view their tablo access only if the access is active" ON "public"."tablo_access" FOR SELECT USING ((("user_id" = ( SELECT "auth"."uid"() AS "uid")) AND ("is_active" = true)));
ALTER TABLE "public"."availabilities" ENABLE ROW LEVEL SECURITY;
ALTER TABLE "public"."calendar_subscriptions" ENABLE ROW LEVEL SECURITY;
ALTER TABLE "public"."devis" ENABLE ROW LEVEL SECURITY;
ALTER TABLE "public"."event_types" ENABLE ROW LEVEL SECURITY;
ALTER TABLE "public"."events" ENABLE ROW LEVEL SECURITY;
ALTER TABLE "public"."feedbacks" ENABLE ROW LEVEL SECURITY;
ALTER TABLE "public"."note_access" ENABLE ROW LEVEL SECURITY;
ALTER TABLE "public"."notes" ENABLE ROW LEVEL SECURITY;
ALTER TABLE "public"."profiles" ENABLE ROW LEVEL SECURITY;
ALTER TABLE "public"."shared_notes" ENABLE ROW LEVEL SECURITY;
ALTER TABLE "public"."tablo_access" ENABLE ROW LEVEL SECURITY;
ALTER TABLE "public"."tablo_invites" ENABLE ROW LEVEL SECURITY;
ALTER TABLE "public"."tablos" ENABLE ROW LEVEL SECURITY;
ALTER TABLE "public"."user_introductions" ENABLE ROW LEVEL SECURITY;
ALTER PUBLICATION "supabase_realtime" OWNER TO "postgres";
GRANT USAGE ON SCHEMA "public" TO "postgres";
GRANT USAGE ON SCHEMA "public" TO "anon";
GRANT USAGE ON SCHEMA "public" TO "authenticated";
GRANT USAGE ON SCHEMA "public" TO "service_role";
GRANT USAGE ON SCHEMA "stripe" TO "service_role";
GRANT ALL ON FUNCTION "public"."create_last_signed_in_on_profiles"() TO "anon";
GRANT ALL ON FUNCTION "public"."create_last_signed_in_on_profiles"() TO "authenticated";
GRANT ALL ON FUNCTION "public"."create_last_signed_in_on_profiles"() TO "service_role";
GRANT ALL ON FUNCTION "public"."create_tablo_access_for_owner"() TO "anon";
GRANT ALL ON FUNCTION "public"."create_tablo_access_for_owner"() TO "authenticated";
GRANT ALL ON FUNCTION "public"."create_tablo_access_for_owner"() TO "service_role";
GRANT ALL ON FUNCTION "public"."generate_random_string"("length" integer) TO "anon";
GRANT ALL ON FUNCTION "public"."generate_random_string"("length" integer) TO "authenticated";
GRANT ALL ON FUNCTION "public"."generate_random_string"("length" integer) TO "service_role";
GRANT ALL ON FUNCTION "public"."get_my_active_subscription"() TO "anon";
GRANT ALL ON FUNCTION "public"."get_my_active_subscription"() TO "authenticated";
GRANT ALL ON FUNCTION "public"."get_my_active_subscription"() TO "service_role";
GRANT ALL ON FUNCTION "public"."get_stripe_prices"() TO "anon";
GRANT ALL ON FUNCTION "public"."get_stripe_prices"() TO "authenticated";
GRANT ALL ON FUNCTION "public"."get_stripe_prices"() TO "service_role";
GRANT ALL ON FUNCTION "public"."get_stripe_products"() TO "anon";
GRANT ALL ON FUNCTION "public"."get_stripe_products"() TO "authenticated";
GRANT ALL ON FUNCTION "public"."get_stripe_products"() TO "service_role";
GRANT ALL ON FUNCTION "public"."get_user_stripe_customer"() TO "anon";
GRANT ALL ON FUNCTION "public"."get_user_stripe_customer"() TO "authenticated";
GRANT ALL ON FUNCTION "public"."get_user_stripe_customer"() TO "service_role";
GRANT ALL ON FUNCTION "public"."get_user_stripe_customer_id"("user_uuid" "uuid") TO "anon";
GRANT ALL ON FUNCTION "public"."get_user_stripe_customer_id"("user_uuid" "uuid") TO "authenticated";
GRANT ALL ON FUNCTION "public"."get_user_stripe_customer_id"("user_uuid" "uuid") TO "service_role";
GRANT ALL ON FUNCTION "public"."get_user_stripe_subscriptions"() TO "anon";
GRANT ALL ON FUNCTION "public"."get_user_stripe_subscriptions"() TO "authenticated";
GRANT ALL ON FUNCTION "public"."get_user_stripe_subscriptions"() TO "service_role";
GRANT ALL ON FUNCTION "public"."get_user_subscription_status"("user_uuid" "uuid") TO "anon";
GRANT ALL ON FUNCTION "public"."get_user_subscription_status"("user_uuid" "uuid") TO "authenticated";
GRANT ALL ON FUNCTION "public"."get_user_subscription_status"("user_uuid" "uuid") TO "service_role";
GRANT ALL ON FUNCTION "public"."handle_event_types_standard_name"() TO "anon";
GRANT ALL ON FUNCTION "public"."handle_event_types_standard_name"() TO "authenticated";
GRANT ALL ON FUNCTION "public"."handle_event_types_standard_name"() TO "service_role";
GRANT ALL ON FUNCTION "public"."handle_new_user"() TO "anon";
GRANT ALL ON FUNCTION "public"."handle_new_user"() TO "authenticated";
GRANT ALL ON FUNCTION "public"."handle_new_user"() TO "service_role";
GRANT ALL ON FUNCTION "public"."is_paying_user"("user_uuid" "uuid") TO "anon";
GRANT ALL ON FUNCTION "public"."is_paying_user"("user_uuid" "uuid") TO "authenticated";
GRANT ALL ON FUNCTION "public"."is_paying_user"("user_uuid" "uuid") TO "service_role";
GRANT ALL ON FUNCTION "public"."set_short_user_id"() TO "anon";
GRANT ALL ON FUNCTION "public"."set_short_user_id"() TO "authenticated";
GRANT ALL ON FUNCTION "public"."set_short_user_id"() TO "service_role";
GRANT ALL ON FUNCTION "public"."set_updated_at"() TO "anon";
GRANT ALL ON FUNCTION "public"."set_updated_at"() TO "authenticated";
GRANT ALL ON FUNCTION "public"."set_updated_at"() TO "service_role";
GRANT ALL ON FUNCTION "public"."update_event_types_updated_at"() TO "anon";
GRANT ALL ON FUNCTION "public"."update_event_types_updated_at"() TO "authenticated";
GRANT ALL ON FUNCTION "public"."update_event_types_updated_at"() TO "service_role";
GRANT ALL ON FUNCTION "public"."update_profile_subscription_status"() TO "anon";
GRANT ALL ON FUNCTION "public"."update_profile_subscription_status"() TO "authenticated";
GRANT ALL ON FUNCTION "public"."update_profile_subscription_status"() TO "service_role";
GRANT ALL ON FUNCTION "public"."update_tablo_invites_on_login"() TO "anon";
GRANT ALL ON FUNCTION "public"."update_tablo_invites_on_login"() TO "authenticated";
GRANT ALL ON FUNCTION "public"."update_tablo_invites_on_login"() TO "service_role";
GRANT ALL ON FUNCTION "public"."update_tablos_updated_at"() TO "anon";
GRANT ALL ON FUNCTION "public"."update_tablos_updated_at"() TO "authenticated";
GRANT ALL ON FUNCTION "public"."update_tablos_updated_at"() TO "service_role";
GRANT ALL ON FUNCTION "public"."update_updated_at_column"() TO "anon";
GRANT ALL ON FUNCTION "public"."update_updated_at_column"() TO "authenticated";
GRANT ALL ON FUNCTION "public"."update_updated_at_column"() TO "service_role";
GRANT ALL ON TABLE "public"."availabilities" TO "anon";
GRANT ALL ON TABLE "public"."availabilities" TO "authenticated";
GRANT ALL ON TABLE "public"."availabilities" TO "service_role";
GRANT ALL ON SEQUENCE "public"."availabilities_id_seq" TO "anon";
GRANT ALL ON SEQUENCE "public"."availabilities_id_seq" TO "authenticated";
GRANT ALL ON SEQUENCE "public"."availabilities_id_seq" TO "service_role";
GRANT ALL ON TABLE "public"."calendar_subscriptions" TO "anon";
GRANT ALL ON TABLE "public"."calendar_subscriptions" TO "authenticated";
GRANT ALL ON TABLE "public"."calendar_subscriptions" TO "service_role";
GRANT ALL ON TABLE "public"."devis" TO "anon";
GRANT ALL ON TABLE "public"."devis" TO "authenticated";
GRANT ALL ON TABLE "public"."devis" TO "service_role";
GRANT ALL ON TABLE "public"."event_types" TO "anon";
GRANT ALL ON TABLE "public"."event_types" TO "authenticated";
GRANT ALL ON TABLE "public"."event_types" TO "service_role";
GRANT ALL ON TABLE "public"."events" TO "anon";
GRANT ALL ON TABLE "public"."events" TO "authenticated";
GRANT ALL ON TABLE "public"."events" TO "service_role";
GRANT ALL ON TABLE "public"."tablos" TO "anon";
GRANT ALL ON TABLE "public"."tablos" TO "authenticated";
GRANT ALL ON TABLE "public"."tablos" TO "service_role";
GRANT ALL ON TABLE "public"."events_and_tablos" TO "anon";
GRANT ALL ON TABLE "public"."events_and_tablos" TO "authenticated";
GRANT ALL ON TABLE "public"."events_and_tablos" TO "service_role";
GRANT ALL ON TABLE "public"."feedbacks" TO "anon";
GRANT ALL ON TABLE "public"."feedbacks" TO "authenticated";
GRANT ALL ON TABLE "public"."feedbacks" TO "service_role";
GRANT ALL ON SEQUENCE "public"."feedbacks_id_seq" TO "anon";
GRANT ALL ON SEQUENCE "public"."feedbacks_id_seq" TO "authenticated";
GRANT ALL ON SEQUENCE "public"."feedbacks_id_seq" TO "service_role";
GRANT ALL ON TABLE "public"."note_access" TO "anon";
GRANT ALL ON TABLE "public"."note_access" TO "authenticated";
GRANT ALL ON TABLE "public"."note_access" TO "service_role";
GRANT ALL ON SEQUENCE "public"."note_access_id_seq" TO "anon";
GRANT ALL ON SEQUENCE "public"."note_access_id_seq" TO "authenticated";
GRANT ALL ON SEQUENCE "public"."note_access_id_seq" TO "service_role";
GRANT ALL ON TABLE "public"."notes" TO "anon";
GRANT ALL ON TABLE "public"."notes" TO "authenticated";
GRANT ALL ON TABLE "public"."notes" TO "service_role";
GRANT ALL ON TABLE "public"."profiles" TO "anon";
GRANT ALL ON TABLE "public"."profiles" TO "authenticated";
GRANT ALL ON TABLE "public"."profiles" TO "service_role";
GRANT ALL ON TABLE "public"."shared_notes" TO "anon";
GRANT ALL ON TABLE "public"."shared_notes" TO "authenticated";
GRANT ALL ON TABLE "public"."shared_notes" TO "service_role";
GRANT ALL ON TABLE "public"."tablo_access" TO "anon";
GRANT ALL ON TABLE "public"."tablo_access" TO "authenticated";
GRANT ALL ON TABLE "public"."tablo_access" TO "service_role";
GRANT ALL ON SEQUENCE "public"."tablo_access_id_seq" TO "anon";
GRANT ALL ON SEQUENCE "public"."tablo_access_id_seq" TO "authenticated";
GRANT ALL ON SEQUENCE "public"."tablo_access_id_seq" TO "service_role";
GRANT ALL ON TABLE "public"."tablo_invites" TO "anon";
GRANT ALL ON TABLE "public"."tablo_invites" TO "authenticated";
GRANT ALL ON TABLE "public"."tablo_invites" TO "service_role";
GRANT ALL ON SEQUENCE "public"."tablo_invites_id_seq" TO "anon";
GRANT ALL ON SEQUENCE "public"."tablo_invites_id_seq" TO "authenticated";
GRANT ALL ON SEQUENCE "public"."tablo_invites_id_seq" TO "service_role";
GRANT ALL ON TABLE "public"."user_introductions" TO "anon";
GRANT ALL ON TABLE "public"."user_introductions" TO "authenticated";
GRANT ALL ON TABLE "public"."user_introductions" TO "service_role";
GRANT ALL ON TABLE "public"."user_tablos" TO "anon";
GRANT ALL ON TABLE "public"."user_tablos" TO "authenticated";
GRANT ALL ON TABLE "public"."user_tablos" TO "service_role";
GRANT ALL ON TABLE "stripe"."active_entitlements" TO "service_role";
GRANT ALL ON TABLE "stripe"."charges" TO "service_role";
GRANT ALL ON TABLE "stripe"."checkout_session_line_items" TO "service_role";
GRANT ALL ON TABLE "stripe"."checkout_sessions" TO "service_role";
GRANT ALL ON TABLE "stripe"."coupons" TO "service_role";
GRANT ALL ON TABLE "stripe"."credit_notes" TO "service_role";
GRANT ALL ON TABLE "stripe"."customers" TO "service_role";
GRANT ALL ON TABLE "stripe"."disputes" TO "service_role";
GRANT ALL ON TABLE "stripe"."early_fraud_warnings" TO "service_role";
GRANT ALL ON TABLE "stripe"."events" TO "service_role";
GRANT ALL ON TABLE "stripe"."features" TO "service_role";
GRANT ALL ON TABLE "stripe"."invoices" TO "service_role";
GRANT ALL ON TABLE "stripe"."payment_intents" TO "service_role";
GRANT ALL ON TABLE "stripe"."payment_methods" TO "service_role";
GRANT ALL ON TABLE "stripe"."payouts" TO "service_role";
GRANT ALL ON TABLE "stripe"."plans" TO "service_role";
GRANT ALL ON TABLE "stripe"."prices" TO "service_role";
GRANT ALL ON TABLE "stripe"."products" TO "service_role";
GRANT ALL ON TABLE "stripe"."refunds" TO "service_role";
GRANT ALL ON TABLE "stripe"."reviews" TO "service_role";
GRANT ALL ON TABLE "stripe"."setup_intents" TO "service_role";
GRANT ALL ON TABLE "stripe"."subscription_items" TO "service_role";
GRANT ALL ON TABLE "stripe"."subscription_schedules" TO "service_role";
GRANT ALL ON TABLE "stripe"."subscriptions" TO "service_role";
GRANT ALL ON TABLE "stripe"."tax_ids" TO "service_role";
ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "postgres";
ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "anon";
ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "authenticated";
ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "service_role";
ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "postgres";
ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "anon";
ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "authenticated";
ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "service_role";
ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "postgres";
ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "anon";
ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "authenticated";
ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "service_role";
ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "stripe" GRANT ALL ON SEQUENCES TO "service_role";
ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "stripe" GRANT ALL ON FUNCTIONS TO "service_role";
ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "stripe" GRANT ALL ON TABLES TO "service_role";
drop extension if exists "pg_net";
drop policy "Anyone can view public notes" on "public"."shared_notes";
create policy "Anyone can view public notes"
on "public"."shared_notes"
as permissive
for select
to anon, authenticated
using ((is_public = true));
CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
CREATE TRIGGER trigger_on_last_signed_in AFTER UPDATE ON auth.users FOR EACH ROW EXECUTE FUNCTION public.create_last_signed_in_on_profiles();
CREATE TRIGGER trigger_update_tablo_invites_on_login AFTER UPDATE ON auth.users FOR EACH ROW EXECUTE FUNCTION public.update_tablo_invites_on_login();
create policy "Anyone can upload an avatar."
on "storage"."objects"
as permissive
for insert
to public
with check ((bucket_id = 'avatars'::text));
create policy "Avatar images are publicly accessible."
on "storage"."objects"
as permissive
for select
to public
using ((bucket_id = 'avatars'::text));