mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-14 10:49:09 +02:00
* Improve support for returnvalues of calling statements.
git-svn-id: trunk@19303 -
This commit is contained in:
parent
ff3791d1f3
commit
aa9df955ee
@ -669,12 +669,19 @@ end;
|
|||||||
|
|
||||||
procedure TIBConnection.Execute(cursor: TSQLCursor;atransaction:tSQLtransaction; AParams : TParams);
|
procedure TIBConnection.Execute(cursor: TSQLCursor;atransaction:tSQLtransaction; AParams : TParams);
|
||||||
var tr : pointer;
|
var tr : pointer;
|
||||||
|
out_SQLDA : PXSQLDA;
|
||||||
begin
|
begin
|
||||||
tr := aTransaction.Handle;
|
tr := aTransaction.Handle;
|
||||||
if Assigned(APArams) and (AParams.count > 0) then SetParameters(cursor, atransaction, AParams);
|
if Assigned(APArams) and (AParams.count > 0) then SetParameters(cursor, atransaction, AParams);
|
||||||
with cursor as TIBCursor do
|
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);
|
CheckError('Execute', Status);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -722,12 +729,23 @@ var
|
|||||||
retcode : integer;
|
retcode : integer;
|
||||||
begin
|
begin
|
||||||
with cursor as TIBCursor do
|
with cursor as TIBCursor do
|
||||||
begin
|
begin
|
||||||
retcode := isc_dsql_fetch(@Status[0], @Statement, 1, SQLDA);
|
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
|
if (retcode <> 0) and (retcode <> 100) then
|
||||||
CheckError('Fetch', Status);
|
CheckError('Fetch', Status);
|
||||||
end;
|
end;
|
||||||
Result := (retcode <> 100);
|
Result := (retcode = 0);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TIBConnection.SetParameters(cursor : TSQLCursor; aTransation : TSQLTransaction; AParams : TParams);
|
procedure TIBConnection.SetParameters(cursor : TSQLCursor; aTransation : TSQLTransaction; AParams : TParams);
|
||||||
|
@ -246,6 +246,7 @@ function TConnectionName.StrToStatementType(s : string) : TStatementType;
|
|||||||
begin
|
begin
|
||||||
S:=Lowercase(s);
|
S:=Lowercase(s);
|
||||||
if s = 'show' then exit(stSelect);
|
if s = 'show' then exit(stSelect);
|
||||||
|
if s = 'call' then exit(stExecProcedure);
|
||||||
result := inherited StrToStatementType(s);
|
result := inherited StrToStatementType(s);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -297,7 +298,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
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
|
If (HMySQL=Nil) then
|
||||||
MySQlError(Nil,SErrServerConnectFailed,Self);
|
MySQlError(Nil,SErrServerConnectFailed,Self);
|
||||||
|
|
||||||
@ -476,7 +477,7 @@ begin
|
|||||||
FStatement:=Buf;
|
FStatement:=Buf;
|
||||||
if assigned(AParams) and (AParams.count > 0) then
|
if assigned(AParams) and (AParams.count > 0) then
|
||||||
FStatement := AParams.ParseSQL(FStatement,false,sqEscapeSlash in ConnOptions, sqEscapeRepeat in ConnOptions,psSimulated,paramBinding,ParamReplaceString);
|
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;
|
FNeedData:=True;
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
@ -493,7 +494,7 @@ Var
|
|||||||
|
|
||||||
begin
|
begin
|
||||||
C:=Cursor as TCursorName;
|
C:=Cursor as TCursorName;
|
||||||
if c.FStatementType=stSelect then
|
if c.FStatementType in [stSelect,stExecProcedure] then
|
||||||
c.FNeedData:=False;
|
c.FNeedData:=False;
|
||||||
If (C.FRes<>Nil) then
|
If (C.FRes<>Nil) then
|
||||||
begin
|
begin
|
||||||
@ -511,6 +512,7 @@ Var
|
|||||||
C : TCursorName;
|
C : TCursorName;
|
||||||
i : integer;
|
i : integer;
|
||||||
ParamNames,ParamValues : array of string;
|
ParamNames,ParamValues : array of string;
|
||||||
|
Res: PMYSQL_RES;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
C:=Cursor as TCursorName;
|
C:=Cursor as TCursorName;
|
||||||
@ -535,7 +537,14 @@ begin
|
|||||||
C.RowsAffected := mysql_affected_rows(FMYSQL);
|
C.RowsAffected := mysql_affected_rows(FMYSQL);
|
||||||
C.LastInsertID := mysql_insert_id(FMYSQL);
|
C.LastInsertID := mysql_insert_id(FMYSQL);
|
||||||
if C.FNeedData then
|
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;
|
end;
|
||||||
end;
|
end;
|
||||||
@ -569,13 +578,10 @@ begin
|
|||||||
ADecimals:=AField^.decimals;
|
ADecimals:=AField^.decimals;
|
||||||
if (ADecimals < 5) and (ASize-2-ADecimals < 15) then //ASize is display size i.e. with sign and decimal point
|
if (ADecimals < 5) and (ASize-2-ADecimals < 15) then //ASize is display size i.e. with sign and decimal point
|
||||||
NewType := ftBCD
|
NewType := ftBCD
|
||||||
else
|
else if (ADecimals = 0) and (ASize < 20) then
|
||||||
begin
|
NewType := ftLargeInt
|
||||||
if (ADecimals = 0) and (ASize < 20) then
|
else
|
||||||
NewType := ftLargeInt
|
NewType := ftFmtBCD;
|
||||||
else
|
|
||||||
NewType := ftFmtBCD;
|
|
||||||
end;
|
|
||||||
NewSize := ADecimals;
|
NewSize := ADecimals;
|
||||||
end;
|
end;
|
||||||
FIELD_TYPE_FLOAT, FIELD_TYPE_DOUBLE:
|
FIELD_TYPE_FLOAT, FIELD_TYPE_DOUBLE:
|
||||||
|
@ -1142,8 +1142,6 @@ begin
|
|||||||
|
|
||||||
if not FIsEof then FIsEOF := not TSQLConnection(Database).Fetch(Fcursor);
|
if not FIsEof then FIsEOF := not TSQLConnection(Database).Fetch(Fcursor);
|
||||||
Result := not FIsEOF;
|
Result := not FIsEOF;
|
||||||
// A stored procedure is always at EOF after its first fetch
|
|
||||||
if FCursor.FStatementType = stExecProcedure then FIsEOF := True;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TCustomSQLQuery.Execute;
|
procedure TCustomSQLQuery.Execute;
|
||||||
|
@ -60,6 +60,7 @@ type
|
|||||||
procedure TestInt;
|
procedure TestInt;
|
||||||
procedure TestScript;
|
procedure TestScript;
|
||||||
procedure TestInsertReturningQuery;
|
procedure TestInsertReturningQuery;
|
||||||
|
procedure TestOpenStoredProc;
|
||||||
|
|
||||||
procedure TestTemporaryTable;
|
procedure TestTemporaryTable;
|
||||||
procedure TestRefresh;
|
procedure TestRefresh;
|
||||||
@ -1185,6 +1186,46 @@ begin
|
|||||||
end;
|
end;
|
||||||
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;
|
procedure TTestFieldTypes.TestClearUpdateableStatus;
|
||||||
// Test if CanModify is correctly disabled in case of a select query without
|
// Test if CanModify is correctly disabled in case of a select query without
|
||||||
// a from-statement.
|
// a from-statement.
|
||||||
@ -1364,13 +1405,13 @@ begin
|
|||||||
') ');
|
') ');
|
||||||
// Firebird/Interbase need a commit after a DDL statement. Not necessary for the other connections
|
// Firebird/Interbase need a commit after a DDL statement. Not necessary for the other connections
|
||||||
TSQLDBConnector(DBConnector).Transaction.CommitRetaining;
|
TSQLDBConnector(DBConnector).Transaction.CommitRetaining;
|
||||||
Query.SQL.Text := 'insert into FPDEV2(ID,NAME) values (1,''test1'')';
|
|
||||||
Query.ExecSQL;
|
|
||||||
query.sql.Text:='select * from FPDEV2';
|
query.sql.Text:='select * from FPDEV2';
|
||||||
Query.Open;
|
Query.Open;
|
||||||
|
Query.InsertRecord([1,'test1']);
|
||||||
|
Query.ApplyUpdates;
|
||||||
|
Query.Close;
|
||||||
|
Query.Open;
|
||||||
AssertEquals(query.FieldByName('NAME').AsString,'test1');
|
AssertEquals(query.FieldByName('NAME').AsString,'test1');
|
||||||
Query.insert;
|
|
||||||
query.fields[1].AsString:='11';
|
|
||||||
query.Close;
|
query.Close;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
Loading…
Reference in New Issue
Block a user