Page 1 of 1

ZQuery Insert/Update problem with MSSQL

Posted: 11.07.2008, 15:46
by nayamda
Hi,

i'm connected to MSSQL_Server via ZEOS Version 6.6.2-RC.
When I send direct Statements to the Server everything is fine:

Code: Select all

zquery.sql.text := 'SELECT * FROM TEST';
zquery.open;
or even

Code: Select all

zquery.sql.text := 'DELETE FROM TEST WHERE bla';
zquery.execsql;
But when i try to insert or append like:

Code: Select all

zquery.insert;
FieldByName('test').AsString := 'Hallo';
zquery.Post;
Nothing happens!
There is no error or stuff. It is simply not inserted.
It even doesn't update any Record.

Is this a Bug? Or even a wrong setting?

----------
ZConnection:

AutoCommit: True;
ReadOnly: False;
SQLHopurGlass: False;
Tag: 0;
TransactIsolationLevel: TiReadCommitted;

ZQUery:

AutoCalcFields: True;
CachedUpdates: False;
Options: [doCalcDefaults, doAlwaysDetailResync];
ParamCheck: True;
Params: empty
ReadOnly: False;
ShowRecordTypes: [usUnmodified,usModiefied, usInserted];
Tag: 0;
UpdateMode: umUpdateChanged;
UpdateObject : ;
WhereMode: wmWhereKeyOnly

Posted: 11.07.2008, 16:52
by btrewern
Try using the TZSQLMonitor to see what queries are being sent to the server.

Ben

Posted: 13.07.2008, 18:25
by Michael
Hi nayamda,

I tested this against an MS SQL Server 2005 Express and got the same result :( ... To get it going I suggest to deploy a TZUpdateSQL for your query. This will work. It is a better practice, anyway, (and I recommend it) to use a TZQuery with a connected TZUpdateSQL in order to have a better control over INSERT/UPDATE and DELETE.

Best Regards!

Posted: 14.07.2008, 09:05
by nayamda
Hi Michael,

ich antworte mal auf deutsch, ich hoffe, dass ist kein Problem.

ein TZUpdateSQL noch mit einzubauen ist für uns leider keine geeignete Lösung.
Wir haben eine Software, die mit Firebird 2.0 und MSSQL arbeiten soll.
Mit Firebird funktioniert ja auch alles soweit und mit den "alten" Zeos hat das auch mit MSSQL funktioniert. Da wir momentan unsere neue Version verschicken wollen wollten wir jetzt nicht noch kurz vor knapp alles umstellen und erneut durchtesten.
Außerdem finde ich diese UpdateSQL-Kompo nicht so wirklich gut, da ich alle geänderten Datenbankfelder manuell einpflegen muss, damit alles gut funktioniert. Hier haben mir die Möglichkeiten einer normalen ZQuery völlig ausgereicht.

Wenn das noch ein Problem in den ZEOS-Kompos ist, wie stehen die Chancen, dass das behoben wird und wie schnell könnte das sein?

Soweit ich das mit dem SQLMonitor sehen konnte werden schon allein die Querys nicht an die DB übertragen.

Posted: 14.07.2008, 14:20
by mdaems
Michael,
nayamda,

This sounds like a problem that should be fixed indeed. Usually this kind of problems isn't that difficult to debug. I would start with a debug point at the top of TZGenericCachedResolver.PostUpdates. (or if there exists a protocol dependent version : that version. eg. for dblib there's TZDBLibCachedResolver.PostUpdates) Unfortunately,I don't use mssql. It may also be necessary to know
- what mssql server version is used
- which protocol is set in the connection (ADO,mssql,???)
- what connectivity dll version is used

Some clarification needed:
Soweit ich das mit dem SQLMonitor sehen konnte werden schon allein die Querys nicht an die DB übertragen.
Does this mean the updates are in the Monitor log or not?

Mark

Posted: 14.07.2008, 14:40
by nayamda
Hi Mark,

i've tried it with MS SQL Server 2005 and Michael has tested it against MS SQL Server 2005 Express.

I use ADO as protocol with SQLOLEDB.1 as provider.
I can't say what dll version is used.

