diff --git a/source/const.inc b/source/const.inc index a8ffe178..bea50b68 100644 --- a/source/const.inc +++ b/source/const.inc @@ -287,26 +287,6 @@ const SContainsNulCharFile = 'This file contains NUL characters. They have been converted to ASCII spaces (SP).'; SContainsNulCharGrid = 'This cell contains NUL characters. They have been converted to ASCII spaces (SP). Press ESC to cancel editing.'; - DBO_NAME = 'Name'; - DBO_TYPE = 'Type'; - DBO_ENGINE = 'Engine'; - DBO_VERSION = 'Version'; - DBO_ROWFORMAT = 'Row_format'; - DBO_ROWS = 'Rows'; - DBO_AVGROWLEN = 'Avg_row_length'; - DBO_DATALEN = 'Data_length'; - DBO_MAXDATALEN = 'Max_data_length'; - DBO_INDEXLEN = 'Index_length'; - DBO_DATAFREE = 'Data_free'; - DBO_AUTOINC = 'Auto_increment'; - DBO_CREATED = 'Create_time'; - DBO_UPDATED = 'Update_time'; - DBO_CHECKED = 'Check_time'; - DBO_COLLATION = 'Collation'; - DBO_CHECKSUM = 'Checksum'; - DBO_CROPTIONS = 'Create_options'; - DBO_COMMENT = 'Comment'; - PKEY = 'PRIMARY'; KEY = 'KEY'; UKEY = 'UNIQUE'; diff --git a/source/copytable.pas b/source/copytable.pas index ff6b9ad3..4388cb18 100644 --- a/source/copytable.pas +++ b/source/copytable.pas @@ -169,6 +169,7 @@ var isFulltext : Boolean; struc_data : Byte; Fixes : TWideStringlist; + DBObjects : TDBObjectList; begin // copy table! @@ -249,17 +250,16 @@ begin // < keys // Add collation and engine clauses - Results := Mainform.FetchActiveDbTableList; - while not Results.Eof do begin - if Results.Col(DBO_NAME) = oldTableName then begin - if Results.ColExists(DBO_COLLATION) and (Results.Col(DBO_COLLATION) <> '') then - strquery := strquery + ' COLLATE ' + Results.Col(DBO_COLLATION); - if Results.ColExists(DBO_ENGINE) and (Results.Col(DBO_ENGINE) <> '') then - strquery := strquery + ' ENGINE=' + Results.Col(DBO_ENGINE); - strquery := strquery + ' COMMENT=' + esc(Results.Col(DBO_COMMENT)); + DBObjects := Mainform.Connection.GetDBObjects(Mainform.ActiveDatabase); + for i:=0 to DBObjects.Count-1 do begin + if DBObjects[i].Name = oldTableName then begin + if DBObjects[i].Collation <> '' then + strquery := strquery + ' COLLATE ' + DBObjects[i].Collation; + if DBObjects[i].Engine <> '' then + strquery := strquery + ' ENGINE=' + DBObjects[i].Engine; + strquery := strquery + ' COMMENT=' + esc(DBObjects[i].Comment); break; end; - Results.Next; end; strquery := strquery + ' SELECT'; diff --git a/source/createdatabase.pas b/source/createdatabase.pas index bdbf649d..6f4198dc 100644 --- a/source/createdatabase.pas +++ b/source/createdatabase.pas @@ -204,8 +204,8 @@ procedure TCreateDatabaseForm.btnOKClick(Sender: TObject); var sql : WideString; AllDatabases, Unions, ObjectsLeft: TWideStringList; - ObjectsInNewDb, ObjectsInOldDb: TMySQLQuery; - OldObjType, NewObjType: TListNodeType; + ObjectsInNewDb, ObjectsInOldDb: TDBObjectList; + i, j: Integer; begin if modifyDB = '' then try sql := GetCreateStatement; @@ -229,30 +229,25 @@ begin Mainform.Connection.Query(sql); end else begin // Rename database - ObjectsInOldDb := MainForm.RefreshDbTableList(modifyDB); + ObjectsInOldDb := MainForm.Connection.GetDBObjects(modifyDB, True); AllDatabases := Mainform.Connection.GetCol('SHOW DATABASES'); if AllDatabases.IndexOf(editDBName.Text) > -1 then - ObjectsInNewDb := MainForm.RefreshDbTableList(editDBName.Text) + ObjectsInNewDb := MainForm.Connection.GetDBObjects(editDBName.Text, True) else ObjectsInNewDb := nil; // Silence compiler warning // Warn if there are tables with same names in new db - while not ObjectsInOldDb.Eof do begin - OldObjType := GetDBObjectType(ObjectsInOldDb); - if not (OldObjType in [lntTable, lntCrashedTable, lntView]) then + for i:=0 to ObjectsInOldDb.Count-1 do begin + if not (ObjectsInOldDb[i].NodeType in [lntTable, lntCrashedTable, lntView]) then Raise Exception.Create('Database "'+modifyDB+'" contains stored routine(s), which cannot be moved.'); if Assigned(ObjectsInNewDb) then begin - ObjectsInNewDb.First; - while not ObjectsInNewDb.Eof do begin - NewObjType := GetDBObjectType(ObjectsInNewDb); - if (ObjectsInOldDb.Col(DBO_NAME) = ObjectsInNewDb.Col(DBO_NAME)) - and (OldObjType = NewObjType) then begin + for j:=0 to ObjectsInNewDb.Count-1 do begin + if (ObjectsInOldDb[i].Name = ObjectsInNewDb[j].Name) + and (ObjectsInOldDb[i].NodeType = ObjectsInNewDb[j].NodeType) then begin // One or more objects have a naming conflict Raise Exception.Create('Database "'+editDBName.Text+'" exists and has objects with same names as in "'+modifyDB+'"'); end; - ObjectsInNewDb.Next; end; end; - ObjectsInOldDb.Next; end; if AllDatabases.IndexOf(editDBName.Text) = -1 then begin @@ -265,19 +260,17 @@ begin Exit; end; // Move all tables, views and procedures to target db - ObjectsInOldDb.First; sql := ''; - while not ObjectsInOldDb.Eof do begin - sql := sql + Mainform.mask(modifyDb)+'.'+Mainform.mask(ObjectsInOldDb.Col(DBO_NAME))+' TO '+ - Mainform.mask(editDBName.Text)+'.'+Mainform.mask(ObjectsInOldDb.Col(DBO_NAME))+', '; - ObjectsInOldDb.Next; + for i:=0 to ObjectsInOldDb.Count-1 do begin + sql := sql + Mainform.mask(modifyDb)+'.'+Mainform.mask(ObjectsInOldDb[i].Name)+' TO '+ + Mainform.mask(editDBName.Text)+'.'+Mainform.mask(ObjectsInOldDb[i].Name)+', '; end; if sql <> '' then begin Delete(sql, Length(sql)-1, 2); sql := 'RENAME TABLE '+sql; Mainform.Connection.Query(sql); - Mainform.ClearDbTableList(modifyDB); - Mainform.ClearDbTableList(editDBName.Text); + Mainform.Connection.ClearDbObjects(modifyDB); + Mainform.Connection.ClearDbObjects(editDBName.Text); end; // Last step for renaming: drop source database ObjectsLeft := TWideStringList.Create; diff --git a/source/helpers.pas b/source/helpers.pas index 798be897..34ac5b21 100644 --- a/source/helpers.pas +++ b/source/helpers.pas @@ -15,8 +15,6 @@ uses Classes, SysUtils, Graphics, GraphUtil, db, clipbrd, dialogs, type - TListNodeType = (lntNone, lntDb, lntTable, lntCrashedTable, lntView, lntFunction, lntProcedure, lntTrigger, lntColumn); - TListNodeTypes = Set of TListNodeType; TListNode = record Text: WideString; NodeType: TListNodeType; @@ -212,7 +210,6 @@ type function GetVTCaptions( VT: TVirtualStringTree; OnlySelected: Boolean = False; Column: Integer = 0; OnlyNodeTypes: TListNodeTypes = [lntNone] ): TWideStringList; procedure SetVTSelection( VT: TVirtualStringTree; Selected: TWideStringList ); function GetTempDir: String; - function GetDBObjectType(TableStatus: TMySQLQuery): TListNodeType; procedure SetWindowSizeGrip(hWnd: HWND; Enable: boolean); procedure SaveUnicodeFile(Filename: String; Text: WideString); procedure OpenTextFile(const Filename: String; out Stream: TFileStream; out FileCharset: TFileCharset); @@ -240,7 +237,6 @@ type procedure SelectNode(VT: TVirtualStringTree; Node: PVirtualNode); overload; function DateBackFriendlyCaption(d: TDateTime): String; procedure InheritFont(AFont: TFont); - function GetTableSize(Results: TMySQLQuery): Int64; function GetLightness(AColor: TColor): Byte; procedure ParseTableStructure(CreateTable: WideString; Columns: TObjectList=nil; Keys: TObjectList=nil; ForeignKeys: TObjectList=nil); procedure ParseViewStructure(ViewName: WideString; Columns: TObjectList); @@ -2186,47 +2182,6 @@ begin end; -// Tell type of db object (table|view) by a given row from a SHOW TABLE STATUS result -function GetDBObjectType(TableStatus: TMySQLQuery): TListNodeType; -var - t: String; -begin - {** - @see http://dev.mysql.com/doc/refman/5.1/en/show-table-status.html - For views, all the fields displayed by SHOW TABLE STATUS are NULL except - that Name indicates the view name and Comment says view. - @note The "Comment" column can contain different content, normally "VIEW" - but for views which is missing its tables, it says - "Views bla references invalid..." - } - Result := lntTable; - if TableStatus.ColExists('Type') then begin - t := TableStatus.Col('Type'); - if t = 'BASE TABLE' then - Result := lntTable - else if t = 'VIEW' then - Result := lntView - else if t = 'FUNCTION' then - Result := lntFunction - else if t = 'PROCEDURE' then - Result := lntProcedure - else if t = 'TRIGGER' then - Result := lntTrigger; - end else begin - if - TableStatus.IsNull(1) and // Engine column is NULL for views - TableStatus.IsNull(2) and - (Pos('VIEW', UpperCase(TableStatus.Col(DBO_COMMENT))) > 0) - then Result := lntView; - if - TableStatus.IsNull(1) and - TableStatus.IsNull(2) and - (Pos('MARKED AS CRASHED', UpperCase(TableStatus.Col(DBO_COMMENT))) > 0) - then Result := lntCrashedTable; - end; -end; - - { Code taken from SizeGripHWND.pas: Copyright (C) 2005, 2006 Volker Siebert @@ -2928,17 +2883,6 @@ begin end; -function GetTableSize(Results: TMySQLQuery): Int64; -var - d, i: String; -begin - d := Results.Col('Data_length', True); - i := Results.Col('Index_length', True); - if (d = '') or (i = '') then Result := -1 - else Result := MakeInt(d) + MakeInt(i); -end; - - function GetLightness(AColor: TColor): Byte; var R, G, B: Byte; diff --git a/source/insertfiles.pas b/source/insertfiles.pas index 7642944e..89b1746c 100644 --- a/source/insertfiles.pas +++ b/source/insertfiles.pas @@ -89,15 +89,15 @@ end; { Read tables from selected DB } procedure TfrmInsertFiles.ComboBoxDBsChange(Sender: TObject); var - Results: TMySQLQuery; + DBObjects: TDBObjectList; + i: Integer; begin // read tables from db ComboBoxTables.Items.Clear; - Results := Mainform.FetchDbTableList(ComboBoxDBs.Text); - while not Results.Eof do begin - if GetDBObjectType(Results) in [lntTable, lntView] then - ComboBoxTables.Items.Add(Results.Col(DBO_NAME)); - Results.Next; + DBObjects := Mainform.Connection.GetDBObjects(ComboBoxDBs.Text); + for i:=0 to DBObjects.Count-1 do begin + if DBObjects[i].NodeType in [lntTable, lntView] then + ComboBoxTables.Items.Add(DBObjects[i].Name); end; if ComboBoxTables.Items.Count > 0 then ComboBoxTables.ItemIndex := 0; diff --git a/source/loaddata.pas b/source/loaddata.pas index d9dbc0b4..157b4138 100644 --- a/source/loaddata.pas +++ b/source/loaddata.pas @@ -137,7 +137,7 @@ end; procedure Tloaddataform.comboDatabaseChange(Sender: TObject); var count, i, selCharsetIndex, v: Integer; - ds: TMySQLQuery; + DBObjects: TDBObjectList; seldb, seltable, dbcreate: WideString; rx: TRegExpr; DefCharset: String; @@ -147,14 +147,13 @@ begin comboTable.Items.Clear; seldb := Mainform.ActiveDatabase; seltable := Mainform.SelectedTable.Text; - ds := Mainform.FetchDbTableList(comboDatabase.Text); - while not ds.Eof do begin - if GetDBObjectType(ds) in [lntTable, lntView] then - comboTable.Items.Add(ds.Col(DBO_NAME)); + DBObjects := Mainform.Connection.GetDBObjects(comboDatabase.Text); + for i:=0 to DBObjects.Count-1 do begin + if DBObjects[i].NodeType in [lntTable, lntView] then + comboTable.Items.Add(DBObjects[i].Name); count := comboTable.Items.Count-1; if (comboDatabase.Text = seldb) and (comboTable.Items[count] = seltable) then comboTable.ItemIndex := count; - ds.Next; end; if comboTable.ItemIndex = -1 then comboTable.ItemIndex := 0; diff --git a/source/main.pas b/source/main.pas index 006b4f32..f8251bfc 100644 --- a/source/main.pas +++ b/source/main.pas @@ -712,7 +712,6 @@ type ReachedEOT : Boolean; FDelimiter: String; viewingdata : Boolean; - CachedTableLists : TWideStringList; EditVariableForm : TfrmEditVariable; FileNameSessionLog : String; FileHandleSessionLog : Textfile; @@ -819,13 +818,6 @@ type property ActiveDatabase : WideString read GetActiveDatabase write SetSelectedDatabase; property SelectedTable : TListNode read GetSelectedTable; - function FetchActiveDbTableList: TMySQLQuery; - function RefreshActiveDbTableList: TMySQLQuery; - function FetchDbTableList(db: WideString): TMySQLQuery; - function RefreshDbTableList(db: WideString): TMySQLQuery; - procedure ClearDbTableList(db: WideString); - function DbTableListCachedAndValid(db: WideString): Boolean; - procedure ClearAllTableLists; procedure TestVTreeDataArray( P: PVTreeDataArray ); function GetVTreeDataArray( VT: TBaseVirtualTree ): PVTreeDataArray; procedure ActivateFileLogging; @@ -1188,8 +1180,6 @@ begin // SQLFiles-History FillPopupQueryLoad; - CachedTableLists := TWideStringList.Create; - Delimiter := GetRegValue(REGNAME_DELIMITER, DEFAULT_DELIMITER); // Delphi work around to force usage of Vista's default font (other OSes will be unaffected) @@ -1669,7 +1659,6 @@ begin // Clear database and table lists DBtree.ClearSelection; DBtree.FocusedNode := nil; - ClearAllTableLists; FreeAndNil(AllDatabases); FreeAndNil(FDataGridSelect); SynMemoFilter.Clear; @@ -2213,7 +2202,7 @@ begin Connection.Query('DROP DATABASE ' + mask(activeDB)); DBtree.FocusedNode := DBtree.GetFirst; DBTree.Selected[DBtree.FocusedNode] := True; - ClearDbTableList(activeDB); + Connection.ClearDbObjects(activeDB); RefreshTree(False); except on E:Exception do @@ -3439,22 +3428,21 @@ procedure TMainForm.DisplayRowCountStats(MatchingRows: Int64); var rows_total : Int64; // total rowcount IsFiltered, IsInnodb: Boolean; - Results: TMySQLQuery; - s: WideString; + DBObjects: TDBObjectList; + i: Integer; begin lblDataTop.Caption := ActiveDatabase + '.' + SelectedTable.Text; IsFiltered := self.DataGridCurrentFilter <> ''; if GetFocusedTreeNodeType = lntTable then begin // Get rowcount from table - Results := FetchActiveDbTableList; + DBObjects := Connection.GetDBObjects(ActiveDatabase); rows_total := -1; IsInnodb := False; - while not Results.Eof do begin - if Results.Col(DBO_NAME) = SelectedTable.Text then begin - s := Results.Col(DBO_ROWS); - if s <> '' then rows_total := MakeInt(s); - IsInnodb := Results.Col(1) = 'InnoDB'; + for i:=0 to DBObjects.Count-1 do begin + if DBObjects[i].Name = SelectedTable.Text then begin + rows_total := DBObjects[i].Rows; + IsInnodb := DBObjects[i].Engine = 'InnoDB'; break; end; end; @@ -3527,178 +3515,12 @@ begin end; -{*** - Look for list of tables for current database in cache. - Retrieve from server if necessary. - @return TMySQLQuery The cached list of tables for the active database. -} -function TMainForm.FetchActiveDbTableList: TMySQLQuery; -begin - Result := FetchDbTableList(ActiveDatabase); -end; - -function TMainForm.FetchDbTableList(db: WideString): TMySQLQuery; -var - Results: TMySQLQuery; - OldCursor: TCursor; - Unions: TWideStringlist; - ListObjectsSQL, Tables: WideString; -begin - if not DbTableListCachedAndValid(db) then begin - // Not in cache, load table list. - OldCursor := Screen.Cursor; - Screen.Cursor := crHourGlass; - ShowStatus('Fetching tables from "' + db + '" ...'); - Unions := TWideStringlist.Create; - if Connection.InformationSchemaObjects.IndexOf('TABLES') > -1 then begin - // Tables and (system) views - Unions.Add('SELECT TABLE_NAME AS '+mask(DBO_NAME)+ - ', TABLE_TYPE AS '+mask(DBO_TYPE)+ - ', ENGINE AS '+mask(DBO_ENGINE)+ - ', VERSION AS '+mask(DBO_VERSION)+ - ', ROW_FORMAT AS '+mask(DBO_ROWFORMAT)+ - ', TABLE_ROWS AS '+mask(DBO_ROWS)+ - ', AVG_ROW_LENGTH AS '+mask(DBO_AVGROWLEN)+ - ', DATA_LENGTH AS '+mask(DBO_DATALEN)+ - ', MAX_DATA_LENGTH AS '+mask(DBO_MAXDATALEN)+ - ', INDEX_LENGTH AS '+mask(DBO_INDEXLEN)+ - ', DATA_FREE AS '+mask(DBO_DATAFREE)+ - ', AUTO_INCREMENT AS '+mask(DBO_AUTOINC)+ - ', CREATE_TIME AS '+mask(DBO_CREATED)+ - ', UPDATE_TIME AS '+mask(DBO_UPDATED)+ - ', CHECK_TIME AS '+mask(DBO_CHECKED)+ - ', TABLE_COLLATION AS '+mask(DBO_COLLATION)+ - ', CHECKSUM AS '+mask(DBO_CHECKSUM)+ - ', CREATE_OPTIONS AS '+mask(DBO_CROPTIONS)+ - ', TABLE_COMMENT AS '+mask(DBO_COMMENT)+ - ' FROM '+mask(DBNAME_INFORMATION_SCHEMA)+'.TABLES ' + - 'WHERE TABLE_SCHEMA = '+esc(db)); - end; - if Connection.InformationSchemaObjects.IndexOf('ROUTINES') > -1 then begin - // Stored routines - Unions.Add('SELECT ROUTINE_NAME AS '+mask(DBO_NAME)+ - ', ROUTINE_TYPE AS '+mask(DBO_TYPE)+ - ', NULL AS '+mask(DBO_ENGINE)+ - ', NULL AS '+mask(DBO_VERSION)+ - ', NULL AS '+mask(DBO_ROWFORMAT)+ - ', NULL AS '+mask(DBO_ROWS)+ - ', NULL AS '+mask(DBO_AVGROWLEN)+ - ', NULL AS '+mask(DBO_DATALEN)+ - ', NULL AS '+mask(DBO_MAXDATALEN)+ - ', NULL AS '+mask(DBO_INDEXLEN)+ - ', NULL AS '+mask(DBO_DATAFREE)+ - ', NULL AS '+mask(DBO_AUTOINC)+ - ', CREATED AS '+mask(DBO_CREATED)+ - ', LAST_ALTERED AS '+mask(DBO_UPDATED)+ - ', NULL AS '+mask(DBO_CHECKED)+ - ', NULL AS '+mask(DBO_COLLATION)+ - ', NULL AS '+mask(DBO_CHECKSUM)+ - ', NULL AS '+mask(DBO_CROPTIONS)+ - ', ROUTINE_COMMENT AS '+mask(DBO_COMMENT)+ - ' FROM '+mask(DBNAME_INFORMATION_SCHEMA)+'.ROUTINES ' + - 'WHERE ROUTINE_SCHEMA = '+esc(db)); - end; - if Connection.InformationSchemaObjects.IndexOf('TRIGGERS') > -1 then begin - // Stored routines - Unions.Add('SELECT TRIGGER_NAME AS '+mask(DBO_NAME)+ - ', ''TRIGGER'' AS '+mask(DBO_TYPE)+ - ', NULL AS '+mask(DBO_ENGINE)+ - ', NULL AS '+mask(DBO_VERSION)+ - ', NULL AS '+mask(DBO_ROWFORMAT)+ - ', NULL AS '+mask(DBO_ROWS)+ - ', NULL AS '+mask(DBO_AVGROWLEN)+ - ', NULL AS '+mask(DBO_DATALEN)+ - ', NULL AS '+mask(DBO_MAXDATALEN)+ - ', NULL AS '+mask(DBO_INDEXLEN)+ - ', NULL AS '+mask(DBO_DATAFREE)+ - ', NULL AS '+mask(DBO_AUTOINC)+ - ', CREATED AS '+mask(DBO_CREATED)+ - ', NULL AS '+mask(DBO_UPDATED)+ - ', NULL AS '+mask(DBO_CHECKED)+ - ', NULL AS '+mask(DBO_COLLATION)+ - ', NULL AS '+mask(DBO_CHECKSUM)+ - ', NULL AS '+mask(DBO_CROPTIONS)+ - ', NULL AS '+mask(DBO_COMMENT)+ - ' FROM '+mask(DBNAME_INFORMATION_SCHEMA)+'.TRIGGERS ' + - 'WHERE TRIGGER_SCHEMA = '+esc(db)); - end; - case Unions.Count of - 0: ListObjectsSQL := 'SHOW TABLE STATUS FROM ' + mask(db); - 1: ListObjectsSQL := Unions[0] + ' ORDER BY `Name`'; - else ListObjectsSQL := '(' + implodestr(') UNION (', Unions) + ') ORDER BY `Name`'; - end; - FreeAndNil(Unions); - try - Results := Connection.GetResults(ListObjectsSQL); - CachedTableLists.AddObject(db, Results); - // Add table names to SQL highlighter - while not Results.Eof do begin - Tables := Tables + Results.Col(DBO_NAME) + CRLF; - Results.Next; - end; - SynSQLSyn1.TableNames.Text := Trim(Tables); - finally - ShowStatus(STATUS_MSG_READY); - Screen.Cursor := OldCursor; - end; - end; - Result := TMySQLQuery(CachedTableLists.Objects[CachedTableLists.IndexOf(db)]); - Result.First; -end; - - -{*** - Nukes cached table list for active database, then refreshes it. - @return TMySQLQuery The newly cached list of tables for the active database. -} -function TMainForm.RefreshActiveDbTableList: TMySQLQuery; -begin - Result := RefreshDbTableList(ActiveDatabase); -end; - -function TMainForm.RefreshDbTableList(db: WideString): TMySQLQuery; -begin - ClearDbTableList(db); - Result := FetchDbTableList(db); -end; - -procedure TMainForm.ClearDbTableList(db: WideString); -var - idx: Integer; - o: TObject; -begin - idx := CachedTableLists.IndexOf(db); - if idx > -1 then begin - o := CachedTableLists.Objects[idx]; - FreeAndNil(o); - CachedTableLists.Delete(idx); - end; -end; - - -{*** - Nukes the table list cache. -} -procedure TMainForm.ClearAllTableLists; -var - idx: Integer; - Results: TMySQLQuery; -begin - for idx := 0 to CachedTableLists.Count - 1 do begin - Results := TMySQLQuery(CachedTableLists.Objects[idx]); - FreeAndNil(Results); - end; - CachedTableLists.Clear; -end; - - procedure TMainForm.LoadDatabaseProperties(db: WideString); var i, img : Integer; - bytes : Int64; - Results : TMySQLQuery; - Cap, - SelectedCaptions: TWideStringList; + Obj: TDBObject; + Objects: TDBObjectList; + Cap, SelectedCaptions: TWideStringList; begin // DB-Properties Screen.Cursor := crHourGlass; @@ -3706,59 +3528,26 @@ begin // Remember selected nodes SelectedCaptions := GetVTCaptions(ListTables, True); - Results := FetchDbTableList(db); + Objects := Connection.GetDBObjects(db); ShowStatus( 'Displaying tables from "' + db + '" ...' ); ListTables.BeginUpdate; ListTables.Clear; - SetLength(VTRowDataListTables, Results.RecordCount); - i := 0; - while not Results.Eof do begin + SetLength(VTRowDataListTables, Objects.Count); + for i:=0 to Objects.Count-1 do begin + Obj := Objects[i]; VTRowDataListTables[i].Captions := TWideStringList.Create; Cap := VTRowDataListTables[i].Captions; // Object name - Cap.Add(Results.Col(DBO_NAME)); - if Results.Col(DBO_ROWS, True) <> '' then - Cap.Add( FormatNumber(Results.Col(DBO_ROWS) ) ) + Cap.Add(Obj.Name); + if Obj.Rows > -1 then Cap.Add(FormatNumber(Obj.Rows)) else Cap.Add(''); - // Size: Data_length + Index_length - bytes := GetTableSize(Results); - if bytes >= 0 then Cap.Add(FormatByteNumber(bytes)) + if Obj.Size > -1 then Cap.Add(FormatByteNumber(Obj.Size)) else Cap.Add(''); - Cap.Add(Results.Col(DBO_CREATED, True)); - Cap.Add(Results.Col(DBO_UPDATED, True)); - Cap.Add(Results.Col(DBO_ENGINE, True)); - Cap.Add(Results.Col(DBO_COMMENT, True)); - Cap.Add(Results.Col(DBO_VERSION, True)); - Cap.Add(Results.Col(DBO_ROWFORMAT, True)); - if Results.Col(DBO_AVGROWLEN, True) <> '' then - Cap.Add( FormatByteNumber(Results.Col(DBO_AVGROWLEN)) ) - else Cap.Add(''); - if Results.Col(DBO_MAXDATALEN, True) <> '' then - Cap.Add( FormatByteNumber(Results.Col(DBO_MAXDATALEN)) ) - else Cap.Add(''); - if Results.Col(DBO_INDEXLEN, True) <> '' then - Cap.Add( FormatByteNumber(Results.Col(DBO_INDEXLEN, True)) ) - else Cap.Add(''); - if Results.Col(DBO_DATAFREE, True) <> '' then - Cap.Add( FormatByteNumber(Results.Col(DBO_DATAFREE)) ) - else Cap.Add(''); - if Results.Col(DBO_AUTOINC, True) <> '' then - Cap.Add( FormatNumber(Results.Col(DBO_AUTOINC)) ) - else Cap.Add(''); - Cap.Add(Results.Col(DBO_AUTOINC)); - Cap.Add(Results.Col(DBO_COLLATION, True)); - Cap.Add(Results.Col(DBO_CHECKSUM, True)); - Cap.Add(Results.Col(DBO_CROPTIONS)); - if Results.ColExists(DBO_TYPE) then - Cap.Add(Results.Col(DBO_TYPE)) - else - Cap.Add('BASE TABLE'); - - VTRowDataListTables[i].NodeType := GetDBObjectType(Results); + VTRowDataListTables[i].NodeType := Obj.NodeType; // Find icon - case VTRowDataListTables[i].NodeType of + case Obj.NodeType of lntTable: img := ICONINDEX_TABLE; lntCrashedTable: img := ICONINDEX_CRASHED_TABLE; lntView: img := ICONINDEX_VIEW; @@ -3768,9 +3557,32 @@ begin else img := -1; end; VTRowDataListTables[i].ImageIndex := img; - - Results.Next; - Inc(i); + if Obj.Created = 0 then Cap.Add('') + else Cap.Add(DateTimeToStr(Obj.Created)); + if Obj.Updated = 0 then Cap.Add('') + else Cap.Add(DateTimeToStr(Obj.Updated)); + Cap.Add(Obj.Engine); + Cap.Add(Obj.Comment); + if Obj.Version > -1 then Cap.Add(IntToStr(Obj.Version)) + else Cap.Add(''); + Cap.Add(Obj.RowFormat); + if Obj.AvgRowLen > -1 then Cap.Add(FormatByteNumber(Obj.AvgRowLen)) + else Cap.Add(''); + if Obj.MaxDataLen > -1 then Cap.Add(FormatByteNumber(Obj.MaxDataLen)) + else Cap.Add(''); + if Obj.IndexLen > -1 then Cap.Add(FormatByteNumber(Obj.IndexLen)) + else Cap.Add(''); + if Obj.DataFree > -1 then Cap.Add(FormatByteNumber(Obj.DataFree)) + else Cap.Add(''); + if Obj.AutoInc > -1 then Cap.Add(FormatNumber(Obj.AutoInc)) + else Cap.Add(''); + if Obj.LastChecked = 0 then Cap.Add('') + else Cap.Add(DateTimeToStr(Obj.LastChecked)); + Cap.Add(Obj.Collation); + if Obj.Checksum > -1 then Cap.Add(IntToStr(Obj.Checksum)) + else Cap.Add(''); + Cap.Add(Obj.CreateOptions); + Cap.Add(Obj.ObjType); end; ListTables.RootNodeCount := Length(VTRowDataListTables); ListTables.EndUpdate; @@ -4103,6 +3915,7 @@ procedure TMainForm.SynCompletionProposalExecute(Kind: SynCompletionType; var i,j : Integer; Results : TMySQLQuery; + DBObjects : TDBObjectList; sql, TableClauses: WideString; Tables : TStringList; tablename : WideString; @@ -4117,14 +3930,10 @@ var Editor : TCustomSynEdit; Queries : TWideStringList; - procedure addTable(Results: TMySQLQuery); - var ObjName, ObjType: WideString; Icon: Integer; + procedure addTable(Obj: TDBObject); + var Icon: Integer; begin - ObjName := Results.Col(0); - ObjType := ''; - if Results.ColExists(DBO_TYPE) then - ObjType := LowerCase(Results.Col(DBO_TYPE)); - case GetDBObjectType(Results) of + case Obj.NodeType of lntTable: Icon := ICONINDEX_TABLE; lntCrashedTable: Icon := ICONINDEX_CRASHED_TABLE; lntFunction: Icon := ICONINDEX_STOREDFUNCTION; @@ -4133,8 +3942,8 @@ var lntTrigger: Icon := ICONINDEX_TRIGGER; else Icon := -1; end; - Proposal.InsertList.Add( ObjName ); - Proposal.ItemList.Add( WideFormat(SYNCOMPLETION_PATTERN, [Icon, ObjType, ObjName]) ); + Proposal.InsertList.Add(Obj.Name); + Proposal.ItemList.Add( WideFormat(SYNCOMPLETION_PATTERN, [Icon, LowerCase(Obj.ObjType), Obj.Name]) ); end; procedure addColumns( tablename: WideString ); @@ -4275,11 +4084,9 @@ begin if i > -1 then begin // Only display tables from specified db Screen.Cursor := crHourGlass; - Results := FetchDbTableList(Databases[i]); - while not Results.Eof do begin - addTable(Results); - Results.Next; - end; + DBObjects := Connection.GetDBObjects(Databases[i]); + for j:=0 to DBObjects.Count-1 do + addTable(DBObjects[j]); Screen.Cursor := crDefault; end; end; @@ -4293,11 +4100,9 @@ begin if ActiveDatabase <> '' then begin // Display tables from current db - Results := FetchActiveDbTableList; - while not Results.Eof do begin - addTable(Results); - Results.Next; - end; + DBObjects := Connection.GetDBObjects(ActiveDatabase); + for j:=0 to DBObjects.Count-1 do + addTable(DBObjects[j]); if Length(CurrentInput) = 0 then // assume that we have already a dbname in memo Proposal.Position := Databases.Count; end; @@ -4385,7 +4190,7 @@ begin NodeData.Captions[0] := NewText; // Now the active tree db has to be updated. But calling RefreshTreeDB here causes an AV // so we do it manually here - RefreshActiveDbTableList; + Connection.ClearDbObjects(ActiveDatabase); DBTree.InvalidateChildren(FindDBNode(ActiveDatabase), True); except on E:Exception do @@ -4923,15 +4728,14 @@ end; function TMainForm.GetTreeNodeType(Tree: TBaseVirtualTree; Node: PVirtualNode): TListNodeType; var - Results: TMySQLQuery; + DBObjects: TDBObjectList; begin Result := lntNone; if Assigned(Node) then case Tree.GetNodeLevel(Node) of 1: Result := lntDb; 2: begin - Results := FetchDbTableList((Tree as TVirtualStringTree).Text[Node.Parent, 0]); - Results.RecNo := Node.Index; - Result := GetDBObjectType(Results); + DBObjects := Connection.GetDBObjects((Tree as TVirtualStringTree).Text[Node.Parent, 0]); + Result := DBObjects[Node.Index].NodeType; end; end; end; @@ -5953,9 +5757,9 @@ procedure TMainForm.DBtreeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString); var - Results: TMySQLQuery; - db, eng: WideString; - i: Integer; + DBObjects: TDBObjectList; + db: WideString; + i, j: Integer; Bytes: Int64; AllListsCached: Boolean; begin @@ -5964,9 +5768,8 @@ begin 0: CellText := Connection.Username + '@' + Connection.Hostname; 1: CellText := Databases[Node.Index]; 2: begin - Results := FetchDbTableList(Databases[Node.Parent.Index]); - Results.RecNo := Node.Index; - CellText := Results.Col(DBO_NAME); + DBObjects := Connection.GetDBObjects(Databases[Node.Parent.Index]); + CellText := DBObjects[Node.Index].Name; end; end; 1: case GetTreeNodeType(Sender, Node) of @@ -5974,7 +5777,7 @@ begin lntNone: begin AllListsCached := true; for i := 0 to Databases.Count - 1 do begin - if not DbTableListCachedAndValid(Databases[i]) then begin + if not Connection.DbObjectsCached(Databases[i]) then begin AllListsCached := false; break; end; @@ -5984,11 +5787,9 @@ begin if AllListsCached then begin Bytes := 0; for i := 0 to Databases.Count - 1 do begin - Results := FetchDbTableList(Databases[i]); - while not Results.Eof do begin - Bytes := Bytes + GetTableSize(Results); - Results.Next; - end; + DBObjects := Connection.GetDBObjects(Databases[i]); + for j:=0 to DBObjects.Count-1 do + Bytes := Bytes + DBObjects[j].Size; end; end; if Bytes >= 0 then CellText := FormatByteNumber(Bytes) @@ -5997,28 +5798,21 @@ begin // Calculate and display the sum of all table sizes in ONE db, if the list is already cached. lntDb: begin db := (Sender as TVirtualStringTree).Text[Node, 0]; - if not DbTableListCachedAndValid(db) then + if not Connection.DbObjectsCached(db) then CellText := '' else begin Bytes := 0; - Results := FetchDbTableList(db); - while not Results.Eof do begin - if Results.ColExists(DBO_TYPE) then eng := Results.Col(DBO_TYPE) - else eng := Results.Col('Engine'); - if UpperCase(eng) <> 'MRG_MYISAM' then - Bytes := Bytes + GetTableSize(Results); - Results.Next; - end; + DBObjects := Connection.GetDBObjects(db); + for i:=0 to DBObjects.Count-1 do + Bytes := Bytes + DBObjects[i].Size; if Bytes >= 0 then CellText := FormatByteNumber(Bytes) else CellText := ''; end; end; lntTable: begin db := (Sender as TVirtualStringTree).Text[Node.Parent, 0]; - Results := FetchDbTableList(db); - Results.RecNo := Node.Index; - Bytes := GetTableSize(Results); - CellText := FormatByteNumber(Bytes); + DBObjects := Connection.GetDBObjects(db); + CellText := FormatByteNumber(DBObjects[Node.Index].Size); end else CellText := ''; // Applies for views and crashed tables end; @@ -6033,7 +5827,7 @@ procedure TMainForm.DBtreeGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer); var - Results: TMySQLQuery; + DBObjects: TDBObjectList; begin if Column > 0 then Exit; @@ -6043,9 +5837,8 @@ begin ImageIndex := ICONINDEX_DB_HIGHLIGHT else ImageIndex := ICONINDEX_DB; 2: begin - Results := FetchDbTableList(Databases[Node.Parent.Index]); - Results.RecNo := Node.Index; - case GetDBObjectType(Results) of + DBObjects := Connection.GetDBObjects(Databases[Node.Parent.Index]); + case DBObjects[Node.Index].NodeType of lntTable: if Kind = ikSelected then ImageIndex := ICONINDEX_TABLE_HIGHLIGHT @@ -6077,7 +5870,6 @@ procedure TMainForm.DBtreeInitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode; var ChildCount: Cardinal); var VT: TVirtualStringTree; - Results: TMySQLQuery; i, j: Integer; DatabasesWanted: TWideStringList; rx: TRegExpr; @@ -6140,8 +5932,7 @@ begin Screen.Cursor := crHourglass; Showstatus( 'Reading Tables...' ); try - Results := FetchDbTableList(Databases[Node.Index]); - ChildCount := Results.RecordCount; + ChildCount := Connection.GetDBObjects(Databases[Node.Index]).Count; finally ShowStatus( STATUS_MSG_READY ); Screen.Cursor := crDefault; @@ -6286,7 +6077,7 @@ begin // ReInit tree population if DoResetTableCache then begin - ClearAllTableLists; + Connection.ClearDbObjects; FreeAndNil(AllDatabases); end; DBtree.ReinitChildren(DBTree.GetFirst, False); @@ -6313,7 +6104,8 @@ var DBNode, FNode: PVirtualNode; oldSelectedTable: TListNode; TableHereHadFocus: Boolean; - Results: TMySQLQuery; + DBObjects: TDBObjectList; + i: Integer; FocusChangeEvent: procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex) of object; begin oldActiveDatabase := ActiveDatabase; @@ -6325,20 +6117,19 @@ begin FocusChangeEvent := DBtree.OnFocusChanged; DBtree.OnFocusChanged := nil; // Refresh db node - RefreshDbTableList(db); + Connection.ClearDbObjects(db); DBTree.ReinitNode(DBNode, true); DBtree.InvalidateChildren(DBNode, false); // Set focus on previously focused table node if TableHereHadFocus then begin - Results := FetchDbTableList(db); - while not Results.Eof do begin + DBObjects := Connection.GetDBObjects(db); + for i:=0 to DBObjects.Count-1 do begin // Need to check if table was renamed, in which case oldSelectedTable is no longer available - if (Results.Col(DBO_NAME) = oldSelectedTable.Text) - and (GetDBObjectType(Results) = oldSelectedTable.NodeType) then begin + if (DBObjects[i].Name = oldSelectedTable.Text) + and (DBObjects[i].NodeType = oldSelectedTable.NodeType) then begin SelectDBObject(oldSelectedTable.Text, oldSelectedTable.NodeType); break; end; - Results.Next; end; end; // Reactivate focus changing event @@ -6405,11 +6196,6 @@ begin end; -function TMainForm.DbTableListCachedAndValid(db: WideString): Boolean; -begin - Result := CachedTableLists.IndexOf(db) > -1; -end; - procedure TMainForm.editFilterSearchChange(Sender: TObject); var Add, Clause: WideString; diff --git a/source/mysql_connection.pas b/source/mysql_connection.pas index de59ac7f..016492c1 100644 --- a/source/mysql_connection.pas +++ b/source/mysql_connection.pas @@ -1,13 +1,32 @@ unit mysql_connection; -{$M+} // Needed to add published properties - interface uses - Classes, SysUtils, windows, mysql_api, mysql_structures, WideStrings, WideStrUtils, cUnicodeCodecs, SynRegExpr; + Classes, SysUtils, windows, mysql_api, mysql_structures, WideStrings, WideStrUtils, cUnicodeCodecs, SynRegExpr, + Contnrs; type + { TDBObjectList and friends } + + TListNodeType = (lntNone, lntDb, lntTable, lntCrashedTable, lntView, lntFunction, lntProcedure, lntTrigger, lntColumn); + TListNodeTypes = Set of TListNodeType; + TDBObject = class + Name, Database, Engine, Comment, RowFormat, CreateOptions, Collation, ObjType: WideString; + Created, Updated, LastChecked: TDateTime; + Rows, Size, Version, AvgRowLen, MaxDataLen, IndexLen, DataLen, DataFree, AutoInc, CheckSum: Int64; + NodeType: TListNodeType; + end; + TDBObjectList = class(TObjectList) + protected + function getItem(AIndex: Integer): TDBObject; virtual; + procedure setItem(AIndex: Integer; AObject: TDBObject); virtual; + public + function Add(AObject: TDBObject): Integer; virtual; + procedure Insert(AIndex: Integer; AObject: TDBObject); virtual; + function First: TDBObject; virtual; + property Items[Index: Integer]: TDBObject read getItem write setItem; default; + end; { TMySQLConnection } @@ -60,6 +79,8 @@ type const DEFAULT_MYSQLOPTIONS = [opCompress, opLocalFiles, opInteractive, opProtocol41, opMultiStatements]; +{$M+} // Needed to add published properties + type TMySQLQuery = class; TMySQLConnection = class(TComponent) @@ -89,6 +110,7 @@ type FCollationTable: TMySQLQuery; FCharsetTable: TMySQLQuery; FInformationSchemaObjects: TWideStringlist; + FDBObjectLists: TWideStringList; procedure SetActive(Value: Boolean); procedure SetDatabase(Value: WideString); function GetThreadId: Cardinal; @@ -121,6 +143,9 @@ type function GetVar(SQL: WideString; Column: Integer=0): WideString; overload; function GetVar(SQL: WideString; Column: WideString): WideString; overload; function Ping: Boolean; + function GetDBObjects(db: WideString; Refresh: Boolean=False): TDBObjectList; + function DbObjectsCached(db: WideString): Boolean; + procedure ClearDbObjects(db: WideString=''); property ThreadId: Cardinal read GetThreadId; property ConnectionUptime: Integer read GetConnectionUptime; property ServerUptime: Integer read GetServerUptime; @@ -185,7 +210,8 @@ type function DataType(Column: Integer): TDataType; function ColExists(Column: WideString): Boolean; function ColIsPrimaryKeyPart(Column: Integer): Boolean; - function IsNull(Column: Integer): Boolean; + function IsNull(Column: Integer): Boolean; overload; + function IsNull(Column: WideString): Boolean; overload; function HasResult: Boolean; property RecNo: Int64 read FRecNo write SetRecNo; property Eof: Boolean read FEof; @@ -776,10 +802,239 @@ begin FreeAndNil(FCharsetTable); FreeAndNil(FTableEngines); FreeAndNil(FInformationSchemaObjects); + ClearDbObjects; FTableEngineDefault := ''; end; +procedure TMySQLConnection.ClearDbObjects(db: WideString=''); +var + i: Integer; +begin + // Free all cached database object lists, or, if db is passed, only that one + if not Assigned(FDBObjectLists) then + Exit; + if db <> '' then begin + i := FDBObjectLists.IndexOf(db); + if i = -1 then + Exit; + TDBObjectList(FDBObjectLists.Objects[i]).Free; + FDBObjectLists.Delete(i); + end else begin + for i:=0 to FDBObjectLists.Count-1 do + TDBObjectList(FDBObjectLists.Objects[i]).Free; + FreeAndNil(FDBObjectLists); + end; +end; + + +function TMySQLConnection.DbObjectsCached(db: WideString): Boolean; +begin + // Check if a table list is stored in cache + Result := Assigned(FDBObjectLists) and (FDBObjectLists.IndexOf(db) > -1); +end; + + +function TMySQLConnection.GetDbObjects(db: WideString; Refresh: Boolean=False): TDBObjectList; +var + obj: TDBObject; + Results: TMySQLQuery; +begin + // Cache and return a db's table list + if Refresh then + ClearDbObjects(db); + if DbObjectsCached(db) then + Result := FDBObjectLists.Objects[FDBObjectLists.IndexOf(db)] as TDBObjectList + else begin + if not Assigned(FDBObjectLists) then + FDBObjectLists := TWideStringList.Create; + Result := TDBObjectList.Create; + Results := nil; + + // Tables and views + try + Results := GetResults('SHOW TABLE STATUS FROM '+QuoteIdent(db)); + except + end; + if Assigned(Results) then begin + while not Results.Eof do begin + obj := TDBObject.Create; + Result.Add(obj); + obj.Name := Results.Col('Name'); + obj.Database := db; + obj.Rows := StrToInt64Def(Results.Col('Rows'), -1); + if Results.IsNull('Data_length') or Results.IsNull('Index_length') then + Obj.Size := -1 + else + Obj.Size := StrToInt64Def(Results.Col('Data_length'), 0) + StrToInt64Def(Results.Col('Index_length'), 0); + Obj.ObjType := 'BASE TABLE'; + Obj.NodeType := lntTable; + if Results.IsNull(1) and Results.IsNull(2) // Engine column is NULL for views + and (Results.Col('Comment') = 'VIEW') then begin + Obj.NodeType := lntView; + Obj.ObjType := 'VIEW'; + end; + if not Results.IsNull('Create_time') then + Obj.Created := StrToDateTime(Results.Col('Create_time')) + else + Obj.Created := 0; + if not Results.IsNull('Update_time') then + Obj.Updated := StrToDateTime(Results.Col('Update_time')) + else + Obj.Updated := 0; + if Results.ColExists('Type') then + Obj.Engine := Results.Col('Type') + else + Obj.Engine := Results.Col('Engine'); + Obj.Comment := Results.Col('Comment'); + Obj.Version := StrToInt64Def(Results.Col('Version'), -1); + Obj.AutoInc := StrToInt64Def(Results.Col('Auto_increment'), -1); + Obj.RowFormat := Results.Col('Row_format'); + Obj.AvgRowLen := StrToInt64Def(Results.Col('Avg_row_length'), -1); + Obj.MaxDataLen := StrToInt64Def(Results.Col('Max_data_length'), -1); + Obj.IndexLen := StrToInt64Def(Results.Col('Index_length'), -1); + Obj.DataLen := StrToInt64Def(Results.Col('Data_length'), -1); + Obj.DataFree := StrToInt64Def(Results.Col('Data_free'), -1); + if not Results.IsNull('Check_time') then + Obj.LastChecked := StrToDateTime(Results.Col('Check_time')) + else + Obj.LastChecked := 0; + Obj.Collation := Results.Col('Collation'); + Obj.CheckSum := StrToInt64Def(Results.Col('Checksum'), -1); + Obj.CreateOptions := Results.Col('Create_options'); + Results.Next; + end; + FreeAndNil(Results); + end; + + // Stored functions + if ServerVersionInt >= 50000 then try + Results := GetResults('SHOW FUNCTION STATUS WHERE '+QuoteIdent('Db')+'='+EscapeString(db)); + except + end; + if Assigned(Results) then begin + while not Results.Eof do begin + obj := TDBObject.Create; + Result.Add(obj); + obj.Name := Results.Col('Name'); + obj.Database := db; + obj.Rows := -1; + Obj.Size := -1; + Obj.ObjType := 'FUNCTION'; + Obj.NodeType := lntFunction; + if not Results.IsNull('Created') then + Obj.Created := StrToDateTime(Results.Col('Created')) + else + Obj.Created := 0; + if not Results.IsNull('Modified') then + Obj.Updated := StrToDateTime(Results.Col('Modified')) + else + Obj.Updated := 0; + Obj.Engine := ''; + Obj.Comment := Results.Col('Comment'); + Obj.Version := -1; + Obj.AutoInc := -1; + Obj.RowFormat := ''; + Obj.AvgRowLen := -1; + Obj.MaxDataLen := -1; + Obj.IndexLen := -1; + Obj.DataLen := -1; + Obj.DataFree := -1; + Obj.LastChecked := 0; + Obj.Collation := ''; + Obj.CheckSum := -1; + Obj.CreateOptions := ''; + Results.Next; + end; + FreeAndNil(Results); + end; + + // Stored procedures + if ServerVersionInt >= 50000 then try + Results := GetResults('SHOW PROCEDURE STATUS WHERE '+QuoteIdent('Db')+'='+EscapeString(db)); + except + end; + if Assigned(Results) then begin + while not Results.Eof do begin + obj := TDBObject.Create; + Result.Add(obj); + obj.Name := Results.Col('Name'); + obj.Database := db; + obj.Rows := -1; + Obj.Size := -1; + Obj.ObjType := 'PROCEDURE'; + Obj.NodeType := lntProcedure; + if not Results.IsNull('Created') then + Obj.Created := StrToDateTime(Results.Col('Created')) + else + Obj.Created := 0; + if not Results.IsNull('Modified') then + Obj.Updated := StrToDateTime(Results.Col('Modified')) + else + Obj.Updated := 0; + Obj.Engine := ''; + Obj.Comment := Results.Col('Comment'); + Obj.Version := -1; + Obj.AutoInc := -1; + Obj.RowFormat := ''; + Obj.AvgRowLen := -1; + Obj.MaxDataLen := -1; + Obj.IndexLen := -1; + Obj.DataLen := -1; + Obj.DataFree := -1; + Obj.LastChecked := 0; + Obj.Collation := ''; + Obj.CheckSum := -1; + Obj.CreateOptions := ''; + Results.Next; + end; + FreeAndNil(Results); + end; + + // Triggers + if ServerVersionInt >= 50010 then try + Results := GetResults('SHOW TRIGGERS FROM '+QuoteIdent(db)); + except + end; + if Assigned(Results) then begin + while not Results.Eof do begin + obj := TDBObject.Create; + Result.Add(obj); + obj.Name := Results.Col('Trigger'); + obj.Database := db; + obj.Rows := -1; + Obj.Size := -1; + Obj.ObjType := 'TRIGGER'; + Obj.NodeType := lntTrigger; + if not Results.IsNull('Created') then + Obj.Created := StrToDateTime(Results.Col('Created')) + else + Obj.Created := 0; + Obj.Updated := 0; + Obj.Engine := ''; + Obj.Comment := Results.Col('Timing')+' '+Results.Col('Event')+' in table '+QuoteIdent(Results.Col('Table')); + Obj.Version := -1; + Obj.AutoInc := -1; + Obj.RowFormat := ''; + Obj.AvgRowLen := -1; + Obj.MaxDataLen := -1; + Obj.IndexLen := -1; + Obj.DataLen := -1; + Obj.DataFree := -1; + Obj.LastChecked := 0; + Obj.Collation := ''; + Obj.CheckSum := -1; + Obj.CreateOptions := ''; + Results.Next; + end; + FreeAndNil(Results); + end; + + FDBObjectLists.AddObject(db, Result); + end; +end; + + { TMySQLQuery } @@ -951,10 +1206,45 @@ begin end; +function TMySQLQuery.IsNull(Column: WideString): Boolean; +begin + Result := IsNull(FColumnNames.IndexOf(Column)); +end; + + function TMySQLQuery.HasResult: Boolean; begin Result := FLastResult <> nil; end; + +{ TDBObjectList } + +function TDBObjectList.Add(AObject: TDBObject): Integer; +begin + Result := inherited Add(AObject); +end; + +function TDBObjectList.First: TDBObject; +begin + Result := inherited First as TDBObject; +end; + +function TDBObjectList.getItem(AIndex: Integer): TDBObject; +begin + Result := inherited GetItem(AIndex) as TDBObject; +end; + +procedure TDBObjectList.Insert(AIndex: Integer; AObject: TDBObject); +begin + inherited Insert(AIndex, AObject); +end; + +procedure TDBObjectList.setItem(AIndex: Integer; AObject: TDBObject); +begin + inherited setItem(AIndex, AObject); +end; + + end. diff --git a/source/selectdbobject.pas b/source/selectdbobject.pas index 37c8f571..f384a165 100644 --- a/source/selectdbobject.pas +++ b/source/selectdbobject.pas @@ -163,14 +163,13 @@ procedure TfrmSelectDBObject.TreeDBOGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer); var - Results: TMySQLQuery; + DBObjects: TDBObjectList; begin case Sender.GetNodeLevel(Node) of 0: ImageIndex := ICONINDEX_DB; 1: begin - Results := Mainform.FetchDbTableList(Mainform.Databases[Node.Parent.Index]); - Results.RecNo := Node.Index; - case GetDBObjectType(Results) of + DBObjects := Mainform.Connection.GetDBObjects(Mainform.Databases[Node.Parent.Index]); + case DBObjects[Node.Index].NodeType of lntCrashedTable: ImageIndex := ICONINDEX_CRASHED_TABLE; lntTable: ImageIndex := ICONINDEX_TABLE; lntView: ImageIndex := ICONINDEX_VIEW; @@ -191,22 +190,21 @@ end; procedure TfrmSelectDBObject.TreeDBOInitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode; var ChildCount: Cardinal); var - Results: TMySQLQuery; + DBObjects: TDBObjectList; cols: TWideStringList; begin // Fetch sub nodes case Sender.GetNodeLevel(Node) of 0: begin // DB expanding - Results := Mainform.FetchDbTableList(Mainform.Databases[Node.Index]); - ChildCount := Results.RecordCount; - SetLength(FColumns[Node.Index], Results.RecordCount); + DBObjects := Mainform.Connection.GetDBObjects(Mainform.Databases[Node.Index]); + ChildCount := DBObjects.Count; + SetLength(FColumns[Node.Index], DBObjects.Count); end; 1: begin // Table expanding - Results := Mainform.FetchDbTableList(Mainform.Databases[Node.Parent.Index]); - Results.RecNo := Node.Index; + DBObjects := Mainform.Connection.GetDBObjects(Mainform.Databases[Node.Parent.Index]); cols := Mainform.Connection.GetCol('SHOW COLUMNS FROM ' + Mainform.mask(Mainform.Databases[Node.Parent.Index])+'.' - + Mainform.Mask(Results.Col(DBO_NAME))); + + Mainform.Mask(DBObjects[Node.Index].Name)); FColumns[Node.Parent.Index][Node.Index] := cols; ChildCount := cols.Count; end; @@ -219,14 +217,13 @@ procedure TfrmSelectDBObject.TreeDBOGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString); var - Results: TMySQLQuery; + DBObjects: TDBObjectList; begin case Sender.GetNodeLevel(Node) of 0: CellText := Mainform.Databases[Node.Index]; 1: begin - Results := Mainform.FetchDbTableList(Mainform.Databases[Node.Parent.Index]); - Results.RecNo := Node.Index; - CellText := Results.Col(DBO_NAME); + DBObjects := Mainform.Connection.GetDBObjects(Mainform.Databases[Node.Parent.Index]); + CellText := DBObjects[Node.Index].Name end; 2: CellText := FColumns[Node.Parent.Parent.Index][Node.Parent.Index][Node.Index]; end; diff --git a/source/table_editor.pas b/source/table_editor.pas index 381d5266..e5f14a18 100644 --- a/source/table_editor.pas +++ b/source/table_editor.pas @@ -1964,10 +1964,11 @@ var VT: TVirtualStringTree; EnumEditor: TEnumEditorLink; SetEditor: TSetEditorLink; - Results: TMySQLQuery; + DBObjects: TDBObjectList; Key: TForeignKey; ColNode: PVirtualNode; Col: PTableColumn; + i: Integer; begin // Init grid editor in foreign key list VT := Sender as TVirtualStringTree; @@ -1986,11 +1987,9 @@ begin 2: begin EnumEditor := TEnumEditorLink.Create(VT); EnumEditor.AllowCustomText := True; - Results := Mainform.FetchActiveDbTableList; - while not Results.Eof do begin - EnumEditor.ValueList.Add(Results.Col(DBO_NAME)); - Results.Next; - end; + DBObjects := Mainform.Connection.GetDBObjects(Mainform.ActiveDatabase); + for i:=0 to DBObjects.Count-1 do + EnumEditor.ValueList.Add(DBObjects[i].Name); EditLink := EnumEditor; end; 3: begin diff --git a/source/tabletools.pas b/source/tabletools.pas index ef69bcf3..9ed16627 100644 --- a/source/tabletools.pas +++ b/source/tabletools.pas @@ -116,10 +116,10 @@ type procedure AddResults(SQL: WideString); procedure AddNotes(Col1, Col2, Col3, Col4: WideString); procedure UpdateResultGrid; - procedure DoMaintenance(db, obj: WideString; NodeType: TListNodeType); - procedure DoFind(db, obj: WideString; NodeType: TListNodeType; RowsInTable: Int64); - procedure DoExport(db, obj: WideString; NodeType: TListNodeType; RowsInTable, AvgRowLen: Int64); - procedure DoBulkTableEdit(db, obj: WideString; NodeType: TListNodeType); + procedure DoMaintenance(DBObj: TDBObject); + procedure DoFind(DBObj: TDBObject); + procedure DoExport(DBObj: TDBObject); + procedure DoBulkTableEdit(DBObj: TDBObject); public { Public declarations } SelectedTables: TWideStringList; @@ -378,7 +378,7 @@ end; procedure TfrmTableTools.TreeObjectsInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates); var - Results: TMySQLQuery; + DBObjects: TDBObjectList; begin // Attach a checkbox to all nodes Mainform.DBtreeInitNode(Sender, ParentNode, Node, InitialStates); @@ -386,10 +386,9 @@ begin Node.CheckState := csUncheckedNormal; case Sender.GetNodeLevel(Node) of 2: begin - Results := Mainform.FetchDbTableList(Mainform.Databases[ParentNode.Index]); - Results.RecNo := Node.Index; + DBObjects := Mainform.Connection.GetDBObjects(Mainform.Databases[ParentNode.Index]); // No checkbox for stored routines - if not (GetDBObjectType(Results) in [lntTable, lntCrashedTable, lntView]) then + if not (DBObjects[Node.Index].NodeType in [lntTable, lntCrashedTable, lntView]) then Node.CheckType := ctNone end; end; @@ -413,12 +412,9 @@ end; procedure TfrmTableTools.Execute(Sender: TObject); var DBNode, TableNode: PVirtualNode; - Results: TMySQLQuery; - NodeType: TListNodeType; - db, table: WideString; - TableSize, RowsInTable, AvgRowLen: Int64; + DBObjects, Views: TDBObjectList; + DBObj: TDBObject; i: Integer; - ViewNodes: TNodeArray; begin Screen.Cursor := crHourGlass; if tabsTools.ActivePage = tabMaintenance then @@ -431,6 +427,8 @@ begin FToolMode := tmBulkTableEdit; ResultGrid.Clear; FResults.Clear; + Views := TDBObjectList.Create; + Views.OwnsObjects := False; // So we can .Free that object afterwards without loosing the contained objects TreeObjects.SetFocus; DBNode := TreeObjects.GetFirstChild(TreeObjects.GetFirst); while Assigned(DBNode) do begin @@ -438,36 +436,29 @@ begin TableNode := TreeObjects.GetFirstChild(DBNode); while Assigned(TableNode) do begin if (csCheckedNormal in [TableNode.CheckState, DBNode.CheckState]) and (TableNode.CheckType <> ctNone) then begin - Results := Mainform.FetchDbTableList(TreeObjects.Text[DBNode, 0]); - Results.RecNo := TableNode.Index; - NodeType := GetDBObjectType(Results); - db := TreeObjects.Text[DBNode, 0]; - table := TreeObjects.Text[TableNode, 0]; + DBObjects := Mainform.Connection.GetDBObjects(TreeObjects.Text[DBNode, 0]); + DBObj := DBObjects[TableNode.Index]; // Find table in cashed dataset and check its size - perhaps it has to be skipped - TableSize := GetTableSize(Results); - RowsInTable := MakeInt(Results.Col(DBO_ROWS)); - AvgRowLen := MakeInt(Results.Col(DBO_AVGROWLEN)); - if (udSkipLargeTables.Position = 0) or ((TableSize div SIZE_MB) < udSkipLargeTables.Position) then try + if (udSkipLargeTables.Position = 0) or ((DBObj.Size div SIZE_MB) < udSkipLargeTables.Position) then try case FToolMode of - tmMaintenance: DoMaintenance(db, table, NodeType); - tmFind: DoFind(db, table, NodeType, RowsInTable); + tmMaintenance: DoMaintenance(DBObj); + tmFind: DoFind(DBObj); tmSQLExport: begin // Views have to be exported at the very end so at least all needed tables are ready when a view gets imported - if NodeType = lntView then begin - SetLength(ViewNodes, Length(ViewNodes)+1); - ViewNodes[Length(ViewNodes)-1] := TableNode; - end else - DoExport(db, table, NodeType, RowsInTable, AvgRowLen); + if DBObj.NodeType = lntView then + Views.Add(DBObj) + else + DoExport(DBObj); end; - tmBulkTableEdit: DoBulkTableEdit(db, table, NodeType); + tmBulkTableEdit: DoBulkTableEdit(DBObj); end; except // The above SQL can easily throw an exception, e.g. if a table is corrupted. // In such cases we create a dummy row, including the error message on E:Exception do - AddNotes(db, table, 'error', E.Message); + AddNotes(DBObj.Database, DBObj.Name, 'error', E.Message); end else begin - AddNotes(db, table, STRSKIPPED+FormatByteNumber(TableSize), ''); + AddNotes(DBObj.Database, DBObj.Name, STRSKIPPED+FormatByteNumber(DBObj.Size), ''); end; end; TableNode := TreeObjects.GetNextSibling(TableNode); @@ -477,13 +468,11 @@ begin end; // Special block for late created views in export mode - if FToolMode = tmSQLExport then for i:=Low(ViewNodes) to High(ViewNodes) do begin - db := TreeObjects.Text[ViewNodes[i].Parent, 0]; - table := TreeObjects.Text[ViewNodes[i], 0]; + if FToolMode = tmSQLExport then for i:=0 to Views.Count-1 do begin try - DoExport(db, table, lntView, 0, 0); + DoExport(Views[i]); except on E:Exception do - AddNotes(db, table, 'error', E.Message); + AddNotes(Views[i].Database, Views[i].Name, 'error', E.Message); end; end; @@ -497,11 +486,11 @@ begin end; -procedure TfrmTableTools.DoMaintenance(db, obj: WideString; NodeType: TListNodeType); +procedure TfrmTableTools.DoMaintenance(DBObj: TDBObject); var SQL: WideString; begin - SQL := UpperCase(comboOperation.Text) + ' TABLE ' + Mainform.mask(db) + '.' + Mainform.mask(obj); + SQL := UpperCase(comboOperation.Text) + ' TABLE ' + Mainform.mask(DBObj.Database) + '.' + Mainform.mask(DBObj.Name); if chkQuick.Enabled and chkQuick.Checked then SQL := SQL + ' QUICK'; if chkFast.Enabled and chkFast.Checked then SQL := SQL + ' FAST'; if chkMedium.Enabled and chkMedium.Checked then SQL := SQL + ' MEDIUM'; @@ -512,14 +501,14 @@ begin end; -procedure TfrmTableTools.DoFind(db, obj: WideString; NodeType: TListNodeType; RowsInTable: Int64); +procedure TfrmTableTools.DoFind(DBObj: TDBObject); var Results: TMySQLQuery; SQL: WideString; HasSelectedDatatype: Boolean; i: Integer; begin - Results := Mainform.Connection.GetResults('SHOW COLUMNS FROM '+Mainform.mask(db)+'.'+Mainform.mask(obj)); + Results := Mainform.Connection.GetResults('SHOW COLUMNS FROM '+Mainform.mask(DBObj.Database)+'.'+Mainform.mask(DBObj.Name)); SQL := ''; while not Results.Eof do begin HasSelectedDatatype := comboDatatypes.ItemIndex = 0; @@ -535,12 +524,12 @@ begin end; if SQL <> '' then begin Delete(SQL, Length(SQL)-3, 3); - SQL := 'SELECT '''+db+''' AS `Database`, '''+obj+''' AS `Table`, COUNT(*) AS `Found rows`, ' - + 'CONCAT(ROUND(100 / '+IntToStr(Max(RowsInTable,1))+' * COUNT(*), 1), ''%'') AS `Relevance` FROM '+Mainform.mask(db)+'.'+Mainform.mask(obj)+' WHERE ' + SQL := 'SELECT '''+DBObj.Database+''' AS `Database`, '''+DBObj.Name+''' AS `Table`, COUNT(*) AS `Found rows`, ' + + 'CONCAT(ROUND(100 / '+IntToStr(Max(DBObj.Rows,1))+' * COUNT(*), 1), ''%'') AS `Relevance` FROM '+Mainform.mask(DBObj.Database)+'.'+Mainform.mask(DBObj.Name)+' WHERE ' + SQL; AddResults(SQL); end else - AddNotes(db, obj, STRSKIPPED+'table doesn''t have columns of selected type ('+comboDatatypes.Text+').', ''); + AddNotes(DBObj.Database, DBObj.Name, STRSKIPPED+'table doesn''t have columns of selected type ('+comboDatatypes.Text+').', ''); end; @@ -846,10 +835,10 @@ begin end; -procedure TfrmTableTools.DoExport(db, obj: WideString; NodeType: TListNodeType; RowsInTable, AvgRowLen: Int64); +procedure TfrmTableTools.DoExport(DBObj: TDBObject); var ToFile, ToDir, ToDb, ToServer, IsLastRowInChunk, NeedsDBStructure: Boolean; - Struc, Header, FinalDbName, BaseInsert, Row, TargetDbAndObject, objtype: WideString; + Struc, Header, FinalDbName, BaseInsert, Row, TargetDbAndObject: WideString; LogRow: TWideStringlist; i: Integer; RowCount, MaxRowsInChunk, RowsInChunk, Limit, Offset, ResultCount: Int64; @@ -863,7 +852,7 @@ var Result := Mainform.mask(s); end; - // Pass output to file or query, and append semicolon if needed + // Pass output to file or query, and append semicolon if needed procedure Output(SQL: WideString; IsEndOfQuery, ForFile, ForDir, ForDb, ForServer: Boolean); var SA: AnsiString; @@ -892,36 +881,31 @@ var end; begin // Handle one table, view or routine in SQL export mode - AddResults('SELECT '+esc(db)+' AS '+Mainform.mask('Database')+', ' + - esc(obj)+' AS '+Mainform.mask('Table')+', ' + - IntToStr(RowsInTable)+' AS '+Mainform.mask('Rows')+', '+ + AddResults('SELECT '+esc(DBObj.Database)+' AS '+Mainform.mask('Database')+', ' + + esc(DBObj.Name)+' AS '+Mainform.mask('Table')+', ' + + IntToStr(DBObj.Rows)+' AS '+Mainform.mask('Rows')+', '+ '0 AS '+Mainform.mask('Duration') ); ToFile := comboExportOutputType.Text = OUTPUT_FILE; ToDir := comboExportOutputType.Text = OUTPUT_DIR; ToDb := comboExportOutputType.Text = OUTPUT_DB; ToServer := Copy(comboExportOutputType.Text, 1, Length(OUTPUT_SERVER)) = OUTPUT_SERVER; - case NodeType of - lntTable, lntCrashedTable: objtype := 'table'; - lntView: objtype := 'view'; - else 'unknown object type'; - end; StartTime := GetTickCount; try if ToDir then begin FreeAndNil(ExportStream); if not DirectoryExists(comboExportOutputTarget.Text) then ForceDirectories(comboExportOutputTarget.Text); - ExportStream := TFileStream.Create(comboExportOutputTarget.Text+'\'+GoodFileName(obj)+'.sql', fmCreate or fmOpenWrite); + ExportStream := TFileStream.Create(comboExportOutputTarget.Text+'\'+GoodFileName(DBObj.Name)+'.sql', fmCreate or fmOpenWrite); end; if ToFile and (not Assigned(ExportStream)) then ExportStream := TFileStream.Create(comboExportOutputTarget.Text, fmCreate or fmOpenWrite); if ToDb or ToServer then ExportStream := TMemoryStream.Create; - if (db<>ExportLastDatabase) or ToDir then begin + if (DBObj.Database<>ExportLastDatabase) or ToDir then begin Header := '# --------------------------------------------------------' + CRLF + WideFormat('# %-30s%s', ['Host:', Mainform.Connection.HostName]) + CRLF + - WideFormat('# %-30s%s', ['Database:', db]) + CRLF + + WideFormat('# %-30s%s', ['Database:', DBObj.Database]) + CRLF + WideFormat('# %-30s%s', ['Server version:', Mainform.Connection.ServerVersionUntouched]) + CRLF + WideFormat('# %-30s%s', ['Server OS:', Mainform.Connection.GetVar('SHOW VARIABLES LIKE ' + esc('version_compile_os'), 1)]) + CRLF + WideFormat('# %-30s%s', [APPNAME + ' version:', FullAppVersion]) + CRLF + @@ -930,7 +914,7 @@ begin '/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;' + CRLF + '/*!40101 SET NAMES '+Mainform.Connection.CharacterSet+' */;' + CRLF + '/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;' + CRLF; - Output(Header, False, db<>ExportLastDatabase, True, False, False); + Output(Header, False, DBObj.Database<>ExportLastDatabase, True, False, False); end; except on E:Exception do begin @@ -940,21 +924,21 @@ begin end; // Database structure. Do that only in single-file and server mode. drop/create/use in directory or database mode makes no sense - FinalDbName := db; + FinalDbName := DBObj.Database; if ToDb or (ToServer and (comboExportOutputTarget.ItemIndex > 0)) then FinalDbName := comboExportOutputTarget.Text; NeedsDBStructure := FinalDbName <> ExportLastDatabase; if chkExportDatabasesDrop.Checked or chkExportDatabasesCreate.Checked then begin - Output(CRLF+'# Dumping database structure for '+db+CRLF, False, NeedsDBStructure, False, False, False); + Output(CRLF+'# Dumping database structure for '+DBObj.Database+CRLF, False, NeedsDBStructure, False, False, False); if chkExportDatabasesDrop.Checked and chkExportDatabasesDrop.Enabled then Output('DROP DATABASE IF EXISTS '+m(FinalDbName), True, NeedsDBStructure, False, False, NeedsDBStructure); if chkExportDatabasesCreate.Checked and chkExportDatabasesCreate.Enabled then begin if Mainform.Connection.ServerVersionInt >= 40100 then begin - Struc := Mainform.Connection.GetVar('SHOW CREATE DATABASE '+m(db), 1); + Struc := Mainform.Connection.GetVar('SHOW CREATE DATABASE '+m(DBObj.Database), 1); // Gracefully ignore it when target database exists, important in server mode Insert('IF NOT EXISTS ', Struc, Pos('DATABASE', Struc) + 9); // Create the right dbname - Struc := WideStringReplace(Struc, db, FinalDbName, []); + Struc := WideStringReplace(Struc, DBObj.Database, FinalDbName, []); end else Struc := 'CREATE DATABASE IF NOT EXISTS '+m(FinalDbName); Output(Struc, True, NeedsDBStructure, False, False, NeedsDBStructure); @@ -968,17 +952,17 @@ begin // Table structure if chkExportTablesDrop.Checked or chkExportTablesCreate.Checked then begin - Output(CRLF+CRLF+'# Dumping structure for '+objtype+' '+db+'.'+obj+CRLF, False, True, True, False, False); + Output(CRLF+CRLF+'# Dumping structure for '+LowerCase(DBObj.ObjType)+' '+DBObj.Database+'.'+DBObj.Name+CRLF, False, True, True, False, False); if chkExportTablesDrop.Checked then begin Struc := 'DROP TABLE IF EXISTS '; if ToDb then Struc := Struc + m(FinalDbName)+'.'; - Struc := Struc + m(obj); + Struc := Struc + m(DBObj.Name); Output(Struc, True, True, True, True, True); end; if chkExportTablesCreate.Checked then begin try - Struc := Mainform.Connection.GetVar('SHOW CREATE TABLE '+m(db)+'.'+m(obj), 1); + Struc := Mainform.Connection.GetVar('SHOW CREATE TABLE '+m(DBObj.Database)+'.'+m(DBObj.Name), 1); Struc := fixNewlines(Struc); // Remove AUTO_INCREMENT clause rx := TRegExpr.Create; @@ -986,12 +970,12 @@ begin rx.Expression := '\sAUTO_INCREMENT\s*\=\s*\d+\s'; Struc := rx.Replace(Struc, ' '); rx.Free; - if NodeType = lntTable then + if DBObj.NodeType = lntTable then Insert('IF NOT EXISTS ', Struc, Pos('TABLE', Struc) + 6); if ToDb then begin - if NodeType = lntTable then + if DBObj.NodeType = lntTable then Insert(m(FinalDbName)+'.', Struc, Pos('EXISTS', Struc) + 7 ) - else if NodeType = lntView then + else if DBObj.NodeType = lntView then Insert(m(FinalDbName)+'.', Struc, Pos('VIEW', Struc) + 5 ); end; Output(Struc, True, True, True, True, True); @@ -1005,24 +989,24 @@ begin end; end; - case NodeType of + case DBObj.NodeType of lntTable, lntCrashedTable: begin // Table data if comboExportData.Text = DATA_NO then begin Output(CRLF+'# Data exporting was unselected.'+CRLF, False, True, True, False, False); - end else if RowsInTable = 0 then begin - Output(CRLF+'# No rows in table '+db+'.'+obj+CRLF, False, True, True, False, False); + end else if DBObj.Rows = 0 then begin + Output(CRLF+'# No rows in table '+DBObj.Database+'.'+DBObj.Name+CRLF, False, True, True, False, False); end else begin - Output(CRLF+'# Dumping data for table '+db+'.'+obj+': '+FormatNumber(RowsInTable)+' rows'+CRLF, False, True, True, False, False); - TargetDbAndObject := m(obj); + Output(CRLF+'# Dumping data for table '+DBObj.Database+'.'+DBObj.Name+': '+FormatNumber(DBObj.Rows)+' rows'+CRLF, False, True, True, False, False); + TargetDbAndObject := m(DBObj.Name); if ToDb then TargetDbAndObject := m(FinalDbName) + '.' + TargetDbAndObject; Offset := 0; RowCount := 0; // Calculate limit so we select ~100MB per loop - Limit := Round(100 * SIZE_MB / Max(AvgRowLen,1)); + Limit := Round(100 * SIZE_MB / Max(DBObj.AvgRowLen,1)); // Calculate max rows per INSERT, so we always get ~800KB - MaxRowsInChunk := Round(SIZE_MB * 0.6 / Max(AvgRowLen,1)); + MaxRowsInChunk := Round(SIZE_MB * 0.6 / Max(DBObj.AvgRowLen,1)); if comboExportData.Text = DATA_REPLACE then Output('DELETE FROM '+TargetDbAndObject, True, True, True, True, True); Output('/*!40000 ALTER TABLE '+TargetDbAndObject+' DISABLE KEYS */', True, True, True, True, True); @@ -1033,7 +1017,7 @@ begin BaseInsert := 'REPLACE INTO '; BaseInsert := BaseInsert + TargetDbAndObject + ' ('; while true do begin - Data := Mainform.Connection.GetResults('SELECT * FROM '+m(db)+'.'+m(obj)+' LIMIT '+IntToStr(Offset)+', '+IntToStr(Limit)); + Data := Mainform.Connection.GetResults('SELECT * FROM '+m(DBObj.Database)+'.'+m(DBObj.Name)+' LIMIT '+IntToStr(Offset)+', '+IntToStr(Limit)); Inc(Offset, Limit); if Data.RecordCount = 0 then break; @@ -1071,7 +1055,7 @@ begin end; Output('', True, True, True, True, True); LogRow := TWideStringList(FResults.Last); - LogRow[2] := FormatNumber(RowCount) + ' / ' + FormatNumber(100/Max(RowsInTable,1)*RowCount, 0)+'%'; + LogRow[2] := FormatNumber(RowCount) + ' / ' + FormatNumber(100/Max(DBObj.Rows,1)*RowCount, 0)+'%'; LogRow[3] := FormatTimeNumber((GetTickCount-StartTime) DIV 1000); UpdateResultGrid; if Data.Eof then @@ -1112,13 +1096,13 @@ begin end; -procedure TfrmTableTools.DoBulkTableEdit(db, obj: WideString; NodeType: TListNodeType); +procedure TfrmTableTools.DoBulkTableEdit(DBObj: TDBObject); var Specs, LogRow: TWideStringList; begin Specs := TWideStringlist.Create; - if chkBulkTableEditDatabase.Checked and (comboBulkTableEditDatabase.Text <> db) then - Specs.Add('RENAME ' + Mainform.mask(comboBulkTableEditDatabase.Text)+'.'+Mainform.mask(obj)); + if chkBulkTableEditDatabase.Checked and (comboBulkTableEditDatabase.Text <> DBObj.Database) then + Specs.Add('RENAME ' + Mainform.mask(comboBulkTableEditDatabase.Text)+'.'+Mainform.mask(DBObj.Name)); if chkBulkTableEditEngine.Checked then begin if Mainform.Connection.ServerVersionInt < 40018 then Specs.Add('TYPE '+comboBulkTableEditEngine.Text) @@ -1133,12 +1117,12 @@ begin end; if chkBulkTableEditResetAutoinc.Checked then Specs.Add('AUTO_INCREMENT=0'); - AddResults('SELECT '+esc(db)+' AS '+Mainform.mask('Database')+', ' + - esc(obj)+' AS '+Mainform.mask('Table')+', ' + + AddResults('SELECT '+esc(DBObj.Database)+' AS '+Mainform.mask('Database')+', ' + + esc(DBObj.Name)+' AS '+Mainform.mask('Table')+', ' + esc('Updating...')+' AS '+Mainform.mask('Operation')+', '+ ''''' AS '+Mainform.mask('Result') ); - Mainform.Connection.Query('ALTER TABLE ' + Mainform.mask(db) + '.' + Mainform.mask(obj) + ' ' + ImplodeStr(', ', Specs)); + Mainform.Connection.Query('ALTER TABLE ' + Mainform.mask(DBObj.Database) + '.' + Mainform.mask(DBObj.Name) + ' ' + ImplodeStr(', ', Specs)); LogRow := TWideStringList(FResults.Last); LogRow[2] := 'Done'; LogRow[3] := 'Success'; diff --git a/source/trigger_editor.pas b/source/trigger_editor.pas index abbcc2ce..55dac94c 100644 --- a/source/trigger_editor.pas +++ b/source/trigger_editor.pas @@ -74,19 +74,20 @@ end; procedure TfrmTriggerEditor.Init(ObjectName: WideString=''; ObjectType: TListNodeType=lntNone); var - Definition, TableList: TMySQLQuery; + Definition: TMySQLQuery; + DBObjects: TDBObjectList; + i: Integer; begin inherited; editName.Text := ''; SynMemoStatement.Text := ''; comboEvent.ItemIndex := 0; comboTiming.ItemIndex := 0; - TableList := Mainform.FetchActiveDbTableList; + DBObjects := Mainform.Connection.GetDBObjects(Mainform.ActiveDatabase); comboTable.Items.Clear; - while not TableList.Eof do begin - if GetDBObjectType(TableList) in [lntTable, lntCrashedTable] then - comboTable.Items.Add(TableList.Col(DBO_NAME)); - TableList.Next; + for i:=0 to DBObjects.Count-1 do begin + if DBObjects[i].NodeType in [lntTable, lntCrashedTable] then + comboTable.Items.Add(DBObjects[i].Name); end; if comboTable.Items.Count > 0 then comboTable.ItemIndex := 0;