Joseph Kain bio photo

Joseph Kain

Professional Software Engineer learning Elixir.

Twitter LinkedIn Github

Recently I decided to try out Ecto 2.0 beta in one of my private projects. In doing so I ran into a small bug and decided to put together a simple case to reproduce the problem and then file an issue against Ecto. This post describes how I went about this process.

In my project I found that Phoenix’s mix ecto.reset command wasn’t working in that seeding the database would fail with an error like this:

** (ArgumentError) no extension found for oid `1822277`
    (postgrex) lib/postgrex/types.ex:298: Postgrex.Types.fetch!/2
    (postgrex) lib/postgrex/types.ex:215: Postgrex.Types.encoder/2
    (elixir) lib/enum.ex:1088: Enum."-map/2-lists^map/1-0-"/2
    (elixir) lib/enum.ex:1088: Enum."-map/2-lists^map/1-0-"/2
    (postgrex) lib/postgrex/query.ex:82: DBConnection.Query.Postgrex.Query.encoders/2
    (postgrex) lib/postgrex/query.ex:43: DBConnection.Query.Postgrex.Query.describe/2
    (db_connection) lib/db_connection.ex:884: DBConnection.describe_execute/5
    (db_connection) lib/db_connection.ex:966: anonymous fn/4 in DBConnection.run_meter/5
    (db_connection) lib/db_connection.ex:1009: DBConnection.run_begin/3
    (db_connection) lib/db_connection.ex:421: DBConnection.query/4
    (ecto) lib/ecto/adapters/sql.ex:380: Ecto.Adapters.SQL.struct/6
    (ecto) lib/ecto/repo/schema.ex:369: Ecto.Repo.Schema.apply/5
    (ecto) lib/ecto/repo/schema.ex:175: anonymous fn/11 in Ecto.Repo.Schema.do_insert/4
    (ecto) lib/ecto/repo/schema.ex:108: Ecto.Repo.Schema.insert!/4
    (elixir) lib/code.ex:363: Code.require_file/2
    (mix) lib/mix/tasks/run.ex:68: Mix.Tasks.Run.run/1
    (mix) lib/mix/task.ex:309: Mix.Task.run_alias/3

After experimenting a bit I concluded that the problem was related to the PostGIS extension. I set about creating a new project with the minimal set of dependencies and code that can reproduce this problem.

For reference, I’m using Elixir 1.2.2 and starting out with a mix.lock file like this:

%{"connection": {:hex, :connection, "1.0.2"},
  "cowboy": {:hex, :cowboy, "1.0.4"},
  "cowlib": {:hex, :cowlib, "1.0.2"},
  "db_connection": {:hex, :db_connection, "0.2.4"},
  "decimal": {:hex, :decimal, "1.1.1"},
  "ecto": {:hex, :ecto, "1.1.4"},
  "fs": {:hex, :fs, "0.9.2"},
  "geo": {:hex, :geo, "1.0.1"},
  "gettext": {:hex, :gettext, "0.10.0"},
  "phoenix": {:hex, :phoenix, "1.1.4"},
  "phoenix_ecto": {:hex, :phoenix_ecto, "2.0.1"},
  "phoenix_html": {:hex, :phoenix_html, "2.5.0"},
  "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.0.3"},
  "plug": {:hex, :plug, "1.1.2"},
  "poison": {:hex, :poison, "1.5.2"},
  "poolboy": {:hex, :poolboy, "1.5.1"},
  "postgrex": {:hex, :postgrex, "0.11.1"},
  "ranch": {:hex, :ranch, "1.2.1"}}

Then I’ll be upgrading ecto and other packages to:

index aca7970..d8d5231 100644
--- a/mix.lock
+++ b/mix.lock
@@ -3,15 +3,16 @@
   "cowlib": {:hex, :cowlib, "1.0.2"},
   "db_connection": {:hex, :db_connection, "0.2.4"},
   "decimal": {:hex, :decimal, "1.1.1"},
+  "ecto": {:hex, :ecto, "2.0.0-beta.1"},
   "fs": {:hex, :fs, "0.9.2"},
   "geo": {:hex, :geo, "1.0.1"},
   "gettext": {:hex, :gettext, "0.10.0"},
   "phoenix": {:hex, :phoenix, "1.1.4"},
