SQL = """
-- datasets table
create table
public.datasets (
    id bigint generated by default as identity not null,
    key character varying not null,
    ndim smallint not null,
    shape int[] not null,
    created_at timestamp with time zone null default now(),
    user_id uuid null,
    is_public boolean not null default false,
    constraint datasets_pkey primary key (id),
    constraint datasets_key_user_id_key unique (key, user_id),
    constraint datasets_user_id_fkey foreign key (user_id) references users (id) on delete set null
) tablespace pg_default;

  -- RLS policy
ALTER TABLE public.datasets ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Allow all actions to the record owner" ON "public"."datasets"
AS PERMISSIVE FOR ALL
TO authenticated
USING (is_public OR (auth.uid() = user_id))
WITH CHECK (is_public OR (auth.uid() = user_id));


-- tensor_float4 table
create table
public.tensors_float4 (
    data_id bigint not null,
    index bigint not null,
    tensor float4[] not null,
    user_id uuid not null,
    is_public boolean not null default false,
    constraint tensors_float4_pkey primary key (data_id, index, user_id),
    constraint tensors_float4_data_id_fkey foreign key (data_id) references datasets (id) on delete cascade,
    constraint tensors_float4_user_id_fkey foreign key (user_id) references users (id) on delete set null
) tablespace pg_default;

-- RLS policy
ALTER TABLE public.tensors_float4 ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Allow all actions to the record owner" ON "public"."tensors_float4"
AS PERMISSIVE FOR ALL
TO authenticated
USING (is_public OR (auth.uid() = user_id))
WITH CHECK (is_public OR (auth.uid() = user_id));

-- create the slicing database function
CREATE OR REPLACE FUNCTION public.tensor_float4_slice(name character varying, index_low integer, index_up integer, slice_low integer[], slice_up integer[])
RETURNS table(tensor float4[])
AS
$$
DECLARE
  query_string text;
  i int;
BEGIN
  query_string := 'SELECT array_agg(tensors_float4.tensor';
  FOR i IN 1..array_length(slice_low, 1) LOOP
    query_string := query_string || '['|| slice_low[i] || ' : ' || slice_up[i] || ']';
  END LOOP;
  query_string := query_string || ') FROM tensors_float4
                   JOIN datasets ON datasets.id = tensors_float4.data_id
                   WHERE datasets.key = ' || quote_literal(name) || '
                   AND tensors_float4.index >= ' || index_low || '
                   AND tensors_float4.index < ' || index_up ;

  RETURN QUERY EXECUTE query_string;
END;
$$ language plpgsql;

-- add usage statistics views
create view
  public.user_usage_details as
select
  users.id,
  users.email,
  datasets.key as dataset,
  sum(pg_column_size(tensors_float4.tensor)::real) / 1024.0::double precision / 1024.0::double precision / 1024.0::double precision as total_size_gb,
  pg_size_pretty(sum(pg_column_size(tensors_float4.tensor))) as size
from
  tensors_float4
  join users on tensors_float4.user_id = users.id
  join datasets on datasets.id = tensors_float4.data_id
group by
  users.id,
  users.email,
  datasets.key;

create view
  public.user_usage_aggregate as
select
  users.id,
  users.email,
  sum(pg_column_size(tensors_float4.tensor)::real) / 1024.0::double precision / 1024.0::double precision / 1024.0::double precision as total_size_gb,
  pg_size_pretty(sum(pg_column_size(tensors_float4.tensor))) as size
from
  tensors_float4
  join users on tensors_float4.user_id = users.id
group by
  users.id,
  users.email;
"""