Closed shdblowers closed 6 years ago
Hi @josevalim, I gave upgrading to Ecto 2.2 another go today, came up against a problem and am hoping you can provide assistance.
Here is the error I am getting:
** (Mssqlex.Error) [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]Multiple identity columns specified for table 'posts_users'. Only one identity column per table is allowed. | ODBC_CODE 42000 | SQL_SERVER_CODE 2744
(ecto) lib/ecto/adapters/sql.ex:200: Ecto.Adapters.SQL.query!/5
(mssql_ecto) lib/mssql_ecto.ex:5: anonymous fn/4 in MssqlEcto.execute_ddl/3
(elixir) lib/enum.ex:1829: Enum."-reduce/3-lists^foldl/2-0-"/3
(mssql_ecto) lib/mssql_ecto.ex:5: MssqlEcto.execute_ddl/3
(ecto) lib/ecto/migration/runner.ex:104: anonymous fn/2 in Ecto.Migration.Runner.flush/0
(elixir) lib/enum.ex:1829: Enum."-reduce/3-lists^foldl/2-0-"/3
(ecto) lib/ecto/migration/runner.ex:102: Ecto.Migration.Runner.flush/0
(stdlib) timer.erl:181: :timer.tc/2
It looks like Ecto 2.2 has introduced the type :bigserial
, or at least it is now more commonly used.
Unfortunately MS SQL Server does not have an equivalent of this field type, previously this conversion was working for us:
def ecto_to_db(:serial), do: "int identity(1,1)"
However, only one field can have the identity property per table, leading to the error you see above.
I am not sure how to proceed here, as MS SQL Server does not have AUTO_INCREMENT
, or UNIQUE
.
any thoughts @jbachhardie ?
I think the sanest course of action is to push the limitations of the database server back to the user. We already map :serial
to int identity(1,1)
let's just map :bigserial
to bigint identity(1,1)
and ignore any tests that want to have multiple in the same table.
There are, of course, workarounds, like using CREATE SEQUENCE
and DEFAULT NEXT VALUE FOR sequence
but I feel like there would be a lot of unexpected behaviour if we tried to magic up some system where the adapter juggles sequences under the hood. Nobody is going to have a SQL Server DB schema with multiple auto-increment columns and they're not going to expect to be able to create one if they're familiar with the software.
In Ecto serial/bigserial is meant to be used once, so the semantics you defined fit perfectly. Mapping bigserial to bigint should be enough. --
José Valimwww.plataformatec.com.br http://www.plataformatec.com.br/Founder and Director of R&D
I have implemented the changes described in elixir-ecto/ecto#2155 and also added code to handle bigserial
as both an ID and a reference.
Fortunately, the test suite is running to completion, unfortunately there is a whole host of errors which can be seen in the Travis CI build log.
Any help fixing them will be appreciated as I am stuck on them, or at least I was at 10pm last night 😄 .
@shdblowers I have a fix that is coming in a couple minutes. :)
@shdblowers here we go: https://github.com/josevalim/mssql_ecto/commit/d4eb1d861e2e4ac7d2ee120ad2351ca1e46eaf36
Summary of changes:
All unit tests should pass. I did not run the integration ones. Feel free to cherry pick the commit and do whatever you want with it. :)
Thanks very much for the help @josevalim, I have cherry picked your commit into the branch and all the unit tests are fixed! 🎉
There are still a few issues with the integration tests, I think the main one being that statements with returning that send back the id return it as a string rather than integer, which I need to investigate further. Also, it would probably be a good idea to re-sync the integration tests with those in Ecto, once that is done I think we'll be ready to support 2.2 :smile:.
OK, so it looks like the issue is that queries which use returning
that aren't using the schema, i.e. TestRepo.delete_all("posts", returning: [:id, :title])
rather than TestRepo.delete_all(Post, returning: true)
will return the id
which is a bigint
as a string value rather than an integer value.
Haven't managed to figure out where I need to make the change to fix this issue, but will carry on with it on another day.
That's probably because :odbc
falls back to a binary representation for bigint
and without the schema we don't have information about the column type to correctly parse it.
Yeah I think that's the reason also. I wonder if then the behaviour should either be:
A. we attempt to parse returning :id
fields as integers, falling back to whatever value they were beforehand if that fails. Which could possibly lead to funky behaviour when the :id
field is not an integer. (If that is even possible to do at the adapter level).
B. leave the functionality as is and document this behaviour in the README.
@jbachhardie is the root issue odbc not understanding that bigint is an int? Since it worked for ints, i am thinking the information is available, but we are not using it? Is odbc capable of returning the types once a query is made?
@shdblowers question: why did you copy the integration tests to mssql repo? Wouldn't it be better to run the ecto repo integration tests instead and use tags to skip what doesn't make sense for mssql?
The OTP :odbc
adapter could absolutely return a more appropriate representation of bigint
(along with the many other standard column types it doesn't support) but it would require rolling out a patch to the FFI in OTP itself. It's something we're often hamstrung by.
To be honest I don't think it would even be that much work to make OTP support the full current ODBC types but volunteers with knowledge of C and Erlang's build system are in short supply.
I cannot help with the C part of things but I can help with the Erlang's build system one. If anyone needs help on getting started there, please let me know or ping me on IRC.
Meanwhile, I think documenting that it will return strings on schemaless queries is a fair enough compromise.
@josevalim with copying the integration tests into our repo, if I remember correctly, it was because some of the tests required slightly tweaking to get it to work of SQL Server and we preferred it passing with a slight tweak than just skipping the test completely.
I'd definitely like to get back to requiring them from the deps folder. I'm also not sure on the value of a test that required tweaking to get it to pass.
Will look at doing that as part of the PR as new tests have been added since we copied them over from Ecto.
Ebert has finished reviewing this Pull Request and has found:
You can see more details about this review at https://ebertapp.io/github/findmypast-oss/mssql_ecto/pulls/34.
The OTP :odbc adapter could absolutely return a more appropriate representation of bigint
Quick question that comes to mind: the :odbc adapter doesn't return the types from the query? So we can do the casting in Elixir land?
@josevalim :odbc
does not return the types from a query, here is an example return from our tests:
{:selected, ['test'], [["123456789012345678901234567890123456"]]}
with the query type, column names and column values coming back.
@jbachhardie @josevalim I have finished with re-syncing the integration tests, taking a similar policy to what was done when we first made this library, i.e. preferring to slightly alter the tests to get them to pass rather than ignore them entirely.
I'm happy for this to be merged now and released as 1.1
.
Any queries / objections before I do so?
Looks good to me, thank you!
Btw, is there anything we can do in the Ecto side to make future versions easier to update?
Looking at the PR into postgrex was useful on seeing what changes needed to be done at an adapter level.
I guess going further with that would be helpful. Sort of like a changelog, but only in the context of what would need to change at an adapter level?
25
Description
Changes to be compatible with Ecto 2.2, but also still backwards-compatible with Ecto 2.1
Motivation and Context
Keep reps up-to-date.
How Has This Been Tested?
Docker
Types of changes
Checklist: