Packages

phoenix_kit

1.7.70
1.7.208 1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib phoenix_kit migrations postgres v73.ex
Raw

lib/phoenix_kit/migrations/postgres/v73.ex

defmodule PhoenixKit.Migrations.Postgres.V73 do
@moduledoc """
V73: Pre-drop prerequisites for Category B tables.
Prepares Category B tables for the eventual `DROP COLUMN id` (V74) by:
1. **SET NOT NULL** on 7 uuid columns that currently allow NULLs
2. **CREATE UNIQUE INDEX** on 3 tables missing unique indexes on `uuid`
3. **ALTER INDEX RENAME** on 4 indexes to match renamed columns
All operations are idempotent — safe to re-run.
"""
use Ecto.Migration
# Tables whose `uuid` column must become NOT NULL
@set_not_null_tables ~w(
phoenix_kit_ai_endpoints
phoenix_kit_ai_prompts
phoenix_kit_consent_logs
phoenix_kit_payment_methods
phoenix_kit_role_permissions
phoenix_kit_subscription_types
phoenix_kit_sync_connections
)
# Tables needing a unique index on `uuid`
@create_unique_index_tables ~w(
phoenix_kit_consent_logs
phoenix_kit_payment_methods
phoenix_kit_subscription_types
)
# {table, old_index_name, new_index_name}
@index_renames [
{"phoenix_kit_post_tag_assignments", "phoenix_kit_post_tag_assignments_post_id_tag_id_index",
"phoenix_kit_post_tag_assignments_post_uuid_tag_uuid_index"},
{"phoenix_kit_post_group_assignments",
"phoenix_kit_post_group_assignments_post_id_group_id_index",
"phoenix_kit_post_group_assignments_post_uuid_group_uuid_index"},
{"phoenix_kit_post_media", "phoenix_kit_post_media_post_id_position_index",
"phoenix_kit_post_media_post_uuid_position_index"},
{"phoenix_kit_file_instances", "phoenix_kit_file_instances_file_id_variant_name_index",
"phoenix_kit_file_instances_file_uuid_variant_name_index"}
]
def up(%{prefix: prefix} = opts) do
escaped_prefix = Map.get(opts, :escaped_prefix, prefix)
flush()
# 1. SET NOT NULL on uuid columns
for table <- @set_not_null_tables do
set_not_null_if_needed(table, "uuid", prefix, escaped_prefix)
end
# 2. CREATE UNIQUE INDEX on uuid columns (CONCURRENTLY not available inside transaction)
for table <- @create_unique_index_tables do
create_unique_index_if_needed(table, "uuid", prefix, escaped_prefix)
end
# 3. ALTER INDEX ... RENAME
for {table, old_name, new_name} <- @index_renames do
rename_index_if_needed(table, old_name, new_name, prefix, escaped_prefix)
end
execute("COMMENT ON TABLE #{prefix_table("phoenix_kit", prefix)} IS '73'")
end
def down(%{prefix: prefix} = opts) do
escaped_prefix = Map.get(opts, :escaped_prefix, prefix)
# 3. Reverse index renames
for {table, old_name, new_name} <- @index_renames do
rename_index_if_needed(table, new_name, old_name, prefix, escaped_prefix)
end
# 2. Drop unique indexes we created
for table <- @create_unique_index_tables do
idx_name = "#{table}_uuid_unique_index"
drop_index_if_exists(idx_name, prefix, escaped_prefix)
end
# 1. Remove NOT NULL (make nullable again)
for table <- @set_not_null_tables do
drop_not_null_if_needed(table, "uuid", prefix, escaped_prefix)
end
execute("COMMENT ON TABLE #{prefix_table("phoenix_kit", prefix)} IS '72'")
end
# ---------------------------------------------------------------------------
# SET NOT NULL
# ---------------------------------------------------------------------------
defp set_not_null_if_needed(table, column, prefix, escaped_prefix) do
if table_exists?(table, escaped_prefix) and column_exists?(table, column, escaped_prefix) do
table_name = prefix_table(table, prefix)
execute("""
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = '#{table}'
AND table_schema = '#{escaped_prefix}'
AND column_name = '#{column}'
AND is_nullable = 'YES'
) THEN
ALTER TABLE #{table_name} ALTER COLUMN #{column} SET NOT NULL;
END IF;
END $$;
""")
end
end
defp drop_not_null_if_needed(table, column, prefix, escaped_prefix) do
if table_exists?(table, escaped_prefix) and column_exists?(table, column, escaped_prefix) do
table_name = prefix_table(table, prefix)
execute("""
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = '#{table}'
AND table_schema = '#{escaped_prefix}'
AND column_name = '#{column}'
AND is_nullable = 'NO'
) AND NOT EXISTS (
SELECT 1 FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu
ON tc.constraint_name = kcu.constraint_name
AND tc.table_schema = kcu.table_schema
WHERE tc.constraint_type = 'PRIMARY KEY'
AND tc.table_name = '#{table}'
AND tc.table_schema = '#{escaped_prefix}'
AND kcu.column_name = '#{column}'
) THEN
ALTER TABLE #{table_name} ALTER COLUMN #{column} DROP NOT NULL;
END IF;
END $$;
""")
end
end
# ---------------------------------------------------------------------------
# UNIQUE INDEX
# ---------------------------------------------------------------------------
defp create_unique_index_if_needed(table, column, prefix, escaped_prefix) do
if table_exists?(table, escaped_prefix) and column_exists?(table, column, escaped_prefix) do
table_name = prefix_table(table, prefix)
idx_name = "#{table}_uuid_unique_index"
execute("""
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_indexes
WHERE tablename = '#{table}'
AND schemaname = '#{escaped_prefix}'
AND indexname = '#{idx_name}'
) THEN
CREATE UNIQUE INDEX #{idx_name} ON #{table_name} (#{column});
END IF;
END $$;
""")
end
end
defp drop_index_if_exists(idx_name, prefix, _escaped_prefix) do
schema = prefix || "public"
execute("""
DROP INDEX IF EXISTS #{schema}.#{idx_name};
""")
end
# ---------------------------------------------------------------------------
# INDEX RENAME
# ---------------------------------------------------------------------------
defp rename_index_if_needed(table, old_name, new_name, prefix, escaped_prefix) do
if table_exists?(table, escaped_prefix) do
schema = prefix || "public"
execute("""
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM pg_indexes
WHERE schemaname = '#{schema}'
AND indexname = '#{old_name}'
) THEN
ALTER INDEX #{schema}.#{old_name} RENAME TO #{new_name};
END IF;
END $$;
""")
end
end
# ---------------------------------------------------------------------------
# Introspection Helpers
# ---------------------------------------------------------------------------
defp table_exists?(table, escaped_prefix) do
case repo().query(
"""
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_name = '#{table}'
AND table_schema = '#{escaped_prefix}'
)
""",
[],
log: false
) do
{:ok, %{rows: [[true]]}} -> true
_ -> false
end
end
defp column_exists?(table, column, escaped_prefix) do
case repo().query(
"""
SELECT EXISTS (
SELECT FROM information_schema.columns
WHERE table_name = '#{table}'
AND column_name = '#{column}'
AND table_schema = '#{escaped_prefix}'
)
""",
[],
log: false
) do
{:ok, %{rows: [[true]]}} -> true
_ -> false
end
end
defp prefix_table(table_name, nil), do: table_name
defp prefix_table(table_name, "public"), do: "public.#{table_name}"
defp prefix_table(table_name, prefix), do: "#{prefix}.#{table_name}"
end