Sorry for that german. The inserts and the updates are not in the Monitor log.
There are only the statements which i send direct to the database.

Posted: 14.07.2008, 15:01
by mdaems
Kein Problem. German is the third language in Belgium (and quite similar to dutch). So I understand it quite well...

So, can you try to debug TZAdoCachedResolver.PostUpdates (Even the fact the code is never executed is useful information)? And eventually TZAdoStatement.GetCurrentResult to check if the CachedResolver is actually created in your case.

Mark

Posted: 14.07.2008, 15:49
by nayamda
I poorly understand your procedures so it's hard for me to debug *g*,
but i'll give it a try.... tomorrow.

thx so far.

Posted: 15.07.2008, 07:29
by nayamda
Ok, let's see what we get.

I'll get into the procedure TZAdoCachedResolver.PostUpdates and the UpdateType is utInserted, what seems to be ok. The FAutoColumnIndex is not bigger than 0 and OldRowAccessor.IsNull(FAutoColumnIndex) is false. Whatever it means.

So the condition:

Code: Select all

if (UpdateType = utInserted) and (FAutoColumnIndex > 0)
    and OldRowAccessor.IsNull(FAutoColumnIndex) then
  begin
    Recordset := FHandle.Execute(RA, null, 0);
    if Recordset.RecordCount > 0 then
    {$IFNDEF VER130BELOW}
      NewRowAccessor.SetLong(FAutoColumnIndex, Recordset.Fields.Item[0].Value);
    {$ELSE}
      NewRowAccessor.SetLong(FAutoColumnIndex,
        Integer(Recordset.Fields.Item[0].Value));
    {$ENDIF}
  end;
won't be executed.


The procedure TZAdoStatement.GetCurrentResult is also executed and the AdoRecordset is assigned. AdoRecordSet.State and adStateOpen is both adStateOpen. The ResultSetConcurrency is rcUpdateable, so the LastResultSet should be a TZCachedResultSet.

Code: Select all

LastResultSet := TZCachedResultSet.Create(NativeResultSet, SQL, TZAdoCachedResolver.Create((Connection as IZAdoConnection).GetAdoConnection, Self, NativeResultSet.GetMetaData))

I hope it helps.

Posted: 15.07.2008, 07:54
by mdaems
I'll get into the procedure TZAdoCachedResolver.PostUpdates and the UpdateType is utInserted, what seems to be ok. The FAutoColumnIndex is not bigger than 0 and OldRowAccessor.IsNull(FAutoColumnIndex) is false. Whatever it means.
It means at least the resolver tries to post the insert/update/delete statements.
This happens in the first line of the procedure

Code: Select all

inherited PostUpdates(Sender, UpdateType, OldRowAccessor, NewRowAccessor);
The conditions that follow just handle the case where 'autonumber' columns must be fetched from the server again to fill these values in the cached dataset after the server assigned a value.

So next thing we should study is TZGenericCachedResolver.PostUpdates.

Code: Select all

{**
  Posts updates to database.
  @param Sender a cached result set object.
  @param UpdateType a type of updates.
  @param OldRowAccessor an accessor object to old column values.
  @param NewRowAccessor an accessor object to new column values.
}
procedure TZGenericCachedResolver.PostUpdates(Sender: IZCachedResultSet;
  UpdateType: TZRowUpdateType; OldRowAccessor, NewRowAccessor: TZRowAccessor);
var
  Statement            : IZPreparedStatement;
  SQL                  : string;
  SQLParams            : TObjectList;
  lUpdateCount         : Integer;
  lValidateUpdateCount : Boolean;
