* Improve support for returnvalues of calling statements.

git-svn-id: trunk@19303 -
This commit is contained in:
marco 2011-09-30 15:41:38 +00:00
parent ff3791d1f3
commit aa9df955ee
4 changed files with 85 additions and 22 deletions

View File

@ -669,12 +669,19 @@ end;
procedure TIBConnection.Execute(cursor: TSQLCursor;atransaction:tSQLtransaction; AParams : TParams);
var tr : pointer;
out_SQLDA : PXSQLDA;
begin
tr := aTransaction.Handle;
if Assigned(APArams) and (AParams.count > 0) then SetParameters(cursor, atransaction, AParams);
with cursor as TIBCursor do
if isc_dsql_execute2(@Status[0], @tr, @Statement, 1, in_SQLDA, nil) <> 0 then
begin
if FStatementType = stExecProcedure then
out_SQLDA := SQLDA
else
out_SQLDA := nil;
if isc_dsql_execute2(@Status[0], @tr, @Statement, 1, in_SQLDA, out_SQLDA) <> 0 then
CheckError('Execute', Status);
end;
end;
@ -722,12 +729,23 @@ var
retcode : integer;
begin
with cursor as TIBCursor do
begin
retcode := isc_dsql_fetch(@Status[0], @Statement, 1, SQLDA);
begin
if FStatementType = stExecProcedure then
//it is not recommended fetch from non-select statement, i.e. statement which have no cursor
//starting from Firebird 2.5 it leads to error 'Invalid cursor reference'
if SQLDA^.SQLD = 0 then
retcode := 100 //no more rows to retrieve
else
begin
retcode := 0;
SQLDA^.SQLD := 0; //hack: mark after first fetch
end
else
retcode := isc_dsql_fetch(@Status[0], @Statement, 1, SQLDA);
if (retcode <> 0) and (retcode <> 100) then
CheckError('Fetch', Status);
end;
Result := (retcode <> 100);
end;
Result := (retcode = 0);
end;
procedure TIBConnection.SetParameters(cursor : TSQLCursor; aTransation : TSQLTransaction; AParams : TParams);

View File

@ -246,6 +246,7 @@ function TConnectionName.StrToStatementType(s : string) : TStatementType;
begin
S:=Lowercase(s);
if s = 'show' then exit(stSelect);
if s = 'call' then exit(stExecProcedure);
result := inherited StrToStatementType(s);
end;
@ -297,7 +298,7 @@ begin
end;
end;
HMySQL:=mysql_real_connect(HMySQL,PChar(H),PChar(U),Pchar(P),Nil,APort,Nil,0);
HMySQL:=mysql_real_connect(HMySQL,PChar(H),PChar(U),Pchar(P),Nil,APort,Nil,CLIENT_MULTI_RESULTS); //CLIENT_MULTI_RESULTS is required by CALL SQL statement(executes stored procedure), that produces result sets
If (HMySQL=Nil) then
MySQlError(Nil,SErrServerConnectFailed,Self);
@ -476,7 +477,7 @@ begin
FStatement:=Buf;
if assigned(AParams) and (AParams.count > 0) then
FStatement := AParams.ParseSQL(FStatement,false,sqEscapeSlash in ConnOptions, sqEscapeRepeat in ConnOptions,psSimulated,paramBinding,ParamReplaceString);
if FStatementType=stSelect then
if FStatementType in [stSelect,stExecProcedure] then
FNeedData:=True;
end
end;
@ -493,7 +494,7 @@ Var
begin
C:=Cursor as TCursorName;
if c.FStatementType=stSelect then
if c.FStatementType in [stSelect,stExecProcedure] then
c.FNeedData:=False;
If (C.FRes<>Nil) then
begin
@ -511,6 +512,7 @@ Var
C : TCursorName;
i : integer;
ParamNames,ParamValues : array of string;
Res: PMYSQL_RES;
begin
C:=Cursor as TCursorName;
@ -535,7 +537,14 @@ begin
C.RowsAffected := mysql_affected_rows(FMYSQL);
C.LastInsertID := mysql_insert_id(FMYSQL);
if C.FNeedData then
C.FRes:=mysql_store_result(FMySQL);
repeat
Res:=mysql_store_result(FMySQL); //returns a null pointer if the statement didn't return a result set
if Res<>nil then
begin
mysql_free_result(C.FRes);
C.FRes:=Res;
end;
until mysql_next_result(FMySQL)<>0;
end;
end;
end;
@ -569,13 +578,10 @@ begin
ADecimals:=AField^.decimals;
if (ADecimals < 5) and (ASize-2-ADecimals < 15) then //ASize is display size i.e. with sign and decimal point
NewType := ftBCD
else
begin
if (ADecimals = 0) and (ASize < 20) then
NewType := ftLargeInt
else
NewType := ftFmtBCD;
end;
else if (ADecimals = 0) and (ASize < 20) then
NewType := ftLargeInt
else
NewType := ftFmtBCD;
NewSize := ADecimals;
end;
FIELD_TYPE_FLOAT, FIELD_TYPE_DOUBLE:

