diff --git a/source/copytable.pas b/source/copytable.pas index c3b38282..aec2e1d8 100644 --- a/source/copytable.pas +++ b/source/copytable.pas @@ -250,7 +250,7 @@ begin nColumns: CellText := _('Columns'); nKeys: CellText := _('Indexes'); nForeignKeys: CellText := _('Foreign keys'); - nData: CellText := f_('Data (%s rows)', [FormatNumber(FDBObj.RowCount)]); + nData: CellText := f_('Data (%s rows)', [FormatNumber(FDBObj.RowCount(False))]); else raise Exception.Create(_(SUnhandledNodeIndex)); end; if Node.Index <> nData then begin @@ -310,7 +310,7 @@ begin end; if ChildCount > 0 then Include(InitialStates, ivsHasChildren); - if (ChildCount = 0) or ((Node.Index = nData) and (FDBObj.RowCount <= 0)) then + if (ChildCount = 0) or ((Node.Index = nData) and (FDBObj.RowCount(False) = 0)) then Node.States := Node.States + [vsDisabled] else if AppSettings.ReadBool(Option) then Node.CheckState := csCheckedNormal; diff --git a/source/dbconnection.pas b/source/dbconnection.pas index 0f685fd9..4b56ce78 100644 --- a/source/dbconnection.pas +++ b/source/dbconnection.pas @@ -144,7 +144,6 @@ type FCreateCodeLoaded: Boolean; FWasSelected: Boolean; FConnection: TDBConnection; - FRowCount: Int64; function GetObjType: String; function GetImageIndex: Integer; function GetOverlayImageIndex: Integer; @@ -173,7 +172,7 @@ type function QuotedDbAndTableName(AlwaysQuote: Boolean=True): String; function QuotedColumn(AlwaysQuote: Boolean=True): String; function SchemaClauseIS(Prefix: String): String; - function RowCount(Reload: Boolean=False): Int64; + function RowCount(Reload: Boolean): Int64; function GetCreateCode: String; overload; function GetCreateCode(RemoveAutoInc, RemoveDefiner: Boolean): String; overload; property ObjType: String read GetObjType; @@ -469,7 +468,7 @@ type function GetCurrentUserHostCombination: String; function GetAllUserHostCombinations: TStringList; function DecodeAPIString(a: AnsiString): String; - function GetRowCount(Obj: TDBObject): Int64; + function GetRowCount(Obj: TDBObject): Int64; virtual; abstract; procedure ClearCache(IncludeDBObjects: Boolean); procedure FetchDbObjects(db: String; var Cache: TDBObjectList); virtual; abstract; procedure SetLockedByThread(Value: TThread); virtual; @@ -605,6 +604,7 @@ type function GetCollationTable: TDBQuery; override; function GetCharsetTable: TDBQuery; override; function GetCreateViewCode(Database, Name: String): String; + function GetRowCount(Obj: TDBObject): Int64; override; procedure FetchDbObjects(db: String; var Cache: TDBObjectList); override; procedure SetLockedByThread(Value: TThread); override; public @@ -636,6 +636,7 @@ type function GetAllDatabases: TStringList; override; function GetCollationTable: TDBQuery; override; function GetCharsetTable: TDBQuery; override; + function GetRowCount(Obj: TDBObject): Int64; override; procedure FetchDbObjects(db: String; var Cache: TDBObjectList); override; public constructor Create(AOwner: TComponent); override; @@ -675,6 +676,7 @@ type procedure Query(SQL: String; DoStoreResult: Boolean=False; LogCategory: TDBLogCategory=lcSQL); override; function Ping(Reconnect: Boolean): Boolean; override; function ConnectionInfo: TStringList; override; + function GetRowCount(Obj: TDBObject): Int64; override; property LastRawResults: TPGRawResults read FLastRawResults; property RegClasses: TOidStringPairs read FRegClasses; function GetTableColumns(Table: TDBObject): TTableColumnList; override; @@ -713,6 +715,7 @@ type procedure Query(SQL: String; DoStoreResult: Boolean=False; LogCategory: TDBLogCategory=lcSQL); override; function Ping(Reconnect: Boolean): Boolean; override; function GetCreateCode(Obj: TDBObject): String; override; + function GetRowCount(Obj: TDBObject): Int64; override; property LastRawResults: TSQLiteRawResults read FLastRawResults; function GetTableColumns(Table: TDBObject): TTableColumnList; override; function GetTableKeys(Table: TDBObject): TTableKeyList; override; @@ -5716,19 +5719,63 @@ begin end; -function TDBConnection.GetRowCount(Obj: TDBObject): Int64; +function TMySQLConnection.GetRowCount(Obj: TDBObject): Int64; +var + Rows: String; +begin + // Get row number from a mysql table + if Parameters.IsProxySQLAdmin then + Rows := GetVar('SELECT COUNT(*) FROM '+QuoteIdent(Obj.Database)+'.'+QuoteIdent(Obj.Name), 0) + else + Rows := GetVar('SHOW TABLE STATUS LIKE '+EscapeString(Obj.Name), 'Rows'); + Result := MakeInt(Rows); +end; + + +function TAdoDBConnection.GetRowCount(Obj: TDBObject): Int64; +var + Rows: String; +begin + // Get row number from a mssql table + if ServerVersionInt >= 900 then begin + Rows := GetVar('SELECT SUM('+QuoteIdent('rows')+') FROM '+QuoteIdent('sys')+'.'+QuoteIdent('partitions')+ + ' WHERE '+QuoteIdent('index_id')+' IN (0, 1)'+ + ' AND '+QuoteIdent('object_id')+' = object_id('+EscapeString(Obj.Database+'.'+Obj.Schema+'.'+Obj.Name)+')' + ); + end else begin + Rows := GetVar('SELECT COUNT(*) FROM '+Obj.QuotedDbAndTableName); + end; + Result := MakeInt(Rows); +end; + + +function TPgConnection.GetRowCount(Obj: TDBObject): Int64; +var + Rows: String; +begin + // Get row number from a postgres table + Rows := GetVar('SELECT '+QuoteIdent('reltuples')+'::bigint FROM '+QuoteIdent('pg_class')+ + ' LEFT JOIN '+QuoteIdent('pg_namespace')+ + ' ON ('+QuoteIdent('pg_namespace')+'.'+QuoteIdent('oid')+' = '+QuoteIdent('pg_class')+'.'+QuoteIdent('relnamespace')+')'+ + ' WHERE '+QuoteIdent('pg_class')+'.'+QuoteIdent('relkind')+'='+EscapeString('r')+ + ' AND '+QuoteIdent('pg_namespace')+'.'+QuoteIdent('nspname')+'='+EscapeString(Obj.Database)+ + ' AND '+QuoteIdent('pg_class')+'.'+QuoteIdent('relname')+'='+EscapeString(Obj.Name) + ); + Result := MakeInt(Rows); +end; + + +function TSQLiteConnection.GetRowCount(Obj: TDBObject): Int64; var Rows: String; begin // Get row number from a table - if Obj.NodeType in [lntView, lntTable] then - Rows := GetVar('SELECT COUNT(*) FROM ' + Obj.QuotedDbAndTableName, 0) - else - Rows := ''; + Rows := GetVar('SELECT COUNT(*) FROM '+QuoteIdent(Obj.Database)+'.'+QuoteIdent(Obj.Name), 0); Result := MakeInt(Rows); end; + procedure TDBConnection.Drop(Obj: TDBObject); begin Query('DROP '+UpperCase(Obj.ObjType)+' '+Obj.QuotedName); @@ -8757,7 +8804,6 @@ begin Database := ''; Schema := ''; Rows := -1; - FRowCount := -1; Size := -1; Created := 0; Updated := 0; @@ -8799,7 +8845,6 @@ begin Updated := s.Updated; Comment := s.Comment; Rows := s.Rows; - FRowCount := s.FRowCount; Size := s.Size; ArgTypes := s.ArgTypes; FCreateCode := s.FCreateCode; @@ -9030,13 +9075,12 @@ begin Result := Connection.GetSQLSpecifity(spISSchemaCol, [Prefix]) + '=' + Connection.EscapeString(Database); end; -function TDBObject.RowCount(Reload: Boolean=False): Int64; +function TDBObject.RowCount(Reload: Boolean): Int64; begin - if (FRowCount = -1) or Reload then begin - if NodeType in [lntTable, lntView] then - FRowCount := Connection.GetRowCount(Self); + if (Rows = -1) or Reload then begin + Rows := Connection.GetRowCount(Self); end; - Result := FRowCount; + Result := Rows; end; procedure TDBObject.Drop; diff --git a/source/tabletools.pas b/source/tabletools.pas index 9f5bef93..02270b55 100644 --- a/source/tabletools.pas +++ b/source/tabletools.pas @@ -994,15 +994,15 @@ begin case DBObj.Connection.Parameters.NetTypeGroup of ngMySQL, ngPgSQL: SQL := 'SELECT '''+DBObj.Database+''' AS '+DBObj.Connection.QuoteIdent('Database')+', '''+DBObj.Name+''' AS '+DBObj.Connection.QuoteIdent('Table')+', COUNT(*) AS '+DBObj.Connection.QuoteIdent('Found rows')+', ' - + 'CONCAT(ROUND(100 / '+IntToStr(Max(DBObj.RowCount,1))+' * COUNT(*), 1), ''%'') AS '+DBObj.Connection.QuoteIdent('Relevance')+' FROM '+DBObj.QuotedDatabase+'.'+DBObj.QuotedName+' WHERE ' + + 'CONCAT(ROUND(100 / '+IntToStr(Max(DBObj.Rows,1))+' * COUNT(*), 1), ''%'') AS '+DBObj.Connection.QuoteIdent('Relevance')+' FROM '+DBObj.QuotedDatabase+'.'+DBObj.QuotedName+' WHERE ' + SQL; ngMSSQL: SQL := 'SELECT '''+DBObj.Database+''' AS '+DBObj.Connection.QuoteIdent('Database')+', '''+DBObj.Name+''' AS '+DBObj.Connection.QuoteIdent('Table')+', COUNT(*) AS '+DBObj.Connection.QuoteIdent('Found rows')+', ' - + 'CONVERT(VARCHAR(10), ROUND(100 / '+IntToStr(Max(DBObj.RowCount,1))+' * COUNT(*), 1)) + ''%'' AS '+DBObj.Connection.QuoteIdent('Relevance')+' FROM '+DBObj.QuotedDatabase+'.'+DBObj.QuotedName+' WHERE ' + + 'CONVERT(VARCHAR(10), ROUND(100 / '+IntToStr(Max(DBObj.Rows,1))+' * COUNT(*), 1)) + ''%'' AS '+DBObj.Connection.QuoteIdent('Relevance')+' FROM '+DBObj.QuotedDatabase+'.'+DBObj.QuotedName+' WHERE ' + SQL; ngSQLite: SQL := 'SELECT '''+DBObj.Database+''' AS '+DBObj.Connection.QuoteIdent('Database')+', '''+DBObj.Name+''' AS '+DBObj.Connection.QuoteIdent('Table')+', COUNT(*) AS '+DBObj.Connection.QuoteIdent('Found rows')+', ' - + '(ROUND(100 / '+IntToStr(Max(DBObj.RowCount,1))+' * COUNT(*), 1) || ''%'') AS '+DBObj.Connection.QuoteIdent('Relevance')+' FROM '+DBObj.QuotedDatabase+'.'+DBObj.QuotedName+' WHERE ' + + '(ROUND(100 / '+IntToStr(Max(DBObj.Rows,1))+' * COUNT(*), 1) || ''%'') AS '+DBObj.Connection.QuoteIdent('Relevance')+' FROM '+DBObj.QuotedDatabase+'.'+DBObj.QuotedName+' WHERE ' + SQL; end; AddResults(SQL, DBObj.Connection); @@ -1474,9 +1474,9 @@ const BytesDone: Int64; begin LogRow := FResults.Last; - Percent := 100 / Max(DBObj.RowCount,1) * Max(RowsDone,1); + Percent := 100 / Max(DBObj.Rows,1) * Max(RowsDone,1); Percent := Min(Percent, 100); - BytesDone := Max(DBObj.Size,0) div Max(DBObj.RowCount,1) * RowsDone; + BytesDone := Max(DBObj.Size,0) div Max(DBObj.Rows,1) * RowsDone; FObjectSizesDoneExact := FObjectSizesDone + BytesDone; LogRow[2] := FormatNumber(RowsDone) + ' / ' + FormatNumber(Percent, 0)+'%'; LogRow[3] := FormatTimeNumber((GetTickCount-StartTime) / 1000, True); @@ -1487,7 +1487,7 @@ begin // Handle one table, view or whatever in SQL export mode AddResults('SELECT '+DBObj.Connection.EscapeString(DBObj.Database)+' AS '+DBObj.Connection.QuoteIdent('Database')+', ' + DBObj.Connection.EscapeString(DBObj.Name)+' AS '+DBObj.Connection.QuoteIdent('Table')+', ' + - IntToStr(DBObj.RowCount)+' AS '+DBObj.Connection.QuoteIdent('Rows')+', '+ + IntToStr(DBObj.Rows)+' AS '+DBObj.Connection.QuoteIdent('Rows')+', '+ '0 AS '+DBObj.Connection.QuoteIdent('Duration') , DBObj.Connection ); @@ -1712,7 +1712,9 @@ begin if menuExportAddComments.Checked then Output('-- '+f_('Table data not exported because this is %s table which holds its data in separate tables.', [DBObj.Engine])+CRLF+CRLF, False, True, True, False, False); end else begin - tmp := FormatNumber(DBObj.RowCount)+' rows'; + tmp := FormatNumber(DBObj.Rows)+' rows'; + if LowerCase(DBObj.Engine) = 'innodb' then + tmp := '~'+tmp+' ('+_('approximately')+')'; if menuExportAddComments.Checked then Output('-- '+f_('Dumping data for table %s.%s: %s', [DBObj.Database, DBObj.Name, tmp])+CRLF, False, True, True, False, False); TargetDbAndObject := Quoter.QuoteIdent(DBObj.Name);