浏览代码

Keep ibconnection open between operations. Use built in insert/update/delete sql generation from select query instead of turbobird code.

Reinier Olislagers 11 年之前
父节点
当前提交
156f99c750
共有 7 个文件被更改,包括 147 次插入136 次删除
  1. 1 1
      TurboBird.lpi
  2. 8 8
      editdatafullrec.lfm
  3. 13 11
      editdatafullrec.lrs
  4. 31 5
      editdatafullrec.pas
  5. 40 9
      edittable.pas
  6. 53 102
      main.pas
  7. 1 0
      querywindow.pas

+ 1 - 1
TurboBird.lpi

@@ -228,6 +228,7 @@
         <Filename Value="editdatafullrec.pas"/>
         <IsPartOfProject Value="True"/>
         <ComponentName Value="fmEditDataFullRec"/>
+        <HasResources Value="True"/>
         <ResourceBaseClass Value="Form"/>
         <UnitName Value="EditDataFullRec"/>
       </Unit16>
@@ -397,7 +398,6 @@
       <Unit39>
         <Filename Value="turbocommon.inc"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="Unit1"/>
       </Unit39>
     </Units>
   </ProjectOptions>

+ 8 - 8
editdatafullrec.lfm

@@ -9,20 +9,20 @@ object fmEditDataFullRec: TfmEditDataFullRec
   ClientWidth = 593
   OnClose = FormClose
   Position = poScreenCenter
-  LCLVersion = '0.9.30'
+  LCLVersion = '1.2.2.0'
   object Label1: TLabel
     Left = 258
-    Height = 18
+    Height = 13
     Top = 27
-    Width = 60
+    Width = 45
     Caption = 'Record #'
     ParentColor = False
   end
   object laPos: TLabel
     Left = 326
-    Height = 18
+    Height = 13
     Top = 27
-    Width = 9
+    Width = 6
     Caption = '0'
     Font.Color = 11610912
     ParentColor = False
@@ -43,6 +43,7 @@ object fmEditDataFullRec: TfmEditDataFullRec
     ClientHeight = 25
     ClientWidth = 241
     DataSource = Datasource1
+    Options = []
     TabOrder = 0
   end
   object bbSave: TBitBtn
@@ -54,16 +55,15 @@ object fmEditDataFullRec: TfmEditDataFullRec
     OnClick = bbSaveClick
     TabOrder = 1
   end
-  object Datasource1: TDatasource
+  object Datasource1: TDataSource
     DataSet = sqEditTable
     left = 408
     top = 128
   end
   object sqEditTable: TSQLQuery
-    IndexName = 'DEFAULT_ORDER'
+    FieldDefs = <>
     AutoCalcFields = False
     AfterScroll = sqEditTableAfterScroll
-    ReadOnly = False
     Params = <>
     left = 385
     top = 48

+ 13 - 11
editdatafullrec.lrs

