-
Notifications
You must be signed in to change notification settings - Fork 543
Conformance tests & bugfixes for SQLServer #1078
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
Changes from all commits
48abfb7
e79679f
c262f90
42b91de
3dce452
bf789f2
ffbcefd
b647078
915daba
21cf9bf
83c2af6
f7d3932
dd8cbb2
f8ab537
ba7f666
4d0fc05
fb8c824
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| version: '2' | ||
| services: | ||
| sqlserver: | ||
| image: mcr.microsoft.com/mssql/server:2019-GA-ubuntu-16.04 | ||
| ports: | ||
| - "1433:1433" | ||
| environment: | ||
| ACCEPT_EULA: Y | ||
| SA_PASSWORD: "Pass@Word1" |
| Original file line number | Diff line number | Diff line change | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -126,8 +126,7 @@ func (m *migration) executeMigrations() (migrationResult, error) { | ||||||||||||||||
| } | |||||||||||||||||
|
|
|||||||||||||||||
| func runCommand(tsql string, db *sql.DB) error { | |||||||||||||||||
| _, err := db.Exec(tsql) | |||||||||||||||||
| if err != nil { | |||||||||||||||||
| if _, err := db.Exec(tsql); err != nil { | |||||||||||||||||
| return err | |||||||||||||||||
| } | |||||||||||||||||
|
|
|||||||||||||||||
|
|
@@ -272,35 +271,85 @@ func (m *migration) createStoredProcedureIfNotExists(db *sql.DB, name string, es | ||||||||||||||||
| /* #nosec */ | |||||||||||||||||
| func (m *migration) ensureUpsertStoredProcedureExists(db *sql.DB, mr migrationResult) error { | |||||||||||||||||
| tsql := fmt.Sprintf(` | |||||||||||||||||
| CREATE PROCEDURE %s ( | |||||||||||||||||
| @Key %s, | |||||||||||||||||
| @Data NVARCHAR(MAX), | |||||||||||||||||
| @RowVersion BINARY(8)) | |||||||||||||||||
| AS | |||||||||||||||||
| IF (@RowVersion IS NOT NULL) | |||||||||||||||||
| BEGIN | |||||||||||||||||
| UPDATE [%s] | |||||||||||||||||
| SET [Data]=@Data, UpdateDate=GETDATE() | |||||||||||||||||
| WHERE [Key]=@Key AND RowVersion = @RowVersion | |||||||||||||||||
|
|
|||||||||||||||||
| RETURN | |||||||||||||||||
| END | |||||||||||||||||
|
|
|||||||||||||||||
| BEGIN TRY | |||||||||||||||||
| INSERT INTO [%s] ([Key], [Data]) VALUES (@Key, @Data); | |||||||||||||||||
| END TRY | |||||||||||||||||
|
|
|||||||||||||||||
| BEGIN CATCH | |||||||||||||||||
| IF ERROR_NUMBER() IN (2601, 2627) | |||||||||||||||||
| UPDATE [%s] | |||||||||||||||||
| SET [Data]=@Data, UpdateDate=GETDATE() | |||||||||||||||||
| WHERE [Key]=@Key AND RowVersion = ISNULL(@RowVersion, RowVersion) | |||||||||||||||||
| END CATCH`, | |||||||||||||||||
| CREATE PROCEDURE %s ( | |||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Overall, I think the logic here is still problematic. I understand the desire not to churn existing code, but it looks like there are still issues relative to the expected behaviors expressed in #2739 1c. Condensed here for reference:
"Write" in this case refers to an upsert (i.e. insert key if it does not already exist, otherwise update). The original code ignores the top half of that table, but it also looks like it already did not implement the bottom half correctly. Please correct any readings of the code here:
Preserving the original logic propagates these issues forwards. The new logic for handling FIRST_WRITE works, although it raises some questions about the specification for behavior:
That seems potentially inconsistent as an error contract for Dapr. @artursouza To keep this moving forwards, since you've already approved the PR and the new code is okay, I suggest we file a separate issue to track the logical inconsistencies in the existing code. In continuing the discussion of Dapr ETag/concurrency state handling, I'll also follow up on the issue with my questions on the expected error handling.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The issue for LAST_WRITE and eTag not null is behavior change beyond the scope of this change because it is a broader concern of the expectations for the state API behavior. I am OK with the current logic in this component. The error handling for silent etag mistmatch in the stored procedure is handled in code by checking number of rows affected. @CodeMonkeyLeet Let me know if you have any other concerns on this PR.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope, I think it's definitely a step forward and we should take it, thanks for reminding me about the downstream handling of returning errors from the zero rows updated check. |
|||||||||||||||||
| @Key %s, | |||||||||||||||||
| @Data NVARCHAR(MAX), | |||||||||||||||||
| @RowVersion BINARY(8), | |||||||||||||||||
| @FirstWrite BIT) | |||||||||||||||||
| AS | |||||||||||||||||
| IF (@FirstWrite=1) | |||||||||||||||||
| BEGIN | |||||||||||||||||
| IF (@RowVersion IS NOT NULL) | |||||||||||||||||
| BEGIN | |||||||||||||||||
| BEGIN TRANSACTION; | |||||||||||||||||
| IF NOT EXISTS (SELECT * FROM [%s] WHERE [KEY]=@KEY AND RowVersion = @RowVersion) | |||||||||||||||||
| BEGIN | |||||||||||||||||
| THROW 2601, ''FIRST-WRITE: COMPETING RECORD ALREADY WRITTEN.'', 1 | |||||||||||||||||
| END | |||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not always the case though: this case can also be because the row was never written before and the key does not exist.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's an excellent point. What do you suggest? Changing the error message? If the record does not exist it's not actually possible to specify an ETAG (Row Version) to be written -- the stored procedure / DB won't allow the use of ETAGs other than to update an existing record.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I would just change the error message to something more general |
|||||||||||||||||
| BEGIN | |||||||||||||||||
| UPDATE [%s] | |||||||||||||||||
| SET [Data]=@Data, UpdateDate=GETDATE() | |||||||||||||||||
| WHERE [Key]=@Key AND RowVersion = @RowVersion | |||||||||||||||||
| END | |||||||||||||||||
| COMMIT; | |||||||||||||||||
| END | |||||||||||||||||
| ELSE | |||||||||||||||||
| BEGIN | |||||||||||||||||
| BEGIN TRANSACTION; | |||||||||||||||||
| IF EXISTS (SELECT * FROM [%s] WHERE [KEY]=@KEY) | |||||||||||||||||
| BEGIN | |||||||||||||||||
| THROW 2601, ''FIRST-WRITE: COMPETING RECORD ALREADY WRITTEN.'', 1 | |||||||||||||||||
| END | |||||||||||||||||
| BEGIN | |||||||||||||||||
| BEGIN TRY | |||||||||||||||||
| INSERT INTO [%s] ([Key], [Data]) VALUES (@Key, @Data); | |||||||||||||||||
| END TRY | |||||||||||||||||
|
|
|||||||||||||||||
| BEGIN CATCH | |||||||||||||||||
| IF ERROR_NUMBER() IN (2601, 2627) | |||||||||||||||||
| UPDATE [%s] | |||||||||||||||||
| SET [Data]=@Data, UpdateDate=GETDATE() | |||||||||||||||||
| WHERE [Key]=@Key AND RowVersion = ISNULL(@RowVersion, RowVersion) | |||||||||||||||||
| END CATCH | |||||||||||||||||
| END | |||||||||||||||||
| COMMIT; | |||||||||||||||||
| END | |||||||||||||||||
| END | |||||||||||||||||
| ELSE | |||||||||||||||||
| BEGIN | |||||||||||||||||
| IF (@RowVersion IS NOT NULL) | |||||||||||||||||
| BEGIN | |||||||||||||||||
| UPDATE [%s] | |||||||||||||||||
| SET [Data]=@Data, UpdateDate=GETDATE() | |||||||||||||||||
| WHERE [Key]=@Key AND RowVersion = @RowVersion | |||||||||||||||||
| RETURN | |||||||||||||||||
| END | |||||||||||||||||
| ELSE | |||||||||||||||||
| BEGIN | |||||||||||||||||
| BEGIN TRY | |||||||||||||||||
| INSERT INTO [%s] ([Key], [Data]) VALUES (@Key, @Data); | |||||||||||||||||
| END TRY | |||||||||||||||||
|
|
|||||||||||||||||
| BEGIN CATCH | |||||||||||||||||
| IF ERROR_NUMBER() IN (2601, 2627) | |||||||||||||||||
| UPDATE [%s] | |||||||||||||||||
| SET [Data]=@Data, UpdateDate=GETDATE() | |||||||||||||||||
| WHERE [Key]=@Key AND RowVersion = ISNULL(@RowVersion, RowVersion) | |||||||||||||||||
| END CATCH | |||||||||||||||||
| END | |||||||||||||||||
| END | |||||||||||||||||
| `, | |||||||||||||||||
| mr.upsertProcFullName, | |||||||||||||||||
| mr.pkColumnType, | |||||||||||||||||
| m.store.tableName, | |||||||||||||||||
| m.store.tableName, | |||||||||||||||||
| m.store.tableName) | |||||||||||||||||
| m.store.tableName, | |||||||||||||||||
| m.store.tableName, | |||||||||||||||||
| m.store.tableName, | |||||||||||||||||
| m.store.tableName, | |||||||||||||||||
| m.store.tableName, | |||||||||||||||||
| m.store.tableName, | |||||||||||||||||
| ) | |||||||||||||||||
|
|
|||||||||||||||||
| return m.createStoredProcedureIfNotExists(db, mr.upsertProcName, tsql) | |||||||||||||||||
| } | |||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| apiVersion: dapr.io/v1alpha1 | ||
| kind: Component | ||
| metadata: | ||
| name: statestore | ||
| spec: | ||
| type: state.sqlserver | ||
| metadata: | ||
| - name: connectionString | ||
| value: "server=localhost;user id=sa;password=Pass@Word1;port=1433;" | ||
| - name: tableName | ||
| value: mytable |
Uh oh!
There was an error while loading. Please reload this page.