diff --git a/.github/workflows/config.yml b/.github/workflows/config.yml
index b497c44da..e734fcc67 100644
--- a/.github/workflows/config.yml
+++ b/.github/workflows/config.yml
@@ -1,9 +1,15 @@
name: SingleStore EntityFramework Core
-on: [push]
+on:
+ pull_request:
+ types: [opened, synchronize, reopened]
+ push:
+ branches: [main]
+ workflow_call:
+ workflow_dispatch:
env:
- DOTNET_VERSION: 8.0.405
+ DOTNET_VERSION: 9.0.101
LICENSE_KEY: ${{ secrets.LICENSE_KEY }}
SQL_USER_PASSWORD: ${{ secrets.SQL_USER_PASSWORD }}
S2MS_API_KEY: ${{ secrets.S2MS_API_KEY }}
@@ -115,13 +121,13 @@ jobs:
run: dotnet build SingleStore.EFCore.sln -c Release
- name: Run Unit Tests
- run: dotnet test test/EFCore.SingleStore.Tests -f net8.0 -c Release --no-build
+ run: dotnet test test/EFCore.SingleStore.Tests -f net9.0 -c Release --no-build
- name: Rebuild migrations
run: pwsh ./test/EFCore.SingleStore.IntegrationTests/scripts/rebuild.ps1
- name: Run Integration Tests
- run: dotnet test test/EFCore.SingleStore.IntegrationTests -f net8.0 -c Release --no-build
+ run: dotnet test test/EFCore.SingleStore.IntegrationTests -f net9.0 -c Release --no-build
- name: Run Functional Tests ${{ matrix.singlestore_version }} - ${{ matrix.func_test_script }}
run: ${{ matrix.func_test_script }}
@@ -161,13 +167,13 @@ jobs:
run: dotnet build SingleStore.EFCore.sln -c Release
- name: Run EFCore.SingleStore.Tests
- run: .\.github\workflows\test_setup\run-test-windows.ps1 -test_block EFCore.SingleStore.Tests -target_framework net8.0
+ run: .\.github\workflows\test_setup\run-test-windows.ps1 -test_block EFCore.SingleStore.Tests -target_framework net9.0
- name: Rebuild migrations
run: .\test\EFCore.SingleStore.IntegrationTests\scripts\rebuild.ps1
- name: Run EFCore.SingleStore.IntegrationTests
- run: .\.github\workflows\test_setup\run-test-windows.ps1 -test_block EFCore.SingleStore.IntegrationTests -target_framework net8.0
+ run: .\.github\workflows\test_setup\run-test-windows.ps1 -test_block EFCore.SingleStore.IntegrationTests -target_framework net9.0
- name: Run Functional Test Block
run: ${{ matrix.func_test_block_path }}
diff --git a/.github/workflows/test_setup/run-functional-tests1.ps1 b/.github/workflows/test_setup/run-functional-tests1.ps1
index 712939ef1..ddec77530 100644
--- a/.github/workflows/test_setup/run-functional-tests1.ps1
+++ b/.github/workflows/test_setup/run-functional-tests1.ps1
@@ -1,124 +1,124 @@
cd test\EFCore.SingleStore.FunctionalTests\
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsQuerySingleStoreTest.'
$TOTAL_FAILURES = ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.Ef6GroupBySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.Ef6GroupBySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.CompositeKeysQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.CompositeKeysQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.CompositeKeysSplitQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.CompositeKeysSplitQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.DateOnlyQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.DateOnlyQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.EscapesSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.EscapesSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.EntitySplittingQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.EntitySplittingQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.FromSqlQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.FromSqlQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.FieldsOnlyLoadSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.FieldsOnlyLoadSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.FiltersInheritanceQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.FiltersInheritanceQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.FromSqlSprocQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.FromSqlSprocQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.FunkyDataQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.FunkyDataQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.GearsOfWarFromSqlQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.GearsOfWarFromSqlQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.GearsOfWarQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.GearsOfWarQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.IncludeOneToOneSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.IncludeOneToOneSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.InheritanceQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.InheritanceQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsCollectionsQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsCollectionsQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftPocoChangeTrackingTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftPocoChangeTrackingTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.BoolIndexingOptimizationDisabledSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.BoolIndexingOptimizationDisabledSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftDomChangeTrackingTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftDomChangeTrackingTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftDomQueryTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftDomQueryTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.InheritanceRelationshipsQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.InheritanceRelationshipsQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsCollectionsSharedTypeQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsCollectionsSharedTypeQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsCollectionsSplitQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsCollectionsSplitQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTFiltersInheritanceQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTFiltersInheritanceQuerySingleStoreTest.'
$TOTAL_FAILURES = ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTGearsOfWarQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTGearsOfWarQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTInheritanceQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTInheritanceQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTRelationshipsQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTRelationshipsQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.WarningsSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.WarningsSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ConcurrencyDetectorDisabledSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ConcurrencyDetectorDisabledSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.CompositeKeyEndToEndSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.CompositeKeyEndToEndSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.CommandInterceptionSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.CommandInterceptionSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.BuiltInDataTypesSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.BuiltInDataTypesSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ConnectionSettingsSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ConnectionSettingsSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~ConnectionInterceptionSingleStoreTest'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~ConnectionInterceptionSingleStoreTest'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ConferencePlannerSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ConferencePlannerSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ConcurrencyDetectorEnabledSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ConcurrencyDetectorEnabledSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.DataAnnotationSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.DataAnnotationSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.CustomConvertersSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.CustomConvertersSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ConvertToProviderTypesSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ConvertToProviderTypesSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ConnectionSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ConnectionSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.EntitySplittingSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.EntitySplittingSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NonSharedModelUpdatesSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NonSharedModelUpdatesSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.DesignTimeSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.DesignTimeSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.DefaultValuesTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.DefaultValuesTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.DatabindingSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.DatabindingSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~FindSingleStoreTest'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~FindSingleStoreTest'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.FieldMappingSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.FieldMappingSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ExistingConnectionSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ExistingConnectionSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.GraphUpdatesSingleStoreTestBase.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.GraphUpdatesSingleStoreTestBase.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.GraphUpdatesSingleStoreClientNoActionTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.GraphUpdatesSingleStoreClientNoActionTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.GraphUpdatesSingleStoreClientCascadeTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.GraphUpdatesSingleStoreClientCascadeTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.FullInfrastructureMigrationsTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.FullInfrastructureMigrationsTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.LoggingSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.LoggingSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.LoadSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.LoadSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.LazyLoadProxySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.LazyLoadProxySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.KeysWithConvertersSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.KeysWithConvertersSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ManyToManyLoadSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ManyToManyLoadSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ManyToManyTrackingSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ManyToManyTrackingSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.MaterializationInterceptionSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.MaterializationInterceptionSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
diff --git a/.github/workflows/test_setup/run-functional-tests2.ps1 b/.github/workflows/test_setup/run-functional-tests2.ps1
index 1ccdb887a..e3bc7e4e3 100644
--- a/.github/workflows/test_setup/run-functional-tests2.ps1
+++ b/.github/workflows/test_setup/run-functional-tests2.ps1
@@ -1,146 +1,146 @@
cd test\EFCore.SingleStore.FunctionalTests\
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsCollectionsSplitSharedTypeQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsCollectionsSplitSharedTypeQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsSharedTypeQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsSharedTypeQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftPocoQueryTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftPocoQueryTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftStringChangeTrackingTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftStringChangeTrackingTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftStringQueryTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftStringQueryTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftDomChangeTrackingTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftDomChangeTrackingTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftDomQueryTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftDomQueryTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftPocoChangeTrackingTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftPocoChangeTrackingTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftPocoQueryTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftPocoQueryTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftStringChangeTrackingTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftStringChangeTrackingTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftStringQueryTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftStringQueryTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ManyToManyHeterogeneousQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ManyToManyHeterogeneousQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.MappingQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.MappingQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.MatchQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.MatchQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindAsTrackingQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindAsTrackingQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindAsNoTrackingQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindAsNoTrackingQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindAggregateQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindAggregateQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindAggregateOperatorsQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindAggregateOperatorsQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindDbFunctionsQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindDbFunctionsQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindCompiledQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindCompiledQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindChangeTrackingQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindChangeTrackingQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindGroupByQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindGroupByQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindFunctionsQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindFunctionsQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindEFPropertyIncludeQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindEFPropertyIncludeQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindKeylessEntitiesQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindKeylessEntitiesQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindJoinQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindJoinQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindIncludeQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindIncludeQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindIncludeNoTrackingQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindIncludeNoTrackingQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindQueryFiltersQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindQueryFiltersQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindMiscellaneousQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindMiscellaneousQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindSelectQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindSelectQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindSetOperationsQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindSetOperationsQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindSplitIncludeNoTrackingQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindSplitIncludeNoTrackingQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindSplitIncludeQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindSplitIncludeQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindSqlQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindSqlQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindStringIncludeQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindStringIncludeQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindWhereQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindWhereQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NullKeysSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NullKeysSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NullSemanticsQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NullSemanticsQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.OwnedEntityQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.OwnedEntityQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.OwnedQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.OwnedQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.QueryFilterFuncletizationSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.QueryFilterFuncletizationSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindNavigationsQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindNavigationsQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SharedTypeQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SharedTypeQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.QueryNoClientEvalSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.QueryNoClientEvalSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SimpleQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SimpleQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SqlExecutorSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SqlExecutorSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ToSqlQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ToSqlQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCGearsOfWarQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCGearsOfWarQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCFiltersInheritanceQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCFiltersInheritanceQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCInheritanceQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCInheritanceQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCManyToManyNoTrackingQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCManyToManyNoTrackingQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCManyToManyQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCManyToManyQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCRelationshipsQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCRelationshipsQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindBulkUpdatesSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindBulkUpdatesSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCFiltersInheritanceBulkUpdatesSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCFiltersInheritanceBulkUpdatesSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCInheritanceBulkUpdatesSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCInheritanceBulkUpdatesSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTFiltersInheritanceBulkUpdatesSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTFiltersInheritanceBulkUpdatesSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTInheritanceBulkUpdatesSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTInheritanceBulkUpdatesSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexTypeBulkUpdatesSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexTypeBulkUpdatesSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexTypesTrackingSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexTypesTrackingSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexTypeQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexTypeQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.BadDataJsonDeserializationSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.BadDataJsonDeserializationSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.AdHocComplexTypeQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.AdHocComplexTypeQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.AdHocAdvancedMappingsQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.AdHocAdvancedMappingsQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.OperatorsProceduralSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.OperatorsProceduralSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.OperatorsQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.OperatorsQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.OptionalDependentQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.OptionalDependentQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SqlQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SqlQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPHInheritanceQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPHInheritanceQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreConnectionStringOptionsValidatorTests.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreConnectionStringOptionsValidatorTests.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
cd ..\..\
diff --git a/.github/workflows/test_setup/run-functional-tests3.ps1 b/.github/workflows/test_setup/run-functional-tests3.ps1
index e46f3c35c..55a294e40 100644
--- a/.github/workflows/test_setup/run-functional-tests3.ps1
+++ b/.github/workflows/test_setup/run-functional-tests3.ps1
@@ -1,78 +1,78 @@
cd test\EFCore.SingleStore.FunctionalTests\
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.MigrationsSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.MigrationsSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.MigrationsInfrastructureSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.MigrationsInfrastructureSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.OptimisticConcurrencySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.OptimisticConcurrencySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NotificationEntitiesSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NotificationEntitiesSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindQueryTaggingQuerySingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindQueryTaggingQuerySingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.MusicStoreSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.MusicStoreSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.PropertyValuesSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.PropertyValuesSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.OverzealousInitializationSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.OverzealousInitializationSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreDatabaseModelFactoryTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreDatabaseModelFactoryTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.StoreValueGenerationSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.StoreValueGenerationSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreTypeMappingTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreTypeMappingTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.StoredProcedureUpdateSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.StoredProcedureUpdateSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreJsonMicrosoftTypeMappingTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreJsonMicrosoftTypeMappingTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreJsonNewtonsoftTypeMappingTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreJsonNewtonsoftTypeMappingTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreUpdateSqlGeneratorTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreUpdateSqlGeneratorTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~SaveChangesInterceptionSingleStoreTest'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~SaveChangesInterceptionSingleStoreTest'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SeedingSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SeedingSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SerializationSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SerializationSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreMigrationsSqlGeneratorTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreMigrationsSqlGeneratorTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreComplianceTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreComplianceTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreApiConsistencyTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreApiConsistencyTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.StoreGeneratedSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.StoreGeneratedSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.StoreGeneratedFixupSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.StoreGeneratedFixupSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SpatialSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SpatialSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreServiceCollectionExtensionsTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreServiceCollectionExtensionsTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreNetTopologySuiteApiConsistencyTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreNetTopologySuiteApiConsistencyTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TableSplittingSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TableSplittingSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTTableSplittingSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTTableSplittingSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TransactionInterceptionSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TransactionInterceptionSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TransactionSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TransactionSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TwoDatabasesSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TwoDatabasesSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.UpdatesSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.UpdatesSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ValueConvertersEndToEndSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ValueConvertersEndToEndSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.WithConstructorsSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.WithConstructorsSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.FiltersInheritanceBulkUpdatesSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.FiltersInheritanceBulkUpdatesSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.InheritanceBulkUpdatesSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.InheritanceBulkUpdatesSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
-dotnet.exe test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NonSharedModelBulkUpdatesSingleStoreTest.'
+dotnet.exe test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NonSharedModelBulkUpdatesSingleStoreTest.'
$TOTAL_FAILURES += ($LASTEXITCODE -ne 0)
cd ..\..\
diff --git a/.github/workflows/test_setup/run_functional_tests1.sh b/.github/workflows/test_setup/run_functional_tests1.sh
index f4e5b9c34..6f2c1daea 100755
--- a/.github/workflows/test_setup/run_functional_tests1.sh
+++ b/.github/workflows/test_setup/run_functional_tests1.sh
@@ -1,168 +1,168 @@
cd test/EFCore.SingleStore.FunctionalTests/
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TwoDatabasesSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TwoDatabasesSingleStoreTest.'
((TOTAL_FAILURES = $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.CompositeKeysQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.CompositeKeysQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.Ef6GroupBySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.Ef6GroupBySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.CompositeKeysSplitQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.CompositeKeysSplitQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.DateOnlyQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.DateOnlyQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.EscapesSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.EscapesSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.EntitySplittingQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.EntitySplittingQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.FromSqlQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.FromSqlQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.FieldsOnlyLoadSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.FieldsOnlyLoadSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.FiltersInheritanceQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.FiltersInheritanceQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.FromSqlSprocQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.FromSqlSprocQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.FunkyDataQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.FunkyDataQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.GearsOfWarFromSqlQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.GearsOfWarFromSqlQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.GearsOfWarQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.GearsOfWarQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.IncludeOneToOneSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.IncludeOneToOneSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.InheritanceQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.InheritanceQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsCollectionsQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsCollectionsQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftPocoChangeTrackingTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftPocoChangeTrackingTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.BoolIndexingOptimizationDisabledSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.BoolIndexingOptimizationDisabledSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftDomChangeTrackingTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftDomChangeTrackingTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftDomQueryTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftDomQueryTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.InheritanceRelationshipsQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.InheritanceRelationshipsQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsCollectionsSharedTypeQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsCollectionsSharedTypeQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsCollectionsSplitQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsCollectionsSplitQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsCollectionsSplitSharedTypeQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsCollectionsSplitSharedTypeQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsSharedTypeQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexNavigationsSharedTypeQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftPocoQueryTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftPocoQueryTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftStringChangeTrackingTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftStringChangeTrackingTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftStringQueryTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonMicrosoftStringQueryTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftDomChangeTrackingTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftDomChangeTrackingTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftDomQueryTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftDomQueryTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftPocoChangeTrackingTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftPocoChangeTrackingTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftPocoQueryTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftPocoQueryTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftStringChangeTrackingTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftStringChangeTrackingTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftStringQueryTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.JsonNewtonsoftStringQueryTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ManyToManyHeterogeneousQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ManyToManyHeterogeneousQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.MappingQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.MappingQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.MatchQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.MatchQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindAsTrackingQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindAsTrackingQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindAsNoTrackingQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindAsNoTrackingQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindAggregateQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindAggregateQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindAggregateOperatorsQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindAggregateOperatorsQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindDbFunctionsQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindDbFunctionsQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindCompiledQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindCompiledQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindChangeTrackingQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindChangeTrackingQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindGroupByQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindGroupByQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindFunctionsQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindFunctionsQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindEFPropertyIncludeQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindEFPropertyIncludeQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindKeylessEntitiesQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindKeylessEntitiesQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindJoinQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindJoinQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindIncludeQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindIncludeQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindIncludeNoTrackingQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindIncludeNoTrackingQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindQueryFiltersQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindQueryFiltersQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindMiscellaneousQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindMiscellaneousQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindSelectQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindSelectQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindSetOperationsQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindSetOperationsQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindSplitIncludeNoTrackingQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindSplitIncludeNoTrackingQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindSplitIncludeQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindSplitIncludeQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindSqlQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindSqlQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindStringIncludeQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindStringIncludeQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindWhereQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindWhereQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NullKeysSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NullKeysSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NullSemanticsQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NullSemanticsQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.OwnedEntityQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.OwnedEntityQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.OwnedQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.OwnedQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.QueryFilterFuncletizationSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.QueryFilterFuncletizationSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindNavigationsQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindNavigationsQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SharedTypeQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SharedTypeQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.QueryNoClientEvalSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.QueryNoClientEvalSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SimpleQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SimpleQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SqlExecutorSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SqlExecutorSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ToSqlQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ToSqlQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.WithConstructorsSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.WithConstructorsSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.FiltersInheritanceBulkUpdatesSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.FiltersInheritanceBulkUpdatesSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.InheritanceBulkUpdatesSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.InheritanceBulkUpdatesSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NonSharedModelBulkUpdatesSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NonSharedModelBulkUpdatesSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindBulkUpdatesSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindBulkUpdatesSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCFiltersInheritanceBulkUpdatesSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCFiltersInheritanceBulkUpdatesSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCInheritanceBulkUpdatesSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCInheritanceBulkUpdatesSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTFiltersInheritanceBulkUpdatesSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTFiltersInheritanceBulkUpdatesSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTInheritanceBulkUpdatesSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTInheritanceBulkUpdatesSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
cd ../../
diff --git a/.github/workflows/test_setup/run_functional_tests2.sh b/.github/workflows/test_setup/run_functional_tests2.sh
index 40e49cd6f..0da9e77f3 100755
--- a/.github/workflows/test_setup/run_functional_tests2.sh
+++ b/.github/workflows/test_setup/run_functional_tests2.sh
@@ -1,176 +1,176 @@
cd test/EFCore.SingleStore.FunctionalTests/
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.FullInfrastructureMigrationsTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.FullInfrastructureMigrationsTest.'
((TOTAL_FAILURES = $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCGearsOfWarQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCGearsOfWarQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCFiltersInheritanceQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCFiltersInheritanceQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCInheritanceQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCInheritanceQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCManyToManyNoTrackingQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCManyToManyNoTrackingQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCManyToManyQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCManyToManyQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCRelationshipsQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPCRelationshipsQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTFiltersInheritanceQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTFiltersInheritanceQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTGearsOfWarQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTGearsOfWarQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTInheritanceQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTInheritanceQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTRelationshipsQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTRelationshipsQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.WarningsSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.WarningsSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ConcurrencyDetectorDisabledSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ConcurrencyDetectorDisabledSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.CompositeKeyEndToEndSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.CompositeKeyEndToEndSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.CommandInterceptionSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.CommandInterceptionSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.BuiltInDataTypesSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.BuiltInDataTypesSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ConnectionSettingsSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ConnectionSettingsSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~ConnectionInterceptionSingleStoreTest'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~ConnectionInterceptionSingleStoreTest'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ConferencePlannerSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ConferencePlannerSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ConcurrencyDetectorEnabledSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ConcurrencyDetectorEnabledSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.DataAnnotationSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.DataAnnotationSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.CustomConvertersSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.CustomConvertersSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ConvertToProviderTypesSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ConvertToProviderTypesSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ConnectionSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ConnectionSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.EntitySplittingSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.EntitySplittingSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NonSharedModelUpdatesSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NonSharedModelUpdatesSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.DesignTimeSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.DesignTimeSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.DefaultValuesTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.DefaultValuesTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.DatabindingSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.DatabindingSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~FindSingleStoreTest'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~FindSingleStoreTest'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.FieldMappingSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.FieldMappingSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ExistingConnectionSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ExistingConnectionSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.GraphUpdatesSingleStoreTestBase.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.GraphUpdatesSingleStoreTestBase.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.GraphUpdatesSingleStoreClientNoActionTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.GraphUpdatesSingleStoreClientNoActionTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.GraphUpdatesSingleStoreClientCascadeTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.GraphUpdatesSingleStoreClientCascadeTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.LoggingSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.LoggingSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.LoadSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.LoadSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.LazyLoadProxySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.LazyLoadProxySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.KeysWithConvertersSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.KeysWithConvertersSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ManyToManyLoadSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ManyToManyLoadSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ManyToManyTrackingSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ManyToManyTrackingSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.MaterializationInterceptionSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.MaterializationInterceptionSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.MigrationsSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.MigrationsSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.MigrationsInfrastructureSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.MigrationsInfrastructureSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.OptimisticConcurrencySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.OptimisticConcurrencySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NotificationEntitiesSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NotificationEntitiesSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindQueryTaggingQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.NorthwindQueryTaggingQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.MusicStoreSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.MusicStoreSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.PropertyValuesSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.PropertyValuesSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.OverzealousInitializationSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.OverzealousInitializationSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreDatabaseModelFactoryTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreDatabaseModelFactoryTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.StoreValueGenerationSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.StoreValueGenerationSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreTypeMappingTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreTypeMappingTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.StoredProcedureUpdateSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.StoredProcedureUpdateSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreJsonMicrosoftTypeMappingTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreJsonMicrosoftTypeMappingTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreJsonNewtonsoftTypeMappingTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreJsonNewtonsoftTypeMappingTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreUpdateSqlGeneratorTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreUpdateSqlGeneratorTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~SaveChangesInterceptionSingleStoreTest'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~SaveChangesInterceptionSingleStoreTest'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SeedingSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SeedingSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SerializationSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SerializationSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreMigrationsSqlGeneratorTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreMigrationsSqlGeneratorTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreComplianceTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreComplianceTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreApiConsistencyTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreApiConsistencyTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.StoreGeneratedSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.StoreGeneratedSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.StoreGeneratedFixupSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.StoreGeneratedFixupSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SpatialSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SpatialSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreServiceCollectionExtensionsTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreServiceCollectionExtensionsTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreNetTopologySuiteApiConsistencyTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreNetTopologySuiteApiConsistencyTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TableSplittingSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TableSplittingSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTTableSplittingSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPTTableSplittingSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TransactionInterceptionSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TransactionInterceptionSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TransactionSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TransactionSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ValueConvertersEndToEndSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ValueConvertersEndToEndSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.UpdatesSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.UpdatesSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexTypeBulkUpdatesSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexTypeBulkUpdatesSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexTypesTrackingSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexTypesTrackingSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexTypeQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.ComplexTypeQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.BadDataJsonDeserializationSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.BadDataJsonDeserializationSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.AdHocComplexTypeQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.AdHocComplexTypeQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.AdHocAdvancedMappingsQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.AdHocAdvancedMappingsQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.OperatorsProceduralSingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.OperatorsProceduralSingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.OperatorsQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.OperatorsQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.OptionalDependentQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.OptionalDependentQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SqlQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SqlQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.TPHInheritanceQuerySingleStoreTest.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.TPHInheritanceQuerySingleStoreTest.'
((TOTAL_FAILURES += $? != 0))
-dotnet test -f net8.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreConnectionStringOptionsValidatorTests.'
+dotnet test -f net9.0 -c Release --no-build --filter 'FullyQualifiedName~.SingleStoreConnectionStringOptionsValidatorTests.'
((TOTAL_FAILURES += $? != 0))
diff --git a/Dependencies.targets b/Dependencies.targets
deleted file mode 100644
index 2238f6241..000000000
--- a/Dependencies.targets
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
- [8.0.13,8.0.999]
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
index b68a0b34d..c69eb8316 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -18,6 +18,7 @@
Copyright 2017-2022 © Pomelo Foundation. Copyright $([System.DateTime]::Now.Year) © SingleStore Inc.
singlestore_logo.png
true
+ true
latest
true
MIT
@@ -32,9 +33,9 @@
net8.0
- net8.0
+ net9.0
net8.0
- net8.0
+ net9.0
net8.0
diff --git a/Directory.Build.targets b/Directory.Build.targets
deleted file mode 100644
index 46b45a2ad..000000000
--- a/Directory.Build.targets
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/Directory.Packages.props b/Directory.Packages.props
new file mode 100644
index 000000000..4424c1ae8
--- /dev/null
+++ b/Directory.Packages.props
@@ -0,0 +1,59 @@
+
+
+
+ [9.0.0,9.0.999]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Pomelo.EFCore.SingleStore.sln b/Pomelo.EFCore.SingleStore.sln
deleted file mode 100644
index cf2435fd0..000000000
--- a/Pomelo.EFCore.SingleStore.sln
+++ /dev/null
@@ -1,102 +0,0 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.29209.152
-MinimumVisualStudioVersion = 15.0.26730.03
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7E8380DB-F015-407B-99C2-26404E551673}"
- ProjectSection(SolutionItems) = preProject
- src\Directory.Build.props = src\Directory.Build.props
- EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{6EC4A4AA-D865-4EF9-BD52-C2D0AA075CDF}"
- ProjectSection(SolutionItems) = preProject
- test\Directory.Build.props = test\Directory.Build.props
- EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F0BD39F9-2120-45AD-9FA0-9B080E38DAE5}"
- ProjectSection(SolutionItems) = preProject
- .editorconfig = .editorconfig
- Development.props.sample = Development.props.sample
- Directory.Build.props = Directory.Build.props
- Directory.Build.targets = Directory.Build.targets
- global.json = global.json
- Version.props = Version.props
- README.md = README.md
- dotnet-tools.json = dotnet-tools.json
- Dependencies.targets = Dependencies.targets
- NuGet.config = NuGet.config
- EndProjectSection
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.SingleStore", "src\EFCore.SingleStore\EFCore.SingleStore.csproj", "{FC2779F0-2A2A-4BE2-B5A8-FDA31A4A404A}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.SingleStore.Tests", "test\EFCore.SingleStore.Tests\EFCore.SingleStore.Tests.csproj", "{DE5212AE-4C17-4702-851D-0F1FC0D32E3C}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.SingleStore.FunctionalTests", "test\EFCore.SingleStore.FunctionalTests\EFCore.SingleStore.FunctionalTests.csproj", "{E7D9C7D9-1CDD-4745-975D-9752AE095418}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.SingleStore.IntegrationTests", "test\EFCore.SingleStore.IntegrationTests\EFCore.SingleStore.IntegrationTests.csproj", "{FF3B4140-42B3-4127-96D8-681858A5387B}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFCore.SingleStore.NTS", "src\EFCore.SingleStore.NTS\EFCore.SingleStore.NTS.csproj", "{7A727AB9-38F0-40E5-B134-7AC830B8D1EC}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFCore.SingleStore.Json.Microsoft", "src\EFCore.SingleStore.Json.Microsoft\EFCore.SingleStore.Json.Microsoft.csproj", "{C27A301A-F47E-4584-88DB-0474AE446405}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFCore.SingleStore.Json.Newtonsoft", "src\EFCore.SingleStore.Json.Newtonsoft\EFCore.SingleStore.Json.Newtonsoft.csproj", "{BBA0BB73-3D75-4F08-992F-A2CF9F52E7AD}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QueryBaselineUpdater", "tools\QueryBaselineUpdater\QueryBaselineUpdater.csproj", "{57293669-2ADF-448F-AE22-B49BAC4A335E}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{DD543966-92C7-4FE6-B953-3270E3E11D46}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {FC2779F0-2A2A-4BE2-B5A8-FDA31A4A404A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {FC2779F0-2A2A-4BE2-B5A8-FDA31A4A404A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {FC2779F0-2A2A-4BE2-B5A8-FDA31A4A404A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {FC2779F0-2A2A-4BE2-B5A8-FDA31A4A404A}.Release|Any CPU.Build.0 = Release|Any CPU
- {DE5212AE-4C17-4702-851D-0F1FC0D32E3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DE5212AE-4C17-4702-851D-0F1FC0D32E3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DE5212AE-4C17-4702-851D-0F1FC0D32E3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DE5212AE-4C17-4702-851D-0F1FC0D32E3C}.Release|Any CPU.Build.0 = Release|Any CPU
- {E7D9C7D9-1CDD-4745-975D-9752AE095418}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {E7D9C7D9-1CDD-4745-975D-9752AE095418}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {E7D9C7D9-1CDD-4745-975D-9752AE095418}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {E7D9C7D9-1CDD-4745-975D-9752AE095418}.Release|Any CPU.Build.0 = Release|Any CPU
- {FF3B4140-42B3-4127-96D8-681858A5387B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {FF3B4140-42B3-4127-96D8-681858A5387B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {FF3B4140-42B3-4127-96D8-681858A5387B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {FF3B4140-42B3-4127-96D8-681858A5387B}.Release|Any CPU.Build.0 = Release|Any CPU
- {7A727AB9-38F0-40E5-B134-7AC830B8D1EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7A727AB9-38F0-40E5-B134-7AC830B8D1EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7A727AB9-38F0-40E5-B134-7AC830B8D1EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7A727AB9-38F0-40E5-B134-7AC830B8D1EC}.Release|Any CPU.Build.0 = Release|Any CPU
- {C27A301A-F47E-4584-88DB-0474AE446405}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C27A301A-F47E-4584-88DB-0474AE446405}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C27A301A-F47E-4584-88DB-0474AE446405}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C27A301A-F47E-4584-88DB-0474AE446405}.Release|Any CPU.Build.0 = Release|Any CPU
- {BBA0BB73-3D75-4F08-992F-A2CF9F52E7AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {BBA0BB73-3D75-4F08-992F-A2CF9F52E7AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {BBA0BB73-3D75-4F08-992F-A2CF9F52E7AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {BBA0BB73-3D75-4F08-992F-A2CF9F52E7AD}.Release|Any CPU.Build.0 = Release|Any CPU
- {57293669-2ADF-448F-AE22-B49BAC4A335E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {57293669-2ADF-448F-AE22-B49BAC4A335E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {57293669-2ADF-448F-AE22-B49BAC4A335E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {57293669-2ADF-448F-AE22-B49BAC4A335E}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {FC2779F0-2A2A-4BE2-B5A8-FDA31A4A404A} = {7E8380DB-F015-407B-99C2-26404E551673}
- {DE5212AE-4C17-4702-851D-0F1FC0D32E3C} = {6EC4A4AA-D865-4EF9-BD52-C2D0AA075CDF}
- {E7D9C7D9-1CDD-4745-975D-9752AE095418} = {6EC4A4AA-D865-4EF9-BD52-C2D0AA075CDF}
- {FF3B4140-42B3-4127-96D8-681858A5387B} = {6EC4A4AA-D865-4EF9-BD52-C2D0AA075CDF}
- {7A727AB9-38F0-40E5-B134-7AC830B8D1EC} = {7E8380DB-F015-407B-99C2-26404E551673}
- {C27A301A-F47E-4584-88DB-0474AE446405} = {7E8380DB-F015-407B-99C2-26404E551673}
- {BBA0BB73-3D75-4F08-992F-A2CF9F52E7AD} = {7E8380DB-F015-407B-99C2-26404E551673}
- {57293669-2ADF-448F-AE22-B49BAC4A335E} = {DD543966-92C7-4FE6-B953-3270E3E11D46}
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {48E34212-4B35-4A81-92F9-3C25D4E76D6C}
- EndGlobalSection
-EndGlobal
diff --git a/Pomelo.snk b/Pomelo.snk
deleted file mode 100644
index 2d064c242..000000000
Binary files a/Pomelo.snk and /dev/null differ
diff --git a/SingleStore.EFCore.sln b/SingleStore.EFCore.sln
index c85d3382c..4c91165e8 100644
--- a/SingleStore.EFCore.sln
+++ b/SingleStore.EFCore.sln
@@ -17,12 +17,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.editorconfig = .editorconfig
Development.props.sample = Development.props.sample
Directory.Build.props = Directory.Build.props
- Directory.Build.targets = Directory.Build.targets
global.json = global.json
Version.props = Version.props
README.md = README.md
dotnet-tools.json = dotnet-tools.json
- Dependencies.targets = Dependencies.targets
+ Directory.Packages.props = Directory.Packages.props
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.SingleStore", "src\EFCore.SingleStore\EFCore.SingleStore.csproj", "{FC2779F0-2A2A-4BE2-B5A8-FDA31A4A404A}"
diff --git a/Version.props b/Version.props
index acf5578e2..1ca9b52d5 100644
--- a/Version.props
+++ b/Version.props
@@ -7,8 +7,8 @@
- "-rtm" - Code quality production ready, major release
- "-servicing" - Code quality production ready, mainly bugfixes
-->
- 8.0.3
- servicing
+ 9.0.0
+ rtm
-
true
true
diff --git a/dotnet-tools.json b/dotnet-tools.json
index a6a3adb61..305bdb11d 100644
--- a/dotnet-tools.json
+++ b/dotnet-tools.json
@@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"dotnet-ef": {
- "version": "8.0.13",
+ "version": "9.0.0",
"commands": [
"dotnet-ef"
]
diff --git a/global.json b/global.json
index b7e335716..db8627a23 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "8.0.100",
+ "version": "9.0.100",
"allowPrerelease": false,
"rollForward": "latestFeature"
}
diff --git a/src/EFCore.SingleStore.Json.Microsoft/EFCore.SingleStore.Json.Microsoft.csproj b/src/EFCore.SingleStore.Json.Microsoft/EFCore.SingleStore.Json.Microsoft.csproj
index a843cbfb9..2f83894c1 100644
--- a/src/EFCore.SingleStore.Json.Microsoft/EFCore.SingleStore.Json.Microsoft.csproj
+++ b/src/EFCore.SingleStore.Json.Microsoft/EFCore.SingleStore.Json.Microsoft.csproj
@@ -60,7 +60,6 @@
-
@@ -69,7 +68,7 @@
-
+
diff --git a/src/EFCore.SingleStore.Json.Newtonsoft/EFCore.SingleStore.Json.Newtonsoft.csproj b/src/EFCore.SingleStore.Json.Newtonsoft/EFCore.SingleStore.Json.Newtonsoft.csproj
index 06f673a8e..6980b622c 100644
--- a/src/EFCore.SingleStore.Json.Newtonsoft/EFCore.SingleStore.Json.Newtonsoft.csproj
+++ b/src/EFCore.SingleStore.Json.Newtonsoft/EFCore.SingleStore.Json.Newtonsoft.csproj
@@ -33,7 +33,7 @@
-
+
@@ -67,7 +67,6 @@
-
diff --git a/src/EFCore.SingleStore.NTS/EFCore.SingleStore.NTS.csproj b/src/EFCore.SingleStore.NTS/EFCore.SingleStore.NTS.csproj
index c9465c7a0..b4802f018 100644
--- a/src/EFCore.SingleStore.NTS/EFCore.SingleStore.NTS.csproj
+++ b/src/EFCore.SingleStore.NTS/EFCore.SingleStore.NTS.csproj
@@ -33,7 +33,7 @@
-
+
@@ -67,7 +67,6 @@
-
diff --git a/src/EFCore.SingleStore.NTS/Query/Internal/SingleStoreGeometryMemberTranslator.cs b/src/EFCore.SingleStore.NTS/Query/Internal/SingleStoreGeometryMemberTranslator.cs
index fd5eb8ec5..828d0ae85 100644
--- a/src/EFCore.SingleStore.NTS/Query/Internal/SingleStoreGeometryMemberTranslator.cs
+++ b/src/EFCore.SingleStore.NTS/Query/Internal/SingleStoreGeometryMemberTranslator.cs
@@ -87,7 +87,7 @@ public virtual SqlExpression Translate(SqlExpression instance, MemberInfo member
{
new CaseWhenClause(
_sqlExpressionFactory.IsNull(instance),
- _sqlExpressionFactory.Constant(null, RelationalTypeMapping.NullMapping))
+ _sqlExpressionFactory.Constant(null, returnType, resultTypeMapping))
},
sqlExpression);
}
diff --git a/src/EFCore.SingleStore.NTS/Query/Internal/SingleStoreLineStringMemberTranslator.cs b/src/EFCore.SingleStore.NTS/Query/Internal/SingleStoreLineStringMemberTranslator.cs
index 7d1dd9447..510e3a26e 100644
--- a/src/EFCore.SingleStore.NTS/Query/Internal/SingleStoreLineStringMemberTranslator.cs
+++ b/src/EFCore.SingleStore.NTS/Query/Internal/SingleStoreLineStringMemberTranslator.cs
@@ -88,7 +88,7 @@ public virtual SqlExpression Translate(SqlExpression instance, MemberInfo member
{
new CaseWhenClause(
_sqlExpressionFactory.IsNull(instance),
- _sqlExpressionFactory.Constant(null, RelationalTypeMapping.NullMapping))
+ _sqlExpressionFactory.Constant(null, returnType, resultTypeMapping))
},
sqlExpression);
}
diff --git a/src/EFCore.SingleStore.NTS/Query/Internal/SingleStoreMultiLineStringMemberTranslator.cs b/src/EFCore.SingleStore.NTS/Query/Internal/SingleStoreMultiLineStringMemberTranslator.cs
index e81900187..a2cfe6353 100644
--- a/src/EFCore.SingleStore.NTS/Query/Internal/SingleStoreMultiLineStringMemberTranslator.cs
+++ b/src/EFCore.SingleStore.NTS/Query/Internal/SingleStoreMultiLineStringMemberTranslator.cs
@@ -46,7 +46,7 @@ public virtual SqlExpression Translate(SqlExpression instance, MemberInfo member
{
new CaseWhenClause(
_sqlExpressionFactory.IsNull(instance),
- _sqlExpressionFactory.Constant(null, RelationalTypeMapping.NullMapping))
+ _sqlExpressionFactory.Constant(null, returnType))
},
sqlExpression);
}
diff --git a/src/EFCore.SingleStore/Design/Internal/SingleStoreAnnotationCodeGenerator.cs b/src/EFCore.SingleStore/Design/Internal/SingleStoreAnnotationCodeGenerator.cs
index 9ed684096..86e9975f4 100644
--- a/src/EFCore.SingleStore/Design/Internal/SingleStoreAnnotationCodeGenerator.cs
+++ b/src/EFCore.SingleStore/Design/Internal/SingleStoreAnnotationCodeGenerator.cs
@@ -13,6 +13,7 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Utilities;
using EntityFrameworkCore.SingleStore.Metadata.Internal;
@@ -82,6 +83,12 @@ private static readonly MethodInfo _propertyHasCharSetMethodInfo
typeof(PropertyBuilder),
typeof(string));
+ private static readonly MethodInfo _complexTypePropertyHasCharSetMethodInfo
+ = typeof(SingleStoreComplexTypePropertyBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(SingleStoreComplexTypePropertyBuilderExtensions.HasCharSet),
+ typeof(ComplexTypePropertyBuilder),
+ typeof(string));
+
public SingleStoreAnnotationCodeGenerator([JetBrains.Annotations.NotNull] AnnotationCodeGeneratorDependencies dependencies)
: base(dependencies)
{
@@ -264,6 +271,13 @@ protected override MethodCallCodeFragment GenerateFluentApi(IProperty property,
switch (annotation.Name)
{
case SingleStoreAnnotationNames.CharSet when annotation.Value is string { Length: > 0 } charSet:
+ if (property.DeclaringType is IComplexType)
+ {
+ return new MethodCallCodeFragment(
+ _complexTypePropertyHasCharSetMethodInfo,
+ charSet);
+ }
+
return new MethodCallCodeFragment(
_propertyHasCharSetMethodInfo,
charSet);
diff --git a/src/EFCore.SingleStore/EFCore.SingleStore.csproj b/src/EFCore.SingleStore/EFCore.SingleStore.csproj
index 3a61616fe..1bed12492 100644
--- a/src/EFCore.SingleStore/EFCore.SingleStore.csproj
+++ b/src/EFCore.SingleStore/EFCore.SingleStore.csproj
@@ -6,6 +6,8 @@
EntityFrameworkCore.SingleStore
EntityFrameworkCore.SingleStore
README.md
+ $(NoWarn);EF9100
+ $(NoWarn);CS1591
@@ -50,7 +52,7 @@
-
+
diff --git a/src/EFCore.SingleStore/Extensions/SingleStoreComplexTypePropertyBuilderExtensions.cs b/src/EFCore.SingleStore/Extensions/SingleStoreComplexTypePropertyBuilderExtensions.cs
new file mode 100644
index 000000000..15f4bc40d
--- /dev/null
+++ b/src/EFCore.SingleStore/Extensions/SingleStoreComplexTypePropertyBuilderExtensions.cs
@@ -0,0 +1,46 @@
+// Copyright (c) Pomelo Foundation. All rights reserved.
+// Copyright (c) SingleStore Inc. All rights reserved.
+// Licensed under the MIT. See LICENSE in the project root for license information.
+
+using JetBrains.Annotations;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using Microsoft.EntityFrameworkCore.Utilities;
+
+// ReSharper disable once CheckNamespace
+namespace Microsoft.EntityFrameworkCore
+{
+ ///
+ /// MySQL specific extension methods for .
+ ///
+ public static class SingleStoreComplexTypePropertyBuilderExtensions
+ {
+ ///
+ /// Configures the charset for the property's column.
+ ///
+ /// The builder for the property being configured.
+ /// The name of the charset to configure for the property's column.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasCharSet(
+ [NotNull] this ComplexTypePropertyBuilder propertyBuilder,
+ string charSet)
+ {
+ Check.NotNull(propertyBuilder, nameof(propertyBuilder));
+
+ var property = propertyBuilder.Metadata;
+ property.SetCharSet(charSet);
+
+ return propertyBuilder;
+ }
+
+ ///
+ /// Configures the charset for the property's column.
+ ///
+ /// The builder for the property being configured.
+ /// The name of the charset to configure for the property's column.
+ /// The same builder instance so that multiple calls can be chained.
+ public static ComplexTypePropertyBuilder HasCharSet(
+ [NotNull] this ComplexTypePropertyBuilder propertyBuilder,
+ string charSet)
+ => (ComplexTypePropertyBuilder)HasCharSet((ComplexTypePropertyBuilder)propertyBuilder, charSet);
+ }
+}
diff --git a/src/EFCore.SingleStore/Extensions/SingleStoreDbContextOptionsBuilderExtensions.cs b/src/EFCore.SingleStore/Extensions/SingleStoreDbContextOptionsBuilderExtensions.cs
index 654ec91dd..730057930 100644
--- a/src/EFCore.SingleStore/Extensions/SingleStoreDbContextOptionsBuilderExtensions.cs
+++ b/src/EFCore.SingleStore/Extensions/SingleStoreDbContextOptionsBuilderExtensions.cs
@@ -55,7 +55,11 @@ public static DbContextOptionsBuilder UseSingleStore(
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
ConfigureWarnings(optionsBuilder);
- mySqlOptionsAction?.Invoke(new SingleStoreDbContextOptionsBuilder(optionsBuilder));
+
+ var mySqlDbContextOptionsBuilder = new SingleStoreDbContextOptionsBuilder(optionsBuilder)
+ .TranslateParameterizedCollectionsToConstants();
+
+ mySqlOptionsAction?.Invoke(mySqlDbContextOptionsBuilder);
return optionsBuilder;
}
@@ -98,7 +102,11 @@ public static DbContextOptionsBuilder UseSingleStore(
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
ConfigureWarnings(optionsBuilder);
- mySqlOptionsAction?.Invoke(new SingleStoreDbContextOptionsBuilder(optionsBuilder));
+
+ var mySqlDbContextOptionsBuilder = new SingleStoreDbContextOptionsBuilder(optionsBuilder)
+ .TranslateParameterizedCollectionsToConstants();
+
+ mySqlOptionsAction?.Invoke(mySqlDbContextOptionsBuilder);
return optionsBuilder;
}
@@ -144,7 +152,11 @@ public static DbContextOptionsBuilder UseSingleStore(
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
ConfigureWarnings(optionsBuilder);
- mySqlOptionsAction?.Invoke(new SingleStoreDbContextOptionsBuilder(optionsBuilder));
+
+ var mySqlDbContextOptionsBuilder = new SingleStoreDbContextOptionsBuilder(optionsBuilder)
+ .TranslateParameterizedCollectionsToConstants();
+
+ mySqlOptionsAction?.Invoke(mySqlDbContextOptionsBuilder);
return optionsBuilder;
}
@@ -189,7 +201,11 @@ public static DbContextOptionsBuilder UseSingleStore(
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
ConfigureWarnings(optionsBuilder);
- mySqlOptionsAction?.Invoke(new SingleStoreDbContextOptionsBuilder(optionsBuilder));
+
+ var mySqlDbContextOptionsBuilder = new SingleStoreDbContextOptionsBuilder(optionsBuilder)
+ .TranslateParameterizedCollectionsToConstants();
+
+ mySqlOptionsAction?.Invoke(mySqlDbContextOptionsBuilder);
return optionsBuilder;
}
diff --git a/src/EFCore.SingleStore/Extensions/SingleStoreDbFunctionsExtensions.cs b/src/EFCore.SingleStore/Extensions/SingleStoreDbFunctionsExtensions.cs
index 091071be0..a25b982d5 100644
--- a/src/EFCore.SingleStore/Extensions/SingleStoreDbFunctionsExtensions.cs
+++ b/src/EFCore.SingleStore/Extensions/SingleStoreDbFunctionsExtensions.cs
@@ -9,6 +9,8 @@
// ReSharper disable once CheckNamespace
namespace Microsoft.EntityFrameworkCore
{
+ // TODO: Change method return types for units of `SECOND` and smaller from `int` to `long`.
+
///
/// Provides CLR methods that get translated to database functions when used in LINQ to Entities queries.
/// The methods on this class are accessed via .
@@ -167,9 +169,11 @@ public static class SingleStoreDbFunctionsExtensions
#endregion ConvertTimeZone
+ #region DateDiffYear
+
///
/// Counts the number of year boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(YEAR,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(YEAR,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -183,7 +187,7 @@ public static int DateDiffYear(
///
/// Counts the number of year boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(YEAR,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(YEAR,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -197,7 +201,7 @@ public static int DateDiffYear(
///
/// Counts the number of year boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(YEAR,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(YEAR,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -211,7 +215,7 @@ public static int DateDiffYear(
///
/// Counts the number of year boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(YEAR,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(YEAR,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -225,7 +229,7 @@ public static int DateDiffYear(
///
/// Counts the number of year boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(YEAR,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(YEAR,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -239,7 +243,7 @@ public static int DateDiffYear(
///
/// Counts the number of year boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(YEAR,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(YEAR,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -251,9 +255,101 @@ public static int DateDiffYear(
DateOnly? endDate)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffYear)));
+ #endregion DateDiffYear
+
+ #region DateDiffQuarter
+
+ ///
+ /// Counts the number of quarter boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(QUARTER,startDate,endDate)`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of quarter boundaries crossed between the dates.
+ public static int DateDiffQuarter(
+ [CanBeNull] this DbFunctions _,
+ DateTime startDate,
+ DateTime endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffQuarter)));
+
+ ///
+ /// Counts the number of quarter boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(QUARTER,startDate,endDate)`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of quarter boundaries crossed between the dates.
+ public static int? DateDiffQuarter(
+ [CanBeNull] this DbFunctions _,
+ DateTime? startDate,
+ DateTime? endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffQuarter)));
+
+ ///
+ /// Counts the number of quarter boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(QUARTER,startDate,endDate)`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of quarter boundaries crossed between the dates.
+ public static int DateDiffQuarter(
+ [CanBeNull] this DbFunctions _,
+ DateTimeOffset startDate,
+ DateTimeOffset endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffQuarter)));
+
+ ///
+ /// Counts the number of quarter boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(QUARTER,startDate,endDate)`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of quarter boundaries crossed between the dates.
+ public static int? DateDiffQuarter(
+ [CanBeNull] this DbFunctions _,
+ DateTimeOffset? startDate,
+ DateTimeOffset? endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffQuarter)));
+
+ ///
+ /// Counts the number of quarter boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(QUARTER,startDate,endDate)`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of quarter boundaries crossed between the dates.
+ public static int DateDiffQuarter(
+ [CanBeNull] this DbFunctions _,
+ DateOnly startDate,
+ DateOnly endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffQuarter)));
+
+ ///
+ /// Counts the number of quarter boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(QUARTER,startDate,endDate)`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of quarter boundaries crossed between the dates.
+ public static int? DateDiffQuarter(
+ [CanBeNull] this DbFunctions _,
+ DateOnly? startDate,
+ DateOnly? endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffQuarter)));
+
+ #endregion DateDiffQuarter
+
+ #region DateDiffMonth
+
///
/// Counts the number of month boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(MONTH,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(MONTH,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -267,7 +363,7 @@ public static int DateDiffMonth(
///
/// Counts the number of month boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(MONTH,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(MONTH,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -281,7 +377,7 @@ public static int DateDiffMonth(
///
/// Counts the number of month boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(MONTH,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(MONTH,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -295,7 +391,7 @@ public static int DateDiffMonth(
///
/// Counts the number of month boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(MONTH,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(MONTH,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -309,7 +405,7 @@ public static int DateDiffMonth(
///
/// Counts the number of month boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(MONTH,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(MONTH,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -323,7 +419,7 @@ public static int DateDiffMonth(
///
/// Counts the number of month boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(MONTH,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(MONTH,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -335,9 +431,101 @@ public static int DateDiffMonth(
DateOnly? endDate)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffMonth)));
+ #endregion DateDiffMonth
+
+ #region DateDiffWeek
+
+ ///
+ /// Counts the number of week boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(WEEK,startDate,endDate)`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of week boundaries crossed between the dates.
+ public static int DateDiffWeek(
+ [CanBeNull] this DbFunctions _,
+ DateTime startDate,
+ DateTime endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffWeek)));
+
+ ///
+ /// Counts the number of week boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(WEEK,startDate,endDate)`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of week boundaries crossed between the dates.
+ public static int? DateDiffWeek(
+ [CanBeNull] this DbFunctions _,
+ DateTime? startDate,
+ DateTime? endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffWeek)));
+
+ ///
+ /// Counts the number of week boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(WEEK,startDate,endDate)`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of week boundaries crossed between the dates.
+ public static int DateDiffWeek(
+ [CanBeNull] this DbFunctions _,
+ DateTimeOffset startDate,
+ DateTimeOffset endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffWeek)));
+
+ ///
+ /// Counts the number of week boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(WEEK,startDate,endDate)`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of week boundaries crossed between the dates.
+ public static int? DateDiffWeek(
+ [CanBeNull] this DbFunctions _,
+ DateTimeOffset? startDate,
+ DateTimeOffset? endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffWeek)));
+
+ ///
+ /// Counts the number of week boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(WEEK,startDate,endDate)`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of week boundaries crossed between the dates.
+ public static int DateDiffWeek(
+ [CanBeNull] this DbFunctions _,
+ DateOnly startDate,
+ DateOnly endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffWeek)));
+
+ ///
+ /// Counts the number of week boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(WEEK,startDate,endDate)`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of week boundaries crossed between the dates.
+ public static int? DateDiffWeek(
+ [CanBeNull] this DbFunctions _,
+ DateOnly? startDate,
+ DateOnly? endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffWeek)));
+
+ #endregion DateDiffWeek
+
+ #region DateDiffDay
+
///
/// Counts the number of day boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(DAY,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(DAY,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -351,7 +539,7 @@ public static int DateDiffDay(
///
/// Counts the number of day boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(DAY,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(DAY,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -365,7 +553,7 @@ public static int DateDiffDay(
///
/// Counts the number of day boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(DAY,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(DAY,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -379,7 +567,7 @@ public static int DateDiffDay(
///
/// Counts the number of day boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(DAY,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(DAY,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -393,7 +581,7 @@ public static int DateDiffDay(
///
/// Counts the number of day boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(DAY,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(DAY,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -407,7 +595,7 @@ public static int DateDiffDay(
///
/// Counts the number of day boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(DAY,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(DAY,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -419,9 +607,13 @@ public static int DateDiffDay(
DateOnly? endDate)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffDay)));
+ #endregion DateDiffDay
+
+ #region DateDiffHour
+
///
/// Counts the number of hour boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(HOUR,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(HOUR,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -435,7 +627,7 @@ public static int DateDiffHour(
///
/// Counts the number of hour boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(HOUR,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(HOUR,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -449,7 +641,7 @@ public static int DateDiffHour(
///
/// Counts the number of hour boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(HOUR,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(HOUR,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -463,7 +655,7 @@ public static int DateDiffHour(
///
/// Counts the number of hour boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(HOUR,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(HOUR,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -477,7 +669,7 @@ public static int DateDiffHour(
///
/// Counts the number of hour boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(HOUR,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(HOUR,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -491,7 +683,7 @@ public static int DateDiffHour(
///
/// Counts the number of hour boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(HOUR,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(HOUR,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -503,9 +695,13 @@ public static int DateDiffHour(
DateOnly? endDate)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffHour)));
+ #endregion DateDiffHour
+
+ #region DateDiffMinute
+
///
/// Counts the number of minute boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(MINUTE,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(MINUTE,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -519,7 +715,7 @@ public static int DateDiffMinute(
///
/// Counts the number of minute boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(MINUTE,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(MINUTE,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -533,7 +729,7 @@ public static int DateDiffMinute(
///
/// Counts the number of minute boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(MINUTE,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(MINUTE,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -547,7 +743,7 @@ public static int DateDiffMinute(
///
/// Counts the number of minute boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(MINUTE,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(MINUTE,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -561,7 +757,7 @@ public static int DateDiffMinute(
///
/// Counts the number of minute boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(MINUTE,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(MINUTE,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -575,7 +771,7 @@ public static int DateDiffMinute(
///
/// Counts the number of minute boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(MINUTE,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(MINUTE,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -587,9 +783,13 @@ public static int DateDiffMinute(
DateOnly? endDate)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffMinute)));
+ #endregion DateDiffMinute
+
+ #region DateDiffSecond
+
///
/// Counts the number of second boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(SECOND,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(SECOND,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -603,7 +803,7 @@ public static int DateDiffSecond(
///
/// Counts the number of second boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(SECOND,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(SECOND,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -617,7 +817,7 @@ public static int DateDiffSecond(
///
/// Counts the number of second boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(SECOND,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(SECOND,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -631,7 +831,7 @@ public static int DateDiffSecond(
///
/// Counts the number of second boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(SECOND,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(SECOND,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -645,7 +845,7 @@ public static int DateDiffSecond(
///
/// Counts the number of second boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(SECOND,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(SECOND,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -659,7 +859,7 @@ public static int DateDiffSecond(
///
/// Counts the number of second boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(SECOND,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(SECOND,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -671,9 +871,101 @@ public static int DateDiffSecond(
DateOnly? endDate)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffSecond)));
+ #endregion DateDiffSecond
+
+ #region DateDiffMillisecond
+
+ ///
+ /// Counts the number of millisecond boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate) DIV 1000`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of millisecond boundaries crossed between the dates.
+ public static int DateDiffMillisecond(
+ [CanBeNull] this DbFunctions _,
+ DateTime startDate,
+ DateTime endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffMillisecond)));
+
+ ///
+ /// Counts the number of millisecond boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate) DIV 1000`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of millisecond boundaries crossed between the dates.
+ public static int? DateDiffMillisecond(
+ [CanBeNull] this DbFunctions _,
+ DateTime? startDate,
+ DateTime? endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffMillisecond)));
+
+ ///
+ /// Counts the number of millisecond boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate) DIV 1000`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of millisecond boundaries crossed between the dates.
+ public static int DateDiffMillisecond(
+ [CanBeNull] this DbFunctions _,
+ DateTimeOffset startDate,
+ DateTimeOffset endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffMillisecond)));
+
+ ///
+ /// Counts the number of millisecond boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate) DIV 1000`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of millisecond boundaries crossed between the dates.
+ public static int? DateDiffMillisecond(
+ [CanBeNull] this DbFunctions _,
+ DateTimeOffset? startDate,
+ DateTimeOffset? endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffMillisecond)));
+
+ ///
+ /// Counts the number of millisecond boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate) DIV 1000`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of millisecond boundaries crossed between the dates.
+ public static int DateDiffMillisecond(
+ [CanBeNull] this DbFunctions _,
+ DateOnly startDate,
+ DateOnly endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffMillisecond)));
+
+ ///
+ /// Counts the number of millisecond boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate) DIV 1000`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of millisecond boundaries crossed between the dates.
+ public static int? DateDiffMillisecond(
+ [CanBeNull] this DbFunctions _,
+ DateOnly? startDate,
+ DateOnly? endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffMillisecond)));
+
+ #endregion DateDiffMillisecond
+
+ #region DateDiffMicrosecond
+
///
/// Counts the number of microsecond boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(MICROSECOND,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -687,7 +979,7 @@ public static int DateDiffMicrosecond(
///
/// Counts the number of microsecond boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(MICROSECOND,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -701,7 +993,7 @@ public static int DateDiffMicrosecond(
///
/// Counts the number of microsecond boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(MICROSECOND,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -715,7 +1007,7 @@ public static int DateDiffMicrosecond(
///
/// Counts the number of microsecond boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(MICROSECOND,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -729,7 +1021,7 @@ public static int DateDiffMicrosecond(
///
/// Counts the number of microsecond boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(MICROSECOND,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -743,7 +1035,7 @@ public static int DateDiffMicrosecond(
///
/// Counts the number of microsecond boundaries crossed between the startDate and endDate.
- /// Corresponds to TIMESTAMPDIFF(MICROSECOND,startDate,endDate).
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate)`.
///
/// The DbFunctions instance.
/// Starting date for the calculation.
@@ -755,6 +1047,186 @@ public static int DateDiffMicrosecond(
DateOnly? endDate)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffMicrosecond)));
+ #endregion DateDiffMicrosecond
+
+ #region DateDiffTick
+
+ ///
+ /// Counts the number of tick boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate) * 10`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of tick boundaries crossed between the dates.
+ public static int DateDiffTick(
+ [CanBeNull] this DbFunctions _,
+ DateTime startDate,
+ DateTime endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffTick)));
+
+ ///
+ /// Counts the number of tick boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate) * 10`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of tick boundaries crossed between the dates.
+ public static int? DateDiffTick(
+ [CanBeNull] this DbFunctions _,
+ DateTime? startDate,
+ DateTime? endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffTick)));
+
+ ///
+ /// Counts the number of tick boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate) * 10`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of tick boundaries crossed between the dates.
+ public static int DateDiffTick(
+ [CanBeNull] this DbFunctions _,
+ DateTimeOffset startDate,
+ DateTimeOffset endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffTick)));
+
+ ///
+ /// Counts the number of tick boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate) * 10`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of tick boundaries crossed between the dates.
+ public static int? DateDiffTick(
+ [CanBeNull] this DbFunctions _,
+ DateTimeOffset? startDate,
+ DateTimeOffset? endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffTick)));
+
+ ///
+ /// Counts the number of tick boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate) * 10`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of tick boundaries crossed between the dates.
+ public static int DateDiffTick(
+ [CanBeNull] this DbFunctions _,
+ DateOnly startDate,
+ DateOnly endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffTick)));
+
+ ///
+ /// Counts the number of tick boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate) * 10`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of tick boundaries crossed between the dates.
+ public static int? DateDiffTick(
+ [CanBeNull] this DbFunctions _,
+ DateOnly? startDate,
+ DateOnly? endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffTick)));
+
+ #endregion DateDiffTick
+
+ #region DateDiffNanosecond
+
+ ///
+ /// Counts the number of nanosecond boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate) * 1000`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of nanosecond boundaries crossed between the dates.
+ public static int DateDiffNanosecond(
+ [CanBeNull] this DbFunctions _,
+ DateTime startDate,
+ DateTime endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffNanosecond)));
+
+ ///
+ /// Counts the number of nanosecond boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate) * 1000`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of nanosecond boundaries crossed between the dates.
+ public static int? DateDiffNanosecond(
+ [CanBeNull] this DbFunctions _,
+ DateTime? startDate,
+ DateTime? endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffNanosecond)));
+
+ ///
+ /// Counts the number of nanosecond boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate) * 1000`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of nanosecond boundaries crossed between the dates.
+ public static int DateDiffNanosecond(
+ [CanBeNull] this DbFunctions _,
+ DateTimeOffset startDate,
+ DateTimeOffset endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffNanosecond)));
+
+ ///
+ /// Counts the number of nanosecond boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate) * 1000`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of nanosecond boundaries crossed between the dates.
+ public static int? DateDiffNanosecond(
+ [CanBeNull] this DbFunctions _,
+ DateTimeOffset? startDate,
+ DateTimeOffset? endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffNanosecond)));
+
+ ///
+ /// Counts the number of nanosecond boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate) * 1000`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of nanosecond boundaries crossed between the dates.
+ public static int DateDiffNanosecond(
+ [CanBeNull] this DbFunctions _,
+ DateOnly startDate,
+ DateOnly endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffNanosecond)));
+
+ ///
+ /// Counts the number of nanosecond boundaries crossed between the startDate and endDate.
+ /// Corresponds to `TIMESTAMPDIFF(MICROSECOND,startDate,endDate) * 1000`.
+ ///
+ /// The DbFunctions instance.
+ /// Starting date for the calculation.
+ /// Ending date for the calculation.
+ /// Number of nanosecond boundaries crossed between the dates.
+ public static int? DateDiffNanosecond(
+ [CanBeNull] this DbFunctions _,
+ DateOnly? startDate,
+ DateOnly? endDate)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DateDiffNanosecond)));
+
+ #endregion DateDiffNanosecond
+
+ #region Like
+
///
///
/// An implementation of the SQL LIKE operation. On relational databases this is usually directly
@@ -769,7 +1241,9 @@ public static int DateDiffMicrosecond(
///
/// The DbFunctions instance.
/// The property of entity that is to be matched.
- /// The pattern which may involve wildcards %,_,[,],^.
+ ///
+ /// The pattern which may involve the wildcards `%` and `_`. The character `\` is used to escape wildcards and itself.
+ ///
/// true if there is a match.
public static bool Like(
[CanBeNull] this DbFunctions _,
@@ -790,10 +1264,10 @@ public static bool Like(
///
/// The DbFunctions instance.
/// The property of entity that is to be matched.
- /// The pattern which may involve wildcards %,_,[,],^.
+ /// The pattern which may involve the wildcards `%` and `_`.
///
- /// The escape character (as a single character string) to use in front of %,_,[,],^
- /// if they are not used as wildcards.
+ /// The escape character (as a single character string) to use in front of `%` and `_` (if they are not used as wildcards), and
+ /// itself.
///
/// true if there is a match.
public static bool Like(
@@ -803,6 +1277,10 @@ public static bool Like(
[CanBeNull] string escapeCharacter)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Like)));
+ #endregion Like
+
+ #region Match
+
///
///
/// An implementation of the SQL MATCH operation for Full Text search.
@@ -883,6 +1361,10 @@ public static double Match(
[CanBeNull] string pattern)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Match)));
+ #endregion Match
+
+ #region Misc
+
///
///
/// For a string argument `value`, Hex() returns a hexadecimal string representation of `value` where
@@ -959,5 +1441,7 @@ public static float Radians(
this DbFunctions _,
float degrees)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Radians)));
+
+ #endregion Misc
}
}
diff --git a/src/EFCore.SingleStore/Extensions/SingleStoreJsonDbFunctionsExtensions.cs b/src/EFCore.SingleStore/Extensions/SingleStoreJsonDbFunctionsExtensions.cs
index 5113f3a0e..c74e329b8 100644
--- a/src/EFCore.SingleStore/Extensions/SingleStoreJsonDbFunctionsExtensions.cs
+++ b/src/EFCore.SingleStore/Extensions/SingleStoreJsonDbFunctionsExtensions.cs
@@ -85,6 +85,20 @@ public static T JsonExtract(
[NotNull] params string[] paths)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(JsonExtract)));
+ ///
+ /// Checks if overlaps .
+ ///
+ /// DbFunctions instance
+ ///
+ /// A JSON column or value. Can be a JSON DOM object, a string property mapped to JSON, or a user POCO mapped to JSON.
+ ///
+ ///
+ /// A JSON column or value. Can be a JSON DOM object, a string, or a user POCO mapped to JSON.
+ ///
+ public static bool JsonOverlaps(
+ [CanBeNull] this DbFunctions _, [NotNull] object json1, [NotNull] object json2)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(JsonOverlaps)));
+
///
/// Checks if contains .
///
diff --git a/src/EFCore.SingleStore/Infrastructure/MariaDbServerVersion.cs b/src/EFCore.SingleStore/Infrastructure/MariaDbServerVersion.cs
index 1d57fb753..f4ce5a6c9 100644
--- a/src/EFCore.SingleStore/Infrastructure/MariaDbServerVersion.cs
+++ b/src/EFCore.SingleStore/Infrastructure/MariaDbServerVersion.cs
@@ -18,6 +18,9 @@ public class MariaDbServerVersion : ServerVersion
public static readonly string MariaDbTypeIdentifier = nameof(ServerType.MariaDb).ToLowerInvariant();
public static readonly ServerVersion LatestSupportedServerVersion = new MariaDbServerVersion(new Version(10, 9, 4));
+ public override string DefaultUtf8CsCollation => Supports.DefaultCharSetUtf8Mb4 ? "utf8mb4_bin" : "utf8_bin";
+ public override string DefaultUtf8CiCollation => Supports.DefaultCharSetUtf8Mb4 ? "utf8mb4_general_ci" : "utf8_general_ci";
+
public override ServerVersionSupport Supports { get; }
public MariaDbServerVersion(Version version)
diff --git a/src/EFCore.SingleStore/Infrastructure/ServerVersion.cs b/src/EFCore.SingleStore/Infrastructure/ServerVersion.cs
index 546d2fdea..da34bb51c 100644
--- a/src/EFCore.SingleStore/Infrastructure/ServerVersion.cs
+++ b/src/EFCore.SingleStore/Infrastructure/ServerVersion.cs
@@ -5,6 +5,8 @@
using System;
using System.Data;
using System.Text.RegularExpressions;
+using System.Threading;
+using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Utilities;
using SingleStoreConnector;
using EntityFrameworkCore.SingleStore.Infrastructure;
@@ -37,8 +39,8 @@ protected ServerVersion(Version version, ServerType type, string typeIdentifier
public virtual int MaxKeyLength => Supports.LargerKeyLength ? 3072 : 767;
public virtual CharSet DefaultCharSet => Supports.DefaultCharSetUtf8Mb4 ? CharSet.Utf8Mb4 : CharSet.Utf8;
- public virtual string DefaultUtf8CsCollation => Supports.DefaultCharSetUtf8Mb4 ? "utf8mb4_bin" : "utf8_bin";
- public virtual string DefaultUtf8CiCollation => Supports.DefaultCharSetUtf8Mb4 ? "utf8mb4_general_ci" : "utf8_general_ci";
+ public abstract string DefaultUtf8CsCollation { get; }
+ public abstract string DefaultUtf8CiCollation { get; }
public override bool Equals(object obj)
=> obj is ServerVersion version &&
@@ -82,6 +84,36 @@ public static ServerVersion AutoDetect(string connectionString)
return Parse(connection.S2ServerVersion);
}
+ ///
+ /// Retrieves the (version number and server type) from a database server.
+ ///
+ /// The connection string.
+ /// A to observe while waiting for the task to complete.
+ ///
+ /// A task that represents the asynchronous auto detect operation. The task result contains the of the database.
+ ///
+ ///
+ /// Uses a connection string to open a connection to the database server and then executes a command.
+ /// The connection will ignore the database specified in the connection string. It therefore makes not difference, whether the
+ /// database already exists or not.
+ ///
+ public static async Task AutoDetectAsync(string connectionString, CancellationToken cancellationToken = default)
+ {
+ var connection = new SingleStoreConnection(
+ new SingleStoreConnectionStringBuilder(connectionString)
+ {
+ Database = string.Empty,
+ AutoEnlist = false,
+ Pooling = false,
+ }.ConnectionString);
+
+ await using (connection.ConfigureAwait(false))
+ {
+ await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
+ return Parse(connection.S2ServerVersion);
+ }
+ }
+
///
/// Retrieves the (version number and server type) from a database server.
///
@@ -118,6 +150,102 @@ public static ServerVersion AutoDetect(SingleStoreConnection connection)
return Parse(serverVersion);
}
+ ///
+ /// Retrieves the (version number and server type) from a database server.
+ ///
+ /// The connection.
+ /// A to observe while waiting for the task to complete.
+ ///
+ /// A task that represents the asynchronous auto detect operation. The task result contains the of the database.
+ ///
+ ///
+ /// Uses a connection to the database server to execute a command.
+ /// If the connection has already been opened, the connection is is being used as is. Otherwise, the connection is being cloned and
+ /// ignores any database specified in the connection string of the connection. It therefore makes not difference, whether the
+ /// database already exists or not, and the of the parameter after the
+ /// return of the call is the same as before the call.
+ ///
+ public static async Task AutoDetectAsync(SingleStoreConnection connection, CancellationToken cancellationToken = default)
+ {
+ string serverVersion;
+
+ if (connection.State != ConnectionState.Open)
+ {
+ var clonedConnection = connection.CloneWith(
+ new SingleStoreConnectionStringBuilder(connection.ConnectionString)
+ {
+ Database = string.Empty,
+ AutoEnlist = false,
+ Pooling = false,
+ }.ConnectionString);
+
+ await using (clonedConnection.ConfigureAwait(false))
+ {
+ await clonedConnection.OpenAsync(cancellationToken).ConfigureAwait(false);
+ serverVersion = clonedConnection.S2ServerVersion;
+ }
+ }
+ else
+ {
+ serverVersion = connection.S2ServerVersion;
+ }
+
+ return Parse(serverVersion);
+ }
+
+ ///
+ /// Retrieves the (version number and server type) from a database server.
+ ///
+ /// The data source.
+ /// The .
+ ///
+ /// Uses a that represents a database to execute a command.
+ /// The data source is used to create a connection to the database server and ignores any database specified in the underlying
+ /// connection string. It therefore makes not difference, whether a specified database already exists or not.
+ ///
+ public static ServerVersion AutoDetect(SingleStoreDataSource dataSource)
+ {
+ using var connection = dataSource.CreateConnection();
+ connection.ConnectionString = new SingleStoreConnectionStringBuilder(connection.ConnectionString)
+ {
+ Database = string.Empty,
+ AutoEnlist = false,
+ Pooling = false,
+ }.ConnectionString;
+ connection.Open();
+ return Parse(connection.S2ServerVersion);
+ }
+
+ ///
+ /// Retrieves the (version number and server type) from a database server.
+ ///
+ /// The data source.
+ /// A to observe while waiting for the task to complete.
+ ///
+ /// A task that represents the asynchronous auto detect operation. The task result contains the of the database.
+ ///
+ ///
+ /// Uses a that represents a database to execute a command.
+ /// The data source is used to create a connection to the database server and ignores any database specified in the underlying
+ /// connection string. It therefore makes not difference, whether a specified database already exists or not.
+ ///
+ public static async Task AutoDetectAsync(SingleStoreDataSource dataSource, CancellationToken cancellationToken = default)
+ {
+ var connection = dataSource.CreateConnection();
+ await using (connection.ConfigureAwait(false))
+ {
+ connection.ConnectionString = new SingleStoreConnectionStringBuilder(connection.ConnectionString)
+ {
+ Database = string.Empty,
+ AutoEnlist = false,
+ Pooling = false,
+ }.ConnectionString;
+
+ await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
+ return Parse(connection.S2ServerVersion);
+ }
+ }
+
///
/// Converts a string, containing the server version and type, into a .
///
diff --git a/src/EFCore.SingleStore/Infrastructure/ServerVersionSupport.cs b/src/EFCore.SingleStore/Infrastructure/ServerVersionSupport.cs
index 52761f85e..f6e187dca 100644
--- a/src/EFCore.SingleStore/Infrastructure/ServerVersionSupport.cs
+++ b/src/EFCore.SingleStore/Infrastructure/ServerVersionSupport.cs
@@ -60,6 +60,7 @@ public virtual bool PropertyOrVersion(string propertyNameOrServerVersion)
public virtual bool CrossApply => false;
public virtual bool OuterReferenceInMultiLevelSubquery => false;
public virtual bool Json => false;
+ public virtual bool JsonOverlaps => false;
public virtual bool GeneratedColumns => false;
public virtual bool NullableGeneratedColumns => false;
public virtual bool ParenthesisEnclosedGeneratedColumnExpressions => false;
@@ -97,6 +98,8 @@ public virtual bool PropertyOrVersion(string propertyNameOrServerVersion)
public virtual bool Values => false;
public virtual bool ValuesWithRows => false;
public virtual bool WhereSubqueryReferencesOuterQuery => false;
+ public virtual bool FieldReferenceInTableValueConstructor => false;
+ public virtual bool CollationCharacterSetApplicabilityWithFullCollationNameColumn => false;
public virtual bool JsonTableImplementationStable => JsonTable;
public virtual bool JsonTableImplementationWithoutMySqlBugs => JsonTable;
diff --git a/src/EFCore.SingleStore/Infrastructure/SingleStoreServerVersion.cs b/src/EFCore.SingleStore/Infrastructure/SingleStoreServerVersion.cs
index 53b08c537..c1bd94151 100644
--- a/src/EFCore.SingleStore/Infrastructure/SingleStoreServerVersion.cs
+++ b/src/EFCore.SingleStore/Infrastructure/SingleStoreServerVersion.cs
@@ -19,6 +19,9 @@ public class SingleStoreServerVersion : ServerVersion
public override ServerVersionSupport Supports { get; }
+ public override string DefaultUtf8CsCollation => Supports.DefaultCharSetUtf8Mb4 ? "utf8mb4_bin" : "utf8_bin";
+ public override string DefaultUtf8CiCollation => Supports.DefaultCharSetUtf8Mb4 ? "utf8mb4_general_ci" : "utf8_general_ci";
+
public SingleStoreServerVersion(Version version)
: base(version, ServerType.SingleStore)
{
@@ -93,9 +96,12 @@ internal SingleStoreServerVersionSupport([NotNull] ServerVersion serverVersion)
public override bool LimitWithNonConstantValue => false;
public override bool JsonTable => false;
public override bool JsonValue => false;
+ public override bool JsonOverlaps => false;
public override bool Values => false;
public override bool ValuesWithRows => false;
public override bool WhereSubqueryReferencesOuterQuery => false;
+ public override bool FieldReferenceInTableValueConstructor => true;
+ public override bool CollationCharacterSetApplicabilityWithFullCollationNameColumn => false;
public override bool JsonTableImplementationStable => false;
public override bool JsonTableImplementationWithoutMySqlBugs => false; // Other non-fatal bugs regarding JSON_TABLE.
diff --git a/src/EFCore.SingleStore/Internal/SingleStoreOptions.cs b/src/EFCore.SingleStore/Internal/SingleStoreOptions.cs
index 04b72bb94..acb0c65ec 100644
--- a/src/EFCore.SingleStore/Internal/SingleStoreOptions.cs
+++ b/src/EFCore.SingleStore/Internal/SingleStoreOptions.cs
@@ -11,6 +11,7 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using EntityFrameworkCore.SingleStore.Infrastructure;
using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Storage.Internal;
using Microsoft.Extensions.DependencyInjection;
using SingleStoreConnector;
@@ -55,7 +56,7 @@ public virtual void Initialize(IDbContextOptions options)
var mySqlOptions = options.FindExtension() ?? new SingleStoreOptionsExtension();
var mySqlJsonOptions = (SingleStoreJsonOptionsExtension)options.Extensions.LastOrDefault(e => e is SingleStoreJsonOptionsExtension);
- ConnectionSettings = GetConnectionSettings(mySqlOptions);
+ ConnectionSettings = GetConnectionSettings(mySqlOptions, options);
DataSource = mySqlOptions.DataSource;
ServerVersion = mySqlOptions.ServerVersion ?? throw new InvalidOperationException($"The {nameof(ServerVersion)} has not been set.");
NoBackslashEscapes = mySqlOptions.NoBackslashEscapes;
@@ -75,7 +76,7 @@ public virtual void Validate(IDbContextOptions options)
{
var mySqlOptions = options.FindExtension() ?? new SingleStoreOptionsExtension();
var mySqlJsonOptions = (SingleStoreJsonOptionsExtension)options.Extensions.LastOrDefault(e => e is SingleStoreJsonOptionsExtension);
- var connectionSettings = GetConnectionSettings(mySqlOptions);
+ var connectionSettings = GetConnectionSettings(mySqlOptions, options);
//
// CHECK: To we have to ensure that the ApplicationServiceProvider itself is not replaced, because we rely on it in our
@@ -245,10 +246,12 @@ protected virtual SingleStoreDefaultDataTypeMappings ApplyDefaultDataTypeMapping
return defaultDataTypeMappings;
}
- private static SingleStoreConnectionSettings GetConnectionSettings(SingleStoreOptionsExtension relationalOptions)
+ private static SingleStoreConnectionSettings GetConnectionSettings(SingleStoreOptionsExtension relationalOptions, IDbContextOptions options)
=> relationalOptions.Connection != null
? new SingleStoreConnectionSettings(relationalOptions.Connection)
- : new SingleStoreConnectionSettings(relationalOptions.ConnectionString);
+ : new SingleStoreConnectionSettings(
+ new NamedConnectionStringResolver(options)
+ .ResolveConnectionString(relationalOptions.ConnectionString ?? string.Empty));
protected virtual bool Equals(SingleStoreOptions other)
{
diff --git a/src/EFCore.SingleStore/Migrations/Internal/SingleStoreHistoryRepository.cs b/src/EFCore.SingleStore/Migrations/Internal/SingleStoreHistoryRepository.cs
index 71a9fe026..469cb5893 100644
--- a/src/EFCore.SingleStore/Migrations/Internal/SingleStoreHistoryRepository.cs
+++ b/src/EFCore.SingleStore/Migrations/Internal/SingleStoreHistoryRepository.cs
@@ -3,17 +3,22 @@
// Licensed under the MIT. See LICENSE in the project root for license information.
using System;
+using System.Data.Common;
using System.Linq;
using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using EntityFrameworkCore.SingleStore.Infrastructure;
+using EntityFrameworkCore.SingleStore.Storage.Internal;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
-using EntityFrameworkCore.SingleStore.Infrastructure;
-using EntityFrameworkCore.SingleStore.Storage.Internal;
+using SingleStoreConnector;
namespace EntityFrameworkCore.SingleStore.Migrations.Internal
{
@@ -21,12 +26,157 @@ public class SingleStoreHistoryRepository : HistoryRepository
{
private const string MigrationsScript = nameof(MigrationsScript);
+ // Similar to the old GET_LOCK timeout (72h), but now used as command timeout while waiting on row lock.
+ private const int LockTimeoutSeconds = 60 * 60 * 24 * 3;
+
private readonly SingleStoreSqlGenerationHelper _sqlGenerationHelper;
+ private readonly string _lockConnectionString;
+
public SingleStoreHistoryRepository([NotNull] HistoryRepositoryDependencies dependencies)
: base(dependencies)
{
_sqlGenerationHelper = (SingleStoreSqlGenerationHelper)dependencies.SqlGenerationHelper;
+
+ // Capture early to avoid password being stripped after Open() (PersistSecurityInfo default is false).
+ _lockConnectionString = dependencies.Connection.ConnectionString
+ ?? dependencies.Connection.DbConnection.ConnectionString;
+ }
+
+ // We now use a dedicated connection + transaction to hold a row lock,
+ // therefore the lock must be released explicitly by disposing the lock object.
+ public override LockReleaseBehavior LockReleaseBehavior
+ => LockReleaseBehavior.Explicit;
+
+ public override IMigrationsDatabaseLock AcquireDatabaseLock()
+ {
+ Dependencies.MigrationsLogger.AcquiringMigrationLock();
+
+ var lockConnection = CreateLockConnection();
+ try
+ {
+ EnsureLockTable(lockConnection);
+
+ var transaction = lockConnection.BeginTransaction();
+
+ AcquireRowLock(lockConnection, transaction);
+
+ return new SingleStoreMigrationDatabaseLock(this, lockConnection, transaction);
+ }
+ catch
+ {
+ lockConnection.Dispose();
+ throw;
+ }
+ }
+
+ public override async Task AcquireDatabaseLockAsync(CancellationToken cancellationToken = default)
+ {
+ Dependencies.MigrationsLogger.AcquiringMigrationLock();
+
+ var lockConnection = CreateLockConnection();
+ try
+ {
+ await EnsureLockTableAsync(lockConnection, cancellationToken).ConfigureAwait(false);
+
+ var transaction = await lockConnection.BeginTransactionAsync(cancellationToken).ConfigureAwait(false);
+
+ await AcquireRowLockAsync(lockConnection, transaction, cancellationToken).ConfigureAwait(false);
+
+ return new SingleStoreMigrationDatabaseLock(this, lockConnection, transaction, cancellationToken);
+ }
+ catch
+ {
+ await lockConnection.DisposeAsync().ConfigureAwait(false);
+ throw;
+ }
+ }
+
+ ///
+ /// Returns the name of the database-wide lock for migrations.
+ ///
+ protected virtual string GetDatabaseLockName(string databaseName)
+ => $"__{databaseName}_EFMigrationsLock";
+
+ private string GetLockTableName()
+ => GetDatabaseLockName(Dependencies.Connection.DbConnection.Database);
+
+ private SingleStoreConnection CreateLockConnection()
+ {
+ var connection = new SingleStoreConnection(_lockConnectionString);
+ connection.Open();
+
+ // Make sure the lock connection is using the same database as the main connection.
+ var db = Dependencies.Connection.DbConnection.Database;
+ if (!string.IsNullOrEmpty(db) &&
+ !string.Equals(connection.Database, db, StringComparison.OrdinalIgnoreCase))
+ {
+ connection.ChangeDatabase(db);
+ }
+
+ return connection;
+ }
+
+ private void EnsureLockTable(SingleStoreConnection connection)
+ {
+ var table = SqlGenerationHelper.DelimitIdentifier(GetLockTableName());
+
+ using var command = connection.CreateCommand();
+ command.CommandTimeout = LockTimeoutSeconds;
+
+ // ROWSTORE ensures fast, predictable locking behavior.
+ command.CommandText = $"""
+CREATE ROWSTORE TABLE IF NOT EXISTS {table} (
+ `Id` INT NOT NULL PRIMARY KEY
+);
+""";
+ command.ExecuteNonQuery();
+
+ command.CommandText = $"""INSERT IGNORE INTO {table} (`Id`) VALUES (1);""";
+ command.ExecuteNonQuery();
+ }
+
+ private async Task EnsureLockTableAsync(SingleStoreConnection connection, CancellationToken cancellationToken)
+ {
+ var table = SqlGenerationHelper.DelimitIdentifier(GetLockTableName());
+
+ using var command = connection.CreateCommand();
+ command.CommandTimeout = LockTimeoutSeconds;
+
+ command.CommandText = $"""
+CREATE ROWSTORE TABLE IF NOT EXISTS {table} (
+ `Id` INT NOT NULL PRIMARY KEY
+);
+""";
+ await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
+
+ command.CommandText = $"""INSERT IGNORE INTO {table} (`Id`) VALUES (1);""";
+ await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
+ }
+
+ private void AcquireRowLock(SingleStoreConnection connection, DbTransaction transaction)
+ {
+ var table = SqlGenerationHelper.DelimitIdentifier(GetLockTableName());
+
+ using var command = connection.CreateCommand();
+ command.Transaction = (SingleStoreTransaction)transaction;
+ command.CommandTimeout = LockTimeoutSeconds;
+
+ // A write statement acquires a row lock that is held until the transaction ends.
+ command.CommandText = $"""UPDATE {table} SET `Id` = `Id` WHERE `Id` = 1;""";
+ command.ExecuteNonQuery();
+ }
+
+ private async Task AcquireRowLockAsync(SingleStoreConnection connection, DbTransaction transaction, CancellationToken cancellationToken)
+ {
+ var table = SqlGenerationHelper.DelimitIdentifier(GetLockTableName());
+
+ using var command = connection.CreateCommand();
+ command.Transaction = (SingleStoreTransaction)transaction;
+ command.CommandTimeout = LockTimeoutSeconds;
+
+ command.CommandText = $"""UPDATE {table} SET `Id` = `Id` WHERE `Id` = 1;""";
+ await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
}
protected override void ConfigureTable([NotNull] EntityTypeBuilder history)
@@ -70,26 +220,10 @@ public override string GetCreateIfNotExistsScript()
return script.Insert(script.IndexOf("CREATE TABLE", StringComparison.Ordinal) + 12, " IF NOT EXISTS");
}
- ///
- /// Overridden by database providers to generate a SQL Script that will `BEGIN` a block
- /// of SQL if and only if the migration with the given identifier does not already exist in the history table.
- ///
- /// The migration identifier.
- /// The generated SQL.
public override string GetBeginIfNotExistsScript(string migrationId) => GetBeginIfScript(migrationId, true);
- ///
- /// Overridden by database providers to generate a SQL Script that will `BEGIN` a block
- /// of SQL if and only if the migration with the given identifier already exists in the history table.
- ///
- /// The migration identifier.
- /// The generated SQL.
public override string GetBeginIfExistsScript(string migrationId) => GetBeginIfScript(migrationId, false);
- ///
- /// Overridden by database providers to generate a SQL script to `END` the SQL block.
- ///
- /// The generated SQL.
public virtual string GetBeginIfScript(string migrationId, bool notExists) => $@"DROP PROCEDURE IF EXISTS {MigrationsScript};
DELIMITER //
CREATE PROCEDURE {MigrationsScript}()
@@ -97,10 +231,6 @@ public override string GetCreateIfNotExistsScript()
IF{(notExists ? " NOT" : null)} EXISTS(SELECT 1 FROM {SqlGenerationHelper.DelimitIdentifier(TableName, TableSchema)} WHERE {SqlGenerationHelper.DelimitIdentifier(MigrationIdColumnName)} = '{migrationId}') THEN
";
- ///
- /// Overridden by database providers to generate a SQL script to `END` the SQL block.
- ///
- /// The generated SQL.
public override string GetEndIfScript() => $@"
END IF;
END //
@@ -118,25 +248,19 @@ public virtual void ConfigureModel(ModelBuilder modelBuilder)
private string _migrationIdColumnName;
private string _productVersionColumnName;
- // Customized implementation.
protected virtual IModel EnsureModel()
{
if (_model == null)
{
var conventionSet = Dependencies.ConventionSetBuilder.CreateConventionSet();
- // Use public API to remove the convention, issue #214
ConventionSet.Remove(conventionSet.ModelInitializedConventions, typeof(DbSetFindingConvention));
ConventionSet.Remove(conventionSet.ModelInitializedConventions, typeof(RelationalDbFunctionAttributeConvention));
var modelBuilder = new ModelBuilder(conventionSet);
- #region Custom implementation
-
ConfigureModel(modelBuilder);
- #endregion
-
modelBuilder.Entity(
x =>
{
@@ -150,7 +274,6 @@ protected virtual IModel EnsureModel()
return _model;
}
- // Original implementation.
public override string GetCreateScript()
{
var model = EnsureModel();
@@ -161,14 +284,12 @@ public override string GetCreateScript()
return string.Concat(commandList.Select(c => c.CommandText));
}
- // Original implementation.
protected override string MigrationIdColumnName
=> _migrationIdColumnName ??= EnsureModel()
.FindEntityType(typeof(HistoryRow))!
.FindProperty(nameof(HistoryRow.MigrationId))!
.GetColumnName();
- // Original implementation.
protected override string ProductVersionColumnName
=> _productVersionColumnName ??= EnsureModel()
.FindEntityType(typeof(HistoryRow))!
@@ -176,5 +297,29 @@ protected override string ProductVersionColumnName
.GetColumnName();
#endregion Necessary implementation because we cannot directly override EnsureModel
+
+ private sealed class SingleStoreMigrationDatabaseLock(
+ SingleStoreHistoryRepository historyRepository,
+ SingleStoreConnection lockConnection,
+ DbTransaction lockTransaction,
+ CancellationToken cancellationToken = default)
+ : IMigrationsDatabaseLock
+ {
+ public IHistoryRepository HistoryRepository => historyRepository;
+
+ public void Dispose()
+ {
+ try { lockTransaction.Rollback(); } catch { /* ignore */ }
+ lockTransaction.Dispose();
+ lockConnection.Dispose();
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+ try { await lockTransaction.RollbackAsync(cancellationToken).ConfigureAwait(false); } catch { /* ignore */ }
+ await lockTransaction.DisposeAsync().ConfigureAwait(false);
+ await lockConnection.DisposeAsync().ConfigureAwait(false);
+ }
+ }
}
}
diff --git a/src/EFCore.SingleStore/Migrations/Internal/SingleStoreMigrationsModelDiffer.cs b/src/EFCore.SingleStore/Migrations/Internal/SingleStoreMigrationsModelDiffer.cs
index e3f15c99d..eb8b465a5 100644
--- a/src/EFCore.SingleStore/Migrations/Internal/SingleStoreMigrationsModelDiffer.cs
+++ b/src/EFCore.SingleStore/Migrations/Internal/SingleStoreMigrationsModelDiffer.cs
@@ -33,11 +33,13 @@ protected static class InternalLocalAnnotationNames
public SingleStoreMigrationsModelDiffer(
IRelationalTypeMappingSource typeMappingSource,
IMigrationsAnnotationProvider migrationsAnnotationProvider,
+ IRelationalAnnotationProvider relationalAnnotationProvider,
IRowIdentityMapFactory rowIdentityMapFactory,
CommandBatchPreparerDependencies commandBatchPreparerDependencies)
: base(
typeMappingSource,
migrationsAnnotationProvider,
+ relationalAnnotationProvider,
rowIdentityMapFactory,
commandBatchPreparerDependencies)
{
diff --git a/src/EFCore.SingleStore/Migrations/Internal/SingleStoreMigrator.cs b/src/EFCore.SingleStore/Migrations/Internal/SingleStoreMigrator.cs
index e47f28303..c8dc10f73 100644
--- a/src/EFCore.SingleStore/Migrations/Internal/SingleStoreMigrator.cs
+++ b/src/EFCore.SingleStore/Migrations/Internal/SingleStoreMigrator.cs
@@ -7,10 +7,10 @@
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
-using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Internal;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
@@ -39,19 +39,23 @@ public class SingleStoreMigrator : Migrator
private readonly IRelationalCommandDiagnosticsLogger _commandLogger;
public SingleStoreMigrator(
- [NotNull] IMigrationsAssembly migrationsAssembly,
- [NotNull] IHistoryRepository historyRepository,
- [NotNull] IDatabaseCreator databaseCreator,
- [NotNull] IMigrationsSqlGenerator migrationsSqlGenerator,
- [NotNull] IRawSqlCommandBuilder rawSqlCommandBuilder,
- [NotNull] IMigrationCommandExecutor migrationCommandExecutor,
- [NotNull] IRelationalConnection connection,
- [NotNull] ISqlGenerationHelper sqlGenerationHelper,
- [NotNull] ICurrentDbContext currentContext,
- [NotNull] IModelRuntimeInitializer modelRuntimeInitializer,
- [NotNull] IDiagnosticsLogger logger,
- [NotNull] IRelationalCommandDiagnosticsLogger commandLogger,
- [NotNull] IDatabaseProvider databaseProvider)
+ IMigrationsAssembly migrationsAssembly,
+ IHistoryRepository historyRepository,
+ IDatabaseCreator databaseCreator,
+ IMigrationsSqlGenerator migrationsSqlGenerator,
+ IRawSqlCommandBuilder rawSqlCommandBuilder,
+ IMigrationCommandExecutor migrationCommandExecutor,
+ IRelationalConnection connection,
+ ISqlGenerationHelper sqlGenerationHelper,
+ ICurrentDbContext currentContext,
+ IModelRuntimeInitializer modelRuntimeInitializer,
+ IDiagnosticsLogger logger,
+ IRelationalCommandDiagnosticsLogger commandLogger,
+ IDatabaseProvider databaseProvider,
+ IMigrationsModelDiffer migrationsModelDiffer,
+ IDesignTimeModel designTimeModel,
+ IDbContextOptions contextOptions,
+ IExecutionStrategy executionStrategy)
: base(
migrationsAssembly,
historyRepository,
@@ -65,7 +69,11 @@ public SingleStoreMigrator(
modelRuntimeInitializer,
logger,
commandLogger,
- databaseProvider)
+ databaseProvider,
+ migrationsModelDiffer,
+ designTimeModel,
+ contextOptions,
+ executionStrategy)
{
_migrationsAssembly = migrationsAssembly;
_rawSqlCommandBuilder = rawSqlCommandBuilder;
@@ -146,13 +154,11 @@ protected virtual List GetAllMigrationOperations(string from
PopulateMigrations(
appliedMigrations,
toMigration,
- out var migrationsToApply,
- out var migrationsToRevert,
- out var actualTargetMigration);
+ out var migratorData);
- return migrationsToApply
+ return migratorData.AppliedMigrations
.SelectMany(x => x.UpOperations)
- .Concat(migrationsToRevert.SelectMany(x => x.DownOperations))
+ .Concat(migratorData.RevertedMigrations.SelectMany(x => x.DownOperations))
.ToList();
}
diff --git a/src/EFCore.SingleStore/Migrations/SingleStoreMigrationsSqlGenerator.cs b/src/EFCore.SingleStore/Migrations/SingleStoreMigrationsSqlGenerator.cs
index 35aef468c..7f26c02b6 100644
--- a/src/EFCore.SingleStore/Migrations/SingleStoreMigrationsSqlGenerator.cs
+++ b/src/EFCore.SingleStore/Migrations/SingleStoreMigrationsSqlGenerator.cs
@@ -322,8 +322,82 @@ protected override void CreateTableConstraints(
CreateTableCheckConstraints(operation, model, builder);
CreateTableForeignKeys(operation, model, builder);*/
}
+
protected override void Generate(AlterTableOperation operation, IModel model, MigrationCommandListBuilder builder)
{
+ var oldCharSet = operation.OldTable[SingleStoreAnnotationNames.CharSet] as string;
+ var newCharSet = operation[SingleStoreAnnotationNames.CharSet] as string;
+
+ var oldCollation = operation.OldTable[RelationalAnnotationNames.Collation] as string;
+ var newCollation = operation[RelationalAnnotationNames.Collation] as string;
+
+ if (newCollation != oldCollation && newCollation != null)
+ {
+ // A new collation has been set. It takes precedence over any defined charset.
+ // if charset also changed, emit it in the same ALTER TABLE.
+ builder.Append("ALTER TABLE ").Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name, operation.Schema));
+
+ if (newCharSet != oldCharSet && newCharSet != null)
+ {
+ builder.Append(" CHARACTER SET ").Append(newCharSet).Append(", ");
+ }
+ else
+ {
+ builder.Append(" ");
+ }
+
+ builder
+ .Append("COLLATE ")
+ .Append(newCollation)
+ .AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
+
+ EndStatement(builder);
+ }
+ else if (newCharSet != oldCharSet ||
+ newCollation != oldCollation && newCollation == null)
+ {
+ // The charset has been changed or the collation has been reset to the default.
+ if (newCharSet != null)
+ {
+ // A new charset has been set without an explicit collation.
+ builder
+ .Append("ALTER TABLE ")
+ .Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name, operation.Schema))
+ .Append(" CHARACTER SET ")
+ .Append(newCharSet)
+ .AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
+
+ EndStatement(builder);
+ }
+ else
+ {
+ // Reset to database defaults (SingleStore): use INFORMATION_SCHEMA.SCHEMATA
+ var resetSql = $@"
+SET @__ss_charset = (
+ SELECT DEFAULT_CHARACTER_SET_NAME
+ FROM INFORMATION_SCHEMA.SCHEMATA
+ WHERE SCHEMA_NAME = DATABASE()
+);
+SET @__ss_collation = (
+ SELECT DEFAULT_COLLATION_NAME
+ FROM INFORMATION_SCHEMA.SCHEMATA
+ WHERE SCHEMA_NAME = DATABASE()
+);
+SET @__ss_stmt = CONCAT(
+ 'ALTER TABLE {Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name, operation.Schema)} ',
+ 'CHARACTER SET ', @__ss_charset,
+ ', COLLATE ', @__ss_collation,
+ '{Dependencies.SqlGenerationHelper.StatementTerminator}'
+);
+PREPARE __ss_exec FROM @__ss_stmt;
+EXECUTE __ss_exec;
+DEALLOCATE PREPARE __ss_exec;
+";
+ builder.AppendLine(resetSql);
+ EndStatement(builder);
+ }
+ }
+
if (operation.Comment != operation.OldTable.Comment)
{
builder.Append("ALTER TABLE ")
@@ -941,7 +1015,8 @@ protected override void ColumnDefinition(
if (operation.ComputedColumnSql == null)
{
- ColumnDefinitionWithCharSet(schema, table, name, operation, model, builder);
+ // AUTO_INCREMENT columns don't support DEFAULT values.
+ ColumnDefinitionWithCharSet(schema, table, name, operation, model, builder, withDefaultValue: !autoIncrement);
GenerateComment(operation.Comment, builder);
@@ -1033,7 +1108,14 @@ private void GenerateComment(string comment, MigrationCommandListBuilder builder
.Append(SingleStoreStringTypeMapping.EscapeSqlLiteralWithLineBreaks(comment, !_options.NoBackslashEscapes, false));
}
- private void ColumnDefinitionWithCharSet(string schema, string table, string name, ColumnOperation operation, IModel model, MigrationCommandListBuilder builder)
+ private void ColumnDefinitionWithCharSet(
+ string schema,
+ string table,
+ string name,
+ ColumnOperation operation,
+ IModel model,
+ MigrationCommandListBuilder builder,
+ bool withDefaultValue)
{
if (operation.ComputedColumnSql != null)
{
@@ -1050,7 +1132,10 @@ private void ColumnDefinitionWithCharSet(string schema, string table, string nam
builder.Append(operation.IsNullable ? " NULL" : " NOT NULL");
- DefaultValue(operation.DefaultValue, operation.DefaultValueSql, columnType, builder);
+ if (withDefaultValue)
+ {
+ DefaultValue(operation.DefaultValue, operation.DefaultValueSql, columnType, builder);
+ }
var srid = operation[SingleStoreAnnotationNames.SpatialReferenceSystemId];
if (srid is int &&
@@ -1212,47 +1297,16 @@ protected override void CreateTablePrimaryKeyConstraint(
[CanBeNull] IModel model,
[NotNull] MigrationCommandListBuilder builder)
{
- Check.NotNull(operation, nameof(operation));
- Check.NotNull(builder, nameof(builder));
-
- var primaryKey = operation.PrimaryKey;
- if (primaryKey != null)
- {
- builder.AppendLine(",");
-
- // MySQL InnoDB has the requirement, that an AUTO_INCREMENT column has to be the first
- // column participating in an index.
-
- var sortedColumnNames = primaryKey.Columns.Length > 1
- ? primaryKey.Columns
- .Select(columnName => operation.Columns.First(co => co.Name == columnName))
- .OrderBy(co => co[SingleStoreAnnotationNames.ValueGenerationStrategy] is SingleStoreValueGenerationStrategy generationStrategy
- && generationStrategy == SingleStoreValueGenerationStrategy.IdentityColumn
- ? 0
- : 1)
- .Select(co => co.Name)
- .ToArray()
- : primaryKey.Columns;
-
- var sortedPrimaryKey = new AddPrimaryKeyOperation()
- {
- Schema = primaryKey.Schema,
- Table = primaryKey.Table,
- Name = primaryKey.Name,
- Columns = sortedColumnNames,
- IsDestructiveChange = primaryKey.IsDestructiveChange,
- };
-
- foreach (var annotation in primaryKey.GetAnnotations())
- {
- sortedPrimaryKey[annotation.Name] = annotation.Value;
- }
-
- PrimaryKeyConstraint(
- sortedPrimaryKey,
- model,
- builder);
- }
+ // We used to move an AUTO_INCREMENT column to the first position in a primary key, if the PK was a compound key and the column
+ // was not in the first position. We did this to satisfy InnoDB.
+ // However, this is technically an inaccuracy, and leads to incompatible FK -> PK mappings in MySQL 8.4.
+ // We will therefore reverse that behavior to leaving the key order unchanged again.
+ // This will lead to two issues:
+ // - Migrations that upgrade vom Pomelo < 9.0 to Pomelo 9.0 will not include this change automatically, because the model
+ // never changed (we only made the change (before and now) here in MySqlMigrationsSqlGenerator).
+ // - There now needs to be an index for those cases, that contains the AUTO_INCREMENT column as its first column.
+
+ base.CreateTablePrimaryKeyConstraint(operation, model, builder);
}
protected override void Generate(
@@ -1415,7 +1469,7 @@ protected override void IndexTraits(MigrationOperation operation, IModel model,
}
}
- protected override void IndexOptions(CreateIndexOperation operation, IModel model, MigrationCommandListBuilder builder)
+ protected override void IndexOptions(MigrationOperation operation, IModel model, MigrationCommandListBuilder builder)
{
// The base implementation supports index filters in form of a WHERE clause.
// This is not supported by MySQL, so we don't call it here.
diff --git a/src/EFCore.SingleStore/Query/ExpressionTranslators/Internal/BitwiseOperationReturnTypeCorrectingExpressionVisitor.cs b/src/EFCore.SingleStore/Query/ExpressionTranslators/Internal/BitwiseOperationReturnTypeCorrectingExpressionVisitor.cs
new file mode 100644
index 000000000..47a442309
--- /dev/null
+++ b/src/EFCore.SingleStore/Query/ExpressionTranslators/Internal/BitwiseOperationReturnTypeCorrectingExpressionVisitor.cs
@@ -0,0 +1,88 @@
+// Copyright (c) Pomelo Foundation. All rights reserved.
+// Copyright (c) SingleStore Inc. All rights reserved.
+// Licensed under the MIT. See LICENSE in the project root for license information.
+
+using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using EntityFrameworkCore.SingleStore.Query.Internal;
+
+namespace EntityFrameworkCore.SingleStore.Query.ExpressionVisitors.Internal;
+
+///
+/// MySQL implicitly casts numbers used in all bitwise operations to BIGINT UNSIGNED.
+/// Bitwise operations are:
+///
+///
+/// Operator
+/// Description
+///
+/// -
+/// &
+/// Bitwise AND
+///
+/// -
+/// >>
+/// Right shift
+///
+/// -
+/// <<
+/// Left shift
+///
+/// -
+/// ^
+/// Bitwise OR
+///
+/// -
+/// ~
+/// Bitwise inversion
+///
+///
+/// We need to cast them back to their expected type.
+///
+public class BitwiseOperationReturnTypeCorrectingExpressionVisitor : ExpressionVisitor
+{
+ private readonly SingleStoreSqlExpressionFactory _sqlExpressionFactory;
+
+ public BitwiseOperationReturnTypeCorrectingExpressionVisitor(SingleStoreSqlExpressionFactory sqlExpressionFactory)
+ {
+ _sqlExpressionFactory = sqlExpressionFactory;
+ }
+
+ protected override Expression VisitExtension(Expression extensionExpression)
+ => extensionExpression switch
+ {
+ SqlUnaryExpression unaryExpression => VisitUnary(unaryExpression),
+ SqlBinaryExpression binaryExpression => VisitBinary(binaryExpression),
+ ShapedQueryExpression shapedQueryExpression => shapedQueryExpression.UpdateQueryExpression(Visit(shapedQueryExpression.QueryExpression)),
+ _ => base.VisitExtension(extensionExpression)
+ };
+
+ protected virtual Expression VisitUnary(SqlUnaryExpression sqlUnaryExpression)
+ => base.VisitExtension(sqlUnaryExpression) is var visitedExpression &&
+ visitedExpression is SqlUnaryExpression { OperatorType: ExpressionType.Not } visitedSqlUnaryExpression &&
+ visitedSqlUnaryExpression.Type != typeof(bool)
+ ? _sqlExpressionFactory.Convert(
+ visitedSqlUnaryExpression,
+ visitedSqlUnaryExpression.Type,
+ visitedSqlUnaryExpression.TypeMapping)
+ : visitedExpression;
+
+ protected virtual Expression VisitBinary(SqlBinaryExpression sqlBinaryExpression)
+ => base.VisitExtension(sqlBinaryExpression) is var visitedExpression &&
+ visitedExpression is SqlBinaryExpression
+ {
+ OperatorType: ExpressionType.And
+ or ExpressionType.RightShift
+ or ExpressionType.LeftShift
+ or ExpressionType.ExclusiveOr
+ or ExpressionType.Or
+ or ExpressionType.Not
+ } visitedSqlBinaryExpression &&
+ visitedSqlBinaryExpression.Type != typeof(bool)
+ ? _sqlExpressionFactory.Convert(
+ visitedSqlBinaryExpression,
+ visitedSqlBinaryExpression.Type,
+ visitedSqlBinaryExpression.TypeMapping)
+ : visitedExpression;
+}
diff --git a/src/EFCore.SingleStore/Query/ExpressionTranslators/Internal/SingleStoreDbFunctionsExtensionsMethodTranslator.cs b/src/EFCore.SingleStore/Query/ExpressionTranslators/Internal/SingleStoreDbFunctionsExtensionsMethodTranslator.cs
index f8d1842d7..b8a08888d 100644
--- a/src/EFCore.SingleStore/Query/ExpressionTranslators/Internal/SingleStoreDbFunctionsExtensionsMethodTranslator.cs
+++ b/src/EFCore.SingleStore/Query/ExpressionTranslators/Internal/SingleStoreDbFunctionsExtensionsMethodTranslator.cs
@@ -233,7 +233,7 @@ SqlExpression RewriteSessionTz(SqlExpression expr)
arguments[2],
match.TypeMapping);
- var excapeChar = arguments.Count == 4
+ var escapeChar = arguments.Count == 4
? InferStringTypeMappingOrApplyDefault(
arguments[3],
match.TypeMapping)
@@ -242,7 +242,7 @@ SqlExpression RewriteSessionTz(SqlExpression expr)
return _sqlExpressionFactory.Like(
match,
pattern,
- excapeChar);
+ escapeChar);
}
if (Equals(method, _isMatchMethodInfo) ||
diff --git a/src/EFCore.SingleStore/Query/ExpressionTranslators/Internal/SingleStoreJsonDbFunctionsTranslator.cs b/src/EFCore.SingleStore/Query/ExpressionTranslators/Internal/SingleStoreJsonDbFunctionsTranslator.cs
index 9621bd7d7..267778b45 100644
--- a/src/EFCore.SingleStore/Query/ExpressionTranslators/Internal/SingleStoreJsonDbFunctionsTranslator.cs
+++ b/src/EFCore.SingleStore/Query/ExpressionTranslators/Internal/SingleStoreJsonDbFunctionsTranslator.cs
@@ -91,6 +91,11 @@ public virtual SqlExpression Translate(
method.ReturnType,
_sqlExpressionFactory.FindMapping(method.ReturnType, "json"),
false),
+ nameof(SingleStoreJsonDbFunctionsExtensions.JsonOverlaps)
+ => _sqlExpressionFactory.NullableFunction(
+ "JSON_OVERLAPS",
+ new[] { Json(args[0]), args[1] },
+ typeof(bool)),
nameof(SingleStoreJsonDbFunctionsExtensions.JsonContains)
=> _sqlExpressionFactory.NullableFunction(
"JSON_CONTAINS",
@@ -128,9 +133,10 @@ public virtual SqlExpression Translate(
.Append(_sqlExpressionFactory.Constant("one"))
.Append(args[1])
.AppendIfTrue(
- args.Length >= 3, () => args.Length >= 4
+ args.Length >= 3,
+ () => args.Length >= 4
? args[3]
- : _sqlExpressionFactory.Constant(null, RelationalTypeMapping.NullMapping))
+ : _sqlExpressionFactory.Constant(null, typeof(string)))
.AppendIfTrue(args.Length >= 3, () => args[2]),
typeof(bool),
null,
diff --git a/src/EFCore.SingleStore/Query/ExpressionTranslators/Internal/SingleStoreJsonTableExpression.cs b/src/EFCore.SingleStore/Query/ExpressionTranslators/Internal/SingleStoreJsonTableExpression.cs
index 2a143a00e..7e83b50b5 100644
--- a/src/EFCore.SingleStore/Query/ExpressionTranslators/Internal/SingleStoreJsonTableExpression.cs
+++ b/src/EFCore.SingleStore/Query/ExpressionTranslators/Internal/SingleStoreJsonTableExpression.cs
@@ -15,7 +15,7 @@ namespace EntityFrameworkCore.SingleStore.Query.ExpressionTranslators.Internal;
///
/// An expression that represents a MySQL JSON_TABLE() function call in a SQL tree.
///
-public class SingleStoreJsonTableExpression : TableValuedFunctionExpression, IClonableTableExpressionBase
+public class SingleStoreJsonTableExpression : TableValuedFunctionExpression
{
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -135,26 +135,6 @@ public virtual SingleStoreJsonTableExpression Update(
? this
: new SingleStoreJsonTableExpression(Alias, jsonExpression, path, columnInfos);
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- // TODO: Deep clone, see #30982
- public virtual TableExpressionBase Clone()
- {
- var clone = new SingleStoreJsonTableExpression(Alias, JsonExpression, Path, ColumnInfos);
-
- foreach (var annotation in GetAnnotations())
- {
- clone.AddAnnotation(annotation.Name, annotation.Value);
- }
-
- return clone;
- }
-
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
diff --git a/src/EFCore.SingleStore/Query/ExpressionTranslators/Internal/SingleStoreStringComparisonMethodTranslator.cs b/src/EFCore.SingleStore/Query/ExpressionTranslators/Internal/SingleStoreStringComparisonMethodTranslator.cs
index 262fd28bb..b738630e6 100644
--- a/src/EFCore.SingleStore/Query/ExpressionTranslators/Internal/SingleStoreStringComparisonMethodTranslator.cs
+++ b/src/EFCore.SingleStore/Query/ExpressionTranslators/Internal/SingleStoreStringComparisonMethodTranslator.cs
@@ -410,13 +410,13 @@ private SqlExpression MakeStartsWithEndsWithExpressionImpl(
// in C# and send a simple LIKE.
return constantPrefixSuffixExpression.Value switch
{
- null => _sqlExpressionFactory.Like(targetTransform(target), _sqlExpressionFactory.Constant(null, stringTypeMapping)),
+ null => _sqlExpressionFactory.Like(targetTransform(target), _sqlExpressionFactory.Constant(null, typeof(string), stringTypeMapping)),
"" => _sqlExpressionFactory.Like(targetTransform(target), _sqlExpressionFactory.Constant("%")),
string s => _sqlExpressionFactory.Like(
targetTransform(target),
prefixSuffixTransform(
_sqlExpressionFactory.Constant(
- $"{(startsWith ? string.Empty : "%")}{(s.Any(IsLikeWildChar) ? EscapeLikePattern(s) : s)}{(startsWith ? "%" : string.Empty)}"))),
+ $"{(startsWith ? string.Empty : "%")}{(s.Any(IsLikeWildOrEscapeChar) ? EscapeLikePattern(s) : s)}{(startsWith ? "%" : string.Empty)}"))),
_ => throw new UnreachableException(),
};
}
@@ -467,17 +467,15 @@ private SqlExpression MakeContainsExpressionImpl(
if (pattern is SqlConstantExpression constantPatternExpression)
{
- // The prefix is constant. Aside from null or empty, we escape all special characters (%, _, \)
- // in C# and send a simple LIKE.
// The prefix is constant. Aside from null or empty, we escape all special characters (%, _, \)
// in C# and send a simple LIKE.
return constantPatternExpression.Value switch
{
- null => _sqlExpressionFactory.Like(targetTransform(target), _sqlExpressionFactory.Constant(null, stringTypeMapping)),
+ null => _sqlExpressionFactory.Like(targetTransform(target), _sqlExpressionFactory.Constant(null, typeof(string), stringTypeMapping)),
"" => _sqlExpressionFactory.Like(targetTransform(target), _sqlExpressionFactory.Constant("%")),
string s => _sqlExpressionFactory.Like(
targetTransform(target),
- patternTransform(_sqlExpressionFactory.Constant($"%{(s.Any(IsLikeWildChar) ? EscapeLikePattern(s) : s)}%"))),
+ patternTransform(_sqlExpressionFactory.Constant($"%{(s.Any(IsLikeWildOrEscapeChar) ? EscapeLikePattern(s) : s)}%"))),
_ => throw new UnreachableException(),
};
}
@@ -519,7 +517,8 @@ private SqlExpression MakeContainsExpressionImpl(
_sqlExpressionFactory.Constant(string.Empty, stringTypeMapping)))));
}
- protected virtual SqlExpression GetLikeExpressionUsingParameter(QueryCompilationContext queryCompilationContext,
+ protected virtual SqlExpression GetLikeExpressionUsingParameter(
+ QueryCompilationContext queryCompilationContext,
SqlExpression target,
Func targetTransform,
SqlExpression pattern,
@@ -696,15 +695,15 @@ private SqlExpression Locate(SqlExpression sub, SqlExpression str, SqlExpression
private const char LikeEscapeChar = '\\';
- private static bool IsLikeWildChar(char c) => c == '%' || c == '_';
+ private static bool IsLikeWildOrEscapeChar(char c) => IsLikeWildChar(c) || LikeEscapeChar == c;
+ private static bool IsLikeWildChar(char c) => c is '%' or '_';
private static string EscapeLikePattern(string pattern)
{
var builder = new StringBuilder();
foreach (var c in pattern)
{
- if (IsLikeWildChar(c) ||
- c == LikeEscapeChar)
+ if (IsLikeWildOrEscapeChar(c))
{
builder.Append(LikeEscapeChar);
}
diff --git a/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreBoolOptimizingExpressionVisitor.cs b/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreBoolOptimizingExpressionVisitor.cs
index 6dd7ad7bb..98997b084 100644
--- a/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreBoolOptimizingExpressionVisitor.cs
+++ b/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreBoolOptimizingExpressionVisitor.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
@@ -99,7 +100,7 @@ protected override Expression VisitColumn(ColumnExpression columnExpression)
}
protected override Expression VisitDelete(DeleteExpression deleteExpression)
- => deleteExpression.Update((SelectExpression)Visit(deleteExpression.SelectExpression));
+ => deleteExpression.Update(deleteExpression.Table, (SelectExpression)Visit(deleteExpression.SelectExpression));
protected override Expression VisitDistinct(DistinctExpression distinctExpression)
{
@@ -245,7 +246,7 @@ protected override Expression VisitSelect(SelectExpression selectExpression)
return changed
? selectExpression.Update(
- projections, tables, predicate, groupBy, havingExpression, orderings, limit, offset)
+ tables, predicate, groupBy, havingExpression, projections, orderings, offset, limit)
: selectExpression;
}
@@ -672,14 +673,25 @@ protected override Expression VisitValues(ValuesExpression valuesExpression)
var parentOptimize = _optimize;
_optimize = false;
- var rowValues = new RowValueExpression[valuesExpression.RowValues.Count];
- for (var i = 0; i < rowValues.Length; i++)
+ switch (valuesExpression)
{
- rowValues[i] = (RowValueExpression)Visit(valuesExpression.RowValues[i]);
- }
+ case { RowValues: not null }:
+ var rowValues = new RowValueExpression[valuesExpression.RowValues!.Count];
+ for (var i = 0; i < rowValues.Length; i++)
+ {
+ rowValues[i] = (RowValueExpression)Visit(valuesExpression.RowValues[i]);
+ }
+ _optimize = parentOptimize;
+ return valuesExpression.Update(rowValues);
- _optimize = parentOptimize;
- return valuesExpression.Update(rowValues);
+ case { ValuesParameter: not null }:
+ var valuesParameter = (SqlParameterExpression)Visit(valuesExpression.ValuesParameter);
+ _optimize = parentOptimize;
+ return valuesExpression.Update(valuesParameter);
+
+ default:
+ throw new UnreachableException();
+ }
}
}
}
diff --git a/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreHavingExpressionVisitor.cs b/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreHavingExpressionVisitor.cs
index df237865d..96188add9 100644
--- a/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreHavingExpressionVisitor.cs
+++ b/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreHavingExpressionVisitor.cs
@@ -9,81 +9,127 @@
using EntityFrameworkCore.SingleStore.Query.Expressions.Internal;
using EntityFrameworkCore.SingleStore.Query.Internal;
-namespace EntityFrameworkCore.SingleStore.Query.ExpressionVisitors.Internal
+namespace EntityFrameworkCore.SingleStore.Query.ExpressionVisitors.Internal;
+
+///
+/// MySQL & MariaDB currently do not support complex expressions in HAVING clauses (e.g. function calls).
+/// Instead, they want you to reference SELECT aliases for those expressions in the HAVING clause.
+/// See https://bugs.mysql.com/bug.php?id=103961
+/// This is only an issue for HAVING expressions that do not contain any aggregate functions.
+///
+public class SingleStoreHavingExpressionVisitor : ExpressionVisitor
{
- public class SingleStoreHavingExpressionVisitor : ExpressionVisitor
+ private readonly SingleStoreSqlExpressionFactory _sqlExpressionFactory;
+ private readonly SingleStoreContainsAggregateFunctionExpressionVisitor _containsAggregateFunctionExpressionVisitor;
+ private bool _usePrePostprocessorMode;
+
+ public SingleStoreHavingExpressionVisitor(SingleStoreSqlExpressionFactory sqlExpressionFactory)
+ {
+ _sqlExpressionFactory = sqlExpressionFactory;
+ _containsAggregateFunctionExpressionVisitor = new SingleStoreContainsAggregateFunctionExpressionVisitor();
+ }
+
+ public virtual Expression Process(Expression expression, bool usePrePostprocessorMode)
{
- private readonly SingleStoreSqlExpressionFactory _sqlExpressionFactory;
- private SingleStoreContainsAggregateFunctionExpressionVisitor _containsAggregateFunctionExpressionVisitor;
+ _usePrePostprocessorMode = usePrePostprocessorMode;
+ return Visit(expression);
+ }
- public SingleStoreHavingExpressionVisitor(SingleStoreSqlExpressionFactory sqlExpressionFactory)
+ protected override Expression VisitExtension(Expression extensionExpression)
+ => extensionExpression switch
{
- _sqlExpressionFactory = sqlExpressionFactory;
+ SelectExpression selectExpression => VisitSelect(selectExpression),
+ ShapedQueryExpression shapedQueryExpression => VisitShapedQuery(shapedQueryExpression),
+ RelationalGroupByShaperExpression relationalGroupByShaperExpression => VisitRelationalGroupByShaper(
+ relationalGroupByShaperExpression),
+ _ => base.VisitExtension(extensionExpression)
+ };
+
+ private Expression VisitRelationalGroupByShaper(RelationalGroupByShaperExpression relationalGroupByShaperExpression)
+ {
+ if (_usePrePostprocessorMode)
+ {
+ Visit(relationalGroupByShaperExpression.KeySelector);
+ Visit(relationalGroupByShaperExpression.ElementSelector);
+ Visit(relationalGroupByShaperExpression.GroupingEnumerable);
+
+ return relationalGroupByShaperExpression;
}
- protected override Expression VisitExtension(Expression extensionExpression)
- => extensionExpression switch
- {
- SelectExpression selectExpression => VisitSelect(selectExpression),
- ShapedQueryExpression shapedQueryExpression => shapedQueryExpression.Update(
- Visit(shapedQueryExpression.QueryExpression), Visit(shapedQueryExpression.ShaperExpression)),
- _ => base.VisitExtension(extensionExpression)
- };
+ return base.VisitExtension(relationalGroupByShaperExpression);
+ }
+
+ private ShapedQueryExpression VisitShapedQuery(ShapedQueryExpression shapedQueryExpression)
+ {
+ if (_usePrePostprocessorMode)
+ {
+ Visit(shapedQueryExpression.QueryExpression);
+ Visit(shapedQueryExpression.ShaperExpression);
- protected virtual Expression VisitSelect(SelectExpression selectExpression)
+ return shapedQueryExpression;
+ }
+
+ return shapedQueryExpression.Update(
+ Visit(shapedQueryExpression.QueryExpression),
+ Visit(shapedQueryExpression.ShaperExpression));
+ }
+
+ protected virtual Expression VisitSelect(SelectExpression selectExpression)
+ {
+ selectExpression = (SelectExpression)base.VisitExtension(selectExpression);
+
+ var havingExpression = selectExpression.Having;
+
+ if (HasHavingExpressionWithoutAggregateFunction(havingExpression))
{
- // MySQL & MariaDB currently do not support complex expressions in HAVING clauses (e.g. function calls).
- // Instead, they want you to reference SELECT aliases for those expressions in the HAVING clause.
- // See https://bugs.mysql.com/bug.php?id=103961
- // This is only an issue for HAVING expressions that do not contain any aggregate functions.
- var havingExpression = selectExpression.Having;
- if (havingExpression is not null &&
- havingExpression is not SqlConstantExpression &&
- havingExpression is not SingleStoreColumnAliasReferenceExpression)
+ if (_usePrePostprocessorMode)
{
- _containsAggregateFunctionExpressionVisitor ??= new SingleStoreContainsAggregateFunctionExpressionVisitor();
- if (!_containsAggregateFunctionExpressionVisitor.ProcessUntilSelect(havingExpression))
- {
- selectExpression.PushdownIntoSubquery();
- var subQuery = (SelectExpression) selectExpression.Tables.Single();
-
- var projectionIndex = subQuery.AddToProjection(havingExpression);
- var alias = subQuery.Projection[projectionIndex].Alias;
-
- var columnAliasReferenceExpression = _sqlExpressionFactory.ColumnAliasReference(
- alias,
- havingExpression,
- havingExpression.Type,
- havingExpression.TypeMapping);
-
- // Having expressions, not containing an aggregate function, need to be part of the GROUP BY clause, because they now also
- // appear as part of the SELECT clause.
- var groupBy = subQuery.GroupBy.ToList();
- groupBy.Add(columnAliasReferenceExpression);
-
- subQuery = subQuery.Update(
- subQuery.Projection,
- subQuery.Tables,
- subQuery.Predicate,
- groupBy,
- columnAliasReferenceExpression,
- subQuery.Orderings,
- subQuery.Limit,
- subQuery.Offset);
-
- selectExpression = selectExpression.Update(
- selectExpression.Projection,
- new[] {subQuery},
- selectExpression.Predicate,
- selectExpression.GroupBy,
- selectExpression.Having,
- selectExpression.Orderings,
- selectExpression.Limit,
- selectExpression.Offset);
- }
+ // This part needs to run before `RelationalQueryTranslationPostprocessor.Process()` is called, so that the
+ // `SelectExpression` is still mutable, and we can call `SelectExpression.PushdownIntoSubquery()`.
+
+ selectExpression.PushdownIntoSubquery();
+
+ // Paradoxically, it seems quite complicated to change the subquery, as long as the outer query is still mutable.
+ // We postpone that work for later, when the outer query is immutable, and we simply use the normal expression visitor
+ // update process.
}
+ else
+ {
+ // This part needs to run after `RelationalQueryTranslationPostprocessor.Process()` is called, so that the
+ // `SelectExpression` is already immutable, and we can simply update the select subquery.
+
+ var projectionIndex = selectExpression.AddToProjection(havingExpression!);
+ var alias = selectExpression.Projection[projectionIndex].Alias;
+
+ var columnAliasReferenceExpression = _sqlExpressionFactory.ColumnAliasReference(
+ alias,
+ havingExpression,
+ havingExpression.Type,
+ havingExpression.TypeMapping);
- return base.VisitExtension(selectExpression);
+ // Having expressions, not containing an aggregate function, need to be part of the GROUP BY clause, because they now
+ // also appear as part of the SELECT clause.
+ selectExpression = selectExpression.Update(
+ selectExpression.Tables,
+ selectExpression.Predicate,
+ selectExpression.GroupBy.Append(columnAliasReferenceExpression).ToList(),
+ having: columnAliasReferenceExpression,
+ selectExpression.Projection,
+ selectExpression.Orderings,
+ selectExpression.Offset,
+ selectExpression.Limit);
+ }
}
+
+ return selectExpression;
}
+
+ ///
+ /// Backed by `EFCore.SingleStore.Tests/Behaviors/HavingBehavior.cs`.
+ ///
+ private bool HasHavingExpressionWithoutAggregateFunction(SqlExpression havingExpression)
+ => havingExpression is not null
+ and not SqlConstantExpression
+ and not SingleStoreColumnAliasReferenceExpression &&
+ !_containsAggregateFunctionExpressionVisitor.ProcessUntilSelect(havingExpression);
}
diff --git a/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreNonWorkingHavingExpressionVisitor.cs b/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreNonWorkingHavingExpressionVisitor.cs
new file mode 100644
index 000000000..f835de7ea
--- /dev/null
+++ b/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreNonWorkingHavingExpressionVisitor.cs
@@ -0,0 +1,234 @@
+// Copyright (c) Pomelo Foundation. All rights reserved.
+// Copyright (c) SingleStore Inc. All rights reserved.
+// Licensed under the MIT. See LICENSE in the project root for license information.
+
+using System.Linq;
+using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using EntityFrameworkCore.SingleStore.Query.Expressions.Internal;
+using EntityFrameworkCore.SingleStore.Query.Internal;
+
+namespace EntityFrameworkCore.SingleStore.Query.ExpressionVisitors.Internal
+{
+ // TODO: 9.0
+ // Remove from codebase.
+ public class SingleStoreNonWorkingHavingExpressionVisitor : ExpressionVisitor
+ {
+ private readonly SingleStoreSqlExpressionFactory _sqlExpressionFactory;
+ private readonly SqlAliasManager _sqlAliasManager;
+ private SingleStoreContainsAggregateFunctionExpressionVisitor _containsAggregateFunctionExpressionVisitor;
+
+ public SingleStoreNonWorkingHavingExpressionVisitor(SingleStoreSqlExpressionFactory sqlExpressionFactory, SqlAliasManager sqlAliasManager)
+ {
+ _sqlExpressionFactory = sqlExpressionFactory;
+ _sqlAliasManager = sqlAliasManager;
+ }
+
+ protected override Expression VisitExtension(Expression extensionExpression)
+ => extensionExpression switch
+ {
+ SelectExpression selectExpression => VisitSelectMutable(selectExpression),
+ // SelectExpression selectExpression => VisitSelectImmutable(selectExpression),
+ ShapedQueryExpression shapedQueryExpression => VisitShapedQuery(shapedQueryExpression),
+ _ => base.VisitExtension(extensionExpression)
+ };
+
+ private ShapedQueryExpression VisitShapedQuery(ShapedQueryExpression shapedQueryExpression)
+ => shapedQueryExpression.Update(
+ (SelectExpression)Visit(shapedQueryExpression.QueryExpression),
+ Visit(shapedQueryExpression.ShaperExpression));
+
+ ///
+ /// This might work, if we would know if the outer query is supposed to be mutable or not (which we cannot directly find out
+ /// because `SelectExpression.IsMutable` is internal) and if we could copy or recreate the projection mappings of the outer query.
+ ///
+ protected virtual Expression VisitSelectMutable(SelectExpression selectExpression)
+ {
+ // MySQL & MariaDB currently do not support complex expressions in HAVING clauses (e.g. function calls).
+ // Instead, they want you to reference SELECT aliases for those expressions in the HAVING clause.
+ // See https://bugs.mysql.com/bug.php?id=103961
+ // This is only an issue for HAVING expressions that do not contain any aggregate functions.
+ var havingExpression = selectExpression.Having;
+ if (havingExpression is not null &&
+ havingExpression is not SqlConstantExpression &&
+ havingExpression is not SingleStoreColumnAliasReferenceExpression)
+ {
+ _containsAggregateFunctionExpressionVisitor ??= new SingleStoreContainsAggregateFunctionExpressionVisitor();
+ if (!_containsAggregateFunctionExpressionVisitor.ProcessUntilSelect(havingExpression))
+ {
+ var newSelectExpression = selectExpression.Clone();
+
+ newSelectExpression.PushdownIntoSubquery();
+
+ var subQuery = (SelectExpression)newSelectExpression.Tables.Single();
+ var projectionIndex = subQuery.AddToProjection(havingExpression);
+ var alias = subQuery.Projection[projectionIndex].Alias;
+
+ var columnAliasReferenceExpression = _sqlExpressionFactory.ColumnAliasReference(
+ alias,
+ havingExpression,
+ havingExpression.Type,
+ havingExpression.TypeMapping);
+
+ // Having expressions, not containing an aggregate function, need to be part of the GROUP BY clause, because they now also
+ // appear as part of the SELECT clause.
+ var groupBy = subQuery.GroupBy.ToList();
+ groupBy.Add(columnAliasReferenceExpression);
+
+ subQuery = new SelectExpression(
+ subQuery.Alias,
+ subQuery.Tables.ToList(),
+ subQuery.Predicate,
+ groupBy,
+ columnAliasReferenceExpression,
+ subQuery.Projection.ToList(),
+ subQuery.IsDistinct,
+ subQuery.Orderings.ToList(),
+ subQuery.Offset,
+ subQuery.Limit,
+ subQuery.Tags,
+ subQuery.GetAnnotations().ToDictionary(a => a.Name, a => a),
+ _sqlAliasManager,
+ isMutable: false
+ );
+
+ newSelectExpression = new SelectExpression(
+ newSelectExpression.Alias,
+ [subQuery],
+ newSelectExpression.Predicate,
+ newSelectExpression.GroupBy.ToList(),
+ newSelectExpression.Having,
+ projections: [],
+ newSelectExpression.IsDistinct,
+ newSelectExpression.Orderings.ToList(),
+ newSelectExpression.Offset,
+ newSelectExpression.Limit,
+ newSelectExpression.Tags,
+ newSelectExpression.GetAnnotations().ToDictionary(a => a.Name, a => a),
+ _sqlAliasManager,
+ isMutable: true
+ );
+
+ //
+ // UNSOLVED: Somehow recreate projection mappings here.
+ //
+
+ selectExpression = newSelectExpression;
+ }
+ }
+
+ return base.VisitExtension(selectExpression);
+ }
+
+ ///
+ /// This basically needs to reimplement `SelectExpression.PushdownIntoSubquery()`, which we are definitely not going to do.
+ ///
+ protected virtual Expression VisitSelectImmutable(SelectExpression selectExpression)
+ {
+ // MySQL & MariaDB currently do not support complex expressions in HAVING clauses (e.g. function calls).
+ // Instead, they want you to reference SELECT aliases for those expressions in the HAVING clause.
+ // See https://bugs.mysql.com/bug.php?id=103961
+ // This is only an issue for HAVING expressions that do not contain any aggregate functions.
+ var havingExpression = selectExpression.Having;
+ if (havingExpression is not null &&
+ havingExpression is not SqlConstantExpression &&
+ havingExpression is not SingleStoreColumnAliasReferenceExpression)
+ {
+ _containsAggregateFunctionExpressionVisitor ??= new SingleStoreContainsAggregateFunctionExpressionVisitor();
+ if (!_containsAggregateFunctionExpressionVisitor.ProcessUntilSelect(havingExpression))
+ {
+ var subquery = selectExpression.Clone();
+ subquery.ReplaceProjection([]);
+
+ var alias = "having";
+ var havingProjectionExpression = new ProjectionExpression(havingExpression, alias);
+ var columnAliasReferenceExpression = _sqlExpressionFactory.ColumnAliasReference(
+ alias,
+ havingExpression,
+ havingExpression.Type,
+ havingExpression.TypeMapping);
+
+ // Having expressions, not containing an aggregate function, need to be part of the GROUP BY clause, because they now also
+ // appear as part of the SELECT clause.
+ subquery = subquery.Update(
+ subquery.Tables,
+ subquery.Predicate,
+ subquery.GroupBy.Append(columnAliasReferenceExpression).ToList(),
+ columnAliasReferenceExpression,
+ subquery.Projection.Append(havingProjectionExpression).ToList(),
+ subquery.Limit is not null || subquery.Offset is not null
+ ? subquery.Orderings
+ : [],
+ subquery.Offset, // Offset/limit parameters got switched around between EF Core 8 and 9 for no good reason.
+ subquery.Limit);
+
+
+ var outerSelectOrderings = selectExpression.Orderings;
+
+ // foreach (var ordering in subquery.Orderings)
+ // {
+ // var orderingExpression = ordering.Expression;
+ // if (liftOrderings && projectionMap.TryGetValue(orderingExpression, out var outerColumn))
+ // {
+ // _orderings.Add(ordering.Update(outerColumn));
+ // }
+ // else if (liftOrderings
+ // && (!IsDistinct
+ // && GroupBy.Count == 0
+ // || GroupBy.Contains(orderingExpression)))
+ // {
+ // _orderings.Add(
+ // ordering.Update(
+ // subquery.GenerateOuterColumn(subqueryAlias, orderingExpression)));
+ // }
+ // else
+ // {
+ // _orderings.Clear();
+ // break;
+ // }
+ // }
+
+
+ selectExpression = selectExpression.Update(
+ [subquery],
+ selectExpression.Predicate,
+ groupBy: [],
+ having: null,
+ subquery.Projection/*.Select(p => new ProjectionExpression(new ColumnExpression(p.Alias, subquery.Alias, p.Type, null)))*/,
+ outerSelectOrderings,
+ null,
+ null);
+ }
+ }
+
+ return base.VisitExtension(selectExpression);
+ }
+
+ // private ColumnExpression GenerateOuterColumn(
+ // SelectExpression subquery,
+ // string tableAlias,
+ // SqlExpression projection/*,
+ // string columnAlias = null*/)
+ // {
+ // // TODO: Add check if we can add projection in subquery to generate out column
+ // // Subquery having Distinct or GroupBy can block it.
+ // var index = subquery.AddToProjection(projection);
+ // var projectionExpression = subquery.Projection[index];
+ // return CreateColumnExpression(projectionExpression, tableAlias);
+ // }
+ //
+ // private static ColumnExpression CreateColumnExpression(ProjectionExpression subqueryProjection, string tableAlias)
+ // => new(
+ // subqueryProjection.Alias,
+ // tableAlias,
+ // subqueryProjection.Type,
+ // subqueryProjection.Expression.TypeMapping!,
+ // subqueryProjection.Expression switch
+ // {
+ // ColumnExpression columnExpression => columnExpression.IsNullable,
+ // SqlConstantExpression sqlConstantExpression => sqlConstantExpression.Value == null,
+ // _ => true
+ // });
+ }
+}
diff --git a/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreParameterInliningExpressionVisitor.cs b/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreParameterInliningExpressionVisitor.cs
index c62f85c51..5a5b1a465 100644
--- a/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreParameterInliningExpressionVisitor.cs
+++ b/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreParameterInliningExpressionVisitor.cs
@@ -108,7 +108,7 @@ protected virtual Expression VisitSqlParameter(SqlParameterExpression sqlParamet
return new SingleStoreInlinedParameterExpression(
sqlParameterExpression,
- _sqlExpressionFactory.Constant(
+ (SqlConstantExpression)_sqlExpressionFactory.Constant(
_parametersValues[sqlParameterExpression.Name],
sqlParameterExpression.TypeMapping));
}
diff --git a/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreQuerySqlGenerator.cs b/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreQuerySqlGenerator.cs
index dff9a70e4..34f513ff4 100644
--- a/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreQuerySqlGenerator.cs
+++ b/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreQuerySqlGenerator.cs
@@ -412,7 +412,7 @@ protected override Expression VisitDelete(DeleteExpression deleteExpression)
}
throw new InvalidOperationException(
- RelationalStrings.ExecuteOperationWithUnsupportedOperatorInSqlGeneration(nameof(RelationalQueryableExtensions.ExecuteDelete)));
+ RelationalStrings.ExecuteOperationWithUnsupportedOperatorInSqlGeneration(nameof(EntityFrameworkQueryableExtensions.ExecuteDelete)));
}
protected override Expression VisitUpdate(UpdateExpression updateExpression)
@@ -480,7 +480,7 @@ protected override Expression VisitUpdate(UpdateExpression updateExpression)
}
throw new InvalidOperationException(
- RelationalStrings.ExecuteOperationWithUnsupportedOperatorInSqlGeneration(nameof(RelationalQueryableExtensions.ExecuteUpdate)));
+ RelationalStrings.ExecuteOperationWithUnsupportedOperatorInSqlGeneration(nameof(EntityFrameworkQueryableExtensions.ExecuteUpdate)));
}
protected override Expression VisitJsonScalar(JsonScalarExpression jsonScalarExpression)
@@ -548,6 +548,11 @@ protected override Expression VisitJsonScalar(JsonScalarExpression jsonScalarExp
protected override void GenerateValues(ValuesExpression valuesExpression)
{
+ if (valuesExpression.RowValues is null)
+ {
+ throw new UnreachableException();
+ }
+
if (_options.ServerVersion.Supports.Values ||
_options.ServerVersion.Supports.ValuesWithRows)
{
@@ -718,33 +723,24 @@ private SqlUnaryExpression VisitConvert(SqlUnaryExpression sqlUnaryExpression)
!castMapping.Equals(sqlUnaryExpression.Operand.TypeMapping.StoreType, StringComparison.OrdinalIgnoreCase) &&
!sameInnerCastStoreType)
{
- var useDecimalToDoubleWorkaround = false;
-
- if (castMapping.StartsWith("double") &&
- !_options.ServerVersion.Supports.DoubleCast)
- {
- useDecimalToDoubleWorkaround = true;
- castMapping = "decimal(65,30)";
- }
-
- if (useDecimalToDoubleWorkaround)
+ // SingleStore does not support CAST(.. AS double) (CAST/CONVERT target types are limited),
+ // but it does support the cast operator ":>" for DOUBLE/FLOAT.
+ if (castMapping.StartsWith("double", StringComparison.OrdinalIgnoreCase) &&
+ !_options.ServerVersion.Supports.DoubleCast)
{
Sql.Append("(");
+ Visit(sqlUnaryExpression.Operand);
+ Sql.Append(" :> ");
+ Sql.Append("double");
+ Sql.Append(")");
}
-
- Sql.Append("CAST(");
- Visit(sqlUnaryExpression.Operand);
- Sql.Append(" AS ");
- Sql.Append(castMapping);
- Sql.Append(")");
-
- // FLOAT and DOUBLE are supported by CAST() as of MySQL 8.0.17.
- // For server versions before that, a workaround is applied, that casts to a DECIMAL,
- // that is then added to 0e0, which results in a DOUBLE.
- // REF: https://dev.mysql.com/doc/refman/8.0/en/number-literals.html
- if (useDecimalToDoubleWorkaround)
+ else
{
- Sql.Append(" + 0e0)");
+ Sql.Append("CAST(");
+ Visit(sqlUnaryExpression.Operand);
+ Sql.Append(" AS ");
+ Sql.Append(castMapping);
+ Sql.Append(")");
}
}
else
diff --git a/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreQueryTranslationPostprocessor.cs b/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreQueryTranslationPostprocessor.cs
index e8edf17bb..4a148e99f 100644
--- a/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreQueryTranslationPostprocessor.cs
+++ b/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreQueryTranslationPostprocessor.cs
@@ -17,7 +17,7 @@ public class SingleStoreQueryTranslationPostprocessor : RelationalQueryTranslati
public SingleStoreQueryTranslationPostprocessor(
QueryTranslationPostprocessorDependencies dependencies,
RelationalQueryTranslationPostprocessorDependencies relationalDependencies,
- QueryCompilationContext queryCompilationContext,
+ SingleStoreQueryCompilationContext queryCompilationContext,
ISingleStoreOptions options,
SingleStoreSqlExpressionFactory sqlExpressionFactory)
: base(dependencies, relationalDependencies, queryCompilationContext)
@@ -28,8 +28,15 @@ public SingleStoreQueryTranslationPostprocessor(
public override Expression Process(Expression query)
{
+ var mySqlHavingExpressionVisitor = new SingleStoreHavingExpressionVisitor(_sqlExpressionFactory);
+
+ query = mySqlHavingExpressionVisitor.Process(query, usePrePostprocessorMode: true);
+
+ // Changes `SelectExpression.IsMutable` from `true` to `false`.
query = base.Process(query);
+ query = mySqlHavingExpressionVisitor.Process(query, usePrePostprocessorMode: false);
+
query = new SingleStoreJsonParameterExpressionVisitor(_sqlExpressionFactory, _options).Visit(query);
if (_options.ServerVersion.Supports.SingleStoreBug96947Workaround)
@@ -37,6 +44,8 @@ public override Expression Process(Expression query)
query = new SingleStoreBug96947WorkaroundExpressionVisitor(_sqlExpressionFactory).Visit(query);
}
+ query = new BitwiseOperationReturnTypeCorrectingExpressionVisitor(_sqlExpressionFactory).Visit(query);
+
return query;
}
}
diff --git a/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreQueryTranslationPostprocessorFactory.cs b/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreQueryTranslationPostprocessorFactory.cs
index 86a6fd1e3..fc9961add 100644
--- a/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreQueryTranslationPostprocessorFactory.cs
+++ b/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreQueryTranslationPostprocessorFactory.cs
@@ -31,7 +31,7 @@ public virtual QueryTranslationPostprocessor Create(QueryCompilationContext quer
=> new SingleStoreQueryTranslationPostprocessor(
_dependencies,
_relationalDependencies,
- queryCompilationContext,
+ (SingleStoreQueryCompilationContext)queryCompilationContext,
_options,
_sqlExpressionFactory);
}
diff --git a/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreQueryableMethodNormalizingExpressionVisitor.cs b/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreQueryableMethodNormalizingExpressionVisitor.cs
index 2c09739c3..d8bfdbf51 100644
--- a/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreQueryableMethodNormalizingExpressionVisitor.cs
+++ b/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreQueryableMethodNormalizingExpressionVisitor.cs
@@ -11,6 +11,10 @@
namespace EntityFrameworkCore.SingleStore.Query.ExpressionVisitors.Internal;
+// TODO: 9.0
+// Remove SingleStoreQueryableMethodNormalizingExpressionVisitor, SingleStoreBipolarExpression and
+// SingleStoreQueryTranslationPreprocessor.NormalizeQueryableMethod (or the whole class) and use ElementAt() directly in Json translation classes.
+
///
/// Skips normalization of array[index].Property to array.Select(e => e.Property).ElementAt(index),
/// because it messes-up our JSON-Array handling in `SingleStoreSqlTranslatingExpressionVisitor`.
@@ -19,7 +23,7 @@ namespace EntityFrameworkCore.SingleStore.Query.ExpressionVisitors.Internal;
public class SingleStoreQueryableMethodNormalizingExpressionVisitor : QueryableMethodNormalizingExpressionVisitor
{
public SingleStoreQueryableMethodNormalizingExpressionVisitor(QueryCompilationContext queryCompilationContext)
- : base(queryCompilationContext)
+ : base(queryCompilationContext, isEfConstantSupported: true)
{
}
@@ -76,7 +80,7 @@ IEnumerable VisitArguments(IEnumerable arguments)
{
foreach (var expression in arguments)
{
- yield return VisitExtension(expression);
+ yield return Visit(expression);
}
}
diff --git a/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreSqlTranslatingExpressionVisitor.cs b/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreSqlTranslatingExpressionVisitor.cs
index 740c762c5..bba144f47 100644
--- a/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreSqlTranslatingExpressionVisitor.cs
+++ b/src/EFCore.SingleStore/Query/ExpressionVisitors/Internal/SingleStoreSqlTranslatingExpressionVisitor.cs
@@ -32,7 +32,8 @@ public class SingleStoreSqlTranslatingExpressionVisitor : RelationalSqlTranslati
protected static readonly MethodInfo[] NewArrayExpressionSupportMethodInfos = Array.Empty()
.Concat(typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethods().Where(m => m.Name is nameof(SingleStoreDbFunctionsExtensions.Match)
or nameof(SingleStoreDbFunctionsExtensions.IsMatch)))
- .Concat(typeof(string).GetRuntimeMethods().Where(m => m.Name == nameof(string.Concat)))
+ .Concat(typeof(string).GetRuntimeMethods().Where(m => m.Name is nameof(string.Concat)
+ or nameof(string.Join)))
.Where(m => m.GetParameters().Any(p => p.ParameterType.IsArray))
.ToArray();
@@ -126,21 +127,7 @@ protected override Expression VisitUnary(UnaryExpression unaryExpression)
ResetTranslationErrorDetails();
}
- var visitedExpression = base.VisitUnary(unaryExpression);
-
- if (visitedExpression is SqlUnaryExpression sqlUnaryExpression &&
- sqlUnaryExpression.OperatorType == ExpressionType.Not &&
- sqlUnaryExpression.Type != typeof(bool))
- {
- // MySQL implicitly casts numbers used in BITWISE NOT operations (~ operator) to BIGINT UNSIGNED.
- // We need to cast them back, to get the expected result.
- return _sqlExpressionFactory.Convert(
- sqlUnaryExpression,
- sqlUnaryExpression.Type,
- sqlUnaryExpression.TypeMapping);
- }
-
- return visitedExpression;
+ return base.VisitUnary(unaryExpression);
}
protected override Expression VisitBinary(BinaryExpression binaryExpression)
@@ -327,7 +314,7 @@ private Expression TranslateByteArrayElementAccess(Expression array, Expression
protected virtual Expression VisitMethodCallNewArray(NewArrayExpression newArrayExpression)
{
- // Needed for SingleStoreDbFunctionsExtensions.Match() and String.Concat() translation.
+ // Needed for SingleStoreDbFunctionsExtensions.Match(), String.Concat() and String.Join() translations.
if (newArrayExpression.Type == typeof(string[]))
{
return _sqlExpressionFactory.ComplexFunctionArgument(
@@ -337,7 +324,7 @@ protected virtual Expression VisitMethodCallNewArray(NewArrayExpression newArray
typeof(string[]));
}
- // Needed for String.Concat() translation.
+ // Needed for String.Concat() translation and String.Join() translations.
if (newArrayExpression.Type == typeof(object[]))
{
var typeMapping = ((SingleStoreStringTypeMapping)Dependencies.TypeMappingSource.GetMapping(typeof(string))).Clone(forceToString: true);
@@ -456,6 +443,20 @@ protected virtual void ResetTranslationErrorDetails()
base.Translate(Expression.Constant(0));
}
+ public override SqlExpression GenerateGreatest(IReadOnlyList expressions, Type resultType)
+ => _sqlExpressionFactory.NullableFunction(
+ "GREATEST",
+ expressions,
+ resultType,
+ true);
+
+ public override SqlExpression GenerateLeast(IReadOnlyList expressions, Type resultType)
+ => _sqlExpressionFactory.NullableFunction(
+ "LEAST",
+ expressions,
+ resultType,
+ true);
+
#region Copied from RelationalSqlTranslatingExpressionVisitor
private static Expression TryRemoveImplicitConvert(Expression expression)
diff --git a/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreBinaryExpression.cs b/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreBinaryExpression.cs
index 4e028b386..e681d09a5 100644
--- a/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreBinaryExpression.cs
+++ b/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreBinaryExpression.cs
@@ -4,6 +4,7 @@
using System;
using System.Linq.Expressions;
+using System.Reflection;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Storage;
@@ -28,6 +29,8 @@ public enum SingleStoreBinaryExpressionOperatorType
public class SingleStoreBinaryExpression : SqlExpression
{
+ private static ConstructorInfo _quotingConstructor;
+
public SingleStoreBinaryExpression(
SingleStoreBinaryExpressionOperatorType operatorType,
SqlExpression left,
@@ -62,6 +65,16 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
return Update(left, right);
}
+ public override Expression Quote()
+ => New(
+ _quotingConstructor ??= typeof(SingleStoreBinaryExpression).GetConstructor(
+ [typeof(SingleStoreBinaryExpressionOperatorType), typeof(SqlExpression), typeof(SqlExpression), typeof(Type), typeof(RelationalTypeMapping)])!,
+ Constant(OperatorType),
+ Left.Quote(),
+ Right.Quote(),
+ Constant(Type),
+ RelationalExpressionQuotingUtilities.QuoteTypeMapping(TypeMapping));
+
public virtual SingleStoreBinaryExpression Update(SqlExpression left, SqlExpression right)
=> left != Left || right != Right
? new SingleStoreBinaryExpression(OperatorType, left, right, Type, TypeMapping)
diff --git a/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreCollateExpression.cs b/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreCollateExpression.cs
index 1d0e221fb..672af0751 100644
--- a/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreCollateExpression.cs
+++ b/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreCollateExpression.cs
@@ -5,6 +5,7 @@
using System;
using System.Linq;
using System.Linq.Expressions;
+using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
@@ -18,6 +19,8 @@ namespace EntityFrameworkCore.SingleStore.Query.Expressions.Internal
///
public class SingleStoreCollateExpression : SqlExpression
{
+ private static ConstructorInfo _quotingConstructor;
+
private readonly SqlExpression _valueExpression;
private readonly string _charset;
private readonly string _collation;
@@ -78,6 +81,15 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
return Update(valueExpression);
}
+ public override Expression Quote()
+ => New(
+ _quotingConstructor ??= typeof(SingleStoreInlinedParameterExpression).GetConstructor(
+ [typeof(SqlExpression), typeof(string), typeof(string), typeof(RelationalTypeMapping)])!,
+ ValueExpression.Quote(),
+ Constant(Charset),
+ Constant(Collation),
+ RelationalExpressionQuotingUtilities.QuoteTypeMapping(TypeMapping));
+
public virtual SingleStoreCollateExpression Update(SqlExpression valueExpression)
=> valueExpression != _valueExpression &&
valueExpression != null
diff --git a/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreColumnAliasReferenceExpression.cs b/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreColumnAliasReferenceExpression.cs
index 332ac48d8..85ca0625f 100644
--- a/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreColumnAliasReferenceExpression.cs
+++ b/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreColumnAliasReferenceExpression.cs
@@ -4,6 +4,7 @@
using System;
using System.Linq.Expressions;
+using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
@@ -16,6 +17,8 @@ namespace EntityFrameworkCore.SingleStore.Query.Expressions.Internal
///
public class SingleStoreColumnAliasReferenceExpression : SqlExpression, IEquatable
{
+ private static ConstructorInfo _quotingConstructor;
+
[NotNull]
public virtual string Alias { get; }
@@ -36,6 +39,15 @@ public SingleStoreColumnAliasReferenceExpression(
protected override Expression VisitChildren(ExpressionVisitor visitor)
=> this;
+ public override Expression Quote()
+ => New(
+ _quotingConstructor ??= typeof(SingleStoreColumnAliasReferenceExpression).GetConstructor(
+ [typeof(string), typeof(SqlExpression), typeof(Type), typeof(RelationalTypeMapping)])!,
+ Constant(Alias),
+ Expression,
+ Constant(Type),
+ RelationalExpressionQuotingUtilities.QuoteTypeMapping(TypeMapping));
+
public virtual SingleStoreColumnAliasReferenceExpression Update(
[NotNull] string alias,
[NotNull] SqlExpression expression)
diff --git a/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreComplexFunctionArgumentExpression.cs b/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreComplexFunctionArgumentExpression.cs
index 48179e2ca..e29062e64 100644
--- a/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreComplexFunctionArgumentExpression.cs
+++ b/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreComplexFunctionArgumentExpression.cs
@@ -6,6 +6,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
+using System.Reflection;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Storage;
@@ -15,6 +16,8 @@ namespace EntityFrameworkCore.SingleStore.Query.Expressions.Internal
{
public class SingleStoreComplexFunctionArgumentExpression : SqlExpression
{
+ private static ConstructorInfo _quotingConstructor;
+
public SingleStoreComplexFunctionArgumentExpression(
IEnumerable argumentParts,
string delimiter,
@@ -53,6 +56,16 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
return Update(argumentParts, Delimiter);
}
+ ///
+ public override Expression Quote()
+ => New(
+ _quotingConstructor ??= typeof(SingleStoreColumnAliasReferenceExpression).GetConstructor(
+ [typeof(IReadOnlyList), typeof(string), typeof(Type), typeof(RelationalTypeMapping)])!,
+ NewArrayInit(typeof(SqlExpression), ArgumentParts.Select(p => p.Quote())),
+ Constant(Delimiter),
+ Constant(Type),
+ RelationalExpressionQuotingUtilities.QuoteTypeMapping(TypeMapping));
+
public virtual SingleStoreComplexFunctionArgumentExpression Update(IReadOnlyList argumentParts, string delimiter)
=> !argumentParts.SequenceEqual(ArgumentParts)
? new SingleStoreComplexFunctionArgumentExpression(argumentParts, delimiter, Type, TypeMapping)
diff --git a/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreInlinedParameterExpression.cs b/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreInlinedParameterExpression.cs
index bf4291058..5191bf279 100644
--- a/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreInlinedParameterExpression.cs
+++ b/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreInlinedParameterExpression.cs
@@ -4,6 +4,7 @@
using System;
using System.Linq.Expressions;
+using System.Reflection;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Utilities;
@@ -12,6 +13,8 @@ namespace EntityFrameworkCore.SingleStore.Query.Expressions.Internal;
public class SingleStoreInlinedParameterExpression : SqlExpression
{
+ private static ConstructorInfo _quotingConstructor;
+
public SingleStoreInlinedParameterExpression(
SqlParameterExpression parameterExpression,
SqlConstantExpression valueExpression)
@@ -23,7 +26,7 @@ public SingleStoreInlinedParameterExpression(
ValueExpression = valueExpression;
}
- public virtual Expression ParameterExpression { get; }
+ public virtual SqlParameterExpression ParameterExpression { get; }
public virtual SqlConstantExpression ValueExpression { get; }
protected override Expression VisitChildren(ExpressionVisitor visitor)
@@ -34,6 +37,14 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
return Update(parameterExpression, valueExpression);
}
+ ///
+ public override Expression Quote()
+ => New(
+ _quotingConstructor ??= typeof(SingleStoreInlinedParameterExpression).GetConstructor(
+ [typeof(SqlParameterExpression), typeof(SqlConstantExpression)])!,
+ ParameterExpression.Quote(),
+ ValueExpression.Quote());
+
protected override void Print(ExpressionPrinter expressionPrinter)
{
expressionPrinter.Visit(ValueExpression);
diff --git a/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreJsonArrayIndexExpression.cs b/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreJsonArrayIndexExpression.cs
index da0183806..e0b7cc5a7 100644
--- a/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreJsonArrayIndexExpression.cs
+++ b/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreJsonArrayIndexExpression.cs
@@ -4,6 +4,7 @@
using System;
using System.Linq.Expressions;
+using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
@@ -16,6 +17,8 @@ namespace EntityFrameworkCore.SingleStore.Query.Expressions.Internal
///
public class SingleStoreJsonArrayIndexExpression : SqlExpression, IEquatable
{
+ private static ConstructorInfo _quotingConstructor;
+
[NotNull]
public virtual SqlExpression Expression { get; }
@@ -31,6 +34,15 @@ public SingleStoreJsonArrayIndexExpression(
protected override Expression VisitChildren(ExpressionVisitor visitor)
=> Update((SqlExpression)visitor.Visit(Expression));
+ ///
+ public override Expression Quote()
+ => New(
+ _quotingConstructor ??= typeof(SingleStoreInlinedParameterExpression).GetConstructor(
+ [typeof(SqlExpression), typeof(Type), typeof(RelationalTypeMapping)])!,
+ Expression.Quote(),
+ Constant(Type),
+ RelationalExpressionQuotingUtilities.QuoteTypeMapping(TypeMapping));
+
public virtual SingleStoreJsonArrayIndexExpression Update(
[NotNull] SqlExpression expression)
=> expression == Expression
diff --git a/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreJsonTraversalExpression.cs b/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreJsonTraversalExpression.cs
index ca228bfc5..e24d528ea 100644
--- a/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreJsonTraversalExpression.cs
+++ b/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreJsonTraversalExpression.cs
@@ -6,6 +6,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
+using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
@@ -18,6 +19,8 @@ namespace EntityFrameworkCore.SingleStore.Query.Expressions.Internal
///
public class SingleStoreJsonTraversalExpression : SqlExpression, IEquatable
{
+ private static ConstructorInfo _quotingConstructor;
+
///
/// The JSON column.
///
@@ -68,6 +71,16 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
(SqlExpression)visitor.Visit(Expression),
Path.Select(p => (SqlExpression)visitor.Visit(p)).ToArray());
+ ///
+ public override Expression Quote()
+ => New(
+ _quotingConstructor ??= typeof(SingleStoreInlinedParameterExpression).GetConstructor(
+ [typeof(SqlExpression), typeof(IReadOnlyList), typeof(bool), typeof(Type), typeof(RelationalTypeMapping)])!,
+ Expression.Quote(),
+ NewArrayInit(typeof(SqlExpression), Path.Select(p => p.Quote())),
+ Constant(ReturnsText),
+ RelationalExpressionQuotingUtilities.QuoteTypeMapping(TypeMapping));
+
public virtual SingleStoreJsonTraversalExpression Update(
[NotNull] SqlExpression expression,
[NotNull] IReadOnlyList path)
diff --git a/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreMatchExpression.cs b/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreMatchExpression.cs
index cd49b836b..efb79acfd 100644
--- a/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreMatchExpression.cs
+++ b/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreMatchExpression.cs
@@ -4,6 +4,7 @@
using System;
using System.Linq.Expressions;
+using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
@@ -15,6 +16,8 @@ namespace EntityFrameworkCore.SingleStore.Query.Expressions.Internal
{
public class SingleStoreMatchExpression : SqlExpression
{
+ private static ConstructorInfo _quotingConstructor;
+
public SingleStoreMatchExpression(
SqlExpression match,
SqlExpression against,
@@ -49,6 +52,15 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
return Update(match, against);
}
+ ///
+ public override Expression Quote()
+ => New(
+ _quotingConstructor ??= typeof(SingleStoreInlinedParameterExpression).GetConstructor(
+ [typeof(SqlExpression), typeof(SqlExpression), typeof(RelationalTypeMapping)])!,
+ Match.Quote(),
+ Against.Quote(),
+ RelationalExpressionQuotingUtilities.QuoteTypeMapping(TypeMapping));
+
public virtual SingleStoreMatchExpression Update(SqlExpression match, SqlExpression against)
=> match != Match || against != Against
? new SingleStoreMatchExpression(
diff --git a/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreRegexpExpression.cs b/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreRegexpExpression.cs
index 49c80f6a2..51f4298ce 100644
--- a/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreRegexpExpression.cs
+++ b/src/EFCore.SingleStore/Query/Expressions/Internal/SingleStoreRegexpExpression.cs
@@ -3,6 +3,7 @@
// Licensed under the MIT. See LICENSE in the project root for license information.
using System.Linq.Expressions;
+using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
@@ -14,6 +15,8 @@ namespace EntityFrameworkCore.SingleStore.Query.Expressions.Internal
{
public class SingleStoreRegexpExpression : SqlExpression
{
+ private static ConstructorInfo _quotingConstructor;
+
public SingleStoreRegexpExpression(
[NotNull] SqlExpression match,
[NotNull] SqlExpression pattern,
@@ -47,6 +50,15 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
return Update(match, pattern);
}
+ ///
+ public override Expression Quote()
+ => New(
+ _quotingConstructor ??= typeof(SingleStoreInlinedParameterExpression).GetConstructor(
+ [typeof(SqlExpression), typeof(SqlExpression), typeof(RelationalTypeMapping)])!,
+ Match.Quote(),
+ Pattern.Quote(),
+ RelationalExpressionQuotingUtilities.QuoteTypeMapping(TypeMapping));
+
public virtual SingleStoreRegexpExpression Update(SqlExpression match, SqlExpression pattern)
=> match != Match ||
pattern != Pattern
diff --git a/src/EFCore.SingleStore/Query/Internal/SingleStoreDateDiffFunctionsTranslator.cs b/src/EFCore.SingleStore/Query/Internal/SingleStoreDateDiffFunctionsTranslator.cs
index b283d9cfe..fd0452ce6 100644
--- a/src/EFCore.SingleStore/Query/Internal/SingleStoreDateDiffFunctionsTranslator.cs
+++ b/src/EFCore.SingleStore/Query/Internal/SingleStoreDateDiffFunctionsTranslator.cs
@@ -27,42 +27,83 @@ private readonly Dictionary _methodInfoDateDiffMapping
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffYear), new[] { typeof(DbFunctions), typeof(DateTimeOffset?), typeof(DateTimeOffset?) }), "YEAR" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffYear), new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) }), "YEAR" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffYear), new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) }), "YEAR" },
+
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffQuarter), new[] { typeof(DbFunctions), typeof(DateTime), typeof(DateTime) }), "QUARTER" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffQuarter), new[] { typeof(DbFunctions), typeof(DateTime?), typeof(DateTime?) }), "QUARTER" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffQuarter), new[] { typeof(DbFunctions), typeof(DateTimeOffset), typeof(DateTimeOffset) }), "QUARTER" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffQuarter), new[] { typeof(DbFunctions), typeof(DateTimeOffset?), typeof(DateTimeOffset?) }), "QUARTER" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffQuarter), new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) }), "QUARTER" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffQuarter), new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) }), "QUARTER" },
+
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMonth), new[] { typeof(DbFunctions), typeof(DateTime), typeof(DateTime) }), "MONTH" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMonth), new[] { typeof(DbFunctions), typeof(DateTime?), typeof(DateTime?) }), "MONTH" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMonth), new[] { typeof(DbFunctions), typeof(DateTimeOffset), typeof(DateTimeOffset) }), "MONTH" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMonth), new[] { typeof(DbFunctions), typeof(DateTimeOffset?), typeof(DateTimeOffset?) }), "MONTH" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMonth), new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) }), "MONTH" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMonth), new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) }), "MONTH" },
+
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffWeek), new[] { typeof(DbFunctions), typeof(DateTime), typeof(DateTime) }), "WEEK" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffWeek), new[] { typeof(DbFunctions), typeof(DateTime?), typeof(DateTime?) }), "WEEK" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffWeek), new[] { typeof(DbFunctions), typeof(DateTimeOffset), typeof(DateTimeOffset) }), "WEEK" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffWeek), new[] { typeof(DbFunctions), typeof(DateTimeOffset?), typeof(DateTimeOffset?) }), "WEEK" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffWeek), new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) }), "WEEK" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffWeek), new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) }), "WEEK" },
+
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffDay), new[] { typeof(DbFunctions), typeof(DateTime), typeof(DateTime) }), "DAY" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffDay), new[] { typeof(DbFunctions), typeof(DateTime?), typeof(DateTime?) }), "DAY" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffDay), new[] { typeof(DbFunctions), typeof(DateTimeOffset), typeof(DateTimeOffset) }), "DAY" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffDay), new[] { typeof(DbFunctions), typeof(DateTimeOffset?), typeof(DateTimeOffset?) }), "DAY" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffDay), new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) }), "DAY" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffDay), new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) }), "DAY" },
+
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffHour), new[] { typeof(DbFunctions), typeof(DateTime), typeof(DateTime) }), "HOUR" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffHour), new[] { typeof(DbFunctions), typeof(DateTime?), typeof(DateTime?) }), "HOUR" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffHour), new[] { typeof(DbFunctions), typeof(DateTimeOffset), typeof(DateTimeOffset) }), "HOUR" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffHour), new[] { typeof(DbFunctions), typeof(DateTimeOffset?), typeof(DateTimeOffset?) }), "HOUR" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffHour), new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) }), "HOUR" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffHour), new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) }), "HOUR" },
+
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMinute), new[] { typeof(DbFunctions), typeof(DateTime), typeof(DateTime) }), "MINUTE" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMinute), new[] { typeof(DbFunctions), typeof(DateTime?), typeof(DateTime?) }), "MINUTE" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMinute), new[] { typeof(DbFunctions), typeof(DateTimeOffset), typeof(DateTimeOffset) }), "MINUTE" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMinute), new[] { typeof(DbFunctions), typeof(DateTimeOffset?), typeof(DateTimeOffset?) }), "MINUTE" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMinute), new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) }), "MINUTE" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMinute), new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) }), "MINUTE" },
+
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffSecond), new[] { typeof(DbFunctions), typeof(DateTime), typeof(DateTime) }), "SECOND" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffSecond), new[] { typeof(DbFunctions), typeof(DateTime?), typeof(DateTime?) }), "SECOND" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffSecond), new[] { typeof(DbFunctions), typeof(DateTimeOffset), typeof(DateTimeOffset) }), "SECOND" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffSecond), new[] { typeof(DbFunctions), typeof(DateTimeOffset?), typeof(DateTimeOffset?) }), "SECOND" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffSecond), new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) }), "SECOND" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffSecond), new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) }), "SECOND" },
+
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMillisecond), new[] { typeof(DbFunctions), typeof(DateTime), typeof(DateTime) }), "MILLISECOND" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMillisecond), new[] { typeof(DbFunctions), typeof(DateTime?), typeof(DateTime?) }), "MILLISECOND" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMillisecond), new[] { typeof(DbFunctions), typeof(DateTimeOffset), typeof(DateTimeOffset) }), "MILLISECOND" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMillisecond), new[] { typeof(DbFunctions), typeof(DateTimeOffset?), typeof(DateTimeOffset?) }), "MILLISECOND" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMillisecond), new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) }), "MILLISECOND" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMillisecond), new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) }), "MILLISECOND" },
+
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMicrosecond), new[] { typeof(DbFunctions), typeof(DateTime), typeof(DateTime) }), "MICROSECOND" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMicrosecond), new[] { typeof(DbFunctions), typeof(DateTime?), typeof(DateTime?) }), "MICROSECOND" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMicrosecond), new[] { typeof(DbFunctions), typeof(DateTimeOffset), typeof(DateTimeOffset) }), "MICROSECOND" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMicrosecond), new[] { typeof(DbFunctions), typeof(DateTimeOffset?), typeof(DateTimeOffset?) }), "MICROSECOND" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMicrosecond), new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) }), "MICROSECOND" },
{ typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffMicrosecond), new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) }), "MICROSECOND" },
+
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffTick), new[] { typeof(DbFunctions), typeof(DateTime), typeof(DateTime) }), "TICK" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffTick), new[] { typeof(DbFunctions), typeof(DateTime?), typeof(DateTime?) }), "TICK" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffTick), new[] { typeof(DbFunctions), typeof(DateTimeOffset), typeof(DateTimeOffset) }), "TICK" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffTick), new[] { typeof(DbFunctions), typeof(DateTimeOffset?), typeof(DateTimeOffset?) }), "TICK" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffTick), new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) }), "TICK" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffTick), new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) }), "TICK" },
+
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffNanosecond), new[] { typeof(DbFunctions), typeof(DateTime), typeof(DateTime) }), "NANOSECOND" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffNanosecond), new[] { typeof(DbFunctions), typeof(DateTime?), typeof(DateTime?) }), "NANOSECOND" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffNanosecond), new[] { typeof(DbFunctions), typeof(DateTimeOffset), typeof(DateTimeOffset) }), "NANOSECOND" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffNanosecond), new[] { typeof(DbFunctions), typeof(DateTimeOffset?), typeof(DateTimeOffset?) }), "NANOSECOND" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffNanosecond), new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) }), "NANOSECOND" },
+ { typeof(SingleStoreDbFunctionsExtensions).GetRuntimeMethod(nameof(SingleStoreDbFunctionsExtensions.DateDiffNanosecond), new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) }), "NANOSECOND" },
};
private readonly SingleStoreSqlExpressionFactory _sqlExpressionFactory;
@@ -87,18 +128,32 @@ public virtual SqlExpression Translate(
startDate = _sqlExpressionFactory.ApplyTypeMapping(startDate, typeMapping);
endDate = _sqlExpressionFactory.ApplyTypeMapping(endDate, typeMapping);
- return _sqlExpressionFactory.NullableFunction(
+ var actualDatePart = datePart is "MILLISECOND"
+ or "TICK"
+ or "NANOSECOND"
+ ? "MICROSECOND"
+ : datePart;
+
+ var timeStampDiffExpression = _sqlExpressionFactory.NullableFunction(
"TIMESTAMPDIFF",
new[]
{
- _sqlExpressionFactory.Fragment(datePart),
+ _sqlExpressionFactory.Fragment(actualDatePart),
startDate,
endDate
},
typeof(int),
typeMapping: null,
onlyNullWhenAnyNullPropagatingArgumentIsNull: true,
- argumentsPropagateNullability: new []{false, true, true});
+ argumentsPropagateNullability: new[] { false, true, true });
+
+ return datePart switch
+ {
+ "MILLISECOND" => _sqlExpressionFactory.SingleStoreIntegerDivide(timeStampDiffExpression, _sqlExpressionFactory.Constant(1_000)),
+ "TICK" => _sqlExpressionFactory.Multiply(timeStampDiffExpression, _sqlExpressionFactory.Constant(10)),
+ "NANOSECOND" => _sqlExpressionFactory.Multiply(timeStampDiffExpression, _sqlExpressionFactory.Constant(1_000)),
+ _ => timeStampDiffExpression
+ };
}
return null;
diff --git a/src/EFCore.SingleStore/Query/Internal/SingleStoreDateTimeMemberTranslator.cs b/src/EFCore.SingleStore/Query/Internal/SingleStoreDateTimeMemberTranslator.cs
index 463925177..e613245e4 100644
--- a/src/EFCore.SingleStore/Query/Internal/SingleStoreDateTimeMemberTranslator.cs
+++ b/src/EFCore.SingleStore/Query/Internal/SingleStoreDateTimeMemberTranslator.cs
@@ -5,12 +5,12 @@
using System;
using System.Collections.Generic;
using System.Reflection;
-using EntityFrameworkCore.SingleStore.Infrastructure.Internal;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using EntityFrameworkCore.SingleStore.Infrastructure.Internal;
using EntityFrameworkCore.SingleStore.Utilities;
namespace EntityFrameworkCore.SingleStore.Query.Internal
@@ -29,11 +29,14 @@ public class SingleStoreDateTimeMemberTranslator : IMemberTranslator
{ nameof(DateTime.Millisecond), ("microsecond", 1000) },
};
private readonly SingleStoreSqlExpressionFactory _sqlExpressionFactory;
+ private readonly ISingleStoreOptions _mySqlOptions;
+
private readonly string _sessionTimeZone;
- public SingleStoreDateTimeMemberTranslator(ISqlExpressionFactory sqlExpressionFactory, IDbContextOptions dbContextOptions)
+ public SingleStoreDateTimeMemberTranslator(ISqlExpressionFactory sqlExpressionFactory, IDbContextOptions dbContextOptions, ISingleStoreOptions mySqlOptions)
{
_sqlExpressionFactory = (SingleStoreSqlExpressionFactory)sqlExpressionFactory;
+ _mySqlOptions = mySqlOptions;
// Read the configured session time zone offset (e.g. "-08:00") from provider options.
// If not configured, we default to "+00:00" (UTC) because SingleStore ignores @@session.time_zone at runtime.
@@ -109,13 +112,17 @@ public virtual SqlExpression Translate(
declaringType == typeof(DateTimeOffset)
? "UTC_TIMESTAMP"
: "CURRENT_TIMESTAMP",
- Array.Empty(),
+ _mySqlOptions.ServerVersion.Supports.DateTime6 ?
+ new [] { _sqlExpressionFactory.Constant(6)} :
+ Array.Empty(),
returnType);
case nameof(DateTime.UtcNow):
return _sqlExpressionFactory.NonNullableFunction(
"UTC_TIMESTAMP",
- Array.Empty(),
+ _mySqlOptions.ServerVersion.Supports.DateTime6 ?
+ new [] { _sqlExpressionFactory.Constant(6)} :
+ ArraySegment.Empty,
returnType);
case nameof(DateTime.Today):
diff --git a/src/EFCore.SingleStore/Query/Internal/SingleStoreDateTimeMethodTranslator.cs b/src/EFCore.SingleStore/Query/Internal/SingleStoreDateTimeMethodTranslator.cs
index 2f8cf0de7..704c23ff5 100644
--- a/src/EFCore.SingleStore/Query/Internal/SingleStoreDateTimeMethodTranslator.cs
+++ b/src/EFCore.SingleStore/Query/Internal/SingleStoreDateTimeMethodTranslator.cs
@@ -46,6 +46,8 @@ public class SingleStoreDateTimeMethodTranslator : IMethodCallTranslator
private static readonly MethodInfo _timeOnlyAddHoursMethod = typeof(TimeOnly).GetRuntimeMethod(nameof(TimeOnly.AddHours), new[] {typeof(double)})!;
private static readonly MethodInfo _timeOnlyAddMinutesMethod = typeof(TimeOnly).GetRuntimeMethod(nameof(TimeOnly.AddMinutes), new[] {typeof(double)})!;
private static readonly MethodInfo _timeOnlyIsBetweenMethod = typeof(TimeOnly).GetRuntimeMethod(nameof(TimeOnly.IsBetween), new[] { typeof(TimeOnly), typeof(TimeOnly) })!;
+ private static readonly MethodInfo _timeOnlyFromDateTimeMethod = typeof(TimeOnly).GetRuntimeMethod(nameof(TimeOnly.FromDateTime), new[] { typeof(DateTime) })!;
+ private static readonly MethodInfo _timeOnlyFromTimeSpanMethod = typeof(TimeOnly).GetRuntimeMethod(nameof(TimeOnly.FromTimeSpan), new[] { typeof(TimeSpan) })!;
private static readonly MethodInfo _dateOnlyFromDateTimeMethod = typeof(DateOnly).GetRuntimeMethod(nameof(DateOnly.FromDateTime), new[] { typeof(DateTime) })!;
private static readonly MethodInfo _dateOnlyToDateTimeMethod = typeof(DateOnly).GetRuntimeMethod(nameof(DateOnly.ToDateTime), new[] { typeof(TimeOnly) })!;
@@ -148,11 +150,31 @@ public virtual SqlExpression Translate(
_sqlExpressionFactory.GreaterThanOrEqual(instance, arguments[0]),
_sqlExpressionFactory.LessThan(instance, arguments[1]));
}
+
+ if (instance is null &&
+ arguments.Count == 1)
+ {
+ if (method == _timeOnlyFromDateTimeMethod)
+ {
+ return _sqlExpressionFactory.NullableFunction(
+ "TIME",
+ arguments,
+ typeof(TimeOnly),
+ onlyNullWhenAnyNullPropagatingArgumentIsNull: true);
+ }
+
+ if (method == _timeOnlyFromTimeSpanMethod)
+ {
+ return _sqlExpressionFactory.Convert(arguments[0], method.ReturnType);
+ }
+ }
}
if (method.DeclaringType == typeof(DateOnly))
{
- if (method == _dateOnlyFromDateTimeMethod)
+ if (method == _dateOnlyFromDateTimeMethod &&
+ instance is null &&
+ arguments.Count == 1)
{
return _sqlExpressionFactory.NullableFunction(
"DATE",
diff --git a/src/EFCore.SingleStore/Query/Internal/SingleStoreMemberTranslatorProvider.cs b/src/EFCore.SingleStore/Query/Internal/SingleStoreMemberTranslatorProvider.cs
index 67c2080b9..77286ea5f 100644
--- a/src/EFCore.SingleStore/Query/Internal/SingleStoreMemberTranslatorProvider.cs
+++ b/src/EFCore.SingleStore/Query/Internal/SingleStoreMemberTranslatorProvider.cs
@@ -5,19 +5,20 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Query;
+using EntityFrameworkCore.SingleStore.Infrastructure.Internal;
namespace EntityFrameworkCore.SingleStore.Query.Internal
{
public class SingleStoreMemberTranslatorProvider : RelationalMemberTranslatorProvider
{
- public SingleStoreMemberTranslatorProvider([NotNull] RelationalMemberTranslatorProviderDependencies dependencies, IDbContextOptions dbContextOptions)
+ public SingleStoreMemberTranslatorProvider([NotNull] RelationalMemberTranslatorProviderDependencies dependencies, IDbContextOptions dbContextOptions, ISingleStoreOptions mySqlOptions)
: base(dependencies)
{
var sqlExpressionFactory = (SingleStoreSqlExpressionFactory)dependencies.SqlExpressionFactory;
AddTranslators(
new IMemberTranslator[] {
- new SingleStoreDateTimeMemberTranslator(sqlExpressionFactory, dbContextOptions),
+ new SingleStoreDateTimeMemberTranslator(sqlExpressionFactory, dbContextOptions, mySqlOptions),
new SingleStoreStringMemberTranslator(sqlExpressionFactory),
new SingleStoreTimeSpanMemberTranslator(sqlExpressionFactory),
});
diff --git a/src/EFCore.SingleStore/Query/Internal/SingleStoreObjectToStringTranslator.cs b/src/EFCore.SingleStore/Query/Internal/SingleStoreObjectToStringTranslator.cs
index d756f8078..bdc09d517 100644
--- a/src/EFCore.SingleStore/Query/Internal/SingleStoreObjectToStringTranslator.cs
+++ b/src/EFCore.SingleStore/Query/Internal/SingleStoreObjectToStringTranslator.cs
@@ -52,41 +52,49 @@ public virtual SqlExpression Translate(
IReadOnlyList arguments,
IDiagnosticsLogger logger)
{
- if (instance == null ||
- method.Name != nameof(ToString) ||
- arguments.Count != 0)
+ if (instance == null || method.Name != nameof(ToString) || arguments.Count != 0)
{
return null;
}
+ if (instance.TypeMapping?.ClrType == typeof(string))
+ {
+ return instance;
+ }
+
if (instance.Type == typeof(bool))
{
- return instance is ColumnExpression columnExpression &&
- columnExpression.IsNullable
- ? _sqlExpressionFactory.Case(
+ if (instance is not ColumnExpression { IsNullable: false })
+ {
+ return _sqlExpressionFactory.Case(
+ instance,
new[]
{
new CaseWhenClause(
- _sqlExpressionFactory.Equal(instance, _sqlExpressionFactory.Constant(false)),
+ _sqlExpressionFactory.Constant(false),
_sqlExpressionFactory.Constant(false.ToString())),
new CaseWhenClause(
- _sqlExpressionFactory.Equal(instance, _sqlExpressionFactory.Constant(true)),
+ _sqlExpressionFactory.Constant(true),
_sqlExpressionFactory.Constant(true.ToString()))
},
- _sqlExpressionFactory.Constant(null))
- : _sqlExpressionFactory.Case(
- new[]
- {
- new CaseWhenClause(
- _sqlExpressionFactory.Equal(instance, _sqlExpressionFactory.Constant(false)),
- _sqlExpressionFactory.Constant(false.ToString()))
- },
- _sqlExpressionFactory.Constant(true.ToString()));
+ _sqlExpressionFactory.Constant(string.Empty));
+ }
+
+ return _sqlExpressionFactory.Case(
+ new[]
+ {
+ new CaseWhenClause(
+ instance,
+ _sqlExpressionFactory.Constant(true.ToString()))
+ },
+ _sqlExpressionFactory.Constant(false.ToString()));
}
- // Translates parameterless Object.ToString() calls.
- return _supportedTypes.Contains(instance.Type.UnwrapNullableType())
- ? _sqlExpressionFactory.Convert(instance, typeof(string))
+ // Enums are handled by EnumMethodTranslator.
+ return _supportedTypes.Contains(instance.Type)
+ ? _sqlExpressionFactory.Coalesce(
+ _sqlExpressionFactory.Convert(instance, typeof(string)),
+ _sqlExpressionFactory.Constant(string.Empty))
: null;
}
}
diff --git a/src/EFCore.SingleStore/Query/Internal/SingleStoreParameterBasedSqlProcessor.cs b/src/EFCore.SingleStore/Query/Internal/SingleStoreParameterBasedSqlProcessor.cs
index e5373bc35..5d5d10561 100644
--- a/src/EFCore.SingleStore/Query/Internal/SingleStoreParameterBasedSqlProcessor.cs
+++ b/src/EFCore.SingleStore/Query/Internal/SingleStoreParameterBasedSqlProcessor.cs
@@ -20,9 +20,9 @@ public class SingleStoreParameterBasedSqlProcessor : RelationalParameterBasedSql
public SingleStoreParameterBasedSqlProcessor(
RelationalParameterBasedSqlProcessorDependencies dependencies,
- bool useRelationalNulls,
+ RelationalParameterBasedSqlProcessorParameters parameters,
ISingleStoreOptions options)
- : base(dependencies, useRelationalNulls)
+ : base(dependencies, parameters)
{
_options = options;
}
@@ -47,8 +47,6 @@ public override Expression Optimize(
queryExpression = new SingleStoreBoolOptimizingExpressionVisitor(Dependencies.SqlExpressionFactory).Visit(queryExpression);
}
- queryExpression = new SingleStoreHavingExpressionVisitor((SingleStoreSqlExpressionFactory)Dependencies.SqlExpressionFactory).Visit(queryExpression);
-
queryExpression = new SingleStoreParameterInliningExpressionVisitor(
Dependencies.TypeMappingSource,
Dependencies.SqlExpressionFactory,
@@ -71,7 +69,7 @@ protected override Expression ProcessSqlNullability(
Check.NotNull(queryExpression, nameof(queryExpression));
Check.NotNull(parametersValues, nameof(parametersValues));
- queryExpression = new SingleStoreSqlNullabilityProcessor(Dependencies, UseRelationalNulls)
+ queryExpression = new SingleStoreSqlNullabilityProcessor(Dependencies, Parameters)
.Process(queryExpression, parametersValues, out canCache);
return queryExpression;
diff --git a/src/EFCore.SingleStore/Query/Internal/SingleStoreParameterBasedSqlProcessorFactory.cs b/src/EFCore.SingleStore/Query/Internal/SingleStoreParameterBasedSqlProcessorFactory.cs
index d2176ace4..4b4b95a9c 100644
--- a/src/EFCore.SingleStore/Query/Internal/SingleStoreParameterBasedSqlProcessorFactory.cs
+++ b/src/EFCore.SingleStore/Query/Internal/SingleStoreParameterBasedSqlProcessorFactory.cs
@@ -21,7 +21,7 @@ public SingleStoreParameterBasedSqlProcessorFactory(
_options = options;
}
- public virtual RelationalParameterBasedSqlProcessor Create(bool useRelationalNulls)
- => new SingleStoreParameterBasedSqlProcessor(_dependencies, useRelationalNulls, _options);
+ public virtual RelationalParameterBasedSqlProcessor Create(RelationalParameterBasedSqlProcessorParameters parameters)
+ => new SingleStoreParameterBasedSqlProcessor(_dependencies, parameters, _options);
}
}
diff --git a/src/EFCore.SingleStore/Query/Internal/SingleStoreQueryCompilationContext.cs b/src/EFCore.SingleStore/Query/Internal/SingleStoreQueryCompilationContext.cs
index 0a4658d21..c13085501 100644
--- a/src/EFCore.SingleStore/Query/Internal/SingleStoreQueryCompilationContext.cs
+++ b/src/EFCore.SingleStore/Query/Internal/SingleStoreQueryCompilationContext.cs
@@ -2,6 +2,7 @@
// Copyright (c) SingleStore Inc. All rights reserved.
// Licensed under the MIT. See LICENSE in the project root for license information.
+using System.Collections.Generic;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query;
@@ -11,13 +12,27 @@ public class SingleStoreQueryCompilationContext : RelationalQueryCompilationCont
{
public SingleStoreQueryCompilationContext(
[NotNull] QueryCompilationContextDependencies dependencies,
- [NotNull] RelationalQueryCompilationContextDependencies relationalDependencies, bool async)
+ [NotNull] RelationalQueryCompilationContextDependencies relationalDependencies,
+ bool async)
: base(dependencies, relationalDependencies, async)
{
}
+ public SingleStoreQueryCompilationContext(
+ [NotNull] QueryCompilationContextDependencies dependencies,
+ [NotNull] RelationalQueryCompilationContextDependencies relationalDependencies,
+ bool async,
+ bool precompiling,
+ IReadOnlySet nonNullableReferenceTypeParameters)
+ : base(dependencies, relationalDependencies, async, precompiling, nonNullableReferenceTypeParameters)
+ {
+ }
+
public override bool IsBuffering
=> base.IsBuffering ||
QuerySplittingBehavior == Microsoft.EntityFrameworkCore.QuerySplittingBehavior.SplitQuery;
+
+ ///
+ public override bool SupportsPrecompiledQuery => false;
}
}
diff --git a/src/EFCore.SingleStore/Query/Internal/SingleStoreQueryCompilationContextFactory.cs b/src/EFCore.SingleStore/Query/Internal/SingleStoreQueryCompilationContextFactory.cs
index 633b90532..fbfef9601 100644
--- a/src/EFCore.SingleStore/Query/Internal/SingleStoreQueryCompilationContextFactory.cs
+++ b/src/EFCore.SingleStore/Query/Internal/SingleStoreQueryCompilationContextFactory.cs
@@ -2,6 +2,7 @@
// Copyright (c) SingleStore Inc. All rights reserved.
// Licensed under the MIT. See LICENSE in the project root for license information.
+using System.Collections.Generic;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Utilities;
@@ -26,5 +27,9 @@ public SingleStoreQueryCompilationContextFactory(
public virtual QueryCompilationContext Create(bool async)
=> new SingleStoreQueryCompilationContext(_dependencies, _relationalDependencies, async);
+
+ public virtual QueryCompilationContext CreatePrecompiled(bool async, IReadOnlySet nonNullableReferenceTypeParameters)
+ => new SingleStoreQueryCompilationContext(
+ _dependencies, _relationalDependencies, async, precompiling: true, nonNullableReferenceTypeParameters);
}
}
diff --git a/src/EFCore.SingleStore/Query/Internal/SingleStoreQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.SingleStore/Query/Internal/SingleStoreQueryableMethodTranslatingExpressionVisitor.cs
index e9d829087..91458adb2 100644
--- a/src/EFCore.SingleStore/Query/Internal/SingleStoreQueryableMethodTranslatingExpressionVisitor.cs
+++ b/src/EFCore.SingleStore/Query/Internal/SingleStoreQueryableMethodTranslatingExpressionVisitor.cs
@@ -3,12 +3,13 @@
// Licensed under the MIT. See LICENSE in the project root for license information.
using System;
-using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
+using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
@@ -16,7 +17,6 @@
using Microsoft.EntityFrameworkCore.Utilities;
using EntityFrameworkCore.SingleStore.Infrastructure.Internal;
using EntityFrameworkCore.SingleStore.Query.ExpressionTranslators.Internal;
-using EntityFrameworkCore.SingleStore.Storage.Internal;
namespace EntityFrameworkCore.SingleStore.Query.Internal;
@@ -25,16 +25,18 @@ public class SingleStoreQueryableMethodTranslatingExpressionVisitor : Relational
private readonly ISingleStoreOptions _options;
private readonly SingleStoreSqlExpressionFactory _sqlExpressionFactory;
private readonly IRelationalTypeMappingSource _typeMappingSource;
+ private readonly SqlAliasManager _sqlAliasManager;
public SingleStoreQueryableMethodTranslatingExpressionVisitor(
QueryableMethodTranslatingExpressionVisitorDependencies dependencies,
RelationalQueryableMethodTranslatingExpressionVisitorDependencies relationalDependencies,
- QueryCompilationContext queryCompilationContext,
+ RelationalQueryCompilationContext relationalQueryCompilationContext,
ISingleStoreOptions options)
- : base(dependencies, relationalDependencies, queryCompilationContext)
+ : base(dependencies, relationalDependencies, relationalQueryCompilationContext)
{
_sqlExpressionFactory = (SingleStoreSqlExpressionFactory)relationalDependencies.SqlExpressionFactory;
_typeMappingSource = relationalDependencies.TypeMappingSource;
+ _sqlAliasManager = relationalQueryCompilationContext.SqlAliasManager;
_options = options;
}
@@ -58,19 +60,20 @@ protected override bool IsNaturallyOrdered(SelectExpression selectExpression)
Orderings:
[
{
- Expression: ColumnExpression { Name: "key", Table: var orderingTable } orderingColumn,
+ Expression: ColumnExpression { Name: "key", TableAlias: var orderingTable } orderingColumn,
IsAscending: true
}
]
}
- && orderingTable == mainTable
- && IsJsonEachKeyColumn(orderingColumn);
-
- bool IsJsonEachKeyColumn(ColumnExpression orderingColumn)
- => orderingColumn.Table is SingleStoreJsonTableExpression
- || (orderingColumn.Table is SelectExpression subquery
- && subquery.Projection.FirstOrDefault(p => p.Alias == "key")?.Expression is ColumnExpression projectedColumn
- && IsJsonEachKeyColumn(projectedColumn));
+ && orderingTable == mainTable.Alias
+ && IsJsonEachKeyColumn(selectExpression, orderingColumn);
+
+ bool IsJsonEachKeyColumn(SelectExpression selectExpression, ColumnExpression orderingColumn)
+ => selectExpression.Tables.FirstOrDefault(t => t.Alias == orderingColumn.TableAlias)?.UnwrapJoin() is TableExpressionBase table
+ && (table is SingleStoreJsonTableExpression
+ || (table is SelectExpression subquery
+ && subquery.Projection.FirstOrDefault(p => p.Alias == "key")?.Expression is ColumnExpression projectedColumn
+ && IsJsonEachKeyColumn(subquery, projectedColumn)));
}
protected override bool IsValidSelectExpressionForExecuteDelete(
@@ -93,11 +96,7 @@ protected override bool IsValidSelectExpressionForExecuteDelete(
var projectionBindingExpression = (ProjectionBindingExpression)shaper.ValueBufferExpression;
var entityProjectionExpression = (StructuralTypeProjectionExpression)selectExpression.GetProjection(projectionBindingExpression);
var column = entityProjectionExpression.BindProperty(shaper.StructuralType.GetProperties().First());
- table = column.Table;
- if (table is JoinExpressionBase joinExpressionBase)
- {
- table = joinExpressionBase.Table;
- }
+ table = selectExpression.GetTable(column).UnwrapJoin();
}
if (table is TableExpression te)
@@ -175,7 +174,7 @@ protected override ShapedQueryExpression TranslateAny(ShapedQueryExpression sour
typeof(int)),
_sqlExpressionFactory.Constant(0));
- return source.UpdateQueryExpression(_sqlExpressionFactory.Select(translation));
+ return source.UpdateQueryExpression(new SelectExpression(translation, _sqlAliasManager));
}
return base.TranslateAny(source, predicate);
@@ -203,7 +202,7 @@ protected override ShapedQueryExpression TranslateElementAtOrDefault(
Limit: null,
Offset: null
} selectExpression
- && orderingColumn.Table == jsonEachExpression
+ && orderingColumn.TableAlias == jsonEachExpression.Alias
&& TranslateExpression(index) is { } translatedIndex)
{
// Index on JSON array
@@ -235,7 +234,7 @@ protected override ShapedQueryExpression TranslateElementAtOrDefault(
translation, _sqlExpressionFactory, projectionColumn.TypeMapping, projectionColumn.IsNullable);
}
- return source.UpdateQueryExpression(_sqlExpressionFactory.Select(translation));
+ return source.UpdateQueryExpression(new SelectExpression(translation, _sqlAliasManager));
}
}
@@ -252,8 +251,10 @@ protected override ShapedQueryExpression TranslatePrimitiveCollection(SqlExpress
{
if (!_options.PrimitiveCollectionsSupport)
{
- AddTranslationErrorDetails("Primitive collections support has not been enabled.");
- return null;
+ throw new InvalidOperationException(
+ CoreStrings.TranslationFailedWithDetails(
+ sqlExpression.Print(),
+ "Primitive collections support has not been enabled."));
}
// if (!_options.ServerVersion.Supports.JsonTableImplementationUsesImplicitLateralJoin &&
@@ -293,21 +294,21 @@ elementTypeMapping is not null
// which case we only have the CLR type (note that we cannot produce different SQLs based on the nullability of an *element* in
// a parameter collection - our caching mechanism only supports varying by the nullability of the parameter itself (i.e. the
// collection).
- // TODO: if property is non-null, GetElementType() should never be null, but we have #31469 for shadow properties
- var isElementNullable = property?.GetElementType() is null
- ? elementClrType.IsNullableType()
- : property.GetElementType()!.IsNullable;
+ var isElementNullable = property?.GetElementType()!.IsNullable;
+
+ var keyColumnTypeMapping = _typeMappingSource.FindMapping(typeof(int))!;
#pragma warning disable EF1001 // Internal EF Core API usage.
var selectExpression = new SelectExpression(
- jsonTableExpression,
- columnName: "value",
- columnType: elementClrType,
- columnTypeMapping: elementTypeMapping,
- isElementNullable,
- identifierColumnName: "key",
- identifierColumnType: typeof(uint),
- identifierColumnTypeMapping: _typeMappingSource.FindMapping(typeof(uint)));
+ [jsonTableExpression],
+ new ColumnExpression(
+ "value",
+ tableAlias,
+ elementClrType.UnwrapNullableType(),
+ elementTypeMapping,
+ isElementNullable ?? elementClrType.IsNullableType()),
+ identifier: [(new ColumnExpression("key", tableAlias, typeof(int), keyColumnTypeMapping, nullable: false), keyColumnTypeMapping.Comparer)],
+ _sqlAliasManager);
#pragma warning restore EF1001 // Internal EF Core API usage.
// JSON_TABLE() doesn't guarantee the ordering of the elements coming out; when using JSON_TABLE() without COLUMNS, a [key] column is returned
@@ -342,12 +343,6 @@ elementTypeMapping is not null
return new ShapedQueryExpression(selectExpression, shaperExpression);
}
- protected override Expression ApplyInferredTypeMappings(
- Expression expression,
- IReadOnlyDictionary<(TableExpressionBase, string), RelationalTypeMapping> inferredTypeMappings)
- => new SingleStoreInferredTypeMappingApplier(
- RelationalDependencies.Model, _typeMappingSource, _sqlExpressionFactory, inferredTypeMappings).Visit(expression);
-
///
/// Wraps the given expression with any SQL logic necessary to convert a value coming out of a JSON document into the relational value
/// represented by the given type mapping.
@@ -364,128 +359,6 @@ private static SqlExpression ApplyJsonSqlConversion(
_ => expression
};
- protected class SingleStoreInferredTypeMappingApplier : RelationalInferredTypeMappingApplier
- {
- private readonly IRelationalTypeMappingSource _typeMappingSource;
- private readonly ISqlExpressionFactory _sqlExpressionFactory;
- private Dictionary _currentSelectInferredTypeMappings;
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public SingleStoreInferredTypeMappingApplier(
- IModel model,
- IRelationalTypeMappingSource typeMappingSource,
- ISqlExpressionFactory sqlExpressionFactory,
- IReadOnlyDictionary<(TableExpressionBase, string), RelationalTypeMapping> inferredTypeMappings)
- : base(model, sqlExpressionFactory, inferredTypeMappings)
- {
- (_typeMappingSource, _sqlExpressionFactory) = (typeMappingSource, sqlExpressionFactory);
- }
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- protected override Expression VisitExtension(Expression expression)
- {
- switch (expression)
- {
- case SingleStoreJsonTableExpression { Name: "JSON_TABLE", Schema: null, IsBuiltIn: true } jsonTableExpression
- when TryGetInferredTypeMapping(jsonTableExpression, "value", out var typeMapping):
- return ApplyTypeMappingsOnJsonTableExpression(jsonTableExpression, typeMapping);
-
- // Above, we applied the type mapping the the parameter that JSON_TABLE accepts as an argument.
- // But the inferred type mapping also needs to be applied as a SQL conversion on the column projections coming out of the
- // SelectExpression containing the JSON_TABLE call. So we set state to know about JSON_TABLE tables and their type mappings
- // in the immediate SelectExpression, and continue visiting down (see ColumnExpression visitation below).
- case SelectExpression selectExpression:
- {
- Dictionary previousSelectInferredTypeMappings = null;
-
- foreach (var table in selectExpression.Tables)
- {
- if (table is TableValuedFunctionExpression { Name: "JSON_TABLE", Schema: null, IsBuiltIn: true } jsonTableExpression
- && TryGetInferredTypeMapping(jsonTableExpression, "value", out var inferredTypeMapping))
- {
- if (previousSelectInferredTypeMappings is null)
- {
- previousSelectInferredTypeMappings = _currentSelectInferredTypeMappings;
- _currentSelectInferredTypeMappings = new Dictionary();
- }
-
- _currentSelectInferredTypeMappings![jsonTableExpression] = inferredTypeMapping;
- }
- }
-
- var visited = base.VisitExtension(expression);
-
- _currentSelectInferredTypeMappings = previousSelectInferredTypeMappings;
-
- return visited;
- }
-
- // Note that we match also ColumnExpressions which already have a type mapping, i.e. coming out of column collections (as
- // opposed to parameter collections, where the type mapping needs to be inferred). This is in order to apply SQL conversion
- // logic later in the process, see note in TranslateCollection.
- case ColumnExpression { Name: "value" } columnExpression
- when _currentSelectInferredTypeMappings?.TryGetValue(columnExpression.Table, out var inferredTypeMapping) is true:
- return ApplyJsonSqlConversion(
- columnExpression.ApplyTypeMapping(inferredTypeMapping),
- _sqlExpressionFactory,
- inferredTypeMapping,
- columnExpression.IsNullable);
-
- default:
- return base.VisitExtension(expression);
- }
- }
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- protected virtual TableValuedFunctionExpression ApplyTypeMappingsOnJsonTableExpression(
- SingleStoreJsonTableExpression jsonTableExpression,
- RelationalTypeMapping inferredTypeMapping)
- {
- // Constant queryables are translated to VALUES, no need for JSON.
- // Column queryables have their type mapping from the model, so we don't ever need to apply an inferred mapping on them.
- if (jsonTableExpression.JsonExpression is not SqlParameterExpression parameterExpression)
- {
- return jsonTableExpression;
- }
-
- if (_typeMappingSource.FindMapping(parameterExpression.Type, Model, inferredTypeMapping) is not SingleStoreStringTypeMapping
- parameterTypeMapping)
- {
- throw new InvalidOperationException("Type mapping for 'string' could not be found or was not a SingleStoreStringTypeMapping");
- }
-
- Check.DebugAssert(parameterTypeMapping.ElementTypeMapping != null, "Collection type mapping missing element mapping.");
-
- return jsonTableExpression.Update(
- parameterExpression.ApplyTypeMapping(parameterTypeMapping),
- jsonTableExpression.Path,
- new[]
- {
- new SingleStoreJsonTableExpression.ColumnInfo
- {
- Name = "value",
- TypeMapping = (RelationalTypeMapping)parameterTypeMapping.ElementTypeMapping,
- Path = new[] { new PathSegment(_sqlExpressionFactory.Constant(0, _typeMappingSource.FindMapping(typeof(int)))) },
- }
- });
- }
- }
-
private sealed class FakeMemberInfo : MemberInfo
{
public FakeMemberInfo(string name)
diff --git a/src/EFCore.SingleStore/Query/Internal/SingleStoreQueryableMethodTranslatingExpressionVisitorFactory.cs b/src/EFCore.SingleStore/Query/Internal/SingleStoreQueryableMethodTranslatingExpressionVisitorFactory.cs
index 2fdab9391..70bd562ad 100644
--- a/src/EFCore.SingleStore/Query/Internal/SingleStoreQueryableMethodTranslatingExpressionVisitorFactory.cs
+++ b/src/EFCore.SingleStore/Query/Internal/SingleStoreQueryableMethodTranslatingExpressionVisitorFactory.cs
@@ -44,5 +44,5 @@ public SingleStoreQueryableMethodTranslatingExpressionVisitorFactory(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual QueryableMethodTranslatingExpressionVisitor Create(QueryCompilationContext queryCompilationContext)
- => new SingleStoreQueryableMethodTranslatingExpressionVisitor(Dependencies, RelationalDependencies, queryCompilationContext, _options);
+ => new SingleStoreQueryableMethodTranslatingExpressionVisitor(Dependencies, RelationalDependencies, (SingleStoreQueryCompilationContext)queryCompilationContext, _options);
}
diff --git a/src/EFCore.SingleStore/Query/Internal/SingleStoreSqlNullabilityProcessor.cs b/src/EFCore.SingleStore/Query/Internal/SingleStoreSqlNullabilityProcessor.cs
index 5d436bbc3..bf18ed0ee 100644
--- a/src/EFCore.SingleStore/Query/Internal/SingleStoreSqlNullabilityProcessor.cs
+++ b/src/EFCore.SingleStore/Query/Internal/SingleStoreSqlNullabilityProcessor.cs
@@ -21,11 +21,11 @@ public class SingleStoreSqlNullabilityProcessor : SqlNullabilityProcessor
/// Creates a new instance of the class.
///
/// Parameter object containing dependencies for this class.
- /// A bool value indicating whether relational null semantics are in use.
+ /// Parameter object containing parameters for this class.
public SingleStoreSqlNullabilityProcessor(
[NotNull] RelationalParameterBasedSqlProcessorDependencies dependencies,
- bool useRelationalNulls)
- : base(dependencies, useRelationalNulls)
+ RelationalParameterBasedSqlProcessorParameters parameters)
+ : base(dependencies, parameters)
=> _sqlExpressionFactory = dependencies.SqlExpressionFactory;
///
diff --git a/src/EFCore.SingleStore/Query/Internal/SingleStoreStringMemberTranslator.cs b/src/EFCore.SingleStore/Query/Internal/SingleStoreStringMemberTranslator.cs
index 8cc9c91b4..970020313 100644
--- a/src/EFCore.SingleStore/Query/Internal/SingleStoreStringMemberTranslator.cs
+++ b/src/EFCore.SingleStore/Query/Internal/SingleStoreStringMemberTranslator.cs
@@ -27,7 +27,7 @@ public virtual SqlExpression Translate(
IDiagnosticsLogger logger)
{
if (member.Name == nameof(string.Length)
- && instance?.Type == typeof(string))
+ && member.DeclaringType == typeof(string))
{
return _sqlExpressionFactory.NullableFunction(
"CHAR_LENGTH",
diff --git a/src/EFCore.SingleStore/Query/Internal/SingleStoreStringMethodTranslator.cs b/src/EFCore.SingleStore/Query/Internal/SingleStoreStringMethodTranslator.cs
index 92bcbc606..a35ecf1b0 100644
--- a/src/EFCore.SingleStore/Query/Internal/SingleStoreStringMethodTranslator.cs
+++ b/src/EFCore.SingleStore/Query/Internal/SingleStoreStringMethodTranslator.cs
@@ -111,6 +111,17 @@ private static readonly MethodInfo _removeMethodInfoWithTwoArgs
p.ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>))))
.ToArray();
+ private static readonly MethodInfo[] _joinMethodInfos = typeof(string).GetRuntimeMethods()
+ .Where(
+ m => m is { Name: nameof(string.Join) } &&
+ m.GetParameters() is { Length: 2 } parameters &&
+ (parameters[0].ParameterType == typeof(string) ||
+ parameters[0].ParameterType == typeof(char)) &&
+ (parameters[1].ParameterType == typeof(string[]) ||
+ parameters[1].ParameterType == typeof(object[]) ||
+ parameters[1].ParameterType == typeof(IEnumerable<>)))
+ .ToArray();
+
private readonly SingleStoreSqlExpressionFactory _sqlExpressionFactory;
public SingleStoreStringMethodTranslator(
@@ -170,7 +181,7 @@ public override SqlExpression Translate(
_sqlExpressionFactory.IsNotNull(replacementArgument),
replaceCall)
},
- _sqlExpressionFactory.Constant(null, RelationalTypeMapping.NullMapping));
+ _sqlExpressionFactory.Constant(null, replaceCall.Type, replaceCall.TypeMapping));
}
if (_toLowerMethodInfo.Equals(method)
@@ -432,6 +443,54 @@ public override SqlExpression Translate(
onlyNullWhenAnyNullPropagatingArgumentIsNull: arguments[0] is not SingleStoreComplexFunctionArgumentExpression);
}
+ if (_joinMethodInfos.Contains(
+ (method.IsGenericMethod
+ ? method.GetGenericMethodDefinition()
+ : null) ?? method))
+ {
+ // Handle
+ // char, object[]
+ // char, string[]
+ // char, IEnumerable
+ // string, object[]
+ // string, string[]
+ // string, IEnumerable
+ // string, IEnumerable
+ //
+ // Some call signature variants can never reach this code, because they will be directly called and thus only their result
+ // is translated.
+ var concatWsArguments = arguments[1] is SingleStoreComplexFunctionArgumentExpression mySqlComplexFunctionArgumentExpression
+ ? [
+ arguments[0],
+ // CONCAT_WS filters out nulls, but string.Join treats them as empty strings; so coalesce (which is a no-op for
+ // non-nullable arguments).
+ mySqlComplexFunctionArgumentExpression.Update(
+ mySqlComplexFunctionArgumentExpression.ArgumentParts
+ .Select(e => _sqlExpressionFactory.Coalesce(e, _sqlExpressionFactory.Constant(string.Empty)))
+ .ToList(),
+ mySqlComplexFunctionArgumentExpression.Delimiter)]
+ : arguments.Select(
+ e => e switch
+ {
+ SqlConstantExpression c => _sqlExpressionFactory.Constant(c.Value.ToString()),
+ SqlParameterExpression p => p.ApplyTypeMapping(
+ ((SingleStoreStringTypeMapping)_typeMappingSource.GetMapping(typeof(string))).Clone(forceToString: true)),
+ _ => e,
+ })
+ .Prepend(arguments[0])
+ .ToArray();
+
+ // We haven't implemented expansion of MySqlComplexFunctionArgumentExpression yet, so the default nullability check would
+ // result in an invalid SQL generation.
+ // TODO: Fix at some point.
+ return _sqlExpressionFactory.NullableFunction(
+ "CONCAT_WS",
+ concatWsArguments,
+ method.ReturnType,
+ onlyNullWhenAnyNullPropagatingArgumentIsNull: arguments[0] is not SingleStoreComplexFunctionArgumentExpression,
+ argumentsPropagateNullability: [true, false]);
+ }
+
return null;
}
diff --git a/src/EFCore.SingleStore/Query/Internal/SkipTakeCollapsingExpressionVisitor.cs b/src/EFCore.SingleStore/Query/Internal/SkipTakeCollapsingExpressionVisitor.cs
index aa5ad31e6..1d88067d4 100644
--- a/src/EFCore.SingleStore/Query/Internal/SkipTakeCollapsingExpressionVisitor.cs
+++ b/src/EFCore.SingleStore/Query/Internal/SkipTakeCollapsingExpressionVisitor.cs
@@ -53,7 +53,6 @@ protected override Expression VisitExtension(Expression extensionExpression)
&& IsZero(selectExpression.Offset))
{
return selectExpression.Update(
- selectExpression.Projection,
selectExpression.Tables,
selectExpression.GroupBy.Count > 0
? selectExpression.Predicate
@@ -62,9 +61,10 @@ protected override Expression VisitExtension(Expression extensionExpression)
selectExpression.GroupBy.Count > 0
? _sqlExpressionFactory.ApplyDefaultTypeMapping(_sqlExpressionFactory.Constant(false))
: null,
+ selectExpression.Projection,
new List(0),
- limit: null,
- offset: null);
+ offset: null,
+ limit: null);
}
bool IsZero(SqlExpression? sqlExpression)
diff --git a/src/EFCore.SingleStore/Scaffolding/Internal/SingleStoreDatabaseModelFactory.cs b/src/EFCore.SingleStore/Scaffolding/Internal/SingleStoreDatabaseModelFactory.cs
index 3ca095199..a7ed91150 100644
--- a/src/EFCore.SingleStore/Scaffolding/Internal/SingleStoreDatabaseModelFactory.cs
+++ b/src/EFCore.SingleStore/Scaffolding/Internal/SingleStoreDatabaseModelFactory.cs
@@ -191,7 +191,7 @@ private static Func GenerateSchemaFilter(IReadOnlyList s
FROM
`INFORMATION_SCHEMA`.`TABLES` as `t`
LEFT JOIN
- `INFORMATION_SCHEMA`.`COLLATION_CHARACTER_SET_APPLICABILITY` as `ccsa` ON `ccsa`.`COLLATION_NAME` = `t`.`TABLE_COLLATION`
+ `INFORMATION_SCHEMA`.`COLLATION_CHARACTER_SET_APPLICABILITY` as `ccsa` ON `ccsa`.`{0}` = `t`.`TABLE_COLLATION`
WHERE
`TABLE_SCHEMA` = SCHEMA()
AND
@@ -205,8 +205,12 @@ protected virtual IEnumerable GetTables(
{
using (var command = connection.CreateCommand())
{
+ var collationColumnName = _options.ServerVersion.Supports.CollationCharacterSetApplicabilityWithFullCollationNameColumn
+ ? "FULL_COLLATION_NAME"
+ : "COLLATION_NAME";
+
var tables = new List();
- command.CommandText = GetTablesQuery;
+ command.CommandText = string.Format(GetTablesQuery, collationColumnName);
using (var reader = command.ExecuteReader())
{
while (reader.Read())
diff --git a/src/EFCore.SingleStore/Storage/Internal/Json/SingleStoreJsonByteArrayAsHexStringReaderWriter.cs b/src/EFCore.SingleStore/Storage/Internal/Json/SingleStoreJsonByteArrayAsHexStringReaderWriter.cs
index f8bc50c02..9153ae2c5 100644
--- a/src/EFCore.SingleStore/Storage/Internal/Json/SingleStoreJsonByteArrayAsHexStringReaderWriter.cs
+++ b/src/EFCore.SingleStore/Storage/Internal/Json/SingleStoreJsonByteArrayAsHexStringReaderWriter.cs
@@ -3,6 +3,8 @@
// Licensed under the MIT. See LICENSE in the project root for license information.
using System;
+using System.Linq.Expressions;
+using System.Reflection;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Storage.Json;
@@ -10,6 +12,9 @@ namespace EntityFrameworkCore.SingleStore.Storage.Internal.Json;
public sealed class SingleStoreJsonByteArrayAsHexStringReaderWriter : JsonValueReaderWriter
{
+ public static readonly PropertyInfo InstanceProperty =
+ typeof(SingleStoreJsonByteArrayAsHexStringReaderWriter).GetProperty(nameof(Instance));
+
public static SingleStoreJsonByteArrayAsHexStringReaderWriter Instance { get; } = new();
private SingleStoreJsonByteArrayAsHexStringReaderWriter()
@@ -21,4 +26,7 @@ public override byte[] FromJsonTyped(ref Utf8JsonReaderManager manager, object e
public override void ToJsonTyped(Utf8JsonWriter writer, byte[] value)
=> writer.WriteStringValue(Convert.ToHexString(value));
+
+ public override Expression ConstructorExpression
+ => Expression.Property(null, InstanceProperty);
}
diff --git a/src/EFCore.SingleStore/Storage/Internal/SingleStoreRelationalConnection.cs b/src/EFCore.SingleStore/Storage/Internal/SingleStoreRelationalConnection.cs
index c6617393b..9ea2fc3c8 100644
--- a/src/EFCore.SingleStore/Storage/Internal/SingleStoreRelationalConnection.cs
+++ b/src/EFCore.SingleStore/Storage/Internal/SingleStoreRelationalConnection.cs
@@ -168,10 +168,10 @@ public virtual ISingleStoreRelationalConnection CreateMasterConnection()
// Apply modified connection string.
var masterMySqlOptions = _dataSource is not null
- ? mySqlOptions.WithConnection(((SingleStoreConnection)CreateDbConnection()).CloneWith(masterConnectionString))
+ ? mySqlOptions.WithConnection(((SingleStoreConnection)CreateDbConnection()).CloneWith(masterConnectionString), owned: true)
: mySqlOptions.Connection is null
? mySqlOptions.WithConnectionString(masterConnectionString)
- : mySqlOptions.WithConnection(DbConnection.CloneWith(masterConnectionString));
+ : mySqlOptions.WithConnection(DbConnection.CloneWith(masterConnectionString), owned: true);
var optionsBuilder = new DbContextOptionsBuilder();
var optionsBuilderInfrastructure = (IDbContextOptionsBuilderInfrastructure)optionsBuilder;
diff --git a/test/Directory.Build.props b/test/Directory.Build.props
index 09501c267..3dcc35d0a 100644
--- a/test/Directory.Build.props
+++ b/test/Directory.Build.props
@@ -14,5 +14,6 @@
+
diff --git a/test/EFCore.SingleStore.FunctionalTests/BuiltInDataTypesSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/BuiltInDataTypesSingleStoreTest.cs
index 97c9d2f83..30804b368 100644
--- a/test/EFCore.SingleStore.FunctionalTests/BuiltInDataTypesSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/BuiltInDataTypesSingleStoreTest.cs
@@ -5,6 +5,7 @@
using System.Linq;
using System.Linq.Expressions;
using System.Text;
+using System.Threading.Tasks;
using EntityFrameworkCore.SingleStore.FunctionalTests.TestUtilities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
@@ -33,12 +34,12 @@ public BuiltInDataTypesSingleStoreTest(BuiltInDataTypesSingleStoreFixture fixtur
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
- public override void Object_to_string_conversion()
+ public override async Task Object_to_string_conversion()
{
using var context = CreateContext();
- var expected = context.Set()
+ var expected = (await context.Set()
.Where(e => e.Id == 13)
- .AsEnumerable()
+ .ToListAsync())
.Select(
b => new
{
@@ -57,7 +58,7 @@ public override void Object_to_string_conversion()
Fixture.ListLoggerFactory.Clear();
- var query = context.Set()
+ var query = await context.Set()
.Where(e => e.Id == 13)
.Select(
b => new
@@ -78,7 +79,7 @@ public override void Object_to_string_conversion()
DateTimeOffset = b.TestDateTimeOffset.ToString(),
TimeSpan = b.TestTimeSpan.ToString()
})
- .ToList();
+ .ToListAsync();
var actual = Assert.Single(query);
Assert.Equal(expected.Sbyte, actual.Sbyte);
@@ -803,7 +804,7 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_set_to_null()
// Overridden because of TestNullableDateTimeOffset, since MySQL does not offer a native data type to save a date/time with
// timezone.
- public override void Can_insert_and_read_back_all_nullable_data_types_with_values_set_to_non_null()
+ public override async Task Can_insert_and_read_back_all_nullable_data_types_with_values_set_to_non_null()
{
using (var context = CreateContext())
{
@@ -840,12 +841,12 @@ public override void Can_insert_and_read_back_all_nullable_data_types_with_value
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(ndt => ndt.Id == 101).ToList().Single();
+ var dt = (await context.Set().Where(ndt => ndt.Id == 101).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(BuiltInNullableDataTypes));
AssertEqualIfMapped(entityType, "TestString", () => dt.TestString);
@@ -1438,7 +1439,7 @@ public void Can_get_column_types_from_built_model()
#region https://github.com/dotnet/efcore/issues/26068
[ConditionalFact]
- public override void Can_insert_and_read_back_all_non_nullable_data_types()
+ public override async Task Can_insert_and_read_back_all_non_nullable_data_types()
{
using (var context = CreateContext())
{
@@ -1473,12 +1474,12 @@ public override void Can_insert_and_read_back_all_non_nullable_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(e => e.Id == 1).ToList().Single();
+ var dt = (await context.Set().Where(e => e.Id == 1).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(BuiltInDataTypes));
AssertEqualIfMapped(entityType, (short)-1234, () => dt.TestInt16);
@@ -1512,37 +1513,37 @@ public override void Can_insert_and_read_back_all_non_nullable_data_types()
}
[ConditionalFact(Skip = "Feature 'FOREIGN KEY' is not supported by SingleStore Distributed.")]
- public override void Can_insert_and_read_back_with_binary_key()
+ public override async Task Can_insert_and_read_back_with_binary_key()
{
- base.Can_insert_and_read_back_with_binary_key();
+ await base.Can_insert_and_read_back_with_binary_key();
}
[ConditionalFact(Skip = "Feature 'FOREIGN KEY' is not supported by SingleStore Distributed.")]
- public override void Can_insert_and_read_back_with_null_binary_foreign_key()
+ public override async Task Can_insert_and_read_back_with_null_binary_foreign_key()
{
- base.Can_insert_and_read_back_with_null_binary_foreign_key();
+ await base.Can_insert_and_read_back_with_null_binary_foreign_key();
}
[ConditionalFact(Skip = "Feature 'FOREIGN KEY' is not supported by SingleStore Distributed.")]
- public override void Can_insert_and_read_back_with_null_string_foreign_key()
+ public override async Task Can_insert_and_read_back_with_null_string_foreign_key()
{
- base.Can_insert_and_read_back_with_null_string_foreign_key();
+ await base.Can_insert_and_read_back_with_null_string_foreign_key();
}
[ConditionalFact(Skip = "Feature 'FOREIGN KEY' is not supported by SingleStore Distributed.")]
- public override void Can_insert_and_read_back_with_string_key()
+ public override async Task Can_insert_and_read_back_with_string_key()
{
- base.Can_insert_and_read_back_with_string_key();
+ await base.Can_insert_and_read_back_with_string_key();
}
[ConditionalFact(Skip = "Feature 'FOREIGN KEY' is not supported by SingleStore Distributed.")]
- public override void Can_read_back_mapped_enum_from_collection_first_or_default()
+ public override async Task Can_read_back_mapped_enum_from_collection_first_or_default()
{
- base.Can_read_back_mapped_enum_from_collection_first_or_default();
+ await base.Can_read_back_mapped_enum_from_collection_first_or_default();
}
[ConditionalFact]
- public override void Can_insert_and_read_back_non_nullable_backed_data_types()
+ public override async Task Can_insert_and_read_back_non_nullable_backed_data_types()
{
using (var context = CreateContext())
{
@@ -1577,12 +1578,12 @@ public override void Can_insert_and_read_back_non_nullable_backed_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(ndt => ndt.Id == 101).ToList().Single();
+ var dt = (await context.Set().Where(ndt => ndt.Id == 101).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(NonNullableBackedDataTypes));
AssertEqualIfMapped(entityType, (short)-1234, () => dt.Int16);
@@ -1616,7 +1617,7 @@ public override void Can_insert_and_read_back_non_nullable_backed_data_types()
}
[ConditionalFact]
- public override void Can_insert_and_read_back_nullable_backed_data_types()
+ public override async Task Can_insert_and_read_back_nullable_backed_data_types()
{
using (var context = CreateContext())
{
@@ -1651,12 +1652,12 @@ public override void Can_insert_and_read_back_nullable_backed_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(ndt => ndt.Id == 101).ToList().Single();
+ var dt = (await context.Set().Where(ndt => ndt.Id == 101).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(NullableBackedDataTypes));
AssertEqualIfMapped(entityType, (short)-1234, () => dt.Int16);
@@ -1689,7 +1690,7 @@ public override void Can_insert_and_read_back_nullable_backed_data_types()
}
[ConditionalFact]
- public override void Can_insert_and_read_back_object_backed_data_types()
+ public override async Task Can_insert_and_read_back_object_backed_data_types()
{
using (var context = CreateContext())
{
@@ -1726,12 +1727,12 @@ public override void Can_insert_and_read_back_object_backed_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(ndt => ndt.Id == 101).ToList().Single();
+ var dt = (await context.Set().Where(ndt => ndt.Id == 101).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(ObjectBackedDataTypes));
AssertEqualIfMapped(entityType, "TestString", () => dt.String);
diff --git a/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesSingleStoreTest.cs
index 6f2e0d782..21aca49d4 100644
--- a/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesSingleStoreTest.cs
@@ -7,7 +7,7 @@
namespace EntityFrameworkCore.SingleStore.FunctionalTests.BulkUpdates;
-public class ComplexTypeBulkUpdatesSingleStoreTest : ComplexTypeBulkUpdatesTestBase<
+public class ComplexTypeBulkUpdatesSingleStoreTest : ComplexTypeBulkUpdatesRelationalTestBase<
ComplexTypeBulkUpdatesSingleStoreTest.ComplexTypeBulkUpdatesSingleStoreFixture>
{
public ComplexTypeBulkUpdatesSingleStoreTest(ComplexTypeBulkUpdatesSingleStoreFixture fixture, ITestOutputHelper testOutputHelper)
@@ -27,9 +27,9 @@ public override async Task Delete_entity_type_with_complex_type(bool async)
""");
}
- public override async Task Delete_complex_type_throws(bool async)
+ public override async Task Delete_complex_type(bool async)
{
- await base.Delete_complex_type_throws(async);
+ await base.Delete_complex_type(async);
AssertSql();
}
@@ -95,17 +95,216 @@ public override async Task Update_multiple_projected_complex_types_via_anonymous
""");
}
- public override async Task Update_projected_complex_type_via_OrderBy_Skip_throws(bool async)
+ public override async Task Update_projected_complex_type_via_OrderBy_Skip(bool async)
{
- await base.Update_projected_complex_type_via_OrderBy_Skip_throws(async);
+ await base.Update_projected_complex_type_via_OrderBy_Skip(async);
AssertExecuteUpdateSql();
}
+ public override async Task Update_complex_type_to_parameter(bool async)
+ {
+ await base.Update_complex_type_to_parameter(async);
+
+ AssertSql(
+"""
+SELECT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`
+FROM `Customer` AS `c`
+""",
+ //
+ """
+@__complex_type_newAddress_0_AddressLine1='New AddressLine1' (Size = 4000)
+@__complex_type_newAddress_0_AddressLine2='New AddressLine2' (Size = 4000)
+@__complex_type_newAddress_0_Tags='["new_tag1","new_tag2"]' (Size = 4000)
+@__complex_type_newAddress_0_ZipCode='99999' (Nullable = true)
+@__complex_type_newAddress_0_Code='FR' (Size = 4000)
+@__complex_type_newAddress_0_FullName='France' (Size = 4000)
+
+UPDATE `Customer` AS `c`
+SET `c`.`ShippingAddress_AddressLine1` = @__complex_type_newAddress_0_AddressLine1,
+ `c`.`ShippingAddress_AddressLine2` = @__complex_type_newAddress_0_AddressLine2,
+ `c`.`ShippingAddress_Tags` = @__complex_type_newAddress_0_Tags,
+ `c`.`ShippingAddress_ZipCode` = @__complex_type_newAddress_0_ZipCode,
+ `c`.`ShippingAddress_Country_Code` = @__complex_type_newAddress_0_Code,
+ `c`.`ShippingAddress_Country_FullName` = @__complex_type_newAddress_0_FullName
+""",
+ //
+ """
+SELECT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`
+FROM `Customer` AS `c`
+""");
+ }
+
+ public override async Task Update_nested_complex_type_to_parameter(bool async)
+ {
+ await base.Update_nested_complex_type_to_parameter(async);
+
+ AssertSql(
+"""
+SELECT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`
+FROM `Customer` AS `c`
+""",
+ //
+ """
+@__complex_type_newCountry_0_Code='FR' (Size = 4000)
+@__complex_type_newCountry_0_FullName='France' (Size = 4000)
+
+UPDATE `Customer` AS `c`
+SET `c`.`ShippingAddress_Country_Code` = @__complex_type_newCountry_0_Code,
+ `c`.`ShippingAddress_Country_FullName` = @__complex_type_newCountry_0_FullName
+""",
+ //
+ """
+SELECT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`
+FROM `Customer` AS `c`
+""");
+ }
+
+ public override async Task Update_complex_type_to_another_database_complex_type(bool async)
+ {
+ await base.Update_complex_type_to_another_database_complex_type(async);
+
+ AssertSql(
+"""
+SELECT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`
+FROM `Customer` AS `c`
+""",
+ //
+ """
+UPDATE `Customer` AS `c`
+SET `c`.`ShippingAddress_AddressLine1` = `c`.`BillingAddress_AddressLine1`,
+ `c`.`ShippingAddress_AddressLine2` = `c`.`BillingAddress_AddressLine2`,
+ `c`.`ShippingAddress_Tags` = `c`.`BillingAddress_Tags`,
+ `c`.`ShippingAddress_ZipCode` = `c`.`BillingAddress_ZipCode`,
+ `c`.`ShippingAddress_Country_Code` = `c`.`ShippingAddress_Country_Code`,
+ `c`.`ShippingAddress_Country_FullName` = `c`.`ShippingAddress_Country_FullName`
+""",
+ //
+ """
+SELECT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`
+FROM `Customer` AS `c`
+""");
+ }
+
+ public override async Task Update_complex_type_to_inline_without_lambda(bool async)
+ {
+ await base.Update_complex_type_to_inline_without_lambda(async);
+
+ AssertSql(
+"""
+SELECT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`
+FROM `Customer` AS `c`
+""",
+ //
+ """
+UPDATE `Customer` AS `c`
+SET `c`.`ShippingAddress_AddressLine1` = 'New AddressLine1',
+ `c`.`ShippingAddress_AddressLine2` = 'New AddressLine2',
+ `c`.`ShippingAddress_Tags` = '["new_tag1","new_tag2"]',
+ `c`.`ShippingAddress_ZipCode` = 99999,
+ `c`.`ShippingAddress_Country_Code` = 'FR',
+ `c`.`ShippingAddress_Country_FullName` = 'France'
+""",
+ //
+ """
+SELECT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`
+FROM `Customer` AS `c`
+""");
+ }
+
+ public override async Task Update_complex_type_to_inline_with_lambda(bool async)
+ {
+ await base.Update_complex_type_to_inline_with_lambda(async);
+
+ AssertSql(
+"""
+SELECT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`
+FROM `Customer` AS `c`
+""",
+ //
+ """
+UPDATE `Customer` AS `c`
+SET `c`.`ShippingAddress_AddressLine1` = 'New AddressLine1',
+ `c`.`ShippingAddress_AddressLine2` = 'New AddressLine2',
+ `c`.`ShippingAddress_Tags` = '["new_tag1","new_tag2"]',
+ `c`.`ShippingAddress_ZipCode` = 99999,
+ `c`.`ShippingAddress_Country_Code` = 'FR',
+ `c`.`ShippingAddress_Country_FullName` = 'France'
+""",
+ //
+ """
+SELECT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`
+FROM `Customer` AS `c`
+""");
+ }
+
+ public override async Task Update_complex_type_to_another_database_complex_type_with_subquery(bool async)
+ {
+ await base.Update_complex_type_to_another_database_complex_type_with_subquery(async);
+
+ AssertSql(
+"""
+@__p_0='1'
+
+SELECT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`
+FROM `Customer` AS `c`
+ORDER BY `c`.`Id`
+LIMIT 18446744073709551610 OFFSET @__p_0
+""",
+ //
+ """
+@__p_0='1'
+
+UPDATE `Customer` AS `c0`
+INNER JOIN (
+ SELECT `c`.`Id`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`
+ FROM `Customer` AS `c`
+ ORDER BY `c`.`Id`
+ LIMIT 18446744073709551610 OFFSET @__p_0
+) AS `c1` ON `c0`.`Id` = `c1`.`Id`
+SET `c0`.`ShippingAddress_AddressLine1` = `c1`.`BillingAddress_AddressLine1`,
+ `c0`.`ShippingAddress_AddressLine2` = `c1`.`BillingAddress_AddressLine2`,
+ `c0`.`ShippingAddress_Tags` = `c1`.`BillingAddress_Tags`,
+ `c0`.`ShippingAddress_ZipCode` = `c1`.`BillingAddress_ZipCode`,
+ `c0`.`ShippingAddress_Country_Code` = `c1`.`ShippingAddress_Country_Code`,
+ `c0`.`ShippingAddress_Country_FullName` = `c1`.`ShippingAddress_Country_FullName`
+""",
+ //
+ """
+@__p_0='1'
+
+SELECT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`
+FROM `Customer` AS `c`
+ORDER BY `c`.`Id`
+LIMIT 18446744073709551610 OFFSET @__p_0
+""");
+ }
+
+ public override async Task Update_collection_inside_complex_type(bool async)
+ {
+ await base.Update_collection_inside_complex_type(async);
+
+ AssertSql(
+"""
+SELECT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`
+FROM `Customer` AS `c`
+""",
+ //
+ """
+UPDATE `Customer` AS `c`
+SET `c`.`ShippingAddress_Tags` = '["new_tag1","new_tag2"]'
+""",
+ //
+ """
+SELECT `c`.`Id`, `c`.`Name`, `c`.`BillingAddress_AddressLine1`, `c`.`BillingAddress_AddressLine2`, `c`.`BillingAddress_Tags`, `c`.`BillingAddress_ZipCode`, `c`.`BillingAddress_Country_Code`, `c`.`BillingAddress_Country_FullName`, `c`.`ShippingAddress_AddressLine1`, `c`.`ShippingAddress_AddressLine2`, `c`.`ShippingAddress_Tags`, `c`.`ShippingAddress_ZipCode`, `c`.`ShippingAddress_Country_Code`, `c`.`ShippingAddress_Country_FullName`
+FROM `Customer` AS `c`
+""");
+ }
+
[ConditionalFact]
public virtual void Check_all_tests_overridden()
{
- TestHelpers.AssertAllMethodsOverridden(GetType());
+ SingleStoreTestHelpers.AssertAllMethodsOverridden(GetType());
}
private void AssertExecuteUpdateSql(params string[] expected)
@@ -123,7 +322,7 @@ protected void ClearLog()
Fixture.TestSqlLoggerFactory.Clear();
}
- public class ComplexTypeBulkUpdatesSingleStoreFixture : ComplexTypeBulkUpdatesFixtureBase
+ public class ComplexTypeBulkUpdatesSingleStoreFixture : ComplexTypeBulkUpdatesRelationalFixtureBase
{
protected override ITestStoreFactory TestStoreFactory
=> SingleStoreTestStoreFactory.Instance;
diff --git a/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSingleStoreTest.cs
index 214d7a222..70a933f65 100644
--- a/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSingleStoreTest.cs
@@ -4,15 +4,20 @@
using Microsoft.EntityFrameworkCore.TestUtilities;
using EntityFrameworkCore.SingleStore.FunctionalTests.TestUtilities;
using Microsoft.EntityFrameworkCore;
+using SingleStoreConnector;
using Xunit;
namespace EntityFrameworkCore.SingleStore.FunctionalTests.BulkUpdates;
-public class NonSharedModelBulkUpdatesSingleStoreTest : NonSharedModelBulkUpdatesTestBase
+public class NonSharedModelBulkUpdatesSingleStoreTest : NonSharedModelBulkUpdatesRelationalTestBase
{
protected override ITestStoreFactory TestStoreFactory
=> SingleStoreTestStoreFactory.Instance;
+ [ConditionalFact]
+ public virtual void Check_all_tests_overridden()
+ => SingleStoreTestHelpers.AssertAllMethodsOverridden(GetType());
+
public override async Task Delete_aggregate_root_when_eager_loaded_owned_collection(bool async)
{
await base.Delete_aggregate_root_when_eager_loaded_owned_collection(async);
@@ -97,7 +102,7 @@ public override async Task Update_owned_and_non_owned_properties_with_table_shar
"""
UPDATE `Owner` AS `o`
SET `o`.`OwnedReference_Number` = CHAR_LENGTH(`o`.`Title`),
- `o`.`Title` = CAST(`o`.`OwnedReference_Number` AS char)
+ `o`.`Title` = COALESCE(CAST(`o`.`OwnedReference_Number` AS char), '')
""");
}
@@ -123,10 +128,10 @@ public override async Task Update_main_table_in_entity_with_entity_splitting(boo
mb.Entity(b =>
b.Property(p => p.Id).HasColumnType("bigint"));
},
- seed: context =>
+ seed: async context =>
{
context.Set().Add(new Blog { Title = "SomeBlog" });
- context.SaveChanges();
+ await context.SaveChangesAsync();
});
await AssertUpdate(
@@ -143,6 +148,7 @@ await AssertUpdate(
""");
}
+ [ConditionalTheory(Skip = "Operation 'Update/Delete right table of a join' is not allowed.")]
public override async Task Update_non_main_table_in_entity_with_entity_splitting(bool async)
{
var contextFactory = await InitializeAsync(
@@ -165,10 +171,10 @@ public override async Task Update_non_main_table_in_entity_with_entity_splitting
mb.Entity(b =>
b.Property(p => p.Id).HasColumnType("bigint"));
},
- seed: context =>
+ seed: async context =>
{
context.Set().Add(new Blog { Title = "SomeBlog" });
- context.SaveChanges();
+ await context.SaveChangesAsync();
});
await AssertUpdate(
@@ -182,7 +188,8 @@ await AssertUpdate(
AssertSql(
"""
-UPDATE `BlogsPart1` AS `b0`
+UPDATE `Blogs` AS `b`
+INNER JOIN `BlogsPart1` AS `b0` ON `b`.`Id` = `b0`.`Id`
SET `b0`.`Rating` = CHAR_LENGTH(`b0`.`Title`),
`b0`.`Title` = CAST(`b0`.`Rating` AS char)
""");
@@ -194,6 +201,50 @@ public override async Task Update_with_alias_uniquification_in_setter_subquery(b
await base.Update_with_alias_uniquification_in_setter_subquery(async);
}
+ public override async Task Update_non_owned_property_on_entity_with_owned_in_join(bool async)
+ {
+ await base.Update_non_owned_property_on_entity_with_owned_in_join(async);
+
+ AssertSql(
+ """
+ UPDATE `Owner` AS `o`
+ INNER JOIN `Owner` AS `o0` ON `o`.`Id` = `o0`.`Id`
+ SET `o`.`Title` = 'NewValue'
+ """);
+ }
+
+ [ConditionalTheory(Skip = "Operation 'Update/Delete right table of a join' is not allowed.")]
+ public override async Task Replace_ColumnExpression_in_column_setter(bool async)
+ {
+ await base.Replace_ColumnExpression_in_column_setter(async);
+
+ AssertSql(
+ """
+ UPDATE `Owner` AS `o`
+ INNER JOIN `OwnedCollection` AS `o0` ON `o`.`Id` = `o0`.`OwnerId`
+ SET `o0`.`Value` = 'SomeValue'
+ """);
+ }
+
+ public override async Task Delete_with_owned_collection_and_non_natively_translatable_query(bool async)
+ {
+ await base.Delete_with_owned_collection_and_non_natively_translatable_query(async);
+
+ AssertSql(
+ """
+ @__p_0='1'
+
+ DELETE `o`
+ FROM `Owner` AS `o`
+ WHERE `o`.`Id` IN (
+ SELECT `o0`.`Id`
+ FROM `Owner` AS `o0`
+ ORDER BY `o0`.`Title`
+ LIMIT 18446744073709551610 OFFSET @__p_0
+ )
+ """);
+ }
+
private void AssertSql(params string[] expected)
=> TestSqlLoggerFactory.AssertBaseline(expected);
diff --git a/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSingleStoreFixture.cs b/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSingleStoreFixture.cs
index db19e172a..69cfbe7cc 100644
--- a/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSingleStoreFixture.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSingleStoreFixture.cs
@@ -7,8 +7,8 @@
namespace EntityFrameworkCore.SingleStore.FunctionalTests.BulkUpdates;
-public class NorthwindBulkUpdatesSingleStoreFixture : NorthwindBulkUpdatesFixture
- where TModelCustomizer : IModelCustomizer, new()
+public class NorthwindBulkUpdatesSingleStoreFixture : NorthwindBulkUpdatesRelationalFixture
+ where TModelCustomizer : ITestModelCustomizer, new()
{
protected override ITestStoreFactory TestStoreFactory
=> SingleStoreNorthwindTestStoreFactory.Instance;
diff --git a/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSingleStoreTest.cs
index f627a14b5..d7244f324 100644
--- a/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSingleStoreTest.cs
@@ -4,14 +4,16 @@
using Microsoft.EntityFrameworkCore.TestModels.Northwind;
using Microsoft.EntityFrameworkCore.TestUtilities;
using SingleStoreConnector;
+using EntityFrameworkCore.SingleStore.FunctionalTests.TestUtilities;
using EntityFrameworkCore.SingleStore.Infrastructure;
using EntityFrameworkCore.SingleStore.Tests;
+using EntityFrameworkCore.SingleStore.Tests.TestUtilities.Attributes;
using Xunit;
using Xunit.Abstractions;
namespace EntityFrameworkCore.SingleStore.FunctionalTests.BulkUpdates;
-public class NorthwindBulkUpdatesSingleStoreTest : NorthwindBulkUpdatesTestBase>
+public class NorthwindBulkUpdatesSingleStoreTest : NorthwindBulkUpdatesRelationalTestBase>
{
public NorthwindBulkUpdatesSingleStoreTest(
NorthwindBulkUpdatesSingleStoreFixture fixture,
@@ -19,12 +21,12 @@ public NorthwindBulkUpdatesSingleStoreTest(
: base(fixture, testOutputHelper)
{
ClearLog();
- // Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
+ Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
[ConditionalFact]
public virtual void Check_all_tests_overridden()
- => TestHelpers.AssertAllMethodsOverridden(GetType());
+ => SingleStoreTestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task Delete_Where_TagWith(bool async)
{
@@ -99,13 +101,13 @@ public override async Task Delete_Where_OrderBy_Skip(bool async)
WHERE EXISTS (
SELECT 1
FROM (
- SELECT `o0`.`OrderID`, `o0`.`ProductID`, `o0`.`Discount`, `o0`.`Quantity`, `o0`.`UnitPrice`
+ SELECT `o0`.`OrderID`, `o0`.`ProductID`
FROM `Order Details` AS `o0`
WHERE `o0`.`OrderID` < 10300
ORDER BY `o0`.`OrderID`
LIMIT 18446744073709551610 OFFSET @__p_0
- ) AS `t`
- WHERE (`t`.`OrderID` = `o`.`OrderID`) AND (`t`.`ProductID` = `o`.`ProductID`))
+ ) AS `o1`
+ WHERE (`o1`.`OrderID` = `o`.`OrderID`) AND (`o1`.`ProductID` = `o`.`ProductID`))
""");
}
@@ -139,13 +141,13 @@ public override async Task Delete_Where_OrderBy_Skip_Take(bool async)
WHERE EXISTS (
SELECT 1
FROM (
- SELECT `o0`.`OrderID`, `o0`.`ProductID`, `o0`.`Discount`, `o0`.`Quantity`, `o0`.`UnitPrice`
+ SELECT `o0`.`OrderID`, `o0`.`ProductID`
FROM `Order Details` AS `o0`
WHERE `o0`.`OrderID` < 10300
ORDER BY `o0`.`OrderID`
LIMIT @__p_0 OFFSET @__p_0
- ) AS `t`
- WHERE (`t`.`OrderID` = `o`.`OrderID`) AND (`t`.`ProductID` = `o`.`ProductID`))
+ ) AS `o1`
+ WHERE (`o1`.`OrderID` = `o`.`OrderID`) AND (`o1`.`ProductID` = `o`.`ProductID`))
""");
}
@@ -162,12 +164,12 @@ public override async Task Delete_Where_Skip(bool async)
WHERE EXISTS (
SELECT 1
FROM (
- SELECT `o0`.`OrderID`, `o0`.`ProductID`, `o0`.`Discount`, `o0`.`Quantity`, `o0`.`UnitPrice`
+ SELECT `o0`.`OrderID`, `o0`.`ProductID`
FROM `Order Details` AS `o0`
WHERE `o0`.`OrderID` < 10300
LIMIT 18446744073709551610 OFFSET @__p_0
- ) AS `t`
- WHERE (`t`.`OrderID` = `o`.`OrderID`) AND (`t`.`ProductID` = `o`.`ProductID`))
+ ) AS `o1`
+ WHERE (`o1`.`OrderID` = `o`.`OrderID`) AND (`o1`.`ProductID` = `o`.`ProductID`))
""");
}
@@ -199,12 +201,12 @@ public override async Task Delete_Where_Skip_Take(bool async)
WHERE EXISTS (
SELECT 1
FROM (
- SELECT `o0`.`OrderID`, `o0`.`ProductID`, `o0`.`Discount`, `o0`.`Quantity`, `o0`.`UnitPrice`
+ SELECT `o0`.`OrderID`, `o0`.`ProductID`
FROM `Order Details` AS `o0`
WHERE `o0`.`OrderID` < 10300
LIMIT @__p_0 OFFSET @__p_0
- ) AS `t`
- WHERE (`t`.`OrderID` = `o`.`OrderID`) AND (`t`.`ProductID` = `o`.`ProductID`))
+ ) AS `o1`
+ WHERE (`o1`.`OrderID` = `o`.`OrderID`) AND (`o1`.`ProductID` = `o`.`ProductID`))
""");
}
@@ -281,16 +283,16 @@ public override async Task Delete_Where_Skip_Take_Skip_Take_causing_subquery(boo
WHERE EXISTS (
SELECT 1
FROM (
- SELECT `t`.`OrderID`, `t`.`ProductID`, `t`.`Discount`, `t`.`Quantity`, `t`.`UnitPrice`
+ SELECT `o0`.`OrderID`, `o0`.`ProductID`
FROM (
- SELECT `o0`.`OrderID`, `o0`.`ProductID`, `o0`.`Discount`, `o0`.`Quantity`, `o0`.`UnitPrice`
- FROM `Order Details` AS `o0`
- WHERE `o0`.`OrderID` < 10300
+ SELECT `o1`.`OrderID`, `o1`.`ProductID`
+ FROM `Order Details` AS `o1`
+ WHERE `o1`.`OrderID` < 10300
LIMIT @__p_0 OFFSET @__p_0
- ) AS `t`
+ ) AS `o0`
LIMIT @__p_2 OFFSET @__p_1
- ) AS `t0`
- WHERE (`t0`.`OrderID` = `o`.`OrderID`) AND (`t0`.`ProductID` = `o`.`ProductID`))
+ ) AS `o2`
+ WHERE (`o2`.`OrderID` = `o`.`OrderID`) AND (`o2`.`ProductID` = `o`.`ProductID`))
""");
}
@@ -332,11 +334,11 @@ WHERE EXISTS (
SELECT 1
FROM `Orders` AS `o0`
INNER JOIN (
- SELECT `o1`.`OrderID`, `o1`.`ProductID`, `o1`.`Discount`, `o1`.`Quantity`, `o1`.`UnitPrice`
- FROM `Order Details` AS `o1`
- WHERE `o1`.`ProductID` > 0
- ) AS `t` ON `o0`.`OrderID` = `t`.`OrderID`
- WHERE (`o0`.`OrderID` < 10250) AND ((`t`.`OrderID` = `o`.`OrderID`) AND (`t`.`ProductID` = `o`.`ProductID`)))
+ SELECT `o2`.`OrderID`, `o2`.`ProductID`
+ FROM `Order Details` AS `o2`
+ WHERE `o2`.`ProductID` > 0
+ ) AS `o1` ON `o0`.`OrderID` = `o1`.`OrderID`
+ WHERE (`o0`.`OrderID` < 10250) AND ((`o1`.`OrderID` = `o`.`OrderID`) AND (`o1`.`ProductID` = `o`.`ProductID`)))
""");
}
@@ -385,8 +387,8 @@ SELECT 1
SELECT `o1`.`OrderID`, `o1`.`ProductID`, `o1`.`Discount`, `o1`.`Quantity`, `o1`.`UnitPrice`
FROM `Order Details` AS `o1`
WHERE `o1`.`OrderID` > 11250
- ) AS `t`
- WHERE (`t`.`OrderID` = `o`.`OrderID`) AND (`t`.`ProductID` = `o`.`ProductID`))
+ ) AS `u`
+ WHERE (`u`.`OrderID` = `o`.`OrderID`) AND (`u`.`ProductID` = `o`.`ProductID`))
""");
}
@@ -401,15 +403,15 @@ public override async Task Delete_Concat(bool async)
WHERE EXISTS (
SELECT 1
FROM (
- SELECT `o0`.`OrderID`, `o0`.`ProductID`, `o0`.`Discount`, `o0`.`Quantity`, `o0`.`UnitPrice`
+ SELECT `o0`.`OrderID`, `o0`.`ProductID`
FROM `Order Details` AS `o0`
WHERE `o0`.`OrderID` < 10250
UNION ALL
- SELECT `o1`.`OrderID`, `o1`.`ProductID`, `o1`.`Discount`, `o1`.`Quantity`, `o1`.`UnitPrice`
+ SELECT `o1`.`OrderID`, `o1`.`ProductID`
FROM `Order Details` AS `o1`
WHERE `o1`.`OrderID` > 11250
- ) AS `t`
- WHERE (`t`.`OrderID` = `o`.`OrderID`) AND (`t`.`ProductID` = `o`.`ProductID`))
+ ) AS `u`
+ WHERE (`u`.`OrderID` = `o`.`OrderID`) AND (`u`.`ProductID` = `o`.`ProductID`))
""");
}
@@ -431,8 +433,8 @@ SELECT 1
SELECT `o1`.`OrderID`, `o1`.`ProductID`, `o1`.`Discount`, `o1`.`Quantity`, `o1`.`UnitPrice`
FROM `Order Details` AS `o1`
WHERE `o1`.`OrderID` > 11250
- ) AS `t`
- WHERE (`t`.`OrderID` = `o`.`OrderID`) AND (`t`.`ProductID` = `o`.`ProductID`))
+ ) AS `i`
+ WHERE (`i`.`OrderID` = `o`.`OrderID`) AND (`i`.`ProductID` = `o`.`ProductID`))
""");
}
@@ -454,8 +456,8 @@ SELECT 1
SELECT `o1`.`OrderID`, `o1`.`ProductID`, `o1`.`Discount`, `o1`.`Quantity`, `o1`.`UnitPrice`
FROM `Order Details` AS `o1`
WHERE `o1`.`OrderID` > 11250
- ) AS `t`
- WHERE (`t`.`OrderID` = `o`.`OrderID`) AND (`t`.`ProductID` = `o`.`ProductID`))
+ ) AS `e`
+ WHERE (`e`.`OrderID` = `o`.`OrderID`) AND (`e`.`ProductID` = `o`.`ProductID`))
""");
}
@@ -525,12 +527,12 @@ public override async Task Delete_with_join(bool async)
DELETE `o`
FROM `Order Details` AS `o`
INNER JOIN (
- SELECT `o0`.`OrderID`, `o0`.`CustomerID`, `o0`.`EmployeeID`, `o0`.`OrderDate`
+ SELECT `o0`.`OrderID`
FROM `Orders` AS `o0`
WHERE `o0`.`OrderID` < 10300
ORDER BY `o0`.`OrderID`
LIMIT @__p_1 OFFSET @__p_0
-) AS `t` ON `o`.`OrderID` = `t`.`OrderID`
+) AS `o1` ON `o`.`OrderID` = `o1`.`OrderID`
""");
}
@@ -546,12 +548,12 @@ public override async Task Delete_with_left_join(bool async)
DELETE `o`
FROM `Order Details` AS `o`
LEFT JOIN (
- SELECT `o0`.`OrderID`, `o0`.`CustomerID`, `o0`.`EmployeeID`, `o0`.`OrderDate`
+ SELECT `o0`.`OrderID`
FROM `Orders` AS `o0`
WHERE `o0`.`OrderID` < 10300
ORDER BY `o0`.`OrderID`
LIMIT @__p_1 OFFSET @__p_0
-) AS `t` ON `o`.`OrderID` = `t`.`OrderID`
+) AS `o1` ON `o`.`OrderID` = `o1`.`OrderID`
WHERE `o`.`OrderID` < 10276
""");
}
@@ -565,12 +567,12 @@ public override async Task Delete_with_cross_join(bool async)
DELETE `o`
FROM `Order Details` AS `o`
CROSS JOIN (
- SELECT `o0`.`OrderID`, `o0`.`CustomerID`, `o0`.`EmployeeID`, `o0`.`OrderDate`
+ SELECT 1
FROM `Orders` AS `o0`
WHERE `o0`.`OrderID` < 10300
ORDER BY `o0`.`OrderID`
LIMIT 100 OFFSET 0
-) AS `t`
+) AS `o1`
WHERE `o`.`OrderID` < 10276
""");
}
@@ -584,12 +586,12 @@ public override async Task Delete_with_cross_apply(bool async)
DELETE `o`
FROM `Order Details` AS `o`
JOIN LATERAL (
- SELECT `o0`.`OrderID`, `o0`.`CustomerID`, `o0`.`EmployeeID`, `o0`.`OrderDate`
+ SELECT 1
FROM `Orders` AS `o0`
WHERE `o0`.`OrderID` < `o`.`OrderID`
ORDER BY `o0`.`OrderID`
LIMIT 100 OFFSET 0
-) AS `t` ON TRUE
+) AS `o1` ON TRUE
WHERE `o`.`OrderID` < 10276
""");
}
@@ -603,12 +605,12 @@ public override async Task Delete_with_outer_apply(bool async)
DELETE `o`
FROM `Order Details` AS `o`
LEFT JOIN LATERAL (
- SELECT `o0`.`OrderID`, `o0`.`CustomerID`, `o0`.`EmployeeID`, `o0`.`OrderDate`
+ SELECT 1
FROM `Orders` AS `o0`
WHERE `o0`.`OrderID` < `o`.`OrderID`
ORDER BY `o0`.`OrderID`
LIMIT 100 OFFSET 0
-) AS `t` ON TRUE
+) AS `o1` ON TRUE
WHERE `o`.`OrderID` < 10276
""");
}
@@ -784,15 +786,15 @@ await AssertUpdate(
@__p_1='4'
@__p_0='2'
-UPDATE `Customers` AS `c`
+UPDATE `Customers` AS `c0`
INNER JOIN (
- SELECT `c0`.`CustomerID`, `c0`.`Address`, `c0`.`City`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`, `c0`.`Region`
- FROM `Customers` AS `c0`
- WHERE `c0`.`CustomerID` LIKE 'F%'
- ORDER BY `c0`.`CustomerID`
+ SELECT `c`.`CustomerID`
+ FROM `Customers` AS `c`
+ WHERE `c`.`CustomerID` LIKE 'F%'
+ ORDER BY `c`.`CustomerID`
LIMIT @__p_1 OFFSET @__p_0
-) AS `t` ON `c`.`CustomerID` = `t`.`CustomerID`
-SET `c`.`ContactName` = 'Updated'
+) AS `c1` ON `c0`.`CustomerID` = `c1`.`CustomerID`
+SET `c0`.`ContactName` = 'Updated'
""");
}
@@ -802,13 +804,13 @@ public override async Task Update_Where_OrderBy_set_constant(bool async)
AssertExecuteUpdateSql(
"""
-UPDATE `Customers` AS `c`
+UPDATE `Customers` AS `c0`
INNER JOIN (
- SELECT `c0`.`CustomerID`, `c0`.`Address`, `c0`.`City`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`, `c0`.`Region`
- FROM `Customers` AS `c0`
- WHERE `c0`.`CustomerID` LIKE 'F%'
-) AS `t` ON `c`.`CustomerID` = `t`.`CustomerID`
-SET `c`.`ContactName` = 'Updated'
+ SELECT `c`.`CustomerID`
+ FROM `Customers` AS `c`
+ WHERE `c`.`CustomerID` LIKE 'F%'
+) AS `c1` ON `c0`.`CustomerID` = `c1`.`CustomerID`
+SET `c0`.`ContactName` = 'Updated'
""");
}
@@ -820,15 +822,15 @@ public override async Task Update_Where_OrderBy_Skip_set_constant(bool async)
"""
@__p_0='4'
-UPDATE `Customers` AS `c`
+UPDATE `Customers` AS `c0`
INNER JOIN (
- SELECT `c0`.`CustomerID`, `c0`.`Address`, `c0`.`City`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`, `c0`.`Region`
- FROM `Customers` AS `c0`
- WHERE `c0`.`CustomerID` LIKE 'F%'
- ORDER BY `c0`.`City`
+ SELECT `c`.`CustomerID`
+ FROM `Customers` AS `c`
+ WHERE `c`.`CustomerID` LIKE 'F%'
+ ORDER BY `c`.`City`
LIMIT 18446744073709551610 OFFSET @__p_0
-) AS `t` ON `c`.`CustomerID` = `t`.`CustomerID`
-SET `c`.`ContactName` = 'Updated'
+) AS `c1` ON `c0`.`CustomerID` = `c1`.`CustomerID`
+SET `c0`.`ContactName` = 'Updated'
""");
}
@@ -840,15 +842,15 @@ public override async Task Update_Where_OrderBy_Take_set_constant(bool async)
"""
@__p_0='4'
-UPDATE `Customers` AS `c`
+UPDATE `Customers` AS `c0`
INNER JOIN (
- SELECT `c0`.`CustomerID`, `c0`.`Address`, `c0`.`City`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`, `c0`.`Region`
- FROM `Customers` AS `c0`
- WHERE `c0`.`CustomerID` LIKE 'F%'
- ORDER BY `c0`.`City`
+ SELECT `c`.`CustomerID`
+ FROM `Customers` AS `c`
+ WHERE `c`.`CustomerID` LIKE 'F%'
+ ORDER BY `c`.`City`
LIMIT @__p_0
-) AS `t` ON `c`.`CustomerID` = `t`.`CustomerID`
-SET `c`.`ContactName` = 'Updated'
+) AS `c1` ON `c0`.`CustomerID` = `c1`.`CustomerID`
+SET `c0`.`ContactName` = 'Updated'
""");
}
@@ -861,15 +863,15 @@ public override async Task Update_Where_OrderBy_Skip_Take_set_constant(bool asyn
@__p_1='4'
@__p_0='2'
-UPDATE `Customers` AS `c`
+UPDATE `Customers` AS `c0`
INNER JOIN (
- SELECT `c0`.`CustomerID`, `c0`.`Address`, `c0`.`City`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`, `c0`.`Region`
- FROM `Customers` AS `c0`
- WHERE `c0`.`CustomerID` LIKE 'F%'
- ORDER BY `c0`.`City`
+ SELECT `c`.`CustomerID`
+ FROM `Customers` AS `c`
+ WHERE `c`.`CustomerID` LIKE 'F%'
+ ORDER BY `c`.`City`
LIMIT @__p_1 OFFSET @__p_0
-) AS `t` ON `c`.`CustomerID` = `t`.`CustomerID`
-SET `c`.`ContactName` = 'Updated'
+) AS `c1` ON `c0`.`CustomerID` = `c1`.`CustomerID`
+SET `c0`.`ContactName` = 'Updated'
""");
}
@@ -882,20 +884,20 @@ public override async Task Update_Where_OrderBy_Skip_Take_Skip_Take_set_constant
@__p_1='6'
@__p_0='2'
-UPDATE `Customers` AS `c`
+UPDATE `Customers` AS `c1`
INNER JOIN (
- SELECT `t`.`CustomerID`, `t`.`Address`, `t`.`City`, `t`.`CompanyName`, `t`.`ContactName`, `t`.`ContactTitle`, `t`.`Country`, `t`.`Fax`, `t`.`Phone`, `t`.`PostalCode`, `t`.`Region`
+ SELECT `c0`.`CustomerID`
FROM (
- SELECT `c0`.`CustomerID`, `c0`.`Address`, `c0`.`City`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`, `c0`.`Region`
- FROM `Customers` AS `c0`
- WHERE `c0`.`CustomerID` LIKE 'F%'
- ORDER BY `c0`.`City`
+ SELECT `c`.`CustomerID`, `c`.`City`
+ FROM `Customers` AS `c`
+ WHERE `c`.`CustomerID` LIKE 'F%'
+ ORDER BY `c`.`City`
LIMIT @__p_1 OFFSET @__p_0
- ) AS `t`
- ORDER BY `t`.`City`
+ ) AS `c0`
+ ORDER BY `c0`.`City`
LIMIT @__p_0 OFFSET @__p_0
-) AS `t0` ON `c`.`CustomerID` = `t0`.`CustomerID`
-SET `c`.`ContactName` = 'Updated'
+) AS `c2` ON `c1`.`CustomerID` = `c2`.`CustomerID`
+SET `c1`.`ContactName` = 'Updated'
""");
}
@@ -973,13 +975,13 @@ public override async Task Update_Where_Distinct_set_constant(bool async)
AssertExecuteUpdateSql(
"""
-UPDATE `Customers` AS `c`
+UPDATE `Customers` AS `c0`
INNER JOIN (
- SELECT DISTINCT `c0`.`CustomerID`, `c0`.`Address`, `c0`.`City`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`, `c0`.`Region`
- FROM `Customers` AS `c0`
- WHERE `c0`.`CustomerID` LIKE 'F%'
-) AS `t` ON `c`.`CustomerID` = `t`.`CustomerID`
-SET `c`.`ContactName` = 'Updated'
+ SELECT DISTINCT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ FROM `Customers` AS `c`
+ WHERE `c`.`CustomerID` LIKE 'F%'
+) AS `c1` ON `c0`.`CustomerID` = `c1`.`CustomerID`
+SET `c0`.`ContactName` = 'Updated'
""");
}
@@ -1135,17 +1137,17 @@ public override async Task Update_Union_set_constant(bool async)
AssertExecuteUpdateSql(
"""
-UPDATE `Customers` AS `c`
+UPDATE `Customers` AS `c1`
INNER JOIN (
+ SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ FROM `Customers` AS `c`
+ WHERE `c`.`CustomerID` LIKE 'F%'
+ UNION
SELECT `c0`.`CustomerID`, `c0`.`Address`, `c0`.`City`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`, `c0`.`Region`
FROM `Customers` AS `c0`
- WHERE `c0`.`CustomerID` LIKE 'F%'
- UNION
- SELECT `c1`.`CustomerID`, `c1`.`Address`, `c1`.`City`, `c1`.`CompanyName`, `c1`.`ContactName`, `c1`.`ContactTitle`, `c1`.`Country`, `c1`.`Fax`, `c1`.`Phone`, `c1`.`PostalCode`, `c1`.`Region`
- FROM `Customers` AS `c1`
- WHERE `c1`.`CustomerID` LIKE 'A%'
-) AS `t` ON `c`.`CustomerID` = `t`.`CustomerID`
-SET `c`.`ContactName` = 'Updated'
+ WHERE `c0`.`CustomerID` LIKE 'A%'
+) AS `u` ON `c1`.`CustomerID` = `u`.`CustomerID`
+SET `c1`.`ContactName` = 'Updated'
""");
}
@@ -1155,17 +1157,17 @@ public override async Task Update_Concat_set_constant(bool async)
AssertExecuteUpdateSql(
"""
-UPDATE `Customers` AS `c`
+UPDATE `Customers` AS `c1`
INNER JOIN (
- SELECT `c0`.`CustomerID`, `c0`.`Address`, `c0`.`City`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`, `c0`.`Region`
- FROM `Customers` AS `c0`
- WHERE `c0`.`CustomerID` LIKE 'F%'
+ SELECT `c`.`CustomerID`
+ FROM `Customers` AS `c`
+ WHERE `c`.`CustomerID` LIKE 'F%'
UNION ALL
- SELECT `c1`.`CustomerID`, `c1`.`Address`, `c1`.`City`, `c1`.`CompanyName`, `c1`.`ContactName`, `c1`.`ContactTitle`, `c1`.`Country`, `c1`.`Fax`, `c1`.`Phone`, `c1`.`PostalCode`, `c1`.`Region`
- FROM `Customers` AS `c1`
- WHERE `c1`.`CustomerID` LIKE 'A%'
-) AS `t` ON `c`.`CustomerID` = `t`.`CustomerID`
-SET `c`.`ContactName` = 'Updated'
+ SELECT `c0`.`CustomerID`
+ FROM `Customers` AS `c0`
+ WHERE `c0`.`CustomerID` LIKE 'A%'
+) AS `u` ON `c1`.`CustomerID` = `u`.`CustomerID`
+SET `c1`.`ContactName` = 'Updated'
""");
}
@@ -1175,17 +1177,17 @@ public override async Task Update_Except_set_constant(bool async)
AssertExecuteUpdateSql(
"""
-UPDATE `Customers` AS `c`
+UPDATE `Customers` AS `c1`
INNER JOIN (
+ SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ FROM `Customers` AS `c`
+ WHERE `c`.`CustomerID` LIKE 'F%'
+ EXCEPT
SELECT `c0`.`CustomerID`, `c0`.`Address`, `c0`.`City`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`, `c0`.`Region`
FROM `Customers` AS `c0`
- WHERE `c0`.`CustomerID` LIKE 'F%'
- EXCEPT
- SELECT `c1`.`CustomerID`, `c1`.`Address`, `c1`.`City`, `c1`.`CompanyName`, `c1`.`ContactName`, `c1`.`ContactTitle`, `c1`.`Country`, `c1`.`Fax`, `c1`.`Phone`, `c1`.`PostalCode`, `c1`.`Region`
- FROM `Customers` AS `c1`
- WHERE `c1`.`CustomerID` LIKE 'A%'
-) AS `t` ON `c`.`CustomerID` = `t`.`CustomerID`
-SET `c`.`ContactName` = 'Updated'
+ WHERE `c0`.`CustomerID` LIKE 'A%'
+) AS `e` ON `c1`.`CustomerID` = `e`.`CustomerID`
+SET `c1`.`ContactName` = 'Updated'
""");
}
@@ -1195,17 +1197,17 @@ public override async Task Update_Intersect_set_constant(bool async)
AssertExecuteUpdateSql(
"""
-UPDATE `Customers` AS `c`
+UPDATE `Customers` AS `c1`
INNER JOIN (
+ SELECT `c`.`CustomerID`, `c`.`Address`, `c`.`City`, `c`.`CompanyName`, `c`.`ContactName`, `c`.`ContactTitle`, `c`.`Country`, `c`.`Fax`, `c`.`Phone`, `c`.`PostalCode`, `c`.`Region`
+ FROM `Customers` AS `c`
+ WHERE `c`.`CustomerID` LIKE 'F%'
+ INTERSECT
SELECT `c0`.`CustomerID`, `c0`.`Address`, `c0`.`City`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`, `c0`.`Region`
FROM `Customers` AS `c0`
- WHERE `c0`.`CustomerID` LIKE 'F%'
- INTERSECT
- SELECT `c1`.`CustomerID`, `c1`.`Address`, `c1`.`City`, `c1`.`CompanyName`, `c1`.`ContactName`, `c1`.`ContactTitle`, `c1`.`Country`, `c1`.`Fax`, `c1`.`Phone`, `c1`.`PostalCode`, `c1`.`Region`
- FROM `Customers` AS `c1`
- WHERE `c1`.`CustomerID` LIKE 'A%'
-) AS `t` ON `c`.`CustomerID` = `t`.`CustomerID`
-SET `c`.`ContactName` = 'Updated'
+ WHERE `c0`.`CustomerID` LIKE 'A%'
+) AS `i` ON `c1`.`CustomerID` = `i`.`CustomerID`
+SET `c1`.`ContactName` = 'Updated'
""");
}
@@ -1217,10 +1219,10 @@ public override async Task Update_with_join_set_constant(bool async)
"""
UPDATE `Customers` AS `c`
INNER JOIN (
- SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ SELECT `o`.`CustomerID`
FROM `Orders` AS `o`
WHERE `o`.`OrderID` < 10300
-) AS `t` ON `c`.`CustomerID` = `t`.`CustomerID`
+) AS `o0` ON `c`.`CustomerID` = `o0`.`CustomerID`
SET `c`.`ContactName` = 'Updated'
WHERE `c`.`CustomerID` LIKE 'F%'
""");
@@ -1234,10 +1236,10 @@ public override async Task Update_with_left_join_set_constant(bool async)
"""
UPDATE `Customers` AS `c`
LEFT JOIN (
- SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ SELECT `o`.`CustomerID`
FROM `Orders` AS `o`
WHERE `o`.`OrderID` < 10300
-) AS `t` ON `c`.`CustomerID` = `t`.`CustomerID`
+) AS `o0` ON `c`.`CustomerID` = `o0`.`CustomerID`
SET `c`.`ContactName` = 'Updated'
WHERE `c`.`CustomerID` LIKE 'F%'
""");
@@ -1251,10 +1253,10 @@ public override async Task Update_with_cross_join_set_constant(bool async)
"""
UPDATE `Customers` AS `c`
CROSS JOIN (
- SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ SELECT 1
FROM `Orders` AS `o`
WHERE `o`.`OrderID` < 10300
-) AS `t`
+) AS `o0`
SET `c`.`ContactName` = 'Updated'
WHERE `c`.`CustomerID` LIKE 'F%'
""");
@@ -1268,10 +1270,10 @@ public override async Task Update_with_cross_apply_set_constant(bool async)
"""
UPDATE `Customers` AS `c`
JOIN LATERAL (
- SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ SELECT 1
FROM `Orders` AS `o`
WHERE (`o`.`OrderID` < 10300) AND (EXTRACT(year FROM `o`.`OrderDate`) < CHAR_LENGTH(`c`.`ContactName`))
-) AS `t` ON TRUE
+) AS `o0` ON TRUE
SET `c`.`ContactName` = 'Updated'
WHERE `c`.`CustomerID` LIKE 'F%'
""");
@@ -1285,10 +1287,10 @@ public override async Task Update_with_outer_apply_set_constant(bool async)
"""
UPDATE `Customers` AS `c`
LEFT JOIN LATERAL (
- SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ SELECT 1
FROM `Orders` AS `o`
WHERE (`o`.`OrderID` < 10300) AND (EXTRACT(year FROM `o`.`OrderDate`) < CHAR_LENGTH(`c`.`ContactName`))
-) AS `t` ON TRUE
+) AS `o0` ON TRUE
SET `c`.`ContactName` = 'Updated'
WHERE `c`.`CustomerID` LIKE 'F%'
""");
@@ -1302,15 +1304,15 @@ public override async Task Update_with_cross_join_left_join_set_constant(bool as
"""
UPDATE `Customers` AS `c`
CROSS JOIN (
- SELECT `c0`.`CustomerID`, `c0`.`Address`, `c0`.`City`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`, `c0`.`Region`
+ SELECT 1
FROM `Customers` AS `c0`
WHERE `c0`.`City` LIKE 'S%'
-) AS `t`
+) AS `c1`
LEFT JOIN (
- SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ SELECT `o`.`CustomerID`
FROM `Orders` AS `o`
WHERE `o`.`OrderID` < 10300
-) AS `t0` ON `c`.`CustomerID` = `t0`.`CustomerID`
+) AS `o0` ON `c`.`CustomerID` = `o0`.`CustomerID`
SET `c`.`ContactName` = 'Updated'
WHERE `c`.`CustomerID` LIKE 'F%'
""");
@@ -1324,15 +1326,15 @@ public override async Task Update_with_cross_join_cross_apply_set_constant(bool
"""
UPDATE `Customers` AS `c`
CROSS JOIN (
- SELECT `c0`.`CustomerID`, `c0`.`Address`, `c0`.`City`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`, `c0`.`Region`
+ SELECT 1
FROM `Customers` AS `c0`
WHERE `c0`.`City` LIKE 'S%'
-) AS `t`
+) AS `c1`
JOIN LATERAL (
- SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ SELECT 1
FROM `Orders` AS `o`
WHERE (`o`.`OrderID` < 10300) AND (EXTRACT(year FROM `o`.`OrderDate`) < CHAR_LENGTH(`c`.`ContactName`))
-) AS `t0` ON TRUE
+) AS `o0` ON TRUE
SET `c`.`ContactName` = 'Updated'
WHERE `c`.`CustomerID` LIKE 'F%'
""");
@@ -1346,15 +1348,15 @@ public override async Task Update_with_cross_join_outer_apply_set_constant(bool
"""
UPDATE `Customers` AS `c`
CROSS JOIN (
- SELECT `c0`.`CustomerID`, `c0`.`Address`, `c0`.`City`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`, `c0`.`Region`
+ SELECT 1
FROM `Customers` AS `c0`
WHERE `c0`.`City` LIKE 'S%'
-) AS `t`
+) AS `c1`
LEFT JOIN LATERAL (
- SELECT `o`.`OrderID`, `o`.`CustomerID`, `o`.`EmployeeID`, `o`.`OrderDate`
+ SELECT 1
FROM `Orders` AS `o`
WHERE (`o`.`OrderID` < 10300) AND (EXTRACT(year FROM `o`.`OrderDate`) < CHAR_LENGTH(`c`.`ContactName`))
-) AS `t0` ON TRUE
+) AS `o0` ON TRUE
SET `c`.`ContactName` = 'Updated'
WHERE `c`.`CustomerID` LIKE 'F%'
""");
@@ -1373,18 +1375,18 @@ public override async Task Update_Where_SelectMany_subquery_set_null(bool async)
AssertExecuteUpdateSql(
"""
-UPDATE `Orders` AS `o`
+UPDATE `Orders` AS `o1`
INNER JOIN (
- SELECT `t`.`OrderID`, `t`.`CustomerID`, `t`.`EmployeeID`, `t`.`OrderDate`, `c`.`CustomerID` AS `CustomerID0`
+ SELECT `o0`.`OrderID`
FROM `Customers` AS `c`
INNER JOIN (
- SELECT `o0`.`OrderID`, `o0`.`CustomerID`, `o0`.`EmployeeID`, `o0`.`OrderDate`
- FROM `Orders` AS `o0`
- WHERE EXTRACT(year FROM `o0`.`OrderDate`) = 1997
- ) AS `t` ON `c`.`CustomerID` = `t`.`CustomerID`
+ SELECT `o`.`OrderID`, `o`.`CustomerID`
+ FROM `Orders` AS `o`
+ WHERE EXTRACT(year FROM `o`.`OrderDate`) = 1997
+ ) AS `o0` ON `c`.`CustomerID` = `o0`.`CustomerID`
WHERE `c`.`CustomerID` LIKE 'F%'
-) AS `t0` ON `o`.`OrderID` = `t0`.`OrderID`
-SET `o`.`OrderDate` = NULL
+) AS `s` ON `o1`.`OrderID` = `s`.`OrderID`
+SET `o1`.`OrderDate` = NULL
""");
}
@@ -1414,11 +1416,11 @@ public override async Task Update_Where_Join_set_property_from_joined_table(bool
"""
UPDATE `Customers` AS `c`
CROSS JOIN (
- SELECT `c0`.`CustomerID`, `c0`.`Address`, `c0`.`City`, `c0`.`CompanyName`, `c0`.`ContactName`, `c0`.`ContactTitle`, `c0`.`Country`, `c0`.`Fax`, `c0`.`Phone`, `c0`.`PostalCode`, `c0`.`Region`
+ SELECT `c0`.`City`
FROM `Customers` AS `c0`
WHERE `c0`.`CustomerID` = 'ALFKI'
-) AS `t`
-SET `c`.`City` = `t`.`City`
+) AS `c1`
+SET `c`.`City` = `c1`.`City`
WHERE `c`.`CustomerID` LIKE 'F%'
""");
}
diff --git a/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesSingleStoreTest.cs
index bfbb8980b..cce565ca8 100644
--- a/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesSingleStoreTest.cs
@@ -1,6 +1,7 @@
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.BulkUpdates;
using Microsoft.EntityFrameworkCore.TestUtilities;
+using EntityFrameworkCore.SingleStore.FunctionalTests.TestUtilities;
using Xunit;
using Xunit.Abstractions;
@@ -19,7 +20,7 @@ public TPCFiltersInheritanceBulkUpdatesSingleStoreTest(
[ConditionalFact]
public virtual void Check_all_tests_overridden()
- => TestHelpers.AssertAllMethodsOverridden(GetType());
+ => SingleStoreTestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task Delete_where_hierarchy(bool async)
{
@@ -51,13 +52,13 @@ public override async Task Delete_where_using_hierarchy(bool async)
WHERE (
SELECT COUNT(*)
FROM (
- SELECT `e`.`Id`, `e`.`CountryId`, `e`.`Name`, `e`.`Species`, `e`.`EagleId`, `e`.`IsFlightless`, `e`.`Group`, NULL AS `FoundOn`, 'Eagle' AS `Discriminator`
+ SELECT `e`.`CountryId`
FROM `Eagle` AS `e`
UNION ALL
- SELECT `k`.`Id`, `k`.`CountryId`, `k`.`Name`, `k`.`Species`, `k`.`EagleId`, `k`.`IsFlightless`, NULL AS `Group`, `k`.`FoundOn`, 'Kiwi' AS `Discriminator`
+ SELECT `k`.`CountryId`
FROM `Kiwi` AS `k`
- ) AS `t`
- WHERE ((`t`.`CountryId` = 1) AND (`c`.`Id` = `t`.`CountryId`)) AND (`t`.`CountryId` > 0)) > 0
+ ) AS `u`
+ WHERE ((`u`.`CountryId` = 1) AND (`c`.`Id` = `u`.`CountryId`)) AND (`u`.`CountryId` > 0)) > 0
""");
}
@@ -72,10 +73,10 @@ public override async Task Delete_where_using_hierarchy_derived(bool async)
WHERE (
SELECT COUNT(*)
FROM (
- SELECT `k`.`Id`, `k`.`CountryId`, `k`.`Name`, `k`.`Species`, `k`.`EagleId`, `k`.`IsFlightless`, NULL AS `Group`, `k`.`FoundOn`, 'Kiwi' AS `Discriminator`
+ SELECT `k`.`CountryId`
FROM `Kiwi` AS `k`
- ) AS `t`
- WHERE ((`t`.`CountryId` = 1) AND (`c`.`Id` = `t`.`CountryId`)) AND (`t`.`CountryId` > 0)) > 0
+ ) AS `u`
+ WHERE ((`u`.`CountryId` = 1) AND (`c`.`Id` = `u`.`CountryId`)) AND (`u`.`CountryId` > 0)) > 0
""");
}
@@ -132,13 +133,13 @@ public override async Task Update_where_using_hierarchy(bool async)
WHERE (
SELECT COUNT(*)
FROM (
- SELECT `e`.`Id`, `e`.`CountryId`, `e`.`Name`, `e`.`Species`, `e`.`EagleId`, `e`.`IsFlightless`, `e`.`Group`, NULL AS `FoundOn`, 'Eagle' AS `Discriminator`
+ SELECT `e`.`CountryId`
FROM `Eagle` AS `e`
UNION ALL
- SELECT `k`.`Id`, `k`.`CountryId`, `k`.`Name`, `k`.`Species`, `k`.`EagleId`, `k`.`IsFlightless`, NULL AS `Group`, `k`.`FoundOn`, 'Kiwi' AS `Discriminator`
+ SELECT `k`.`CountryId`
FROM `Kiwi` AS `k`
- ) AS `t`
- WHERE ((`t`.`CountryId` = 1) AND (`c`.`Id` = `t`.`CountryId`)) AND (`t`.`CountryId` > 0)) > 0
+ ) AS `u`
+ WHERE ((`u`.`CountryId` = 1) AND (`c`.`Id` = `u`.`CountryId`)) AND (`u`.`CountryId` > 0)) > 0
""");
}
@@ -153,10 +154,10 @@ public override async Task Update_where_using_hierarchy_derived(bool async)
WHERE (
SELECT COUNT(*)
FROM (
- SELECT `k`.`Id`, `k`.`CountryId`, `k`.`Name`, `k`.`Species`, `k`.`EagleId`, `k`.`IsFlightless`, NULL AS `Group`, `k`.`FoundOn`, 'Kiwi' AS `Discriminator`
+ SELECT `k`.`CountryId`
FROM `Kiwi` AS `k`
- ) AS `t`
- WHERE ((`t`.`CountryId` = 1) AND (`c`.`Id` = `t`.`CountryId`)) AND (`t`.`CountryId` > 0)) > 0
+ ) AS `u`
+ WHERE ((`u`.`CountryId` = 1) AND (`c`.`Id` = `u`.`CountryId`)) AND (`u`.`CountryId` > 0)) > 0
""");
}
@@ -173,15 +174,15 @@ public override async Task Update_base_type(bool async)
AssertSql(
"""
-SELECT `t`.`Id`, `t`.`CountryId`, `t`.`Name`, `t`.`Species`, `t`.`EagleId`, `t`.`IsFlightless`, `t`.`Group`, `t`.`FoundOn`, `t`.`Discriminator`
+SELECT `u`.`Id`, `u`.`CountryId`, `u`.`Name`, `u`.`Species`, `u`.`EagleId`, `u`.`IsFlightless`, `u`.`Group`, `u`.`FoundOn`, `u`.`Discriminator`
FROM (
SELECT `e`.`Id`, `e`.`CountryId`, `e`.`Name`, `e`.`Species`, `e`.`EagleId`, `e`.`IsFlightless`, `e`.`Group`, NULL AS `FoundOn`, 'Eagle' AS `Discriminator`
FROM `Eagle` AS `e`
UNION ALL
SELECT `k`.`Id`, `k`.`CountryId`, `k`.`Name`, `k`.`Species`, `k`.`EagleId`, `k`.`IsFlightless`, NULL AS `Group`, `k`.`FoundOn`, 'Kiwi' AS `Discriminator`
FROM `Kiwi` AS `k`
-) AS `t`
-WHERE (`t`.`CountryId` = 1) AND (`t`.`Name` = 'Great spotted kiwi')
+) AS `u`
+WHERE (`u`.`CountryId` = 1) AND (`u`.`Name` = 'Great spotted kiwi')
""");
}
@@ -191,12 +192,12 @@ public override async Task Update_base_type_with_OfType(bool async)
AssertSql(
"""
-SELECT `t`.`Id`, `t`.`CountryId`, `t`.`Name`, `t`.`Species`, `t`.`EagleId`, `t`.`IsFlightless`, `t`.`FoundOn`, `t`.`Discriminator`
+SELECT `u`.`Id`, `u`.`CountryId`, `u`.`Name`, `u`.`Species`, `u`.`EagleId`, `u`.`IsFlightless`, `u`.`FoundOn`, `u`.`Discriminator`
FROM (
SELECT `k`.`Id`, `k`.`CountryId`, `k`.`Name`, `k`.`Species`, `k`.`EagleId`, `k`.`IsFlightless`, `k`.`FoundOn`, 'Kiwi' AS `Discriminator`
FROM `Kiwi` AS `k`
-) AS `t`
-WHERE `t`.`CountryId` = 1
+) AS `u`
+WHERE `u`.`CountryId` = 1
""");
}
diff --git a/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesSingleStoreTest.cs
index dc28a5585..d37e0666f 100644
--- a/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesSingleStoreTest.cs
@@ -1,6 +1,7 @@
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.BulkUpdates;
using Microsoft.EntityFrameworkCore.TestUtilities;
+using EntityFrameworkCore.SingleStore.FunctionalTests.TestUtilities;
using Xunit;
using Xunit.Abstractions;
@@ -16,6 +17,10 @@ public TPCInheritanceBulkUpdatesSingleStoreTest(
ClearLog();
}
+ [ConditionalFact]
+ public virtual void Check_all_tests_overridden()
+ => SingleStoreTestHelpers.AssertAllMethodsOverridden(GetType());
+
public override async Task Delete_where_hierarchy(bool async)
{
await base.Delete_where_hierarchy(async);
@@ -46,13 +51,13 @@ public override async Task Delete_where_using_hierarchy(bool async)
WHERE (
SELECT COUNT(*)
FROM (
- SELECT `e`.`Id`, `e`.`CountryId`, `e`.`Name`, `e`.`Species`, `e`.`EagleId`, `e`.`IsFlightless`, `e`.`Group`, NULL AS `FoundOn`, 'Eagle' AS `Discriminator`
+ SELECT `e`.`CountryId`
FROM `Eagle` AS `e`
UNION ALL
- SELECT `k`.`Id`, `k`.`CountryId`, `k`.`Name`, `k`.`Species`, `k`.`EagleId`, `k`.`IsFlightless`, NULL AS `Group`, `k`.`FoundOn`, 'Kiwi' AS `Discriminator`
+ SELECT `k`.`CountryId`
FROM `Kiwi` AS `k`
- ) AS `t`
- WHERE (`c`.`Id` = `t`.`CountryId`) AND (`t`.`CountryId` > 0)) > 0
+ ) AS `u`
+ WHERE (`c`.`Id` = `u`.`CountryId`) AND (`u`.`CountryId` > 0)) > 0
""");
}
@@ -67,10 +72,10 @@ public override async Task Delete_where_using_hierarchy_derived(bool async)
WHERE (
SELECT COUNT(*)
FROM (
- SELECT `k`.`Id`, `k`.`CountryId`, `k`.`Name`, `k`.`Species`, `k`.`EagleId`, `k`.`IsFlightless`, NULL AS `Group`, `k`.`FoundOn`, 'Kiwi' AS `Discriminator`
+ SELECT `k`.`CountryId`
FROM `Kiwi` AS `k`
- ) AS `t`
- WHERE (`c`.`Id` = `t`.`CountryId`) AND (`t`.`CountryId` > 0)) > 0
+ ) AS `u`
+ WHERE (`c`.`Id` = `u`.`CountryId`) AND (`u`.`CountryId` > 0)) > 0
""");
}
@@ -127,13 +132,13 @@ public override async Task Update_where_using_hierarchy(bool async)
WHERE (
SELECT COUNT(*)
FROM (
- SELECT `e`.`Id`, `e`.`CountryId`, `e`.`Name`, `e`.`Species`, `e`.`EagleId`, `e`.`IsFlightless`, `e`.`Group`, NULL AS `FoundOn`, 'Eagle' AS `Discriminator`
+ SELECT `e`.`CountryId`
FROM `Eagle` AS `e`
UNION ALL
- SELECT `k`.`Id`, `k`.`CountryId`, `k`.`Name`, `k`.`Species`, `k`.`EagleId`, `k`.`IsFlightless`, NULL AS `Group`, `k`.`FoundOn`, 'Kiwi' AS `Discriminator`
+ SELECT `k`.`CountryId`
FROM `Kiwi` AS `k`
- ) AS `t`
- WHERE (`c`.`Id` = `t`.`CountryId`) AND (`t`.`CountryId` > 0)) > 0
+ ) AS `u`
+ WHERE (`c`.`Id` = `u`.`CountryId`) AND (`u`.`CountryId` > 0)) > 0
""");
}
@@ -148,10 +153,10 @@ public override async Task Update_where_using_hierarchy_derived(bool async)
WHERE (
SELECT COUNT(*)
FROM (
- SELECT `k`.`Id`, `k`.`CountryId`, `k`.`Name`, `k`.`Species`, `k`.`EagleId`, `k`.`IsFlightless`, NULL AS `Group`, `k`.`FoundOn`, 'Kiwi' AS `Discriminator`
+ SELECT `k`.`CountryId`
FROM `Kiwi` AS `k`
- ) AS `t`
- WHERE (`c`.`Id` = `t`.`CountryId`) AND (`t`.`CountryId` > 0)) > 0
+ ) AS `u`
+ WHERE (`c`.`Id` = `u`.`CountryId`) AND (`u`.`CountryId` > 0)) > 0
""");
}
@@ -168,15 +173,15 @@ public override async Task Update_base_type(bool async)
AssertSql(
"""
-SELECT `t`.`Id`, `t`.`CountryId`, `t`.`Name`, `t`.`Species`, `t`.`EagleId`, `t`.`IsFlightless`, `t`.`Group`, `t`.`FoundOn`, `t`.`Discriminator`
+SELECT `u`.`Id`, `u`.`CountryId`, `u`.`Name`, `u`.`Species`, `u`.`EagleId`, `u`.`IsFlightless`, `u`.`Group`, `u`.`FoundOn`, `u`.`Discriminator`
FROM (
SELECT `e`.`Id`, `e`.`CountryId`, `e`.`Name`, `e`.`Species`, `e`.`EagleId`, `e`.`IsFlightless`, `e`.`Group`, NULL AS `FoundOn`, 'Eagle' AS `Discriminator`
FROM `Eagle` AS `e`
UNION ALL
SELECT `k`.`Id`, `k`.`CountryId`, `k`.`Name`, `k`.`Species`, `k`.`EagleId`, `k`.`IsFlightless`, NULL AS `Group`, `k`.`FoundOn`, 'Kiwi' AS `Discriminator`
FROM `Kiwi` AS `k`
-) AS `t`
-WHERE `t`.`Name` = 'Great spotted kiwi'
+) AS `u`
+WHERE `u`.`Name` = 'Great spotted kiwi'
""");
}
diff --git a/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPHFiltersInheritanceBulkUpdatesSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPHFiltersInheritanceBulkUpdatesSingleStoreTest.cs
index d583bf4a9..18b509770 100644
--- a/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPHFiltersInheritanceBulkUpdatesSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPHFiltersInheritanceBulkUpdatesSingleStoreTest.cs
@@ -1,11 +1,7 @@
-using System;
using System.Threading.Tasks;
-using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.BulkUpdates;
-using Microsoft.EntityFrameworkCore.TestUtilities;
-using SingleStoreConnector;
+using EntityFrameworkCore.SingleStore.FunctionalTests.TestUtilities;
using EntityFrameworkCore.SingleStore.Infrastructure;
-using EntityFrameworkCore.SingleStore.Tests;
using EntityFrameworkCore.SingleStore.Tests.TestUtilities.Attributes;
using Xunit;
@@ -22,7 +18,7 @@ public TPHFiltersInheritanceBulkUpdatesSingleStoreTest(TPHFiltersInheritanceBulk
[ConditionalFact]
public virtual void Check_all_tests_overridden()
- => TestHelpers.AssertAllMethodsOverridden(GetType());
+ => SingleStoreTestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task Delete_where_hierarchy(bool async)
{
@@ -151,13 +147,6 @@ SELECT COUNT(*)
""");
}
- public override async Task Update_where_keyless_entity_mapped_to_sql_query(bool async)
- {
- await base.Update_where_keyless_entity_mapped_to_sql_query(async);
-
- AssertExecuteUpdateSql();
- }
-
public override async Task Update_base_type(bool async)
{
await base.Update_base_type(async);
@@ -227,19 +216,12 @@ public override async Task Delete_where_hierarchy_subquery(bool async)
AssertSql();
}
- public override async Task Delete_where_keyless_entity_mapped_to_sql_query(bool async)
- {
- await base.Delete_where_keyless_entity_mapped_to_sql_query(async);
-
- AssertSql();
- }
-
- protected override void ClearLog()
- => Fixture.TestSqlLoggerFactory.Clear();
-
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
private void AssertExecuteUpdateSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected, forUpdate: true);
+
+ private void ClearLog()
+ => Fixture.TestSqlLoggerFactory.Clear();
}
diff --git a/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPHInheritanceBulkUpdatesSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPHInheritanceBulkUpdatesSingleStoreTest.cs
index 6f1a7005b..4d3234189 100644
--- a/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPHInheritanceBulkUpdatesSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPHInheritanceBulkUpdatesSingleStoreTest.cs
@@ -2,20 +2,28 @@
using Microsoft.EntityFrameworkCore.BulkUpdates;
using Microsoft.EntityFrameworkCore.TestUtilities;
using SingleStoreConnector;
+using EntityFrameworkCore.SingleStore.FunctionalTests.TestUtilities;
using EntityFrameworkCore.SingleStore.Infrastructure;
using EntityFrameworkCore.SingleStore.Tests.TestUtilities.Attributes;
using Xunit;
+using Xunit.Abstractions;
namespace EntityFrameworkCore.SingleStore.FunctionalTests.BulkUpdates;
public class TPHInheritanceBulkUpdatesSingleStoreTest : TPHInheritanceBulkUpdatesTestBase
{
- public TPHInheritanceBulkUpdatesSingleStoreTest(TPHInheritanceBulkUpdatesSingleStoreFixture fixture)
- : base(fixture)
+ public TPHInheritanceBulkUpdatesSingleStoreTest(
+ TPHInheritanceBulkUpdatesSingleStoreFixture fixture,
+ ITestOutputHelper testOutputHelper)
+ : base(fixture, testOutputHelper)
{
ClearLog();
}
+ [ConditionalFact]
+ public virtual void Check_all_tests_overridden()
+ => SingleStoreTestHelpers.AssertAllMethodsOverridden(GetType());
+
public override async Task Delete_where_hierarchy(bool async)
{
await base.Delete_where_hierarchy(async);
diff --git a/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesSingleStoreTest.cs
index 2a13f3b3b..ba58d338f 100644
--- a/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesSingleStoreTest.cs
@@ -2,6 +2,7 @@
using EntityFrameworkCore.SingleStore.Tests;
using Microsoft.EntityFrameworkCore.BulkUpdates;
using Microsoft.EntityFrameworkCore.TestUtilities;
+using EntityFrameworkCore.SingleStore.FunctionalTests.TestUtilities;
using Xunit;
using Xunit.Abstractions;
@@ -20,7 +21,7 @@ public TPTFiltersInheritanceBulkUpdatesSingleStoreTest(
[ConditionalFact]
public virtual void Check_all_tests_overridden()
- => TestHelpers.AssertAllMethodsOverridden(GetType());
+ => SingleStoreTestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task Delete_where_hierarchy(bool async)
{
@@ -47,9 +48,6 @@ public override async Task Delete_where_using_hierarchy(bool async)
WHERE (
SELECT COUNT(*)
FROM `Animals` AS `a`
- LEFT JOIN `Birds` AS `b` ON `a`.`Id` = `b`.`Id`
- LEFT JOIN `Eagle` AS `e` ON `a`.`Id` = `e`.`Id`
- LEFT JOIN `Kiwi` AS `k` ON `a`.`Id` = `k`.`Id`
WHERE ((`a`.`CountryId` = 1) AND (`c`.`Id` = `a`.`CountryId`)) AND (`a`.`CountryId` > 0)) > 0
""");
}
@@ -72,8 +70,6 @@ public override async Task Delete_where_using_hierarchy_derived(bool async)
WHERE (
SELECT COUNT(*)
FROM `Animals` AS `a`
- LEFT JOIN `Birds` AS `b` ON `a`.`Id` = `b`.`Id`
- LEFT JOIN `Eagle` AS `e` ON `a`.`Id` = `e`.`Id`
LEFT JOIN `Kiwi` AS `k` ON `a`.`Id` = `k`.`Id`
WHERE (((`a`.`CountryId` = 1) AND (`c`.`Id` = `a`.`CountryId`)) AND `k`.`Id` IS NOT NULL) AND (`a`.`CountryId` > 0)) > 0
""");
@@ -139,9 +135,6 @@ public override async Task Update_where_using_hierarchy(bool async)
WHERE (
SELECT COUNT(*)
FROM `Animals` AS `a`
- LEFT JOIN `Birds` AS `b` ON `a`.`Id` = `b`.`Id`
- LEFT JOIN `Eagle` AS `e` ON `a`.`Id` = `e`.`Id`
- LEFT JOIN `Kiwi` AS `k` ON `a`.`Id` = `k`.`Id`
WHERE ((`a`.`CountryId` = 1) AND (`c`.`Id` = `a`.`CountryId`)) AND (`a`.`CountryId` > 0)) > 0
""");
}
@@ -164,8 +157,6 @@ public override async Task Update_where_using_hierarchy_derived(bool async)
WHERE (
SELECT COUNT(*)
FROM `Animals` AS `a`
- LEFT JOIN `Birds` AS `b` ON `a`.`Id` = `b`.`Id`
- LEFT JOIN `Eagle` AS `e` ON `a`.`Id` = `e`.`Id`
LEFT JOIN `Kiwi` AS `k` ON `a`.`Id` = `k`.`Id`
WHERE (((`a`.`CountryId` = 1) AND (`c`.`Id` = `a`.`CountryId`)) AND `k`.`Id` IS NOT NULL) AND (`a`.`CountryId` > 0)) > 0
""");
diff --git a/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesSingleStoreTest.cs
index 117f81abd..e87499a0e 100644
--- a/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesSingleStoreTest.cs
@@ -2,6 +2,7 @@
using EntityFrameworkCore.SingleStore.Tests;
using Microsoft.EntityFrameworkCore.BulkUpdates;
using Microsoft.EntityFrameworkCore.TestUtilities;
+using EntityFrameworkCore.SingleStore.FunctionalTests.TestUtilities;
using Xunit;
using Xunit.Abstractions;
@@ -17,6 +18,10 @@ public TPTInheritanceBulkUpdatesSingleStoreTest(
ClearLog();
}
+ [ConditionalFact]
+ public virtual void Check_all_tests_overridden()
+ => SingleStoreTestHelpers.AssertAllMethodsOverridden(GetType());
+
public override async Task Delete_where_hierarchy(bool async)
{
await base.Delete_where_hierarchy(async);
@@ -105,9 +110,6 @@ public override async Task Update_where_using_hierarchy(bool async)
WHERE (
SELECT COUNT(*)
FROM `Animals` AS `a`
- LEFT JOIN `Birds` AS `b` ON `a`.`Id` = `b`.`Id`
- LEFT JOIN `Eagle` AS `e` ON `a`.`Id` = `e`.`Id`
- LEFT JOIN `Kiwi` AS `k` ON `a`.`Id` = `k`.`Id`
WHERE (`c`.`Id` = `a`.`CountryId`) AND (`a`.`CountryId` > 0)) > 0
""");
}
@@ -130,8 +132,6 @@ public override async Task Update_where_using_hierarchy_derived(bool async)
WHERE (
SELECT COUNT(*)
FROM `Animals` AS `a`
- LEFT JOIN `Birds` AS `b` ON `a`.`Id` = `b`.`Id`
- LEFT JOIN `Eagle` AS `e` ON `a`.`Id` = `e`.`Id`
LEFT JOIN `Kiwi` AS `k` ON `a`.`Id` = `k`.`Id`
WHERE ((`c`.`Id` = `a`.`CountryId`) AND `k`.`Id` IS NOT NULL) AND (`a`.`CountryId` > 0)) > 0
""");
diff --git a/test/EFCore.SingleStore.FunctionalTests/ConnectionSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/ConnectionSingleStoreTest.cs
index b8fb5c9d9..3cf5df7bc 100644
--- a/test/EFCore.SingleStore.FunctionalTests/ConnectionSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/ConnectionSingleStoreTest.cs
@@ -1,4 +1,6 @@
using System;
+using System.Threading;
+using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@@ -82,11 +84,11 @@ public virtual void ConnectionAttributes()
}
[Fact]
- public void UseSingleStore_IncludesConnectorAttributes_InConnectionString()
+ public async Task UseSingleStore_IncludesConnectorAttributes_InConnectionString()
{
- using var _ = ((SingleStoreTestStore)SingleStoreNorthwindTestStoreFactory.Instance
+ await using var _ = await ((SingleStoreTestStore)SingleStoreNorthwindTestStoreFactory.Instance
.GetOrCreate("ConnectionAttributesTest"))
- .Initialize(null, (Func)null);
+ .InitializeAsync(null, (Func)null);
var cs = SingleStoreTestStore.CreateConnectionString("ConnectionAttributesTest");
var optionsBuilder = new DbContextOptionsBuilder();
@@ -109,65 +111,65 @@ public void UseSingleStore_IncludesConnectorAttributes_InConnectionString()
}
[Fact]
- public void Can_create_admin_connection_with_data_source()
+ public async Task Can_create_admin_connection_with_data_source()
{
- using var _ = ((SingleStoreTestStore)SingleStoreNorthwindTestStoreFactory.Instance
+ await using var _ = await ((SingleStoreTestStore)SingleStoreNorthwindTestStoreFactory.Instance
.GetOrCreate("ConnectionTest"))
- .Initialize(null, (Func)null);
+ .InitializeAsync(null, (Func)null);
- using var dataSource = new SingleStoreDataSourceBuilder(SingleStoreTestStore.CreateConnectionString("ConnectionTest")).Build();
+ await using var dataSource = new SingleStoreDataSourceBuilder(SingleStoreTestStore.CreateConnectionString("ConnectionTest")).Build();
var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseSingleStore(dataSource, b => b.ApplyConfiguration());
- using var context = new GeneralOptionsContext(optionsBuilder.Options);
+ await using var context = new GeneralOptionsContext(optionsBuilder.Options);
var relationalConnection = context.GetService();
- using var masterConnection = relationalConnection.CreateMasterConnection();
+ await using var masterConnection = relationalConnection.CreateMasterConnection();
Assert.Equal(string.Empty, new SingleStoreConnectionStringBuilder(masterConnection.ConnectionString).Database);
- masterConnection.Open();
+ await masterConnection.OpenAsync(default);
}
[Fact]
- public void Can_create_admin_connection_with_connection_string()
+ public async Task Can_create_admin_connection_with_connection_string()
{
- using var _ = ((SingleStoreTestStore)SingleStoreNorthwindTestStoreFactory.Instance
+ await using var _ = await ((SingleStoreTestStore)SingleStoreNorthwindTestStoreFactory.Instance
.GetOrCreate("ConnectionTest"))
- .Initialize(null, (Func)null);
+ .InitializeAsync(null, (Func)null);
var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseSingleStore(SingleStoreTestStore.CreateConnectionString("ConnectionTest"),
b => b.ApplyConfiguration());
- using var context = new GeneralOptionsContext(optionsBuilder.Options);
+ await using var context = new GeneralOptionsContext(optionsBuilder.Options);
var relationalConnection = context.GetService();
- using var masterConnection = relationalConnection.CreateMasterConnection();
+ await using var masterConnection = relationalConnection.CreateMasterConnection();
Assert.Equal(string.Empty, new SingleStoreConnectionStringBuilder(masterConnection.ConnectionString).Database);
- masterConnection.Open();
+ await masterConnection.OpenAsync(default);
}
[Fact]
- public void Can_create_admin_connection_with_connection()
+ public async Task Can_create_admin_connection_with_connection()
{
- using var _ = ((SingleStoreTestStore)SingleStoreNorthwindTestStoreFactory.Instance
+ await using var _ = await ((SingleStoreTestStore)SingleStoreNorthwindTestStoreFactory.Instance
.GetOrCreate("ConnectionTestWithConnection"))
- .Initialize(null, (Func)null);
+ .InitializeAsync(null, (Func)null);
- using var connection = new SingleStoreConnection(SingleStoreTestStore.CreateConnectionString("ConnectionTestWithConnection"));
+ await using var connection = new SingleStoreConnection(SingleStoreTestStore.CreateConnectionString("ConnectionTestWithConnection"));
var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseSingleStore(connection, b => b.ApplyConfiguration());
- using var context = new GeneralOptionsContext(optionsBuilder.Options);
+ await using var context = new GeneralOptionsContext(optionsBuilder.Options);
var relationalConnection = context.GetService();
- using var masterConnection = relationalConnection.CreateMasterConnection();
+ await using var masterConnection = relationalConnection.CreateMasterConnection();
Assert.Equal(string.Empty, new SingleStoreConnectionStringBuilder(masterConnection.ConnectionString).Database);
- masterConnection.Open();
+ await masterConnection.OpenAsync(default);
}
diff --git a/test/EFCore.SingleStore.FunctionalTests/ConvertToProviderTypesSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/ConvertToProviderTypesSingleStoreTest.cs
index ba58d25d1..e094c8d9b 100644
--- a/test/EFCore.SingleStore.FunctionalTests/ConvertToProviderTypesSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/ConvertToProviderTypesSingleStoreTest.cs
@@ -2,6 +2,7 @@
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
+using System.Threading.Tasks;
using EntityFrameworkCore.SingleStore.FunctionalTests.TestUtilities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
@@ -24,43 +25,44 @@ public ConvertToProviderTypesSingleStoreTest(ConvertToProviderTypesSingleStoreFi
}
[ConditionalFact(Skip = "Feature 'FOREIGN KEY' is not supported by SingleStore Distributed.")]
- public override void Can_read_back_mapped_enum_from_collection_first_or_default()
+ public override async Task Can_read_back_mapped_enum_from_collection_first_or_default()
{
- base.Can_read_back_mapped_enum_from_collection_first_or_default();
+ await base.Can_read_back_mapped_enum_from_collection_first_or_default();
}
[ConditionalFact(Skip = "Feature 'FOREIGN KEY' is not supported by SingleStore Distributed.")]
- public override void Can_insert_and_read_back_with_binary_key()
+ public override async Task Can_insert_and_read_back_with_binary_key()
{
- base.Can_insert_and_read_back_with_binary_key();
+ await base.Can_insert_and_read_back_with_binary_key();
}
[ConditionalFact(Skip = "Feature 'FOREIGN KEY' is not supported by SingleStore Distributed.")]
- public override void Can_insert_and_read_back_with_null_binary_foreign_key()
+ public override async Task Can_insert_and_read_back_with_null_binary_foreign_key()
{
- base.Can_insert_and_read_back_with_null_binary_foreign_key();
+ await base.Can_insert_and_read_back_with_null_binary_foreign_key();
}
[ConditionalFact(Skip = "Feature 'FOREIGN KEY' is not supported by SingleStore Distributed.")]
- public override void Can_insert_and_read_back_with_null_string_foreign_key()
+ public override async Task Can_insert_and_read_back_with_null_string_foreign_key()
{
- base.Can_insert_and_read_back_with_null_string_foreign_key();
+ await base.Can_insert_and_read_back_with_null_string_foreign_key();
}
[ConditionalFact(Skip = "Feature 'FOREIGN KEY' is not supported by SingleStore Distributed.")]
- public override void Can_insert_and_read_back_with_string_key()
+ public override async Task Can_insert_and_read_back_with_string_key()
{
- base.Can_insert_and_read_back_with_string_key();
+ await base.Can_insert_and_read_back_with_string_key();
}
[ConditionalFact(Skip = "Further investigation is needed to determine why it is failing with SingleStore")]
- public override void Optional_datetime_reading_null_from_database()
+ public override async Task Optional_datetime_reading_null_from_database()
{
- base.Optional_datetime_reading_null_from_database();
+ await base.Optional_datetime_reading_null_from_database();
}
- public override void Can_perform_query_with_ansi_strings_test()
+ public override Task Can_perform_query_with_ansi_strings_test()
{
+ return Task.CompletedTask;
}
// TODO: Needed to customize:
@@ -69,7 +71,7 @@ public override void Can_perform_query_with_ansi_strings_test()
#region https://github.com/dotnet/efcore/issues/26068
[ConditionalFact]
- public override void Can_insert_and_read_back_all_non_nullable_data_types()
+ public override async Task Can_insert_and_read_back_all_non_nullable_data_types()
{
using (var context = CreateContext())
{
@@ -104,12 +106,12 @@ public override void Can_insert_and_read_back_all_non_nullable_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(e => e.Id == 1).ToList().Single();
+ var dt = (await context.Set().Where(e => e.Id == 1).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(BuiltInDataTypes));
AssertEqualIfMapped(entityType, (short)-1234, () => dt.TestInt16);
@@ -143,7 +145,7 @@ public override void Can_insert_and_read_back_all_non_nullable_data_types()
}
[ConditionalFact]
- public override void Can_insert_and_read_back_non_nullable_backed_data_types()
+ public override async Task Can_insert_and_read_back_non_nullable_backed_data_types()
{
using (var context = CreateContext())
{
@@ -178,12 +180,12 @@ public override void Can_insert_and_read_back_non_nullable_backed_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(ndt => ndt.Id == 101).ToList().Single();
+ var dt = (await context.Set().Where(ndt => ndt.Id == 101).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(NonNullableBackedDataTypes));
AssertEqualIfMapped(entityType, (short)-1234, () => dt.Int16);
@@ -217,7 +219,7 @@ public override void Can_insert_and_read_back_non_nullable_backed_data_types()
}
[ConditionalFact]
- public override void Can_insert_and_read_back_nullable_backed_data_types()
+ public override async Task Can_insert_and_read_back_nullable_backed_data_types()
{
using (var context = CreateContext())
{
@@ -252,12 +254,12 @@ public override void Can_insert_and_read_back_nullable_backed_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(ndt => ndt.Id == 101).ToList().Single();
+ var dt = (await context.Set().Where(ndt => ndt.Id == 101).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(NullableBackedDataTypes));
AssertEqualIfMapped(entityType, (short)-1234, () => dt.Int16);
@@ -290,7 +292,7 @@ public override void Can_insert_and_read_back_nullable_backed_data_types()
}
[ConditionalFact]
- public override void Can_insert_and_read_back_object_backed_data_types()
+ public override async Task Can_insert_and_read_back_object_backed_data_types()
{
using (var context = CreateContext())
{
@@ -327,12 +329,12 @@ public override void Can_insert_and_read_back_object_backed_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(ndt => ndt.Id == 101).ToList().Single();
+ var dt = (await context.Set().Where(ndt => ndt.Id == 101).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(ObjectBackedDataTypes));
AssertEqualIfMapped(entityType, "TestString", () => dt.String);
diff --git a/test/EFCore.SingleStore.FunctionalTests/CustomConvertersSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/CustomConvertersSingleStoreTest.cs
index 500a39d65..5759d632f 100644
--- a/test/EFCore.SingleStore.FunctionalTests/CustomConvertersSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/CustomConvertersSingleStoreTest.cs
@@ -2,6 +2,7 @@
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
+using System.Threading.Tasks;
using EntityFrameworkCore.SingleStore.FunctionalTests.TestUtilities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
@@ -27,9 +28,9 @@ public override void Value_conversion_on_enum_collection_contains()
}
[ConditionalFact(Skip = "Further investigation is needed to determine why it is failing with SingleStore")]
- public override void Optional_datetime_reading_null_from_database()
+ public override async Task Optional_datetime_reading_null_from_database()
{
- base.Optional_datetime_reading_null_from_database();
+ await base.Optional_datetime_reading_null_from_database();
}
// TODO: Needed to customize:
@@ -38,43 +39,43 @@ public override void Optional_datetime_reading_null_from_database()
#region https://github.com/dotnet/efcore/issues/26068
[ConditionalFact(Skip = "Feature 'FOREIGN KEY' is not supported by SingleStore Distributed.")]
- public override void Can_insert_and_read_back_with_binary_key()
+ public override async Task Can_insert_and_read_back_with_binary_key()
{
- base.Can_insert_and_read_back_with_binary_key();
+ await base.Can_insert_and_read_back_with_binary_key();
}
[ConditionalFact(Skip = "Feature 'FOREIGN KEY' is not supported by SingleStore Distributed.")]
- public override void Can_insert_and_read_back_with_null_binary_foreign_key()
+ public override async Task Can_insert_and_read_back_with_null_binary_foreign_key()
{
- base.Can_insert_and_read_back_with_null_binary_foreign_key();
+ await base.Can_insert_and_read_back_with_null_binary_foreign_key();
}
[ConditionalFact(Skip = "Feature 'FOREIGN KEY' is not supported by SingleStore Distributed.")]
- public override void Can_insert_and_read_back_with_null_string_foreign_key()
+ public override async Task Can_insert_and_read_back_with_null_string_foreign_key()
{
- base.Can_insert_and_read_back_with_null_string_foreign_key();
+ await base.Can_insert_and_read_back_with_null_string_foreign_key();
}
[ConditionalFact(Skip = "Feature 'FOREIGN KEY' is not supported by SingleStore Distributed.")]
- public override void Can_insert_and_read_back_with_string_key()
+ public override async Task Can_insert_and_read_back_with_string_key()
{
- base.Can_insert_and_read_back_with_string_key();
+ await base.Can_insert_and_read_back_with_string_key();
}
[ConditionalFact(Skip = "Feature 'FOREIGN KEY' is not supported by SingleStore Distributed.")]
- public override void Can_read_back_mapped_enum_from_collection_first_or_default()
+ public override async Task Can_read_back_mapped_enum_from_collection_first_or_default()
{
- base.Can_read_back_mapped_enum_from_collection_first_or_default();
+ await base.Can_read_back_mapped_enum_from_collection_first_or_default();
}
[ConditionalFact(Skip = "Feature 'FOREIGN KEY' is not supported by SingleStore Distributed.")]
- public override void Can_insert_and_read_back_with_case_insensitive_string_key()
+ public override async Task Can_insert_and_read_back_with_case_insensitive_string_key()
{
- base.Can_insert_and_read_back_with_case_insensitive_string_key();
+ await base.Can_insert_and_read_back_with_case_insensitive_string_key();
}
[ConditionalFact]
- public override void Can_insert_and_read_back_all_non_nullable_data_types()
+ public override async Task Can_insert_and_read_back_all_non_nullable_data_types()
{
using (var context = CreateContext())
{
@@ -109,12 +110,12 @@ public override void Can_insert_and_read_back_all_non_nullable_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(e => e.Id == 1).ToList().Single();
+ var dt = (await context.Set().Where(e => e.Id == 1).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(BuiltInDataTypes));
AssertEqualIfMapped(entityType, (short)-1234, () => dt.TestInt16);
@@ -148,7 +149,7 @@ public override void Can_insert_and_read_back_all_non_nullable_data_types()
}
[ConditionalFact]
- public override void Can_insert_and_read_back_non_nullable_backed_data_types()
+ public override async Task Can_insert_and_read_back_non_nullable_backed_data_types()
{
using (var context = CreateContext())
{
@@ -183,12 +184,12 @@ public override void Can_insert_and_read_back_non_nullable_backed_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(ndt => ndt.Id == 101).ToList().Single();
+ var dt = (await context.Set().Where(ndt => ndt.Id == 101).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(NonNullableBackedDataTypes));
AssertEqualIfMapped(entityType, (short)-1234, () => dt.Int16);
@@ -222,7 +223,7 @@ public override void Can_insert_and_read_back_non_nullable_backed_data_types()
}
[ConditionalFact]
- public override void Can_insert_and_read_back_nullable_backed_data_types()
+ public override async Task Can_insert_and_read_back_nullable_backed_data_types()
{
using (var context = CreateContext())
{
@@ -257,12 +258,12 @@ public override void Can_insert_and_read_back_nullable_backed_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(ndt => ndt.Id == 101).ToList().Single();
+ var dt = (await context.Set().Where(ndt => ndt.Id == 101).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(NullableBackedDataTypes));
AssertEqualIfMapped(entityType, (short)-1234, () => dt.Int16);
@@ -295,7 +296,7 @@ public override void Can_insert_and_read_back_nullable_backed_data_types()
}
[ConditionalFact]
- public override void Can_insert_and_read_back_object_backed_data_types()
+ public override async Task Can_insert_and_read_back_object_backed_data_types()
{
using (var context = CreateContext())
{
@@ -332,12 +333,12 @@ public override void Can_insert_and_read_back_object_backed_data_types()
EnumS8 = EnumS8.SomeValue
});
- Assert.Equal(1, context.SaveChanges());
+ Assert.Equal(1, await context.SaveChangesAsync());
}
using (var context = CreateContext())
{
- var dt = context.Set().Where(ndt => ndt.Id == 101).ToList().Single();
+ var dt = (await context.Set().Where(ndt => ndt.Id == 101).ToListAsync()).Single();
var entityType = context.Model.FindEntityType(typeof(ObjectBackedDataTypes));
AssertEqualIfMapped(entityType, "TestString", () => dt.String);
diff --git a/test/EFCore.SingleStore.FunctionalTests/DataAnnotationSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/DataAnnotationSingleStoreTest.cs
index 7479bbe84..cd8aab252 100644
--- a/test/EFCore.SingleStore.FunctionalTests/DataAnnotationSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/DataAnnotationSingleStoreTest.cs
@@ -1,3 +1,4 @@
+using System.Threading.Tasks;
using EntityFrameworkCore.SingleStore.FunctionalTests.TestUtilities;
using EntityFrameworkCore.SingleStore.Tests;
using Microsoft.EntityFrameworkCore;
@@ -110,29 +111,31 @@ public override IModel TableNameAttribute_affects_table_name_in_TPH()
return model;
}
- public override void TimestampAttribute_throws_if_value_in_database_changed()
+ public override Task TimestampAttribute_throws_if_value_in_database_changed()
{
// We're skipping this test when we're running tests on Managed Service due to the specifics of
// how AUTO_INCREMENT works (https://docs.singlestore.com/cloud/reference/sql-reference/data-definition-language-ddl/create-table/#auto-increment-behavior)
if (AppConfig.ManagedService)
{
- return;
+ return Task.CompletedTask;
}
- base.TimestampAttribute_throws_if_value_in_database_changed();
+
+ return base.TimestampAttribute_throws_if_value_in_database_changed();
}
- public override void Table_can_configure_TPT_with_Owned()
+ public override Task Table_can_configure_TPT_with_Owned()
{
// We're skipping this test when we're running tests on Managed Service due to the specifics of
// how AUTO_INCREMENT works (https://docs.singlestore.com/cloud/reference/sql-reference/data-definition-language-ddl/create-table/#auto-increment-behavior)
if (AppConfig.ManagedService)
{
- return;
+ return Task.CompletedTask;
}
- base.Table_can_configure_TPT_with_Owned();
+
+ return base.Table_can_configure_TPT_with_Owned();
}
- public override void ConcurrencyCheckAttribute_throws_if_value_in_database_changed()
+ public override async Task ConcurrencyCheckAttribute_throws_if_value_in_database_changed()
{
// We're skipping this test when we're running tests on Managed Service due to the specifics of
// how AUTO_INCREMENT works (https://docs.singlestore.com/cloud/reference/sql-reference/data-definition-language-ddl/create-table/#auto-increment-behavior)
@@ -140,7 +143,8 @@ public override void ConcurrencyCheckAttribute_throws_if_value_in_database_chang
{
return;
}
- base.ConcurrencyCheckAttribute_throws_if_value_in_database_changed();
+
+ await base.ConcurrencyCheckAttribute_throws_if_value_in_database_changed();
AssertSql(
"""
@@ -182,9 +186,9 @@ LIMIT 1
""");
}
- public override void DatabaseGeneratedAttribute_autogenerates_values_when_set_to_identity()
+ public override async Task DatabaseGeneratedAttribute_autogenerates_values_when_set_to_identity()
{
- base.DatabaseGeneratedAttribute_autogenerates_values_when_set_to_identity();
+ await base.DatabaseGeneratedAttribute_autogenerates_values_when_set_to_identity();
if (AppConfig.ServerVersion.Supports.Returning)
{
@@ -310,15 +314,15 @@ protected class TableWithCollation
}
[ConditionalFact(Skip = "SingleStore truncates the data if VARCHAR column is too short to store this data.")]
- public override void MaxLengthAttribute_throws_while_inserting_value_longer_than_max_length()
+ public override async Task MaxLengthAttribute_throws_while_inserting_value_longer_than_max_length()
{
- base.MaxLengthAttribute_throws_while_inserting_value_longer_than_max_length();
+ await base.MaxLengthAttribute_throws_while_inserting_value_longer_than_max_length();
}
[ConditionalFact(Skip = "SingleStore truncates the data if VARCHAR column is too short to store this data.")]
- public override void StringLengthAttribute_throws_while_inserting_value_longer_than_max_length()
+ public override async Task StringLengthAttribute_throws_while_inserting_value_longer_than_max_length()
{
- base.StringLengthAttribute_throws_while_inserting_value_longer_than_max_length();
+ await base.StringLengthAttribute_throws_while_inserting_value_longer_than_max_length();
}
protected static IMutableEntityType GetEntityType(ModelBuilder modelBuilder)
diff --git a/test/EFCore.SingleStore.FunctionalTests/EFCore.SingleStore.FunctionalTests.csproj b/test/EFCore.SingleStore.FunctionalTests/EFCore.SingleStore.FunctionalTests.csproj
index 453bdacbe..940479468 100644
--- a/test/EFCore.SingleStore.FunctionalTests/EFCore.SingleStore.FunctionalTests.csproj
+++ b/test/EFCore.SingleStore.FunctionalTests/EFCore.SingleStore.FunctionalTests.csproj
@@ -19,20 +19,21 @@
$(DefineConstants);SPECIFIC_TEST_ORDER
+
+
+
+
+
-
-
-
-
-
+
@@ -67,11 +68,17 @@
$(LocalEFCoreRepository)\artifacts\bin\EFCore.Design.Tests\$(LocalEFCoreRepositoryConfiguration)\$(EfCoreTestTargetFramework)\Microsoft.EntityFrameworkCore.Design.dll
+
+
+
+
+
+
+
-
-
+
diff --git a/test/EFCore.SingleStore.FunctionalTests/ExistingConnectionSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/ExistingConnectionSingleStoreTest.cs
index ffec5067f..3f99c4247 100644
--- a/test/EFCore.SingleStore.FunctionalTests/ExistingConnectionSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/ExistingConnectionSingleStoreTest.cs
@@ -23,16 +23,16 @@ private static async Task Can_use_an_existing_closed_connection(bool openConnect
.AddEntityFrameworkSingleStore()
.BuildServiceProvider();
- using (var store = (SingleStoreTestStore)SingleStoreNorthwindTestStoreFactory.Instance
+ await using (var store = (SingleStoreTestStore)await SingleStoreNorthwindTestStoreFactory.Instance
.GetOrCreate(null)
- .Initialize(null, (Func)null))
+ .InitializeAsync(null, (Func)null))
{
store.CloseConnection();
var openCount = 0;
var closeCount = 0;
- using (var connection = new SingleStoreConnection(store.ConnectionString))
+ await using (var connection = new SingleStoreConnection(store.ConnectionString))
{
if (openConnection)
{
@@ -56,7 +56,7 @@ private static async Task Can_use_an_existing_closed_connection(bool openConnect
.UseInternalServiceProvider(serviceProvider)
.Options;
- using (var context = new NorthwindContext(options))
+ await using (var context = new NorthwindContext(options))
{
Assert.Equal(91, await context.Customers.CountAsync());
}
@@ -84,9 +84,9 @@ private static async Task Closed_connection_missing_AllowUserVariables_true_in_o
.AddEntityFrameworkSingleStore()
.BuildServiceProvider();
- await using (var store = (SingleStoreTestStore)SingleStoreNorthwindTestStoreFactory.Instance
+ await using (var store = (SingleStoreTestStore)await SingleStoreNorthwindTestStoreFactory.Instance
.GetOrCreate(null)
- .Initialize(null, (Func)null))
+ .InitializeAsync(null, (Func)null))
{
store.CloseConnection();
@@ -99,7 +99,7 @@ private static async Task Closed_connection_missing_AllowUserVariables_true_in_o
.UseInternalServiceProvider(serviceProvider)
.Options;
- using var context = new NorthwindContext(options);
+ await using var context = new NorthwindContext(options);
Assert.Equal(91, await context.Customers.CountAsync());
}
@@ -109,9 +109,9 @@ private static async Task Closed_connection_missing_AllowUserVariables_true_in_o
[Fact]
private static async Task Opened_connection_missing_AllowUserVariables_true_in_original_connection_string_throws()
{
- await using (var store = (SingleStoreTestStore)SingleStoreNorthwindTestStoreFactory.Instance
+ await using (var store = (SingleStoreTestStore)await SingleStoreNorthwindTestStoreFactory.Instance
.GetOrCreate(null)
- .Initialize(null, (Func)null))
+ .InitializeAsync(null, (Func)null))
{
store.CloseConnection();
@@ -145,9 +145,9 @@ private static async Task Closed_connection_missing_UseAffectedRows_false_in_ori
.AddEntityFrameworkSingleStore()
.BuildServiceProvider();
- await using (var store = (SingleStoreTestStore)SingleStoreNorthwindTestStoreFactory.Instance
+ await using (var store = (SingleStoreTestStore)await SingleStoreNorthwindTestStoreFactory.Instance
.GetOrCreate(null)
- .Initialize(null, (Func)null))
+ .InitializeAsync(null, (Func)null))
{
store.CloseConnection();
@@ -160,7 +160,7 @@ private static async Task Closed_connection_missing_UseAffectedRows_false_in_ori
.UseInternalServiceProvider(serviceProvider)
.Options;
- using var context = new NorthwindContext(options);
+ await using var context = new NorthwindContext(options);
Assert.Equal(91, await context.Customers.CountAsync());
}
@@ -170,9 +170,9 @@ private static async Task Closed_connection_missing_UseAffectedRows_false_in_ori
[Fact]
private static async Task Opened_connection_missing_UseAffectedRows_false_in_original_connection_string_throws()
{
- await using (var store = (SingleStoreTestStore)SingleStoreNorthwindTestStoreFactory.Instance
+ await using (var store = (SingleStoreTestStore)await SingleStoreNorthwindTestStoreFactory.Instance
.GetOrCreate(null)
- .Initialize(null, (Func)null))
+ .InitializeAsync(null, (Func)null))
{
store.CloseConnection();
diff --git a/test/EFCore.SingleStore.FunctionalTests/FullMigrationsSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/FullMigrationsSingleStoreTest.cs
index a82d0d344..46978296e 100644
--- a/test/EFCore.SingleStore.FunctionalTests/FullMigrationsSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/FullMigrationsSingleStoreTest.cs
@@ -3,6 +3,7 @@
using System.Text.RegularExpressions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Microsoft.Extensions.DependencyInjection;
@@ -48,15 +49,17 @@ public virtual void Can_create_stored_procedure_script_without_custom_delimiter_
fromMigration: Migration.InitialDatabase,
toMigration: "00000000000002_MigrationPrimaryKeyChange2"));
+ // TODO: 9.0
+ // Pomelo helper stored procedure statements should be inside the transaction scope.
Assert.Equal(
- @"CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` (
+"""
+CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` (
`MigrationId` varchar(150) CHARACTER SET utf8mb4 NOT NULL,
`ProductVersion` varchar(32) CHARACTER SET utf8mb4 NOT NULL,
CONSTRAINT `PK___EFMigrationsHistory` PRIMARY KEY (`MigrationId`)
) CHARACTER SET=utf8mb4;
START TRANSACTION;
-
CREATE TABLE `Table1` (
`Id` int NOT NULL,
`AlternatePK` int NOT NULL,
@@ -66,10 +69,6 @@ public virtual void Can_create_stored_procedure_script_without_custom_delimiter_
INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
VALUES ('00000000000001_MigrationPrimaryKeyChange1', '7.0.0-test');
-COMMIT;
-
-START TRANSACTION;
-
DROP PROCEDURE IF EXISTS `POMELO_BEFORE_DROP_PRIMARY_KEY`;
CREATE PROCEDURE `POMELO_BEFORE_DROP_PRIMARY_KEY`(IN `SCHEMA_NAME_ARGUMENT` VARCHAR(255), IN `TABLE_NAME_ARGUMENT` VARCHAR(255))
BEGIN
@@ -159,7 +158,8 @@ INTO PRIMARY_KEY_COLUMN_NAME
DROP PROCEDURE `POMELO_AFTER_ADD_PRIMARY_KEY`;
COMMIT;
-",
+
+""",
Sql,
ignoreLineEndingDifferences: true);
}
@@ -188,7 +188,8 @@ public virtual void Can_generate_idempotent_up_scripts_with_primary_key_related_
h => Assert.Equal("00000000000002_MigrationPrimaryKeyChange2", h.MigrationId));
Assert.Equal(
- @"DROP PROCEDURE IF EXISTS `POMELO_BEFORE_DROP_PRIMARY_KEY`;
+"""
+DROP PROCEDURE IF EXISTS `POMELO_BEFORE_DROP_PRIMARY_KEY`;
DELIMITER //
CREATE PROCEDURE `POMELO_BEFORE_DROP_PRIMARY_KEY`(IN `SCHEMA_NAME_ARGUMENT` VARCHAR(255), IN `TABLE_NAME_ARGUMENT` VARCHAR(255))
BEGIN
@@ -277,7 +278,6 @@ INTO PRIMARY_KEY_COLUMN_NAME
) CHARACTER SET=utf8mb4;
START TRANSACTION;
-
DROP PROCEDURE IF EXISTS MigrationsScript;
DELIMITER //
CREATE PROCEDURE MigrationsScript()
@@ -311,10 +311,6 @@ IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000
CALL MigrationsScript();
DROP PROCEDURE MigrationsScript;
-COMMIT;
-
-START TRANSACTION;
-
DROP PROCEDURE IF EXISTS MigrationsScript;
DELIMITER //
CREATE PROCEDURE MigrationsScript()
@@ -366,7 +362,134 @@ IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000
DROP PROCEDURE `POMELO_AFTER_ADD_PRIMARY_KEY`;
-",
+""",
+ Sql,
+ ignoreLineEndingDifferences: true);
+ }
+
+ [ConditionalFact]
+ public virtual void Alter_column_change_primary_key_will_not_try_to_declare_default_value_in_sql()
+ {
+ using var db = Fixture.CreateContext(
+ new ServiceCollection()
+ .AddScoped());
+
+ db.Database.EnsureDeleted();
+ db.Database.EnsureCreated();
+
+ var migrator = (SingleStoreTestMigrator)db.GetService();
+ migrator.MigrationsSqlGenerationOptionsOverrider = options => options & ~MigrationsSqlGenerationOptions.Script;
+
+ SetSql(migrator.GenerateScript());
+
+ //Assert.False(Sql.Contains("DEFAULT 0"));
+ Assert.Equal(
+"""
+CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` (
+ `MigrationId` varchar(150) CHARACTER SET utf8mb4 NOT NULL,
+ `ProductVersion` varchar(32) CHARACTER SET utf8mb4 NOT NULL,
+ CONSTRAINT `PK___EFMigrationsHistory` PRIMARY KEY (`MigrationId`)
+) CHARACTER SET=utf8mb4;
+
+START TRANSACTION;
+CREATE TABLE `IceCreams` (
+ `Name` varchar(32) NOT NULL,
+ CONSTRAINT `PK_IceCreams` PRIMARY KEY (`Name`)
+);
+
+INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
+VALUES ('00000000000001_Migration1', '7.0.0-test');
+
+DROP PROCEDURE IF EXISTS `POMELO_BEFORE_DROP_PRIMARY_KEY`;
+CREATE PROCEDURE `POMELO_BEFORE_DROP_PRIMARY_KEY`(IN `SCHEMA_NAME_ARGUMENT` VARCHAR(255), IN `TABLE_NAME_ARGUMENT` VARCHAR(255))
+BEGIN
+ DECLARE HAS_AUTO_INCREMENT_ID TINYINT(1);
+ DECLARE PRIMARY_KEY_COLUMN_NAME VARCHAR(255);
+ DECLARE PRIMARY_KEY_TYPE VARCHAR(255);
+ DECLARE SQL_EXP VARCHAR(1000);
+ SELECT COUNT(*)
+ INTO HAS_AUTO_INCREMENT_ID
+ FROM `information_schema`.`COLUMNS`
+ WHERE `TABLE_SCHEMA` = (SELECT IFNULL(SCHEMA_NAME_ARGUMENT, SCHEMA()))
+ AND `TABLE_NAME` = TABLE_NAME_ARGUMENT
+ AND `Extra` = 'auto_increment'
+ AND `COLUMN_KEY` = 'PRI'
+ LIMIT 1;
+ IF HAS_AUTO_INCREMENT_ID THEN
+ SELECT `COLUMN_TYPE`
+ INTO PRIMARY_KEY_TYPE
+ FROM `information_schema`.`COLUMNS`
+ WHERE `TABLE_SCHEMA` = (SELECT IFNULL(SCHEMA_NAME_ARGUMENT, SCHEMA()))
+ AND `TABLE_NAME` = TABLE_NAME_ARGUMENT
+ AND `COLUMN_KEY` = 'PRI'
+ LIMIT 1;
+ SELECT `COLUMN_NAME`
+ INTO PRIMARY_KEY_COLUMN_NAME
+ FROM `information_schema`.`COLUMNS`
+ WHERE `TABLE_SCHEMA` = (SELECT IFNULL(SCHEMA_NAME_ARGUMENT, SCHEMA()))
+ AND `TABLE_NAME` = TABLE_NAME_ARGUMENT
+ AND `COLUMN_KEY` = 'PRI'
+ LIMIT 1;
+ SET SQL_EXP = CONCAT('ALTER TABLE `', (SELECT IFNULL(SCHEMA_NAME_ARGUMENT, SCHEMA())), '`.`', TABLE_NAME_ARGUMENT, '` MODIFY COLUMN `', PRIMARY_KEY_COLUMN_NAME, '` ', PRIMARY_KEY_TYPE, ' NOT NULL;');
+ SET @SQL_EXP = SQL_EXP;
+ PREPARE SQL_EXP_EXECUTE FROM @SQL_EXP;
+ EXECUTE SQL_EXP_EXECUTE;
+ DEALLOCATE PREPARE SQL_EXP_EXECUTE;
+ END IF;
+END;
+DROP PROCEDURE IF EXISTS `POMELO_AFTER_ADD_PRIMARY_KEY`;
+CREATE PROCEDURE `POMELO_AFTER_ADD_PRIMARY_KEY`(IN `SCHEMA_NAME_ARGUMENT` VARCHAR(255), IN `TABLE_NAME_ARGUMENT` VARCHAR(255), IN `COLUMN_NAME_ARGUMENT` VARCHAR(255))
+BEGIN
+ DECLARE HAS_AUTO_INCREMENT_ID INT(11);
+ DECLARE PRIMARY_KEY_COLUMN_NAME VARCHAR(255);
+ DECLARE PRIMARY_KEY_TYPE VARCHAR(255);
+ DECLARE SQL_EXP VARCHAR(1000);
+ SELECT COUNT(*)
+ INTO HAS_AUTO_INCREMENT_ID
+ FROM `information_schema`.`COLUMNS`
+ WHERE `TABLE_SCHEMA` = (SELECT IFNULL(SCHEMA_NAME_ARGUMENT, SCHEMA()))
+ AND `TABLE_NAME` = TABLE_NAME_ARGUMENT
+ AND `COLUMN_NAME` = COLUMN_NAME_ARGUMENT
+ AND `COLUMN_TYPE` LIKE '%int%'
+ AND `COLUMN_KEY` = 'PRI';
+ IF HAS_AUTO_INCREMENT_ID THEN
+ SELECT `COLUMN_TYPE`
+ INTO PRIMARY_KEY_TYPE
+ FROM `information_schema`.`COLUMNS`
+ WHERE `TABLE_SCHEMA` = (SELECT IFNULL(SCHEMA_NAME_ARGUMENT, SCHEMA()))
+ AND `TABLE_NAME` = TABLE_NAME_ARGUMENT
+ AND `COLUMN_NAME` = COLUMN_NAME_ARGUMENT
+ AND `COLUMN_TYPE` LIKE '%int%'
+ AND `COLUMN_KEY` = 'PRI';
+ SELECT `COLUMN_NAME`
+ INTO PRIMARY_KEY_COLUMN_NAME
+ FROM `information_schema`.`COLUMNS`
+ WHERE `TABLE_SCHEMA` = (SELECT IFNULL(SCHEMA_NAME_ARGUMENT, SCHEMA()))
+ AND `TABLE_NAME` = TABLE_NAME_ARGUMENT
+ AND `COLUMN_NAME` = COLUMN_NAME_ARGUMENT
+ AND `COLUMN_TYPE` LIKE '%int%'
+ AND `COLUMN_KEY` = 'PRI';
+ SET SQL_EXP = CONCAT('ALTER TABLE `', (SELECT IFNULL(SCHEMA_NAME_ARGUMENT, SCHEMA())), '`.`', TABLE_NAME_ARGUMENT, '` MODIFY COLUMN `', PRIMARY_KEY_COLUMN_NAME, '` ', PRIMARY_KEY_TYPE, ' NOT NULL AUTO_INCREMENT;');
+ SET @SQL_EXP = SQL_EXP;
+ PREPARE SQL_EXP_EXECUTE FROM @SQL_EXP;
+ EXECUTE SQL_EXP_EXECUTE;
+ DEALLOCATE PREPARE SQL_EXP_EXECUTE;
+ END IF;
+END;
+CALL POMELO_BEFORE_DROP_PRIMARY_KEY(NULL, 'IceCreams');
+ALTER TABLE `IceCreams` DROP PRIMARY KEY;
+
+ALTER TABLE `IceCreams` ADD `IceCreamId` int NOT NULL AUTO_INCREMENT;
+
+INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
+VALUES ('00000000000002_Migration2', '7.0.0-test');
+
+DROP PROCEDURE `POMELO_BEFORE_DROP_PRIMARY_KEY`;
+DROP PROCEDURE `POMELO_AFTER_ADD_PRIMARY_KEY`;
+COMMIT;
+
+
+""",
Sql,
ignoreLineEndingDifferences: true);
}
@@ -394,14 +517,14 @@ public virtual void Drop_primary_key_with_recreating_foreign_keys()
h => Assert.Equal("00000000000002_MigrationDropPrimaryKeyWithRecreatingForeignKeys2", h.MigrationId));
Assert.Equal(
- @"CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` (
+"""
+CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` (
`MigrationId` varchar(150) CHARACTER SET utf8mb4 NOT NULL,
`ProductVersion` varchar(32) CHARACTER SET utf8mb4 NOT NULL,
CONSTRAINT `PK___EFMigrationsHistory` PRIMARY KEY (`MigrationId`)
) CHARACTER SET=utf8mb4;
START TRANSACTION;
-
CREATE TABLE `Foo` (
`FooId` int NOT NULL,
CONSTRAINT `PK_Foo` PRIMARY KEY (`FooId`)
@@ -423,10 +546,6 @@ public virtual void Drop_primary_key_with_recreating_foreign_keys()
INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
VALUES ('00000000000001_MigrationDropPrimaryKeyWithRecreatingForeignKeys1', '7.0.0-test');
-COMMIT;
-
-START TRANSACTION;
-
DROP PROCEDURE IF EXISTS `POMELO_BEFORE_DROP_PRIMARY_KEY`;
DELIMITER //
CREATE PROCEDURE `POMELO_BEFORE_DROP_PRIMARY_KEY`(IN `SCHEMA_NAME_ARGUMENT` VARCHAR(255), IN `TABLE_NAME_ARGUMENT` VARCHAR(255))
@@ -533,7 +652,7 @@ INTO PRIMARY_KEY_COLUMN_NAME
COMMIT;
-",
+""",
Sql,
ignoreLineEndingDifferences: true);
}
@@ -656,6 +775,83 @@ protected override void Down(MigrationBuilder migrationBuilder)
#endregion MigrationPrimaryKeyChange
+ public class MigrationPrimaryKeyChangeFromStringToIntContext : DbContext
+ {
+ public MigrationPrimaryKeyChangeFromStringToIntContext(DbContextOptions options)
+ : base(options)
+ {
+ }
+
+ public static class Migrations
+ {
+ [DbContext(typeof(MigrationPrimaryKeyChangeFromStringToIntContext))]
+ [Migration("00000000000001_Migration1")]
+ private class Migration1 : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ MigrationsInfrastructureFixtureBase.ActiveProvider = migrationBuilder.ActiveProvider;
+
+ migrationBuilder
+ .CreateTable(
+ name: "IceCreams",
+ columns: table => new
+ {
+ Name = table.Column(type: "varchar(32)", maxLength: 32, nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_IceCreams", x => x.Name);
+ });
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ => migrationBuilder.DropTable("IceCreams");
+ }
+
+ [DbContext(typeof(MigrationPrimaryKeyChangeFromStringToIntContext))]
+ [Migration("00000000000002_Migration2")]
+ private class Migration2 : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropPrimaryKey(
+ name: "PK_IceCreams",
+ table: "IceCreams");
+
+ migrationBuilder.AddColumn(
+ name: "IceCreamId",
+ table: "IceCreams",
+ type: "int",
+ nullable: false,
+ defaultValue: 0)
+ .Annotation("MySql:ValueGenerationStrategy", SingleStoreValueGenerationStrategy.IdentityColumn);
+
+ migrationBuilder.AddPrimaryKey(
+ name: "PK_IceCreams",
+ table: "IceCreams",
+ column: "IceCreamId");
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropPrimaryKey(
+ name: "PK_IceCreams",
+ table: "IceCreams");
+
+ migrationBuilder.DropColumn(
+ name: "IceCreamId",
+ table: "IceCreams");
+
+ migrationBuilder.AddPrimaryKey(
+ name: "PK_IceCreams",
+ table: "IceCreams",
+ column: "Name");
+ }
+ }
+ }
+ }
+
#region MigrationDropPrimaryKeyWithRecreatingForeignKeys
public class MigrationDropPrimaryKeyWithRecreatingForeignKeysContext : PoolableDbContext
diff --git a/test/EFCore.SingleStore.FunctionalTests/GraphUpdatesSingleStoreTestBase.cs b/test/EFCore.SingleStore.FunctionalTests/GraphUpdatesSingleStoreTestBase.cs
index 93731f89f..a71432e7a 100644
--- a/test/EFCore.SingleStore.FunctionalTests/GraphUpdatesSingleStoreTestBase.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/GraphUpdatesSingleStoreTestBase.cs
@@ -63,6 +63,24 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
modelBuilder.Entity().Property("CategoryId").HasDefaultValue(1);
modelBuilder.Entity().Property(e => e.CategoryId).HasDefaultValue(2);
+
+ modelBuilder.Entity>(
+ b =>
+ {
+ b.Property(e => e.PrimaryGroup).HasDefaultValue(1).HasSentinel(1);
+ });
+
+ modelBuilder.Entity>(
+ b =>
+ {
+ b.Property(e => e.PrimaryGroup).HasDefaultValue(true);
+ });
+
+ modelBuilder.Entity>(
+ b =>
+ {
+ b.Property(e => e.PrimaryGroup).HasDefaultValue(true);
+ });
}
}
}
diff --git a/test/EFCore.SingleStore.FunctionalTests/ManyToManyLoadSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/ManyToManyLoadSingleStoreTest.cs
index 9a0f90c71..00860a048 100644
--- a/test/EFCore.SingleStore.FunctionalTests/ManyToManyLoadSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/ManyToManyLoadSingleStoreTest.cs
@@ -14,7 +14,7 @@ public ManyToManyLoadSingleStoreTest(ManyToManyLoadSingleStoreFixture fixture)
{
}
- public class ManyToManyLoadSingleStoreFixture : ManyToManyLoadFixtureBase
+ public class ManyToManyLoadSingleStoreFixture : ManyToManyLoadFixtureBase, ITestSqlLoggerFactory
{
public TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ListLoggerFactory;
protected override ITestStoreFactory TestStoreFactory => SingleStoreTestStoreFactory.Instance;
diff --git a/test/EFCore.SingleStore.FunctionalTests/ManyToManyTrackingSingleStoreTestBase.cs b/test/EFCore.SingleStore.FunctionalTests/ManyToManyTrackingSingleStoreTestBase.cs
index 61472a5c6..ea97f0dac 100644
--- a/test/EFCore.SingleStore.FunctionalTests/ManyToManyTrackingSingleStoreTestBase.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/ManyToManyTrackingSingleStoreTestBase.cs
@@ -26,8 +26,10 @@ protected ManyToManyTrackingSingleStoreTestBase(TFixture fixture)
protected override void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
=> facade.UseTransaction(transaction.GetDbTransaction());
- public class ManyToManyTrackingSingleStoreFixtureBase : ManyToManyTrackingRelationalFixture
+ public class ManyToManyTrackingSingleStoreFixtureBase : ManyToManyTrackingRelationalFixture, ITestSqlLoggerFactory
{
+ public TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ListLoggerFactory;
+
protected override ITestStoreFactory TestStoreFactory => SingleStoreTestStoreFactory.Instance;
protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context)
diff --git a/test/EFCore.SingleStore.FunctionalTests/MaterializationInterceptionSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/MaterializationInterceptionSingleStoreTest.cs
index 22aebb204..2d552564d 100644
--- a/test/EFCore.SingleStore.FunctionalTests/MaterializationInterceptionSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/MaterializationInterceptionSingleStoreTest.cs
@@ -1,28 +1,21 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Diagnostics;
-using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.TestUtilities;
-using Microsoft.Extensions.DependencyInjection;
using EntityFrameworkCore.SingleStore.FunctionalTests.TestUtilities;
using EntityFrameworkCore.SingleStore.Storage.Internal;
using EntityFrameworkCore.SingleStore.Tests;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace EntityFrameworkCore.SingleStore.FunctionalTests;
-public class MaterializationInterceptionSingleStoreTest :
- MaterializationInterceptionTestBase,
- IClassFixture
+public class MaterializationInterceptionSingleStoreTest : MaterializationInterceptionTestBase
{
- public MaterializationInterceptionSingleStoreTest(MaterializationInterceptionSingleStoreFixture fixture)
- : base(fixture)
- {
- }
-
[ConditionalTheory]
- public override async Task Intercept_query_materialization_with_owned_types_projecting_collection(bool async)
+ public override async Task Intercept_query_materialization_with_owned_types_projecting_collection(bool async, bool usePooling)
{
// We're skipping this test when we're running tests on Managed Service due to the specifics of
// how AUTO_INCREMENT works (https://docs.singlestore.com/cloud/reference/sql-reference/data-definition-language-ddl/create-table/#auto-increment-behavior)
@@ -31,7 +24,7 @@ public override async Task Intercept_query_materialization_with_owned_types_proj
return;
}
- await base.Intercept_query_materialization_with_owned_types_projecting_collection(async);
+ await base.Intercept_query_materialization_with_owned_types_projecting_collection(async, usePooling);
}
public class SingleStoreLibraryContext : LibraryContext
@@ -64,27 +57,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
}
}
- public override LibraryContext CreateContext(IEnumerable interceptors, bool inject)
- => new SingleStoreLibraryContext(Fixture.CreateOptions(interceptors, inject));
-
- public class MaterializationInterceptionSingleStoreFixture : SingletonInterceptorsFixtureBase
- {
- protected override string StoreName
- => "MaterializationInterception";
-
- protected override ITestStoreFactory TestStoreFactory
- => SingleStoreTestStoreFactory.Instance;
-
- protected override IServiceCollection InjectInterceptors(
- IServiceCollection serviceCollection,
- IEnumerable injectedInterceptors)
- => base.InjectInterceptors(serviceCollection.AddEntityFrameworkSingleStore(), injectedInterceptors);
-
- public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
- {
- new SingleStoreDbContextOptionsBuilder(base.AddOptions(builder))
- .ExecutionStrategy(d => new SingleStoreExecutionStrategy(d));
- return builder;
- }
- }
+ protected override ITestStoreFactory TestStoreFactory
+ => SingleStoreTestStoreFactory.Instance;
}
diff --git a/test/EFCore.SingleStore.FunctionalTests/MigrationsInfrastructureSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/MigrationsInfrastructureSingleStoreTest.cs
index d09bdcdda..359dc06ef 100644
--- a/test/EFCore.SingleStore.FunctionalTests/MigrationsInfrastructureSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/MigrationsInfrastructureSingleStoreTest.cs
@@ -52,140 +52,44 @@ public override void Can_generate_no_migration_script()
ignoreLineEndingDifferences: true);
}
- public override void Can_generate_up_scripts()
+ public override void Can_apply_one_migration()
{
- base.Can_generate_up_scripts();
-
- Assert.Equal(
-"""
-CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` (
- `MigrationId` varchar(150) CHARACTER SET utf8mb4 NOT NULL,
- `ProductVersion` varchar(32) CHARACTER SET utf8mb4 NOT NULL,
- CONSTRAINT `PK___EFMigrationsHistory` PRIMARY KEY (`MigrationId`)
-) CHARACTER SET=utf8mb4;
-
-START TRANSACTION;
-
-CREATE TABLE `Table1` (
- `Id` int NOT NULL,
- `Foo` int NOT NULL,
- `Description` longtext NOT NULL,
- CONSTRAINT `PK_Table1` PRIMARY KEY (`Id`)
-);
-
-INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000001_Migration1', '7.0.0-test');
-
-COMMIT;
-
-START TRANSACTION;
-
-ALTER TABLE `Table1` CHANGE `Foo` `Bar`;
-
-INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000002_Migration2', '7.0.0-test');
-
-COMMIT;
-
-START TRANSACTION;
-
-INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000003_Migration3', '7.0.0-test');
-
-COMMIT;
-
-START TRANSACTION;
-
-INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000004_Migration4', '7.0.0-test');
-
-COMMIT;
-
-START TRANSACTION;
-
-INSERT INTO Table1 (Id, Bar, Description) VALUES (-1, ' ', 'Value With
-
-Empty Lines')
-
-INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000005_Migration5', '7.0.0-test');
-
-COMMIT;
-
-START TRANSACTION;
-
-INSERT INTO Table1 (Id, Bar, Description) VALUES (-2, ' ', 'GO
-Value With
-
-Empty Lines')
-
-INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000006_Migration6', '7.0.0-test');
-
-COMMIT;
-
-START TRANSACTION;
-
-INSERT INTO Table1 (Id, Bar, Description) VALUES (-3, ' ', 'GO
-Value With
-
-GO
-
-Empty Lines
-GO')
-
-INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000007_Migration7', '7.0.0-test');
-
-COMMIT;
-
+ base.Can_apply_one_migration();
-""",
- Sql,
- ignoreLineEndingDifferences: true);
+ Assert.Null(Sql);
}
- public override void Can_generate_one_up_script()
+ public override void Can_apply_one_migration_in_parallel()
{
- base.Can_generate_one_up_script();
-
- Assert.Equal(
- @"START TRANSACTION;
+ base.Can_apply_one_migration_in_parallel();
-ALTER TABLE `Table1` CHANGE `Foo` `Bar`;
-
-INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000002_Migration2', '7.0.0-test');
-
-COMMIT;
-
-",
- Sql,
- ignoreLineEndingDifferences: true);
+ Assert.Null(Sql);
}
- public override void Can_generate_up_script_using_names()
+ public override void Can_apply_second_migration_in_parallel()
{
- base.Can_generate_up_script_using_names();
+ base.Can_apply_second_migration_in_parallel();
- Assert.Equal(
- @"START TRANSACTION;
+ Assert.Null(Sql);
+ }
-ALTER TABLE `Table1` CHANGE `Foo` `Bar`;
+ public override async Task Can_apply_one_migration_in_parallel_async()
+ {
+ await base.Can_apply_one_migration_in_parallel_async();
-INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000002_Migration2', '7.0.0-test');
+ Assert.Null(Sql);
+ }
-COMMIT;
+ public override async Task Can_apply_second_migration_in_parallel_async()
+ {
+ await base.Can_apply_second_migration_in_parallel_async();
-",
- Sql,
- ignoreLineEndingDifferences: true);
+ Assert.Null(Sql);
}
- public override void Can_generate_idempotent_up_scripts()
+ public override async Task Can_generate_up_and_down_scripts()
{
- base.Can_generate_idempotent_up_scripts();
+ await base.Can_generate_up_and_down_scripts();
Assert.Equal(
"""
@@ -196,382 +100,75 @@ public override void Can_generate_idempotent_up_scripts()
) CHARACTER SET=utf8mb4;
START TRANSACTION;
+CREATE TABLE `Table1` (
+ `Id` int NOT NULL,
+ `Foo` int NOT NULL,
+ `Description` longtext NOT NULL,
+ CONSTRAINT `PK_Table1` PRIMARY KEY (`Id`)
+);
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000001_Migration1') THEN
-
- CREATE TABLE `Table1` (
- `Id` int NOT NULL,
- `Foo` int NOT NULL,
- `Description` longtext NOT NULL,
- CONSTRAINT `PK_Table1` PRIMARY KEY (`Id`)
- );
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000001_Migration1') THEN
-
- INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000001_Migration1', '7.0.0-test');
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-COMMIT;
-
-START TRANSACTION;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000002_Migration2') THEN
-
- ALTER TABLE `Table1` CHANGE `Foo` `Bar`;
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000002_Migration2') THEN
-
- INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000002_Migration2', '7.0.0-test');
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-COMMIT;
-
-START TRANSACTION;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000003_Migration3') THEN
-
- INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000003_Migration3', '7.0.0-test');
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-COMMIT;
-
-START TRANSACTION;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000004_Migration4') THEN
-
- INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000004_Migration4', '7.0.0-test');
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-COMMIT;
-
-START TRANSACTION;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000005_Migration5') THEN
-
- INSERT INTO Table1 (Id, Bar, Description) VALUES (-1, ' ', 'Value With
-
- Empty Lines')
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000005_Migration5') THEN
-
- INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000005_Migration5', '7.0.0-test');
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-COMMIT;
-
-START TRANSACTION;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000006_Migration6') THEN
-
- INSERT INTO Table1 (Id, Bar, Description) VALUES (-2, ' ', 'GO
- Value With
-
- Empty Lines')
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000006_Migration6') THEN
-
- INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000006_Migration6', '7.0.0-test');
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-COMMIT;
-
-START TRANSACTION;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000007_Migration7') THEN
-
- INSERT INTO Table1 (Id, Bar, Description) VALUES (-3, ' ', 'GO
- Value With
-
- GO
-
- Empty Lines
- GO')
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000007_Migration7') THEN
-
- INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000007_Migration7', '7.0.0-test');
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-COMMIT;
-
-
-""",
- Sql,
- ignoreLineEndingDifferences: true);
- }
-
- public override void Can_generate_down_scripts()
- {
- base.Can_generate_down_scripts();
-
- Assert.Equal(
- @"START TRANSACTION;
-
-ALTER TABLE `Table1` CHANGE `Bar` `Foo`;
-
-DELETE FROM `__EFMigrationsHistory`
-WHERE `MigrationId` = '00000000000002_Migration2';
-
-COMMIT;
-
-START TRANSACTION;
-
-DROP TABLE `Table1`;
-
-DELETE FROM `__EFMigrationsHistory`
-WHERE `MigrationId` = '00000000000001_Migration1';
-
-COMMIT;
-
-",
- Sql,
- ignoreLineEndingDifferences: true);
- }
-
- public override void Can_generate_one_down_script()
- {
- base.Can_generate_one_down_script();
-
- Assert.Equal(
- @"START TRANSACTION;
-
-ALTER TABLE `Table1` CHANGE `Bar` `Foo`;
-
-DELETE FROM `__EFMigrationsHistory`
-WHERE `MigrationId` = '00000000000002_Migration2';
-
-COMMIT;
-
-",
- Sql,
- ignoreLineEndingDifferences: true);
- }
-
- public override void Can_generate_down_script_using_names()
- {
- base.Can_generate_down_script_using_names();
-
- Assert.Equal(
- @"START TRANSACTION;
-
-ALTER TABLE `Table1` CHANGE `Bar` `Foo`;
-
-DELETE FROM `__EFMigrationsHistory`
-WHERE `MigrationId` = '00000000000002_Migration2';
-
-COMMIT;
-
-",
- Sql,
- ignoreLineEndingDifferences: true);
- }
-
- public override void Can_generate_idempotent_down_scripts()
- {
- base.Can_generate_idempotent_down_scripts();
+INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
+VALUES ('00000000000001_Migration1', '7.0.0-test');
- Assert.Equal(
- @"START TRANSACTION;
+ALTER TABLE `Table1` CHANGE `Foo` `Bar`;
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000002_Migration2') THEN
+INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
+VALUES ('00000000000002_Migration2', '7.0.0-test');
- ALTER TABLE `Table1` CHANGE `Bar` `Foo`;
+INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
+VALUES ('00000000000003_Migration3', '7.0.0-test');
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
+INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
+VALUES ('00000000000004_Migration4', '7.0.0-test');
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000002_Migration2') THEN
+INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
+VALUES ('00000000000005_Migration5', '7.0.0-test');
- DELETE FROM `__EFMigrationsHistory`
- WHERE `MigrationId` = '00000000000002_Migration2';
+INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
+VALUES ('00000000000006_Migration6', '7.0.0-test');
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
+INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
+VALUES ('00000000000007_Migration7', '7.0.0-test');
COMMIT;
START TRANSACTION;
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000007_Migration7';
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000001_Migration1') THEN
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000006_Migration6';
- DROP TABLE `Table1`;
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000005_Migration5';
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000004_Migration4';
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000001_Migration1') THEN
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000003_Migration3';
- DELETE FROM `__EFMigrationsHistory`
- WHERE `MigrationId` = '00000000000001_Migration1';
+ALTER TABLE `Table1` CHANGE `Bar` `Foo`;
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000002_Migration2';
+
+DROP TABLE `Table1`;
+
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000001_Migration1';
COMMIT;
-",
+
+""",
Sql,
ignoreLineEndingDifferences: true);
}
- public override void Can_get_active_provider()
- {
- base.Can_get_active_provider();
-
- Assert.Equal("EntityFrameworkCore.SingleStore", ActiveProvider);
- }
-
- public override void Can_generate_up_scripts_noTransactions()
+ public override async Task Can_generate_up_and_down_scripts_noTransactions()
{
- base.Can_generate_up_scripts_noTransactions();
+ await base.Can_generate_up_and_down_scripts_noTransactions();
Assert.Equal(
"""
@@ -602,31 +199,95 @@ public override void Can_generate_up_scripts_noTransactions()
INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
VALUES ('00000000000004_Migration4', '7.0.0-test');
-INSERT INTO Table1 (Id, Bar, Description) VALUES (-1, ' ', 'Value With
+INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
+VALUES ('00000000000005_Migration5', '7.0.0-test');
-Empty Lines')
+INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
+VALUES ('00000000000006_Migration6', '7.0.0-test');
INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000005_Migration5', '7.0.0-test');
+VALUES ('00000000000007_Migration7', '7.0.0-test');
+
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000007_Migration7';
+
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000006_Migration6';
+
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000005_Migration5';
+
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000004_Migration4';
+
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000003_Migration3';
+
+ALTER TABLE `Table1` CHANGE `Bar` `Foo`;
+
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000002_Migration2';
-INSERT INTO Table1 (Id, Bar, Description) VALUES (-2, ' ', 'GO
-Value With
+DROP TABLE `Table1`;
+
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000001_Migration1';
+
+
+""",
+ Sql,
+ ignoreLineEndingDifferences: true);
+ }
+
+ public override async Task Can_generate_one_up_and_down_script()
+ {
+ await base.Can_generate_one_up_and_down_script();
-Empty Lines')
+ Assert.Equal(
+"""
+START TRANSACTION;
+ALTER TABLE `Table1` CHANGE `Foo` `Bar`;
INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000006_Migration6', '7.0.0-test');
+VALUES ('00000000000002_Migration2', '7.0.0-test');
+
+COMMIT;
+
+START TRANSACTION;
+ALTER TABLE `Table1` CHANGE `Bar` `Foo`;
+
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000002_Migration2';
+
+COMMIT;
-INSERT INTO Table1 (Id, Bar, Description) VALUES (-3, ' ', 'GO
-Value With
-GO
+""",
+ Sql,
+ ignoreLineEndingDifferences: true);
+ }
+
+ public override async Task Can_generate_up_and_down_script_using_names()
+ {
+ await base.Can_generate_up_and_down_script_using_names();
-Empty Lines
-GO')
+ Assert.Equal(
+"""
+START TRANSACTION;
+ALTER TABLE `Table1` CHANGE `Foo` `Bar`;
INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
-VALUES ('00000000000007_Migration7', '7.0.0-test');
+VALUES ('00000000000002_Migration2', '7.0.0-test');
+
+COMMIT;
+
+START TRANSACTION;
+ALTER TABLE `Table1` CHANGE `Bar` `Foo`;
+
+DELETE FROM `__EFMigrationsHistory`
+WHERE `MigrationId` = '00000000000002_Migration2';
+
+COMMIT;
""",
@@ -634,10 +295,11 @@ Empty Lines
ignoreLineEndingDifferences: true);
}
- public override void Can_generate_idempotent_up_scripts_noTransactions()
+ public override async Task Can_generate_idempotent_up_and_down_scripts()
{
- base.Can_generate_idempotent_up_scripts_noTransactions();
+ var exception = await Assert.ThrowsAsync(() => base.Can_generate_idempotent_up_and_down_scripts());
+ Assert.Equal("'DELIMITER' should not be used with SingleStoreConnector. See https://mysqlconnector.net/delimiter", exception.Message);
Assert.Equal(
"""
CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` (
@@ -646,6 +308,7 @@ public override void Can_generate_idempotent_up_scripts_noTransactions()
CONSTRAINT `PK___EFMigrationsHistory` PRIMARY KEY (`MigrationId`)
) CHARACTER SET=utf8mb4;
+START TRANSACTION;
DROP PROCEDURE IF EXISTS MigrationsScript;
DELIMITER //
CREATE PROCEDURE MigrationsScript()
@@ -709,77 +372,39 @@ IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000
CALL MigrationsScript();
DROP PROCEDURE MigrationsScript;
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000003_Migration3') THEN
-
- INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000003_Migration3', '7.0.0-test');
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000004_Migration4') THEN
-
- INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000004_Migration4', '7.0.0-test');
-
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000005_Migration5') THEN
-
- INSERT INTO Table1 (Id, Bar, Description) VALUES (-1, ' ', 'Value With
-
- Empty Lines')
+COMMIT;
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
-DROP PROCEDURE IF EXISTS MigrationsScript;
-DELIMITER //
-CREATE PROCEDURE MigrationsScript()
-BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000005_Migration5') THEN
+""",
+ Sql,
+ ignoreLineEndingDifferences: true);
+ }
- INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000005_Migration5', '7.0.0-test');
+ public override async Task Can_generate_idempotent_up_and_down_scripts_noTransactions()
+ {
+ var exception = await Assert.ThrowsAsync(() => base.Can_generate_idempotent_up_and_down_scripts_noTransactions());
- END IF;
-END //
-DELIMITER ;
-CALL MigrationsScript();
-DROP PROCEDURE MigrationsScript;
+ Assert.Equal("'DELIMITER' should not be used with SingleStoreConnector. See https://mysqlconnector.net/delimiter", exception.Message);
+ Assert.Equal(
+"""
+CREATE TABLE IF NOT EXISTS `__EFMigrationsHistory` (
+ `MigrationId` varchar(150) CHARACTER SET utf8mb4 NOT NULL,
+ `ProductVersion` varchar(32) CHARACTER SET utf8mb4 NOT NULL,
+ CONSTRAINT `PK___EFMigrationsHistory` PRIMARY KEY (`MigrationId`)
+) CHARACTER SET=utf8mb4;
DROP PROCEDURE IF EXISTS MigrationsScript;
DELIMITER //
CREATE PROCEDURE MigrationsScript()
BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000006_Migration6') THEN
-
- INSERT INTO Table1 (Id, Bar, Description) VALUES (-2, ' ', 'GO
- Value With
+ IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000001_Migration1') THEN
- Empty Lines')
+ CREATE TABLE `Table1` (
+ `Id` int NOT NULL,
+ `Foo` int NOT NULL,
+ `Description` longtext NOT NULL,
+ CONSTRAINT `PK_Table1` PRIMARY KEY (`Id`)
+ );
END IF;
END //
@@ -791,10 +416,10 @@ Value With
DELIMITER //
CREATE PROCEDURE MigrationsScript()
BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000006_Migration6') THEN
+ IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000001_Migration1') THEN
INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000006_Migration6', '7.0.0-test');
+ VALUES ('00000000000001_Migration1', '7.0.0-test');
END IF;
END //
@@ -806,15 +431,9 @@ IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000
DELIMITER //
CREATE PROCEDURE MigrationsScript()
BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000007_Migration7') THEN
-
- INSERT INTO Table1 (Id, Bar, Description) VALUES (-3, ' ', 'GO
- Value With
-
- GO
+ IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000002_Migration2') THEN
- Empty Lines
- GO')
+ ALTER TABLE `Table1` CHANGE `Foo` `Bar`;
END IF;
END //
@@ -826,10 +445,10 @@ Empty Lines
DELIMITER //
CREATE PROCEDURE MigrationsScript()
BEGIN
- IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000007_Migration7') THEN
+ IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000000000002_Migration2') THEN
INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
- VALUES ('00000000000007_Migration7', '7.0.0-test');
+ VALUES ('00000000000002_Migration2', '7.0.0-test');
END IF;
END //
@@ -843,17 +462,13 @@ IF NOT EXISTS(SELECT 1 FROM `__EFMigrationsHistory` WHERE `MigrationId` = '00000
ignoreLineEndingDifferences: true);
}
- public override void Can_apply_one_migration()
+ public override void Can_get_active_provider()
{
- base.Can_apply_one_migration();
+ base.Can_get_active_provider();
- Assert.Null(Sql);
+ Assert.Equal("EntityFrameworkCore.SingleStore", ActiveProvider);
}
- // Issue https://github.com/dotnet/efcore/issues/33331 isn’t applicable in our CI runs because the
- // test environment sets `data_conversion_compatibility_level = '6.0'`, which avoids the overflow
- // behavior the tests were asserting.
-
[ConditionalFact(Skip = "TODO: Implement")]
public override void Can_diff_against_2_2_model()
{
@@ -878,6 +493,44 @@ public override void Can_diff_against_2_1_ASP_NET_Identity_model()
throw new NotImplementedException();
}
+ public override void Can_apply_all_migrations()
+ {
+ base.Can_apply_all_migrations();
+
+ Assert.Null(Sql);
+ }
+
+ public override void Can_apply_range_of_migrations()
+ {
+ base.Can_apply_range_of_migrations();
+
+ Assert.Null(Sql);
+ }
+
+ public override void Can_revert_all_migrations()
+ {
+ base.Can_revert_all_migrations();
+
+ Assert.Null(Sql);
+ }
+
+ public override void Can_revert_one_migrations()
+ {
+ base.Can_revert_one_migrations();
+
+ Assert.Null(Sql);
+ }
+
+ protected override Task ExecuteSqlAsync(string value)
+ => ((SingleStoreTestStore)Fixture.TestStore).ExecuteNonQueryAsync(value);
+
+ public override async Task Can_apply_all_migrations_async()
+ {
+ await base.Can_apply_all_migrations_async();
+
+ Assert.Null(Sql);
+ }
+
public class MigrationsInfrastructureSingleStoreFixture : MigrationsInfrastructureFixtureBase
{
protected override ITestStoreFactory TestStoreFactory
diff --git a/test/EFCore.SingleStore.FunctionalTests/MigrationsSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/MigrationsSingleStoreTest.cs
index 33388293b..6327d7d66 100644
--- a/test/EFCore.SingleStore.FunctionalTests/MigrationsSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/MigrationsSingleStoreTest.cs
@@ -28,6 +28,8 @@ namespace EntityFrameworkCore.SingleStore.FunctionalTests
{
public class MigrationsSingleStoreTest : MigrationsTestBase
{
+ private readonly IRelationalTypeMappingSource _typeMappingSource;
+
public MigrationsSingleStoreTest(MigrationsSingleStoreFixture fixture, ITestOutputHelper testOutputHelper)
: base(fixture)
{
@@ -41,6 +43,8 @@ public MigrationsSingleStoreTest(MigrationsSingleStoreFixture fixture, ITestOutp
command.ExecuteNonQuery();
}
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
+
+ _typeMappingSource = Fixture.ServiceProvider.GetService();
}
[ConditionalTheory(Skip = "SingleStore only supports online ALTER TABLE")]
@@ -55,34 +59,52 @@ public override Task Alter_index_make_unique()
return base.Alter_index_make_unique();
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Alter_column_change_computed_recreates_indexes()
+ public override async Task Alter_check_constraint()
{
- return base.Alter_column_change_computed_recreates_indexes();
- }
+ await base.Alter_check_constraint();
- [ConditionalTheory(Skip = "TODO")]
- public override Task Alter_column_reset_collation()
- {
- return base.Alter_column_reset_collation();
+ AssertSql(
+ """
+ ALTER TABLE `People` DROP CONSTRAINT `CK_People_Foo`;
+ """,
+ //
+ """
+ ALTER TABLE `People` ADD CONSTRAINT `CK_People_Foo` CHECK (`DriverLicense` > 1);
+ """);
}
- [ConditionalTheory(Skip = "TODO: Syntax issue in MySQL 7 only.")]
- public override Task Alter_check_constraint()
+ public override async Task Alter_column_make_computed(bool? stored)
{
- return base.Alter_check_constraint();
- }
+ if (stored == true)
+ {
+ await base.Alter_column_make_computed(stored);
- [ConditionalTheory(Skip = "TODO")]
- public override Task Alter_column_make_computed(bool? stored)
- {
- return base.Alter_column_make_computed(stored);
+ var computedColumnTypeSql = stored == true ? " STORED" : "";
+
+ AssertSql(
+ $"""
+ ALTER TABLE `People` MODIFY COLUMN `Sum` int AS (`X` + `Y`){computedColumnTypeSql};
+ """);
+ }
+ else
+ {
+ var exception = await Assert.ThrowsAsync(() => base.Alter_column_make_computed(stored));
+ Assert.True(exception.Message is "'Changing the STORED status' is not supported for generated columns."
+ or "This is not yet supported for generated columns");
+ }
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Add_column_computed_with_collation(bool stored)
+ public override async Task Add_column_computed_with_collation(bool stored)
{
- return base.Add_column_computed_with_collation(stored);
+ await base.Add_column_computed_with_collation(stored);
+
+ var computedColumnTypeSql = stored ? " STORED" : "";
+ var nullableGeneratedColumnSql = AppConfig.ServerVersion.Supports.NullableGeneratedColumns ? " NULL" : string.Empty;
+
+ AssertSql(
+ $"""
+ ALTER TABLE `People` ADD `Name` longtext CHARACTER SET utf8mb4 COLLATE {NonDefaultCollation} AS ('hello'){computedColumnTypeSql}{nullableGeneratedColumnSql};
+ """);
}
[ConditionalFact(Skip = "BLOB/TEXT columns can't have a default value in SingleStore.")]
@@ -308,12 +330,6 @@ await Test(
});
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Add_primary_key_int()
- {
- return base.Add_primary_key_int();
- }
-
[ConditionalTheory(Skip = "TODO")]
public override async Task Add_primary_key_string()
{
@@ -321,45 +337,69 @@ public override async Task Add_primary_key_string()
}
[ConditionalTheory(Skip = "TODO")]
- public override Task Add_primary_key_composite_with_name()
+ public override async Task Add_primary_key_composite_with_name()
{
- return base.Add_primary_key_composite_with_name();
+ await base.Add_primary_key_composite_with_name();
+
+ AssertSql(
+"""
+ALTER TABLE `People` ADD CONSTRAINT `PK_Foo` PRIMARY KEY (`SomeField1`, `SomeField2`);
+""");
}
[ConditionalTheory(Skip = "TODO")]
- public override Task Add_primary_key_with_name()
+ public override async Task Add_primary_key_with_name()
{
- return base.Add_primary_key_with_name();
+ await base.Add_primary_key_with_name();
}
[ConditionalTheory(Skip = "TODO")]
- public override Task Add_unique_constraint()
+ public override async Task Add_unique_constraint()
{
- return base.Add_unique_constraint();
+ await base.Add_unique_constraint();
}
[ConditionalTheory(Skip = "TODO")]
- public override Task Add_unique_constraint_composite_with_name()
+ public override async Task Add_unique_constraint_composite_with_name()
{
- return base.Add_unique_constraint_composite_with_name();
+ await base.Add_unique_constraint_composite_with_name();
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Alter_column_change_computed_type()
+ public override async Task Alter_column_change_computed_type()
{
- return base.Alter_column_change_computed_type();
+ var exception = await Assert.ThrowsAsync(() => base.Alter_column_change_computed_type());
+ Assert.True(exception.Message is "'Changing the STORED status' is not supported for generated columns."
+ or "This is not yet supported for generated columns");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Alter_column_change_type()
+ public override async Task Alter_column_change_type()
{
- return base.Alter_column_change_type();
+ // await base.Alter_column_change_type();
+ await Test(
+ builder => builder.Entity("People").Property("Id"),
+ builder => builder.Entity("People").Property("SomeColumn"),
+ builder => builder.Entity("People").Property("SomeColumn"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var column = Assert.Single(table.Columns, c => c.Name == "SomeColumn");
+ Assert.StartsWith(_typeMappingSource.FindMapping(typeof(long)).StoreTypeNameBase, column.StoreType);
+ });
+
+ AssertSql(
+"""
+ALTER TABLE `People` MODIFY COLUMN `SomeColumn` bigint NOT NULL;
+""");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Alter_column_set_collation()
+ public override async Task Alter_column_set_collation()
{
- return base.Alter_column_set_collation();
+ await base.Alter_column_set_collation();
+
+ AssertSql(
+ $"""
+ ALTER TABLE `People` MODIFY COLUMN `Name` longtext CHARACTER SET utf8mb4 COLLATE {NonDefaultCollation} NULL;
+ """);
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
@@ -368,19 +408,24 @@ public override async Task Alter_sequence_all_settings()
await base.Alter_sequence_all_settings();
AssertSql(
- """
+"""
ALTER SEQUENCE `foo` INCREMENT BY 2 MINVALUE -5 MAXVALUE 10 CYCLE;
""",
//
- """
+"""
ALTER SEQUENCE `foo` START WITH -3 RESTART;
""");
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
- public override Task Alter_sequence_increment_by()
+ public override async Task Alter_sequence_increment_by()
{
- return base.Alter_sequence_increment_by();
+ await base.Alter_sequence_increment_by();
+
+ AssertSql(
+"""
+ALTER SEQUENCE `foo` INCREMENT BY 2 NO MINVALUE NO MAXVALUE NOCYCLE;
+""");
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
@@ -404,7 +449,7 @@ public override async Task Alter_table_add_comment_non_default_schema()
await base.Alter_table_add_comment_non_default_schema();
AssertSql(
- @"ALTER TABLE `SomeOtherSchema_People` COMMENT 'Table comment';");
+ @"ALTER TABLE `People` COMMENT 'Table comment';");
}
[ConditionalFact(Skip = "SingleStore's ALTER TABLE command doesn't work with comments")]
@@ -425,34 +470,58 @@ public override Task Create_index_unique()
return base.Create_index_unique();
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Create_index_with_filter()
+ [ConditionalFact(Skip = "SingleStore does not support filtered indices.")]
+ public override async Task Create_index_with_filter()
{
- return base.Create_index_with_filter();
+ await base.Create_index_with_filter();
+
+ AssertSql("");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Create_schema()
+ public override async Task Create_schema()
{
- return base.Create_schema();
+ await base.Create_schema();
+
+ AssertSql(
+"""
+CREATE TABLE `People` (
+ `Id` int NOT NULL AUTO_INCREMENT,
+ CONSTRAINT `PK_People` PRIMARY KEY (`Id`)
+) CHARACTER SET=utf8mb4;
+""");
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
- public override Task Create_sequence()
+ public override async Task Create_sequence()
{
- return base.Create_sequence();
+ await base.Create_sequence();
+
+ AssertSql(
+"""
+CREATE SEQUENCE `TestSequence` START WITH 1 INCREMENT BY 1 NOCYCLE;
+""");
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
- public override Task Create_sequence_long()
+ public override async Task Create_sequence_long()
{
- return base.Create_sequence_long();
+ await base.Create_sequence_long();
+
+ AssertSql(
+"""
+CREATE SEQUENCE `TestSequence` START WITH 1 INCREMENT BY 1 NOCYCLE;
+""");
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
- public override Task Create_sequence_short()
+ public override async Task Create_sequence_short()
{
- return base.Create_sequence_short();
+ await base.Create_sequence_short();
+
+ AssertSql(
+"""
+CREATE SEQUENCE `TestSequence` START WITH 1 INCREMENT BY 1 NOCYCLE;
+""");
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
@@ -472,7 +541,7 @@ await Test(
// Assert.Equal("TestSequence", sequence.Name);
// Assert.Equal("dbo2", sequence.Schema);
- Assert.Equal("dbo2_TestSequence", sequence.Name);
+ Assert.Equal("TestSequence", sequence.Name);
Assert.Equal(3, sequence.StartValue);
Assert.Equal(2, sequence.IncrementBy);
@@ -483,14 +552,14 @@ await Test(
AssertSql(
"""
-CREATE SEQUENCE `dbo2_TestSequence` START WITH 3 INCREMENT BY 2 MINVALUE 2 MAXVALUE 916 CYCLE;
+CREATE SEQUENCE `TestSequence` START WITH 3 INCREMENT BY 2 MINVALUE 2 MAXVALUE 916 CYCLE;
""");
}
[ConditionalTheory(Skip = "TODO")]
- public override Task Create_table_all_settings()
+ public override async Task Create_table_all_settings()
{
- return base.Create_table_all_settings();
+ await base.Create_table_all_settings();
}
public override async Task Create_table_with_multiline_comments()
@@ -511,10 +580,12 @@ More information can
be found in the docs.';");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Create_unique_index_with_filter()
+ [ConditionalFact(Skip = "SingleStore does not support filtered indices.")]
+ public override async Task Create_unique_index_with_filter()
{
- return base.Create_unique_index_with_filter();
+ await base.Create_unique_index_with_filter();
+
+ AssertSql("");
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.DescendingIndexes))]
@@ -546,10 +617,14 @@ public override async Task Alter_index_change_sort_order()
@"CREATE INDEX `IX_People_X_Y_Z` ON `People` (`X`, `Y` DESC, `Z`);");
}
- [ConditionalTheory(Skip = "TODO: Syntax issue in MySQL 7 only.")]
- public override Task Drop_check_constraint()
+ public override async Task Drop_check_constraint()
{
- return base.Drop_check_constraint();
+ await base.Drop_check_constraint();
+
+ AssertSql(
+ """
+ ALTER TABLE `People` DROP CONSTRAINT `CK_People_Foo`;
+ """);
}
[ConditionalTheory(Skip = "TODO")]
@@ -571,9 +646,14 @@ public override async Task Drop_primary_key_string()
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
- public override Task Drop_sequence()
+ public override async Task Drop_sequence()
{
- return base.Drop_sequence();
+ await base.Drop_sequence();
+
+ AssertSql(
+"""
+DROP SEQUENCE `TestSequence`;
+""");
}
[ConditionalFact(Skip = "Unique indexes won't be created due to the suppression of foreign keys implementation.")]
@@ -582,6 +662,7 @@ public override Task Drop_unique_constraint()
return base.Drop_unique_constraint();
}
+ [ConditionalFact(Skip = "There are no schemas in SingleStore, that a sequence can be moved between.")]
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
public override async Task Move_sequence()
{
@@ -593,19 +674,45 @@ await Test(
var sequence = Assert.Single(model.Sequences);
// Assert.Equal("TestSequenceSchema", sequence.Schema);
// Assert.Equal("TestSequence", sequence.Name);
- Assert.Equal("TestSequenceSchema_TestSequenceMove", sequence.Name);
+ Assert.Equal("TestSequenceMove", sequence.Name);
});
+ AssertSql("");
+ }
+
+ [SupportedServerVersionCondition(nameof(ServerVersionSupport.DefaultExpression), nameof(ServerVersionSupport.AlternativeDefaultExpression))]
+ public override async Task Add_required_primitive_collection_with_custom_default_value_sql_to_existing_table()
+ {
+ // Classic/literal default values like `DEFAULT '[3, 2, 1]'` are not allowed for `json`, `blob` or `text` data types, but
+ // default *expressions* like `DEFAULT ('[3, 2, 1]')` are.
+ await base.Add_required_primitive_collection_with_custom_default_value_sql_to_existing_table_core("('[3, 2, 1]')");
+
AssertSql(
"""
-ALTER TABLE `TestSequenceMove` RENAME `TestSequenceSchema_TestSequenceMove`;
+ALTER TABLE `Customers` ADD `Numbers` longtext CHARACTER SET utf8mb4 NOT NULL DEFAULT ('[3, 2, 1]');
""");
}
- [ConditionalTheory(Skip = "TODO")]
- public override Task Move_table()
+ public override async Task Add_required_primitve_collection_with_custom_default_value_sql_to_existing_table()
{
- return base.Move_table();
+ // Classic/literal default values like `DEFAULT '[3, 2, 1]'` are not allowed for `json`, `blob` or `text` data types, but
+ // default *expressions* like `DEFAULT ('[3, 2, 1]')` are.
+ await base.Add_required_primitve_collection_with_custom_default_value_sql_to_existing_table_core("('[3, 2, 1]')");
+
+ AssertSql(
+"""
+ALTER TABLE `Customers` ADD `Numbers` longtext CHARACTER SET utf8mb4 NOT NULL DEFAULT ('[3, 2, 1]');
+""");
+ }
+
+ public override async Task Move_table()
+ {
+ await base.Move_table();
+
+ AssertSql(
+"""
+ALTER TABLE `TestTable` RENAME `TestTable`;
+""");
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.Sequences))]
@@ -649,15 +756,37 @@ public override Task Rename_table_with_primary_key()
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.GeneratedColumns))]
- public override Task Add_column_with_computedSql(bool? stored)
+ public override async Task Add_column_with_computedSql(bool? stored)
{
- return base.Add_column_with_computedSql(null);
+ await base.Add_column_with_computedSql(stored);
+
+ var computedColumnTypeSql = stored == true ? " STORED" : "";
+ var nullableGeneratedColumnSql = AppConfig.ServerVersion.Supports.NullableGeneratedColumns ? " NULL" : string.Empty;
+
+ AssertSql(
+ $"""
+ ALTER TABLE `People` ADD `Sum` longtext CHARACTER SET utf8mb4 AS (`X` + `Y`){computedColumnTypeSql}{nullableGeneratedColumnSql};
+ """);
}
[SupportedServerVersionCondition(nameof(ServerVersionSupport.GeneratedColumns))]
- public override Task Create_table_with_computed_column(bool? stored)
+ public override async Task Create_table_with_computed_column(bool? stored)
{
- return base.Create_table_with_computed_column(null);
+ await base.Create_table_with_computed_column(stored);
+
+ var computedColumnTypeSql = stored == true ? " STORED" : "";
+ var nullableGeneratedColumnSql = AppConfig.ServerVersion.Supports.NullableGeneratedColumns ? " NULL" : string.Empty;
+
+ AssertSql(
+$"""
+ CREATE TABLE `People` (
+ `Id` int NOT NULL AUTO_INCREMENT,
+ `Sum` longtext CHARACTER SET utf8mb4 AS (`X` + `Y`){computedColumnTypeSql}{nullableGeneratedColumnSql},
+ `X` int NOT NULL,
+ `Y` int NOT NULL,
+ CONSTRAINT `PK_People` PRIMARY KEY (`Id`)
+ ) CHARACTER SET=utf8mb4;
+ """);
}
[ConditionalFact(Skip = "ALTER TABLE doesn't support changing computed columns.'")]
@@ -1104,17 +1233,24 @@ await Test(
e.Property("Name");
e.Property("Brand")
.HasCharSet(NonDefaultCharSet);
+
+ e.ComplexProperty>("ComplexProperty")
+ .Property("Brand")
+ .HasCharSet(NonDefaultCharSet);
}),
result =>
{
var table = Assert.Single(result.Tables);
var nameColumn = Assert.Single(table.Columns.Where(c => c.Name == "Name"));
var brandColumn = Assert.Single(table.Columns.Where(c => c.Name == "Brand"));
+ var complexBrandColumn = Assert.Single(table.Columns.Where(c => c.Name == "ComplexProperty_Brand"));
Assert.Null(nameColumn[SingleStoreAnnotationNames.CharSet]);
Assert.Equal(NonDefaultCollation2, nameColumn.Collation);
Assert.Equal(S2ServerVersion.Supports.DefaultCharSetUtf8Mb4? null : NonDefaultCharSet, brandColumn[SingleStoreAnnotationNames.CharSet]);
Assert.NotEqual(DefaultCollation, brandColumn.Collation);
+ Assert.Equal(NonDefaultCharSet, complexBrandColumn[SingleStoreAnnotationNames.CharSet]);
+ Assert.NotEqual(DefaultCollation, complexBrandColumn.Collation);
});
}
@@ -1158,7 +1294,8 @@ await Test(
[ConditionalFact]
public override Task Add_column_with_collation()
- => Test(
+ {
+ Test(
builder => builder.Entity("People").Property("Id"),
builder => { },
builder => builder.Entity("People").Property("Name")
@@ -1174,6 +1311,14 @@ public override Task Add_column_with_collation()
}
});
+ AssertSql(
+ """
+ ALTER TABLE `People` ADD `Name` longtext CHARACTER SET utf8mb4 COLLATE {NonDefaultCollation2} NULL;
+ """);
+
+ return Task.CompletedTask;
+ }
+
[ConditionalFact]
public virtual async Task Create_table_longtext_column_with_string_length_and_legacy_charset_definition_in_column_type()
{
@@ -1402,16 +1547,6 @@ public override Task Rename_table()
},
withConventions: false);
- public override async Task Add_required_primitve_collection_with_custom_default_value_sql_to_existing_table()
- {
- await Add_required_primitve_collection_with_custom_default_value_sql_to_existing_table_core("'[3, 2, 1]'");
-
- AssertSql(
- """
- ALTER TABLE `Customers` ADD `Numbers` varchar(127) CHARACTER SET utf8mb4 NOT NULL DEFAULT '[3, 2, 1]';
- """);
- }
-
protected override Task Add_required_primitve_collection_with_custom_default_value_sql_to_existing_table_core(string defaultValueSql)
=> Test(
builder => builder.Entity(
@@ -1447,84 +1582,548 @@ protected override Task Add_required_primitve_collection_with_custom_default_val
Assert.Single(customersTable.PrimaryKey!.Columns));
});
- public override Task Add_required_primitve_collection_with_custom_converter_and_custom_default_value_to_existing_table()
- => Test(
- builder => builder.Entity(
- "Customer", e =>
- {
- e.Property("Id").ValueGeneratedOnAdd();
- e.HasKey("Id");
- e.Property("Name");
- e.ToTable("Customers");
- }),
- builder => builder.Entity(
- "Customer", e =>
- {
- e.Property("Id").ValueGeneratedOnAdd();
- e.HasKey("Id");
- e.Property("Name");
- e.Property>("Numbers")
- .HasMaxLength(127) // <-- MySQL requires a `varchar(n)` instead of a `longtext` type for default value support
- .HasConversion(new ValueConverter, string>(
- convertToProviderExpression: x => x != null && x.Count > 0 ? "some numbers" : "nothing",
- convertFromProviderExpression: x => x == "nothing" ? new List { } : new List { 7, 8, 9 }))
- .HasDefaultValue(new List { 42 })
- .IsRequired();
- e.ToTable("Customers");
- }),
- model =>
- {
- var customersTable = Assert.Single(model.Tables.Where(t => t.Name == "Customers"));
+ public override async Task Create_table()
+ {
+ await base.Create_table();
+ AssertSql(
+ """
+ CREATE TABLE `People` (
+ `Id` int NOT NULL,
+ `Name` longtext CHARACTER SET utf8mb4 NULL,
+ CONSTRAINT `PK_People` PRIMARY KEY (`Id`)
+ ) CHARACTER SET=utf8mb4;
+ """);
+ }
- Assert.Collection(
- customersTable.Columns,
- c => Assert.Equal("Id", c.Name),
- c => Assert.Equal("Name", c.Name),
- c => Assert.Equal("Numbers", c.Name));
- Assert.Same(
- customersTable.Columns.Single(c => c.Name == "Id"),
- Assert.Single(customersTable.PrimaryKey!.Columns));
- });
+ public override async Task Create_table_no_key()
+ {
+ await base.Create_table_no_key();
- public override Task Add_required_primitve_collection_with_custom_default_value_to_existing_table()
- => Test(
- builder => builder.Entity(
- "Customer", e =>
- {
- e.Property("Id").ValueGeneratedOnAdd();
- e.HasKey("Id");
- e.Property("Name");
- e.ToTable("Customers");
- }),
- builder => builder.Entity(
- "Customer", e =>
- {
- e.Property("Id").ValueGeneratedOnAdd();
- e.HasKey("Id");
- e.Property("Name");
- e.Property>("Numbers")
- .HasMaxLength(127) // <-- MySQL requires a `varchar(n)` instead of a `longtext` type for default value support
- .IsRequired().HasDefaultValue(new List { 1, 2, 3 });
- e.ToTable("Customers");
- }),
- model =>
- {
- var customersTable = Assert.Single(model.Tables.Where(t => t.Name == "Customers"));
+ AssertSql(
+ """
+ CREATE TABLE `Anonymous` (
+ `SomeColumn` int NOT NULL
+ ) CHARACTER SET=utf8mb4;
+ """);
+ }
- Assert.Collection(
- customersTable.Columns,
- c => Assert.Equal("Id", c.Name),
- c => Assert.Equal("Name", c.Name),
- c => Assert.Equal("Numbers", c.Name));
- Assert.Same(
- customersTable.Columns.Single(c => c.Name == "Id"),
- Assert.Single(customersTable.PrimaryKey!.Columns));
- });
+ public override async Task Create_table_with_comments()
+ {
+ await base.Create_table_with_comments();
+
+ AssertSql(
+ """
+ CREATE TABLE `People` (
+ `Id` int NOT NULL,
+ `Name` longtext CHARACTER SET utf8mb4 NULL COMMENT 'Column comment',
+ CONSTRAINT `PK_People` PRIMARY KEY (`Id`)
+ ) CHARACTER SET=utf8mb4 COMMENT='Table comment';
+ """);
+ }
+
+ public override async Task Drop_table()
+ {
+ await base.Drop_table();
+
+ AssertSql(
+ """
+ DROP TABLE `People`;
+ """);
+ }
+
+ public override async Task Add_column_with_defaultValueSql_unspecified()
+ {
+ await base.Add_column_with_defaultValueSql_unspecified();
+
+ AssertSql();
+ }
+
+ public override async Task Add_column_with_defaultValue_unspecified()
+ {
+ await base.Add_column_with_defaultValue_unspecified();
+
+ AssertSql();
+ }
+
+ public override async Task Add_column_with_computedSql_unspecified()
+ {
+ await base.Add_column_with_computedSql_unspecified();
+
+ AssertSql();
+ }
+
+ public override async Task Add_column_with_required()
+ {
+ await base.Add_column_with_required();
+
+ AssertSql(
+"""
+ALTER TABLE `People` ADD `Name` longtext CHARACTER SET utf8mb4 NOT NULL;
+""");
+ }
+
+ public override async Task Add_column_with_ansi()
+ {
+ await base.Add_column_with_ansi();
+
+ AssertSql(
+"""
+ALTER TABLE `People` ADD `Name` longtext CHARACTER SET utf8mb4 NULL;
+""");
+ }
+
+ public override async Task Add_column_with_max_length()
+ {
+ await base.Add_column_with_max_length();
+
+ AssertSql(
+"""
+ALTER TABLE `People` ADD `Name` varchar(30) CHARACTER SET utf8mb4 NULL;
+""");
+ }
+
+ public override async Task Add_column_with_unbounded_max_length()
+ {
+ await base.Add_column_with_unbounded_max_length();
+
+ AssertSql(
+"""
+ALTER TABLE `People` ADD `Name` longtext CHARACTER SET utf8mb4 NULL;
+""");
+ }
+
+ public override async Task Add_column_with_max_length_on_derived()
+ {
+ await base.Add_column_with_max_length_on_derived();
+
+ AssertSql();
+ }
+
+ public override async Task Add_column_with_fixed_length()
+ {
+ await base.Add_column_with_fixed_length();
+
+ AssertSql(
+"""
+ALTER TABLE `People` ADD `Name` char(100) CHARACTER SET utf8mb4 NULL;
+""");
+ }
+
+ public override async Task Add_column_with_comment()
+ {
+ await base.Add_column_with_comment();
+
+ AssertSql(
+"""
+ALTER TABLE `People` ADD `FullName` longtext CHARACTER SET utf8mb4 NULL COMMENT 'My comment';
+""");
+ }
+
+ public override async Task Add_column_shared()
+ {
+ await base.Add_column_shared();
+
+ AssertSql();
+ }
+
+ public override async Task Alter_column_change_computed_recreates_indexes()
+ {
+ await base.Alter_column_change_computed_recreates_indexes();
+
+ AssertSql(
+"""
+ALTER TABLE `People` MODIFY COLUMN `Sum` int AS (`X` - `Y`);
+""");
+ }
+
+ public override async Task Alter_column_add_comment()
+ {
+ await base.Alter_column_add_comment();
+
+ AssertSql(
+"""
+ALTER TABLE `People` MODIFY COLUMN `Id` int NOT NULL COMMENT 'Some comment';
+""");
+ }
+
+ public override async Task Alter_column_change_comment()
+ {
+ await base.Alter_column_change_comment();
+
+ AssertSql(
+"""
+ALTER TABLE `People` MODIFY COLUMN `Id` int NOT NULL COMMENT 'Some comment2';
+""");
+ }
+
+ public override async Task Alter_column_remove_comment()
+ {
+ await base.Alter_column_remove_comment();
+
+ AssertSql(
+"""
+ALTER TABLE `People` MODIFY COLUMN `Id` int NOT NULL;
+""");
+ }
+
+ public override async Task Alter_column_reset_collation()
+ {
+ await base.Alter_column_reset_collation();
+
+ AssertSql(
+"""
+ALTER TABLE `People` MODIFY COLUMN `Name` longtext CHARACTER SET utf8mb4 NULL;
+""");
+ }
+
+ public override async Task Drop_column()
+ {
+ await base.Drop_column();
+
+ AssertSql(
+"""
+ALTER TABLE `People` DROP COLUMN `SomeColumn`;
+""");
+ }
+
+ public override async Task Drop_column_computed_and_non_computed_with_dependency()
+ {
+ await base.Drop_column_computed_and_non_computed_with_dependency();
+
+ AssertSql(
+"""
+ALTER TABLE `People` DROP COLUMN `Y`;
+""",
+ //
+ """
+ALTER TABLE `People` DROP COLUMN `X`;
+""");
+ }
+
+ public override async Task Rename_column()
+ {
+ await base.Rename_column();
+
+ AssertSql(
+"""
+ALTER TABLE `People` CHANGE `SomeColumn` `SomeOtherColumn`;
+""");
+ }
+
+ public override async Task Create_index()
+ {
+ await base.Create_index();
+
+ AssertSql(
+"""
+ALTER TABLE `People` MODIFY COLUMN `FirstName` varchar(255) CHARACTER SET utf8mb4 NULL;
+""",
+ //
+ """
+CREATE INDEX `IX_People_FirstName` ON `People` (`FirstName`);
+""");
+ }
+
+ public override async Task Drop_index()
+ {
+ await base.Drop_index();
+
+ AssertSql(
+"""
+ALTER TABLE `People` DROP INDEX `IX_People_SomeField`;
+""");
+ }
+
+ public override async Task Add_primary_key_int()
+ {
+ await base.Add_primary_key_int();
+
+ AssertSql(
+"""
+ALTER TABLE `People` MODIFY COLUMN `SomeField` int NOT NULL AUTO_INCREMENT,
+ADD CONSTRAINT `PK_People` PRIMARY KEY (`SomeField`);
+""");
+ }
+
+ public override async Task InsertDataOperation()
+ {
+ await base.InsertDataOperation();
+
+ AssertSql(
+"""
+INSERT INTO `Person` (`Id`, `Name`)
+VALUES (1, 'Daenerys Targaryen'),
+(2, 'John Snow'),
+(3, 'Arya Stark'),
+(4, 'Harry Strickland'),
+(5, NULL);
+""");
+ }
+
+ public override async Task DeleteDataOperation_simple_key()
+ {
+ await base.DeleteDataOperation_simple_key();
+
+ AssertSql(
+ AppConfig.ServerVersion.Supports.Returning
+ ? """
+DELETE FROM `Person`
+WHERE `Id` = 2
+RETURNING 1;
+"""
+ : """
+DELETE FROM `Person`
+WHERE `Id` = 2;
+SELECT ROW_COUNT();
+""");
+ }
+
+ public override async Task DeleteDataOperation_composite_key()
+ {
+ await base.DeleteDataOperation_composite_key();
+
+ AssertSql(
+ AppConfig.ServerVersion.Supports.Returning
+ ? """
+DELETE FROM `Person`
+WHERE `AnotherId` = 12 AND `Id` = 2
+RETURNING 1;
+"""
+ : """
+DELETE FROM `Person`
+WHERE `AnotherId` = 12 AND `Id` = 2;
+SELECT ROW_COUNT();
+""");
+ }
+
+ public override async Task UpdateDataOperation_simple_key()
+ {
+ await base.UpdateDataOperation_simple_key();
+
+ AssertSql(
+"""
+UPDATE `Person` SET `Name` = 'Another John Snow'
+WHERE `Id` = 2;
+SELECT ROW_COUNT();
+""");
+ }
+
+ public override async Task UpdateDataOperation_composite_key()
+ {
+ await base.UpdateDataOperation_composite_key();
+
+ AssertSql(
+"""
+UPDATE `Person` SET `Name` = 'Another John Snow'
+WHERE `AnotherId` = 11 AND `Id` = 2;
+SELECT ROW_COUNT();
+""");
+ }
+
+ public override async Task UpdateDataOperation_multiple_columns()
+ {
+ await base.UpdateDataOperation_multiple_columns();
+
+ AssertSql(
+"""
+UPDATE `Person` SET `Age` = 21, `Name` = 'Another John Snow'
+WHERE `Id` = 2;
+SELECT ROW_COUNT();
+""");
+ }
+
+ public override async Task SqlOperation()
+ {
+ await base.SqlOperation();
+
+ AssertSql(
+"""
+-- I <3 DDL
+""");
+ }
+
+ public override async Task Create_table_with_complex_type_with_required_properties_on_derived_entity_in_TPH()
+ {
+ await base.Create_table_with_complex_type_with_required_properties_on_derived_entity_in_TPH();
+
+ AssertSql(
+"""
+CREATE TABLE `Contacts` (
+ `Id` int NOT NULL,
+ `Discriminator` varchar(8) CHARACTER SET utf8mb4 NOT NULL,
+ `Name` longtext CHARACTER SET utf8mb4 NULL,
+ `Number` int NULL,
+ `MyComplex_Prop` longtext NULL,
+ `MyComplex_MyNestedComplex_Bar` datetime(6) NULL,
+ `MyComplex_MyNestedComplex_Foo` int NULL,
+ CONSTRAINT `PK_Contacts` PRIMARY KEY (`Id`)
+) CHARACTER SET=utf8mb4;
+""");
+ }
+
+ public override async Task Add_required_primitive_collection_to_existing_table()
+ {
+ await base.Add_required_primitive_collection_to_existing_table();
+
+ AssertSql(
+"""
+ALTER TABLE `Customers` ADD `Numbers` longtext CHARACTER SET utf8mb4 NOT NULL;
+""");
+ }
+
+ public override async Task Add_required_primitive_collection_with_custom_default_value_to_existing_table()
+ {
+ await base.Add_required_primitive_collection_with_custom_default_value_to_existing_table();
+
+ AssertSql(
+"""
+ALTER TABLE `Customers` ADD `Numbers` longtext CHARACTER SET utf8mb4 NOT NULL DEFAULT ('[1,2,3]');
+""");
+ }
+
+ public override async Task Add_required_primitive_collection_with_custom_converter_to_existing_table()
+ {
+ await base.Add_required_primitive_collection_with_custom_converter_to_existing_table();
+
+ AssertSql();
+ }
+
+ public override async Task Add_required_primitive_collection_with_custom_converter_and_custom_default_value_to_existing_table()
+ {
+ await base.Add_required_primitive_collection_with_custom_converter_and_custom_default_value_to_existing_table();
+
+ AssertSql(
+"""
+ALTER TABLE `Customers` ADD `Numbers` longtext CHARACTER SET utf8mb4 NOT NULL DEFAULT ('some numbers');
+""");
+ }
+
+ public override async Task Add_optional_primitive_collection_to_existing_table()
+ {
+ await base.Add_optional_primitive_collection_to_existing_table();
+
+ AssertSql(
+"""
+ALTER TABLE `Customers` ADD `Numbers` longtext CHARACTER SET utf8mb4 NULL;
+""");
+ }
+
+ public override async Task Create_table_with_required_primitive_collection()
+ {
+ await base.Create_table_with_required_primitive_collection();
+
+ AssertSql(
+"""
+CREATE TABLE `Customers` (
+ `Id` int NOT NULL,
+ `Name` longtext CHARACTER SET utf8mb4 NULL,
+ `Numbers` longtext CHARACTER SET utf8mb4 NOT NULL,
+ CONSTRAINT `PK_Customers` PRIMARY KEY (`Id`)
+) CHARACTER SET=utf8mb4;
+""");
+ }
+
+ public override async Task Create_table_with_optional_primitive_collection()
+ {
+ await base.Create_table_with_optional_primitive_collection();
+
+ AssertSql(
+"""
+CREATE TABLE `Customers` (
+ `Id` int NOT NULL,
+ `Name` longtext CHARACTER SET utf8mb4 NULL,
+ `Numbers` longtext CHARACTER SET utf8mb4 NULL,
+ CONSTRAINT `PK_Customers` PRIMARY KEY (`Id`)
+) CHARACTER SET=utf8mb4;
+""");
+ }
+
+ public override async Task Add_required_primitve_collection_to_existing_table()
+ {
+ await base.Add_required_primitve_collection_to_existing_table();
+
+ AssertSql(
+"""
+ALTER TABLE `Customers` ADD `Numbers` longtext CHARACTER SET utf8mb4 NOT NULL;
+""");
+ }
+
+ public override async Task Add_required_primitve_collection_with_custom_default_value_to_existing_table()
+ {
+ await base.Add_required_primitve_collection_with_custom_default_value_to_existing_table();
+
+ AssertSql(
+"""
+ALTER TABLE `Customers` ADD `Numbers` longtext CHARACTER SET utf8mb4 NOT NULL DEFAULT ('[1,2,3]');
+""");
+ }
+
+ public override async Task Add_required_primitve_collection_with_custom_converter_to_existing_table()
+ {
+ await base.Add_required_primitve_collection_with_custom_converter_to_existing_table();
+
+ AssertSql();
+ }
+
+ public override async Task Add_required_primitve_collection_with_custom_converter_and_custom_default_value_to_existing_table()
+ {
+ await base.Add_required_primitve_collection_with_custom_converter_and_custom_default_value_to_existing_table();
+
+ AssertSql(
+"""
+ALTER TABLE `Customers` ADD `Numbers` longtext CHARACTER SET utf8mb4 NOT NULL DEFAULT ('some numbers');
+""");
+ }
+
+ #region ToJson
+
+ public override Task Create_table_with_json_column()
+ => Assert.ThrowsAsync(() => base.Create_table_with_json_column());
+
+ public override Task Create_table_with_json_column_explicit_json_column_names()
+ => Assert.ThrowsAsync(() => base.Create_table_with_json_column_explicit_json_column_names());
+
+ public override Task Rename_table_with_json_column()
+ => Assert.ThrowsAsync(() => base.Rename_table_with_json_column());
+
+ public override Task Add_json_columns_to_existing_table()
+ => Assert.ThrowsAsync(() => base.Add_json_columns_to_existing_table());
+
+ public override Task Convert_json_entities_to_regular_owned()
+ => Assert.ThrowsAsync(() => base.Convert_json_entities_to_regular_owned());
+
+ public override Task Convert_regular_owned_entities_to_json()
+ => Assert.ThrowsAsync(() => base.Convert_regular_owned_entities_to_json());
+
+ public override Task Convert_string_column_to_a_json_column_containing_reference()
+ => Assert.ThrowsAsync(() => base.Convert_string_column_to_a_json_column_containing_reference());
+
+ public override Task Convert_string_column_to_a_json_column_containing_required_reference()
+ => Assert.ThrowsAsync(() => base.Convert_string_column_to_a_json_column_containing_required_reference());
+
+ public override Task Convert_string_column_to_a_json_column_containing_collection()
+ => Assert.ThrowsAsync(() => base.Convert_string_column_to_a_json_column_containing_collection());
+
+ public override Task Drop_json_columns_from_existing_table()
+ => Assert.ThrowsAsync(() => base.Drop_json_columns_from_existing_table());
+
+ public override Task Rename_json_column()
+ => Assert.ThrowsAsync(() => base.Rename_json_column());
+
+ #endregion ToJson
+
+ [ConditionalFact]
+ public virtual void Check_all_tests_overridden()
+ => SingleStoreTestHelpers.AssertAllMethodsOverridden(GetType());
// The constraint name for a primary key is always PRIMARY in MySQL.
protected override bool AssertConstraintNames
=> false;
+ // SingleStore does not support the concept of schemas.
+ protected override bool AssertSchemaNames
+ => false;
+
protected virtual string DefaultCollation => ((SingleStoreTestStore)Fixture.TestStore).DatabaseCollation;
protected override string NonDefaultCollation
diff --git a/test/EFCore.SingleStore.FunctionalTests/ModelBuilding/SingleStoreModelBuilderGenericTest.cs b/test/EFCore.SingleStore.FunctionalTests/ModelBuilding/SingleStoreModelBuilderGenericTest.cs
new file mode 100644
index 000000000..a042e0620
--- /dev/null
+++ b/test/EFCore.SingleStore.FunctionalTests/ModelBuilding/SingleStoreModelBuilderGenericTest.cs
@@ -0,0 +1,69 @@
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.ModelBuilding;
+using Xunit;
+
+namespace EntityFrameworkCore.SingleStore.FunctionalTests.ModelBuilding;
+
+public class SingleStoreModelBuilderGenericTest : SingleStoreModelBuilderTestBase
+{
+ public class SingleStoreGenericNonRelationship(SingleStoreModelBuilderFixture fixture) : SingleStoreNonRelationship(fixture)
+ {
+ protected override TestModelBuilder CreateModelBuilder(
+ Action configure)
+ => new ModelBuilderTest.GenericTestModelBuilder(Fixture, configure);
+ }
+
+ public class SingleStoreGenericComplexType(SingleStoreModelBuilderFixture fixture) : SingleStoreComplexType(fixture)
+ {
+ protected override TestModelBuilder CreateModelBuilder(
+ Action configure)
+ => new ModelBuilderTest.GenericTestModelBuilder(Fixture, configure);
+ }
+
+ public class SingleStoreGenericInheritance(SingleStoreModelBuilderFixture fixture) : SingleStoreInheritance(fixture)
+ {
+ protected override TestModelBuilder CreateModelBuilder(
+ Action configure)
+ => new ModelBuilderTest.GenericTestModelBuilder(Fixture, configure);
+ }
+
+ public class SingleStoreGenericOneToMany(SingleStoreModelBuilderFixture fixture) : SingleStoreOneToMany(fixture)
+ {
+ protected override TestModelBuilder CreateModelBuilder(
+ Action configure)
+ => new ModelBuilderTest.GenericTestModelBuilder(Fixture, configure);
+ }
+
+ public class SingleStoreGenericManyToOne(SingleStoreModelBuilderFixture fixture) : SingleStoreManyToOne(fixture)
+ {
+ protected override TestModelBuilder CreateModelBuilder(
+ Action configure)
+ => new ModelBuilderTest.GenericTestModelBuilder(Fixture, configure);
+ }
+
+ public class SingleStoreGenericOneToOne(SingleStoreModelBuilderFixture fixture) : SingleStoreOneToOne(fixture)
+ {
+ protected override TestModelBuilder CreateModelBuilder(
+ Action configure)
+ => new ModelBuilderTest.GenericTestModelBuilder(Fixture, configure);
+ }
+
+ public class SingleStoreGenericManyToMany(SingleStoreModelBuilderFixture fixture) : SingleStoreManyToMany(fixture)
+ {
+ protected override TestModelBuilder CreateModelBuilder(
+ Action configure)
+ => new ModelBuilderTest.GenericTestModelBuilder(Fixture, configure);
+ }
+
+ internal class SingleStoreGenericOwnedTypes(SingleStoreModelBuilderFixture fixture) : SingleStoreOwnedTypes(fixture)
+ {
+ // MySQL stored procedures do not support result columns.
+ public override void Can_use_sproc_mapping_with_owned_reference()
+ => Assert.Throws(() => base.Can_use_sproc_mapping_with_owned_reference());
+
+ protected override TestModelBuilder CreateModelBuilder(
+ Action configure)
+ => new ModelBuilderTest.GenericTestModelBuilder(Fixture, configure);
+ }
+}
diff --git a/test/EFCore.SingleStore.FunctionalTests/ModelBuilding/SingleStoreModelBuilderTestBase.cs b/test/EFCore.SingleStore.FunctionalTests/ModelBuilding/SingleStoreModelBuilderTestBase.cs
new file mode 100644
index 000000000..82b439589
--- /dev/null
+++ b/test/EFCore.SingleStore.FunctionalTests/ModelBuilding/SingleStoreModelBuilderTestBase.cs
@@ -0,0 +1,39 @@
+using Microsoft.EntityFrameworkCore.ModelBuilding;
+using Microsoft.EntityFrameworkCore.TestUtilities;
+using EntityFrameworkCore.SingleStore.FunctionalTests.TestUtilities;
+using Xunit;
+
+namespace EntityFrameworkCore.SingleStore.FunctionalTests.ModelBuilding;
+
+public class SingleStoreModelBuilderTestBase : RelationalModelBuilderTest
+{
+ public abstract class SingleStoreNonRelationship(SingleStoreModelBuilderFixture fixture)
+ : RelationalNonRelationshipTestBase(fixture), IClassFixture;
+
+ public abstract class SingleStoreComplexType(SingleStoreModelBuilderFixture fixture)
+ : RelationalComplexTypeTestBase(fixture), IClassFixture;
+
+ public abstract class SingleStoreInheritance(SingleStoreModelBuilderFixture fixture)
+ : RelationalInheritanceTestBase(fixture), IClassFixture;
+
+ public abstract class SingleStoreOneToMany(SingleStoreModelBuilderFixture fixture)
+ : RelationalOneToManyTestBase(fixture), IClassFixture;
+
+ public abstract class SingleStoreManyToOne(SingleStoreModelBuilderFixture fixture)
+ : RelationalManyToOneTestBase(fixture), IClassFixture;
+
+ public abstract class SingleStoreOneToOne(SingleStoreModelBuilderFixture fixture)
+ : RelationalOneToOneTestBase(fixture), IClassFixture;
+
+ public abstract class SingleStoreManyToMany(SingleStoreModelBuilderFixture fixture)
+ : RelationalManyToManyTestBase(fixture), IClassFixture;
+
+ public abstract class SingleStoreOwnedTypes(SingleStoreModelBuilderFixture fixture)
+ : RelationalOwnedTypesTestBase(fixture), IClassFixture;
+
+ public class SingleStoreModelBuilderFixture : RelationalModelBuilderFixture
+ {
+ public override TestHelpers TestHelpers
+ => SingleStoreTestHelpers.Instance;
+ }
+}
diff --git a/test/EFCore.SingleStore.FunctionalTests/MusicStoreSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/MusicStoreSingleStoreTest.cs
index aa6693d4b..03df8f8f4 100644
--- a/test/EFCore.SingleStore.FunctionalTests/MusicStoreSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/MusicStoreSingleStoreTest.cs
@@ -109,7 +109,7 @@ public override async Task Browse_ReturnsViewWithGenre()
await base.Browse_ReturnsViewWithGenre();
}
- public override void Music_store_project_to_mapped_entity()
+ public override async Task Music_store_project_to_mapped_entity()
{
// We're skipping this test when we're running tests on Managed Service due to the specifics of
// how AUTO_INCREMENT works (https://docs.singlestore.com/cloud/reference/sql-reference/data-definition-language-ddl/create-table/#auto-increment-behavior)
@@ -117,7 +117,7 @@ public override void Music_store_project_to_mapped_entity()
{
return;
}
- base.Music_store_project_to_mapped_entity();
+ await base.Music_store_project_to_mapped_entity();
}
public override async Task RemoveFromCart_removes_items_from_cart()
diff --git a/test/EFCore.SingleStore.FunctionalTests/OptimisticConcurrencySingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/OptimisticConcurrencySingleStoreTest.cs
index bdcaf248c..41d5cbcad 100644
--- a/test/EFCore.SingleStore.FunctionalTests/OptimisticConcurrencySingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/OptimisticConcurrencySingleStoreTest.cs
@@ -1,9 +1,7 @@
-using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
-using Microsoft.EntityFrameworkCore.TestModels.ConcurrencyModel;
using Xunit;
namespace EntityFrameworkCore.SingleStore.FunctionalTests
@@ -18,19 +16,6 @@ public OptimisticConcurrencySingleStoreTest(F1SingleStoreFixture fixture)
protected override void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
=> facade.UseTransaction(transaction.GetDbTransaction());
- protected override Task ConcurrencyTestAsync(
- Action storeChange, Action clientChange,
- Action resolver, Action validator)
- {
- return base.ConcurrencyTestAsync(c =>
- {
- storeChange(c);
- // CHECK: Is this still/really needed?
- // Need to wait to make CURRENT_TIMESTAMP return different values reliably
- Task.Delay(100);
- }, clientChange, resolver, validator);
- }
-
[ConditionalFact(Skip = "#588")]
public override Task Updating_then_deleting_the_same_entity_results_in_DbUpdateConcurrencyException_which_can_be_resolved_with_store_values()
{
diff --git a/test/EFCore.SingleStore.FunctionalTests/PropertyValuesSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/PropertyValuesSingleStoreTest.cs
index e8f356670..52fd28eb5 100644
--- a/test/EFCore.SingleStore.FunctionalTests/PropertyValuesSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/PropertyValuesSingleStoreTest.cs
@@ -23,6 +23,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
modelBuilder.Entity()
.Property(e => e.Id)
.HasColumnType("bigint");
+ modelBuilder.Entity()
+ .Property(e => e.Id)
+ .HasColumnType("bigint");
base.OnModelCreating(modelBuilder, context);
}
diff --git a/test/EFCore.SingleStore.FunctionalTests/ProxyGraphUpdatesSingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/ProxyGraphUpdatesSingleStoreTest.cs
index 8c78cd258..5148315b5 100644
--- a/test/EFCore.SingleStore.FunctionalTests/ProxyGraphUpdatesSingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/ProxyGraphUpdatesSingleStoreTest.cs
@@ -1,4 +1,5 @@
-using Microsoft.EntityFrameworkCore;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.TestUtilities;
@@ -64,9 +65,8 @@ public ChangeTracking(ProxyGraphUpdatesWithChangeTrackingSingleStoreFixture fixt
}
// Needs lazy loading
- public override void Save_two_entity_cycle_with_lazy_loading()
- {
- }
+ public override Task Save_two_entity_cycle_with_lazy_loading()
+ => Task.CompletedTask;
protected override bool DoesLazyLoading
=> false;
diff --git a/test/EFCore.SingleStore.FunctionalTests/Query/AdHocAdvancedMappingsQuerySingleStoreTest.cs b/test/EFCore.SingleStore.FunctionalTests/Query/AdHocAdvancedMappingsQuerySingleStoreTest.cs
index d690d2375..c75304682 100644
--- a/test/EFCore.SingleStore.FunctionalTests/Query/AdHocAdvancedMappingsQuerySingleStoreTest.cs
+++ b/test/EFCore.SingleStore.FunctionalTests/Query/AdHocAdvancedMappingsQuerySingleStoreTest.cs
@@ -1,7 +1,12 @@
+using System;
+using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.TestUtilities;
using EntityFrameworkCore.SingleStore.FunctionalTests.TestUtilities;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Xunit;
namespace EntityFrameworkCore.SingleStore.FunctionalTests.Query;
@@ -10,6 +15,39 @@ public class AdHocAdvancedMappingsQuerySingleStoreTest : AdHocAdvancedMappingsQu
protected override ITestStoreFactory TestStoreFactory
=> SingleStoreTestStoreFactory.Instance;
+ [SkippableTheory]
+ public override async Task Query_generates_correct_datetime2_parameter_definition(int? fractionalSeconds, string postfix)
+ {
+ if (fractionalSeconds is not (0 or 6))
+ {
+ Skip.If(true, "SingleStore supports DATETIME precision only 0 or 6.");
+ }
+
+ await base.Query_generates_correct_datetime2_parameter_definition(fractionalSeconds, postfix);
+ }
+
+ [SkippableTheory]
+ public override async Task Query_generates_correct_datetimeoffset_parameter_definition(int? fractionalSeconds, string postfix)
+ {
+ if (fractionalSeconds is not (0 or 6))
+ {
+ Skip.If(true, "SingleStore supports DATETIME precision only 0 or 6.");
+ }
+
+ await base.Query_generates_correct_datetimeoffset_parameter_definition(fractionalSeconds, postfix);
+ }
+
+ [SkippableTheory]
+ public override async Task Query_generates_correct_timespan_parameter_definition(int? fractionalSeconds, string postfix)
+ {
+ if (fractionalSeconds is not (0 or 6))
+ {
+ Skip.If(true, "SingleStore supports DATETIME precision only 0 or 6.");
+ }
+
+ await base.Query_generates_correct_timespan_parameter_definition(fractionalSeconds, postfix);
+ }
+
public override async Task Two_similar_complex_properties_projected_with_split_query1()
{
await base.Two_similar_complex_properties_projected_with_split_query1();
@@ -22,13 +60,13 @@ ORDER BY `o`.`Id`
""",
//
"""
-SELECT `t`.`Id`, `t`.`NestedId`, `t`.`OfferId`, `t`.`payment_brutto`, `t`.`payment_netto`, `t`.`Id0`, `t`.`payment_brutto0`, `t`.`payment_netto0`, `o`.`Id`
+SELECT `s`.`Id`, `s`.`NestedId`, `s`.`OfferId`, `s`.`payment_brutto`, `s`.`payment_netto`, `s`.`Id0`, `s`.`payment_brutto0`, `s`.`payment_netto0`, `o`.`Id`
FROM `Offers` AS `o`
INNER JOIN (
SELECT `v`.`Id`, `v`.`NestedId`, `v`.`OfferId`, `v`.`payment_brutto`, `v`.`payment_netto`, `n`.`Id` AS `Id0`, `n`.`payment_brutto` AS `payment_brutto0`, `n`.`payment_netto` AS `payment_netto0`
FROM `Variation` AS `v`
LEFT JOIN `NestedEntity` AS `n` ON `v`.`NestedId` = `n`.`Id`
-) AS `t` ON `o`.`Id` = `t`.`OfferId`
+) AS `s` ON `o`.`Id` = `s`.`OfferId`
ORDER BY `o`.`Id`
""");
}
@@ -47,19 +85,19 @@ LIMIT 2
""",
//
"""
-SELECT `t0`.`Id`, `t0`.`NestedId`, `t0`.`OfferId`, `t0`.`payment_brutto`, `t0`.`payment_netto`, `t0`.`Id0`, `t0`.`payment_brutto0`, `t0`.`payment_netto0`, `t`.`Id`
+SELECT `s`.`Id`, `s`.`NestedId`, `s`.`OfferId`, `s`.`payment_brutto`, `s`.`payment_netto`, `s`.`Id0`, `s`.`payment_brutto0`, `s`.`payment_netto0`, `o0`.`Id`
FROM (
SELECT `o`.`Id`
FROM `Offers` AS `o`
WHERE `o`.`Id` = 1
LIMIT 1
-) AS `t`
+) AS `o0`
INNER JOIN (
SELECT `v`.`Id`, `v`.`NestedId`, `v`.`OfferId`, `v`.`payment_brutto`, `v`.`payment_netto`, `n`.`Id` AS `Id0`, `n`.`payment_brutto` AS `payment_brutto0`, `n`.`payment_netto` AS `payment_netto0`
FROM `Variation` AS `v`
LEFT JOIN `NestedEntity` AS `n` ON `v`.`NestedId` = `n`.`Id`
-) AS `t0` ON `t`.`Id` = `t0`.`OfferId`
-ORDER BY `t`.`Id`
+) AS `s` ON `o0`.`Id` = `s`.`OfferId`
+ORDER BY `o0`.`Id`
""");
}
@@ -71,7 +109,7 @@ public override async Task Projecting_one_of_two_similar_complex_types_picks_the
"""
@__p_0='10'
-SELECT `a`.`Id`, `t`.`Info_Created0` AS `Created`
+SELECT `a`.`Id`, `s`.`Info_Created0` AS `Created`
FROM (
SELECT `c`.`Id`, `b`.`AId`, `b`.`Info_Created` AS `Info_Created0`
FROM `Cs` AS `c`
@@ -79,9 +117,159 @@ public override async Task Projecting_one_of_two_similar_complex_types_picks_the
WHERE `b`.`AId` = 1
ORDER BY `c`.`Id`
LIMIT @__p_0
-) AS `t`
-LEFT JOIN `As` AS `a` ON `t`.`AId` = `a`.`Id`
-ORDER BY `t`.`Id`
+) AS `s`
+LEFT JOIN `As` AS `a` ON `s`.`AId` = `a`.`Id`
+ORDER BY `s`.`Id`
""");
}
+
+ public override async Task Projection_failing_with_EnumToStringConverter()
+ {
+ var contextFactory = await InitializeAsync(seed: c => c.SeedAsync(), onModelCreating: modelBuilder =>
+ {
+ // We're changing the data type of the fields from INT to BIGINT, because in SingleStore
+ // on a sharded (distributed) table, AUTO_INCREMENT can only be used on a BIGINT column
+ modelBuilder.Entity()
+ .Property(e => e.Id)
+ .HasColumnType("bigint");
+
+ modelBuilder.Entity()
+ .Property(e => e.Id)
+ .HasColumnType("bigint");
+ });
+
+ using var context = contextFactory.CreateContext();
+ var query = from p in context.Products
+ join c in context.Categories on p.CategoryId equals c.Id into grouping
+ from c in grouping.DefaultIfEmpty()
+ select new Context15684.ProductDto
+ {
+ Id = p.Id,
+ Name = p.Name,
+ CategoryName = c == null ? "Other" : c.Name,
+ CategoryStatus = c == null ? Context15684.CategoryStatus.Active : c.Status
+ };
+ var result = query.ToList();
+ Assert.Equal(2, result.Count);
+ }
+
+ public override async Task Projecting_correlated_collection_along_with_non_mapped_property()
+ {
+ var contextFactory = await InitializeAsync(seed: c => c.SeedAsync(), onModelCreating: modelBuilder =>
+ {
+ // We're changing the data type of the fields from INT to BIGINT, because in SingleStore
+ // on a sharded (distributed) table, AUTO_INCREMENT can only be used on a BIGINT column
+ modelBuilder.Entity()
+ .Property(e => e.Id)
+ .HasColumnType("bigint");
+
+ modelBuilder.Entity()
+ .Property(e => e.Id)
+ .HasColumnType("bigint");
+ });
+
+ using (var context = contextFactory.CreateContext())
+ {
+ var result = context.Blogs.Select(
+ e => new
+ {
+ e.Id,
+ e.Title,
+ FirstPostName = e.Posts.Where(i => i.Name.Contains("2")).ToList()
+ }).ToList();
+ }
+
+ using (var context = contextFactory.CreateContext())
+ {
+ var result = context.Blogs.Select(
+ e => new
+ {
+ e.Id,
+ e.Title,
+ FirstPostName = e.Posts.OrderBy(i => i.Id).FirstOrDefault().Name
+ }).ToList();
+ }
+ }
+
+ [ConditionalFact]
+ public override async Task Double_convert_interface_created_expression_tree()
+ {
+ var contextFactory = await InitializeAsync(seed: c => c.SeedAsync(), onModelCreating: modelBuilder =>
+ {
+ // We're changing the data type of the fields from INT to BIGINT, because in SingleStore
+ // on a sharded (distributed) table, AUTO_INCREMENT can only be used on a BIGINT column
+ modelBuilder.Entity()
+ .Property(e => e.Id)
+ .HasColumnType("bigint");
+
+ modelBuilder.Entity()
+ .Property(e => e.Id)
+ .HasColumnType("bigint");
+ });
+
+ using var context = contextFactory.CreateContext();
+ var expression = Context17794.HasAction17794(Context17794.Actions.Accepted);
+ var query = context.Offers.Where(expression).Count();
+
+ Assert.Equal(1, query);
+ }
+
+ public override async Task Casts_are_removed_from_expression_tree_when_redundant()
+ {
+ var contextFactory = await InitializeAsync(seed: c => c.SeedAsync(), onModelCreating: modelBuilder =>
+ {
+ // We're changing the data type of the fields from INT to BIGINT, because in SingleStore
+ // on a sharded (distributed) table, AUTO_INCREMENT can only be used on a BIGINT column
+ modelBuilder.Entity()
+ .Property(e => e.Id)
+ .HasColumnType("bigint");
+ });
+
+ using (var context = contextFactory.CreateContext())
+ {
+ var queryBase = (IQueryable)context.MockEntities;
+ var id = 1;
+ var query = queryBase.Cast().FirstOrDefault(x => x.Id == id);
+
+ Assert.Equal(1, query.Id);
+ }
+
+ using (var context = contextFactory.CreateContext())
+ {
+ var queryBase = (IQueryable)context.MockEntities;
+ var query = queryBase.Cast