如何调整Elixir应用适配Heroku上heroku_ext schema的PostgreSQL扩展
解决Heroku Review App中PostgreSQL扩展架构错误的Elixir调整方案
错误背景
创建Heroku Review App时遇到以下数据库错误:
psql:/priv/repo/structure.sql:25: ERROR: Extensions can only be created on heroku_ext schema CONTEXT: PL/pgSQL function inline_code_block line 7 at RAISE
该问题因Heroku 2022年8月1日起实施的PostgreSQL扩展架构规则变更导致——所有扩展必须创建在heroku_ext schema下。
分场景解决方案
1. 在迁移中创建扩展
修改Ecto迁移文件,指定扩展创建到heroku_ext schema,并兼容本地开发环境:
defmodule MyApp.Repo.Migrations.AddUnaccentExtension do use Ecto.Migration def up do db_name = Application.get_env(:my_app, MyApp.Repo)[:database] if System.get_env("HEROKU") do # Heroku环境:指定schema创建扩展,并更新数据库搜索路径 execute "CREATE EXTENSION IF NOT EXISTS unaccent WITH SCHEMA heroku_ext;" execute "ALTER DATABASE #{db_name} SET search_path TO public, heroku_ext;" else # 本地环境:默认schema创建扩展 execute "CREATE EXTENSION IF NOT EXISTS unaccent;" end end def down do db_name = Application.get_env(:my_app, MyApp.Repo)[:database] if System.get_env("HEROKU") do execute "DROP EXTENSION IF EXISTS unaccent;" execute "ALTER DATABASE #{db_name} SET search_path TO public;" else execute "DROP EXTENSION IF EXISTS unaccent;" end end end
- 部署到Heroku时,需在控制台添加
HEROKU环境变量(值设为true即可)。
2. 在priv/repo/structure.sql中创建扩展用于mix ecto.load
修改structure.sql中的扩展创建逻辑,自动适配本地/Heroku环境:
-- 自动判断环境,创建对应schema的扩展 SELECT CASE WHEN EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'heroku_ext') THEN 'CREATE EXTENSION IF NOT EXISTS unaccent WITH SCHEMA heroku_ext;' ELSE 'CREATE EXTENSION IF NOT EXISTS unaccent;' END \gexec -- 全局设置搜索路径(自动适配环境) SELECT CASE WHEN EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'heroku_ext') THEN format('ALTER DATABASE %I SET search_path TO public, heroku_ext;', current_database()) ELSE format('ALTER DATABASE %I SET search_path TO public;', current_database()) END \gexec
- 执行
mix ecto.load时,该逻辑会自动匹配当前环境,无需手动切换配置。
3. 代码中使用扩展函数(如UNACCENT())
有两种方式确保函数调用正常:
方式一:设置连接级搜索路径
在生产环境配置中,为Repo添加search_path参数,让所有数据库连接自动包含heroku_ext:
# config/prod.exs config :my_app, MyApp.Repo, url: System.get_env("DATABASE_URL"), pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"), parameters: [search_path: "public, heroku_ext"]
设置后,代码中可直接调用函数,无需添加schema前缀:
from u in User, where: fragment("unaccent(?) ILIKE unaccent(?)", u.name, ^search_term)
方式二:手动指定schema前缀
如果不想修改全局搜索路径,可在查询中显式指定heroku_ext schema:
from u in User, where: fragment("heroku_ext.unaccent(?) ILIKE heroku_ext.unaccent(?)", u.name, ^search_term)
内容的提问来源于stack exchange,提问作者Szymon Jeż
相关产品推荐
相关产品推荐