View File

@ -1142,8 +1142,6 @@ begin
if not FIsEof then FIsEOF := not TSQLConnection(Database).Fetch(Fcursor);
Result := not FIsEOF;
// A stored procedure is always at EOF after its first fetch
if FCursor.FStatementType = stExecProcedure then FIsEOF := True;
end;
procedure TCustomSQLQuery.Execute;

View File

@ -60,6 +60,7 @@ type
procedure TestInt;
procedure TestScript;
procedure TestInsertReturningQuery;
procedure TestOpenStoredProc;
procedure TestTemporaryTable;
procedure TestRefresh;
@ -1185,6 +1186,46 @@ begin
end;
end;
procedure TTestFieldTypes.TestOpenStoredProc;
begin
with TSQLDBConnector(DBConnector) do
begin
if SQLDbType in MySQLdbTypes then
begin
Connection.ExecuteDirect('create procedure FPDEV_PROC() select 1 union select 2;');
Query.SQL.Text:='call FPDEV_PROC';
end
else if SQLDbType = interbase then
begin
Connection.ExecuteDirect('create procedure FPDEV_PROC returns (r integer) as begin r=1; end');
Query.SQL.Text:='execute procedure FPDEV_PROC';
end
else
begin
Ignore('This test does not apply to this sqldb-connection type, since it does not support selectable stored procedures.');
Exit;
end;
Transaction.CommitRetaining;
try
Query.Open;
AssertEquals(1, Query.Fields[0].AsInteger);
Query.Next;
if not(SQLDbType in [interbase]) then
begin
AssertFalse('Eof after 1st row', Query.Eof);
AssertEquals(2, Query.Fields[0].AsInteger);
Query.Next;
end;
AssertTrue('No Eof after last row', Query.Eof);
Query.Close;
finally
Connection.ExecuteDirect('drop procedure FPDEV_PROC');
Transaction.CommitRetaining;
end;
end;
end;
procedure TTestFieldTypes.TestClearUpdateableStatus;
// Test if CanModify is correctly disabled in case of a select query without
// a from-statement.
@ -1364,13 +1405,13 @@ begin
') ');
// Firebird/Interbase need a commit after a DDL statement. Not necessary for the other connections
TSQLDBConnector(DBConnector).Transaction.CommitRetaining;
Query.SQL.Text := 'insert into FPDEV2(ID,NAME) values (1,''test1'')';
Query.ExecSQL;
query.sql.Text:='select * from FPDEV2';
Query.Open;
Query.InsertRecord([1,'test1']);
Query.ApplyUpdates;
Query.Close;
Query.Open;
AssertEquals(query.FieldByName('NAME').AsString,'test1');
Query.insert;
query.fields[1].AsString:='11';
query.Close;
end;
end;