Shouldn't UniDirectional datasets support .First...?
Posted: 23.04.2021, 20:23
I had an issue with specific "Operation is not allowed in FORWARD ONLY mode" exceptions if dataset was opened from a background thread. As it turns out, the issue was half mine. Imagine the following code:
If you make ZReadOnlyQuery1 UniDirectional, this code will throw an exception on FieldByName.AsString. The issue is, .FetchAll cycles through the dataset, leaving the active cursor in the resultset as the last, but rownum on 1 in the dataset. GetAsString tries to reposition the cursor, which is not allowed in an unidirectional dataset. So the solution is, call .First in the thread (see commented out).
But, according to Zeos this is not allowed (ZAbstractRODataSet.pas : 3848):
Clue #1:
According to this site:
If I comment out the check in InternalFirst and call .First in the background thread, the code no longer throws an exception and finishes the cycle.
The issue is reproducible on Oracle, in MySQL the original version (without .First) worked. The question is still there... Shouldn't be .First allowed in UniDirectional mode...?
If we agree that it should, I can create a pull request if you want.
Code: Select all
Type
TOpenThread = Class(TThread)
strict private
_dataset: TZAbstractRODataSet;
protected
Procedure Execute; Override;
public
Constructor Create(inDataSet: TZAbstractRODataSet); ReIntroduce;
End;
constructor TOpenThread.Create(inDataSet: TZAbstractRODataSet);
begin
inherited Create(False);
_dataset := inDataSet;
end;
Procedure TOpenThread.Execute;
Begin
_dataset.Open;
_dataset.FetchAll;
// _dataset.First;
End;
procedure TForm2.Button2Click(Sender: TObject);
Var
ot: TOpenThread;
s: String;
begin
ot := TOpenThread.Create(ZReadOnlyQuery1);
ot.WaitFor;
ot.Free;
With ZReadOnlyQuery1 Do
Begin
While Not Eof Do
Begin
s := FieldByName('STRINGFIELD').AsString;
Next;
End;
Close;
End;
end;
But, according to Zeos this is not allowed (ZAbstractRODataSet.pas : 3848):
Code: Select all
procedure TZAbstractRODataset.InternalFirst;
begin
if CurrentRow > 0 then
CheckBiDirectional;
CurrentRow := 0;
end;
procedure TZAbstractRODataset.CheckBiDirectional;
begin
if IsUniDirectional then
raise EZDatabaseError.Create(SOperationIsNotAllowed1);
end;
According to this site:
Clue #2:The only supported navigation methods are the First and Next methods. Most others raise exceptions. Some, such as the methods involved in bookmark support, simply do nothing.
If I comment out the check in InternalFirst and call .First in the background thread, the code no longer throws an exception and finishes the cycle.
The issue is reproducible on Oracle, in MySQL the original version (without .First) worked. The question is still there... Shouldn't be .First allowed in UniDirectional mode...?
If we agree that it should, I can create a pull request if you want.