-
Notifications
You must be signed in to change notification settings - Fork 331
First version of dynamic command for Ecto.Migration #158
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Added Ecto.Migration.dynamic/1 macro
Added integration test for ensuring correct order
Added special function clausule in Migration.Runner in order to not afftect currect adapters implementations.
Ensure that {:dynamic, macro, env} command is self-reversible in runner.
TODO:
Discuss the implementation
Documentation and specs
Related issue: #157
|
@josevalim Looks like I did not broke anything and that's a success already. 😄 I never touched I wanted to check it in "real" usage, so I have added a debug queries in migration inside integration tests just to make sure that
For sure I did not added any checks for that right now as well as tests for such usage. Do I need to add some checks or it would just raise without any extra code? Should I add more tests? If I do not missed anything and it looks good for you I will add documentation and specs asap. |
|
When trying changes in my library I had a small problem with unquoting and I had simplified it by adding |
|
@josevalim: Is it a random test error (see my previous commit check)? In previous commit it's: 1) test unique constraint (Ecto.Integration.RepoTest)
deps/ecto/integration_test/cases/repo.exs:329
** (Postgrex.Error) ERROR 26000 (invalid_sql_statement_name) prepared statement "ecto_insert_posts" does not exist
code: {:ok, _} = TestRepo.insert(changeset)
stacktrace:
(ecto_sql) lib/ecto/adapters/sql.ex:629: Ecto.Adapters.SQL.raise_sql_call_error/1
(ecto) lib/ecto/repo/schema.ex:651: Ecto.Repo.Schema.apply/4
(ecto) lib/ecto/repo/schema.ex:262: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4
deps/ecto/integration_test/cases/repo.exs:331: (test)in latest commit it's 1) test insert and update with changeset (Ecto.Integration.RepoTest)
deps/ecto/integration_test/cases/repo.exs:166
** (Postgrex.Error) ERROR 26000 (invalid_sql_statement_name) prepared statement "ecto_update_posts" does not exist
code: assert %Post{text: "y", title: "world", temp: "unknown"} = TestRepo.update!(changeset)
stacktrace:
(ecto_sql) lib/ecto/adapters/sql.ex:629: Ecto.Adapters.SQL.raise_sql_call_error/1
(ecto) lib/ecto/repo/schema.ex:651: Ecto.Repo.Schema.apply/4
(ecto) lib/ecto/repo/schema.ex:349: anonymous fn/15 in Ecto.Repo.Schema.do_update/4
(ecto) lib/ecto/repo/schema.ex:177: Ecto.Repo.Schema.update!/4
deps/ecto/integration_test/cases/repo.exs:179: (test)but I did not touched other tests, so … I'm a bit confused here Also I'm about to release a release canditate for my new library something like today or tomorrow. I just need to take a look again at the whole project with "fresh eye". However the code (which uses new |
|
Yes, those are heisenbugs that I fail to reproduce locally. Regarding the feature, I could not look into a lot of detail yet, but note that |
integration_test/sql/migration.exs
Outdated
| def change do | ||
| execute @migrate_first, @rollback_second | ||
|
|
||
| dynamic migrate_middle: @migrate_middle, rollback_middle: @rollback_middle do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you make it an anonymous function, then you don't need to inject variables.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@josevalim Completely agree
It's always darkest before dawn
|
Btw, we still need a PR that raises on non-reversible flush. :) |
@josevalim Originally I was focused on making my library work. After doing this PR I'm much better following how I will first correct this PR as you said, after that I would quickly update my library and at end I would create a separate PR for
Not sure if I'm able to do this today, but if I would have some time tomorrow then I could take a look at this and give at least a hint. |
… function with arity 0 or 1. Function with arity 1 would accept optional migration direction which would make migrations shorter. Updated documentation and tests.
|
@josevalim How does it look like now? I have added a small enhancement. Developer could optionally use a function with one arity and in such case we would give and migration direction. It's because I think that many people working with reversible migrations would write something like: dynamic(fn -> my_dynamic_func(direction()) end)With this small enhancement we would have: dynamic(&my_dynamic_func/1)which definitely looks much better. Looks like all tests passed on |
|
Hi @Eiji7! I have put some thoughts into this and your questions, and I think I have arrived at a simpler API. What if instead of introducing a new name, we simply allow anonymous functions in And similar to the semantics of To me, this has the advantage of simplifying both our code and our API surface, plus we use semantics that are already well defined. Once we do this, we should be ready to merge this. |
…te/1` and `execute/2`. Updated `execute/1` and `execute/2` documentation. Updated tests
|
@josevalim Done 😃 We really need to fix the problem with failing tests. Is my issue helpful? #160 If you are still unable to reproduce it then I can give you some information you which you think may be important as it's failing for me quite often. btw.
hmm … ok? Did you noticed such error before on CI? |
|
I didn't have a time to look at it #160 yet but I have done similar steps locally and I cannot reproduce it at all. Can you reproduce it consistently? On every run? I am wondering if the issues doesn't happen on Mac and I need a Linux.
Nope. But by definition it is not your bug (segmentation faults are always VM bugs). |
integration_test/sql/migration.exs
Outdated
| end | ||
| end | ||
|
|
||
| defmodule MigrationWithDynamicCommand do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as the other one, let's not use integration tests for these. We also need to rename Dynamic to ExecuteFun or similar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I renamed most of thing, but forgot this one.
lib/ecto/migration.ex
Outdated
| execute "CREATE EXTENSION postgres_fdw", "DROP EXTENSION postgres_fdw" | ||
| alias Ecto.Adapters.SQL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need a bit more structure around this code example. Maybe add some leading paragraph about how it works and define the whole migration file, so the defp makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will change it to full example, but I believe that it would be nice to place such paragraph inside Flushing section which would show difference between those two functions.
lib/ecto/migration.ex
Outdated
| execute(&execute_up/0, &execute_down/0) | ||
| defp execute_up, do: SQL.query!(repo(), "select 'Up query …';", [], [log: :info]) | ||
| defp execute_down, do: SQL.query!(repo(), "select 'Down query …';", [], [log: :info]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
repo().query!should also work. (i.e. no need to alias SQL).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EDIT: Sorry, just noticed this:
Line 8 in a5592af
| defmacro __before_compile__(_opts), do: :ok |
Will replace calls with Logger.info/1 as there is no need to call any query function at all.
|
The implementation looks great, we just need some final improvements on docs and move the integration test. |
This happens often, but not every time I run tests. Just calling this task few times gave me this error. Unfortunately I have no Mac to test it on.
Yes, of course. I was just really surprised seeing that. 😄 |
Moved migration from integration_test/sql/migrations.exs to test/ecto/migrator_test.exs Updated "Flushing" -> "Executing and flushing" section by adding extra paragraph which mentions difference between those 2 solutions. Made documentation cleaner by wrapping code inside example module. Changed Ecto.Adapters.SQL.query!/4 calls to Logger.info/1 in order to increase readability.
# Conflicts: # test/ecto/migrator_test.exs
|
@josevalim Sorry for that merge, I forgot to pull my own changes before 😄 Anyway it should be ready to go now. |
lib/ecto/migration/runner.ex
Outdated
| end | ||
| end | ||
|
|
||
| defp reverse(func) when is_function(func, 0), do: func |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per comment below, I think this clause is wrong, we should remove it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's right, it was based on reverse for old dynamic proposal and now it should not work like that any more.
Added test to ensure raising FunctionClauseError. Updated test by removing unsupported `execute/1` call on rollback.
|
Ok, hope that I did not missed anything else. 😃 |
test/ecto/migrator_test.exs
Outdated
| setup do | ||
| Process.put(:migrated_versions, [1, 2, 3]) | ||
| :ok | ||
| {:ok, migration_number: System.unique_integer([:positive]) + @base_migration} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we not do this change for now? If we are going to do so, then we should change for the whole file. Your call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you think so then no problem. I have added this because I was using same call for generating unique integer in more than one test/2 calls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but other tests call System.unique_integer([:positive]) too. So I would prefer to change all tests at once or none. :)
|
❤️ 💚 💙 💛 💜 |
Ecto.Migration.dynamic/1macro.Migration.Runnerin order to not afftect currect adapters implementations.{:dynamic, func}command is self-reversible in runner.TODO:
Related issue: #157