-  "phoenix_ecto": {:hex, :phoenix_ecto, "2.0.1"},
+  "phoenix_ecto": {:hex, :phoenix_ecto, "3.0.0-beta.2"},
   "phoenix_html": {:hex, :phoenix_html, "2.5.0"},
   "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.0.3"},
   "plug": {:hex, :plug, "1.1.2"},
-  "poison": {:hex, :poison, "1.5.2"},
+  "poison": {:hex, :poison, "2.1.0"},
   "poolboy": {:hex, :poolboy, "1.5.1"},
   "postgrex": {:hex, :postgrex, "0.11.1"},
   "ranch": {:hex, :ranch, "1.2.1"}}

Setup the app

First, let’s create a brand new Elixir / Phoenix application:

$ mix phoenix.new oid_migration_issue
* creating oid_migration_issue/config/config.exs
* creating oid_migration_issue/config/dev.exs
* creating oid_migration_issue/config/prod.exs
* creating oid_migration_issue/config/prod.secret.exs
* creating oid_migration_issue/config/test.exs
* creating oid_migration_issue/lib/oid_migration_issue.ex
* creating oid_migration_issue/lib/oid_migration_issue/endpoint.ex
* creating oid_migration_issue/test/views/error_view_test.exs
* creating oid_migration_issue/test/support/conn_case.ex
* creating oid_migration_issue/test/support/channel_case.ex
* creating oid_migration_issue/test/test_helper.exs
* creating oid_migration_issue/web/channels/user_socket.ex
* creating oid_migration_issue/web/router.ex
* creating oid_migration_issue/web/views/error_view.ex
* creating oid_migration_issue/web/web.ex
* creating oid_migration_issue/mix.exs
* creating oid_migration_issue/README.md
* creating oid_migration_issue/web/gettext.ex
* creating oid_migration_issue/priv/gettext/errors.pot
* creating oid_migration_issue/priv/gettext/en/LC_MESSAGES/errors.po
* creating oid_migration_issue/web/views/error_helpers.ex
* creating oid_migration_issue/lib/oid_migration_issue/repo.ex
* creating oid_migration_issue/test/support/model_case.ex
* creating oid_migration_issue/priv/repo/seeds.exs
* creating oid_migration_issue/.gitignore
* creating oid_migration_issue/brunch-config.js
* creating oid_migration_issue/package.json
* creating oid_migration_issue/web/static/css/app.css
* creating oid_migration_issue/web/static/js/app.js
* creating oid_migration_issue/web/static/js/socket.js
* creating oid_migration_issue/web/static/assets/robots.txt
* creating oid_migration_issue/web/static/assets/images/phoenix.png
* creating oid_migration_issue/web/static/assets/favicon.ico
* creating oid_migration_issue/test/controllers/page_controller_test.exs
* creating oid_migration_issue/test/views/layout_view_test.exs
* creating oid_migration_issue/test/views/page_view_test.exs
* creating oid_migration_issue/web/controllers/page_controller.ex
* creating oid_migration_issue/web/templates/layout/app.html.eex
* creating oid_migration_issue/web/templates/page/index.html.eex
* creating oid_migration_issue/web/views/layout_view.ex
* creating oid_migration_issue/web/views/page_view.ex

Fetch and install dependencies? [Yn]
* running mix deps.get
* running npm install && node node_modules/brunch/bin/brunch build

We are all set! Run your Phoenix application:

    $ cd oid_migration_issue
    $ mix phoenix.server

You can also run your app inside IEx (Interactive Elixir) as:

    $ iex -S mix phoenix.server

Before moving on, configure your database in config/dev.exs and run:

    $ mix ecto.create

I won’t follow the instructions and run mix ecto.create just yet. Instead, I’ll add Geo as a dependency.

--- a/mix.exs
+++ b/mix.exs
@@ -36,7 +36,9 @@ defmodule OidMigrationIssue.Mixfile do
      {:phoenix_html, "~> 2.4"},
      {:phoenix_live_reload, "~> 1.0", only: :dev},
      {:gettext, "~> 0.9"},
-     {:cowboy, "~> 1.0"}]
+     {:cowboy, "~> 1.0"},
+     {:geo, "~> 1.0"}
+   ]
   end
$ mix deps.get
Running dependency resolution
Dependency resolution completed
  geo: 1.0.1
