Bläddra i källkod

* fixes Mantis #14730 dealing with oldvalue=null

git-svn-id: trunk@19267 -
marco 14 år sedan
förälder
incheckning
448e3db450

+ 1 - 0
.gitattributes

@@ -2006,6 +2006,7 @@ packages/fcl-db/tests/testsqlfiles.lpr svneol=native#text/plain
 packages/fcl-db/tests/testsqlscanner.lpi svneol=native#text/plain
 packages/fcl-db/tests/testsqlscanner.lpr svneol=native#text/plain
 packages/fcl-db/tests/testsqlscript.pas svneol=native#text/plain
+packages/fcl-db/tests/testwherenull.lpr svneol=native#text/plain
 packages/fcl-db/tests/toolsunit.pas svneol=native#text/plain
 packages/fcl-db/tests/xmlxsdexporttestcase1.pas svneol=native#text/plain
 packages/fcl-extra/Makefile svneol=native#text/plain

+ 29 - 37
packages/fcl-db/src/sqldb/sqldb.pp

@@ -1544,17 +1544,17 @@ Procedure TCustomSQLQuery.ApplyRecUpdate(UpdateKind : TUpdateKind);
 
 var FieldNamesQuoteChars : TQuoteChars;
 
-  procedure InitialiseModifyQuery(var qry : TCustomSQLQuery; aSQL: String);
+  function InitialiseModifyQuery(var qry : TCustomSQLQuery): TCustomSQLQuery;
 
   begin
-    qry := TCustomSQLQuery.Create(nil);
-    with qry do
-      begin
-      ParseSQL := False;
-      DataBase := Self.DataBase;
-      Transaction := Self.Transaction;
-      SQL.text := aSQL;
-      end;
+    if not assigned(qry) then
+    begin
+      qry := TCustomSQLQuery.Create(nil);
+      qry.ParseSQL := False;
+      qry.DataBase := Self.DataBase;
+      qry.Transaction := Self.Transaction;
+    end;
+    Result:=qry;
   end;
 
   procedure UpdateWherePart(var sql_where : string;x : integer);
@@ -1562,8 +1562,11 @@ var FieldNamesQuoteChars : TQuoteChars;
   begin
     if (pfInKey in Fields[x].ProviderFlags) or
        ((FUpdateMode = upWhereAll) and (pfInWhere in Fields[x].ProviderFlags)) or
-       ((FUpdateMode = UpWhereChanged) and (pfInWhere in Fields[x].ProviderFlags) and (fields[x].value <> fields[x].oldvalue)) then
-      sql_where := sql_where + '(' + FieldNamesQuoteChars[0] + fields[x].FieldName + FieldNamesQuoteChars[1] + '= :"' + 'OLD_' + fields[x].FieldName + '") and ';
+       ((FUpdateMode = UpWhereChanged) and (pfInWhere in Fields[x].ProviderFlags) and (Fields[x].Value <> Fields[x].OldValue)) then
+       if Fields[x].OldValue = NULL then
+          sql_where := sql_where + FieldNamesQuoteChars[0] + Fields[x].FieldName + FieldNamesQuoteChars[1] + ' is null and '
+       else
+          sql_where := sql_where + '(' + FieldNamesQuoteChars[0] + Fields[x].FieldName + FieldNamesQuoteChars[1] + '= :"' + 'OLD_' + Fields[x].FieldName + '") and ';
   end;
 
   function ModifyRecQuery : string;
@@ -1632,6 +1635,7 @@ var FieldNamesQuoteChars : TQuoteChars;
   end;
 
 var qry : TCustomSQLQuery;
+    s   : string;
     x   : integer;
     Fld : TField;
 
@@ -1639,37 +1643,25 @@ begin
   FieldNamesQuoteChars := TSQLConnection(DataBase).FieldNameQuoteChars;
 
   case UpdateKind of
-    ukModify : begin
-               if not assigned(FUpdateQry) then
-                 begin
-                 if (trim(FUpdateSQL.Text)<> '') then
-                   InitialiseModifyQuery(FUpdateQry,FUpdateSQL.Text)
-                 else
-                   InitialiseModifyQuery(FUpdateQry,ModifyRecQuery);
-                 end;
-               qry := FUpdateQry;
-               end;
     ukInsert : begin
