简体   繁体   中英

Programmatically restoring ms-sql system database from C#

While there are plenty of questions and answers for dealing with programmatic backup restoration from the perspective of restoring a "normal" database, doing so for a system database like [master] , [msdb] , or [model] I run into many varied issues. I wonder if anyone has something to contribute, especially for [master] , but all of these appear to cause issues. For now, I've started ms-sql in single-user mode (the entire service) to restore the master database with the T3608 flag, but the moment I try to run a RESTORE DATABASE query on it this breaks my connection with a broken pipe message. Inspecting the services console services.msc reveals that the database service is no longer running.

In other words, running this query appears to crash sql-server with no useful message result.

Let's look at some example code. Query generation code looks like:

string query = "RESTORE DATABASE @dbName FROM DISK " + 
    "= @backupPath WITH FILE = @fileID, REPLACE, ";
if (dataFileName != "") {
    query += "MOVE @dataFileName TO @dataFileLocation, ";
}
if (logFileName != "") {
    query += "MOVE @logFileName TO @logFileLocation, ";
}
query += "STATS=1";
SqlCommand cmdData = new SqlCommand(query, conn);
cmdData.Parameters.AddWithValue("@dbName", databaseName);
cmdData.Parameters.AddWithValue("@backupPath", filePath);
cmdData.Parameters.AddWithValue("@fileID", fileIndex);
if (dataFileName != "") {
    cmdData.Parameters.AddWithValue("@dataFileName", dataFileName);
    cmdData.Parameters.AddWithValue("@dataFileLocation", mdfPath);
}
if (logFileName != "") {
    cmdData.Parameters.AddWithValue("@logFileName", logFileName);
    cmdData.Parameters.AddWithValue("@logFileLocation", ldfPath);
}
cmdData.ExecuteNonQuery();

So it's pretty much a simplest possible example. FileIndex is an integer containing the backup file index, all the other variables that aren't initialized here are strings containing the various paths and names. Note that manually executing this kind of query produces the expected result (a backup file is restored).

The event log makes things even weirder. I observe this message:

Snapshot isolation or read committed snapshot is not available in database 'master' because SQL Server was started with one or more undocumented trace flags that prevent enabling database for versioning. Transaction started with snapshot isolation will fail and a query running under read committed snapshot will succeed but will resort back to lock based read committed.

where it presumably complains about T3608. The event log also contains messages about "succesful restoration", but inspecting the contents of the DATA folder reveals that my program does not in fact put back database files that I manually delete, but appears to do nothing instead?

I am very confused by sql server's strange behaviour at this point. Can anyone shed some light on how this could possibly happen?

Edit:

Some more digging and experimentation with changing the process reveals that the msdb and model databases are in fact also needed or SQL server will not start up properly. Hence all three need to be present to be able to restore them, and you can only restore the three, what I will now call special databases in special, convoluted, specific ways that are different for each and every one of them.

Exactly what and how I am not sure about yet. The internet contains a bunch of conflicting advice, and not one documented procedure works straight up.

I've discovered that ms-sql keeps a special folder "Template Data". In here there are a bunch of templates for the special databases. I can copy these templates into the real DATA folder to have a running sql-server to restore the backed up special databases from.

But: doing this: during the restore of the non-master specials (msdb and model) the server still terminates itself. It's likely that actually there isn't anything being restored.

Further inspecting log files reveals these two interesting errors though:

During redoing of a logged operation in database 'model', an error occurred at log record ID (32:151:1). Typically, the specific failure is previously logged as an error in the Windows Event Log service. Restore the database from a full backup, or repair the database.

The operating system returned error 5(Access Denied.) to SQL Server during a read at offset 0000000000000000 in file 'E:\\backupdb\\MSSQL12.TBUINST\\MSSQL\\Template Data\\modellog.ldf'. Additional messages in the SQL Server error log and system event log may provide more detail. This is a severe system-level error condition that threatens database integrity and must be corrected immediately. Complete a full database consistency check (DBCC CHECKDB). This error can be caused by many factors; for more information, see SQL Server Books Online.

Why is it now trying to read something in "Template data"? Is it because the template system db or msdb db or model db contains refs to the hard-coded path(s) of the template model/msdb/etc db inside of itself and I have to issue move commands? When/How in the restore process do I issue these?

I also realized this: Presuming that I restore the 'master' database first, and that it contains the paths to the other special databases, why is SQL server now searching in the template data folder? It does, after all, restart when restoring the master database, so the paths in there should now be the defaults. Not to mention that, in every single restore operation, I explicity specify where both the .bak file and the new .ldf and .mdf files are to be located? This makes zero sense.

SQL Server automatically stops when the master database restore completes. You'll need to handle the resultant connection error in your code, restart SQL Server, and then continue restoring the other system databases.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM