2012-10-12 12:37:55 -02:00
|
|
|
|
using System;
|
2013-07-08 17:29:26 +10:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Data;
|
2012-12-19 15:23:05 -01:00
|
|
|
|
using System.Linq;
|
2012-11-27 15:07:53 -01:00
|
|
|
|
using Umbraco.Core.Logging;
|
2012-12-27 19:53:01 -01:00
|
|
|
|
using Umbraco.Core.Models.Rdbms;
|
2012-12-19 15:23:05 -01:00
|
|
|
|
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
2012-10-14 12:56:02 -02:00
|
|
|
|
using Umbraco.Core.Persistence.Migrations.Initial;
|
2013-07-26 18:13:56 +10:00
|
|
|
|
using Umbraco.Core.Persistence.Querying;
|
2012-10-19 13:20:57 -02:00
|
|
|
|
using Umbraco.Core.Persistence.SqlSyntax;
|
2012-10-12 12:37:55 -02:00
|
|
|
|
|
|
|
|
|
|
namespace Umbraco.Core.Persistence
|
|
|
|
|
|
{
|
|
|
|
|
|
public static class PetaPocoExtensions
|
|
|
|
|
|
{
|
2012-10-25 18:38:23 -02:00
|
|
|
|
internal delegate void CreateTableEventHandler(string tableName, Database db, TableCreationEventArgs e);
|
|
|
|
|
|
|
|
|
|
|
|
internal static event CreateTableEventHandler NewTable;
|
|
|
|
|
|
|
2012-10-12 12:37:55 -02:00
|
|
|
|
public static void CreateTable<T>(this Database db)
|
|
|
|
|
|
where T : new()
|
|
|
|
|
|
{
|
|
|
|
|
|
var tableType = typeof(T);
|
|
|
|
|
|
CreateTable(db, false, tableType);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static void CreateTable<T>(this Database db, bool overwrite)
|
|
|
|
|
|
where T : new()
|
|
|
|
|
|
{
|
|
|
|
|
|
var tableType = typeof(T);
|
|
|
|
|
|
CreateTable(db, overwrite, tableType);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-07-08 17:29:26 +10:00
|
|
|
|
public static void BulkInsertRecords<T>(this Database db, IEnumerable<T> collection)
|
|
|
|
|
|
{
|
|
|
|
|
|
using (var tr = db.GetTransaction())
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (SqlSyntaxContext.SqlSyntaxProvider is SqlCeSyntaxProvider)
|
|
|
|
|
|
{
|
|
|
|
|
|
//SqlCe doesn't support bulk insert statements!
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var poco in collection)
|
|
|
|
|
|
{
|
|
|
|
|
|
db.Insert(poco);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2013-07-26 18:13:56 +10:00
|
|
|
|
string[] sqlStatements;
|
|
|
|
|
|
var cmds = db.GenerateBulkInsertCommand(collection, db.Connection, out sqlStatements);
|
|
|
|
|
|
for (var i = 0; i < sqlStatements.Length; i++)
|
2013-07-08 17:29:26 +10:00
|
|
|
|
{
|
2013-07-26 18:13:56 +10:00
|
|
|
|
using (var cmd = cmds[i])
|
|
|
|
|
|
{
|
|
|
|
|
|
cmd.CommandText = sqlStatements[i];
|
|
|
|
|
|
cmd.ExecuteNonQuery();
|
|
|
|
|
|
}
|
2013-07-08 17:29:26 +10:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
tr.Complete();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
tr.Dispose();
|
|
|
|
|
|
throw;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-07-26 18:13:56 +10:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Creates a bulk insert command
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <typeparam name="T"></typeparam>
|
|
|
|
|
|
/// <param name="db"></param>
|
|
|
|
|
|
/// <param name="collection"></param>
|
|
|
|
|
|
/// <param name="connection"></param>
|
|
|
|
|
|
/// <param name="sql"></param>
|
|
|
|
|
|
/// <returns>Sql commands with populated command parameters required to execute the sql statement</returns>
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// The limits for number of parameters are 2100 (in sql server, I think there's many more allowed in mysql). So
|
|
|
|
|
|
/// we need to detect that many params and split somehow.
|
|
|
|
|
|
/// For some reason the 2100 limit is not actually allowed even though the exception from sql server mentions 2100 as a max, perhaps it is 2099
|
|
|
|
|
|
/// that is max. I've reduced it to 2000 anyways.
|
|
|
|
|
|
/// </remarks>
|
|
|
|
|
|
internal static IDbCommand[] GenerateBulkInsertCommand<T>(
|
|
|
|
|
|
this Database db,
|
|
|
|
|
|
IEnumerable<T> collection,
|
|
|
|
|
|
IDbConnection connection,
|
|
|
|
|
|
out string[] sql)
|
2013-07-08 17:29:26 +10:00
|
|
|
|
{
|
2013-07-26 18:13:56 +10:00
|
|
|
|
//A filter used below a few times to get all columns except result cols and not the primary key if it is auto-incremental
|
|
|
|
|
|
Func<Database.PocoData, KeyValuePair<string, Database.PocoColumn>, bool> includeColumn = (data, column) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
if (column.Value.ResultColumn) return false;
|
|
|
|
|
|
if (data.TableInfo.AutoIncrement && column.Key == data.TableInfo.PrimaryKey) return false;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2013-07-08 17:29:26 +10:00
|
|
|
|
var pd = Database.PocoData.ForType(typeof(T));
|
|
|
|
|
|
var tableName = db.EscapeTableName(pd.TableInfo.TableName);
|
|
|
|
|
|
|
2013-07-26 18:13:56 +10:00
|
|
|
|
//get all columns to include and format for sql
|
|
|
|
|
|
var cols = string.Join(", ",
|
|
|
|
|
|
pd.Columns
|
|
|
|
|
|
.Where(c => includeColumn(pd, c))
|
|
|
|
|
|
.Select(c => tableName + "." + db.EscapeSqlIdentifier(c.Key)).ToArray());
|
|
|
|
|
|
|
|
|
|
|
|
var itemArray = collection.ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
//calculate number of parameters per item
|
|
|
|
|
|
var paramsPerItem = pd.Columns.Count(i => includeColumn(pd, i));
|
|
|
|
|
|
|
|
|
|
|
|
//Example calc:
|
|
|
|
|
|
// Given: we have 4168 items in the itemArray, each item contains 8 command parameters (values to be inserterted)
|
|
|
|
|
|
// 2100 / 8 = 262.5
|
|
|
|
|
|
// Math.Floor(2100 / 8) = 262 items per trans
|
|
|
|
|
|
// 4168 / 262 = 15.908... = there will be 16 trans in total
|
|
|
|
|
|
|
|
|
|
|
|
//all items will be included if we have disabled db parameters
|
|
|
|
|
|
var itemsPerTrans = Math.Floor(2000.00 / paramsPerItem);
|
|
|
|
|
|
//there will only be one transaction if we have disabled db parameters
|
|
|
|
|
|
var numTrans = Math.Ceiling(itemArray.Length / itemsPerTrans);
|
|
|
|
|
|
|
|
|
|
|
|
var sqlQueries = new List<string>();
|
|
|
|
|
|
var commands = new List<IDbCommand>();
|
|
|
|
|
|
|
|
|
|
|
|
for (var tIndex = 0; tIndex < numTrans; tIndex++)
|
2013-07-08 17:29:26 +10:00
|
|
|
|
{
|
2013-07-26 18:13:56 +10:00
|
|
|
|
var itemsForTrans = itemArray
|
|
|
|
|
|
.Skip(tIndex * (int)itemsPerTrans)
|
|
|
|
|
|
.Take((int)itemsPerTrans);
|
|
|
|
|
|
|
|
|
|
|
|
var cmd = db.CreateCommand(connection, "");
|
|
|
|
|
|
var pocoValues = new List<string>();
|
|
|
|
|
|
var index = 0;
|
|
|
|
|
|
foreach (var poco in itemsForTrans)
|
2013-07-08 17:29:26 +10:00
|
|
|
|
{
|
2013-07-26 18:13:56 +10:00
|
|
|
|
var values = new List<string>();
|
|
|
|
|
|
//get all columns except result cols and not the primary key if it is auto-incremental
|
|
|
|
|
|
foreach (var i in pd.Columns.Where(x => includeColumn(pd, x)))
|
2013-07-17 11:28:46 +10:00
|
|
|
|
{
|
2013-07-26 18:13:56 +10:00
|
|
|
|
db.AddParam(cmd, i.Value.GetValue(poco), "@");
|
|
|
|
|
|
values.Add(string.Format("{0}{1}", "@", index++));
|
2013-07-17 11:28:46 +10:00
|
|
|
|
}
|
2013-07-26 18:13:56 +10:00
|
|
|
|
pocoValues.Add("(" + string.Join(",", values.ToArray()) + ")");
|
2013-07-08 17:29:26 +10:00
|
|
|
|
}
|
2013-07-26 18:13:56 +10:00
|
|
|
|
|
|
|
|
|
|
var sqlResult = string.Format("INSERT INTO {0} ({1}) VALUES {2}", tableName, cols, string.Join(", ", pocoValues));
|
|
|
|
|
|
sqlQueries.Add(sqlResult);
|
|
|
|
|
|
commands.Add(cmd);
|
2013-07-08 17:29:26 +10:00
|
|
|
|
}
|
2013-07-26 18:13:56 +10:00
|
|
|
|
|
|
|
|
|
|
sql = sqlQueries.ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
return commands.ToArray();
|
2013-07-08 17:29:26 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-10-12 12:37:55 -02:00
|
|
|
|
public static void CreateTable(this Database db, bool overwrite, Type modelType)
|
|
|
|
|
|
{
|
2012-10-19 13:20:57 -02:00
|
|
|
|
var tableDefinition = DefinitionFactory.GetTableDefinition(modelType);
|
2012-12-19 15:23:05 -01:00
|
|
|
|
var tableName = tableDefinition.Name;
|
2012-10-12 12:37:55 -02:00
|
|
|
|
|
2013-03-09 10:43:34 -01:00
|
|
|
|
string createSql = SqlSyntaxContext.SqlSyntaxProvider.Format(tableDefinition);
|
|
|
|
|
|
string createPrimaryKeySql = SqlSyntaxContext.SqlSyntaxProvider.FormatPrimaryKey(tableDefinition);
|
|
|
|
|
|
var foreignSql = SqlSyntaxContext.SqlSyntaxProvider.Format(tableDefinition.ForeignKeys);
|
|
|
|
|
|
var indexSql = SqlSyntaxContext.SqlSyntaxProvider.Format(tableDefinition.Indexes);
|
2012-10-18 11:49:44 -02:00
|
|
|
|
|
2012-10-12 12:37:55 -02:00
|
|
|
|
var tableExist = db.TableExist(tableName);
|
2012-10-19 13:20:57 -02:00
|
|
|
|
if (overwrite && tableExist)
|
2012-10-12 12:37:55 -02:00
|
|
|
|
{
|
|
|
|
|
|
db.DropTable(tableName);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-12-27 18:52:47 -01:00
|
|
|
|
if (tableExist == false)
|
2012-10-12 12:37:55 -02:00
|
|
|
|
{
|
2012-12-05 12:37:00 -01:00
|
|
|
|
using (var transaction = db.GetTransaction())
|
2012-10-25 18:38:23 -02:00
|
|
|
|
{
|
2012-12-05 12:37:00 -01:00
|
|
|
|
//Execute the Create Table sql
|
|
|
|
|
|
int created = db.Execute(new Sql(createSql));
|
2012-12-19 15:23:05 -01:00
|
|
|
|
LogHelper.Info<Database>(string.Format("Create Table sql {0}:\n {1}", created, createSql));
|
2012-12-04 08:16:10 -01:00
|
|
|
|
|
2012-12-05 12:37:00 -01:00
|
|
|
|
//If any statements exists for the primary key execute them here
|
|
|
|
|
|
if (!string.IsNullOrEmpty(createPrimaryKeySql))
|
2012-12-19 15:23:05 -01:00
|
|
|
|
{
|
|
|
|
|
|
int createdPk = db.Execute(new Sql(createPrimaryKeySql));
|
|
|
|
|
|
LogHelper.Info<Database>(string.Format("Primary Key sql {0}:\n {1}", createdPk, createPrimaryKeySql));
|
|
|
|
|
|
}
|
2012-12-05 08:39:31 -01:00
|
|
|
|
|
2012-12-05 12:37:00 -01:00
|
|
|
|
//Fires the NewTable event, which is used internally to insert base data before adding constrants to the schema
|
|
|
|
|
|
if (NewTable != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var e = new TableCreationEventArgs();
|
2012-12-05 14:18:08 -01:00
|
|
|
|
|
|
|
|
|
|
//Turn on identity insert if db provider is not mysql
|
2013-03-09 10:43:34 -01:00
|
|
|
|
if (SqlSyntaxContext.SqlSyntaxProvider.SupportsIdentityInsert() && tableDefinition.Columns.Any(x => x.IsIdentity))
|
|
|
|
|
|
db.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(tableName))));
|
2013-07-08 17:29:26 +10:00
|
|
|
|
|
2012-12-05 14:18:08 -01:00
|
|
|
|
//Call the NewTable-event to trigger the insert of base/default data
|
2012-12-05 12:37:00 -01:00
|
|
|
|
NewTable(tableName, db, e);
|
2012-12-05 14:18:08 -01:00
|
|
|
|
|
|
|
|
|
|
//Turn off identity insert if db provider is not mysql
|
2013-03-09 10:43:34 -01:00
|
|
|
|
if (SqlSyntaxContext.SqlSyntaxProvider.SupportsIdentityInsert() && tableDefinition.Columns.Any(x => x.IsIdentity))
|
|
|
|
|
|
db.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF;", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(tableName))));
|
2012-12-27 19:53:01 -01:00
|
|
|
|
|
|
|
|
|
|
//Special case for MySql
|
2013-03-09 10:43:34 -01:00
|
|
|
|
if (SqlSyntaxContext.SqlSyntaxProvider is MySqlSyntaxProvider && tableName.Equals("umbracoUser"))
|
2012-12-27 19:53:01 -01:00
|
|
|
|
{
|
|
|
|
|
|
db.Update<UserDto>("SET id = @IdAfter WHERE id = @IdBefore AND userLogin = @Login", new { IdAfter = 0, IdBefore = 1, Login = "admin" });
|
|
|
|
|
|
}
|
2012-12-05 12:37:00 -01:00
|
|
|
|
}
|
2012-10-30 15:03:58 -01:00
|
|
|
|
|
2012-12-05 12:37:00 -01:00
|
|
|
|
//Loop through foreignkey statements and execute sql
|
|
|
|
|
|
foreach (var sql in foreignSql)
|
|
|
|
|
|
{
|
|
|
|
|
|
int createdFk = db.Execute(new Sql(sql));
|
2012-12-19 15:23:05 -01:00
|
|
|
|
LogHelper.Info<Database>(string.Format("Create Foreign Key sql {0}:\n {1}", createdFk, sql));
|
2012-12-05 12:37:00 -01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//Loop through index statements and execute sql
|
|
|
|
|
|
foreach (var sql in indexSql)
|
2012-10-30 15:03:58 -01:00
|
|
|
|
{
|
2012-12-05 12:37:00 -01:00
|
|
|
|
int createdIndex = db.Execute(new Sql(sql));
|
2012-12-19 15:23:05 -01:00
|
|
|
|
LogHelper.Info<Database>(string.Format("Create Index sql {0}:\n {1}", createdIndex, sql));
|
2012-10-30 15:03:58 -01:00
|
|
|
|
}
|
2012-12-05 12:37:00 -01:00
|
|
|
|
|
|
|
|
|
|
transaction.Complete();
|
2012-10-30 15:03:58 -01:00
|
|
|
|
}
|
2012-10-12 12:37:55 -02:00
|
|
|
|
}
|
2012-11-27 15:07:53 -01:00
|
|
|
|
|
|
|
|
|
|
LogHelper.Info<Database>(string.Format("New table '{0}' was created", tableName));
|
2012-10-12 12:37:55 -02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static void DropTable<T>(this Database db)
|
|
|
|
|
|
where T : new()
|
|
|
|
|
|
{
|
2013-07-08 17:29:26 +10:00
|
|
|
|
Type type = typeof(T);
|
2012-10-12 12:37:55 -02:00
|
|
|
|
var tableNameAttribute = type.FirstAttribute<TableNameAttribute>();
|
|
|
|
|
|
if (tableNameAttribute == null)
|
|
|
|
|
|
throw new Exception(
|
|
|
|
|
|
string.Format(
|
|
|
|
|
|
"The Type '{0}' does not contain a TableNameAttribute, which is used to find the name of the table to drop. The operation could not be completed.",
|
|
|
|
|
|
type.Name));
|
|
|
|
|
|
|
|
|
|
|
|
string tableName = tableNameAttribute.Value;
|
|
|
|
|
|
DropTable(db, tableName);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static void DropTable(this Database db, string tableName)
|
|
|
|
|
|
{
|
2013-03-19 23:46:13 +06:00
|
|
|
|
var sql = new Sql(string.Format(
|
|
|
|
|
|
SqlSyntaxContext.SqlSyntaxProvider.DropTable,
|
|
|
|
|
|
SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(tableName)));
|
|
|
|
|
|
db.Execute(sql);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static void TruncateTable(this Database db, string tableName)
|
|
|
|
|
|
{
|
|
|
|
|
|
var sql = new Sql(string.Format(
|
|
|
|
|
|
SqlSyntaxContext.SqlSyntaxProvider.TruncateTable,
|
|
|
|
|
|
SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(tableName)));
|
2012-10-12 12:37:55 -02:00
|
|
|
|
db.Execute(sql);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static bool TableExist(this Database db, string tableName)
|
|
|
|
|
|
{
|
2013-03-09 10:43:34 -01:00
|
|
|
|
return SqlSyntaxContext.SqlSyntaxProvider.DoesTableExist(db, tableName);
|
2012-10-12 12:37:55 -02:00
|
|
|
|
}
|
2012-10-14 12:56:02 -02:00
|
|
|
|
|
2012-12-19 12:14:49 -01:00
|
|
|
|
public static bool TableExist(this UmbracoDatabase db, string tableName)
|
|
|
|
|
|
{
|
2013-03-09 10:43:34 -01:00
|
|
|
|
return SqlSyntaxContext.SqlSyntaxProvider.DoesTableExist(db, tableName);
|
2012-10-12 12:37:55 -02:00
|
|
|
|
}
|
2012-10-14 12:56:02 -02:00
|
|
|
|
|
2012-11-29 12:40:48 -01:00
|
|
|
|
public static void CreateDatabaseSchema(this Database db)
|
2012-10-14 12:56:02 -02:00
|
|
|
|
{
|
2013-02-08 11:37:50 -01:00
|
|
|
|
CreateDatabaseSchema(db, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-03-14 02:43:17 +04:00
|
|
|
|
internal static void UninstallDatabaseSchema(this Database db)
|
|
|
|
|
|
{
|
|
|
|
|
|
var creation = new DatabaseSchemaCreation(db);
|
|
|
|
|
|
creation.UninstallDatabaseSchema();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-02-08 11:37:50 -01:00
|
|
|
|
internal static void CreateDatabaseSchema(this Database db, bool guardConfiguration)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (guardConfiguration && ApplicationContext.Current.IsConfigured)
|
2013-02-07 13:56:00 -01:00
|
|
|
|
throw new Exception("Umbraco is already configured!");
|
|
|
|
|
|
|
2012-10-25 18:38:23 -02:00
|
|
|
|
NewTable += PetaPocoExtensions_NewTable;
|
|
|
|
|
|
|
2012-11-27 15:07:53 -01:00
|
|
|
|
LogHelper.Info<Database>("Initializing database schema creation");
|
|
|
|
|
|
|
2012-12-04 08:16:10 -01:00
|
|
|
|
var creation = new DatabaseSchemaCreation(db);
|
2012-10-23 07:30:00 -02:00
|
|
|
|
creation.InitializeDatabaseSchema();
|
2012-11-01 15:06:35 -01:00
|
|
|
|
|
2013-02-06 09:04:46 -01:00
|
|
|
|
LogHelper.Info<Database>("Finalized database schema creation");
|
|
|
|
|
|
|
2012-11-01 15:06:35 -01:00
|
|
|
|
NewTable -= PetaPocoExtensions_NewTable;
|
2012-10-14 12:56:02 -02:00
|
|
|
|
}
|
2012-10-24 12:38:13 -02:00
|
|
|
|
|
2012-12-27 18:52:47 -01:00
|
|
|
|
public static DatabaseProviders GetDatabaseProvider(this Database db)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ApplicationContext.Current.DatabaseContext.DatabaseProvider;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-10-29 09:49:31 -01:00
|
|
|
|
private static void PetaPocoExtensions_NewTable(string tableName, Database db, TableCreationEventArgs e)
|
2012-10-24 12:38:13 -02:00
|
|
|
|
{
|
|
|
|
|
|
var baseDataCreation = new BaseDataCreation(db);
|
2012-10-25 18:38:23 -02:00
|
|
|
|
baseDataCreation.InitializeBaseData(tableName);
|
2012-10-24 12:38:13 -02:00
|
|
|
|
}
|
2012-10-12 12:37:55 -02:00
|
|
|
|
}
|
2012-10-25 18:38:23 -02:00
|
|
|
|
|
2013-07-08 17:29:26 +10:00
|
|
|
|
internal class TableCreationEventArgs : System.ComponentModel.CancelEventArgs { }
|
2012-10-12 12:37:55 -02:00
|
|
|
|
}
|