-               if not assigned(FInsertQry) then
-                 begin
-                 if (trim(FInsertSQL.Text)<> '') then
-                   InitialiseModifyQuery(FInsertQry,FInsertSQL.Text)
-                 else
-                   InitialiseModifyQuery(FInsertQry,InsertRecQuery);
-                 end;
-               qry := FInsertQry;
+               s := trim(FInsertSQL.Text);
+               if s = '' then s := InsertRecQuery;
+               qry := InitialiseModifyQuery(FInsertQry);
+               end;
+    ukModify : begin
+               s := trim(FUpdateSQL.Text);
+               if (s='') and (not assigned(FUpdateQry) or (UpdateMode<>upWhereKeyOnly)) then //first time or dynamic where part
+                 s := ModifyRecQuery;
+               qry := InitialiseModifyQuery(FUpdateQry);
                end;
     ukDelete : begin
-               if not assigned(FDeleteQry) then
-                 begin
-                 if (trim(FDeleteSQL.Text)<> '') then
-                   InitialiseModifyQuery(FDeleteQry,FDeleteSQL.Text)
-                 else
-                   InitialiseModifyQuery(FDeleteQry,DeleteRecQuery);
-                 end;
-               qry := FDeleteQry;
+               s := trim(FDeleteSQL.Text);
+               if (s='') and (not assigned(FDeleteQry) or (UpdateMode<>upWhereKeyOnly)) then
+                 s := DeleteRecQuery;
+               qry := InitialiseModifyQuery(FDeleteQry);
                end;
   end;
+  if (qry.SQL.Text<>s) and (s<>'') then qry.SQL.Text:=s; //assign only when changed, to avoid UnPrepare/Prepare
   assert(qry.sql.Text<>'');
   with qry do
     begin

+ 55 - 4
packages/fcl-db/tests/testbufdatasetstreams.pas

@@ -32,6 +32,8 @@ type
     procedure SeveralEditsChange(ADataset: TCustomBufDataset);
     procedure DeleteAllChange(ADataset: TCustomBufDataset);
     procedure DeleteAllInsertChange(ADataset: TCustomBufDataset);
+    procedure NullInsertChange(ADataset: TCustomBufDataset);
+    procedure NullEditChange(ADataset: TCustomBufDataset);
   protected
     procedure SetUp; override;
     procedure TearDown; override;
@@ -53,6 +55,7 @@ type
     procedure SeveralEditsApplUpd;
     procedure DeleteAllApplUpd;
     procedure DeleteAllInsertApplUpd;
+    procedure NullInsertUpdateApplUpd;
 
     procedure TestBasicsXML;
     procedure TestSimpleEditXML;
@@ -99,6 +102,7 @@ begin
   if Inserts then
     begin
     ChangedDs := TSQLDBConnector(DBConnector).Query;
+    ChangedDs.Close;
     TSQLQuery(ChangedDS).SQL.Text:='SELECT * FROM FPDEV WHERE (ID < 16) or (ID>100) ORDER BY ID';
 
     OrgDs.IndexFieldNames:='ID';
@@ -274,14 +278,43 @@ begin
     end;
 end;
 
-procedure TTestBufDatasetStreams.SetUp;
+procedure TTestBufDatasetStreams.NullInsertChange(ADataset: TCustomBufDataset);
 begin
-  DBConnector.StartTest;
+  with ADataset do
+  begin
+    AssertTrue(Locate('ID',11,[]));
+    Delete; //11
+    Delete; //12
+    Delete; //13
+    Delete; //14
+    Append;
+    FieldByName('ID').AsInteger:=11;
+    //FieldByName('NAME').Clear;
+    Post;
+    AppendRecord([12,'AfterNull']);
+    AppendRecord([13,null]);
+    AppendRecord([14,'AfterNull']);
+    //Append; Post;
+  end;
 end;
 
-procedure TTestBufDatasetStreams.TearDown;
+procedure TTestBufDatasetStreams.NullEditChange(ADataset: TCustomBufDataset);
+var i: integer;
 begin
