Page 1 of 1
wmWhereKeyOnly fails for VIEWs
Posted: 15.05.2014, 12:38
by Fr0sT
There's a problem in TZGenericCachedResolver.DefineWhereKeyColumns when the current table is actually a VIEW. Currently the procedure tries to get primary keys by DatabaseMetadata.GetPrimaryKeys(%view_name%) but obviously fails because the key actually belongs to a table not to a view. So the wmWhereAll mode is used in all cases. In my case it results in update fail.
I presume TZGenericCachedResolver must regard the owner dataset's Properties.KeyFields setting and only retrieve primary keys from DB metadata when this setting is empty.
I use Firebird 2.5, Delphi 7 and Zeos 7.1.3 but 7.2 version seem not having changes in this area. Problem is straightforward but I could provide a test project if needed.
Re: wmWhereKeyOnly fails for VIEWs
Posted: 15.09.2017, 15:30
by Fr0sT
Seems like I did it.
src/dbc/ZDbcGenericResolver.pas
Code: Select all
src/dbc/ZDbcGenericResolver.pas | 89 +++++++++++++++++++++++++++++------------
1 file changed, 64 insertions(+), 25 deletions(-)
diff --git a/src/dbc/ZDbcGenericResolver.pas b/src/dbc/ZDbcGenericResolver.pas
index 3f0de89..8b63950 100644
--- a/src/dbc/ZDbcGenericResolver.pas
+++ b/src/dbc/ZDbcGenericResolver.pas
@@ -177,7 +177,8 @@ type
implementation
-uses ZMessages, ZSysUtils, ZDbcMetadata, ZDbcUtils
+uses ZMessages, ZSysUtils, ZDbcMetadata, ZDbcUtils, ZDatasetUtils, ZTokenizer,
+ ZAbstractRODataset
{$IFDEF FAST_MOVE}, ZFastCode{$ENDIF};
{ TZResolverParameter }
@@ -420,12 +421,38 @@ end;
@param Columns a collection of key columns.
}
procedure TZGenericCachedResolver.DefineWhereKeyColumns(Columns: TObjectList);
+
+ function AddColumn(const Table, ColumnName: string; WhereColumns: TObjectList): Boolean;
+ var
+ ColIdx, I: Integer;
+ begin
+ Result := False;
+ ColIdx := InvalidDbcIndex;
+ for I := FirstDbcIndex to Metadata.GetColumnCount{$IFDEF GENERIC_INDEX}-1{$ENDIF} do
+ if (ColumnName = Metadata.GetColumnName(I))
+ and (Table = Metadata.GetTableName(I)) then
+ begin
+ ColIdx := I;
+ Break;
+ end;
+ if ColIdx = InvalidDbcIndex then
+ begin
+ WhereColumns.Clear;
+ Exit;
+ end;
+ WhereColumns.Add(TZResolverParameter.Create(I, ColumnName,
+ stUnknown, False, ''));
+ Result := True;
+ end;
+
var
I: Integer;
- Found: Boolean;
- ColumnName: string;
+ KeyFields: string;
Catalog, Schema, Table: string;
PrimaryKeys: IZResultSet;
+ Tokens: TStrings;
+ TokenType: TZTokenType;
+ TokenValue: string;
begin
{ Use precached values. }
if WhereColumns.Count > 0 then
@@ -450,31 +477,43 @@ begin
{ Tryes to define primary keys. }
if not WhereAll then
begin
- {For exact results: quote all identifiers SEE: http://sourceforge.net/p/zeoslib/tickets/81/
- If table names have mixed case ConstructNameCondition will return wrong results
- and we fall back to WhereAll}
- PrimaryKeys := DatabaseMetadata.GetPrimaryKeys(IdentifierConvertor.Quote(Catalog),
- IdentifierConvertor.Quote(Schema), IdentifierConvertor.Quote(Table));
- while PrimaryKeys.Next do
+ KeyFields := FStatement.GetParameters.Values['KeyFields'];
+ { Let user define key fields }
+ if KeyFields <> '' then
begin
- ColumnName := PrimaryKeys.GetString(ColumnNameIndex);
- Found := False;
- for I := FirstDbcIndex to Metadata.GetColumnCount{$IFDEF GENERIC_INDEX}-1{$ENDIF} do
- begin
- if (ColumnName = Metadata.GetColumnName(I))
- and (Table = Metadata.GetTableName(I)) then
+ Tokens := CommonTokenizer.TokenizeBufferToList(KeyFields,
+ [toSkipEOF, toSkipWhitespaces, toUnifyNumbers, toDecodeStrings]);
+
+ try
+ for I := 0 to Tokens.Count - 1 do
begin
- Found := True;
- Break;
+ TokenType := TZTokenType({$IFDEF oldFPC}Pointer({$ENDIF}
+ Tokens.Objects[I]{$IFDEF oldFPC}){$ENDIF});
+ TokenValue := Tokens[I];
+
+ if TokenType in [ttWord, ttQuoted] then
+ begin
+ if not AddColumn(Table, TokenValue, WhereColumns) then
+ Break;
+ end
+ else if (TokenValue <> ',') and (TokenValue <> ';') then
+ raise EZDatabaseError.Create(Format(SIncorrectSymbol, [TokenValue]));
end;
+ finally
+ Tokens.Free;
end;
- if not Found then
- begin
- WhereColumns.Clear;
- Break;
- end;
- WhereColumns.Add(TZResolverParameter.Create(I, ColumnName,
- stUnknown, False, ''));
+ end
+ else
+ { Ask DB for key fields }
+ begin
+ {For exact results: quote all identifiers SEE: http://sourceforge.net/p/zeoslib/tickets/81/
+ If table names have mixed case ConstructNameCondition will return wrong results
+ and we fall back to WhereAll}
+ PrimaryKeys := DatabaseMetadata.GetPrimaryKeys(IdentifierConvertor.Quote(Catalog),
+ IdentifierConvertor.Quote(Schema), IdentifierConvertor.Quote(Table));
+ while PrimaryKeys.Next do
+ if not AddColumn(Table, PrimaryKeys.GetString(ColumnNameIndex), WhereColumns) then
+ Break;
end;
end;
@@ -854,7 +893,7 @@ begin
begin
if DeleteStatement = nil then
begin
- SQL := FormDeleteStatement(FDeleteParams, OldRowAccessor);
+ SQL := FormDeleteStatement(FDeleteParams, OldRowAccessor);
If Assigned(DeleteStatement) and (SQL <> DeleteStatement.GetSQL) then
DeleteStatement := nil;
If not Assigned(DeleteStatement) then
or on GitHub