fireproofsocks

fireproofsocks

Creating PostGres SCHEMA in Migration

Sorry for some similar posts – I’m narrowing the problems. I’m trying to get my PostGres database setup using Ecto migrations. This includes creating a separate PostGres schema for the app’s data. I don’t mind if Ecto creates its migration tables in the public schema, I want the app to keep its data in a different schema, e.g. core.

I’ve tried this a couple ways, but nothing seems to work. In my 1st migration, I have tried:

execute("CREATE SCHEMA core")
flush()   # <-- I tried adding this too
Repo.query!("CREATE SCHEMA core")

But nothing seems to take – mix ecto.migrate crashes with an error.

** (Postgrex.Error) ERROR 3F000 (invalid_schema_name) schema "core" does not exist

What am I missing? If I log into the database using a standalone client, I can execute these queries using the same username/password credentials.

This post seemed similar: Creating postgres schemas (schema_prefix)
but It seemed to be more about where Ecto put its migration tables rather than where the app stored its data.
Likewise Ecto migration isn't creating schema in postgres seemed relevant, but my command is inside the change or up function… it should be working.

Am I the only one locking down my database by defining multiple PostGres schemas? If others are doing this, how are you doing it? Are you doing it manually? Or are you able to run these commands via a migration?

Most Liked

kip

kip

ex_cldr Core Team

DDL statements in Postgres need to be in their own transactions. So its possible you are trying to create the schema and create tables (or do other DML) in the same migration?

If you haven’t already, try to separate the schema creation into its own migration and then anything that uses that schema into other migrations.

(and apologies if you’ve already done that, not totally clear from the above)

trisolaran

trisolaran

Hi @fireproofsocks ,

I quickly set up a new phoenix project from scratch using phx.new.

I simply added two migrations:

20221011044913_schema.exs

defmodule Migrations.Repo.Migrations.Schema do
  use Ecto.Migration

  def change do
    execute("CREATE SCHEMA core")
  end
end

20221011044917_table.exs

defmodule Migrations.Repo.Migrations.Table do
  use Ecto.Migration

  def change do
    create table(:posts, prefix: "core") do
      add(:title, :string, default: "Untitled")
      add(:body, :text)

      timestamps()
    end
  end
end

I did nothing else.

Then I ran:

mix ecto.create
mix ecto.migrate

Everything worked as expected:

Can you please show the full stacktrace for this error?

Can you please also share your Repo config?

fireproofsocks

fireproofsocks

Oh man – like always, I was the problem.

I had to isolate the CREATE SCHEMA statement to its own migration so it happened inside its own transaction (per @kip’s suggestion).

But I didn’t realize until this morning that I had duplicated module names in my migrations (the warning flew by too fast for me to notice last night). So that meant that one of my migration modules was essentially ignored (i.e. replaced by another)… and predictably, things did not work. So now that I’ve had a few cups of coffee, I think I’m back on track for this. Thank you!!

The longer response here gets a bit philosophical… i.e. when should IaC solutions (like Terraform) step in to set things up vs. when should migrations set things up? E.g. if an AWS RDS instance gets created with an admin user (not a super-admin), whose job is it to set up the user for the app and its permissions?

One solution is put all of that db setup into Terraform, but then the local dev environment might not match the production one. Another solution would be to have the app bootstrap this stuff… but there are some caveats.

The trick is that there would have to be a runtime config that would prevent Ecto from starting normally because the normal Ecto config could be referencing a user/password that don’t exist yet. (i.e. the database wouldn’t have a role with those credentials yet). So… you could start up the app without Ecto and then prompt a user to manually run migrations via Ecto.Migrator.run/3. For this, a human would use the admin username and password and run the migrations (these credentials would be securely stored OUTSIDE the app), which would create the limited user for the app, create schemas, create tables, etc… Then you could restart the app with Ecto running normally because only then would the app’s limited user exist.

The other part of this approach would be for the day-to-day development to ensure mix ecto.migrate works. Basically, you’d want to make sure the migrations didn’t try to recreate a user that already exists etc.

I might need more coffee, but I think the plan is doable… I’m just not sure if this is an efficient way of solving it.

fireproofsocks

fireproofsocks

The more I think about this, the more I think that the app shouldn’t be involved with any of this “DBA” stuff. The app can define schemas and tables but even that gets sticky because the database user that the app uses may or may not have permissions to create schemas etc. It feels like such a Catch-22 or chicken-and-egg problem.

The thing that really makes the case for me is the fact that PostGres doesn’t store the roles and grants in the app’s database – they get stored somewhere OUTSIDE of that database, so dropping and re-creating the database won’t fix any roles/permissions problems. As soon as you start dealing with users/roles/permissions, you fly off the radar of what Ecto can control. To me, that starts getting into DBA territory because it’s beyond what the app needs to know about to do its job.

Where Next?

Popular in Questions Top

vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
Kagamiiiii
Student &amp; New to elixir. Nice language. I want to convert a english character, e.g. “a”, which is stored in a variable, to it’s asci...
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
chewm
Hi guys, nice to meet you to the whole forum, I’m new here, I’m trying to configure visual studio code for elixir, right now the intellis...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New

Other popular topics Top

dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New

We're in Beta

About us Mission Statement