Can not call sp_detach_db on a database that is offline - c#

I can run this command in SqlManager to detach the db
ALTER DATABASE mydb SET OFFLINE WITH ROLLBACK IMMEDIATE
GO
dbo.sp_detach_db #dbname = N'mydb',#keepfulltextindexfile = N'false'
When I use the same connection running the same commadn via ado.net fails with error:
The database 'mydb' can not be opened because it is offline
(Error is translated from german.)
The Ado.Net code is
SqlCommand cmdOffline = new SqlCommand(#"ALTER DATABASE mydb SET OFFLINE WITH ROLLBACK IMMEDIATE");
cmdOffline.Connection = prepareMasterDBConnection;
cmdOffline.ExecuteNonQuery();
SqlCommand cmdDetach = new SqlCommand(#"dbo.sp_detach_db #dbname = N'mydb',#keepfulltextindexfile = N'false'");
cmdDetach.Connection = prepareMasterDBConnection;
cmdDetach.ExecuteNonQuery();
The connection is set to master - DB and open. The first commadn exceutes sucessfully.
What is the difference here when calling the code from ado and from sql-manager?

If your goal is to avoid conflicting connections while dropping it, rather than setting it offline before detaching, I would use the command, ALTER DATABASE mydb SET SINGLE_USER WITH ROLLBACK IMMEDIATE instead of setting it offline (and reverse it with ALTER DATABASE mydb SET MULTI_USER).

Detach needs to do some stuff before it detaches. Like s_detach says (my bold)
#skipchecks = 'skipchecks'
Specifies whether to skip or run
UPDATE STATISTIC. skipchecks is a
nvarchar(10) value, with a default
value of NULL. To skip UPDATE
STATISTICS, specify true. To
explicitly run UPDATE STATISTICS,
specify false.
By default, UPDATE STATISTICS is
performed to update information about
the data in the tables and indexes in
the SQL Server 2005 Database Engine.
Performing UPDATE STATISTICS is useful
for databases that are to be moved to
read-only media.
When it's offline, you can't do that...

Related

C# SQL ExecuteNonQuery Returns Control Before Query Completes

I'm using a C# console application to restore a database nightly. I have the C# app call a stored procedure to perform the restore using ExecuteNonQuery. Then it runs a second stored procedure on the newly restored database. Sometimes (not always) that second stored procedure fails with the error:
Database 'dbnamehere' cannot be opened. It is in the middle of a restore.
My understanding is that control shouldn't be passed to the second ExecuteNonQuery before the first ExecuteNonQuery finishes. So, why is this happening? What's the solution for this?
C#
command.CommandType = System.Data.CommandType.StoredProcedure;
command.CommandText = #"dbnamehere.dbo.sprocDailyRestore";
command.CommandTimeout = 3600;
command.ExecuteNonQuery();
SQL
ALTER DATABASE dbnamehere SET single_user WITH ROLLBACK IMMEDIATE;
RESTORE DATABASE dbnamehere
FROM DISK = 'C:\mypathhere\filename.bak'
WITH MOVE 'filename' TO 'C:\MSSQL\Data\dbnamehere_Primary_Data.mdf',
MOVE 'ETLStaging_4D600DAC' TO 'C:\MSSQL\Data\dbnamehere_ETLStaging_Data.mdf',
MOVE 'RealAnalytics_7AFB9B94' TO 'C:\MSSQL\Data\dbnamehere_Analytics_Data.mdf',
MOVE 'filename_log' TO 'C:\MSSQL\Data\dbnamehere_Primary_Log.ldf',
REPLACE, NORECOVERY;
RESTORE DATABASE dbnamehere
FROM DISK = 'C:\scheduled-tasks\RealPageDownloader\Repo\filename.diff'
WITH MOVE 'filename' TO 'C:\MSSQL\Data\dbnamehere_Primary_Data.mdf',
MOVE 'ETLStaging_4D600DAC' TO 'C:\MSSQL\Data\dbnamehere_ETLStaging_Data.mdf',
MOVE 'RealAnalytics_7AFB9B94' TO 'C:\MSSQL\Data\dbnamehere_Analytics_Data.mdf',
MOVE 'filename_log' TO 'C:\MSSQL\Data\dbnamehere_Primary_Log.ldf',
REPLACE;
ALTER DATABASE dbnamehere SET multi_user;
I should note both stored procedures are executed using ExecuteNonQuery and both stored procedures live in a different database than the one being restored.

Minimum access levels to run MySql stored procedure

I am trying to setup my .NET 4.7.1 program that is connecting to a MySQL database 8.0 to use the minimum privileges to run.
The .NET program is using MySql.Data to make connection. The minimum right for a user to execute a stored procedure is typically only EXECUTE privilege. This works fine from MySQL workbench or command line.
Upon running the .NET program this does return the following exception:
System.Data.SqlTypes.SqlNullValueException: 'Data is Null. This method or property cannot be called on Null values.'
To make it easy, I have create a very small demo program to demonstrate the issue.
Setup of the database:
CREATE DATABASE Spike;
CREATE PROCEDURE TestAccess()
BEGIN
END;
CREATE USER Spike#localhost IDENTIFIED WITH mysql_native_password BY 'sample';
GRANT EXECUTE ON PROCEDURE `TestAccess` TO Spike#localhost;
Setup program code:
static void Main(string[] args)
{
using (MySqlConnection conn = new MySqlConnection("Server=localhost;Database=Spike;uid=Spike;pwd=sample"))
{
conn.Open();
Console.WriteLine("Connection open");
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = conn;
cmd.CommandText = "TestAccess";
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.ExecuteNonQuery();
Console.WriteLine("Query executed");
}
Console.ReadKey();
}
The crash happens at the line cmd.ExecuteNonQuery();
The stack from the crash is interesting, since it seems to indicate that the information_schema is queried. When logging all statements I can see that the last statement before the exception is:
SELECT * FROM information_schema.routines WHERE 1=1 AND routine_schema LIKE 'Spike' AND routine_name LIKE 'TestAccess'
I cannot grant different rights on information_schema, but I could give more rights on the stored procedure to make more information visible in the routines table, this feels wrong however. Simple tests with granting CREATE and ALTER access also did not work.
Is there something else I can do, without granting too much privileges?
This appears to be a bug in Connector/NET, similar to bug 75301 but a little different. When it's trying to determine parameter metadata for the procedure, it first creates a MySqlSchemaCollection named Procedures with all metadata about the procedure. (This is the SELECT * FROM information_schema.routines WHERE 1=1 AND routine_schema LIKE 'Spike' AND routine_name LIKE 'TestAccess' query you see in your log.)
The Spike user account doesn't have permission to read the ROUTINE_DEFINITION column, so it is NULL. Connector/NET expects this field to be non-NULL and throws a SqlNullValueException exception trying to read it.
There are two workarounds:
1) The first, which you've discovered, is to set CheckParameters=False in your connection string. This will disable retrieval of stored procedure metadata (avoiding the crash), but may lead to harder-to-debug problems calling other stored procedures if you don't get the order and type of parameters exactly right. (Connector/NET can no longer map them for you using the metadata.)
2) Switch to a different ADO.NET MySQL library that doesn't have this bug: MySqlConnector on NuGet. It's highly compatible with Connector/NET, performs faster, and fixes a lot of known issues.
I found an answer with which I am quite pleased. It is changing the connection string by adding CheckParameters=false:
using (MySqlConnection conn = new MySqlConnection("Server=localhost;Database=Spike;uid=Spike;pwd=sample;CheckParameters=false"))
This disables parameter checking, and thereby information_schema queries.