begin
  if (UpdateType = utDeleted)
    and (OldRowAccessor.RowBuffer.UpdateType = utInserted) then
    Exit;

  SQLParams := TObjectList.Create;
  try
    case UpdateType of
      utInserted:  //---> create an update statement based on the old and new row values
        SQL := FormInsertStatement(SQLParams, NewRowAccessor);
      utDeleted:
        SQL := FormDeleteStatement(SQLParams, OldRowAccessor);
      utModified:
        SQL := FormUpdateStatement(SQLParams, OldRowAccessor, NewRowAccessor);
      else
        Exit;
    end;

    if SQL <> '' then  //---> what's in SQL??
    begin
      Statement := Connection.PrepareStatement(SQL);
      FillStatement(Statement, SQLParams, OldRowAccessor, NewRowAccessor);
      // if Property ValidateUpdateCount isn't set : assume it's true
      lValidateUpdateCount := (Sender.GetStatement.GetParameters.IndexOfName('ValidateUpdateCount') = -1)
                            or StrToBoolEx(Sender.GetStatement.GetParameters.Values['ValidateUpdateCount']);

      lUpdateCount := Statement.ExecuteUpdatePrepared;  //---> here the work should be done....
      if  (lValidateUpdateCount)
      and (lUpdateCount <> 1   ) then
        raise EZSQLException.Create(Format(SInvalidUpdateCount, [lUpdateCount]));
    end;
  finally
    SQLParams.Free;
  end;
end;
See my comments with '//--->'.

Mark

Posted: 15.07.2008, 08:53
by nayamda

Code: Select all

// if Property ValidateUpdateCount isn't set : assume it's true
      lValidateUpdateCount := (Sender.GetStatement.GetParameters.IndexOfName('ValidateUpdateCount') = -1)
                            or StrToBoolEx(Sender.GetStatement.GetParameters.Values['ValidateUpdateCount']);

      lUpdateCount := Statement.ExecuteUpdatePrepared;  //---> here the work should be done....
      if  (lValidateUpdateCount)
      and (lUpdateCount <> 1   ) then
        raise EZSQLException.Create(Format(SInvalidUpdateCount, [lUpdateCount]));
These lines were not in my procedure?!
But also the SQL-String is empty, so

Code: Select all

SQL := FormInsertStatement(SQLParams, ewRowAccessor);
provides an empty string.

Within the FormInsertStatement there are the lines

Code: Select all

  DefineInsertColumns(Columns);
  if Columns.Count = 0 then
  begin
    Result := '';
    Exit;
  end;
and Columns.Count is in this case 0.

In the procedure DefineInsertColumns Metadata.GetTableName(I) provides the right tablename and Metadata.GetColumnName(I) provides also the right column, so the metadata seems to be correct. But Metadata.IsWritable(I) is never true, so the InsertColumns objectlist keeps empty.

Code: Select all

procedure TZGenericCachedResolver.DefineInsertColumns(Columns: TObjectList);
var
  I: Integer;
begin
  { Precache insert parameters. }
  if InsertColumns.Count = 0 then
  begin
    for I := 1 to Metadata.GetColumnCount do
    begin
      if (Metadata.GetTableName(I) <> '') and (Metadata.GetColumnName(I) <> '')
        and Metadata.IsWritable(I) then
      begin
        InsertColumns.Add(TZResolverParameter.Create(I,
          Metadata.GetColumnName(I), Metadata.GetColumnType(I), True, ''));
      end;
    end;
  end;
  { Use cached insert parameters }
  CopyResolveParameters(InsertColumns, Columns);
end;

Posted: 15.07.2008, 09:22
by mdaems
No problem about the missing lines. That's a new addition in SVN versions.

But this brings me to the point : what zeoslib version are you using? Because there was an ado fix because of post http://zeos.firmos.at/viewtopic.php?t=1769.

To see the changes of this fix : http://fisheye2.atlassian.com/changelog/zeos?cs=352

Please verify if your code contains this fix. It's not in 6.6.2 and SVN revisions below 352 (Testing branch) or 361 (Trunk).
If the fix is included already we can continue with the text below.

Seems like the current code doesn't interpret the Metadata from the server right.

If I'm correct this should be retrieved using the TZAdoDatabaseMetadata.GetColumns function.
What happens at this line ?

Code: Select all

          Result.UpdateBooleanByName('WRITABLE',
            (Flags and (DBCOLUMNFLAGS_WRITE or DBCOLUMNFLAGS_WRITEUNKNOWN) <> 0)); 
?

Posted: 15.07.2008, 09:58
by nayamda
I'm using the official version 6.6.2 RC.

This code doesn't contain this fix.
I've inserted this fix into my code and it works!

Thx alot for all that help.

Can you close the Bugtracker issue, please?!

http://zeosbugs.firmos.at/view.php?id=121