10

Some of my MS SQL stored procedures produce messages using the 'print' command. In my Delphi 2007 application, which connects to MS SQL using TADOConnection, how can I view the output of those 'print' commands?

Key requirements: 1) I can't run the query more than once; it might be updating things. 2) I need to see the 'print' results even if datasets are returned.

apenwarr
  • 10,838
  • 6
  • 47
  • 58

4 Answers4

10

That was an interesting one...
The OnInfoMessage event from the ADOConnection works but the devil is in the details!

Main points:

  • use CursorLocation = clUseServer instead of the default clUseClient.
  • use Open and not ExecProc with your ADOStoredProc.
  • use NextRecordset from the current one to get the following, but be sure to check you have one open.
  • use SET NOCOUNT = ON in your stored procedure.

SQL side: your stored procedure

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[FG_TEST]') AND type in (N'P', N'PC'))
  DROP PROCEDURE [dbo].[FG_TEST]
GO
-- =============================================
-- Author:      François
-- Description: test multi ADO with info
-- =============================================
CREATE PROCEDURE FG_TEST
AS
BEGIN
    -- SET NOCOUNT ON absolutely NEEDED
    SET NOCOUNT ON;

    PRINT '*** start ***'

    SELECT 'one' as Set1Field1

    PRINT '*** done once ***'

    SELECT 'two' as Set2Field2

    PRINT '*** done again ***'

    SELECT 'three' as Set3Field3

    PRINT '***finish ***'
END
GO

Delphi side:
Create a new VCL Forms Application.
Put a Memo and a Button in your Form.
Copy the following text, change the Catalog and Data Source and paste it onto your form.

object ADOConnection1: TADOConnection
  ConnectionString = 
    'Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security In' +
    'fo=False;Initial Catalog=xxxYOURxxxDBxxx;Data Source=xxxYOURxxxSERVERxxx'
  CursorLocation = clUseServer
  LoginPrompt = False
  Provider = 'SQLOLEDB.1'
  OnInfoMessage = ADOConnection1InfoMessage
  Left = 24
  Top = 216
end
object ADOStoredProc1: TADOStoredProc
  Connection = ADOConnection1
  CursorLocation = clUseServer
  ProcedureName = 'FG_TEST;1'
  Parameters = <>
  Left = 24
  Top = 264
end

In the OnInfoMessage of the ADOConnection put:

Memo1.Lines.Add(Error.Description);

For the ButtonClick, paste this code:

procedure TForm1.Button1Click(Sender: TObject);
const
  adStateOpen = $00000001; // or defined in ADOInt
var
  I: Integer;
  ARecordSet: _Recordset;
begin
  Memo1.Lines.Add('==========================');

  ADOStoredProc1.Open; // not ExecProc !!!!!

  ARecordSet := ADOStoredProc1.Recordset;
  while Assigned(ARecordSet) do
  begin
    // do whatever with current RecordSet
    while not ADOStoredProc1.Eof do
    begin
      Memo1.Lines.Add(ADOStoredProc1.Fields[0].FieldName + ': ' + ADOStoredProc1.Fields[0].Value);
      ADOStoredProc1.Next;
    end;
    // switch to subsequent RecordSet if any
    ARecordSet := ADOStoredProc1.NextRecordset(I);
    if Assigned(ARecordSet) and ((ARecordSet.State and adStateOpen) <> 0) then
      ADOStoredProc1.Recordset := ARecordSet
    else
      Break;
  end;

  ADOStoredProc1.Close;
end;
CarenRose
  • 1,266
  • 1
  • 12
  • 24
Francesca
  • 21,452
  • 4
  • 49
  • 90
  • This definitely put me on the right track: for flexibility, I used a TADOCommand instead of a TADOStoredProc, and it still works. The SET NOCOUNT ON also seems to be optional: it just prints extra messages if you don't have it. And clUseServer makes the recordsets unusable in a TDBGrid :( – apenwarr Nov 03 '08 at 17:58
  • 1
    The OninfoMessage code above only shows the first PRINT message. To print them all (eg. if there is more than 1 PRINT statements between the SELECT statements) do this: var i: integer; begin for i := 0 to AdoConnection1.Errors.Count - 1 do begin // cxMemo1.Lines.Add(Error.Description); cxMemo1.Lines.Add( ADOConnection1.Errors.Item[i].Description); end; end; – Freddie bell Oct 30 '12 at 18:20
3

In .net's connection classes there is an event called InfoMessage. In a handler for this event you can retrieve the InfoMessage (print statements) from the event args.

I believe Delphi has a similar event called "OnInfoMessage" that would help you.

AlexCuse
  • 18,008
  • 5
  • 42
  • 51
  • 1
    This is close! It works if I set command.ExecuteOptions = [eoExecuteNoRecords]. But that prevents me from getting any datasets. Hmm... – apenwarr Oct 31 '08 at 20:13
0

Some enhancements to Francois' code (as tested with DXE2) to cater for multiple print statements and the results from a variable number of selects. The changes are subtle.

procedure TForm1.ADOConnection1InfoMessage(Connection: TADOConnection;
  const Error: Error; var EventStatus: TEventStatus);
var
  i: integer;
begin
  // show ALL print statements
  for i := 0 to AdoConnection1.Errors.Count - 1 do
  begin
    // was: cxMemo1.Lines.Add(Error.Description);
    cxMemo1.Lines.Add(
      ADOConnection1.Errors.Item[i].Description);
  end;
end;

procedure TForm1.cxButton1Click(Sender: TObject);
const
  adStateOpen = $00000001; // or uses ADOInt
var
  records: Integer;
  ARecordSet: _RecordSet;
begin
  cxMemo1.Lines.Add('==========================');

  ADOStoredProc1.Open;

  try
    ARecordSet := ADOStoredProc1.RecordSet; // initial fetch
    while Assigned(ARecordSet) do
    begin
      // assign the recordset to a DataSets recordset to traverse
      AdoDataSet1.Recordset := ARecordSet;
      // do whatever with current ARecordSet
      while not ADODataSet1.eof do
      begin
        cxMemo1.Lines.Add(ADODataSet1.Fields[0].FieldName + 
          ': ' + ADODataSet1.Fields[0].Value);
        AdoDataSet1.Next;
      end;
      // fetch next recordset if there is one
      ARecordSet := ADOStoredProc1.NextRecordSet(records);
      if Assigned(ARecordSet) and ((ARecordSet.State and adStateOpen) <> 0) then
        ADOStoredProc1.Recordset := ARecordSet
      else
        Break;
    end;
  finally
    ADOStoredProc1.Close;
  end;

end;
Freddie bell
  • 192
  • 1
  • 8
0

I dont think that is possible. You might use a temp table to dump print statements and return it alongwith results.

shahkalpesh
  • 33,172
  • 3
  • 63
  • 88