Is it possible to run console app on database change/update?

I have a database A which is connected to my website(ASP.NET MVC). Whenever there is a change/update in database A through the website, I want to run a console app to grab updated data from database A and pull it down to database B.
Is this possible to implement this function using SqlDependency or Service Broker, or is there a better way of doing it?
There is number of ways how you can do that. To name a few:
setup database mirroring
backup/restore whole db (can easily be overkill)
use custom scripts to update one db to another
use sync framework from ado.net
use some custom code to update second db
While you can setup first three to be completely on database level, 4,5 (and 3 as well) uses some application.
In order to call your code on time you can use both push and pull approaches, so either setup a timer or use SqlDependency to have a callback when update happened.
On database level you can setup trigger or have a recurring job setup.
You may implement SQL SERVER CLR integration in following ways:
Enable CLR with SQL server: https://msdn.microsoft.com/en-us/library/ms131048(SQL.100).aspx
Write CLR trigger : https://msdn.microsoft.com/en-us/library/ms131093(v=sql.100).aspx
For more info :https://msdn.microsoft.com/en-us/library/ms254498%28v=vs.110%29.aspx
UPDATE:
You may create a sp like bellow and call this sp in a trigger for that table: CREDIT :
CREATE PROCEDURE dbo.usp_ExecCmdShellProcess
AS
BEGIN
DECLARE #job NVARCHAR(100) ;
SET #job = 'xp_cmdshell replacement - ' + CONVERT(NVARCHAR, GETDATE(), 121) ;
EXEC msdb..sp_add_job #job_name = #job,
#description = 'Automated job to execute command shell script',
#owner_login_name = 'sa', #delete_level = 1 ;
EXEC msdb..sp_add_jobstep #job_name = #job, #step_id = 1,
#step_name = 'Command Shell Execution', #subsystem = 'CMDEXEC',
#command = 'c:\Testconsole.exe', #on_success_action = 1 ;
EXEC msdb..sp_add_jobserver #job_name = #job ;
EXEC msdb..sp_start_job #job_name = #job ;
END ;
GO
and I can't say better ,but I think you can go for a trigger and call a clr function in the SQL server a https://msdn.microsoft.com/en-us/library/w2kae45k.aspx.

C# Recovering Database stuck in 'Restoring...'

