Skip to content

Add support for identity primary keys#145

Merged
slewis74 merged 30 commits into
masterfrom
ap/identity-pk-support
Jul 6, 2021
Merged

Add support for identity primary keys#145
slewis74 merged 30 commits into
masterfrom
ap/identity-pk-support

Conversation

@APErebus
Copy link
Copy Markdown
Collaborator

This PR adds support for identity number (short/int/long) primary keys.

There is a new Identity() method that chains off the Id() method in the DocumentMap. This marks the Id column as an identity column.

e.g. Id(c => c.Id).Identity()

This works by retrieving the identity value as part of the INSERT and updating the Id on the document, very similar to how the RowVersion is updated.

As there are now 2 output parameters, we need to change to use a table parameter to store them in. This also handles multiple inserts in a single statement

@APErebus APErebus force-pushed the ap/identity-pk-support branch from 8fdd56d to 92ba9ed Compare June 9, 2021 00:03
@APErebus APErebus marked this pull request as ready for review June 10, 2021 06:02
{
public DocumentWithIdentityIdAndRowVersionMap()
{
Id(t => t.Id).Identity();
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how you define an IDENTITY primary key column

{
SchemaGenerator.WriteTableSchema(map.Build(), null, schema);
}
schema.AppendLine($"ALTER TABLE [TestSchema].[{nameof(DocumentWithRowVersion)}] ADD [RowVersion] rowversion");
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handled in SchemaGenerator now

}

protected async Task<TResult[]> ReadResultsAsync<TResult>(PreparedCommand preparedCommand, Func<DbDataReader, TResult> mapper)
protected async Task<TResult[]> ReadResultsAsync<TResult>(PreparedCommand preparedCommand, Func<DbDataReader, Task<TResult>> mapper)
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReadResultsAsync now is an async mapper func (async all the way down)

Comment on lines +217 to +221
public string AllocateId<TDocument>()
{
var mapping = configuration.DocumentMaps.Resolve<TDocument>();
return AllocateId(mapping);
}
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handy new overload (save a lot of AllocateId(typeof(MyDocument)))

@@ -216,7 +230,7 @@ public string AllocateId(string tableName, string idPrefix)
return AllocateId(tableName, key => $"{idPrefix}-{key}");
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This overload is a problem. Ideally you shouldn't be able to Allocate an Id for an Identity column (the db is doing it for you) and it should throw an exception, however you only receive a string name, so we either have to look up the mapping (which we are already doing above) or we just have to let the code fail badly.

I would be in favour of removing the public API string AllocateId(string tableName, string idPrefix) and only allowing public API's that take something that has a DocumentMap, where we can change this.

How often are you allocating Id's for a table that you don't have a document map for... seems weird

Copy link
Copy Markdown
Contributor

@slewis74 slewis74 Jun 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Octopus server does exactly this to allocate Tag Ids. They are technically a child of TagSet, but the Ids are issued as though it was a table of its own. Agreed it is an edge case usage, but it is used. Handling for the prefixes has been cleaned up in #149, which builds on top of this PR's work.

DataModificationOutput[] ExecuteDataModification(PreparedCommand command)
{
if (!command.Mapping.IsRowVersioningEnabled)
if (!command.Mapping.HasModificationOutputs)
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are 2 different possible modification outputs, the RowVersion and/or the new Id


namespace Nevermore.Mapping
{
public class IdColumnMapping : ColumnMapping, IIdColumnMappingBuilder
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a new inherited version of ColumnMapping, specific to the Id column, allowing for specific Id column mapping

Comment on lines -71 to -73
catch (Exception ex)
{
throw new Exception($"Exception occurred while executing a reader for `{command.CommandText}`", ex);
}
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This exception handler was bad:tm:

It was catching unique constraint exception too early in the call chain and hiding the underlying sql error. The reason this changed in this PR is that previously it only threw the exception when the reader made the 1st call to .Read(), but the exception is now thrown here because the generated SQL is different and the exception is thrown at this point, rather than when read.

Comment on lines +42 to +43
if (mapping.IdColumn.IsIdentity && options.CustomAssignedId != null)
throw new InvalidOperationException($"{nameof(InsertOptions)}.{nameof(InsertOptions.CustomAssignedId)} is not supported for identity Id columns.");
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You obv can't set an custom assigned Id with an identity column

Comment on lines +496 to +498
[typeof(short)] = "smallint",
[typeof(int)] = "int",
[typeof(long)] = "bigint"
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because we have to define a TABLE row version, we need the sql type that matches our Id column.

Copy link
Copy Markdown
Contributor

@slewis74 slewis74 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once #149 merges in to here I think this is going to be 👍

@slewis74 slewis74 force-pushed the ap/identity-pk-support branch from dc6bf6f to fd85926 Compare June 24, 2021 23:04
slewis74 and others added 20 commits June 25, 2021 09:12
…ver uses this a lot for related documents determination
…yKeyHandler, it only makes sense for the IStringBasedPrimitivePrimaryKeyHandler handlers
…builders, and take config in to make sure that every document map ends up with an IdColumn that has a PrimaryKeyHandler and specifies whether it is an Identity
slewis74 added 4 commits June 25, 2021 09:12
…eated to pass to the `KeyHandler` method for the IdColumn, to customise the id handling for a given document map
…ration code, it didn't cater for custom primary key handlers. The handlers now supply it with the information it needs.
@slewis74
Copy link
Copy Markdown
Contributor

slewis74 commented Jul 2, 2021

This page in the Wiki should be updated after this PR merges

@slewis74 slewis74 merged commit 3256d56 into master Jul 6, 2021
@slewis74 slewis74 deleted the ap/identity-pk-support branch July 6, 2021 23:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants