Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add setting to advanced store options to enable case-sensitive names handling #2766

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Added UseCaseSensitiveQualifiedNamesWhenApplyingChanges to advanced s…
…tore options to enable case sensitive names handling

Bumped Weasel to 6.2.0
oskardudycz committed Nov 3, 2023
commit ee4df4265bfe45b34889bba9b141467832afec30
2 changes: 1 addition & 1 deletion Analysis.Build.props
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@
<ExcludeAssets>none</ExcludeAssets>
<IncludeAssets>all</IncludeAssets>
</PackageReference>
<PackageReference Include="Meziantou.Analyzer" Version="2.0.93">
<PackageReference Include="Meziantou.Analyzer" Version="2.0.103">
<PrivateAssets>all</PrivateAssets>
<ExcludeAssets>none</ExcludeAssets>
<IncludeAssets>all</IncludeAssets>
2 changes: 1 addition & 1 deletion docs/configuration/multitenancy.md
Original file line number Diff line number Diff line change
@@ -101,7 +101,7 @@ If you don't know the tenant upfront, you can create and apply changes dynamical
var tenant = await theStore.Tenancy.GetTenantAsync(tenantId);
await tenant.Database.ApplyAllConfiguredChangesToDatabaseAsync();
```
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/CoreTests/adding_custom_schema_objects.cs#L151-L154' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_manual_single_tenancy_apply_changes' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/CoreTests/adding_custom_schema_objects.cs#L151-L156' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_manual_single_tenancy_apply_changes' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

You can place this code somewhere in the tenant initialization code. For instance:
32 changes: 14 additions & 18 deletions docs/scenarios/using-sequence-for-unique-id.md
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ public class MatterId: FeatureSchemaBase
private readonly int _startFrom;
private readonly string _schema;

public MatterId(StoreOptions options, int startFrom) : base(nameof(MatterId), options.Advanced.Migrator)
public MatterId(StoreOptions options, int startFrom): base(nameof(MatterId), options.Advanced.Migrator)
{
_startFrom = startFrom;
_schema = options.DatabaseSchemaName;
@@ -26,11 +26,12 @@ public class MatterId: FeatureSchemaBase
protected override IEnumerable<ISchemaObject> schemaObjects()
{
// We return a sequence that starts from the value provided in the ctor
yield return new Sequence(new DbObjectName(_schema, $"mt_{nameof(MatterId).ToLowerInvariant()}"), _startFrom);
yield return new Sequence(new PostgresqlObjectName(_schema, $"mt_{nameof(MatterId).ToLowerInvariant()}"),
_startFrom);
}
}
```
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/Marten.Testing/Examples/ScenarioUsingSequenceForUniqueId.cs#L17-L36' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_scenario-usingsequenceforuniqueid-setup' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/Marten.Testing/Examples/ScenarioUsingSequenceForUniqueId.cs#L15-L37' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_scenario-usingsequenceforuniqueid-setup' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

This sequence yielding customization will be plugged into Marten via the store configuration
@@ -40,7 +41,7 @@ This sequence yielding customization will be plugged into Marten via the store c
```cs
storeOptions.Storage.Add(new MatterId(storeOptions, 10000));
```
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/Marten.Testing/Examples/ScenarioUsingSequenceForUniqueId.cs#L43-L45' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_scenario-usingsequenceforuniqueid-storesetup-1' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/Marten.Testing/Examples/ScenarioUsingSequenceForUniqueId.cs#L44-L48' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_scenario-usingsequenceforuniqueid-storesetup-1' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

and then executed against the database (generating & executing the DDL statements that create the required database objects):
@@ -50,7 +51,7 @@ and then executed against the database (generating & executing the DDL statement
```cs
await theStore.Storage.ApplyAllConfiguredChangesToDatabaseAsync();
```
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/Marten.Testing/Examples/ScenarioUsingSequenceForUniqueId.cs#L48-L50' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_scenario-usingsequenceforuniqueid-storesetup-2' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/Marten.Testing/Examples/ScenarioUsingSequenceForUniqueId.cs#L51-L55' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_scenario-usingsequenceforuniqueid-storesetup-2' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

We introduce a few types with `Guid` identifiers, whom we reference to our end users by numbers, encapsulated in the `Matter` field:
@@ -61,17 +62,20 @@ We introduce a few types with `Guid` identifiers, whom we reference to our end u
public class Contract
{
public Guid Id { get; set; }

public int Matter { get; set; }
// Other fields...
}

public class Inquiry
{
public Guid Id { get; set; }

public int Matter { get; set; }
// Other fields...
}
```
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/Marten.Testing/Examples/ScenarioUsingSequenceForUniqueId.cs#L79-L92' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_scenario-usingsequenceforuniqueid-setup-types' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/Marten.Testing/Examples/ScenarioUsingSequenceForUniqueId.cs#L77-L95' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_scenario-usingsequenceforuniqueid-setup-types' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Now, when creating and persisting such types, we first query the database for a new and unique running number. While we generate (or if wanted, let Marten generate) non-human-readable, system-internal identifiers for the created instances, we assign to them the newly generated and unique human-readable identifier:
@@ -85,24 +89,16 @@ await using var session = theStore.LightweightSession();
// Generate a new, unique identifier
var nextMatter = session.NextInSequence(matter);

var contract = new Contract
{
Id = Guid.NewGuid(),
Matter = nextMatter
};
var contract = new Contract { Id = Guid.NewGuid(), Matter = nextMatter };

var inquiry = new Inquiry
{
Id = Guid.NewGuid(),
Matter = nextMatter
};
var inquiry = new Inquiry { Id = Guid.NewGuid(), Matter = nextMatter };

session.Store(contract);
session.Store(inquiry);

await session.SaveChangesAsync();
```
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/Marten.Testing/Examples/ScenarioUsingSequenceForUniqueId.cs#L52-L76' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_scenario-usingsequenceforuniqueid-querymatter' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/Marten.Testing/Examples/ScenarioUsingSequenceForUniqueId.cs#L57-L74' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_scenario-usingsequenceforuniqueid-querymatter' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Lastly, we have an extension method (used above) as a shorthand for generating the SQL statement for a sequence value query:
@@ -119,5 +115,5 @@ public static class SessionExtensions
}
}
```
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/Marten.Testing/Examples/ScenarioUsingSequenceForUniqueId.cs#L95-L104' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_scenario-usingsequenceforuniqueid-setup-extensions' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/Marten.Testing/Examples/ScenarioUsingSequenceForUniqueId.cs#L98-L109' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_scenario-usingsequenceforuniqueid-setup-extensions' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->
16 changes: 8 additions & 8 deletions docs/schema/extensions.md
Original file line number Diff line number Diff line change
@@ -7,25 +7,25 @@ Not to worry though, Marten comes with a base class that makes it a bit simpler
<!-- snippet: sample_creating-a-fake-schema-feature -->
<a id='snippet-sample_creating-a-fake-schema-feature'></a>
```cs
public class FakeStorage : FeatureSchemaBase
public class FakeStorage: FeatureSchemaBase
{
private readonly StoreOptions _options;

public FakeStorage(StoreOptions options) : base("fake", options.Advanced.Migrator)
public FakeStorage(StoreOptions options): base("fake", options.Advanced.Migrator)
{
_options = options;
}

protected override IEnumerable<ISchemaObject> schemaObjects()
{
var table = new Table(new DbObjectName(_options.DatabaseSchemaName, "mt_fake_table"));
var table = new Table(new PostgresqlObjectName(_options.DatabaseSchemaName, "mt_fake_table"));
table.AddColumn("name", "varchar");

yield return table;
}
}
```
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/DocumentDbTests/Configuration/ability_to_add_custom_storage_features.cs#L48-L67' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_creating-a-fake-schema-feature' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/DocumentDbTests/Configuration/ability_to_add_custom_storage_features.cs#L49-L69' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_creating-a-fake-schema-feature' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Now, to actually apply this feature to your Marten applications, use this syntax:
@@ -44,7 +44,7 @@ var store = DocumentStore.For(_ =>
_.Storage.Add(new FakeStorage(_));
});
```
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/DocumentDbTests/Configuration/ability_to_add_custom_storage_features.cs#L33-L44' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_adding-schema-feature' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/DocumentDbTests/Configuration/ability_to_add_custom_storage_features.cs#L32-L45' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_adding-schema-feature' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Do note that when you use the `Add<T>()` syntax, Marten will pass along the current `StoreOptions` to the constructor function if there is a constructor with that signature. Otherwise, it uses the no-arg constructor.
@@ -94,7 +94,7 @@ StoreOptions(opts =>
opts.RegisterDocumentType<Target>();

// Create a user defined function to act as a ternary operator similar to SQL Server
var function = new Function(new DbObjectName("public", "iif"), @"
var function = new Function(new PostgresqlObjectName("public", "iif"), @"
create or replace function iif(
condition boolean, -- if condition
true_result anyelement, -- then
@@ -109,7 +109,7 @@ $f$ language sql immutable;

await theStore.Storage.ApplyAllConfiguredChangesToDatabaseAsync();
```
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/CoreTests/adding_custom_schema_objects.cs#L190-L212' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_customschemafunction' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/CoreTests/adding_custom_schema_objects.cs#L192-L214' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_customschemafunction' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

## Sequence
@@ -131,7 +131,7 @@ StoreOptions(opts =>

await theStore.Storage.ApplyAllConfiguredChangesToDatabaseAsync();
```
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/CoreTests/adding_custom_schema_objects.cs#L226-L240' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_customschemasequence' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/CoreTests/adding_custom_schema_objects.cs#L228-L242' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_customschemasequence' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

## Extension
2 changes: 1 addition & 1 deletion src/CoreTests/CoreTests.csproj
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit" Version="2.5.3"/>
<PackageReference Include="xunit" Version="2.6.1"/>
<PackageReference Include="NSubstitute" Version="5.1.0"/>
<PackageReference Include="Shouldly" Version="4.2.1"/>
</ItemGroup>
4 changes: 3 additions & 1 deletion src/CoreTests/adding_custom_schema_objects.cs
Original file line number Diff line number Diff line change
@@ -149,8 +149,10 @@ public async Task enable_an_extension_with_multitenancy_with_tenants_upfront_thr
});

#region sample_manual_single_tenancy_apply_changes

var tenant = await theStore.Tenancy.GetTenantAsync(tenantId);
await tenant.Database.ApplyAllConfiguredChangesToDatabaseAsync();

#endregion

await using var sessionNext = theStore.QuerySession(tenantId);
@@ -194,7 +196,7 @@ public async Task create_a_function()
opts.RegisterDocumentType<Target>();

// Create a user defined function to act as a ternary operator similar to SQL Server
var function = new Function(new DbObjectName("public", "iif"), @"
var function = new Function(new PostgresqlObjectName("public", "iif"), @"
create or replace function iif(
condition boolean, -- if condition
true_result anyelement, -- then
Original file line number Diff line number Diff line change
@@ -6,12 +6,13 @@
using Marten.Testing.Harness;
using Weasel.Core;
using Weasel.Core.Migrations;
using Weasel.Postgresql;
using Weasel.Postgresql.Tables;
using Xunit;

namespace DocumentDbTests.Bugs;

public class Bug_2523_using_query_on_custom_storage_table : BugIntegrationContext
public class Bug_2523_using_query_on_custom_storage_table: BugIntegrationContext
{
[Fact]
public async Task WhenCustomTableIsUsedInABatchWithOtherDocumentResetAllShouldWork()
@@ -27,25 +28,26 @@ public async Task WhenCustomTableIsUsedInABatchWithOtherDocumentResetAllShouldWo

await using var session = store.LightweightSession();
session.QueueSqlCommand(CustomTableStorage.InsertSql, Guid.NewGuid().ToString());
session.Insert(new User{FirstName = "John", LastName = "Doe"});
session.Insert(new User { FirstName = "John", LastName = "Doe" });
await session.SaveChangesAsync();

await store.Advanced.ResetAllData();
}
}

public class CustomTableStorage : FeatureSchemaBase
public class CustomTableStorage: FeatureSchemaBase
{
private const string TableName = "mt_custom_table";
public const string InsertSql = $"insert into bugs.{TableName}(id) values(?)";

private readonly StoreOptions _options;

public CustomTableStorage(StoreOptions options) : base("custom_table", options.Advanced.Migrator) => _options = options;
public CustomTableStorage(StoreOptions options): base("custom_table", options.Advanced.Migrator) =>
_options = options;

protected override IEnumerable<ISchemaObject> schemaObjects()
{
var table = new Table(new DbObjectName("bugs", TableName));
var table = new Table(new PostgresqlObjectName("bugs", TableName));
table.AddColumn<string>("id").AsPrimaryKey();
yield return table;
}
Original file line number Diff line number Diff line change
@@ -98,29 +98,29 @@ internal class spaceAfterTableNameSchema: testSchema
{
protected override IEnumerable<ISchemaObject> SchemaObjects()
{
var table = new Table(new DbObjectName(SchemaConstants.DefaultSchema,"test_space_after"));
var table = new Table(new PostgresqlObjectName(SchemaConstants.DefaultSchema, "test_space_after"));
table.AddColumn("space_after ", "int");
return new List<ISchemaObject> {table};
return new List<ISchemaObject> { table };
}
}

internal class spaceBeforeTableNameSchema: testSchema
{
protected override IEnumerable<ISchemaObject> SchemaObjects()
{
var table = new Table(new DbObjectName(SchemaConstants.DefaultSchema,"test_space_before"));
var table = new Table(new PostgresqlObjectName(SchemaConstants.DefaultSchema, "test_space_before"));
table.AddColumn(" space_before", "int");
return new List<ISchemaObject> {table};
return new List<ISchemaObject> { table };
}
}

internal class spaceInNameSchema: testSchema
{
protected override IEnumerable<ISchemaObject> SchemaObjects()
{
var table = new Table(new DbObjectName(SchemaConstants.DefaultSchema,"test_space_in"));
var table = new Table(new PostgresqlObjectName(SchemaConstants.DefaultSchema, "test_space_in"));
table.AddColumn("space inname", "int");
return new List<ISchemaObject> {table};
return new List<ISchemaObject> { table };
}
}
}
}
Original file line number Diff line number Diff line change
@@ -17,12 +17,13 @@ public class Bug_1338_Validate_Null_ForeignKeyDefinition_ReferenceDocumenType: B
[Fact]
public void StorageFeatures_AllActiveFeatures_Should_Not_Throw_With_ExternalForeignKeyDefinitions()
{
theStore.StorageFeatures.AllActiveFeatures(theStore.Tenancy.Default.Database).All(x => x != null).ShouldBeTrue();
theStore.StorageFeatures.AllActiveFeatures(theStore.Tenancy.Default.Database).All(x => x != null)
.ShouldBeTrue();
}

public async Task InitializeAsync()
{
var table = new Table(new DbObjectName(SchemaName, "external_table"));
var table = new Table(new PostgresqlObjectName(SchemaName, "external_table"));
table.AddColumn("id", "integer").AsPrimaryKey();

await using var dbConn = new NpgsqlConnection(ConnectionSource.ConnectionString);
@@ -40,7 +41,6 @@ public async Task InitializeAsync()
}, false);

await theStore.Advanced.Clean.DeleteDocumentsByTypeAsync(typeof(ClassWithExternalForeignKey));

}

public Task DisposeAsync()
@@ -60,10 +60,9 @@ public async Task UnitOfWork_GetTypeDependencies_Should_Not_Throw_With_ExternalF
// and finally, the function that we want to regression test"
// UnitOfWork.GetTypeDependencies(ClassWithExternalForeignKey)
await using var session = theStore.LightweightSession();
session.Insert(new ClassWithExternalForeignKey {Id = 1, ForeignId = 1});
session.Insert(new ClassWithExternalForeignKey { Id = 1, ForeignId = 1 });
await session.SaveChangesAsync();
}

}

public class ClassWithExternalForeignKey
Loading