Improving support for SQL Server 2008#5616
Conversation
Use TOP instead of LIMIT for queries against SQL server <11.0
Nested queries still fail due to how WITH() queries must be defined
Adding shortcut for running integration tests in containers
|
@mickhansen Any comments on this request? Just checking to see if there's anything that needs to change to get this merged. |
|
Sorry for not getting back to you @tahosa, this change brings up a few questions in regards to how we should organize the code. In general i'm not a whole lot happy about having the order code be duplicated inside limit. I don't know much about SQL Server but apparently you need to enforce an order if there's a limit? But perhaps that could be done earlier as part of a normalization step instead. |
|
The way it was implemented, yes, an order clause is necessary to use For 2008 and earlier though, the problem remains because the |
|
After some tinkering and testing, it looks like that ripped code block is necessary, unless we can otherwise pass more information to the function. Since an I think the current approach is the most straightforward way of implementing this. I can maybe see extracting the |
|
At a higher level than the query generator (likely the query interface), i would parse the options. If limit and no order by, add a default order of the primary key. That way the query generator doesn't have to care about adding a potential missing order, it just adds the limit. |
|
That approach would work for the Server 2012 and newer implementation, but we still need the actual order clause (if any) in order to support the older versions. Because we have to write a quite hackish nested query to get offsets to work, we at least need the ordering there to make sure the correct rows are returned for the offset. |
|
After some experimentation, it looks like implementing it as you describe isn't particularly simple. Since the ordering needs to be applied everywhere // Added to lib/query-interface.js at line 660 inside definition for select()
switch (this.sequelize.options.dialect) {
case 'mssql': // Add default order if limit or offset is used
if((options.limit || options.offset) && !options.order) {
options.order = [[model.primaryKeyField, 'ASC']];
}
break;
} |
|
Ah yeah, field aliasing is definitely a problem. We'll have to revisit this issue when i refactor how we handle renaming of fields. We'll have to consider this carefully, i feel it adds a lot of ugly code to the codebase. |
|
Is there anything in particular you'd like cleaned up in this PR? With the latest changes, I was at least able to eliminate the code duplication to find the orders, which I think does make it a lot cleaner. I agree though that the query to get SQL 2008 working is not very pretty. I can clean up the formatting and comment it more thoroughly to make it clear what it's doing for better maintainability. |
|
Mostly how selectFragmentBuilder is currently handled by calling it for string matches of SELECT. |
|
I'm not sure if I understand what you're saying about I feel this is a little clunky due to how |
|
Thanks, just today I stumbled into the same issues as the authors of the referenced issues. Hope it gets merged soon 👍 |
|
@mickhansen, any more comments or problems stopping this from merging? |
|
@tahosa No i'm sorry i've just been super busy :( |
|
/cc @janmeier |
|
The wrapping code is sort of wracking my brain, we definitely need some unit testing around it so we don't break it. |
|
I've added unit tests for the weird query, as well as some more documentation about what it's doing and cleaning up the formatting a bit. |
|
Bumping this again. Any other updates that are needed before this can be merged? |
|
This is going to sound bikesheddy, but i generally just don't like the approach with selectSqlFragmentBuilder, but i've been too swamped to think of better way myself. /cc @janmeier |
| //console.log(`Replacing '${mainQueryItems[0]}' with '${string}'`) | ||
| mainQueryItems[i] = string; | ||
| break; | ||
| } |
There was a problem hiding this comment.
What happens here exactly? You attempt to find the first query item containg a select statement and then replace? Shouldn't there be an easier way of doing this.
There was a problem hiding this comment.
You are correct, more or less about what this is for. It was originally designed to handle nesting queries, but since that's handled already by the recursive subquery calls, I'll simplify it.
|
Can we rename |
|
I've changed the function name and clarified what the loop is doing and how it works. I agree that this is less than ideal, but I think it would take a significant reorganization of the The problem stems from the assumption that queries must always be constructed in the order If I can get some time, I will consider tackling the job of breaking |
|
Thanks a bunch @tahosa ! (: |
This fixes most of the problems related to #5492, and #4404 with regard to support for older versions of SQL Server. It also updates the Docker image to the latest NodeJS stable and adds an npm task for running integration tests in Docker.
The main piece of this update is to alter the assumption that all database engines have the parts of a query in the same order (e.g. column definitions, then table definitions, then where conditions, then joins, then limits, then offsets). SQL Server 2008 has a different ordering, so the existing query builder could not build valid LIMIT and OFFSET type queries.
I pulled the query assembler into an overridable method which remains unchanged for all dialects except mssql. For mssql, there is now version handling to generate queries which work for SQL server 2008 and earlier as well as for 2012 and newer using the better new syntax.
Unit and integration tests were run against all dialects, and against 3 different versions of SQL Server Express (2008, 2012, 2014) and all passed. Where relevant, some tests were modified to ensure complex functionality was tested (e.g.
WHEREclauses in nested includes).