Skip to content

Commit

Permalink
#1143 Optimized the RepoDb.PostgreSql InsertAll operation.
Browse files Browse the repository at this point in the history
  • Loading branch information
mikependon committed Mar 22, 2023
1 parent 8901d5d commit 0819860
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -309,9 +309,11 @@ public void TestPostgreSqlStatementBuilderCreateInsertAll()
3,
null,
null);
var expected = "INSERT INTO \"Table\" ( \"Id\", \"Name\", \"Address\" ) VALUES ( @Id, @Name, @Address ) RETURNING NULL AS \"Result\", @__RepoDb_OrderColumn_0 AS \"OrderColumn\" ; " +
"INSERT INTO \"Table\" ( \"Id\", \"Name\", \"Address\" ) VALUES ( @Id_1, @Name_1, @Address_1 ) RETURNING NULL AS \"Result\", @__RepoDb_OrderColumn_1 AS \"OrderColumn\" ; " +
"INSERT INTO \"Table\" ( \"Id\", \"Name\", \"Address\" ) VALUES ( @Id_2, @Name_2, @Address_2 ) RETURNING NULL AS \"Result\", @__RepoDb_OrderColumn_2 AS \"OrderColumn\" ;";
var expected = "INSERT INTO \"Table\" ( \"Id\", \"Name\", \"Address\" ) " +
"VALUES " +
"( @Id, @Name, @Address ) , " +
"( @Id_1, @Name_1, @Address_1 ) , " +
"( @Id_2, @Name_2, @Address_2 ) ;";

// Assert
Assert.AreEqual(expected, query);
Expand All @@ -329,9 +331,12 @@ public void TestPostgreSqlStatementBuilderCreateInserAlltWithPrimary()
3,
new DbField("Id", true, false, false, typeof(int), null, null, null, null),
null);
var expected = "INSERT INTO \"Table\" ( \"Id\", \"Name\", \"Address\" ) VALUES ( @Id, @Name, @Address ) RETURNING CAST(\"Id\" AS INTEGER) AS \"Result\", @__RepoDb_OrderColumn_0 AS \"OrderColumn\" ; " +
"INSERT INTO \"Table\" ( \"Id\", \"Name\", \"Address\" ) VALUES ( @Id_1, @Name_1, @Address_1 ) RETURNING CAST(\"Id\" AS INTEGER) AS \"Result\", @__RepoDb_OrderColumn_1 AS \"OrderColumn\" ; " +
"INSERT INTO \"Table\" ( \"Id\", \"Name\", \"Address\" ) VALUES ( @Id_2, @Name_2, @Address_2 ) RETURNING CAST(\"Id\" AS INTEGER) AS \"Result\", @__RepoDb_OrderColumn_2 AS \"OrderColumn\" ;";
var expected = "INSERT INTO \"Table\" ( \"Id\", \"Name\", \"Address\" ) " +
"VALUES " +
"( @Id, @Name, @Address ) , " +
"( @Id_1, @Name_1, @Address_1 ) , " +
"( @Id_2, @Name_2, @Address_2 ) " +
"RETURNING CAST(\"Id\" AS INTEGER) AS \"Result\" ;";

// Assert
Assert.AreEqual(expected, query);
Expand All @@ -349,9 +354,12 @@ public void TestPostgreSqlStatementBuilderCreateInsertAllWithIdentity()
3,
null,
new DbField("Id", false, true, false, typeof(int), null, null, null, null));
var expected = "INSERT INTO \"Table\" ( \"Name\", \"Address\" ) VALUES ( @Name, @Address ) RETURNING CAST(\"Id\" AS INTEGER) AS \"Result\", @__RepoDb_OrderColumn_0 AS \"OrderColumn\" ; " +
"INSERT INTO \"Table\" ( \"Name\", \"Address\" ) VALUES ( @Name_1, @Address_1 ) RETURNING CAST(\"Id\" AS INTEGER) AS \"Result\", @__RepoDb_OrderColumn_1 AS \"OrderColumn\" ; " +
"INSERT INTO \"Table\" ( \"Name\", \"Address\" ) VALUES ( @Name_2, @Address_2 ) RETURNING CAST(\"Id\" AS INTEGER) AS \"Result\", @__RepoDb_OrderColumn_2 AS \"OrderColumn\" ;";
var expected = "INSERT INTO \"Table\" ( \"Name\", \"Address\" ) " +
"VALUES " +
"( @Name, @Address ) , " +
"( @Name_1, @Address_1 ) , " +
"( @Name_2, @Address_2 ) " +
"RETURNING CAST(\"Id\" AS INTEGER) AS \"Result\" ;";

// Assert
Assert.AreEqual(expected, query);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,41 +223,128 @@ public override string CreateInsertAll(string tableName,
DbField identityField = null,
string hints = null)
{
// Call the base
var commandText = base.CreateInsertAll(tableName,
fields,
batchSize,
primaryField,
identityField,
hints);
#region Old

// PGSQL is failing if we are using sub-table expression.
// See: https://github.com/mikependon/RepoDB/issues/1143

//// Call the base
//var commandText = base.CreateInsertAll(tableName,
// fields,
// batchSize,
// primaryField,
// identityField,
// hints);

//// Variables needed
//var keyColumn = GetReturnKeyColumnAsDbField(primaryField, identityField);

//// Set the return value
//if (keyColumn != null)
//{
// var dbType = new ClientTypeToDbTypeResolver().Resolve(keyColumn.Type);
// var databaseType = (dbType != null) ? new DbTypeToPostgreSqlStringNameResolver().Resolve(dbType.Value) : null;
// var returnValue = keyColumn == null ? "NULL" :
// string.IsNullOrWhiteSpace(databaseType) ?
// keyColumn.Name.AsQuoted(DbSetting) :
// string.Concat("CAST(", keyColumn.Name.AsQuoted(DbSetting), " AS ", databaseType, ")");
// var result = string.Concat("RETURNING ", returnValue, $" AS ", "Result".AsQuoted(DbSetting), " ");
// commandText = commandText.Insert(commandText.Length - 1, result);
//}

//// Return the query
//return commandText;

#endregion

// Variables needed
var keyColumn = GetReturnKeyColumnAsDbField(primaryField, identityField);
var returnValue = "NULL";
// Ensure with guards
GuardTableName(tableName);
GuardHints(hints);
GuardPrimary(primaryField);
GuardIdentity(identityField);

// Key Column
if (keyColumn != null)
// Validate the multiple statement execution
ValidateMultipleStatementExecution(batchSize);

// Verify the fields
if (fields?.Any() != true)
{
var databaseType = GetDatabaseType(keyColumn);
returnValue = string.IsNullOrWhiteSpace(databaseType) ?
keyColumn.Name.AsQuoted(DbSetting) : $"CAST({keyColumn.Name.AsQuoted(DbSetting)} AS {databaseType})";
throw new EmptyException("The list of fields cannot be null or empty.");
}

// Set the return value
var commandTexts = new List<string>();
var splitted = commandText.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
// Primary Key
if (primaryField != null &&
primaryField.HasDefaultValue == false &&
!string.Equals(primaryField.Name, identityField?.Name, StringComparison.OrdinalIgnoreCase))
{
var isPresent = fields
.FirstOrDefault(f =>
string.Equals(f.Name, primaryField.Name, StringComparison.OrdinalIgnoreCase)) != null;

if (isPresent == false)
{
throw new PrimaryFieldNotFoundException($"As the primary field '{primaryField.Name}' is not an identity nor has a default value, it must be present on the insert operation.");
}
}

// Insertable fields
var insertableFields = fields
.Where(f =>
!string.Equals(f.Name, identityField?.Name, StringComparison.OrdinalIgnoreCase));

// Initialize the builder
var builder = new QueryBuilder();

for (var index = 0; index < splitted.Length; index++)
// Build the query
builder.Clear();

// Compose
builder
.Insert()
.Into()
.TableNameFrom(tableName, DbSetting)
.HintsFrom(hints)
.OpenParen()
.FieldsFrom(insertableFields, DbSetting)
.CloseParen()
.Values();

// Iterate the indexes
for (var index = 0; index < batchSize; index++)
{
var line = splitted[index].Trim();
commandTexts.Add(string.Concat(line, " RETURNING ", returnValue, " AS ", "Result".AsQuoted(DbSetting), ", ",
$"{DbSetting.ParameterPrefix}__RepoDb_OrderColumn_{index} AS ", "OrderColumn".AsQuoted(DbSetting), " ;"));
builder
.OpenParen()
.ParametersFrom(insertableFields, index, DbSetting)
.CloseParen();

if (index < batchSize - 1)
{
builder
.WriteText(",");
}
}

commandText = commandTexts.Join(" ");
// Variables needed
var keyColumn = GetReturnKeyColumnAsDbField(primaryField, identityField);

// Set the return value
if (keyColumn != null)
{
var dbType = new ClientTypeToDbTypeResolver().Resolve(keyColumn.Type);
var databaseType = (dbType != null) ? new DbTypeToPostgreSqlStringNameResolver().Resolve(dbType.Value) : null;
var returnValue = keyColumn == null ? "NULL" :
string.IsNullOrWhiteSpace(databaseType) ?
keyColumn.Name.AsQuoted(DbSetting) :
string.Concat("CAST(", keyColumn.Name.AsQuoted(DbSetting), " AS ", databaseType, ")");
builder
.WriteText(
string.Concat("RETURNING ", returnValue, $" AS ", "Result".AsQuoted(DbSetting)));
}

// Return the query
return commandText;
return builder
.End()
.GetString();
}

#endregion
Expand Down

0 comments on commit 0819860

Please sign in to comment.