DocSpace-client/common/ASC.Data.Backup.Core/Tasks/Modules/ModuleSpecificsBase.cs

271 lines
11 KiB
C#
Raw Normal View History

2020-05-20 15:14:44 +00:00
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
*
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
*
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
*
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
*
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
* relevant author attributions when distributing the software. If the display of the logo in its graphic
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
*/
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.IO;
using System.Linq;
2020-08-27 14:01:37 +00:00
2020-05-20 15:14:44 +00:00
using ASC.Data.Backup.Exceptions;
using ASC.Data.Backup.Tasks.Data;
namespace ASC.Data.Backup.Tasks.Modules
{
public abstract class ModuleSpecificsBase : IModuleSpecifics
{
public abstract ModuleName ModuleName { get; }
private string _connectionStringName;
public virtual string ConnectionStringName
{
2020-09-29 11:51:34 +00:00
get { return _connectionStringName ??= ModuleName.ToString().ToLower(); }
2020-05-20 15:14:44 +00:00
}
2020-08-27 14:01:37 +00:00
2020-05-20 15:14:44 +00:00
public abstract IEnumerable<TableInfo> Tables { get; }
public abstract IEnumerable<RelationInfo> TableRelations { get; }
2020-09-28 15:42:03 +00:00
private readonly Helpers helpers;
2022-01-24 09:57:01 +00:00
protected ModuleSpecificsBase(Helpers helpers)
2020-05-26 08:52:47 +00:00
{
this.helpers = helpers;
}
2020-05-20 15:14:44 +00:00
public IEnumerable<TableInfo> GetTablesOrdered()
{
var notOrderedTables = new List<TableInfo>(Tables);
2020-05-29 14:50:39 +00:00
var totalTablesCount = notOrderedTables.Count;
var orderedTablesCount = 0;
2020-05-20 15:14:44 +00:00
while (orderedTablesCount < totalTablesCount)
{
2020-05-29 14:50:39 +00:00
var orderedTablesCountBeforeIter = orderedTablesCount; // ensure we not in infinite loop...
2020-05-20 15:14:44 +00:00
2020-05-29 14:50:39 +00:00
var i = 0;
2020-05-20 15:14:44 +00:00
while (i < notOrderedTables.Count)
{
var table = notOrderedTables[i];
var parentTables = TableRelations
.Where(x => x.FitsForTable(table.Name) && !x.IsExternal() && !x.IsSelfRelation() && x.Importance != RelationImportance.Low)
.Select(x => x.ParentTable);
if (parentTables.All(x => notOrderedTables.All(y => !string.Equals(y.Name, x, StringComparison.InvariantCultureIgnoreCase))))
{
notOrderedTables.RemoveAt(i);
orderedTablesCount++;
yield return table;
}
else
{
i++;
}
}
if (orderedTablesCountBeforeIter == orderedTablesCount) // ensure we not in infinite loop...
throw ThrowHelper.CantOrderTables(notOrderedTables.Select(x => x.Name));
}
}
public DbCommand CreateSelectCommand(DbConnection connection, int tenantId, TableInfo table, int limit, int offset)
{
2020-05-26 08:52:47 +00:00
var command = connection.CreateCommand();
command.CommandText = string.Format("select t.* from {0} as t {1} limit {2},{3};", table.Name, GetSelectCommandConditionText(tenantId, table), offset, limit);
return command;
2020-05-20 15:14:44 +00:00
}
public DbCommand CreateDeleteCommand(DbConnection connection, int tenantId, TableInfo table)
{
2020-05-26 08:52:47 +00:00
var command = connection.CreateCommand();
2022-01-14 13:12:37 +00:00
command.CommandText = $"delete t.* from {table.Name} as t {GetDeleteCommandConditionText(tenantId, table)};";
2020-05-26 08:52:47 +00:00
return command;
2020-05-20 15:14:44 +00:00
}
public DbCommand CreateInsertCommand(bool dump, DbConnection connection, ColumnMapper columnMapper, TableInfo table, DataRowInfo row)
{
if (table.InsertMethod == InsertMethod.None)
return null;
2020-10-12 13:52:31 +00:00
if (!TryPrepareRow(dump, connection, columnMapper, table, row, out var valuesForInsert))
2020-05-20 15:14:44 +00:00
return null;
var columns = valuesForInsert.Keys.Intersect(table.Columns).ToArray();
2022-01-14 13:12:37 +00:00
var insert = table.InsertMethod != InsertMethod.Ignore
2020-05-20 15:14:44 +00:00
? table.InsertMethod.ToString().ToLower()
2022-01-14 13:12:37 +00:00
: "insert ignore";
var insertCommantText = $"{insert} into {table.Name}({string.Join(",", columns)}) values({string.Join(",", columns.Select(c => "@" + c))});";
2020-05-20 15:14:44 +00:00
2020-05-26 08:52:47 +00:00
var command = connection.CreateCommand();
command.CommandText = insertCommantText;
2020-05-20 15:14:44 +00:00
foreach (var parameter in valuesForInsert)
{
2020-10-12 19:39:23 +00:00
AddParameter(command, parameter.Key, parameter.Value);
2020-05-20 15:14:44 +00:00
}
return command;
}
2020-05-26 08:52:47 +00:00
public DbCommand AddParameter(DbCommand command, string name, object value)
{
var p = command.CreateParameter();
if (!string.IsNullOrEmpty(name))
{
2022-01-18 12:40:28 +00:00
p.ParameterName = name.StartsWith('@') ? name : "@" + name;
2020-05-26 08:52:47 +00:00
}
2020-05-20 15:14:44 +00:00
2020-05-26 08:52:47 +00:00
p.Value = GetParameterValue(value);
2020-10-12 19:39:23 +00:00
command.Parameters.Add(p);
2020-05-26 08:52:47 +00:00
return command;
}
public object GetParameterValue(object value)
{
if (value == null)
{
return DBNull.Value;
}
2020-09-28 14:39:13 +00:00
if (value is Enum @enum)
2020-05-26 08:52:47 +00:00
{
return @enum.ToString("d");
}
2020-09-28 14:45:05 +00:00
if (value is DateTime d)
2020-05-26 08:52:47 +00:00
{
return new DateTime(d.Year, d.Month, d.Day, d.Hour, d.Minute, d.Second, DateTimeKind.Unspecified);
}
return value;
}
2020-05-20 15:14:44 +00:00
public virtual bool TryAdjustFilePath(bool dump, ColumnMapper columnMapper, ref string filePath)
{
return true;
}
protected virtual string GetSelectCommandConditionText(int tenantId, TableInfo table)
{
if (!table.HasTenantColumn())
throw ThrowHelper.CantDetectTenant(table.Name);
return string.Format("where t.{0} = {1}", table.TenantColumn, tenantId);
}
protected virtual string GetDeleteCommandConditionText(int tenantId, TableInfo table)
{
return GetSelectCommandConditionText(tenantId, table);
}
protected virtual bool TryPrepareRow(bool dump, DbConnection connection, ColumnMapper columnMapper, TableInfo table, DataRowInfo row, out Dictionary<string, object> preparedRow)
{
preparedRow = new Dictionary<string, object>();
var parentRelations = TableRelations
.Where(x => x.FitsForRow(row) && x.Importance != RelationImportance.Low)
.GroupBy(x => x.ChildColumn)
.ToDictionary(x => x.Key);
foreach (var columnName in row.ColumnNames)
{
if (table.IdType == IdType.Autoincrement && columnName.Equals(table.IdColumn, StringComparison.OrdinalIgnoreCase))
continue;
var val = row[columnName];
if (!parentRelations.ContainsKey(columnName))
{
if (!TryPrepareValue(connection, columnMapper, table, columnName, ref val))
return false;
}
else
{
if (!TryPrepareValue(dump, connection, columnMapper, table, columnName, parentRelations[columnName], ref val))
return false;
if (!table.HasIdColumn() && !table.HasTenantColumn() && val == row[columnName])
return false;
}
preparedRow.Add(columnName, val);
}
return true;
}
protected virtual bool TryPrepareValue(DbConnection connection, ColumnMapper columnMapper, TableInfo table, string columnName, ref object value)
{
if (columnName.Equals(table.TenantColumn, StringComparison.OrdinalIgnoreCase))
{
2020-05-29 14:50:39 +00:00
var tenantMapping = columnMapper.GetTenantMapping();
2020-05-20 15:14:44 +00:00
if (tenantMapping < 1)
return false;
value = tenantMapping;
return true;
}
if (table.UserIDColumns.Any(x => columnName.Equals(x, StringComparison.OrdinalIgnoreCase)))
{
var strVal = Convert.ToString(value);
2020-05-29 14:50:39 +00:00
var userMapping = columnMapper.GetUserMapping(strVal);
2020-05-20 15:14:44 +00:00
if (userMapping == null)
2020-05-26 08:52:47 +00:00
return helpers.IsEmptyOrSystemUser(strVal);
2020-05-20 15:14:44 +00:00
value = userMapping;
return true;
}
var mapping = columnMapper.GetMapping(table.Name, columnName, value);
if (mapping != null)
value = mapping;
return true;
}
protected virtual bool TryPrepareValue(bool dump, DbConnection connection, ColumnMapper columnMapper, TableInfo table, string columnName, IEnumerable<RelationInfo> relations, ref object value)
{
return TryPrepareValue(connection, columnMapper, relations.Single(), ref value);
}
protected virtual bool TryPrepareValue(DbConnection connection, ColumnMapper columnMapper, RelationInfo relation, ref object value)
{
var mappedValue = columnMapper.GetMapping(relation.ParentTable, relation.ParentColumn, value);
if (mappedValue != null)
{
value = mappedValue;
return true;
2021-08-31 09:40:28 +00:00
}
2020-05-20 15:14:44 +00:00
return value == null ||
2020-10-12 13:52:31 +00:00
Guid.TryParse(Convert.ToString(value), out _) ||
int.TryParse(Convert.ToString(value), out _);
2020-05-20 15:14:44 +00:00
}
public virtual void PrepareData(DataTable data)
{
// nothing to do
}
public virtual Stream PrepareData(string key, Stream stream, ColumnMapper columnMapper)
{
return stream;
}
}
}