@@ -1,11 +1,13 @@
+{ This is an automatically generated lazarus resource file }
+
 LazarusResources.Add('TfmEditDataFullRec','FORMDATA',[
   'TPF0'#18'TfmEditDataFullRec'#17'fmEditDataFullRec'#4'Left'#3#20#2#6'Height'#3
   +','#1#3'Top'#3'H'#1#5'Width'#3'Q'#2#13'ActiveControl'#7#6'bbSave'#7'Caption'
   +#6#21'Edit Data Full Record'#12'ClientHeight'#3','#1#11'ClientWidth'#3'Q'#2#7
-  +'OnClose'#7#9'FormClose'#8'Position'#7#14'poScreenCenter'#10'LCLVersion'#6#6
-  +'0.9.30'#0#6'TLabel'#6'Label1'#4'Left'#3#2#1#6'Height'#2#18#3'Top'#2#27#5'Wi'
-  +'dth'#2'<'#7'Caption'#6#8'Record #'#11'ParentColor'#8#0#0#6'TLabel'#5'laPos'
-  +#4'Left'#3'F'#1#6'Height'#2#18#3'Top'#2#27#5'Width'#2#9#7'Caption'#6#1'0'#10
+  +'OnClose'#7#9'FormClose'#8'Position'#7#14'poScreenCenter'#10'LCLVersion'#6#7
+  +'1.2.2.0'#0#6'TLabel'#6'Label1'#4'Left'#3#2#1#6'Height'#2#13#3'Top'#2#27#5'W'
+  +'idth'#2'-'#7'Caption'#6#8'Record #'#11'ParentColor'#8#0#0#6'TLabel'#5'laPos'
+  +#4'Left'#3'F'#1#6'Height'#2#13#3'Top'#2#27#5'Width'#2#6#7'Caption'#6#1'0'#10
   +'Font.Color'#4' +'#177#0#11'ParentColor'#8#10'ParentFont'#8#0#0#12'TDBNaviga'
   +'tor'#12'DBNavigator1'#4'Left'#2#8#6'Height'#2#25#3'Top'#2#21#5'Width'#3#241
   +#0#10'BevelOuter'#7#6'bvNone'#29'ChildSizing.EnlargeHorizontal'#7#14'crsScal'
@@ -13,11 +15,11 @@ LazarusResources.Add('TfmEditDataFullRec','FORMDATA',[
   +'g.ShrinkHorizontal'#7#14'crsScaleChilds'#26'ChildSizing.ShrinkVertical'#7#14
   +'crsScaleChilds'#18'ChildSizing.Layout'#7#29'cclLeftToRightThenTopToBottom'
   +#27'ChildSizing.ControlsPerLine'#2'd'#12'ClientHeight'#2#25#11'ClientWidth'#3
-  +#241#0#10'DataSource'#7#11'Datasource1'#8'TabOrder'#2#0#0#0#7'TBitBtn'#6'bbS'
-  +'ave'#4'Left'#3#237#1#6'Height'#2#30#3'Top'#2#16#5'Width'#2'K'#7'Caption'#6#4
-  +'Save'#7'OnClick'#7#11'bbSaveClick'#8'TabOrder'#2#1#0#0#11'TDatasource'#11'D'
-  +'atasource1'#7'DataSet'#7#11'sqEditTable'#4'left'#3#152#1#3'top'#3#128#0#0#0
-  +#9'TSQLQuery'#11'sqEditTable'#9'IndexName'#6#13'DEFAULT_ORDER'#14'AutoCalcFi'
-  +'elds'#8#11'AfterScroll'#7#22'sqEditTableAfterScroll'#8'ReadOnly'#8#6'Params'
-  +#14#0#4'left'#3#129#1#3'top'#2'0'#0#0#0
+  +#241#0#10'DataSource'#7#11'Datasource1'#7'Options'#11#0#8'TabOrder'#2#0#0#0#7
+  +'TBitBtn'#6'bbSave'#4'Left'#3#237#1#6'Height'#2#30#3'Top'#2#16#5'Width'#2'K'
+  +#7'Caption'#6#4'Save'#7'OnClick'#7#11'bbSaveClick'#8'TabOrder'#2#1#0#0#11'TD'
+  +'ataSource'#11'Datasource1'#7'DataSet'#7#11'sqEditTable'#4'left'#3#152#1#3't'
+  +'op'#3#128#0#0#0#9'TSQLQuery'#11'sqEditTable'#9'FieldDefs'#14#0#14'AutoCalcF'
+  +'ields'#8#11'AfterScroll'#7#22'sqEditTableAfterScroll'#6'Params'#14#0#4'left'
+  +#3#129#1#3'top'#2'0'#0#0#0
 ]);

+ 31 - 5
editdatafullrec.pas

@@ -93,11 +93,38 @@ begin
 
 
   sqEditTable.SQL.Text:= 'select * from ' +  ATableName;
-
-  bbSave.Visible:= fmMain.ChangeQueryToUpdatable(dbIndex, ATableName, sqEditTable);
-  if not bbSave.Visible then
-    ShowMessage('Primary key does not exist; table can not be edited');
   sqEditTable.Open;
+  bbSave.Visible:= true;
+  {
+  // ASSUME there's a generator/trigger
+  //todo: verify this assumption using code to check this out. Then also modify
+  //insert statement to leave out the relevant fields if not present
+  FieldsList:= TStringList.Create;
+  try
+    if fmmain.GetPrimaryKeyFields(dbIndex, ATableName, FieldsList) then
+    begin
+      bbSave.Visible:= true;
+      for i:= 0 to FieldsList.Count -1 do
+      begin
+        try
+          sqEditTable.FieldByName(FieldsList[i]).Required:=false;
+        except
+          // field does not exist => error
+          bbSave.Visible:=false;
+          break;
+        end;
+      end;
+    end
+    else
+    begin
+      bbSave.Visible:= false;
+    end;
+  finally
+    FieldsList.Free;
+  end;
+  }
+  if not(bbSave.Visible) then
+    ShowMessage('Primary key is not found for this table. It can not be edited.');
 
   ATop:= 70;
   for i:= 0 to sqEditTable.Fields.Count - 1 do
@@ -164,7 +191,6 @@ begin
       Inc(ATop, 30);
   end;
   Height:= ATop + 10;
-
 end;
 
 initialization

+ 40 - 9
edittable.pas

@@ -63,7 +63,6 @@ begin
   try
     if sqEditTable.State in [dsInsert, dsEdit] then
       sqEditTable.Post;
-
     if sqEditTable.Active then
       sqEditTable.ApplyUpdates;
     if SQLTrans.Active then
@@ -92,23 +91,55 @@ begin
 end;
 
 procedure TfmEditTable.Init(dbIndex: Integer; ATableName: string);
+var
+  FieldsList: TStringList;
+  i: integer;
+  PKField: TField;
 begin
   sqEditTable.Close;
   if ibConnection = nil then
   begin
     ibConnection:= Rec.IBConnection;
-    ibConnection.Close;
+    if not(ibConnection.Connected) then
+      ibConnection.Open;
     sqlTrans:= Rec.SQLTrans;
     sqEditTable.DataBase:= ibConnection;
   end;
-
-  bbSave.Visible:= fmMain.ChangeQueryToUpdatable(dbIndex, ATableName, sqEditTable);
-  if not bbSave.Visible then
-    ShowMessage('Primary key is not found for this table. It can not be edited.');
-
-  sqEditTable.Close;
+  //todo: deal with quoted identifiers in ATableName here and elsewhere
   sqEditTable.SQL.Text:= 'select * from ' + ATableName;
-  sqEditTable.Open;
+  sqEditTable.Open; // need to have open query in order to access fields below
+
+  bbSave.Visible:= true;
+  {
+  // ASSUME there's a generator/trigger
+  //todo: verify this assumption using code to check this out. Then also modify
+  //insert statement to leave out the relevant fields if not present
+  FieldsList:= TStringList.Create;
+  try
+    if fmmain.GetPrimaryKeyFields(dbIndex, ATableName, FieldsList) then
+    begin
+      bbSave.Visible:= true;
+      for i:= 0 to FieldsList.Count -1 do
+      begin
+        try
+          sqEditTable.FieldByName(FieldsList[i]).Required:=false;
+        except
+          // field does not exist => error
+          bbSave.Visible:=false;
+          break;
+        end;
+      end;
+    end
+    else
+    begin
+      bbSave.Visible:= false;
+    end;
+  finally
+    FieldsList.Free;
+  end;
+  }
+  if not(bbSave.Visible) then
+    ShowMessage('Primary key is not found for this table. It can not be edited.');
 end;
 
 initialization

+ 53 - 102
main.pas

@@ -201,6 +201,7 @@ type
     function RemoveSpecialChars(AText: string): string;
     // Remove RegisteredDatabases and clean up memory held by its objects
     procedure ReleaseRegisteredDatabases;
+    // Set connection for SQLQuery1 to selected registered database
     procedure SetConnection(Index: Integer);
     procedure SetFocus; override; // solve a bug in Lazarus
     { private declarations }
@@ -222,6 +223,8 @@ type
     // Get name of index used for primary key
     // Also returns name of constraint used
     function GetPrimaryKeyIndexName(DatabaseIndex: Integer; ATableName: string; var ConstraintName: string): string;
+    // Get primary key field(s) names into KeyFields
+    function GetPrimaryKeyFields(DatabaseIndex: Integer; ATableName: string; var KeyFields: TStringList): boolean;
     function GetConstraintFields(ATableName, AIndexName: string; var List: TStringList): Boolean;
     // Get fields information for specified table
     // Fills SQLQuery1 with details
@@ -243,8 +246,6 @@ type
       OnCommitProcedure: TNotifyEvent = nil);
     procedure ViewTableFields(ATableName: string; dbIndex: Integer; AStringGrid: TStringGrid);
     procedure ShowIndicesManagement(AForm: TForm; DatabaseIndex: Integer; ATableName: string);
-    // Taking a normal query, try to make sure the UpdateSQL and InsertSQL work.
-    function ChangeQueryToUpdatable(DatabaseIndex: Integer; ATableName: string; sqQuery: TSQLQuery): Boolean;
     function GetTableNames(dbIndex: Integer): string;
     function CreateNewTrigger(dbIndex: Integer; ATableName: string; OnCommitProcedure: TNotifyEvent = nil): Boolean;
     function AddToSQLHistory(DatabaseTitle: string; SQLType, SQLStatement: string): Boolean;
@@ -1368,7 +1369,9 @@ begin
   if ibConnection <> RegisteredDatabases[Index].IBConnection then
   begin
     ibConnection:= RegisteredDatabases[Index].IBConnection;
-    ibConnection.Close;
+    // This used to say ibConnection.Close which will simply also close all open
+    // queries - not a good idea
+    //ibConnection.Close;
     sqlTransaction:= RegisteredDatabases[Index].SQLTrans;
     ibConnection.Transaction:= sqlTransaction;
     SQLQuery1.DataBase:= ibConnection;
@@ -1982,7 +1985,7 @@ var
   dbIndex: Integer;
   ScriptList: TStringList;
   Line: string;
-  PKName: string;
+  PKIndexName: string;
   ConstraintName: string;
   List: TStringList;
   i: Integer;
@@ -2023,7 +2026,7 @@ begin
       QWindow.meQuery.Lines.Add('');
 
       // Script Secondary indices
-      PKName:= GetPrimaryKeyIndexName(dbIndex, ATableName, ConstraintName);
+      PKIndexName:= GetPrimaryKeyIndexName(dbIndex, ATableName, ConstraintName);
       List:= TStringList.Create;
       try
         with dmSysTables do
@@ -2031,7 +2034,7 @@ begin
         with sqQuery do
         while not EOF do
         begin
-          if PKName <> Trim(FieldByName('RDB$Index_name').AsString) then
+          if PKIndexName <> Trim(FieldByName('RDB$Index_name').AsString) then
           begin
             Line:= 'create ';
             if FieldByName('RDB$Unique_Flag').AsString = '1' then
@@ -2122,7 +2125,7 @@ var
   AFieldName: string;
   WhereClause: string;
   Skipped: Boolean;
-  PKeyName: string;
+  PKIndexName: string;
   dbIndex: Integer;
   ConstraintName: string;
   LastParam: string;
@@ -2182,10 +2185,10 @@ begin
     // Primary Keys
     WhereClause:= '';
     PKFieldsList:= TStringList.Create;
-    PKeyName:= GetPrimaryKeyIndexName(dbIndex, ATableName, ConstraintName);
-    if PKeyName <> '' then
+    PKIndexName:= GetPrimaryKeyIndexName(dbIndex, ATableName, ConstraintName);
+    if PKIndexName <> '' then
     begin
-      GetConstraintFields(ATableName, PKeyName, PKFieldsList);
+      GetConstraintFields(ATableName, PKIndexName, PKFieldsList);
       for i:= 0 to PKFieldsList.Count - 1 do
       begin
         WhereClause:= WhereClause + PKFieldsList[i] + ' = :' + PKFieldsList[i];
@@ -3043,7 +3046,7 @@ var
   i: Integer;
   PKFieldsList: TStringList;
   DefaultValue: string;
-  PKeyName: string;
+  PKIndexName: string;
   ConstraintName: string;
 begin
   try
@@ -3111,9 +3114,9 @@ begin
     // Primary Keys
     PKFieldsList:= TStringList.Create;
     try
-      PKeyName:= GetPrimaryKeyIndexName(dbIndex, ATableName, ConstraintName);
-      if PKeyName <> '' then
-        GetConstraintFields(ATableName, PKeyName, PKFieldsList);
+      PKIndexName:= GetPrimaryKeyIndexName(dbIndex, ATableName, ConstraintName);
+      if PKIndexName <> '' then
+        GetConstraintFields(ATableName, PKIndexName, PKFieldsList);
 
       with AStringGrid do
       for i:= 1 to RowCount - 1 do
@@ -3183,7 +3186,7 @@ var
   FieldTitle: string;
   FieldNode: TTreeNode;
   PKFieldsList: TStringList;
-  PKeyName: string;
+  PKIndexName: string;
   ConstraintName: string;
   AFieldName: string;
   i: Integer;
@@ -3197,9 +3200,9 @@ begin
     // Primary Keys
     PKFieldsList:= TStringList.Create;
     try
-      PKeyName:= GetPrimaryKeyIndexName(dbIndex, Node.Text, ConstraintName);
-      if PKeyName <> '' then
-        GetConstraintFields(Node.Text, PKeyName, PKFieldsList);
+      PKIndexName:= GetPrimaryKeyIndexName(dbIndex, Node.Text, ConstraintName);
+      if PKIndexName <> '' then
+        GetConstraintFields(Node.Text, PKIndexName, PKFieldsList);
 
       // Fields
       FieldsList:= TStringList.Create;
@@ -4145,6 +4148,38 @@ begin
   SQLQuery1.Close;
 end;
 
+function TfmMain.GetPrimaryKeyFields(DatabaseIndex: Integer;
+  ATableName: string; var KeyFields: TStringList): boolean;
+const
+  // Select field(s) that make up primary key
+  Template=' SELECT r.rdb$field_name ' +
+    ' FROM RDB$RELATION_FIELDS r ' +
+    ' LEFT JOIN RDB$FIELDS f ON r.RDB$FIELD_SOURCE = f.RDB$FIELD_NAME ' +
+    ' LEFT JOIN RDB$INDEX_SEGMENTS s ON s.RDB$FIELD_NAME=r.RDB$FIELD_NAME ' +
+    ' LEFT JOIN RDB$INDICES i ON i.RDB$INDEX_NAME = s.RDB$INDEX_NAME ' +
+    ' AND i.RDB$RELATION_NAME=r.RDB$RELATION_NAME ' +
+    ' LEFT JOIN RDB$RELATION_CONSTRAINTS rc ON rc.RDB$INDEX_NAME = s.RDB$INDEX_NAME ' +
+    ' AND rc.RDB$INDEX_NAME = i.RDB$INDEX_NAME ' +
+    ' AND rc.RDB$RELATION_NAME = i.RDB$RELATION_NAME ' +
+    ' WHERE r.RDB$RELATION_NAME=''%s'' AND ' +
+    ' rc.RDB$CONSTRAINT_TYPE = ''PRIMARY KEY'' ';
+begin
+  result:= false;
+  KeyFields.Clear;
+  SQLQuery1.Close;
+  SetConnection(DatabaseIndex);
+  SQLQuery1.Close;
+  SQLQuery1.SQL.Text:=format(Template,[UpperCase(ATableName)]);
+  SQLQuery1.Open;
+  while not(SQLQuery1.EOF) do
+  begin
+    KeyFields.Add(Trim(SQLQuery1.FieldByName('rdb$field_name').AsString));
+    SQLQuery1.Next;
+  end;
+  SQLQuery1.Close;
+  result:= true;
+end;
+
 (*********  Get constrain fields  *********)
 
 function TfmMain.GetConstraintFields(ATableName, AIndexName: string; var List: TStringList): Boolean;
@@ -4173,90 +4208,6 @@ begin
   Result:= List.Count > 0;
 end;
 
-
-function TfmMain.ChangeQueryToUpdatable(DatabaseIndex: Integer; ATableName: string; sqQuery: TSQLQuery): Boolean;
-var
-  KeyList, FieldsList: TStringList;
-  PKName: string;
-  sqPrimaryKey: TSQLQuery;
-  i: Integer;
-  WhereClause: string;
-  ConstraintName: string;
-begin
-  Result:= false;
-  SetConnection(DatabaseIndex);
-
-  sqQuery.UpdateSQL.Clear;
-  sqQuery.DeleteSQL.Clear;
-  sqQuery.InsertSQL.Clear;
-
-  KeyList:= TStringList.Create;
-  FieldsList:= TStringList.Create;
-  try
-    PKName:= fmMain.GetPrimaryKeyIndexName(DatabaseIndex, ATableName, ConstraintName);
-    if (PKName <> '') then
-    begin
-      sqPrimaryKey:= TSQLQuery.Create(nil);
-      try
-        sqPrimaryKey.DataBase:= IBConnection;
-        GetIndexFields(ATableName, PKName, sqPrimaryKey, KeyList);
-        GetFields(DatabaseIndex, ATableName, FieldsList);
-
-        // Update SQL
-        sqQuery.UpdateSQL.Add('update ' + ATableName + ' set ');
-        for i:= 0 to FieldsList.Count - 1 do
-        begin
-          if KeyList.IndexOf(FieldsList[i]) = -1 then
-          begin
-            sqQuery.UpdateSQL.Add(FieldsList[i] + ' = :' + FieldsList[i]);
-            sqQuery.UpdateSQL.Add(',');
-          end;
-        end;
-        sqQuery.UpdateSQL.Delete(sqQuery.UpdateSQL.Count - 1); // Delete last comma
-
-        // Key where clause
-        WhereClause:= ' where ';
-        for i:= 0 to KeyList.Count - 1 do
-        begin
-          WhereClause:= WhereClause + KeyList[i] + ' = :' + KeyList[i];
-          if i + 1 < KeyList.Count then
-            WhereClause:= WhereClause + ' and ';
-        end;
-        sqQuery.UpdateSQL.Add(WhereClause);
-
-        // Insert SQL
-        sqQuery.InsertSQL.Add('insert into ' + ATableName + ' (');
-        for i:= 0 to FieldsList.Count - 1 do
-        begin
-          sqQuery.InsertSQL.Add(FieldsList[i]);
-          if i < FieldsList.Count - 1 then
-            sqQuery.InsertSQL.Add(',')
-          else
-           sqQuery.InsertSQL.Add(') values (');
-        end;
-
-        for i:= 0 to FieldsList.Count - 1 do
-        begin
-          sqQuery.InsertSQL.Add(':' + FieldsList[i]);
-          if i < FieldsList.Count - 1 then
-            sqQuery.InsertSQL.Add(',')
-          else
-            sqQuery.InsertSQL.Add(')');
-        end;
-
-        // Delete SQL
-        sqQuery.DeleteSQL.Text:= 'delete from ' + ATableName + WhereClause;
-      finally
-        sqPrimaryKey.Free;
-      end;
-      Result:= true;
-    end;
-  finally
-    KeyList.Free;
-    FieldsList.Free;
-  end;
-end;
-
 (********  Get table names   ********)
 
 function TfmMain.GetTableNames(dbIndex: Integer): string;

+ 1 - 0
querywindow.pas

@@ -152,6 +152,7 @@ type
     procedure tbSaveClick(Sender: TObject);
   private
     { private declarations }
+    // Index of selected registered database
     fdbIndex: Integer;
     RegRec: TRegisteredDatabase;
     ResultControls: array of TObject;