-  DBConnector.StopTest;
+  //depends on procedure TTestBufDatasetStreams.NullInsertChange
+  if ADataSet is TSQLQuery then
+    with ADataset as TSQLQuery do
+    begin
+      AssertTrue(Locate('ID',11,[]));
+      for i:=11 to 14 do
+      begin
+        Edit;
+        FieldByName('NAME').AsString:='TestName'+inttostr(i);
+        Post;
+        Next;
+      end;
+      UpdateMode:=upWhereAll; //test when also null fields will be in where
+    end;
 end;
 
 procedure TTestBufDatasetStreams.TestSimpleEditCancelUpd;
@@ -459,6 +492,24 @@ begin
   TestChangesApplyUpdates(@DeleteAllInsertChange, False);
 end;
 
+procedure TTestBufDatasetStreams.NullInsertUpdateApplUpd;
+begin
+  TestChangesApplyUpdates(@NullInsertChange, True);
+  TestChangesApplyUpdates(@NullEditChange, True);
+end;
+
+
+procedure TTestBufDatasetStreams.SetUp;
+begin
+  DBConnector.StartTest;
+end;
+
+procedure TTestBufDatasetStreams.TearDown;
+begin
+  DBConnector.StopTest;
+end;
+
+
 initialization
   if uppercase(dbconnectorname)='SQL' then
     RegisterTestDecorator(TDBBasicsTestSetup, TTestBufDatasetStreams);

+ 85 - 0
packages/fcl-db/tests/testwherenull.lpr

@@ -0,0 +1,85 @@
+program testWhereNULL;
+
+{$mode objfpc}{$H+}
+
+uses
+  {$IFDEF UNIX}{$IFDEF UseCThreads}
+  cthreads,
+  {$ENDIF}{$ENDIF}
+  Classes, SysUtils,
+  db, sqldb, sqlite3conn, variants;
+
+
+var
+  Conn: TSQLite3Connection;
+  Tran: TSQLTransaction;
+  Q: TSQLQuery;
+
+  sql: string;
+  i: integer;
+
+begin
+  Conn:=TSQLite3Connection.Create(nil);
+  Conn.DatabaseName:='test.db';
+
+  Tran:=TSQLTransaction.Create(nil);
+  Tran.DataBase:=Conn;
+
+  Q:=TSQLQuery.Create(nil);
+  Q.DataBase:=Conn;
+
+  Conn.Open;
+  writeln('Connected');
+
+  Conn.ExecuteDirect('CREATE TEMPORARY TABLE t (int_field INT, string_field VARCHAR(30))');
+  writeln('Temporary table created');
+
+  Q.SQL.Text:='SELECT * FROM t';
+  Q.UpdateMode:=upWhereAll; // <-- UpdateMode is upWhereAll or upWhereCahnged
+  Q.Open;
+  Q.AppendRecord([NULL,'a']);
+  Q.AppendRecord([2,'c']);
+  Q.ApplyUpdates;
+  Q.Close;
+
+  writeln('1. Bug: second row has instead of 2 in first column NULL');
+  Q.Open;
+  Q.Next;
+  writeln('Value of ', Q.Fields[0].FieldName,' is: ', Q.Fields[0].AsString, ' expected: 2');
+  Q.Close;
+
+  writeln;
+  writeln('2. Case update of record, where some value is null (upWhereAll or upWhereChanged)');
+  Q.Open;
+  Q.Edit;
+  Q.Fields[1].AsString:='b';
+  Q.Post;
+  Q.ApplyUpdates;
+  Q.Close;
+
+  Q.Open;
+  writeln('Value of ', Q.Fields[1].FieldName,' is: ', Q.Fields[1].AsString,' expected: b');
+  Q.Close;
+
+  writeln;
+  writeln('3. Case delete of record, where some value is null (upWhereAll or upWhereChanged)');
+  Q.Open;
+  Q.Delete;
+  Q.ApplyUpdates;
+  Q.Close;
+
+  Q.Open;
+  writeln('Number of rows: ', Q.RecordCount, ' expected: 1');
+  Q.Close;
+
+  //END
+  Tran.Commit;
+  Conn.Close;
+
+  Q.Free;
+  Tran.Free;
+  Conn.Free;
+  writeln('End. Press any key');
+  readln;
+end.
+