I am inserting a SQL Database Backup and Recovery section into my program. I have been using MSDN Examples (tryed another and it wouldnt work).
MSDN Page - http://msdn.microsoft.com/en-us/library/ms162133.aspx
I have got the Backup file working, and have tested the file in Management Studio.
But I am having trouble with the Recovery of the file.
The code seems to be working, but the database in SQL Server is stuck in "Restoring..."
if (openFile.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
Server sqlServer = new Server();
Restore sqlRestore = new Restore();
sqlRestore.NoRecovery = true;
sqlRestore.Action = RestoreActionType.Database;
BackupDeviceItem deviceItem = new BackupDeviceItem(openFile.FileName, DeviceType.File);
sqlRestore.Devices.Add(deviceItem);
sqlRestore.Database = "firstRecoverTest";
sqlRestore.SqlRestore(sqlServer);
//Add the recovered database into the Entity Table
SqlCommand intoEntity = new SqlCommand("IF NOT EXISTS(SELECT entityName FROM Entitys WHERE entityName = 'firstRecoveryTest') INSERT INTO Entitys VALUES ('firstRecoveryTest');", sqlConnection);
sqlConnection.Open();
intoEntity.ExecuteNonQuery();
Database db = default(Database);
db = sqlServer.Databases["firstRecoverTest"];
db.RecoveryModel = (RecoveryModel)1;
db.AutoClose = true;
//db.Alter();
}
In the example there is a db.Alter(); function, but that throws an error that says "Alter failed for Database 'firstRecoverTest'".
Please let me know your thoughts
Thanks in advance
UPDATE
After inserting the "ReplaceDatabase = true;" there was no change in the end result.
Also stepping though the code line by line, shows that it is making it through.
The "db.Alter();" is just that placed at the end of the code (shown as comment). It is used in the creation of the backup and works without error.
InnerError shows this information when using db.Alter();
"ALTER DATABASE is not permitted while a database is in the Restoring state"
The interesting part is the SQL Log files. I am getting 3 Logs:
"Starting up database 'firstRecoverTest'."
"The database 'firstRecoverTest' is marked RESTORING and is in a state that does not allow recovery to be run."
"Database was restored: Database: firstRecoverTest, creation date(time): 2011/09/20(15:44:48), first LSN: 37:159:37, last LSN: 37:175:1, number of dump devices: 1, device information: (FILE=1, TYPE=DISK: {'C:\Users\Administrator\Desktop\installer_backup'}). Informational message. No user action required."
However, when I do a normal recover using SQL Management Studio there is 2 more log entrys saying
"Starting up database '[databaseName]'."
"Restore is complete on database '[databaseName]'. The database is now available"
I don't have enough reputation to post a small image of how it is in SQL Management Studio unfortunatly.
You should try either dropping the database or using sqlRestore.ReplaceDatabase = true;.
http://msdn.microsoft.com/en-us/library/microsoft.sqlserver.management.smo.restore.replacedatabase.aspx
If it looks like nothing is happening you can start the process in a seperate thread and wire up the events for notification of progress changes by using these.
sqlRestore.Complete += new ServerMessageEventHandler(completionEvent);
sqlRestore.PercentCompleteNotification = 10; // Call progress event every x percent change.
sqlRestore.PercentComplete += new PercentCompleteEventHandler(progressEvent);
If that doesn't work can you please post the Alter code that wasn't working. Also check SQL server logs and permissions.
UPDATED
Ok that update makes more sense. The reason is because you are setting sqlRestore.NoRecovery = true;. This has the effect that after the restore is done the DB is kept in a recovery state so you can restore an additional differential backup in addition to the full you just restored.
While it is in this state you will not be able to do anything more to the database. I would suggest that unless you require it you leave the default which is NoRecovery = false.
See http://msdn.microsoft.com/en-us/library/microsoft.sqlserver.management.smo.backuprestorebase.norecovery.aspx
Hope that helps.

C#: DB2 test available connection first

I have a C# .NET program running an ETL which connects to a DB2 database. Sometimes this database is down, so I'd like to do a health check at the beginning of the application to see if the database is available, without actually calling any stored procedures or pushing any data. Here's an example of the code I'm using now:
OdbcConnection myODBCConnection = new OdbcConnection("DSN=DB2AA;UID=ABCD;PWD=1234;");
OdbcCommand myODBCCommand = new OdbcCommand();
myODBCCommand.CommandType = CommandType.StoredProcedure;
myODBCCommand.CommandText = "{CALL SYSPROC.ABC001(?, ?)}";
myODBCCommand.Parameters.Add("INPUT", OdbcType.VarChar, 500);
myODBCCommand.Parameters["INPUT"] = myString
myODBCCommand.Connection = myODBCConnection
myODBCConnection.Open();
OdbcTransaction myTrans;
myTrans = myODBCConnection.BeginTransaction();
myODBCCommand.Transaction = myTrans;
myTrans.Commit();
myODBCCommand.ExecuteNonQuery();
myODBCConnection.Close();
What's the best way to test this connection without actually pushing any data?
You can simply run some innoccuous select query to check to see if the db is available.
You can try to do something as simple as:
Select 1
Or
Select getdate()
Those simple queries don't even touch any tables but will return only if the rdbms is running.
Note: those examples are for sql server but might work for db2. I haven't had to do a live check on a db2 yet though the similar concept should be doable.
Note 2: after a closer look at your code, all you should really have/need to do is check for success of your odbc connection's .Open() call.

Categories