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
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion Analysis.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
2 changes: 1 addition & 1 deletion docs/configuration/multitenancy.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
32 changes: 14 additions & 18 deletions docs/scenarios/using-sequence-for-unique-id.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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):
Expand All @@ -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:
Expand All @@ -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:
Expand All @@ -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:
Expand All @@ -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
Expand Up @@ -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:
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/schema/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ storeOptions.CreateDatabasesForTenants(c =>
});
});
```
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/CoreTests/create_database_Tests.cs#L41-L57' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_marten_create_database' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/CoreTests/create_database_Tests.cs#L42-L60' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_marten_create_database' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Databases are checked for existence upon store initialization. By default, connection attempts are made against the databases specified for tenants. If a connection attempt results in an invalid catalog error (3D000), database creation is triggered. `ITenantDatabaseCreationExpressions.CheckAgainstPgDatabase` can be used to alter this behavior to check for database existence from `pg_database`.
Expand Down
2 changes: 1 addition & 1 deletion src/CoreTests/CoreTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
25 changes: 25 additions & 0 deletions src/CoreTests/Schema/CaseSensitveSchemaGenerationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Threading.Tasks;
using Marten.Testing.Documents;
using Marten.Testing.Harness;
using Xunit;

namespace CoreTests.Schema;

public class CaseSensitveSchemaGenerationTests: OneOffConfigurationsContext
{
[Fact]
public async Task AppliesChanges()
{
StoreOptions(options =>
{
options.Advanced.UseCaseSensitiveQualifiedNamesWhenApplyingChanges = true;

options.Schema
.For<User>()
.Index(u => u.FirstName, i => i.Name = "cAsEsEnSiTivE");
});

await theStore.Storage.ApplyAllConfiguredChangesToDatabaseAsync();
await theStore.Storage.Database.AssertDatabaseMatchesConfigurationAsync();
}
}
4 changes: 3 additions & 1 deletion src/CoreTests/adding_custom_schema_objects.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
20 changes: 12 additions & 8 deletions src/CoreTests/create_database_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
namespace CoreTests;

[Collection("multi-tenancy")]
public class create_database_Tests : IDisposable
public class create_database_Tests: IDisposable
{
[Fact]
public async Task can_create_new_database_when_one_does_not_exist_for_default_tenant_with_DatabaseGenerator()
Expand All @@ -22,10 +22,10 @@ public async Task can_create_new_database_when_one_does_not_exist_for_default_te

TryDropDb(dbName);

using (var store1 = DocumentStore.For(_ =>
{
_.Connection(dbToCreateConnectionString);
}))
await using (var store1 = DocumentStore.For(_ =>
{
_.Connection(dbToCreateConnectionString);
}))
{
await Should.ThrowAsync<PostgresException>(async () =>
{
Expand All @@ -35,10 +35,12 @@ await Should.ThrowAsync<PostgresException>(async () =>

var dbCreated = false;

using var store = DocumentStore.For(storeOptions =>
await using var store = DocumentStore.For(storeOptions =>
{
storeOptions.Connection(dbToCreateConnectionString);

#region sample_marten_create_database

storeOptions.CreateDatabasesForTenants(c =>
{
// Specify a db to which to connect in case database needs to be created.
Expand All @@ -54,11 +56,13 @@ await Should.ThrowAsync<PostgresException>(async () =>
dbCreated = true;
});
});

#endregion
});
// That should be done with Hosted Service, but let's test it also here
var databaseGenerator = new DatabaseGenerator();
await databaseGenerator.CreateDatabasesAsync(store.Tenancy, store.Options.CreateDatabases).ConfigureAwait(false);
await databaseGenerator.CreateDatabasesAsync(store.Tenancy, store.Options.CreateDatabases)
.ConfigureAwait(false);

await store.Advanced.Clean.CompletelyRemoveAllAsync();

Expand Down Expand Up @@ -86,7 +90,6 @@ public void can_use_existing_database_without_calling_into_create()
.ConnectionLimit(-1)
.OnDatabaseCreated(___ => dbCreated = true);
});

});

store.Advanced.Clean.CompletelyRemoveAll();
Expand Down Expand Up @@ -142,6 +145,7 @@ private static bool TryDropDb(string db)
{
return false;
}

return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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;
}
Expand Down
Loading
Loading