Mutations on table with triggers - MsSql#1630
Conversation
seantleonard
left a comment
There was a problem hiding this comment.
Added more feedback. Thanks for updating so far, i think the most helpful thing now would be to get a few examples of the queries you generate with you business logic in mssqlquerybuilder. i dont' think the issue RFC captures all these examples.
severussundar
left a comment
There was a problem hiding this comment.
LGTM! Kudos for the detailed explanation about different approaches investigated/considered in the issue comments and the PR description.
seantleonard
left a comment
There was a problem hiding this comment.
I wrote some feedback on what a more helpful test description would look like. Instead of just saying 'validate that request succeeds' and what happens in the engine, you should explain the significance of the expected results. e.g. The returned Selection set field X has value Y because trigger Z changed the value.
| // Given input item { salary: 100 }, this test verifies that the selection would return salary = 50. | ||
| // Thus confirming that we return the data being updated by the trigger where, | ||
| // the trigger behavior is that it updates the salary to max(0,min(50,salary)). | ||
| string graphQLMutationName = "updateInternData"; | ||
| string graphQLMutation = @" | ||
| mutation{ | ||
| updateInternData(id: 1, months: 3, item: { salary: 100 }) { | ||
| id |
There was a problem hiding this comment.
nvm. looked at wrong query.
seantleonard
left a comment
There was a problem hiding this comment.
Thanks for bearing with me and addressing feedback. The code comments are very helpful and appreciated and will be useful whenever we read this code in the future
Happy to learn and get better :) |
Why make this change?
When there is a DML trigger configured on a table, we cannot use OUTPUT clause to return data. When an insert DML trigger is configured, OUTPUT clause cannot be used to return data because of the insert operation. When an update DML trigger is configured, OUTPUT clause cannot be used to return data because of the update operation. In such cases, we need to use a subsequent select statement to get the data after the mutation.
Details
What would handling of different kind of mutations look like when a trigger is enabled on a table?
DELETE: We don't need to return any data for deletions, so nothing will change for deletions.
UPDATE: For updates, we will cease to use the output clause. Instead, we will do a subsequent select query on the table to get the data. Since PK would already be provided to us we can easily do a select query. Not to mention, we can do this only when we determine that there is indeed a trigger enabled on the table. If not, we can continue with the same query that we generate in present day.
INSERT: When it comes to insertions, we can perform insertions on two kind of tables:
SCOPE_IDENTITY()method provided by Sql Server.. Once we get the autogen PK, in addition to having an inner join for non-autogen PKs as discussed in the previous approach, we can add an additionalWHEREpredicatewhere autogen_PK_field = SCOPE_IDENTITY(). Let us discuss more about this approach and the concerns:Delving deeper into Approach#3:
We can use
OUTPUTclause coupled withINTOclause to store the values for the primary keys in a temporary table. And then do a subsequent select query on the table to get the data using those primary keys. In the temporary table, we insert only those columns from the PK which are not autogenerated. Why? Because we cannot insert a value for an autogenerated column even in a temporary table. Hence in the select query, we will add an additional WHERE predicate (alongwith the join) for the autogenerated column in the PK. It is to be noted that MsSql supports only one IDENTITY/autogenerated column per table.We can choose to go with this approach only if we determine that there are any triggers enabled on the table.
Two assumptions which I am making here that I want to put ahead (btw, I don't think those are valid use cases) are:
What if primary key column has a datatype of timestamp? This concern arise because we cannot insert value for
timestampcolumn (which is what the approach includes - inserting value viaINTOclause). Well,rowversion/timestampis intended for versioning and tracking changes within a table, rather than serving as a primary key. Also, by design, primary key values should not be modified after they have been assigned to a record. So having a PK with timestamp datatype does not seem to be a valid use case to me.What if the trigger updates the values of the primary keys and the subsequent select query we are going to perform returns no data because the values of the PK has been changed in the table? This concern arises because technically, it is possible for triggers to update the value of PK columns. However, this is generally not recommended and can lead to undesirable consequences. Changing the value of a primary key can result in data integrity issues, as it may cause conflicts or break referential integrity constraints if other tables are referencing the primary key. It can also lead to confusion and difficulty in identifying records uniquely.
What is this change?
IsUpdateDMLTriggerEnabledandIsInsertDMLTriggerEnabledtoSourceDefinitionclass which if true will indicate that there is a corresponding DML trigger enabled on the table.SqlMetadataProvider.PopulateTriggerMetadataForTablewhose overridden implementation inMsSqlMetadataProviderwill populate the properties mentioned in (1) with appropriate metadata values.SqlInsertStructure,SqlUpsertQueryStructure,SqlUpdateStructureso that when trigger is configured for the mutation operation, a subsequent select query is used instead of theOUTPUTclause to return data.SCOPE_IDENTITY()function provided by sql server. Reference: https://learn.microsoft.com/en-us/sql/t-sql/functions/scope-identity-transact-sql?view=sql-server-ver16How was this tested?