Packages

phoenix_kit

1.7.95
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 v68.ex
Raw

lib/phoenix_kit/migrations/postgres/v68.ex

defmodule PhoenixKit.Migrations.Postgres.V68 do
@moduledoc """
V68: Allow NULL slug for timestamp-mode publishing posts
Timestamp-mode posts are identified by (post_date, post_time), not by slug.
The NOT NULL constraint on slug was incorrectly applied to all modes.
## Changes
1. Drop NOT NULL constraint on `slug` column in `phoenix_kit_publishing_posts`
2. Replace unique index `idx_publishing_posts_group_slug` with a partial index
that only enforces uniqueness for slug-mode posts (where slug IS NOT NULL)
3. Add unique index on `(group_uuid, post_date, post_time)` for timestamp-mode posts
All operations are idempotent.
"""
use Ecto.Migration
def up(%{prefix: prefix} = opts) do
escaped_prefix = Map.get(opts, :escaped_prefix, prefix)
flush()
if table_exists?(:phoenix_kit_publishing_posts, escaped_prefix) do
table = prefix_table("phoenix_kit_publishing_posts", prefix)
# 1. Drop NOT NULL on slug
execute("ALTER TABLE #{table} ALTER COLUMN slug DROP NOT NULL")
# 2. Replace the unconditional unique index with a partial one (slug-mode only)
execute("DROP INDEX IF EXISTS idx_publishing_posts_group_slug")
execute("""
CREATE UNIQUE INDEX IF NOT EXISTS idx_publishing_posts_group_slug
ON #{table} (group_uuid, slug)
WHERE slug IS NOT NULL
""")
# 3. Add unique constraint for timestamp-mode posts
execute("""
CREATE UNIQUE INDEX IF NOT EXISTS idx_publishing_posts_group_date_time_unique
ON #{table} (group_uuid, post_date, post_time)
WHERE post_date IS NOT NULL AND post_time IS NOT NULL
""")
end
execute("COMMENT ON TABLE #{prefix_table("phoenix_kit", prefix)} IS '68'")
end
def down(%{prefix: prefix} = opts) do
escaped_prefix = Map.get(opts, :escaped_prefix, prefix)
if table_exists?(:phoenix_kit_publishing_posts, escaped_prefix) do
table = prefix_table("phoenix_kit_publishing_posts", prefix)
# Remove timestamp unique index
execute("DROP INDEX IF EXISTS idx_publishing_posts_group_date_time_unique")
# Backfill NULL slugs before restoring unconditional unique index
execute("UPDATE #{table} SET slug = 'migrated-' || uuid WHERE slug IS NULL")
# Restore unconditional unique index (safe now that all slugs are non-NULL)
execute("DROP INDEX IF EXISTS idx_publishing_posts_group_slug")
execute("""
CREATE UNIQUE INDEX IF NOT EXISTS idx_publishing_posts_group_slug
ON #{table} (group_uuid, slug)
""")
# Restore NOT NULL
execute("ALTER TABLE #{table} ALTER COLUMN slug SET NOT NULL")
end
execute("COMMENT ON TABLE #{prefix_table("phoenix_kit", prefix)} IS '67'")
end
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
defp table_exists?(table, escaped_prefix) do
table_name = Atom.to_string(table)
case repo().query(
"""
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_name = '#{table_name}'
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