5

The output of sql commands that is visible to users who interactively run SQL commands from SQL Server Management studio, is different than the output you get back from executing an ADO command or ADO query object.

USE [DBNAME] 
BACKUP DATABASE [DBNAME] TO 
 DISK = 'C:\SqlBackup\Backup.mdf'

The successful completion output is like this:

Processed 465200 pages for database 'DBNAME', file 'filename' on file 2.
Processed 2 pages for database 'DBNAME', file 'filename_log' on file 2.
BACKUP DATABASE successfully processed 465202 pages in 90.595 seconds (40.116 MB/sec).

When I execute either a TADOCommand or TADOQuery with the CommandText or SQL set as above, I do not get any such output. How do I read this "secondary output" from the execution of an SQL command? I'm hoping that perhaps via some raw ADO operations I might be able to execute a command and get back the information above, for success, as well as any errors in performing an Sql backup.

Update: The answer below works better for me than my naive attempt, which did not work, using plain Delphi TADOCommand and TADOConnection classes:

  • create TADOCommand and TADOConnection.
  • execute command.
  • get info-messages back.

The problem I experienced in my own coding attempts, is that my first command is "use dbname" and the only recordset I traversed in my code, was the results of the "use dbname" command, not the second command I was executing. The accepted answer below traverses all recordsets that come back from executing the ADO command, and thus it works much better. Since I'm doing all this in a background thread, I actually think it's better to create the raw Com Objects anyways, and avoid any VCL entanglement in my thread. The code below could be a nice component if anybody is interested, let me know and I might make an open source "SQL Backup for Delphi" component.

Warren P
  • 65,725
  • 40
  • 181
  • 316
  • 1
    Possibly related? http://stackoverflow.com/questions/254486/view-output-of-print-statements-using-adoconnection-in-delphi?rq=1 – Warren P Aug 10 '12 at 20:35
  • Could it be that your ADOQuery or ADOCommand is timing out before it receives a reply from the db? CommandTimeOut's default value is 30. – A Lombardo Aug 10 '12 at 20:45
  • Define TADOConnection.OnInfoMessage handler to process this message from SQL server – valex Aug 10 '12 at 21:38
  • Have a look at [this thread on linkedin](http://www.linkedin.com/groupItem?view=&gid=101829&type=member&item=138229518&qid=60222b72-7b4a-412d-b0df-ff09b3f37065&trk=group_most_recent_rich-0-b-ttl&goback=%2Egmr_101829) – Michael Riley - AKA Gunny Aug 11 '12 at 00:37
  • @valexhome; Sadly the `OnInfoMessage` output does not include the output from the backup command, only something that was a result of the `use [dbname]` part was successful. So, close, but no cigar. – Warren P Aug 11 '12 at 21:36
  • I don't know if this is possible. How about wrapping the backup inside a stored procedure, and storing the output as stored procedure output parameter? Then in Delphi ,just read that parameter? – Hendra Aug 12 '12 at 06:43
  • If I could do it in a stored procedure then it should just work as a series of commands (an SQL script) inside a `TADOQuery`, right? Whatever magical stored procedure code one could use, should also be able to run directly inside `TADOQuery`. Or, via raw ADO OLE hacks. – Warren P Aug 12 '12 at 13:55
  • I'd be interested in the component. Going to mix it with 7z and a little bit of magic. – Mark Robinson Nov 08 '12 at 13:16
  • 1
    I've currently mixed it with AES 256 encryption and ZLIB compression. – Warren P Nov 09 '12 at 13:27

1 Answers1

5

Here is an example. I've tested it with D7 and MSSQL2000. And it adds to Memo1 all messages from server:

29 percent backed up.
58 percent backed up.
82 percent backed up.
98 percent backed up.
Processed 408 pages for database 'NorthWind', file 'Northwind' on file 1.
100 percent backed up.
Processed 1 pages for database 'NorthWind', file 'Northwind_log' on file 1.
BACKUP DATABASE successfully processed 409 pages in 0.124 seconds (26.962 MB/sec).

Also if it takes a long time consider to implement a WHILE loop not in the main thread.

uses AdoInt,ComObj;
.....

procedure TForm1.Button1Click(Sender: TObject);
var cmd  : _Command;
    Conn : _Connection;
    RA   : OleVariant;
    rs   :_RecordSet;
    n    : Integer;
begin
  Memo1.Clear;

  Conn := CreateComObject(CLASS_Connection) as _Connection;
  Conn.ConnectionString := 'Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=NorthWind;Data Source=SQL_Server';
  Conn.Open(Conn.ConnectionString,'','',Integer(adConnectUnspecified));

  cmd := CreateComObject(CLASS_Command) as _Command;
  cmd.CommandType := adCmdText;
  cmd.Set_ActiveConnection(Conn);
  cmd.CommandText := 'BACKUP DATABASE [NorthWind] TO  DISK = N''c:\sql_backup\NorthWind'' WITH  INIT ,  NOUNLOAD ,  NAME = N''NortWind backup'',  NOSKIP ,  STATS = 10,  NOFORMAT;';
  rs:=cmd.Execute(RA,0,Integer(adCmdText));

  while (rs<>nil) do
  begin
   for n:=0 to(Conn.Errors.Count-1)do begin
    Memo1.Lines.Add(Conn.Errors.Item[n].Description);
   end;
   rs:=rs.NextRecordset(RA);
  end;

  cmd.Set_ActiveConnection(nil);
  Conn.Close;
  cmd  := nil;
  Conn := nil;
end;

I've found this thread (Russian) for stored procedure and correct it for BACKUP command.

valex
  • 23,966
  • 7
  • 43
  • 60
  • The above code would work with native Delphi objects, if the native delphi wrappers didn't break the design intention shown above where we pass in RA, and get back `rs := NextRecordSet(RA)`. – Warren P Aug 13 '12 at 13:42
  • 4
    I've tested the above in Delphi 2007 and XE2, with MS SQL Server 2008 R2 and SQL Server 2012, and it works there too. I moved it to a background thread, because that was always my plan. If anyone wants an open source component version (threaded) of the above, please let me know (upvote takes less space than comments). – Warren P Aug 13 '12 at 14:08
  • 1
    Strange how it's called `Errors` when it contains things which aren't an error :-/ – Jerry Dodge Nov 01 '15 at 19:58