574 lines
29 KiB
C#
574 lines
29 KiB
C#
#if SQLSERVER
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Data;
|
|
|
|
namespace Sdaleo.Systems.SQLServer
|
|
{
|
|
|
|
/// <summary>
|
|
/// Specific actions for SQL Server Databases
|
|
/// </summary>
|
|
public class SQLServerDatabase
|
|
{
|
|
#region Database List
|
|
|
|
/// <summary>
|
|
/// Returns a array of any found Databases on the specified SQL Server intance
|
|
/// </summary>
|
|
/// <param name="credential">SQL Server Credentials</param>
|
|
/// <param name="DatabaseList">list of Databases if any found, or null if not</param>
|
|
/// <returns>DBError Object with ErrorOccured, if error Occured</returns>
|
|
public static DBError DatabasesListAll(IConnectDb credential, out string[] DatabaseList)
|
|
{
|
|
DatabaseList = null;
|
|
DBError dbError = ValidationConsts.IsCredentialValid(credential, DBSystem.SQL_SERVER);
|
|
if (dbError.ErrorOccured)
|
|
return dbError;
|
|
|
|
// Execute the Query
|
|
string sql = "SELECT [name] FROM sys.databases";
|
|
DB db = DB.Create(credential.DBMS.WithoutDatabase());
|
|
DBRetVal retVal = db.FillDataTable(sql);
|
|
if (retVal.IsValid)
|
|
{
|
|
List<string> DataBaseNames = new List<String>();
|
|
foreach (DataRow row in retVal.GetDataTableRetVal().Rows)
|
|
DataBaseNames.Add(row["name"].ToString());
|
|
DatabaseList = DataBaseNames.ToArray();
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a array of any found Databases that are NOT SQL Default Databases (like master, model, etc);
|
|
/// </summary>
|
|
/// <param name="credential">SQL Server Credentials</param>
|
|
/// <param name="DatabaseList">list of Databases if any found, or null if not</param>
|
|
/// <returns>DBError Object with ErrorOccured, if error Occured</returns>
|
|
public static DBError DatabasesListNonDefault(IConnectDb credential, out string[] DatabaseList)
|
|
{
|
|
DatabaseList = null;
|
|
DBError dbError = ValidationConsts.IsCredentialValid(credential, DBSystem.SQL_SERVER);
|
|
if (dbError.ErrorOccured)
|
|
return dbError;
|
|
|
|
// Look for all Databases that are Non-Default
|
|
string[] DatabaseListAll = null;
|
|
DBError retVal = DatabasesListAll(credential, out DatabaseListAll);
|
|
if(!retVal.ErrorOccured && DatabaseListAll != null)
|
|
{
|
|
List<String> DatabaseListNonDefault = new List<String>();
|
|
foreach (string DatabaseName in DatabaseListAll)
|
|
{
|
|
if(!SQLServerUtilities.IsDefaultDatabaseName(DatabaseName))
|
|
DatabaseListNonDefault.Add(DatabaseName);
|
|
}
|
|
DatabaseList = DatabaseListNonDefault.ToArray();
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Alter Database
|
|
|
|
/// <summary>
|
|
/// Use this to Raise the specified Database to it's highest Compatibility Level as allowed
|
|
/// on the specified SQLServer (2005/2008/etc.)
|
|
/// </summary>
|
|
/// <param name="credential">SQL Server Credentials with Database Info</param>
|
|
/// <returns>DBError Object with ErrorOccured, if error Occured</returns>
|
|
public static DBError DatabaseRaiseCompatibility(IConnectDb credential)
|
|
{
|
|
DBError dbError = ValidationConsts.IsCredentialValid(credential, DBSystem.SQL_SERVER);
|
|
if (dbError.ErrorOccured)
|
|
return dbError;
|
|
|
|
if (!credential.DBMS.IsDatabaseSetAndNonSystem)
|
|
return DBError.Create("Invalid Database Passed In via Credential");
|
|
|
|
// How to change compatibility level has changed in sql server 2008 (so we'll try to
|
|
// use the newer way of doing it if we can.
|
|
string sql = string.Empty;
|
|
SQLServerVersion ServerVersion;
|
|
DBError dberror = SQLServerGeneral.GetSQLServerVersion(credential, out ServerVersion);
|
|
if (dberror.ErrorOccured)
|
|
return dberror;
|
|
|
|
switch (ServerVersion)
|
|
{
|
|
case SQLServerVersion.SQL_SERVER_2008:
|
|
sql = String.Format("ALTER DATABASE [{0}] SET COMPATIBILITY_LEVEL = {1}", credential.DBMS.Database, (int)ServerVersion);
|
|
break;
|
|
|
|
default:
|
|
sql = String.Format("EXEC sp_dbcmptlevel [{0}], {1}", credential.DBMS.Database, (int)ServerVersion);
|
|
break;
|
|
}
|
|
|
|
// Execute the Query and return result
|
|
DB db = DB.Create(credential.DBMS.WithoutDatabase());
|
|
DBRetVal retVal = db.ExecuteNonQuery(sql);
|
|
return retVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allows the caller to set the Database into Single User Mode. This is * microsoft recommended *
|
|
/// for certain type of operations on the Database to make sure nobody else can use the DB.
|
|
/// Note: If Database is in Use * This will timeout
|
|
/// </summary>
|
|
/// <param name="credential">SQL Server Credentials with Database Info</param>
|
|
/// <returns>DBError Object with ErrorOccured, if error Occured</returns>
|
|
public static DBError DatabaseSetToSingleUserMode(IConnectDb credential)
|
|
{
|
|
DBError dbError = ValidationConsts.IsCredentialValid(credential, DBSystem.SQL_SERVER);
|
|
if (dbError.ErrorOccured)
|
|
return dbError;
|
|
|
|
if (!credential.DBMS.IsDatabaseSetAndNonSystem)
|
|
return DBError.Create("Invalid Database Passed In via Credential");
|
|
|
|
// Execute the Query and return result
|
|
string sql = String.Format("ALTER DATABASE [{0}] SET SINGLE_USER", credential.DBMS.Database);
|
|
DB db = DB.Create(credential.DBMS.WithoutDatabase());
|
|
DBRetVal retVal = db.ExecuteNonQuery(sql);
|
|
return retVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allows the caller to set the Database into Multi User Mode. This should be called
|
|
/// if the caller called DatabaseSetToSingleUserMode() previously
|
|
/// </summary>
|
|
/// <param name="credential">SQL Server Credentials with Database Info</param>
|
|
/// <returns>DBError Object with ErrorOccured, if error Occured</returns>
|
|
public static DBError DatabaseSetToMultiUserMode(IConnectDb credential)
|
|
{
|
|
DBError dbError = ValidationConsts.IsCredentialValid(credential, DBSystem.SQL_SERVER);
|
|
if (dbError.ErrorOccured)
|
|
return dbError;
|
|
|
|
if (!credential.DBMS.IsDatabaseSetAndNonSystem)
|
|
return DBError.Create("Invalid Database Passed In via Credential");
|
|
|
|
// Execute the Query and return result
|
|
string sql = String.Format("ALTER DATABASE [{0}] SET MULTI_USER", credential.DBMS.Database);
|
|
DB db = DB.Create(credential.DBMS.WithoutDatabase());
|
|
DBRetVal retVal = db.ExecuteNonQuery(sql);
|
|
return retVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calls DBCC SHRINKDATABASE and DBCC UPDATEUSAGE on the passed in Database
|
|
/// </summary>
|
|
/// <param name="credential">SQL Server Credentials with Database Info</param>
|
|
/// <returns>DBError Object with ErrorOccured, if error Occured</returns>
|
|
public static DBError DatabaseShrink(IConnectDb credential)
|
|
{
|
|
DBError dbError = ValidationConsts.IsCredentialValid(credential, DBSystem.SQL_SERVER);
|
|
if (dbError.ErrorOccured)
|
|
return dbError;
|
|
|
|
if (!credential.DBMS.IsDatabaseSetAndNonSystem)
|
|
return DBError.Create("Invalid Database Passed In via Credential");
|
|
|
|
// Execute the Query and return result
|
|
DB db = DB.Create(credential.DBMS.WithoutDatabase());
|
|
string sql = String.Format("DBCC SHRINKDATABASE ([{0}])", credential.DBMS.Database);
|
|
DBRetVal retVal = db.ExecuteNonQuery(sql);
|
|
if (!retVal.ErrorOccured)
|
|
{
|
|
sql = String.Format("DBCC UPDATEUSAGE ([{0}])", credential.DBMS.Database);
|
|
retVal = db.ExecuteNonQuery(sql);
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Iterates all tables on the database and enables/disables all trigers for each table
|
|
/// </summary>
|
|
/// <param name="credential">SQL Server Credentials with Database Info</param>
|
|
/// <param name="bEnable">true to enable, false to disable</param>
|
|
/// <returns>DBError Object with ErrorOccured, if error Occured</returns>
|
|
public static DBError EnableDisableAllTriggers(IConnectDb credential, bool bEnable)
|
|
{
|
|
DBError dbError = ValidationConsts.IsCredentialValid(credential, DBSystem.SQL_SERVER);
|
|
if (dbError.ErrorOccured)
|
|
return dbError;
|
|
|
|
if (!credential.DBMS.IsDatabaseSetAndNonSystem)
|
|
return DBError.Create("Invalid Database Passed In via Credential");
|
|
|
|
// Get All Table Names from the Database
|
|
string[] TableNames = null;
|
|
DBError dberror = SQLServerTable.Tables(credential, out TableNames);
|
|
if (dberror.ErrorOccured)
|
|
return dberror;
|
|
|
|
// Execute the Query and return result
|
|
DB db = DB.Create(credential);
|
|
DBRetVal retVal = new DBRetVal();
|
|
foreach (string TableName in TableNames)
|
|
{
|
|
// Execute the Query for each Table
|
|
string sql = String.Format("ALTER TABLE [{0}] {1} TRIGGER ALL", TableName, (bEnable ? "ENABLE" : "DISABLE"));
|
|
retVal = db.ExecuteNonQuery(sql);
|
|
if (retVal.ErrorOccured)
|
|
break;
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Databae Create / Exists
|
|
|
|
/// <summary>
|
|
/// Checks to see if we can query the specified server for the Database (Existence check)
|
|
/// </summary>
|
|
/// <param name="credential">SQL Server Credentials with Database Info</param>
|
|
/// <param name="bExists">Returns true if the Database Exists, false otherwise</param>
|
|
/// <returns>DBError Object with ErrorOccured, if error Occured</returns>
|
|
public static DBError DatabaseExists(IConnectDb credential, out bool bExists)
|
|
{
|
|
bExists = false;
|
|
DBError dbError = ValidationConsts.IsCredentialValid(credential, DBSystem.SQL_SERVER);
|
|
if (dbError.ErrorOccured)
|
|
return dbError;
|
|
|
|
if (!credential.DBMS.IsDatabaseSetAndNonSystem)
|
|
return DBError.Create("Invalid Database Passed In via Credential");
|
|
|
|
// Execute the Query and return result
|
|
string sql = String.Format("EXEC sp_helpdb @dbname = [{0}]", credential.DBMS.Database);
|
|
DB db = DB.Create(credential.DBMS.WithoutDatabase());
|
|
DBRetVal retVal = db.ExecuteNonQuery(sql);
|
|
bExists = !retVal.ErrorOccured;
|
|
return retVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a new Database with the specified DatabaseName in the specified DataPath, if specified, or DefaultPath otherwise
|
|
/// </summary>
|
|
/// <param name="credential">SQL Server Credentials with Database Info</param>
|
|
/// <param name="DataPath">Data Path to Create Database in (not required)</param>
|
|
/// <param name="TryAttachDBOnError">true to allow Attach Database when error occurs, false otherwise</param>
|
|
/// <returns>DBError Object with ErrorOccured, if error Occured</returns>
|
|
public static DBError DatabaseCreate(IConnectDb credential, string DataPath, bool TryAttachDBOnError)
|
|
{
|
|
DBError dbError = ValidationConsts.IsCredentialValid(credential, DBSystem.SQL_SERVER);
|
|
if (dbError.ErrorOccured)
|
|
return dbError;
|
|
|
|
if (!credential.DBMS.IsDatabaseSetAndNonSystem)
|
|
return DBError.Create("Invalid Database Passed In via Credential");
|
|
|
|
// if DataPath is empty, get default datapath for SQL ServerInstance
|
|
if (String.IsNullOrEmpty(DataPath))
|
|
SQLServerGeneral.SQLServerDefaultPath(credential.DBMS.WithoutDatabase(), out DataPath);
|
|
|
|
// Fill in Database FileName and Path Details
|
|
string sql = String.Format("CREATE DATABASE [{0}] ", credential.DBMS.Database);
|
|
if (!String.IsNullOrEmpty(DataPath))
|
|
sql += String.Format(@"ON (NAME = [{0}_dat], FILENAME = '{1}\{0}.dat') LOG ON (NAME = [{0}_log], FILENAME = '{1}\{0}_log.ldf')", credential.DBMS.Database, DataPath);
|
|
|
|
// Execute the Command
|
|
DB db = DB.Create(credential.DBMS.WithoutDatabase());
|
|
DBRetVal retVal = db.ExecuteNonQuery(sql);
|
|
|
|
// Upon DB Creation error, see if we could possibly attach the DB
|
|
bool bErrorOccuredThatCouldMaybeBeResolvedByAttaching = false;
|
|
if (retVal.ErrorOccured && !DBMS.GetAllErrorNumbers(DBSystem.SQL_SERVER, retVal).Contains(ErrorConst.ERROR_DB_ALREADY_EXISTS) && (
|
|
DBMS.GetAllErrorNumbers(DBSystem.SQL_SERVER, retVal).Contains(ErrorConst.ERROR_DB_SOME_FILE_NAMES_COULD_NOT_BE_CREATED) ||
|
|
DBMS.GetAllErrorNumbers(DBSystem.SQL_SERVER, retVal).Contains(ErrorConst.ERROR_DB_CAN_NOT_CREATE_FILE_BECAUSE_IT_ALREADY_EXISTS) ||
|
|
DBMS.GetAllErrorNumbers(DBSystem.SQL_SERVER, retVal).Contains(ErrorConst.ERROR_DB_THERE_ALREADY_IS_AN_OBJECT_IN_THE_DB_WITH_THIS_NAME)))
|
|
{
|
|
bErrorOccuredThatCouldMaybeBeResolvedByAttaching = true;
|
|
}
|
|
|
|
// Upon Error, and if we are allowed to attach && datapath is valid, try to attach
|
|
if (retVal.ErrorOccured && TryAttachDBOnError && bErrorOccuredThatCouldMaybeBeResolvedByAttaching && !String.IsNullOrEmpty(DataPath))
|
|
{
|
|
String[] strFileNames = new String[]
|
|
{
|
|
string.Format(@"{1}\{0}.dat", credential.DBMS.Database, DataPath),
|
|
string.Format(@"{1}\{0}_log.ldf", credential.DBMS.Database, DataPath)
|
|
};
|
|
DBError retValError = DatabaseAttach(credential, strFileNames);
|
|
return retValError;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Database Attach / Dettach
|
|
|
|
/// <summary>
|
|
/// Attaches a Database to an SQL Server Instance
|
|
/// </summary>
|
|
/// <param name="credential">SQL Server Credentials with Database Info</param>
|
|
/// <param name="strFileNames">Pass in the filenames when attaching Database (required)</param>
|
|
/// <returns>DBError Object with ErrorOccured, if error Occured</returns>
|
|
public static DBError DatabaseAttach(IConnectDb credential, string[] strFileNames)
|
|
{
|
|
DBError dbError = ValidationConsts.IsCredentialValid(credential, DBSystem.SQL_SERVER);
|
|
if (dbError.ErrorOccured)
|
|
return dbError;
|
|
|
|
if (!credential.DBMS.IsDatabaseSetAndNonSystem)
|
|
return DBError.Create("Invalid Database Passed In via Credential");
|
|
|
|
if ((strFileNames == null || strFileNames.Length == 0))
|
|
return DBError.Create("Invalid FileNames Passed In");
|
|
|
|
// Generate the SQL Query
|
|
string sql = String.Format("EXEC sp_attach_db @dbname = [{0}]", credential.DBMS.Database);
|
|
for (int i = 0; i < strFileNames.Length; ++i)
|
|
sql = sql + String.Format(", @filename{0:d1} = '{1}'", (i + 1), strFileNames[i]);
|
|
|
|
// Execute the Query
|
|
DB db = DB.Create(credential.DBMS.WithoutDatabase());
|
|
DBRetVal retVal = db.ExecuteNonQuery(sql);
|
|
|
|
// If Error Occured try renaming dat files to mdf * legacy code *
|
|
bool bRetryAttachingErrorOccured = false;
|
|
if (retVal.ErrorOccured &&
|
|
(DBMS.GetAllErrorNumbers(DBSystem.SQL_SERVER, retVal).Contains(ErrorConst.ERROR_DB_THERE_ALREADY_IS_AN_OBJECT_IN_THE_DB_WITH_THIS_NAME) ||
|
|
DBMS.GetAllErrorNumbers(DBSystem.SQL_SERVER, retVal).Contains(ErrorConst.ERROR_DB_DEVICE_ACTIVATION_FAILED)))
|
|
{
|
|
bRetryAttachingErrorOccured = true;
|
|
}
|
|
|
|
// Retry on Attach if failure occured &
|
|
// filenames contain .dat files & we have an attach specific error * LEGACY CODE Usuage *
|
|
if (retVal.ErrorOccured && bRetryAttachingErrorOccured)
|
|
{
|
|
bool bRetry = false;
|
|
List<string> retryArr = new List<string>();
|
|
foreach (string strFileName in strFileNames)
|
|
{
|
|
if (strFileName.Contains(".dat"))
|
|
{
|
|
strFileName.Replace(".dat", ".mdf");
|
|
bRetry = true;
|
|
}
|
|
retryArr.Add(strFileName);
|
|
}
|
|
|
|
// Only do an actually retry if .dat files were replaced
|
|
if (bRetry)
|
|
return DatabaseAttach(credential, retryArr.ToArray());
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dettaches a Database from an SQL Server Instance
|
|
/// </summary>
|
|
/// <param name="credential">SQL Server Credentials with Database Info</param>
|
|
/// <returns>DBError Object with ErrorOccured, if error Occured</returns>
|
|
public static DBError DatabaseDettach(IConnectDb credential)
|
|
{
|
|
DBError dbError = ValidationConsts.IsCredentialValid(credential, DBSystem.SQL_SERVER);
|
|
if (dbError.ErrorOccured)
|
|
return dbError;
|
|
|
|
if (!credential.DBMS.IsDatabaseSetAndNonSystem)
|
|
return DBError.Create("Invalid Database Passed In via Credential");
|
|
|
|
// Execute the Query and return result
|
|
string sql = String.Format("sp_detach_db @dbname = [{0}]", credential.DBMS.Database);
|
|
DB db = DB.Create(credential.DBMS.WithoutDatabase());
|
|
DBRetVal retVal = db.ExecuteNonQuery(sql);
|
|
return retVal;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Database Copy
|
|
|
|
/// <summary>
|
|
/// Copies a specified Database on the specified SQLServer into a new specified Database
|
|
/// </summary>
|
|
/// <param name="credential">SQL Server Credentials with Database Info</param>
|
|
/// <param name="DatabaseNameDest">Name of Database To be copied To (required)</param>
|
|
/// <returns>DBError Object with ErrorOccured, if error Occured</returns>
|
|
public static DBError DatabaseCopy(IConnectDb credential, string DatabaseNameDest)
|
|
{
|
|
DBError dbError = ValidationConsts.IsCredentialValid(credential, DBSystem.SQL_SERVER);
|
|
if (dbError.ErrorOccured)
|
|
return dbError;
|
|
|
|
if (!credential.DBMS.IsDatabaseSetAndNonSystem)
|
|
return DBError.Create("Invalid Database Passed In via Credential");
|
|
|
|
dbError = Validation.IsValidDatabaseName(DatabaseNameDest);
|
|
if (dbError.ErrorOccured)
|
|
return dbError;
|
|
|
|
|
|
string strDefaultPath;
|
|
DBError dberror = SQLServerGeneral.SQLServerDefaultPath(credential, out strDefaultPath);
|
|
if (dberror.ErrorOccured)
|
|
return dberror;
|
|
|
|
// First Step - Backup the source database to a custom Backup file
|
|
string sql = String.Format(@"BACKUP DATABASE [{0}] TO DISK = '{1}\{0}.DB Backup' WITH INIT, NAME = '{0} Backup', PASSWORD = 'dbCopy Backup', MEDIAPASSWORD = 'XaseY 1-33', INIT , FORMAT", credential.DBMS.Database, strDefaultPath);
|
|
DB db = DB.Create(credential.DBMS.WithoutDatabase());
|
|
DBRetVal retVal = db.ExecuteNonQuery(sql);
|
|
if (retVal.ErrorOccured)
|
|
return retVal;
|
|
|
|
// Second Step - Retrieve the dat & log Logical Names for the newly backed-up Database
|
|
string strDataLogical = string.Empty;
|
|
string strLogLogical = string.Empty;
|
|
sql = String.Format(@"RESTORE FILELISTONLY FROM DISK = '{0}\{1}.DB Backup' WITH FILE = 1, PASSWORD = 'dbCopy Backup', MEDIAPASSWORD = 'XaseY 1-33'", strDefaultPath, credential.DBMS.Database);
|
|
retVal = db.FillDataTable(sql);
|
|
if (!retVal.IsValid)
|
|
return retVal;
|
|
foreach (DataRow row in retVal.GetDataTableRetVal().Rows)
|
|
{
|
|
switch (row["Type"].ToString())
|
|
{
|
|
case "D":
|
|
strDataLogical = row["LogicalName"].ToString();
|
|
break;
|
|
|
|
case "L":
|
|
strLogLogical = row["LogicalName"].ToString();
|
|
break;
|
|
}
|
|
}
|
|
if (String.IsNullOrEmpty(strDataLogical) || String.IsNullOrEmpty(strLogLogical))
|
|
return DBError.Create("DataLogical or LogLogical could not be found");
|
|
|
|
// Third Step - Restore Backup file to the new Database name
|
|
sql = String.Format(@"RESTORE DATABASE [{0}] FROM DISK = '{1}\{2}.DB Backup' WITH FILE = 1, PASSWORD = 'dbCopy Backup', MEDIAPASSWORD = 'XaseY 1-33', MOVE '{3}' TO '{1}\{0}.dat', MOVE '{4}' TO '{1}\{0}_log.ldf', RECOVERY, REPLACE", DatabaseNameDest, strDefaultPath, credential.DBMS.Database, strDataLogical, strLogLogical);
|
|
retVal = db.ExecuteNonQuery(sql);
|
|
if (retVal.ErrorOccured)
|
|
{
|
|
// try again
|
|
retVal = db.ExecuteNonQuery(sql);
|
|
if (!retVal.IsValid)
|
|
return retVal;
|
|
}
|
|
|
|
// Fourth Step - Try to Delete the Backup file (No Need to have a potentially massive data file just sitting there
|
|
// ~SQL Server blocked access to procedure 'sys.xp_cmdshell' of component 'xp_cmdshell' because this component is turned off as part of the
|
|
// security configuration for this server. A system administrator can enable the use of 'xp_cmdshell' by using sp_configure.
|
|
// For more information about enabling 'xp_cmdshell', see "Surface Area Configuration" in SQL Server Books Online
|
|
// sql = String.Format("exec master.dbo.xp_cmdshell 'del /Q /F \"{0}\\{1}.DB Backup\"'", strDefaultPath, DatabaseNameSrc);
|
|
// db.ExecuteNonQuery(sql);
|
|
|
|
// Legacy Usuage, we are leaving the Backup File behind (*leaking the file* so to speak)
|
|
return retVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies a specified Database on the specified SQLServer into a new specified Database onto a different specified Instance * Must be on Same Server *
|
|
/// </summary>
|
|
/// <param name="credentialSrc">SQL Server Credentials with Database Info to be copied from (required)</param>
|
|
/// <param name="credentialDest">SQL Server Credentials with Database Info to be copied to (required)</param>
|
|
/// <returns>DBError Object with ErrorOccured, if error Occured</returns>
|
|
public static DBError DatabaseCopySepInst(IConnectDb credentialSrc, IConnectDb credentialDst)
|
|
{
|
|
DBError dbError = ValidationConsts.IsCredentialValid(credentialSrc, DBSystem.SQL_SERVER);
|
|
if (dbError.ErrorOccured)
|
|
return dbError;
|
|
|
|
if (!credentialSrc.DBMS.IsDatabaseSetAndNonSystem)
|
|
return DBError.Create("Invalid Database Passed In via Credential");
|
|
|
|
dbError = ValidationConsts.IsCredentialValid(credentialDst, DBSystem.SQL_SERVER);
|
|
if (dbError.ErrorOccured)
|
|
return dbError;
|
|
|
|
if (!credentialDst.DBMS.IsDatabaseSetAndNonSystem)
|
|
return DBError.Create("Invalid Database Passed In via Credential");
|
|
|
|
if (String.Compare(credentialSrc.DBMS.Server, credentialDst.DBMS.Server, true) != 0)
|
|
return DBError.Create("Must be on the Same Server");
|
|
|
|
if (String.Compare(credentialSrc.DBMS.Instance, credentialDst.DBMS.Instance, true) == 0)
|
|
return DBError.Create("Can not be the same Instance");
|
|
|
|
// Get Src Default Path *of the DB*
|
|
string strDefaultPathSrc;
|
|
DBError dberror = SQLServerGeneral.SQLServerDefaultPath(credentialSrc, out strDefaultPathSrc);
|
|
if (dberror.ErrorOccured)
|
|
return dberror;
|
|
|
|
// Get Dest Default Path *of the Server*
|
|
string strDefaultPathDest;
|
|
dberror = SQLServerGeneral.SQLServerDefaultPath(credentialDst, out strDefaultPathDest);
|
|
if (dberror.ErrorOccured)
|
|
return dberror;
|
|
|
|
// Make sure that both Paths aren't the same *Two instances should never have the same path!*
|
|
if (strDefaultPathSrc.ToUpper() == strDefaultPathDest.ToUpper())
|
|
return DBError.Create(String.Format("The File Path of the Source Database {0} and the Destination Database {1} on Server {1} are identical.", credentialSrc.DBMS.Database, credentialDst.DBMS.Database, credentialDst.DBMS.Server));
|
|
|
|
// Create Src N' Dest Connections * No Limit on the Command timeout *
|
|
// Could be very large database that we are trying to copy
|
|
DB dbSrc = DB.Create(credentialSrc.DBMS.WithoutDatabase().Timeouts.WithCommandTimeout(0));
|
|
DB dbDst = DB.Create(credentialDst.DBMS.WithoutDatabase().Timeouts.WithCommandTimeout(0));
|
|
|
|
// First Step - Backup the source database to a custom DB Backup file to the DESTINATION path
|
|
string sql = String.Format(@"BACKUP DATABASE [{0}] TO DISK = '{1}\{0}.DB Backup' WITH INIT, NAME = '{0} Backup', PASSWORD = 'dbCopy Backup', MEDIAPASSWORD = 'XaseY 1-33', INIT , FORMAT", credentialSrc.DBMS.Database, strDefaultPathSrc);
|
|
DBRetVal retVal = dbSrc.ExecuteNonQuery(sql);
|
|
if (retVal.ErrorOccured)
|
|
return retVal;
|
|
|
|
// Second Step - Retrieve the dat & log Logical Names for the newly backed-up Database File
|
|
string strDataLogical = string.Empty;
|
|
string strLogLogical = string.Empty;
|
|
sql = String.Format(@"RESTORE FILELISTONLY FROM DISK = '{0}\{1}.DB Backup' WITH FILE = 1, PASSWORD = 'dbCopy Backup', MEDIAPASSWORD = 'XaseY 1-33'", strDefaultPathSrc, credentialSrc.DBMS.Database);
|
|
retVal = dbSrc.FillDataTable(sql);
|
|
if (!retVal.IsValid)
|
|
return retVal;
|
|
foreach (DataRow row in retVal.GetDataTableRetVal().Rows)
|
|
{
|
|
switch (row["Type"].ToString())
|
|
{
|
|
case "D":
|
|
strDataLogical = row["LogicalName"].ToString();
|
|
break;
|
|
|
|
case "L":
|
|
strLogLogical = row["LogicalName"].ToString();
|
|
break;
|
|
}
|
|
}
|
|
if (String.IsNullOrEmpty(strDataLogical) || String.IsNullOrEmpty(strLogLogical))
|
|
return DBError.Create(String.Format("The retrieval of Logical Names for Database '{0}' failed for Server Instance '{1}' in Path '{2}'.", credentialSrc.DBMS.Database, credentialSrc.DataSource, strDefaultPathDest));
|
|
|
|
// Third Step - Restore Backup file to the new Database name onto the new (Dest) instance
|
|
sql = String.Format(@"RESTORE DATABASE [{0}] FROM DISK = '{1}\{2}.DB Backup' WITH FILE = 1, PASSWORD = 'dbCopy Backup', MEDIAPASSWORD = 'XaseY 1-33', MOVE '{3}' TO '{5}\{0}.dat', MOVE '{4}' TO '{5}\{0}_log.ldf', RECOVERY, REPLACE", credentialDst.DBMS.Database, strDefaultPathSrc, credentialSrc.DBMS.Database, strDataLogical, strLogLogical, strDefaultPathDest);
|
|
retVal = dbDst.ExecuteNonQuery(sql);
|
|
if (!retVal.ErrorOccured)
|
|
{
|
|
// try again
|
|
retVal = dbDst.ExecuteNonQuery(sql);
|
|
if (!retVal.IsValid)
|
|
return retVal;
|
|
}
|
|
|
|
// Fourth Step - Try to Delete the Backup file (No Need to have a potentially massive data file just sitting there
|
|
// ~SQL Server blocked access to procedure 'sys.xp_cmdshell' of component 'xp_cmdshell' because this component is turned off as part of the
|
|
// security configuration for this server. A system administrator can enable the use of 'xp_cmdshell' by using sp_configure.
|
|
// For more information about enabling 'xp_cmdshell', see "Surface Area Configuration" in SQL Server Books Online
|
|
// sql = String.Format("exec master.dbo.xp_cmdshell 'del /Q /F \"{0}\\{1}.DB Backup\"'", strDefaultPath, DatabaseNameSrc);
|
|
// db.RunSQLCommandTextExecuteNonQuery(sql);
|
|
|
|
// Legacy Usuage, we are leaving the Backup File behind (*leaking the file* so to speak)
|
|
return retVal;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|
|
#endif |