* Getting geo (Hex package)
Checking package (https://s3.amazonaws.com/s3.hex.pm/tarballs/geo-1.0.1.tar)
Using locally cached package

And enable the extension for Ecto:

--- a/config/dev.exs
+++ b/config/dev.exs
@@ -39,4 +39,5 @@ config :oid_migration_issue, OidMigrationIssue.Repo,
   password: "postgres",
   database: "oid_migration_issue_dev",
   hostname: "localhost",
-  pool_size: 10
+  pool_size: 10,
+  extensions: [{Geo.PostGIS.Extension, library: Geo}]

We also need to enable the PostGIS extension in the database via a migration. We’ll start by generating a new migration:

$ mix ecto.gen.migration enable_postgis
* creating priv/repo/migrations
* creating priv/repo/migrations/20160303080907_enable_postgis.exs

Of course, the timestamp will differ if you run this command yourself.

Next, we fill the following (taken from the Geo README) into the migration:

defmodule OidMigrationIssue.Repo.Migrations.EnablePostgis do
  use Ecto.Migration

  def up do
    execute "CREATE EXTENSION IF NOT EXISTS postgis"
  end

  def down do
    execute "DROP EXTENSION IF EXISTS postgis"
  end
end

Let’s try it out with the ecto.setup alias. This command is a convinient alias that Phoenix creates that just runs

  • ecto.create to create the database
  • ecto.migrate to migrate the database
  • run priv/repo/seeds.exs to seed the database.

    $ mix ecto.setup The database for OidMigrationIssue.Repo has been created.

    00:10:47.066 [info] == Running OidMigrationIssue.Repo.Migrations.EnablePostgis.up/0 forward

    00:10:47.067 [info] execute “CREATE EXTENSION IF NOT EXISTS postgis”

    00:10:48.611 [info] == Migrated in 15.4s

So far, so good.

Creating a PostGIS model

Now that we have Geo and PostGIS configured we need to put them to use and create a model:

$ mix phoenix.gen.model GeoModel geo_models geom:Geo.Point
** (Mix) Unknown type `Geo.Point` given to generator

Hmm, ok so we can’t use the Geo.Point type here. Instead we’ll lie and fix up the generated code later:

$ mix phoenix.gen.model GeoModel geo_models geom:string
* creating priv/repo/migrations/20160303081329_create_geo_model.exs
* creating web/models/geo_model.ex
* creating test/models/geo_model_test.exs

Remember to update your repository by running migrations:

    $ mix ecto.migrate

Then we’ll switch over to using Geo.Point and :geometry:

diff --git a/priv/repo/migrations/20160303081329_create_geo_model.exs b/priv/repo/migrations/20160303081329_create_geo_model.exs
index 62db09b..abc0c55 100644
--- a/priv/repo/migrations/20160303081329_create_geo_model.exs
+++ b/priv/repo/migrations/20160303081329_create_geo_model.exs
@@ -3,7 +3,7 @@ defmodule OidMigrationIssue.Repo.Migrations.CreateGeoModel do

   def change do
     create table(:geo_models) do
-      add :geom, :string
+      add :geom, :geometry

       timestamps
     end
diff --git a/test/models/geo_model_test.exs b/test/models/geo_model_test.exs
index d6f380b..29cb661 100644
--- a/test/models/geo_model_test.exs
+++ b/test/models/geo_model_test.exs
@@ -3,7 +3,7 @@ defmodule OidMigrationIssue.GeoModelTest do

   alias OidMigrationIssue.GeoModel

-  @valid_attrs %{geom: "some content"}
+  @valid_attrs %{geom: %Geo.Point{coordinates: {30, -90}, srid: 4326}}
   @invalid_attrs %{}

   test "changeset with valid attributes" do
diff --git a/web/models/geo_model.ex b/web/models/geo_model.ex
index f0eeeff..46dfe49 100644
--- a/web/models/geo_model.ex
+++ b/web/models/geo_model.ex
@@ -2,7 +2,7 @@ defmodule OidMigrationIssue.GeoModel do
   use OidMigrationIssue.Web, :model

   schema "geo_models" do
-    field :geom, :string
+    field :geom, Geo.Point

     timestamps
   end

Adding Seeds

Let’s just write a simple seeds script to seed a point into our database. Here’s my seeds.exs script:

alias OidMigrationIssue.{Repo, GeoModel}

%GeoModel{}
|> GeoModel.changeset(%{geom: %Geo.Point{coordinates: {30, -90}, srid: 4326}})
|> Repo.insert!

Then, we can use ecto.reset to reset our database using the new seeds:

$ mix ecto.reset
The database for OidMigrationIssue.Repo has been dropped.
The database for OidMigrationIssue.Repo has been created.

00:21:44.809 [info]  == Running OidMigrationIssue.Repo.Migrations.EnablePostgis.up/0 forward

00:21:44.811 [info]  execute "CREATE EXTENSION IF NOT EXISTS postgis"

00:21:45.941 [info]  == Migrated in 11.2s

00:21:45.996 [info]  == Running OidMigrationIssue.Repo.Migrations.CreateGeoModel.change/0 forward

00:21:45.996 [info]  create table geo_models

00:21:46.006 [info]  == Migrated in 0.1s
[debug] INSERT INTO "geo_models" ("inserted_at", "updated_at", "geom") VALUES ($1, $2, $3) RETURNING "id" [{ {2016, 3, 3}, {8, 21, 46, 0} }, { {2016, 3, 3}, {8, 21, 46, 0} }, %Geo.Point{coordinates: {30, -90}, srid: 4326}] OK query=43.3ms

Good, everything seems to be working fine.

Updating to Ecto 2.0 beta

The next step is to upgrade to Ecto 2.0 beta. We’ll follow the instructions from http://blog.plataformatec.com.br/2016/02/ecto-2-0-0-beta-0-is-out/. Fortunately, we don’t have that much to do since we’ve written so little code.

diff --git a/mix.exs b/mix.exs
index 58cda12..f2a3e9e 100644
--- a/mix.exs
+++ b/mix.exs
@@ -32,7 +32,7 @@ defmodule OidMigrationIssue.Mixfile do
   defp deps do
     [{:phoenix, "~> 1.1.4"},
      {:postgrex, ">= 0.0.0"},
-     {:phoenix_ecto, "~> 2.0"},
+     {:phoenix_ecto, "~> 3.0.0-beta"},
      {:phoenix_html, "~> 2.4"},
      {:phoenix_live_reload, "~> 1.0", only: :dev},
      {:gettext, "~> 0.9"},
diff --git a/test/support/channel_case.ex b/test/support/channel_case.ex
index ca65c92..bd242ec 100644
--- a/test/support/channel_case.ex
+++ b/test/support/channel_case.ex
@@ -32,10 +32,7 @@ defmodule OidMigrationIssue.ChannelCase do
   end

   setup tags do
-    unless tags[:async] do
-      Ecto.Adapters.SQL.restart_test_transaction(OidMigrationIssue.Repo, [])
-    end
-
+    :ok = Ecto.Adapters.SQL.Sandbox.checkout(Demo.Repo)
     :ok
   end
 end
diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex
index 8affedf..67dd527 100644
--- a/test/support/conn_case.ex
+++ b/test/support/conn_case.ex
@@ -33,10 +33,7 @@ defmodule OidMigrationIssue.ConnCase do
   end

   setup tags do
-    unless tags[:async] do
-      Ecto.Adapters.SQL.restart_test_transaction(OidMigrationIssue.Repo, [])
-    end
-
+    :ok = Ecto.Adapters.SQL.Sandbox.checkout(Demo.Repo)
     {:ok, conn: Phoenix.ConnTest.conn()}
   end
 end
diff --git a/test/support/model_case.ex b/test/support/model_case.ex
index c3b0e07..6db2da6 100644
--- a/test/support/model_case.ex
+++ b/test/support/model_case.ex
@@ -26,10 +26,7 @@ defmodule OidMigrationIssue.ModelCase do
   end

   setup tags do
-    unless tags[:async] do
-      Ecto.Adapters.SQL.restart_test_transaction(OidMigrationIssue.Repo, [])
-    end
-
+    :ok = Ecto.Adapters.SQL.Sandbox.checkout(Demo.Repo)
     :ok
   end

diff --git a/test/test_helper.exs b/test/test_helper.exs
index 973f1a8..6892146 100644
--- a/test/test_helper.exs
+++ b/test/test_helper.exs
@@ -2,5 +2,4 @@ ExUnit.start

 Mix.Task.run "ecto.create", ~w(-r OidMigrationIssue.Repo --quiet)
 Mix.Task.run "ecto.migrate", ~w(-r OidMigrationIssue.Repo --quiet)
-Ecto.Adapters.SQL.begin_test_transaction(OidMigrationIssue.Repo)
-
+Ecto.Adapters.SQL.Sandbox.mode(OidMigrationIssue.Repo, :manual)

Now, to install the deps:

$ mix deps.get
Running dependency resolution
Conflict on ecto
  mix.lock: 1.1.4
  phoenix_ecto from 3.0.0-beta.0 to 3.0.0-beta.2: ~> 2.0-beta

** (Mix) Hex dependency resolution failed, relax the version requirements
or unlock dependencies

We need to follow the instructions here and unlock ecto:

$ mix deps.unlock ecto
$ mix deps.get
Running dependency resolution
Conflict on ecto 2.0.0-beta.0, 2.0.0-beta.1
  geo 1.0.1: ~> 1.1
  phoenix_ecto from 3.0.0-beta.0 to 3.0.0-beta.2: ~> 2.0-beta

** (Mix) Hex dependency resolution failed, relax the version requirements
or unlock dependencies

Ah, so Geo says it depends on Ecto 1.1. I’ve tested this before and I think Geo can work fine with Ecto 2.0 so let’s override it:

diff --git a/mix.exs b/mix.exs
index f2a3e9e..1772008 100644
--- a/mix.exs
+++ b/mix.exs
@@ -35,6 +35,7 @@ defmodule OidMigrationIssue.Mixfile do
      {:phoenix_ecto, "~> 3.0.0-beta"},
      {:phoenix_html, "~> 2.4"},
      {:phoenix_live_reload, "~> 1.0", only: :dev},
+     {:ecto, "~> 2.0-beta", override: true},
      {:gettext, "~> 0.9"},
      {:cowboy, "~> 1.0"},
      {:geo, "~> 1.0"}

And now we can install the deps we want:

$ mix deps.unlock --all
$ mix deps.get
Running dependency resolution
Dependency resolution completed
  connection: 1.0.2
  cowboy: 1.0.4
  cowlib: 1.0.2
  db_connection: 0.2.4
  decimal: 1.1.1
  ecto: 2.0.0-beta.1
  fs: 0.9.2
  geo: 1.0.1
  gettext: 0.10.0
  phoenix: 1.1.4
  phoenix_ecto: 3.0.0-beta.2
  phoenix_html: 2.5.0
  phoenix_live_reload: 1.0.3
  plug: 1.1.2
  poison: 2.1.0
  poolboy: 1.5.1
  postgrex: 0.11.1
  ranch: 1.2.1
* Updating poison (Hex package)
Checking package (https://s3.amazonaws.com/s3.hex.pm/tarballs/poison-2.1.0.tar)
Using locally cached package

Ecto reset

Now, let’s retry our ecto.reset command again:

$ mix ecto.reset
The database for OidMigrationIssue.Repo has been dropped.
The database for OidMigrationIssue.Repo has been created.

00:34:57.457 [info]  == Running OidMigrationIssue.Repo.Migrations.EnablePostgis.up/0 forward

00:34:57.459 [info]  execute "CREATE EXTENSION IF NOT EXISTS postgis"

00:34:58.653 [info]  == Migrated in 11.9s

00:34:58.710 [info]  == Running OidMigrationIssue.Repo.Migrations.CreateGeoModel.change/0 forward

00:34:58.710 [info]  create table geo_models

00:34:58.715 [info]  == Migrated in 0.0s
** (ArgumentError) no extension found for oid `1822277`
    (postgrex) lib/postgrex/types.ex:298: Postgrex.Types.fetch!/2
    (postgrex) lib/postgrex/types.ex:215: Postgrex.Types.encoder/2
    (elixir) lib/enum.ex:1088: Enum."-map/2-lists^map/1-0-"/2
    (elixir) lib/enum.ex:1088: Enum."-map/2-lists^map/1-0-"/2
    (postgrex) lib/postgrex/query.ex:82: DBConnection.Query.Postgrex.Query.encoders/2
    (postgrex) lib/postgrex/query.ex:43: DBConnection.Query.Postgrex.Query.describe/2
    (db_connection) lib/db_connection.ex:884: DBConnection.describe_execute/5
    (db_connection) lib/db_connection.ex:966: anonymous fn/4 in DBConnection.run_meter/5
    (db_connection) lib/db_connection.ex:1009: DBConnection.run_begin/3
    (db_connection) lib/db_connection.ex:421: DBConnection.query/4
    (ecto) lib/ecto/adapters/sql.ex:380: Ecto.Adapters.SQL.struct/6
    (ecto) lib/ecto/repo/schema.ex:369: Ecto.Repo.Schema.apply/5
    (ecto) lib/ecto/repo/schema.ex:175: anonymous fn/11 in Ecto.Repo.Schema.do_insert/4
    (ecto) lib/ecto/repo/schema.ex:108: Ecto.Repo.Schema.insert!/4
    (elixir) lib/code.ex:363: Code.require_file/2
    (mix) lib/mix/tasks/run.ex:68: Mix.Tasks.Run.run/1
    (mix) lib/mix/task.ex:309: Mix.Task.run_alias/3

Ok, good. This is the failure we expected. Our reproduction case is doing its job. Let’s confirm it’s behaving the way I claim it is.

Note that if I remove the seed command from the aliases like this:

diff --git a/mix.exs b/mix.exs
index 1772008..ccd92b7 100644
--- a/mix.exs
+++ b/mix.exs
@@ -49,7 +49,7 @@ defmodule OidMigrationIssue.Mixfile do
   #
   # See the documentation for `Mix` for more info on aliases.
   defp aliases do
-    ["ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
+    ["ecto.setup": ["ecto.create", "ecto.migrate"],
      "ecto.reset": ["ecto.drop", "ecto.setup"]]
   end
 end

and then run reset and seed as two commands then everything works as it should:

$ mix ecto.reset && mix run priv/repo/seeds.exs
The database for OidMigrationIssue.Repo has been dropped.
The database for OidMigrationIssue.Repo has been created.

09:10:52.465 [info]  == Running OidMigrationIssue.Repo.Migrations.EnablePostgis.up/0 forward

09:10:52.467 [info]  execute "CREATE EXTENSION IF NOT EXISTS postgis"

09:10:53.649 [info]  == Migrated in 11.8s

09:10:53.702 [info]  == Running OidMigrationIssue.Repo.Migrations.CreateGeoModel.change/0 forward

09:10:53.702 [info]  create table geo_models

09:10:53.707 [info]  == Migrated in 0.0s
[debug] INSERT INTO "geo_models" ("inserted_at","updated_at","geom") VALUES ($1,$2,$3) RETURNING "id" [{ {2016, 3, 3}, {17, 10, 54, 0} }, { {2016, 3, 3}, {17, 10, 54, 0} }, %Geo.Point{coordinates: {30, -90}, srid: 4326}] OK query=6.7ms queue=48.7ms

So this appears to be a problem running the commands within a single mix invocation.

Look over the existing issues

Before I go ahead and file an issue against Ecto I should check to see if there is one already filed. Searching, I see Reload TYPE ENUM (Postgre) before migration which has been fixed. But, reading through the issue, it sounds like it describes a very similar issue that would come up when migrating a database due to the Postgres types cache. I wonder if our issue is due to the same root cause? That is, the problem has been fixed for migrations but the problem affects seeds as well.

I can try adding some code to my seeds based on the fix for the above issue. Based on my reading of the fixes I need to add:

diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs
index 13152f0..dd2b82b 100644
--- a/priv/repo/seeds.exs
+++ b/priv/repo/seeds.exs
@@ -12,6 +12,9 @@

 alias OidMigrationIssue.{Repo, GeoModel}

+{:ok, _} = Application.ensure_all_started(:ecto)
+{:ok, _} = Application.ensure_all_started(:postgrex)
+
 %GeoModel{}
 |> GeoModel.changeset(%{geom: %Geo.Point{coordinates: {30, -90}, srid: 4326}})
 |> Repo.insert!

But running this version I still get the same error.

Try out Ecto master

We should try out the master branch of Ecto to see if the issue has been fixed there but not yet published in a release. To do this we make this change to mix.exs:

diff --git a/mix.exs b/mix.exs
index 1772008..5c0827b 100644
--- a/mix.exs
+++ b/mix.exs
@@ -35,7 +35,7 @@ defmodule OidMigrationIssue.Mixfile do
      {:phoenix_ecto, "~> 3.0.0-beta"},
      {:phoenix_html, "~> 2.4"},
      {:phoenix_live_reload, "~> 1.0", only: :dev},
-     {:ecto, "~> 2.0-beta", override: true},
+     {:ecto, github: "elixir-lang/ecto", branch: "master", override: true},
      {:gettext, "~> 0.9"},
      {:cowboy, "~> 1.0"},
      {:geo, "~> 1.0"}

Then update the deps

$ mix deps.get
* Getting ecto (https://github.com/elixir-lang/ecto.git)
Cloning into '/Users/jkain/Documents/Projects/elixir/oid_migration_issue/deps/ecto'...
remote: Counting objects: 25829, done.
remote: Compressing objects: 100% (232/232), done.
remote: Total 25829 (delta 110), reused 2 (delta 2), pack-reused 25594
Receiving objects: 100% (25829/25829), 6.58 MiB | 2.59 MiB/s, done.
Resolving deltas: 100% (14906/14906), done.
Checking connectivity... done.
Running dependency resolution

I see the same problem:

$ mix ecto.reset
The database for OidMigrationIssue.Repo has been dropped.
The database for OidMigrationIssue.Repo has been created.

09:20:59.536 [info]  == Running OidMigrationIssue.Repo.Migrations.EnablePostgis.up/0 forward

09:20:59.537 [info]  execute "CREATE EXTENSION IF NOT EXISTS postgis"

09:21:00.774 [info]  == Migrated in 12.3s

09:21:00.817 [info]  == Running OidMigrationIssue.Repo.Migrations.CreateGeoModel.change/0 forward

09:21:00.817 [info]  create table geo_models

09:21:00.822 [info]  == Migrated in 0.0s
** (ArgumentError) no extension found for oid `1841936`
    (postgrex) lib/postgrex/types.ex:298: Postgrex.Types.fetch!/2
    (postgrex) lib/postgrex/types.ex:215: Postgrex.Types.encoder/2
    (elixir) lib/enum.ex:1088: Enum."-map/2-lists^map/1-0-"/2
    (elixir) lib/enum.ex:1088: Enum."-map/2-lists^map/1-0-"/2
    (postgrex) lib/postgrex/query.ex:82: DBConnection.Query.Postgrex.Query.encoders/2
    (postgrex) lib/postgrex/query.ex:43: DBConnection.Query.Postgrex.Query.describe/2
    (db_connection) lib/db_connection.ex:884: DBConnection.describe_execute/5
    (db_connection) lib/db_connection.ex:966: anonymous fn/4 in DBConnection.run_meter/5
    (db_connection) lib/db_connection.ex:1009: DBConnection.run_begin/3
    (db_connection) lib/db_connection.ex:421: DBConnection.query/4
    (ecto) lib/ecto/adapters/sql.ex:387: Ecto.Adapters.SQL.struct/6
    (ecto) lib/ecto/repo/schema.ex:369: Ecto.Repo.Schema.apply/5
    (ecto) lib/ecto/repo/schema.ex:175: anonymous fn/11 in Ecto.Repo.Schema.do_insert/4
    (ecto) lib/ecto/repo/schema.ex:108: Ecto.Repo.Schema.insert!/4
    (elixir) lib/code.ex:363: Code.require_file/2
    (mix) lib/mix/tasks/run.ex:68: Mix.Tasks.Run.run/1
    (mix) lib/mix/task.ex:309: Mix.Task.run_alias/3

File an Issue and See it Fixed

I filed a new issue and filled in the template and a link to the repo for this simple reproducer.

The issue was fixed in under an hour and half. Here’s the commit.

The fix is in master, let’s try it out. We already have the change to fetch Ecto from master. I just need to unlock Ecto and update to the newest master. Then

$ mix ecto.reset
The database for OidMigrationIssue.Repo has been dropped.
The database for OidMigrationIssue.Repo has been created.

07:37:25.415 [info]  == Running OidMigrationIssue.Repo.Migrations.EnablePostgis.up/0 forward

07:37:25.417 [info]  execute "CREATE EXTENSION IF NOT EXISTS postgis"

07:37:26.941 [info]  == Migrated in 15.2s

07:37:26.994 [info]  == Running OidMigrationIssue.Repo.Migrations.CreateGeoModel.change/0 forward

07:37:26.994 [info]  create table geo_models

07:37:26.999 [info]  == Migrated in 0.0s
[debug] INSERT INTO "geo_models" ("inserted_at","updated_at","geom") VALUES ($1,$2,$3) RETURNING "id" [{ {2016, 3, 5}, {15, 37, 27, 0} }, { {2016, 3, 5}, {15, 37, 27, 0} }, %Geo.Point{coordinates: {30, -90}, srid: 4326}] OK query=7.0ms queue=27.3ms

It works! Thanks José and Ecto team!

Conclusion

I hope you enjoyed this post on my experiences with Ecto 2.0-beta and building a simple reproduction case. I know it was a good oppourtunity for me to help out with the testing of Ecto 2.0.