mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2025-08-06 18:24:26 +08:00

+ Delete [Del] (Deletes a snippetfile) + Help [F1] (Calls SQL help for selected keyword or function)
5858 lines
180 KiB
ObjectPascal
5858 lines
180 KiB
ObjectPascal
unit Childwin;
|
||
|
||
|
||
// -------------------------------------
|
||
// MDI-Child-Window
|
||
// -------------------------------------
|
||
|
||
|
||
interface
|
||
|
||
|
||
uses
|
||
Synchronization,
|
||
Windows, Classes, Graphics, Forms, Controls, StdCtrls,
|
||
ExtCtrls, ComCtrls, ImgList, SysUtils, Dialogs, Menus,
|
||
SynEdit, SynMemo, SynEditHighlighter, SynHighlighterSQL, SynEditSearch,
|
||
SynEditTypes, Registry, Spin, Clipbrd, Shellapi,
|
||
Buttons, CheckLst, ToolWin, Db, DBGrids,
|
||
DBCtrls, helpers,
|
||
Grids, messages, Mask, ZDataset,
|
||
ZAbstractRODataset, ZConnection,
|
||
ZSqlMonitor, ZPlainMySqlDriver, EDBImage, ZAbstractDataset, ZDbcLogging,
|
||
SynCompletionProposal, HeidiComp, SynEditMiscClasses, MysqlQuery,
|
||
MysqlQueryThread, queryprogress, communication, MysqlConn, smdbgrid, Tabs;
|
||
|
||
|
||
type
|
||
TMDIChild = class(TForm)
|
||
Panel1: TPanel;
|
||
DBtree: TTreeView;
|
||
Splitter1: TSplitter;
|
||
TableShow: TPanel;
|
||
PageControlMain: TPageControl;
|
||
tabData: TTabSheet;
|
||
tabDatabase: TTabSheet;
|
||
Splitter2: TSplitter;
|
||
tabQuery: TTabSheet;
|
||
popupTreeView: TPopupMenu;
|
||
Drop1: TMenuItem;
|
||
pnlDatabaseTop: TPanel;
|
||
tabTable: TTabSheet;
|
||
pnlTableTop: TPanel;
|
||
popupDbGrid: TPopupMenu;
|
||
menuviewdata: TMenuItem;
|
||
menuproperties: TMenuItem;
|
||
menuinsert: TMenuItem;
|
||
menudroptable: TMenuItem;
|
||
menuemptytable: TMenuItem;
|
||
tabHost: TTabSheet;
|
||
PageControlHost: TPageControl;
|
||
tabVariables: TTabSheet;
|
||
tabProcessList: TTabSheet;
|
||
ListVariables: TSortListView;
|
||
ListProcesses: TSortListView;
|
||
popupHost: TPopupMenu;
|
||
Kill1: TMenuItem;
|
||
NewDatabase1: TMenuItem;
|
||
ListTables: TSortListView;
|
||
Refresh1: TMenuItem;
|
||
Panel4: TPanel;
|
||
pnlDataTop: TPanel;
|
||
pnlQueryTop: TPanel;
|
||
tlbDataLeft1: TToolBar;
|
||
btnDbViewData: TToolButton;
|
||
btnDbProperties: TToolButton;
|
||
btnDbInsertRecord: TToolButton;
|
||
btnDbEmptyTable: TToolButton;
|
||
tlbTableLeft1: TToolBar;
|
||
menurefresh: TMenuItem;
|
||
N2: TMenuItem;
|
||
pnlQueryMemo: TPanel;
|
||
btnTableDropField: TToolButton;
|
||
btnTableViewData: TToolButton;
|
||
SynSQLSyn1: TSynSQLSyn;
|
||
SynMemoQuery: TSynMemo;
|
||
spltQuery: TSplitter;
|
||
menucreatetable: TMenuItem;
|
||
OpenDialog1: TOpenDialog;
|
||
TimerHostUptime: TTimer;
|
||
popupTableGrid: TPopupMenu;
|
||
Refresh2: TMenuItem;
|
||
DropField1: TMenuItem;
|
||
N3: TMenuItem;
|
||
N5: TMenuItem;
|
||
PopupmenuDropDatabase: TMenuItem;
|
||
btnTableInsertRecord: TToolButton;
|
||
popupDataGrid: TPopupMenu;
|
||
Refresh3: TMenuItem;
|
||
Insertrecord2: TMenuItem;
|
||
btnTableAddField: TToolButton;
|
||
MenuAddField: TMenuItem;
|
||
btnTableEditField: TToolButton;
|
||
MenuEditField: TMenuItem;
|
||
popupResultGrid: TPopupMenu;
|
||
Copyrecords1: TMenuItem;
|
||
CopyasCSVData1: TMenuItem;
|
||
N9: TMenuItem;
|
||
LabelResultinfo: TLabel;
|
||
MenuAdvancedProperties: TMenuItem;
|
||
N10: TMenuItem;
|
||
MenuRenameTable: TMenuItem;
|
||
MenuViewBlob: TMenuItem;
|
||
TimerConnected: TTimer;
|
||
N12: TMenuItem;
|
||
MenuTableComment: TMenuItem;
|
||
popupSqlLog: TPopupMenu;
|
||
Clear2: TMenuItem;
|
||
Copy1: TMenuItem;
|
||
N13: TMenuItem;
|
||
EditQuery1: TMenuItem;
|
||
Markall3: TMenuItem;
|
||
N15: TMenuItem;
|
||
MenuOptimize: TMenuItem;
|
||
MenuCheck: TMenuItem;
|
||
MenuAnalyze: TMenuItem;
|
||
MenuRepair: TMenuItem;
|
||
More1: TMenuItem;
|
||
TimerConnectErrorCloseWindow: TTimer;
|
||
PopupMenuDropTable: TMenuItem;
|
||
N17: TMenuItem;
|
||
pnlTableList: TPanel;
|
||
ListColumns: TSortListView;
|
||
CopycontentsasHTML1: TMenuItem;
|
||
CopycontentsasHTML2: TMenuItem;
|
||
Copy3: TMenuItem;
|
||
Paste2: TMenuItem;
|
||
N4: TMenuItem;
|
||
gridData: TSMDBGrid;
|
||
DataSource1: TDataSource;
|
||
gridQuery: TSMDBGrid;
|
||
DataSource2: TDataSource;
|
||
Copytableas1: TMenuItem;
|
||
Filter1: TMenuItem;
|
||
MenuLimit: TMenuItem;
|
||
Delete1: TMenuItem;
|
||
N6: TMenuItem;
|
||
QF1: TMenuItem;
|
||
QF2: TMenuItem;
|
||
QuickFilter1: TMenuItem;
|
||
QF3: TMenuItem;
|
||
QF4: TMenuItem;
|
||
N7: TMenuItem;
|
||
DropFilter1: TMenuItem;
|
||
Table1: TMenuItem;
|
||
popupFilterOpenFile: TPopupMenu;
|
||
OpenDialog2: TOpenDialog;
|
||
PrintList2: TMenuItem;
|
||
PrintList3: TMenuItem;
|
||
PrintList4: TMenuItem;
|
||
N1: TMenuItem;
|
||
MenuChangeType: TMenuItem;
|
||
MenuChangeType2: TMenuItem;
|
||
MenuChangeType3: TMenuItem;
|
||
MenuChangeTypeOther: TMenuItem;
|
||
N8: TMenuItem;
|
||
MenuChangeType1: TMenuItem;
|
||
MenuChangeType4: TMenuItem;
|
||
MenuCopyTable: TMenuItem;
|
||
PageControlBottom: TPageControl;
|
||
tabSQLLog: TTabSheet;
|
||
tabBlobEditor: TTabSheet;
|
||
tabFilter: TTabSheet;
|
||
SynMemoSQLLog: TSynMemo;
|
||
ToolBar3: TToolBar;
|
||
btnBlobWordWrap: TToolButton;
|
||
btnBlobLoad: TToolButton;
|
||
btnBlobSave: TToolButton;
|
||
PageControlBlobEditors: TPageControl;
|
||
tabBlobEditorText: TTabSheet;
|
||
DBMemo1: TDBMemo;
|
||
tabBlobEditorImage: TTabSheet;
|
||
SynMemoFilter: TSynMemo;
|
||
btnDbCopyTable: TToolButton;
|
||
btnDbDropTable: TToolButton;
|
||
N18: TMenuItem;
|
||
MenuChangeType5: TMenuItem;
|
||
selectall1: TMenuItem;
|
||
MenuAutoupdate: TMenuItem;
|
||
TimerProcesslist: TTimer;
|
||
Set1: TMenuItem;
|
||
EnableAutoRefresh: TMenuItem;
|
||
DisableAutoRefresh: TMenuItem;
|
||
Saveastextfile1: TMenuItem;
|
||
QF7: TMenuItem;
|
||
QF5: TMenuItem;
|
||
QF6: TMenuItem;
|
||
pnlQueryToolbar: TPanel;
|
||
ToolBarQuery: TToolBar;
|
||
btnQueryRun: TToolButton;
|
||
btnQueryRunSelected: TToolButton;
|
||
btnQueryLoad: TToolButton;
|
||
btnQuerySave: TToolButton;
|
||
btnQueryFind: TToolButton;
|
||
PanelCharsInQueryWindow: TPanel;
|
||
btnQueryStopOnErrors: TToolButton;
|
||
Panel10: TPanel;
|
||
ComboBoxWhereFilters: TComboBox;
|
||
ToolBar4: TToolBar;
|
||
btnFilterSet: TToolButton;
|
||
btnFilterLoad: TToolButton;
|
||
btnFilterSave: TToolButton;
|
||
btnFilterClear: TToolButton;
|
||
sepFilter1: TToolButton;
|
||
btnFilterPrevious: TToolButton;
|
||
btnFilterNext: TToolButton;
|
||
QF8: TMenuItem;
|
||
QF10: TMenuItem;
|
||
QF11: TMenuItem;
|
||
QF9: TMenuItem;
|
||
QF12: TMenuItem;
|
||
CopyasXMLdata1: TMenuItem;
|
||
CopyasXMLdata2: TMenuItem;
|
||
Exportdata1: TMenuItem;
|
||
Exportdata2: TMenuItem;
|
||
SaveDialogExportData: TExportSaveDialog;
|
||
N11: TMenuItem;
|
||
ProgressBarQuery: TProgressBar;
|
||
btnQueryReplace: TToolButton;
|
||
Copy4: TMenuItem;
|
||
N14: TMenuItem;
|
||
DataInsertDateTime: TMenuItem;
|
||
DataTimestamp: TMenuItem;
|
||
DataDateTime: TMenuItem;
|
||
DataTime: TMenuItem;
|
||
DataDate: TMenuItem;
|
||
DataYear: TMenuItem;
|
||
ViewasHTML1: TMenuItem;
|
||
btnBlobViewAsHtml: TToolButton;
|
||
HTMLview1: TMenuItem;
|
||
btnBlobCopy: TToolButton;
|
||
ScrollBox1: TScrollBox;
|
||
InsertfilesintoBLOBfields1: TMenuItem;
|
||
InsertfilesintoBLOBfields2: TMenuItem;
|
||
InsertfilesintoBLOBfields3: TMenuItem;
|
||
N19: TMenuItem;
|
||
setNULL1: TMenuItem;
|
||
ZSQLMonitor1: TZSQLMonitor;
|
||
EDBImage1: TEDBImage;
|
||
Exporttables1: TMenuItem;
|
||
Exporttables2: TMenuItem;
|
||
EditDataSearch: TEdit;
|
||
ButtonDataSearch: TButton;
|
||
Find1: TMenuItem;
|
||
popupDbGridHeader: TPopupMenu;
|
||
DefaultColumnLayout1: TMenuItem;
|
||
N20: TMenuItem;
|
||
SynCompletionProposal1: TSynCompletionProposal;
|
||
popupQueryLoad: TPopupMenu;
|
||
OpenDialogSQLFile: TOpenDialog;
|
||
SaveDialogSQLFile: TSaveDialog;
|
||
btnQuerySaveSnippet: TToolButton;
|
||
FindDialogQuery: TFindDialog;
|
||
SynEditSearch1: TSynEditSearch;
|
||
ReplaceDialogQuery: TReplaceDialog;
|
||
N16: TMenuItem;
|
||
ManageIndexes1: TMenuItem;
|
||
btnTableManageIndexes: TToolButton;
|
||
tabCommandStats: TTabSheet;
|
||
ListCommandStats: TSortListView;
|
||
CheckBoxDataSearch: TCheckBox;
|
||
QF13: TMenuItem;
|
||
QF14: TMenuItem;
|
||
QF15: TMenuItem;
|
||
QF16: TMenuItem;
|
||
QF17: TMenuItem;
|
||
N21: TMenuItem;
|
||
btnUnsafeEdit: TToolButton;
|
||
btnColumnSelection: TSpeedButton;
|
||
btnAltTerminator: TToolButton;
|
||
pnlQueryHelpers: TPanel;
|
||
tabsetQueryHelpers: TTabSet;
|
||
lboxQueryHelpers: TListBox;
|
||
popupQuery: TPopupMenu;
|
||
MenuRun: TMenuItem;
|
||
MenuRunSelection: TMenuItem;
|
||
MenuRunLine: TMenuItem;
|
||
MenuSetFilter: TMenuItem;
|
||
MenuItem1: TMenuItem;
|
||
menucopy: TMenuItem;
|
||
menupaste: TMenuItem;
|
||
menuload: TMenuItem;
|
||
menusave: TMenuItem;
|
||
menuclear: TMenuItem;
|
||
MenuFind: TMenuItem;
|
||
MenuReplace: TMenuItem;
|
||
MenuItem2: TMenuItem;
|
||
lblDataTop: TLabel;
|
||
btnDataSorting: TSpeedButton;
|
||
spltQueryHelpers: TSplitter;
|
||
menuRenameColumn: TMenuItem;
|
||
N22: TMenuItem;
|
||
N23: TMenuItem;
|
||
menuInsertFileAtCursor: TMenuItem;
|
||
menuSaveSelectionToFile: TMenuItem;
|
||
menuSaveAsSnippet: TMenuItem;
|
||
menuSaveSelectionAsSnippet: TMenuItem;
|
||
popupQueryHelpers: TPopupMenu;
|
||
menuDeleteSnippet: TMenuItem;
|
||
menuHelp: TMenuItem;
|
||
procedure menuRenameColumnClick(Sender: TObject);
|
||
procedure ListColumnsEdited(Sender: TObject; Item: TListItem;
|
||
var S: string);
|
||
procedure menuclearClick(Sender: TObject);
|
||
procedure popupQueryPopup(Sender: TObject);
|
||
procedure lboxQueryHelpersClick(Sender: TObject);
|
||
procedure lboxQueryHelpersDblClick(Sender: TObject);
|
||
procedure tabsetQueryHelpersChange(Sender: TObject; NewTab: Integer;
|
||
var AllowChange: Boolean);
|
||
procedure btnTableViewDataClick(Sender: TObject);
|
||
procedure btnDbViewDataClick(Sender: TObject);
|
||
procedure btnDataClick(Sender: TObject);
|
||
procedure DBtreeExpanding(Sender: TObject; Node: TTreeNode;
|
||
var AllowExpansion: Boolean);
|
||
procedure ListTablesSelectItem(Sender: TObject; Item: TListItem;
|
||
Selected: Boolean);
|
||
procedure ListColumnsSelectItem(Sender: TObject; Item: TListItem;
|
||
Selected: Boolean);
|
||
procedure ListProcessesSelectItem(Sender: TObject; Item: TListItem;
|
||
Selected: Boolean);
|
||
procedure DBMemo1Exit(Sender: TObject);
|
||
procedure btnUnsafeEditClick(Sender: TObject);
|
||
procedure gridMouseDown(Sender: TObject; Button: TMouseButton;
|
||
Shift: TShiftState; X, Y: Integer);
|
||
procedure controlsKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
|
||
procedure CallSQLHelp(Sender: TObject);
|
||
procedure CallSQLHelpWithKeyword(keyword: String);
|
||
procedure ManageIndexes1Click(Sender: TObject);
|
||
procedure ZQueryGridAfterPost(DataSet: TDataSet);
|
||
procedure btnQueryReplaceClick(Sender: TObject);
|
||
procedure ReplaceDialogQueryReplace(Sender: TObject);
|
||
procedure ReplaceDialogQueryFind(Sender: TObject);
|
||
procedure FindDialogQueryFind(Sender: TObject);
|
||
procedure btnQuerySaveSnippetClick(Sender: TObject);
|
||
procedure SynCompletionProposal1AfterCodeCompletion(Sender: TObject;
|
||
const Value: String; Shift: TShiftState; Index: Integer; EndToken: Char);
|
||
procedure btnDbPropertiesClick(Sender: TObject);
|
||
procedure popupDbGridPopup(Sender: TObject);
|
||
procedure SynCompletionProposal1CodeCompletion(Sender: TObject;
|
||
var Value: String; Shift: TShiftState; Index: Integer; EndToken: Char);
|
||
procedure SynCompletionProposal1Execute(Kind: TSynCompletionType;
|
||
Sender: TObject; var CurrentInput: String; var x, y: Integer;
|
||
var CanExecute: Boolean);
|
||
procedure PerformConnect;
|
||
procedure ToolButton4Click(Sender: TObject);
|
||
procedure FormClose(Sender: TObject; var Action: TCloseAction);
|
||
procedure ReadDatabasesAndTables(Sender: TObject);
|
||
procedure DBtreeChange(Sender: TObject; Node: TTreeNode);
|
||
procedure pcChange(Sender: TObject);
|
||
procedure viewdata(Sender: TObject);
|
||
procedure ShowDBProperties(Sender: TObject);
|
||
procedure ShowTableProperties(Sender: TObject);
|
||
procedure ValidateControls(FrmIsFocussed: Boolean = true);
|
||
procedure SelectHost;
|
||
procedure SelectDatabase(db: String);
|
||
procedure SelectTable(db: String; table: String; switchView: boolean = true);
|
||
procedure ShowTableData(table: string);
|
||
procedure ShowTable(Sender: TObject);
|
||
procedure EmptyTable(Sender: TObject);
|
||
procedure DropDB(Sender: TObject);
|
||
procedure LogSQL(msg: String = ''; comment: Boolean = true );
|
||
procedure ShowVariablesAndProcesses(Sender: TObject);
|
||
procedure CreateDatabase(Sender: TObject);
|
||
procedure KillProcess(Sender: TObject);
|
||
procedure PageControlHostChange(Sender: TObject);
|
||
procedure ExecSQLClick(Sender: TObject; Selection: Boolean = false;
|
||
CurrentLine: Boolean=false);
|
||
procedure DropField(Sender: TObject);
|
||
procedure SynMemoQueryChange(Sender: TObject);
|
||
procedure CreateTable(Sender: TObject);
|
||
procedure TimerHostUptimeTimer(Sender: TObject);
|
||
procedure FormActivate(Sender: TObject);
|
||
procedure FormDeactivate(Sender: TObject);
|
||
procedure FormShow(Sender: TObject);
|
||
procedure UpdateField(Sender: TObject);
|
||
procedure MenuAdvancedPropertiesClick(Sender: TObject);
|
||
procedure ListTablesEdited(Sender: TObject; Item: TListItem;
|
||
var S: String);
|
||
procedure MenuRenameTableClick(Sender: TObject);
|
||
procedure MenuViewBlobClick(Sender: TObject);
|
||
procedure TimerConnectedTimer(Sender: TObject);
|
||
procedure Clear1Click(Sender: TObject);
|
||
procedure MenuTableCommentClick(Sender: TObject);
|
||
procedure Clear2Click(Sender: TObject);
|
||
procedure EditQuery1Click(Sender: TObject);
|
||
procedure Markall3Click(Sender: TObject);
|
||
procedure ReadWindowOptions;
|
||
procedure More1Click(Sender: TObject);
|
||
procedure MenuOptimizeClick(Sender: TObject);
|
||
procedure MenuCheckClick(Sender: TObject);
|
||
procedure MenuAnalyzeClick(Sender: TObject);
|
||
procedure MenuRepairClick(Sender: TObject);
|
||
procedure ListTablesDblClick(Sender: TObject);
|
||
procedure TimerConnectErrorCloseWindowTimer(Sender: TObject);
|
||
procedure gridDataTitleClick(Column: TColumn);
|
||
procedure Filter1Click(Sender: TObject);
|
||
procedure MenuLimitClick(Sender: TObject);
|
||
procedure Delete1Click(Sender: TObject);
|
||
procedure QuickFilterClick(Sender: TObject);
|
||
procedure btnBlobWordWrapClick(Sender: TObject);
|
||
procedure PageControlBlobEditorsChange(Sender: TObject);
|
||
procedure btnBlobSaveClick(Sender: TObject);
|
||
procedure btnBlobLoadClick(Sender: TObject);
|
||
procedure btnFilterLoadClick(Sender: TObject);
|
||
procedure btnFilterSaveClick(Sender: TObject);
|
||
procedure setFilter(Sender: TObject);
|
||
procedure ClearFilter(Sender: TObject);
|
||
procedure LoadSQLWhereFile(Sender: TObject);
|
||
procedure DropFilter1Click(Sender: TObject);
|
||
procedure MenuChangeTypeClick(Sender: TObject);
|
||
procedure MenuChangeTypeOtherClick(Sender: TObject);
|
||
procedure InsertRecord(Sender: TObject);
|
||
procedure selectall1Click(Sender: TObject);
|
||
procedure popupResultGridPopup(Sender: TObject);
|
||
procedure gridDataColumnMoved(Sender: TObject; FromIndex,
|
||
ToIndex: Integer);
|
||
procedure Autoupdate1Click(Sender: TObject);
|
||
procedure EnableAutoRefreshClick(Sender: TObject);
|
||
procedure ShowProcessList(sender: TObject);
|
||
procedure DisableAutoRefreshClick(Sender: TObject);
|
||
procedure SynMemoQueryDragOver(Sender, Source: TObject; X, Y: Integer;
|
||
State: TDragState; var Accept: Boolean);
|
||
procedure SynMemoQueryDragDrop(Sender, Source: TObject; X, Y: Integer);
|
||
procedure SynMemoQueryDropFiles(Sender: TObject; X, Y: Integer;
|
||
AFiles: TStrings);
|
||
procedure SynMemoQueryKeyUp(Sender: TObject; var Key: Word;
|
||
Shift: TShiftState);
|
||
procedure SynMemoQueryMouseUp(Sender: TObject; Button: TMouseButton;
|
||
Shift: TShiftState; X, Y: Integer);
|
||
procedure popupHostPopup(Sender: TObject);
|
||
procedure ListTablesEditing(Sender: TObject; Item: TListItem;
|
||
var AllowEdit: Boolean);
|
||
procedure Saveastextfile1Click(Sender: TObject);
|
||
procedure popupTreeViewPopup(Sender: TObject);
|
||
procedure btnQueryFindClick(Sender: TObject);
|
||
procedure btnQuerySaveClick(Sender: TObject);
|
||
procedure btnQueryLoadClick(Sender: TObject);
|
||
procedure btnFilterPreviousClick(Sender: TObject);
|
||
procedure btnFilterNextClick(Sender: TObject);
|
||
procedure ComboBoxWhereFiltersChange(Sender: TObject);
|
||
procedure btnQueryStopOnErrorsClick(Sender: TObject);
|
||
procedure DBGridDblClick(Sender: TObject);
|
||
procedure SaveDialogExportDataTypeChange(Sender: TObject);
|
||
procedure DBGridGetCellParams(Sender: TObject; Field: TField;
|
||
AFont: TFont; var Background: TColor; Highlight: Boolean);
|
||
procedure popupDataGridPopup(Sender: TObject);
|
||
procedure InsertDate(Sender: TObject);
|
||
procedure btnBlobCopyClick(Sender: TObject);
|
||
procedure setNULL1Click(Sender: TObject);
|
||
procedure MenuAddFieldClick(Sender: TObject);
|
||
procedure ZQueryGridBeforeClose(DataSet: TDataSet);
|
||
function GetNamedVar( SQLQuery: String; x: String;
|
||
HandleErrors: Boolean = false; DisplayErrors: Boolean = false ) : String;
|
||
function GetVar( SQLQuery: String; x: Integer = 0;
|
||
HandleErrors: Boolean = false; DisplayErrors: Boolean = false ) : String;
|
||
function GetResults( SQLQuery: String;
|
||
HandleErrors: Boolean = false; DisplayErrors: Boolean = false ): TDataSet;
|
||
function GetCol( SQLQuery: String; x: Integer = 0;
|
||
HandleErrors: Boolean = false; DisplayErrors: Boolean = false ) : TStringList;
|
||
procedure ZSQLMonitor1LogTrace(Sender: TObject; Event: TZLoggingEvent);
|
||
procedure ResizeImageToFit;
|
||
procedure Splitter2Moved(Sender: TObject);
|
||
procedure DataSourceDataChange(Sender: TObject; Field: TField);
|
||
procedure DBGridColEnter(Sender: TObject);
|
||
procedure ZQuery1EditError(DataSet: TDataSet; E: EDatabaseError;
|
||
var Action: TDataAction);
|
||
procedure FormResize(Sender: TObject);
|
||
procedure ButtonDataSearchClick(Sender: TObject);
|
||
procedure EditDataSearchEnter(Sender: TObject);
|
||
procedure EditDataSearchExit(Sender: TObject);
|
||
procedure ListTablesColumnRightClick(Sender: TObject;
|
||
Column: TListColumn; Point: TPoint);
|
||
procedure MenuTablelistColumnsClick(Sender: TObject);
|
||
procedure ListTablesMouseDown(Sender: TObject; Button: TMouseButton;
|
||
Shift: TShiftState; X, Y: Integer);
|
||
function mask(str: String) : String;
|
||
procedure CheckConnection();
|
||
procedure QueryLoad( filename: String; ReplaceContent: Boolean = true );
|
||
procedure AddOrRemoveFromQueryLoadHistory( filename: String;
|
||
AddIt: Boolean = true; CheckIfFileExists: Boolean = true );
|
||
procedure popupQueryLoadClick( sender: TObject );
|
||
procedure FillPopupQueryLoad;
|
||
procedure PopupQueryLoadRemoveAbsentFiles( sender: TObject );
|
||
procedure ExecuteNonQuery(SQLQuery: String);
|
||
function ExecuteQuery(query: String): TDataSet;
|
||
function CreateOrGetRemoteQueryTab(sender: THandle): THandle;
|
||
procedure menuDeleteSnippetClick(Sender: TObject);
|
||
function GetCalculatedLimit( Table: String ): Int64;
|
||
procedure menuInsertFileAtCursorClick(Sender: TObject);
|
||
procedure RunAsyncPost(ds: TDeferDataSet);
|
||
|
||
private
|
||
strHostRunning : String;
|
||
strHostNotRunning : String;
|
||
uptime : Integer;
|
||
time_connected : Cardinal;
|
||
OnlyDBs,
|
||
OnlyDBs2 : TStringList; // used on connecting
|
||
viewingdata : Boolean;
|
||
WhereFilters : TStringList;
|
||
WhereFiltersIndex : Integer;
|
||
StopOnErrors, WordWrap : Boolean;
|
||
FMysqlConn : TMysqlConn;
|
||
FConn : TOpenConnProf;
|
||
QueryRunningInterlock : Integer;
|
||
lastUsedDB : String;
|
||
UserQueryFired : Boolean;
|
||
CachedTableLists : TStringList;
|
||
QueryHelpersSelectedItems : Array[0..3] of Integer;
|
||
|
||
function GetQueryRunning: Boolean;
|
||
procedure SetQueryRunning(running: Boolean);
|
||
procedure GridHighlightChanged(Sender: TObject);
|
||
procedure SaveBlob;
|
||
function GetActiveGrid: TSMDBGrid;
|
||
procedure WaitForQueryCompletion(WaitForm: TForm);
|
||
function RunThreadedQuery(AQuery : String) : TMysqlQuery;
|
||
procedure DisplayRowCountStats(ds: TDataSet);
|
||
procedure insertFunction(Sender: TObject);
|
||
|
||
public
|
||
ActualDatabase : String;
|
||
ActualTable : String;
|
||
dataselected : Boolean;
|
||
editing : Boolean;
|
||
mysql_version : Integer;
|
||
tnodehost : TTreeNode;
|
||
Description : String;
|
||
DBRightClickSelectedItem : TTreeNode; // TreeNode for dropping with right-click
|
||
|
||
FProgressForm : TFrmQueryProgress;
|
||
procedure Init(AConn : POpenConnProf; AMysqlConn : TMysqlConn);
|
||
//procedure HandleQueryNotification(ASender : TMysqlQuery; AEvent : Integer);
|
||
function GetVisualDataset() : TDataSet;
|
||
|
||
function ExecUpdateQuery(sql: string; HandleErrors: Boolean = false; DisplayErrors: Boolean = false): Int64;
|
||
function ExecSelectQuery(sql: string; HandleErrors: Boolean = false; DisplayErrors: Boolean = false): TDataSet;
|
||
procedure ExecUseQuery(db: string; HandleErrors: Boolean = false; DisplayErrors: Boolean = false);
|
||
|
||
property FQueryRunning: Boolean read GetQueryRunning write SetQueryRunning;
|
||
property ActiveGrid: TSMDBGrid read GetActiveGrid;
|
||
property MysqlConn : TMysqlConn read FMysqlConn;
|
||
property Conn : TOpenConnProf read FConn;
|
||
|
||
function FetchActiveDbTableList: TDataSet;
|
||
function RefreshActiveDbTableList: TDataSet;
|
||
procedure ClearAllTableLists;
|
||
procedure UpdateTreeTableList;
|
||
procedure EnsureActiveDatabase;
|
||
end;
|
||
|
||
|
||
type
|
||
// Represents errors already "handled" (shown to user),
|
||
// which can thus safely be ignored.
|
||
THandledSQLError = class(Exception)
|
||
end;
|
||
|
||
|
||
// -----------------------------------------------------------------------------
|
||
implementation
|
||
|
||
|
||
uses
|
||
Main, createtable, fieldeditor, tbl_properties, tblcomment,
|
||
optimizetables, copytable, sqlhelp, printlist,
|
||
column_selection, data_sorting, runsqlfile, mysql;
|
||
|
||
|
||
{$I const.inc}
|
||
|
||
|
||
{$R *.DFM}
|
||
|
||
|
||
function TMDIChild.CreateOrGetRemoteQueryTab(sender: THandle): THandle;
|
||
begin
|
||
// Should create a tab for commands from another window,
|
||
// or return a handle to an existing tab if one already exists for that window.
|
||
//
|
||
// TODO: Implement this when multiple tabs are implemented.
|
||
// Return a tab's handle instead of the childwin's handle.
|
||
result := Self.Handle;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.PerformConnect;
|
||
var
|
||
charset : String;
|
||
v : String[10];
|
||
versions : TStringList;
|
||
begin
|
||
try
|
||
time_connected := 0;
|
||
TimerConnected.Enabled := true;
|
||
LogSQL( 'Connection established with host "' + FMysqlConn.Connection.hostname +
|
||
'" on port ' + IntToStr(FMysqlConn.Connection.Port) );
|
||
LogSQL( 'Connection-ID: ' + IntToStr( MySQLConn.Connection.GetThreadId ) );
|
||
|
||
{***
|
||
Detect server version
|
||
}
|
||
v := GetVar( 'SELECT VERSION()' );
|
||
versions := explode( '.', v );
|
||
mysql_version := MakeInt( versions[0] ) * 10000 + MakeInt( versions[1] ) *
|
||
100 + MakeInt( versions[2] );
|
||
strHostRunning := FConn.MysqlParams.Host + ' running MySQL-Version ' + v +
|
||
' / Uptime: %s';
|
||
strHostNotRunning := 'Disconnected from ' + FConn.MysqlParams.Host + '.';
|
||
// On Re-Connection, try to restore lost properties
|
||
{***
|
||
SET NAMES statement available since MySQL 4.1.0 .
|
||
Older versions throw a SQL-error: "Unknown system variable 'NAMES'"
|
||
@see http://lists.phpbar.de/pipermail/opengeodb/2005-September/002455.html
|
||
}
|
||
if ( mysql_version >= 40100 ) then
|
||
begin
|
||
charset := ConvertWindowsCodepageToMysqlCharacterSet( GetACP() );
|
||
if ( charset = '' ) then
|
||
begin
|
||
LogSQL( 'Could not find a MySQL character set to match the current ' +
|
||
'Windows ANSI codepage.', true );
|
||
LogSQL( Format( 'Use SHOW CHARACTER SET to see MySQL character sets; ' +
|
||
'if you can find one that you are certain matches %d, please report' +
|
||
' it via http://rfe.heidisql.com/.', [GetACP()] ), true );
|
||
end
|
||
else
|
||
begin
|
||
ExecuteNonQuery( 'SET NAMES ' + charset );
|
||
end;
|
||
end;
|
||
|
||
if ( FMysqlConn.Connection.Database <> '' ) then
|
||
begin
|
||
ExecUseQuery( FMysqlConn.Connection.Database );
|
||
end;
|
||
except
|
||
on E: Exception do
|
||
begin
|
||
LogSQL( E.Message, true );
|
||
Screen.Cursor := crDefault;
|
||
MessageDlg( E.Message, mtError, [mbOK], 0 );
|
||
raise;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
|
||
function TMDIChild.GetQueryRunning: Boolean;
|
||
begin
|
||
Result := ( QueryRunningInterlock = 1 );
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.SetQueryRunning(running: Boolean);
|
||
var
|
||
newValue : Integer;
|
||
oldValue : Integer;
|
||
begin
|
||
if ( running ) then
|
||
begin
|
||
newValue := 1;
|
||
end
|
||
else
|
||
begin
|
||
newValue := 0;
|
||
end;
|
||
|
||
oldValue := InterlockedExchange( QueryRunningInterlock, newValue );
|
||
if ( newValue = oldValue ) then
|
||
begin
|
||
case ( newValue ) of
|
||
1 :
|
||
begin
|
||
raise Exception.Create( 'Error: Default connection is ' +
|
||
'already executing a query.' );
|
||
end;
|
||
0 :
|
||
begin
|
||
raise Exception.Create( 'Internal badness: Double reset of running ' +
|
||
'flag.' );
|
||
end;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
|
||
// Check the tabletype of the selected table in the Popupmenu of ListTables
|
||
procedure TMDIChild.popupDbGridPopup(Sender: TObject);
|
||
var
|
||
i : byte;
|
||
SelectedEngine : String;
|
||
begin
|
||
if ( ListTables.SelCount <> 1 ) then
|
||
begin
|
||
Exit;
|
||
end;
|
||
|
||
for i := 0 to ( ListTables.Columns.Count - 1 ) do
|
||
begin
|
||
if ( ListTables.Columns[i].Caption = 'Engine' ) then
|
||
begin
|
||
SelectedEngine := ListTables.Selected.SubItems[i - 1];
|
||
Break;
|
||
end;
|
||
end;
|
||
|
||
for i := 0 to ( MenuChangeType.Count - 1 ) do
|
||
begin
|
||
MenuChangeType.Items[i].Checked := ( MenuChangeType.Items[i].Caption = SelectedEngine );
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.Init(AConn : POpenConnProf; AMysqlConn : TMysqlConn);
|
||
var
|
||
AutoReconnect : Boolean;
|
||
menuitem : TMenuItem;
|
||
winName : String;
|
||
i, j : Integer;
|
||
treenode : TTreeNode;
|
||
ds : TDataSet;
|
||
miGroup,
|
||
miFunction : TMenuItem;
|
||
functioncats : TStringList;
|
||
begin
|
||
QueryRunningInterlock := 0;
|
||
UserQueryFired := False;
|
||
CachedTableLists := TStringList.Create;
|
||
|
||
FConn := AConn^;
|
||
FMysqlConn := AMysqlConn; // we're now responsible to free it
|
||
|
||
FConn.MysqlConn := FMysqlConn.Connection; // use this connection (instead of zConn)
|
||
|
||
// Initialization: establish connection and read some vars from registry
|
||
MainForm.Showstatus( 'Creating window...', 2, true );
|
||
|
||
// Temporarily disable AutoReconnect in Registry
|
||
// in case of unexpected application-termination
|
||
AutoReconnect := false;
|
||
with ( TRegistry.Create() ) do
|
||
begin
|
||
OpenKey( REGPATH, true );
|
||
if ( Valueexists( 'Autoreconnect' ) ) then
|
||
begin
|
||
if ( ReadBool( 'AutoReconnect' ) ) then
|
||
begin
|
||
AutoReconnect := true;
|
||
WriteBool( 'AutoReconnect', false );
|
||
end;
|
||
end;
|
||
CloseKey();
|
||
end;
|
||
|
||
ReadWindowOptions();
|
||
|
||
MainForm.Showstatus( 'Connecting to ' + FConn.MysqlParams.Host + '...', 2, true );
|
||
|
||
try
|
||
PerformConnect();
|
||
except
|
||
TimerConnectErrorCloseWindow.Enabled := true;
|
||
Exit;
|
||
end;
|
||
|
||
Description := FMysqlConn.Description;;
|
||
Caption := Description;
|
||
OnlyDBs := explode( ';', FConn.DatabaseList );
|
||
if ( FConn.DatabaseListSort ) then
|
||
begin
|
||
OnlyDBs.Sort();
|
||
end;
|
||
|
||
// Fill variables-list, processlist and DB-tree
|
||
ShowVariablesAndProcesses( Self );
|
||
ReadDatabasesAndTables( Self );
|
||
|
||
// Re-enable AutoReconnect in Registry!
|
||
if ( AutoReconnect ) then
|
||
begin
|
||
with ( TRegistry.Create() ) do
|
||
begin
|
||
OpenKey( REGPATH, true );
|
||
WriteBool( 'AutoReconnect', true );
|
||
CloseKey();
|
||
end;
|
||
end;
|
||
|
||
// Set some defaults
|
||
ActualDatabase := '';
|
||
ActualTable := '';
|
||
|
||
// Read engine-types for popupmenu in database tab
|
||
if ( mysql_version >= 40102 ) then
|
||
begin
|
||
for i := ( MenuChangeType.Count - 1 ) downto 0 do
|
||
begin
|
||
MenuChangeType.Delete(i);
|
||
end;
|
||
ds := GetResults( 'SHOW ENGINES' );
|
||
for i := 0 to ( ds.RecordCount - 1 ) do
|
||
begin
|
||
menuitem := TMenuItem.Create(self);
|
||
menuitem.Caption := ds.FieldByName('Engine').AsString;
|
||
menuitem.Hint := ds.FieldByName('Comment').AsString;
|
||
if ( UpperCase( ds.FieldByName('Support').AsString ) = 'NO' ) then
|
||
begin
|
||
menuitem.Enabled := false;
|
||
menuitem.Hint := menuitem.Hint + ' ('+STR_NOTSUPPORTED+')';
|
||
end;
|
||
menuitem.OnClick := MenuChangeTypeClick;
|
||
MenuChangeType.Add( menuitem );
|
||
ds.Next();
|
||
end;
|
||
end;
|
||
|
||
// Define window properties
|
||
SetWindowConnected( true );
|
||
i := SetWindowName( Description );
|
||
winName := Description;
|
||
if ( i <> 0 ) then
|
||
begin
|
||
winName := winName + Format( ' (%d)', [i] );
|
||
end;
|
||
Application.Title := winName + ' - ' + APPNAME;
|
||
|
||
// Reselect last used database
|
||
if ( ( MainForm.GetRegValue( 'RestoreLastUsedDB', true ) ) and ( lastUsedDB <> '' ) ) then
|
||
begin
|
||
for j := 0 to ( DBTree.Items.Count - 1 ) do
|
||
begin
|
||
treenode := DBTree.Items[j];
|
||
if ( ( treenode.Level = 1 ) and ( treenode.Text = lastUsedDB ) ) then
|
||
begin
|
||
DBTree.Selected := treenode;
|
||
Break;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
// Set the grid-cells to always-edit-mode.
|
||
gridData.Options := gridData.Options + [dgAlwaysShowEditor];
|
||
gridQuery.Options := gridQuery.Options + [dgAlwaysShowEditor];
|
||
|
||
|
||
// read function-list into menu
|
||
functioncats := GetFunctionCategories;
|
||
|
||
for i:=0 to functioncats.Count-1 do
|
||
begin
|
||
// Create a menu item which gets subitems later
|
||
miGroup := TMenuItem.Create(self);
|
||
miGroup.Caption := functioncats[i];
|
||
popupQuery.Items.add(miGroup);
|
||
for j:=0 to Length(MySqlFunctions)-1 do
|
||
begin
|
||
if MySqlFunctions[j].Category <> functioncats[i] then
|
||
continue;
|
||
miFunction := TMenuItem.Create(self);
|
||
miFunction.Caption := MySqlFunctions[j].Name;
|
||
miFunction.ImageIndex := 86;
|
||
// Prevent generating a hotkey
|
||
miFunction.Caption := StringReplace(miFunction.Caption, '&', '&&', [rfReplaceAll]);
|
||
// Prevent generating a seperator line
|
||
if miFunction.Caption = '-' then
|
||
miFunction.Caption := '&-';
|
||
miFunction.Hint := MySqlFunctions[j].Name + MySqlFunctions[j].Declaration;
|
||
// Take care of needed server version
|
||
if MySqlFunctions[j].Version <= mysql_version then
|
||
begin
|
||
if MySqlFunctions[j].Description <> '' then
|
||
miFunction.Hint := miFunction.Hint + ' - ' + Copy(MySqlFunctions[j].Description, 0, 200 );
|
||
miFunction.Tag := j;
|
||
// Place menuitem on menu
|
||
miFunction.OnClick := insertFunction;
|
||
end
|
||
else
|
||
begin
|
||
miFunction.Hint := miFunction.Hint + ' - ('+STR_NOTSUPPORTED+', needs >= '+ConvertServerVersion(MySqlFunctions[j].Version)+')';
|
||
miFunction.Enabled := False;
|
||
end;
|
||
// Prevent generating a seperator for ShortHint and LongHint
|
||
miFunction.Hint := StringReplace( miFunction.Hint, '|', '<27>', [rfReplaceAll] );
|
||
miGroup.Add(miFunction);
|
||
end;
|
||
end;
|
||
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.ReadWindowOptions;
|
||
var
|
||
ws : String;
|
||
i : Integer;
|
||
menuitem : Tmenuitem;
|
||
begin
|
||
with ( TRegistry.Create() ) do
|
||
begin
|
||
if OpenKey( REGPATH, true ) then
|
||
begin
|
||
ws := ReadString( 'childwinstate' );
|
||
if ( ws = 'Normal' ) then
|
||
begin
|
||
WindowState := wsNormal;
|
||
if ValueExists( 'childwinleft' ) then
|
||
begin
|
||
Left := ReadInteger( 'childwinleft' );
|
||
Top := ReadInteger( 'childwintop' );
|
||
Width := ReadInteger( 'childwinwidth' );
|
||
Height := ReadInteger( 'childwinheight' );
|
||
end;
|
||
end
|
||
else
|
||
if ( ws = 'Minimized' ) then
|
||
begin
|
||
WindowState := wsMinimized
|
||
end
|
||
else
|
||
if ( ws = 'Maximized' ) then
|
||
begin
|
||
WindowState := wsMaximized;
|
||
end;
|
||
|
||
// Other values:
|
||
if ( ValueExists( 'querymemoheight' ) ) then
|
||
begin
|
||
pnlQueryMemo.Height := ReadInteger('querymemoheight');
|
||
end;
|
||
|
||
if ValueExists( 'queryhelperswidth' ) then
|
||
begin
|
||
pnlQueryHelpers.Width := ReadInteger('queryhelperswidth');
|
||
end;
|
||
|
||
if ( ValueExists( 'dbtreewidth' ) ) then
|
||
begin
|
||
DBtree.Width := ReadInteger( 'dbtreewidth' );
|
||
end;
|
||
|
||
if ( ValueExists( 'sqloutheight' ) ) then
|
||
begin
|
||
PageControlBottom.Height := ReadInteger( 'sqloutheight' );
|
||
end;
|
||
|
||
if ( ValueExists( 'DefaultColWidth' ) ) then
|
||
begin
|
||
Mainform.DefaultColWidth := ReadInteger( 'DefaultColWidth' );
|
||
end
|
||
else
|
||
begin
|
||
Mainform.DefaultColWidth := 100;
|
||
end;
|
||
|
||
// SQL-Font:
|
||
if ( ( ValueExists( 'FontName' ) ) and ( ValueExists('FontSize') ) ) then
|
||
begin
|
||
SynMemoQuery.Font.Name := ReadString( 'FontName' );
|
||
SynMemoSQLLog.Font.Name := ReadString( 'FontName' );
|
||
SynMemoQuery.Font.Size := ReadInteger( 'FontSize' );
|
||
SynMemoSQLLog.Font.Size := ReadInteger( 'FontSize' );
|
||
end;
|
||
|
||
// Data-Font:
|
||
if ( ( ValueExists( 'DataFontName' ) ) and ( ValueExists( 'DataFontSize' ) ) ) then
|
||
begin
|
||
gridData.Font.Name := ReadString( 'DataFontName' );
|
||
gridQuery.Font.Name := ReadString( 'DataFontName' );
|
||
DBMemo1.Font.Name := ReadString( 'DataFontName' );
|
||
gridData.Font.Size := ReadInteger( 'DataFontSize' );
|
||
gridQuery.Font.Size := ReadInteger( 'DataFontSize' );
|
||
DBMemo1.Font.Size := ReadInteger( 'DataFontSize' );
|
||
end;
|
||
|
||
// Color coding:
|
||
if ( ValueExists( 'SQLColKeyAttri' ) ) then
|
||
begin
|
||
SynSQLSyn1.KeyAttri.Foreground := StringToColor( ReadString( 'SQLColKeyAttri' ) );
|
||
end;
|
||
|
||
if ( ValueExists( 'SQLColFunctionAttri' ) ) then
|
||
begin
|
||
SynSQLSyn1.FunctionAttri.Foreground := StringToColor( ReadString( 'SQLColFunctionAttri' ) );
|
||
end;
|
||
|
||
if ( ValueExists( 'SQLColDataTypeAttri' ) ) then
|
||
begin
|
||
SynSQLSyn1.DataTypeAttri.Foreground := StringToColor( ReadString( 'SQLColDataTypeAttri' ) );
|
||
end;
|
||
|
||
if ( ValueExists( 'SQLColNumberAttri' ) ) then
|
||
begin
|
||
SynSQLSyn1.NumberAttri.Foreground := StringToColor( ReadString( 'SQLColNumberAttri' ) );
|
||
end;
|
||
|
||
if ( ValueExists( 'SQLColStringAttri' ) ) then
|
||
begin
|
||
SynSQLSyn1.StringAttri.Foreground := StringToColor( ReadString( 'SQLColStringAttri' ) );
|
||
end;
|
||
|
||
if ( ValueExists( 'SQLColCommentAttri' ) ) then
|
||
begin
|
||
SynSQLSyn1.CommentAttri.Foreground := StringToColor( ReadString( 'SQLColCommentAttri' ) );
|
||
end;
|
||
|
||
if ( ValueExists( 'SQLColTablenameAttri' ) ) then
|
||
begin
|
||
SynSQLSyn1.TablenameAttri.Foreground := StringToColor( ReadString( 'SQLColTablenameAttri' ) );
|
||
end;
|
||
|
||
// SQLFiles-History
|
||
FillPopupQueryLoad();
|
||
|
||
// SQL-Filter-Files-History
|
||
i := 1;
|
||
popupFilterOpenFile.Items.Clear();
|
||
while ( ValueExists( 'SQLWhereFile' + IntToStr(i) ) ) do
|
||
begin
|
||
menuitem := Tmenuitem.Create(Self);
|
||
menuitem.Caption := IntToStr( popupFilterOpenFile.Items.count + 1) + ' ' + ReadString( 'SQLWhereFile' + IntToStr(i) );
|
||
menuitem.OnClick := LoadSQLWhereFile;
|
||
popupFilterOpenFile.Items.Add( menuitem );
|
||
inc( i );
|
||
end;
|
||
|
||
// Synchronize internal variables with defaults from DFM.
|
||
StopOnErrors := btnQueryStopOnErrors.Down;
|
||
|
||
// Open server-specific registry-folder.
|
||
// relative from already opened folder!
|
||
OpenKey( 'Servers\' + FConn.Description, true );
|
||
|
||
// Set last used database, select it later in Init
|
||
lastUsedDB := ReadString( 'lastUsedDB' );
|
||
end;
|
||
CloseKey();
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.FormClose(Sender: TObject; var Action: TCloseAction);
|
||
var
|
||
ws : String;
|
||
begin
|
||
SetWindowConnected( false );
|
||
SetWindowName( main.discname );
|
||
Application.Title := APPNAME;
|
||
|
||
// Closing connection and saving some vars into registry
|
||
case ( WindowState ) of
|
||
wsNormal :
|
||
begin
|
||
ws := 'Normal';
|
||
end;
|
||
wsMinimized :
|
||
begin
|
||
ws := 'Minimized';
|
||
end;
|
||
wsMaximized :
|
||
begin
|
||
ws := 'Maximized';
|
||
end;
|
||
end;
|
||
|
||
with ( TRegistry.Create() ) do
|
||
begin
|
||
if ( OpenKey( REGPATH, true ) ) then
|
||
begin
|
||
// Window state and position
|
||
WriteString( 'childwinstate', ws );
|
||
WriteInteger( 'childwinleft', left );
|
||
WriteInteger( 'childwintop', top );
|
||
WriteInteger( 'childwinwidth', width );
|
||
WriteInteger( 'childwinheight', height );
|
||
|
||
WriteInteger( 'querymemoheight', pnlQueryMemo.Height );
|
||
WriteInteger( 'queryhelperswidth', pnlQueryHelpers.Width );
|
||
WriteInteger( 'dbtreewidth', dbtree.width );
|
||
WriteInteger( 'sqloutheight', PageControlBottom.Height );
|
||
|
||
// Open server-specific registry-folder.
|
||
// relative from already opened folder!
|
||
OpenKey( 'Servers\' + FConn.Description, true );
|
||
WriteString( 'lastUsedDB', ActualDatabase );
|
||
end;
|
||
end;
|
||
|
||
FormDeactivate( Sender );
|
||
mainform.ToolBarData.Visible := false;
|
||
Action := caFree;
|
||
|
||
SetWindowConnected( false );
|
||
SetWindowName( main.discname );
|
||
Application.Title := APPNAME;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.LogSQL(msg: String = ''; comment: Boolean = true);
|
||
begin
|
||
// Add a sql-command or info-line to history-memo
|
||
while ( SynMemoSQLLog.Lines.Count > mainform.logsqlnum ) do
|
||
begin
|
||
SynMemoSQLLog.Lines.Delete(0);
|
||
end;
|
||
|
||
// Shorten very long messages
|
||
if ( Length( msg ) > SQLLOG_CHAR_LIMIT ) then
|
||
begin
|
||
msg :=
|
||
'/* Very large SQL query, showing first ' +
|
||
FormatNumber( SQLLOG_CHAR_LIMIT ) +
|
||
' characters: */ ' +
|
||
Copy( msg, 0, SQLLOG_CHAR_LIMIT ) +
|
||
' ...';
|
||
end;
|
||
msg := StringReplace( msg, #9, ' ', [rfReplaceAll] );
|
||
msg := StringReplace( msg, #10, ' ', [rfReplaceAll] );
|
||
msg := StringReplace( msg, #13, ' ', [rfReplaceAll] );
|
||
msg := StringReplace( msg, ' ', ' ', [rfReplaceAll] );
|
||
if ( comment ) then
|
||
begin
|
||
msg := '/* ' + msg + ' */';
|
||
end;
|
||
SynMemoSQLLog.Lines.Add( msg );
|
||
SynMemoSQLLog.SetBookMark( 0, 0, SynMemoSQLLog.Lines.Count );
|
||
SynMemoSQLLog.GotoBookMark( 0 );
|
||
SynMemoSQLLog.ClearBookMark( 0 );
|
||
SynMemoSQLLog.Repaint();
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.ReadDatabasesAndTables(Sender: TObject);
|
||
var
|
||
tnode : TTreeNode;
|
||
tmpSelected : TTreeNode;
|
||
i : Integer;
|
||
specialDbs : TStringList;
|
||
dbName : String;
|
||
ds : TDataSet;
|
||
begin
|
||
// Fill DBTree
|
||
Screen.Cursor := crHourGlass;
|
||
dataselected := false;
|
||
DBTree.OnChange := nil;
|
||
DBTree.items.Clear();
|
||
ClearAllTableLists;
|
||
|
||
tnodehost := DBtree.Items.Add( nil, FConn.MysqlParams.User + '@' + FConn.MysqlParams.Host ); // Host or Root
|
||
tnodehost.ImageIndex := 41;
|
||
tnodehost.SelectedIndex := 41;
|
||
|
||
Screen.Cursor := crSQLWait;
|
||
mainform.Showstatus( 'Reading Databases...', 2, true );
|
||
if ( OnlyDBs.Count = 0 ) then
|
||
begin
|
||
OnlyDBs2 := TStringList.Create();
|
||
specialDbs := TStringList.Create();
|
||
ds := GetResults( 'SHOW DATABASES' );
|
||
for i:=1 to ( ds.RecordCount ) do
|
||
begin
|
||
dbName := ds.FieldByName('Database').AsString;
|
||
if ( dbName = DBNAME_INFORMATION_SCHEMA ) then
|
||
begin
|
||
specialDbs.Insert( 0, dbName )
|
||
end
|
||
(*
|
||
else
|
||
if ( dbName = DBNAME_MYSQL ) then
|
||
begin
|
||
specialDbs.Add( dbName );
|
||
end
|
||
*)
|
||
else
|
||
begin
|
||
OnlyDBs2.Add( dbName );
|
||
end;
|
||
ds.Next();
|
||
end;
|
||
OnlyDBs2.Sort();
|
||
// Prioritised position of system-databases
|
||
for i := ( specialDbs.Count - 1 ) downto 0 do
|
||
begin
|
||
OnlyDBs2.Insert( 0, specialDbs[i] );
|
||
end;
|
||
end
|
||
else
|
||
begin
|
||
OnlyDBs2 := OnlyDBs;
|
||
end;
|
||
|
||
// Avoids excessive InitializeKeywordLists() calls.
|
||
SynSQLSyn1.TableNames.BeginUpdate();
|
||
|
||
// Let synedit know all database names so that they can be highlighted
|
||
// TODO: Is this right? Adding "<db name>.<table name>" seems to make more sense..
|
||
SynSQLSyn1.TableNames.AddStrings( OnlyDBs2 );
|
||
SynSQLSyn1.TableNames.EndUpdate();
|
||
|
||
// List Databases and Tables-Names
|
||
tmpSelected := nil;
|
||
for i := 0 to ( OnlyDBs2.Count - 1 ) do
|
||
begin
|
||
tnode := DBtree.Items.AddChild( tnodehost, OnlyDBs2[i] );
|
||
tnode.ImageIndex := 37;
|
||
tnode.SelectedIndex := 38;
|
||
// Add dummy-node, will be replaced by real tables on expanding
|
||
DBTree.Items.AddChild( tnode, DUMMY_NODE_TEXT );
|
||
if ( ActualDatabase = OnlyDBs2[i] ) then
|
||
begin
|
||
tmpSelected := tnode;
|
||
end;
|
||
end;
|
||
|
||
mainform.showstatus( IntToStr( OnlyDBs2.Count ) + ' Databases' );
|
||
tnodehost.Expand( false );
|
||
DBTree.OnChange := DBtreeChange;
|
||
if ( tmpSelected <> nil ) then
|
||
begin
|
||
DBTree.Selected := tmpSelected;
|
||
end
|
||
else
|
||
begin
|
||
DBTree.Selected := tnodehost;
|
||
end;
|
||
DBtreeChange( Self, tnodehost );
|
||
MainForm.ShowStatus( STATUS_MSG_READY, 2 );
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.SelectHost;
|
||
begin
|
||
if (not DBTree.Dragging) and (
|
||
(PageControlMain.ActivePage = tabDatabase) or
|
||
(PageControlMain.ActivePage = tabTable) or
|
||
(PageControlMain.ActivePage = tabData)
|
||
) then PageControlMain.ActivePage := tabHost;
|
||
|
||
tabDatabase.TabVisible := false;
|
||
tabTable.TabVisible := false;
|
||
tabData.TabVisible := false;
|
||
|
||
Caption := Description;
|
||
ActualDatabase := '';
|
||
ActualTable := '';
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.SelectDatabase(db: String);
|
||
begin
|
||
if (not DBTree.Dragging) and (
|
||
(PageControlMain.ActivePage = tabTable) or
|
||
(PageControlMain.ActivePage = tabData)
|
||
) then PageControlMain.ActivePage := tabDatabase;
|
||
|
||
tabDatabase.TabVisible := true;
|
||
tabTable.TabVisible := false;
|
||
tabData.TabVisible := false;
|
||
|
||
ListTables.Items.Clear();
|
||
ListColumns.Items.Clear();
|
||
pnlTableTop.Caption := 'Table-Properties';
|
||
Caption := Description + ' - /' + ActualDatabase;
|
||
ActualDatabase := db;
|
||
ActualTable := '';
|
||
try
|
||
ShowDBProperties( Self );
|
||
except
|
||
// Clear selection which we couldn't satisfy.
|
||
if ( DBtree.Items.Count < 1 ) then
|
||
begin
|
||
DBtree.Selected := nil
|
||
end
|
||
else
|
||
begin
|
||
DBtree.Selected := DBtree.Items[0];
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.SelectTable(db: String; table: String; switchView: boolean = true);
|
||
begin
|
||
dataselected := false;
|
||
ActualDatabase := db;
|
||
ActualTable := table;
|
||
if switchView then ShowTableProperties( Self );
|
||
Caption := Description + ' - /' + ActualDatabase + '/' + ActualTable;
|
||
end;
|
||
|
||
// React on dbtree-clicks
|
||
procedure TMDIChild.DBtreeChange(Sender: TObject; Node: TTreeNode);
|
||
begin
|
||
if ( Node = nil ) then
|
||
begin
|
||
raise Exception.Create( 'Internal badness: No host node in object tree.' );
|
||
end;
|
||
Screen.Cursor := crHourGlass;
|
||
|
||
case ( Node.Level ) of
|
||
0 : // Root / Host chosen
|
||
begin
|
||
SelectHost();
|
||
end;
|
||
1 : // DB chosen
|
||
begin
|
||
SelectDatabase( Node.Text );
|
||
end;
|
||
2 : // Table chosen
|
||
begin
|
||
SelectTable( Node.Parent.Text, Node.Text );
|
||
end;
|
||
end;
|
||
|
||
pcChange( Self );
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
|
||
|
||
{***
|
||
A database-node is about to be expanded:
|
||
Drop the dummy-node and add all tables
|
||
}
|
||
procedure TMDIChild.DBtreeExpanding(Sender: TObject; Node: TTreeNode;
|
||
var AllowExpansion: Boolean);
|
||
var
|
||
i : Integer;
|
||
TableNames : TStringList;
|
||
begin
|
||
if ( ( Node.getFirstChild <> nil ) and ( Node.getFirstChild.Text = DUMMY_NODE_TEXT ) ) then
|
||
begin
|
||
// Drop dummynode
|
||
for i := ( Node.Count - 1 ) downto 0 do
|
||
begin
|
||
Node.Item[i].Delete();
|
||
end;
|
||
|
||
// Get all tables into dbtree
|
||
TableNames := GetCol( 'SHOW TABLES FROM ' + mask( Node.Text ) );
|
||
for i := 0 to ( TableNames.Count - 1 ) do
|
||
begin
|
||
with ( DBTree.Items.AddChild( Node, TableNames[i] ) ) do
|
||
begin
|
||
ImageIndex := 39;
|
||
SelectedIndex := 40;
|
||
end;
|
||
end;
|
||
|
||
// Add tables to syntax-highlighter
|
||
AddUniqueItemsToList( TableNames, SynSQLSyn1.TableNames );
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.viewdata(Sender: TObject);
|
||
var
|
||
sorting : String;
|
||
DropDown : TStringList;
|
||
i : Integer;
|
||
j : Integer;
|
||
Columns : TStringList;
|
||
PrimaryKeyColumns : TStringList;
|
||
reg : TRegistry;
|
||
reg_value : String;
|
||
orderclauses : TStringList;
|
||
columnname : String;
|
||
columnexists : Boolean;
|
||
select_base : String;
|
||
limit : Int64;
|
||
ds : TDataSet;
|
||
conn : TOpenConnProf;
|
||
sl_query : TStringList;
|
||
manualLimit : boolean;
|
||
manualLimitEnd : integer;
|
||
DisplayedColumnsList : TStringList;
|
||
tmp : TDataSet;
|
||
RewriteOrderClause : Boolean;
|
||
begin
|
||
viewingdata := true;
|
||
try
|
||
sl_query := TStringList.Create();
|
||
|
||
// limit number of rows automatically if first time this table is shown
|
||
if ( not dataselected ) then begin
|
||
manualLimit := false;
|
||
manualLimitEnd := -1;
|
||
with TRegistry.Create do begin
|
||
if OpenKey(REGPATH, true) then begin
|
||
if Valueexists('DataLimitEnd') then begin
|
||
manualLimitEnd := ReadInteger('DataLimitEnd');
|
||
if Valueexists('DataLimit') then manualLimit := ReadBool('DataLimit');
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
// limit number of rows fetched according to preferences
|
||
if manualLimit then begin
|
||
// manual limit set in preferences
|
||
limit := manualLimitEnd;
|
||
end else begin
|
||
// no tick in preferences check box - auto-limit:
|
||
// limit number of rows fetched if more than ~ 5 MB of data
|
||
limit := GetCalculatedLimit( ActualTable );
|
||
end;
|
||
|
||
// adjust limit in GUI
|
||
mainform.ToolBarData.Visible := true;
|
||
if ( limit = -1 ) then
|
||
begin
|
||
mainform.CheckBoxLimit.Checked := false;
|
||
end
|
||
else
|
||
begin
|
||
mainform.CheckBoxLimit.Checked := true;
|
||
mainform.EditLimitStart.Text := '0';
|
||
mainform.EditLimitEnd.Text := IntToStr( limit );
|
||
end;
|
||
mainform.Repaint();
|
||
end;
|
||
|
||
// set db-aware-component's properties...
|
||
DBMemo1.DataField := '';
|
||
DBMemo1.DataSource := DataSource1;
|
||
EDBImage1.DataField := '';
|
||
EDBImage1.DataSource := DataSource1;
|
||
|
||
reg := TRegistry.Create();
|
||
reg.OpenKey( REGPATH + '\Servers\' + FConn.Description, true );
|
||
|
||
if ( not dataselected ) then
|
||
begin
|
||
SynMemoFilter.Text := '';
|
||
// Read cached WHERE-clause and set filter
|
||
reg_value := 'WHERECLAUSE_' + ActualDatabase + '.' + ActualTable;
|
||
if ( reg.ValueExists( reg_value ) ) then
|
||
begin
|
||
SynMemoFilter.Text := reg.ReadString( reg_value );
|
||
// Ensure the user can see its previous specified filter
|
||
// in case of an SQL-error, it's important that he can delete it
|
||
tabFilter.tabVisible := true;
|
||
PageControlBottom.ActivePage := tabFilter;
|
||
end;
|
||
end;
|
||
|
||
// Read cached ORDER-clause and set Grid.Sortcolumns
|
||
reg_value := 'ORDERCLAUSE_' + ActualDatabase + '.' + ActualTable;
|
||
sorting := '';
|
||
gridData.SortColumns.Clear();
|
||
if ( reg.ValueExists( reg_value ) ) then
|
||
begin
|
||
orderclauses := explode( ',', reg.ReadString( reg_value ) );
|
||
RewriteOrderClause := False;
|
||
for i := 0 to ( orderclauses.Count - 1 ) do
|
||
begin
|
||
columnname := Trim( Copy( orderclauses[i], 0, LastPos( ' ', orderclauses[i] ) ) );
|
||
columnname := trimc( columnname, '`' );
|
||
columnexists := false;
|
||
|
||
for j := 0 to ( ListColumns.Items.Count - 1 ) do
|
||
begin
|
||
if ( ListColumns.Items[j].Caption = columnname ) then
|
||
begin
|
||
columnexists := true;
|
||
Break;
|
||
end;
|
||
end;
|
||
|
||
if ( not columnexists ) then
|
||
begin
|
||
LogSQL( 'Notice: A stored ORDER-BY clause could not be applied, '+
|
||
'because the column "' + columnname + '" does not exist!');
|
||
RewriteOrderClause := True;
|
||
Continue;
|
||
end;
|
||
|
||
// Add column to order clause
|
||
if sorting <> '' then
|
||
sorting := sorting + ', ';
|
||
sorting := sorting + orderclauses[i];
|
||
|
||
with ( gridData.SortColumns.Add() ) do
|
||
begin
|
||
Fieldname := columnname;
|
||
if ( Copy( orderclauses[i], ( Length( orderclauses[i] ) - 3 ), 4 ) = 'DESC' ) then
|
||
begin
|
||
SortType := stAscending;
|
||
end
|
||
else
|
||
begin
|
||
SortType := stDescending;
|
||
end;
|
||
end;
|
||
end;
|
||
// Old orderclause contained a no longer existing column, so overwrite it
|
||
if RewriteOrderClause then
|
||
begin
|
||
reg.WriteString( reg_value, sorting );
|
||
end;
|
||
end;
|
||
|
||
if ( sorting <> '' ) then
|
||
begin
|
||
sorting := 'ORDER BY ' + sorting;
|
||
// Signal for the user that we applied an ORDER-clause
|
||
btnDataSorting.Font.Color := clRed;
|
||
end
|
||
else
|
||
begin
|
||
btnDataSorting.Font.Color := clWindowText;
|
||
end;
|
||
|
||
MenuLimit.Checked := Mainform.CheckBoxLimit.Checked;
|
||
Columns := TStringList.Create();
|
||
PrimaryKeyColumns := TStringList.Create();
|
||
|
||
if ( ( ActualTable <> '' ) and ( ActualDatabase <> '' ) ) then
|
||
begin
|
||
// Ensure <Table> and <Data> are visible
|
||
tabTable.TabVisible := true;
|
||
tabData.TabVisible := true;
|
||
// Switch to <Data>
|
||
PageControlMain.ActivePage := tabData;
|
||
|
||
MainForm.ShowStatus( 'Retrieving data...', 2, true );
|
||
|
||
// Read columns to display from registry
|
||
with( TRegistry.Create ) do
|
||
begin
|
||
OpenKey( REGPATH + '\Servers\' + FConn.Description, true );
|
||
DisplayedColumnsList := explode( '`', ReadString(REGNAME_DISPLAYEDCOLUMNS + '_' + ActualDatabase + '.' + ActualTable));
|
||
end;
|
||
|
||
// Prepare SELECT statement
|
||
select_base := 'SELECT ';
|
||
// Try to calc the rowcount regardless of a given LIMIT
|
||
// Only needed if the user specified a WHERE-clause
|
||
if (
|
||
( mysql_version >= 40000 ) and
|
||
( Trim( Self.SynMemoFilter.Text ) <> '' )
|
||
) then
|
||
begin
|
||
select_base := select_base + ' SQL_CALC_FOUND_ROWS';
|
||
end;
|
||
// Selected columns
|
||
if DisplayedColumnsList.Count = 0 then
|
||
begin
|
||
select_base := select_base + ' *';
|
||
btnColumnSelection.Font.Color := clBtnText;
|
||
end
|
||
else
|
||
begin
|
||
for i := 0 to DisplayedColumnsList.Count - 1 do
|
||
begin
|
||
select_base := select_base + ' ' + mask(DisplayedColumnsList[i]) + ',';
|
||
end;
|
||
// Cut last comma
|
||
select_base := copy( select_base, 1, Length(select_base)-1 );
|
||
// Signal for the user that we now hide some fields
|
||
btnColumnSelection.Font.Color := clRed;
|
||
end;
|
||
select_base := select_base + ' FROM ' + mask( ActualTable );
|
||
sl_query.Add( select_base );
|
||
// Apply custom WHERE filter
|
||
if ( Trim( Self.SynMemoFilter.Text ) <> '' ) then
|
||
begin
|
||
sl_query.Add( 'WHERE ' + Trim( Self.SynMemoFilter.Text ) );
|
||
end;
|
||
// Apply custom ORDER BY if detected in registry
|
||
if ( sorting <> '' ) then
|
||
begin
|
||
sl_query.Add( sorting );
|
||
end;
|
||
// Apply LIMIT
|
||
if ( mainform.CheckBoxLimit.Checked ) then
|
||
begin
|
||
sl_query.Add('LIMIT ' + mainform.EditLimitStart.Text + ', ' + mainform.EditLimitEnd.Text );
|
||
end;
|
||
try
|
||
conn := FConn;
|
||
conn.MysqlParams.Database := ActualDatabase;
|
||
|
||
// Avoid excessive GUI updates.
|
||
if ( DataSource1.DataSet <> nil ) then
|
||
begin
|
||
DataSource1.DataSet.DisableControls();
|
||
end;
|
||
|
||
// free previous resultset
|
||
tmp := DataSource1.DataSet;
|
||
DataSource1.DataSet := nil;
|
||
FreeAndNil(tmp);
|
||
|
||
// start query (with wait dialog)
|
||
SynMemoFilter.Color := clWindow;
|
||
ds := GetResults(sl_query.Text, false);
|
||
|
||
MainForm.ShowStatus( 'Filling grid with record-data...', 2, true );
|
||
ds.DisableControls();
|
||
DataSource1.DataSet := ds;
|
||
|
||
// Attach After- and Before-Events to the new dataset
|
||
ds.AfterPost := ZQueryGridAfterPost;
|
||
ds.AfterDelete := ZQueryGridAfterPost;
|
||
ds.BeforeClose := ZQueryGridBeforeClose;
|
||
except
|
||
on E:Exception do
|
||
begin
|
||
// Most likely we have a wrong filter-clause when this happens
|
||
LogSQL( E.Message, True );
|
||
// Put the user with his nose onto the wrong filter he specified
|
||
if ( SynMemoFilter.CanFocus ) then
|
||
begin
|
||
SynMemoFilter.SetFocus();
|
||
end;
|
||
SynMemoFilter.Color := $008080FF; // light pink
|
||
MessageDlg( E.Message, mtError, [mbOK], 0 );
|
||
MainForm.ShowStatus( STATUS_MSG_READY, 2 );
|
||
Screen.Cursor := crDefault;
|
||
Exit;
|
||
end;
|
||
end;
|
||
|
||
MainForm.ShowStatus( STATUS_MSG_READY, 2 );
|
||
|
||
for i := 0 to ( ListColumns.Items.Count - 1 ) do
|
||
begin
|
||
Columns.Add( ListColumns.Items[i].Caption );
|
||
|
||
// give all enum-fields a PickList with its Items
|
||
if ( StrCmpBegin( 'enum', ListColumns.Items[i].SubItems[0]) ) then
|
||
begin
|
||
DropDown := explode( ''',''', getEnumValues( ListColumns.Items[i].SubItems[0] ) );
|
||
for j := 0 to ( DropDown.Count - 1 ) do
|
||
begin
|
||
DropDown[j] := trimc( DropDown[j], '''' );
|
||
end;
|
||
|
||
for j := 0 to ( gridData.Columns.Count - 1 ) do
|
||
begin
|
||
if ( gridData.Columns[j].FieldName = ListColumns.Items[i].Caption ) then
|
||
begin
|
||
gridData.Columns[j].PickList := DropDown;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
// make PK-columns = fsBold
|
||
for j := 0 to ( gridData.Columns.Count - 1 ) do
|
||
begin
|
||
if (
|
||
( gridData.Columns[j].FieldName = ListColumns.Items[i].Caption ) and
|
||
( ListColumns.Items[i].ImageIndex = 26 )
|
||
) then
|
||
begin
|
||
PrimaryKeyColumns.Add( ListColumns.Items[i].Caption );
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
for j := 0 to ( gridData.Columns.Count - 1 ) do
|
||
begin
|
||
// for letting NULLs being inserted into "NOT NULL" fields
|
||
// in mysql5+, the server rejects inserts with NULLs in NOT NULL-fields,
|
||
// so the Required-check on client-side is not needed at any time
|
||
ds.Fields[j].Required := false;
|
||
|
||
// set column-width
|
||
if (
|
||
(Mainform.DefaultColWidth <> 0) and
|
||
(gridData.Columns[j].Width > Mainform.DefaultColWidth)
|
||
) then
|
||
begin
|
||
gridData.Columns[j].Width := Mainform.DefaultColWidth;
|
||
end;
|
||
|
||
// make PK-columns = fsBold
|
||
for i := 0 to ( PrimaryKeyColumns.Count - 1 ) do
|
||
begin
|
||
if (PrimaryKeyColumns[i] = gridData.Columns[j].Fieldname) then
|
||
begin
|
||
gridData.Columns[j].Font.Style := gridData.Columns[j].Font.Style + [fsBold];
|
||
gridData.Columns[j].Color := $02EEEEEE;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
DisplayRowCountStats(ds);
|
||
dataselected := true;
|
||
viewingdata := false;
|
||
ds.EnableControls();
|
||
pcChange(self);
|
||
end;
|
||
|
||
Screen.Cursor := crDefault;
|
||
FreeAndNil (sl_query);
|
||
finally
|
||
viewingdata := false;
|
||
end;
|
||
end;
|
||
|
||
|
||
{***
|
||
Calculate + display total rowcount and found rows matching to filter
|
||
in data-tab
|
||
}
|
||
procedure TMDIChild.DisplayRowCountStats(ds: TDataSet);
|
||
var
|
||
rows_matching : Int64; // rows matching to where-filter
|
||
rows_total : Int64; // total rowcount
|
||
begin
|
||
if ( ActiveGrid = gridQuery ) then
|
||
begin
|
||
Exit;
|
||
end;
|
||
|
||
// Get rowcount
|
||
rows_total := StrToInt64( GetVar( 'SELECT COUNT(*) FROM ' +
|
||
mask( ActualTable ), 0 ) );
|
||
|
||
lblDataTop.Caption := ActualDatabase + '.' + ActualTable + ': ' +
|
||
FormatNumber( rows_total ) + ' records total';
|
||
|
||
{***
|
||
@note: FOUND_ROWS() gives us a correct number, but this number
|
||
belongs in most cases to a different query than the previous SELECT,
|
||
because Zeos does some automagically
|
||
SHOW TABLES LIKE '' and
|
||
SHOW COLUMNS LIKE '%'
|
||
between the above SELECT and a following "SELECT FOUND_ROWS()".
|
||
This problem has been introduced with the fix of
|
||
a faulty caching-mechanism in Zeos (rev 312).
|
||
and those queries seem to reset the FOUND_ROWS() since MySQL 5.0
|
||
The workaround is to store FOUND_ROWS() immediately after query-execution
|
||
in a variable, which we are selecting here.
|
||
@see TZMySQLResultSet:Create
|
||
@see TZMySQLResultSet:Open
|
||
}
|
||
if Trim( SynMemoFilter.Text ) <> '' then
|
||
begin
|
||
if mysql_version >= 40000 then
|
||
begin
|
||
rows_matching := StrToInt64Def(GetVar('SELECT @found_rows'), 0);
|
||
end
|
||
else
|
||
begin
|
||
rows_matching := ActiveGrid.DataSource.DataSet.RecordCount;
|
||
end;
|
||
end
|
||
else
|
||
begin
|
||
rows_matching := rows_total;
|
||
end;
|
||
|
||
if (
|
||
( rows_matching <> rows_total ) and
|
||
( Trim( SynMemoFilter.Text ) <> '' )
|
||
) then
|
||
begin
|
||
lblDataTop.Caption := lblDataTop.Caption + ', ' + FormatNumber(rows_matching) +
|
||
' matching to filter';
|
||
end;
|
||
|
||
if (
|
||
( rows_matching = rows_total ) and
|
||
( Trim( SynMemoFilter.Text ) <> '')
|
||
) then
|
||
begin
|
||
lblDataTop.Caption := lblDataTop.Caption + ', filter matches all records';
|
||
end;
|
||
|
||
if (
|
||
( mainform.CheckBoxLimit.Checked ) and
|
||
( rows_matching > StrToIntDef( mainform.EditLimitEnd.Text, 0 ) )
|
||
) then
|
||
begin
|
||
lblDataTop.Caption := lblDataTop.Caption + ', limited to ' +
|
||
FormatNumber( ds.RecordCount );
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.WaitForQueryCompletion(WaitForm: TForm);
|
||
begin
|
||
debug( 'Waiting for query to complete.' );
|
||
WaitForm.ShowModal();
|
||
debug( 'Query complete.' );
|
||
end;
|
||
|
||
|
||
{***
|
||
Occurs when active tab has changed.
|
||
}
|
||
procedure TMDIChild.pcChange(Sender: TObject);
|
||
var
|
||
dummy : Boolean;
|
||
begin
|
||
tabFilter.tabVisible := (PageControlMain.ActivePage = tabData);
|
||
|
||
Mainform.ExecuteQuery.Enabled := PageControlMain.ActivePage = tabQuery;
|
||
Mainform.ExecuteSelection.Enabled := PageControlMain.ActivePage = tabQuery;
|
||
Mainform.ExecuteLine.Enabled := PageControlMain.ActivePage = tabQuery;
|
||
if (PageControlMain.ActivePage = tabData) and (not dataselected) then
|
||
viewdata(self);
|
||
if PageControlMain.ActivePage = tabQuery then
|
||
begin
|
||
if ActualDatabase <> '' then
|
||
pnlQueryTop.Caption := 'SQL-Query on Database ' + ActualDatabase + ':'
|
||
else
|
||
pnlQueryTop.Caption := 'SQL-Query on Host ' + FConn.MysqlParams.Host + ':';
|
||
// Manually invoke OnChange event of tabset to fill helper list with data
|
||
tabsetQueryHelpers.OnChange( Sender, tabsetQueryHelpers.TabIndex, dummy);
|
||
end;
|
||
|
||
// Move focus to relevant controls in order for them to receive keyboard events.
|
||
if PageControlMain.ActivePage = tabDatabase then ListTables.SetFocus;
|
||
if PageControlMain.ActivePage = tabTable then ListColumns.SetFocus;
|
||
if PageControlMain.ActivePage = tabData then gridData.SetFocus;
|
||
|
||
// Ensure controls are in a valid state
|
||
ValidateControls;
|
||
|
||
// Show processlist if it's visible now but empty yet
|
||
if ListProcesses.Items.Count = 0 then
|
||
ShowProcessList( self );
|
||
end;
|
||
|
||
|
||
{***
|
||
Ensures that we're connected to the currently selected database.
|
||
}
|
||
procedure TMDIChild.EnsureActiveDatabase;
|
||
begin
|
||
// Blank = current database undefined
|
||
if ActualDatabase = '' then Exit;
|
||
if (FMysqlConn.Connection.Database <> ActualDatabase) or UserQueryFired then begin
|
||
FMysqlConn.Connection.Database := ActualDatabase;
|
||
ExecUseQuery(ActualDatabase, false, false);
|
||
UserQueryFired := false;
|
||
end;
|
||
end;
|
||
|
||
|
||
{***
|
||
Look for list of tables for current database in cache.
|
||
Retrieve from server if necessary.
|
||
@return TDataSet The cached list of tables for the active database.
|
||
}
|
||
function TMDIChild.FetchActiveDbTableList: TDataSet;
|
||
var
|
||
ds: TDataSet;
|
||
OldCursor: TCursor;
|
||
begin
|
||
if CachedTableLists.IndexOf(ActualDatabase) = -1 then begin
|
||
// Not in cache, load table list.
|
||
OldCursor := Screen.Cursor;
|
||
Screen.Cursor := crHourGlass;
|
||
MainForm.ShowStatus('Displaying tables from ' + ActualDatabase + '...', 2, true);
|
||
if mysql_version >= 32300 then begin
|
||
ds := GetResults('SHOW TABLE STATUS', false, false);
|
||
end else begin
|
||
// contains table names, nothing else.
|
||
ds := GetResults('SHOW TABLES', false, false);
|
||
// could clean up data (rename first column to 'Name') and
|
||
// and add row counters to data set as a new field by using
|
||
// SELECT COUNT(*), but that would potentially be rather slow.
|
||
end;
|
||
CachedTableLists.AddObject(ActualDatabase, ds);
|
||
Screen.Cursor := OldCursor;
|
||
end;
|
||
Result := TDataSet(CachedTableLists.Objects[CachedTableLists.IndexOf(ActualDatabase)]);
|
||
Result.First;
|
||
end;
|
||
|
||
|
||
{***
|
||
Nukes cached table list for active database, then refreshes it.
|
||
@return TDataSet The newly cached list of tables for the active database.
|
||
}
|
||
function TMDIChild.RefreshActiveDbTableList: TDataSet;
|
||
var
|
||
idx: Integer;
|
||
o: TObject;
|
||
begin
|
||
idx := CachedTableLists.IndexOf(ActualDatabase);
|
||
if idx > -1 then begin
|
||
o := CachedTableLists.Objects[idx];
|
||
FreeAndNil(o);
|
||
CachedTableLists.Delete(idx);
|
||
end;
|
||
Result := FetchActiveDbTableList;
|
||
end;
|
||
|
||
|
||
{***
|
||
Nukes the table list cache.
|
||
}
|
||
procedure TMDIChild.ClearAllTableLists;
|
||
var
|
||
idx: Integer;
|
||
o: TObject;
|
||
begin
|
||
for idx := 0 to CachedTableLists.Count - 1 do begin
|
||
o := CachedTableLists.Objects[idx];
|
||
FreeAndNil(o);
|
||
end;
|
||
CachedTableLists.Clear;
|
||
end;
|
||
|
||
|
||
{***
|
||
Updates tree with table list for currently selected database (or table).
|
||
}
|
||
procedure TMDIChild.UpdateTreeTableList;
|
||
var
|
||
tndb : TTreenode;
|
||
ds: TDataSet;
|
||
t, u: Integer;
|
||
begin
|
||
if DBTree.Selected.Level = 1 then tndb := DBTree.Selected
|
||
else if DBTree.Selected.Level = 2 then tndb := DBTree.Selected.Parent
|
||
else exit;
|
||
|
||
// get all tables back into dbtree
|
||
for u:=tndb.Count-1 downto 0 do tndb.Item[u].delete;
|
||
ds := FetchActiveDbTableList;
|
||
for t:=0 to ds.RecordCount-1 do
|
||
begin
|
||
with DBtree.Items.AddChild(tndb, ds.Fields[0].AsString) do
|
||
begin
|
||
ImageIndex := 39;
|
||
selectedIndex := 40;
|
||
end;
|
||
ds.Next;
|
||
end;
|
||
end;
|
||
|
||
|
||
{ Show tables and their properties on the tabsheet "Database" }
|
||
procedure TMDIChild.ShowDBProperties(Sender: TObject);
|
||
var
|
||
n : TListItem;
|
||
i,j,k : Integer;
|
||
bytes : Extended;
|
||
menuitem : TMenuItem;
|
||
TablelistColumns: TStringList;
|
||
column : TListColumn;
|
||
ds : TDataSet;
|
||
begin
|
||
// DB-Properties
|
||
Screen.Cursor := crHourGlass;
|
||
MainForm.ShowStatus( 'Reading from database ' + ActualDatabase + '...', 2, true );
|
||
Mainform.ButtonDropDatabase.Hint := 'Drop Database...|Drop Database ' + ActualDatabase + '...';
|
||
|
||
// Refresh chosen in table list?
|
||
if sender = menurefresh then RefreshActiveDbTableList;
|
||
|
||
// Populate database subitems (= tables) in tree.
|
||
UpdateTreeTableList;
|
||
|
||
ds := FetchActiveDbTableList;
|
||
try
|
||
// Generate items for popupDbGridHeader
|
||
for i:=popupDbGridHeader.Items.Count-1 downto 2 do
|
||
popupDbGridHeader.Items.Delete( i );
|
||
with TRegistry.Create do
|
||
begin
|
||
openkey( REGPATH + '\Servers\' + FConn.Description, true );
|
||
if ValueExists( 'TablelistDefaultColumns' ) then
|
||
popupDbGridHeader.Items[0].Checked := ReadBool( 'TablelistDefaultColumns' );
|
||
if ValueExists( 'TablelistColumns' ) then
|
||
TablelistColumns := Explode( ',', ReadString( 'TablelistColumns' ) )
|
||
else
|
||
TablelistColumns := TStringList.Create;
|
||
free;
|
||
end;
|
||
for i:=0 to ds.FieldCount-1 do
|
||
begin
|
||
menuitem := TMenuItem.Create( self );
|
||
menuitem.Caption := ds.Fields[i].Fieldname;
|
||
menuitem.Tag := 2;
|
||
menuitem.OnClick := MenuTablelistColumnsClick;
|
||
if i=0 then
|
||
begin
|
||
menuitem.Enabled := false; // tablename should always be kept
|
||
menuitem.Checked := true;
|
||
end
|
||
else if TablelistColumns.Count > 0 then
|
||
begin
|
||
menuitem.Checked := TablelistColumns.IndexOf( menuitem.Caption ) > -1;
|
||
end;
|
||
popupDbGridHeader.Items.Add( menuitem );
|
||
end;
|
||
|
||
ListTables.Items.BeginUpdate;
|
||
ListTables.Items.Clear;
|
||
ListTables.Columns.BeginUpdate;
|
||
ListTables.Columns.Clear;
|
||
column := ListTables.Columns.Add;
|
||
column.Caption := 'Table';
|
||
column.Width := -1;
|
||
if popupDbGridHeader.Items[0].Checked then
|
||
begin // Default columns - initialize column headers
|
||
column := ListTables.Columns.Add;
|
||
column.Caption := 'Records';
|
||
column.Alignment := taRightJustify;
|
||
column.Width := 80;
|
||
|
||
column := ListTables.Columns.Add;
|
||
column.Caption := 'Size';
|
||
column.Alignment := taRightJustify;
|
||
column.Width := -1;
|
||
|
||
column := ListTables.Columns.Add;
|
||
column.Caption := 'Created';
|
||
column.Width := -1;
|
||
|
||
column := ListTables.Columns.Add;
|
||
column.Caption := 'Updated';
|
||
column.Width := -1;
|
||
|
||
column := ListTables.Columns.Add;
|
||
column.Caption := 'Engine';
|
||
column.Width := -1;
|
||
|
||
column := ListTables.Columns.Add;
|
||
column.Caption := 'Comment';
|
||
column.Width := -1;
|
||
end;
|
||
for i:=0 to TablelistColumns.Count-1 do
|
||
begin
|
||
column := ListTables.Columns.Add;
|
||
column.Caption := TablelistColumns[i];
|
||
column.Width := -1;
|
||
column.MinWidth := 50;
|
||
column.Autosize := true;
|
||
end;
|
||
|
||
for i := 1 to ds.RecordCount do
|
||
begin
|
||
n := ListTables.Items.Add;
|
||
n.ImageIndex := 39;
|
||
// Table
|
||
n.Caption := ds.Fields[0].AsString;
|
||
if (mysql_version >= 32300) and popupDbGridHeader.Items[0].Checked then
|
||
begin // Default columns
|
||
// Records
|
||
n.SubItems.Add( FormatNumber( ds.FieldByName('Rows').AsFloat ) );
|
||
// Size: Data_length + Index_length
|
||
bytes := ds.FieldByName('Data_length').AsFloat + ds.FieldByName('Index_length').AsFloat;
|
||
n.SubItems.Add( FormatNumber( bytes / 1024 + 1 ) + ' KB');
|
||
// Created:
|
||
if not ds.FieldByName('Create_time').IsNull then
|
||
n.SubItems.Add( ds.FieldByName('Create_time').AsString )
|
||
else
|
||
n.SubItems.Add('N/A');
|
||
|
||
// Updated:
|
||
if not ds.FieldByName('Update_time').IsNull then
|
||
n.SubItems.Add( ds.FieldByName('Update_time').AsString )
|
||
else
|
||
n.SubItems.Add('N/A');
|
||
|
||
// Type
|
||
if ds.FindField('Type')<>nil then
|
||
n.SubItems.Add( ds.FieldByName('Type').AsString )
|
||
else if ds.FindField('Engine')<>nil then
|
||
n.SubItems.Add( ds.FieldByName('Engine').AsString )
|
||
else
|
||
n.SubItems.Add('');
|
||
|
||
// Comment
|
||
n.SubItems.Add( ds.FieldByName('Comment').AsString );
|
||
end;
|
||
for j:=0 to TablelistColumns.Count-1 do
|
||
begin
|
||
for k:=0 to ds.FieldCount-1 do
|
||
begin
|
||
if TablelistColumns[j] = ds.Fields[k].FieldName then
|
||
begin
|
||
if ds.Fields[k].DataType in [ftInteger, ftSmallint, ftWord, ftFloat, ftWord ] then
|
||
begin
|
||
// Number
|
||
// TODO: doesn't match any column
|
||
ListTables.Columns[n.SubItems.Count].Alignment := taRightJustify;
|
||
n.SubItems.Add( FormatNumber( ds.Fields[k].AsFloat ) );
|
||
end
|
||
else
|
||
// String
|
||
n.SubItems.Add( ds.Fields[k].AsString );
|
||
end;
|
||
end;
|
||
end;
|
||
ds.Next;
|
||
end;
|
||
mainform.showstatus(ActualDatabase + ': ' + IntToStr(ds.RecordCount) +' table(s)');
|
||
finally
|
||
ListTables.Columns.EndUpdate;
|
||
ListTables.Items.EndUpdate;
|
||
// Remove existing column-sort-images
|
||
// (TODO: auomatically invoke this method in TSortListView itself)
|
||
ListTables.ClearSortColumnImages;
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
Screen.Cursor := crHourglass;
|
||
|
||
pnlDatabaseTop.Caption := 'Database ' + ActualDatabase + ': ' + IntToStr(ListTables.Items.Count) + ' table(s)';
|
||
MainForm.ShowStatus( STATUS_MSG_READY, 2 );
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
|
||
|
||
|
||
{ Show columns of selected table, indicate indexed columns by certain icons }
|
||
procedure TMDIChild.ShowTableProperties(Sender: TObject);
|
||
var
|
||
i,j : Integer;
|
||
n : TListItem;
|
||
tn, tndb : TTreeNode;
|
||
isFulltext : Boolean;
|
||
ds : TDataSet;
|
||
dummy: Boolean;
|
||
begin
|
||
// Table-Properties
|
||
|
||
Screen.Cursor := crHourGlass;
|
||
|
||
if (not DBTree.Dragging) and (
|
||
(PageControlMain.ActivePage = tabHost) or
|
||
(PageControlMain.ActivePage = tabDatabase)
|
||
) then PageControlMain.ActivePage := tabTable;
|
||
|
||
tabDatabase.TabVisible := true;
|
||
tabTable.TabVisible := true;
|
||
tabData.TabVisible := true;
|
||
|
||
pnlTableTop.Caption := 'Table-Properties for ' + ActualDatabase + ': ' + ActualTable;
|
||
|
||
// set current node in DBTree to ActualTable:
|
||
with DBTree do begin
|
||
if Selected.Level = 1 then tndb := Selected
|
||
else if Selected.Level = 2 then tndb := Selected.Parent
|
||
else if Selected.Level = 3 then tndb := Selected.Parent.Parent
|
||
else exit;
|
||
end;
|
||
tn := tndb.getFirstChild;
|
||
for i:=0 to tndb.Count -1 do
|
||
begin
|
||
if ActualTable = tn.Text then
|
||
begin
|
||
DBTree.Selected := tn; // select table
|
||
break;
|
||
end;
|
||
tn := tndb.GetNextChild(tn);
|
||
end;
|
||
|
||
// and the other way around: set current listitem in tableslist to ActualTable:
|
||
for i:=0 to ListTables.items.Count -1 do
|
||
begin
|
||
// ListTables.Items[i].Selected := (ActualTable = ListTables.Items[i].Caption); // ListTablesOnChange will be called by this line
|
||
end;
|
||
|
||
MainForm.ShowStatus( 'Reading table properties...', 2, true );
|
||
ListColumns.Items.BeginUpdate;
|
||
ListColumns.Items.Clear;
|
||
Try
|
||
ds := GetResults( 'SHOW COLUMNS FROM ' + mask(ActualTable), false );
|
||
|
||
for i:=1 to ds.RecordCount do
|
||
begin
|
||
n := ListColumns.Items.Add;
|
||
n.ImageIndex := 62;
|
||
|
||
n.Caption := ds.FieldByName('Field').AsString;
|
||
n.Subitems.Add( ds.FieldByName('Type').AsString );
|
||
if lowercase( ds.FieldByName('Null').AsString ) = 'yes' then
|
||
n.Subitems.Add('Yes')
|
||
else n.Subitems.Add('No');
|
||
n.Subitems.Add( ds.FieldByName('Default').AsString );
|
||
n.Subitems.Add( ds.FieldByName('Extra').AsString );
|
||
ds.Next;
|
||
end;
|
||
|
||
// Manually invoke OnChange event of tabset to fill helper list with data
|
||
if tabsetQueryHelpers.TabIndex = 0 then
|
||
tabsetQueryHelpers.OnChange( Sender, tabsetQueryHelpers.TabIndex, dummy);
|
||
|
||
{*
|
||
TODO: Create drag-drop box next to query window with these columns.
|
||
// add fields to dbtree for drag'n dropping purpose
|
||
if not DBTree.Selected.HasChildren then
|
||
begin
|
||
ds.First;
|
||
for i:=1 to ds.RecordCount do begin
|
||
tn := DBtree.Items.AddChild(Dbtree.Selected, ds.FieldByName('Field').AsString );
|
||
if ds.FieldByName('Key').AsString = 'PRI' then
|
||
tn.ImageIndex := 26
|
||
else
|
||
tn.ImageIndex := 62;
|
||
tn.SelectedIndex := tn.ImageIndex;
|
||
ds.Next;
|
||
end;
|
||
end;
|
||
*}
|
||
|
||
Screen.Cursor := crHourglass;
|
||
ds := GetResults( 'SHOW KEYS FROM ' + mask(ActualTable) );
|
||
for i:=1 to ds.RecordCount do
|
||
begin
|
||
// primary key
|
||
if ds.FieldByName('Key_name').AsString = 'PRIMARY' then
|
||
begin
|
||
for j:=0 to ListColumns.Items.Count-1 do
|
||
begin
|
||
if ds.FieldByName('Column_name').AsString = ListColumns.Items[j].Caption then
|
||
begin
|
||
ListColumns.Items[j].ImageIndex := 26;
|
||
break;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
// index
|
||
if (ds.FieldByName('Key_name').AsString <> 'PRIMARY')
|
||
and (ds.FieldByName('Non_unique').AsString = '1') then
|
||
begin
|
||
for j:=0 to ListColumns.Items.Count-1 do
|
||
begin
|
||
if ds.FieldByName('Column_name').AsString = ListColumns.Items[j].Caption then
|
||
begin
|
||
if ListColumns.Items[j].ImageIndex = 62 then // Only apply if it's the default image
|
||
ListColumns.Items[j].ImageIndex := 63;
|
||
break;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
// unique
|
||
if (ds.FieldByName('Key_name').AsString <> 'PRIMARY') and (ds.FieldByName('Non_unique').AsString = '0') then
|
||
begin
|
||
for j:=0 to ListColumns.Items.Count-1 do
|
||
begin
|
||
if ds.FieldByName('Column_name').AsString = ListColumns.Items[j].Caption then
|
||
begin
|
||
if ListColumns.Items[j].ImageIndex = 62 then // Only apply if it's the default image
|
||
ListColumns.Items[j].ImageIndex := 64;
|
||
break;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
// column is part of a fulltext key, available since 3.23.xx
|
||
if mysql_version < 40002 then
|
||
isFulltext := (ds.FieldByName('Comment').AsString = 'FULLTEXT')
|
||
else
|
||
isFulltext := (ds.FieldByName('Index_type').AsString = 'FULLTEXT');
|
||
if (ds.FieldByName('Key_name').AsString <> 'PRIMARY') and isFulltext then
|
||
begin
|
||
for j:=0 to ListColumns.Items.Count-1 do
|
||
begin
|
||
if ds.FieldByName('Column_name').AsString = ListColumns.Items[j].Caption then
|
||
begin
|
||
if ListColumns.Items[j].ImageIndex = 62 then // Only apply if it's the default image
|
||
ListColumns.Items[j].ImageIndex := 65;
|
||
break;
|
||
end;
|
||
end;
|
||
end;
|
||
ds.Next;
|
||
end;
|
||
|
||
finally
|
||
ListColumns.Items.EndUpdate;
|
||
// Remove existing column-sort-images
|
||
// (TODO: auomatically invoke this method in TSortListView itself)
|
||
ListColumns.ClearSortColumnImages;
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
|
||
MainForm.ShowStatus( STATUS_MSG_READY, 2, false );
|
||
MainForm.showstatus(ActualDatabase + ': '+ ActualTable + ': ' + IntToStr(ListColumns.Items.count) +' field(s)');
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
|
||
|
||
{***
|
||
Execute a query and return a resultset
|
||
The currently active connection is used
|
||
|
||
@param String The single SQL-query to be executed on the server
|
||
}
|
||
function TMDIChild.ExecuteQuery(query: String): TDataSet;
|
||
begin
|
||
result := GetResults(query, false, false);
|
||
end;
|
||
|
||
|
||
{***
|
||
Execute a query without returning a resultset
|
||
The currently active connection is used
|
||
|
||
@param String The single SQL-query to be executed on the server
|
||
}
|
||
procedure TMDIChild.ExecuteNonQuery(SQLQuery: String);
|
||
begin
|
||
ExecUpdateQuery(SQLQuery);
|
||
end;
|
||
|
||
|
||
|
||
{***
|
||
Selection in ListTables is changing
|
||
Used to change the iconindex of selected items and to trigger ValidateControls
|
||
}
|
||
procedure TMDIChild.ListTablesSelectItem(Sender: TObject; Item: TListItem;
|
||
Selected: Boolean);
|
||
var
|
||
i: Integer;
|
||
begin
|
||
for i := 0 to ListTables.Items.Count - 1 do
|
||
ListTables.Items[i].ImageIndex := 39;
|
||
if (ListTables.Selected <> nil) then ListTables.Selected.ImageIndex := 40;
|
||
ValidateControls;
|
||
end;
|
||
|
||
|
||
|
||
{***
|
||
Enable/disable various buttons and menu items.
|
||
Invoked when
|
||
- active sheet changes
|
||
- highlighted database changes
|
||
- ChildWindow is activated / deactivated
|
||
@param Boolean Is this form activated in terms of our remaining MDI-functionality?
|
||
Only used with False by FormDeactivate-procedure to
|
||
deactivate various controls on mainform
|
||
}
|
||
procedure TMDIChild.ValidateControls( FrmIsFocussed: Boolean = true );
|
||
var
|
||
tableSelected : Boolean;
|
||
inDataOrQueryTab, inDataOrQueryTabNotEmpty : Boolean;
|
||
begin
|
||
// Make sure that main menu "drop table" affects table selected in tree view,
|
||
// not table (now invisibly) selected on the database grid.
|
||
if (PageControlMain.ActivePage <> tabDatabase) then ListTables.Selected := nil;
|
||
|
||
tableSelected := (ListTables.Selected <> nil) and FrmIsFocussed;
|
||
btnDbProperties.Enabled := tableSelected;
|
||
menuproperties.Enabled := tableSelected;
|
||
btnDbInsertRecord.Enabled := tableSelected;
|
||
menuinsert.Enabled := tableSelected;
|
||
btnDbViewData.Enabled := tableSelected;
|
||
menuviewdata.Enabled := tableSelected;
|
||
btnDbEmptyTable.Enabled := tableSelected;
|
||
menuemptytable.Enabled := tableSelected;
|
||
MenuAdvancedProperties.Enabled := tableSelected;
|
||
MenuRenameTable.Enabled := tableSelected;
|
||
MenuChangeType1.Enabled := tableSelected;
|
||
MenuChangeType2.Enabled := tableSelected;
|
||
MenuChangeType3.Enabled := tableSelected;
|
||
MenuChangeType4.Enabled := tableSelected;
|
||
MenuChangeType5.Enabled := tableSelected;
|
||
MenuChangeTypeOther.Enabled := tableSelected;
|
||
Mainform.CopyTable.Enabled := tableSelected;
|
||
MenuTableComment.Enabled := tableSelected;
|
||
MenuOptimize.Enabled := tableSelected;
|
||
MenuCheck.Enabled := tableSelected;
|
||
MenuAnalyze.Enabled := tableSelected;
|
||
MenuRepair.Enabled := tableSelected;
|
||
|
||
MainForm.ButtonDropDatabase.Enabled := (ActualDatabase <> '') and FrmIsFocussed;
|
||
MainForm.DropTable.Enabled := tableSelected or ((PageControlMain.ActivePage <> tabDatabase) and (ActualTable <> '') and FrmIsFocussed);
|
||
MainForm.ButtonCreateTable.Enabled := (ActualDatabase <> '') and FrmIsFocussed;
|
||
MainForm.ButtonImportTextFile.Enabled := (mysql_version >= 32206) and FrmIsFocussed;
|
||
MainForm.MenuImportTextFile.Enabled := MainForm.ButtonImportTextFile.Enabled;
|
||
|
||
with MainForm do
|
||
begin
|
||
ButtonRefresh.Enabled := FrmIsFocussed;
|
||
ButtonReload.Enabled := FrmIsFocussed;
|
||
ExportTables.Enabled := FrmIsFocussed;
|
||
ButtonImportTextfile.Enabled := FrmIsFocussed;
|
||
ButtonCreateTable.Enabled := FrmIsFocussed;
|
||
ButtonCreateDatabase.Enabled := FrmIsFocussed;
|
||
ButtonDropDatabase.Enabled := false;
|
||
MenuRefresh.Enabled := FrmIsFocussed;
|
||
MenuExport.Enabled := FrmIsFocussed;
|
||
MenuImportTextFile.Enabled := FrmIsFocussed;
|
||
MenuCreateTable.Enabled := FrmIsFocussed;
|
||
MenuCreateDatabase.Enabled := FrmIsFocussed;
|
||
MenuDropDatabase.Enabled := FrmIsFocussed;
|
||
DropTable.Enabled := FrmIsFocussed;
|
||
LoadSQL.Enabled := FrmIsFocussed;
|
||
MenuFlushHosts.Enabled := FrmIsFocussed;
|
||
MenuFlushLogs.Enabled := FrmIsFocussed;
|
||
FlushUserPrivileges1.Enabled := FrmIsFocussed;
|
||
MenuFlushTables.Enabled := FrmIsFocussed;
|
||
MenuFlushTableswithreadlock.Enabled := FrmIsFocussed;
|
||
MenuFlushStatus.Enabled := FrmIsFocussed;
|
||
UserManager.Enabled := FrmIsFocussed;
|
||
Diagnostics.Enabled := FrmIsFocussed;
|
||
InsertFiles.Enabled := FrmIsFocussed;
|
||
{***
|
||
Activate export-options if we're on Data- or Query-tab
|
||
PrintList should only be active if we're focussing one of the ListViews,
|
||
at least as long we are not able to print DBGrids
|
||
@see Issue 1686582
|
||
}
|
||
inDataOrQueryTab := FrmIsFocussed and ((PageControlMain.ActivePage = tabData) or (PageControlMain.ActivePage = tabQuery));
|
||
PrintList.Enabled := (not inDataOrQueryTab) and FrmIsFocussed;
|
||
{***
|
||
@note ansgarbecker, 2007-31-03
|
||
1. For data-tab-queries (threaded queries) the TDatasource does *not* have
|
||
an linked TDataset/TZquery in case of a SQL-error. To avoid an AV with
|
||
NIL-reference we have to first check if the Dataset is existant - if not,
|
||
assume the datagrid is empty.
|
||
2. For query-tab-queries (not threaded) the TZQuery is linked
|
||
to the TDatasource and we can safely ask for TDataset.IsEmpty without
|
||
causing a NIL-reference.
|
||
@todo use the same threaded process for both datagrid and query-grid
|
||
}
|
||
inDataOrQueryTabNotEmpty := inDataOrQueryTab and
|
||
not (
|
||
(getActiveGrid.DataSource.DataSet = nil)
|
||
or getActiveGrid.DataSource.DataSet.IsEmpty
|
||
);
|
||
Copy2CSV.Enabled := inDataOrQueryTabNotEmpty;
|
||
CopyHTMLtable.Enabled := inDataOrQueryTabNotEmpty;
|
||
Copy2XML.Enabled := inDataOrQueryTabNotEmpty;
|
||
ExportData.Enabled := inDataOrQueryTabNotEmpty;
|
||
HTMLView.Enabled := inDataOrQueryTabNotEmpty;
|
||
Self.Delete1.Enabled := inDataOrQueryTabNotEmpty; // Menuitem in popupDataGrid ("Delete record(s)")
|
||
ToolBarData.visible := (PageControlMain.ActivePage = tabData);
|
||
if FrmIsFocussed then
|
||
DBNavigator1.DataSource := DataSource1;
|
||
btnSQLHelp.Enabled := (mysql_version >= 40100) and FrmIsFocussed;
|
||
menuSQLHelp.Enabled := btnSQLHelp.Enabled and FrmIsFocussed;
|
||
|
||
if not FrmIsFocussed then
|
||
begin
|
||
MainForm.showstatus('', 1); // empty connected_time
|
||
end;
|
||
tabBlobEditor.tabVisible := inDataOrQueryTab;
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.ShowTableData(table: string);
|
||
begin
|
||
SelectTable(ActualDatabase, table, false);
|
||
PageControlMain.ActivePage := tabData;
|
||
viewdata(self);
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.ShowTable(Sender: TObject);
|
||
var
|
||
i : Integer;
|
||
tn, tndb : TTreeNode;
|
||
begin
|
||
if DBTree.Selected.Level = 1 then tndb := DBTree.Selected
|
||
else if DBTree.Selected.Level = 2 then tndb := DBTree.Selected.Parent
|
||
else exit;
|
||
|
||
tn := tndb.getFirstChild;
|
||
for i:=0 to tndb.Count -1 do begin
|
||
if ListTables.Selected.Caption = tn.Text then
|
||
begin
|
||
DBTree.Selected := tn;
|
||
ShowTableData(tn.Text);
|
||
break;
|
||
end;
|
||
tn := tndb.GetNextChild(tn);
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.EmptyTable(Sender: TObject);
|
||
var
|
||
t : TStringList;
|
||
i : Integer;
|
||
begin
|
||
// Empty Table(s)
|
||
if ListTables.SelCount = 0 then
|
||
exit;
|
||
t := TStringlist.Create;
|
||
with ListTables do
|
||
for i:=0 to Items.count-1 do
|
||
if Items[i].Selected then
|
||
t.add(Items[i].Caption);
|
||
|
||
if MessageDlg('Empty ' + IntToStr(t.count) + ' Table(s) ?' + crlf + '(' + implodestr(', ', t) + ')', mtConfirmation, [mbok,mbcancel], 0) <> mrok then
|
||
exit;
|
||
|
||
Screen.Cursor := crSQLWait;
|
||
for i:=0 to t.count-1 do
|
||
ExecUpdateQuery( 'DELETE FROM ' + mask(t[i]) );
|
||
ShowDBProperties(self);
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.DropDB(Sender: TObject);
|
||
var
|
||
tndb_ : TTreeNode;
|
||
begin
|
||
// Drop DB.
|
||
tndb_ := nil;
|
||
if (Sender as TComponent).Name = 'PopupmenuDropDatabase' then
|
||
// drop cmd from popupmenu
|
||
tndb_ := DBRightClickSelectedItem
|
||
else case DBTree.Selected.Level of // drop cmd from toolbar
|
||
1 : tndb_ := DBTree.Selected;
|
||
2 : tndb_ := DBTree.Selected.Parent;
|
||
3 : tndb_ := DBTree.Selected.Parent.Parent;
|
||
end;
|
||
|
||
if tndb_ = nil then raise Exception.Create('Internal error: Cannot drop NIL database.');
|
||
|
||
if MessageDlg('Drop Database "'+tndb_.Text+'"?' + crlf + crlf + 'WARNING: You will lose all tables in database '+tndb_.Text+'!', mtConfirmation, [mbok,mbcancel], 0) <> mrok then
|
||
abort;
|
||
|
||
Screen.Cursor := crSQLWait;
|
||
try
|
||
ExecUpdateQuery( 'DROP DATABASE ' + mask(tndb_.Text) );
|
||
if OnlyDBs.Count > 0 then
|
||
begin
|
||
if OnlyDBs.IndexOf(tndb_.Text) > -1 then
|
||
begin
|
||
OnlyDBs.Delete( OnlyDBs.IndexOf(tndb_.Text) );
|
||
with TRegistry.Create do
|
||
begin
|
||
if OpenKey(REGPATH + '\Servers\' + FConn.Description, false) then
|
||
begin
|
||
WriteString( 'OnlyDBs', ImplodeStr( ';', OnlyDBs ) );
|
||
CloseKey;
|
||
end;
|
||
end;
|
||
end;
|
||
end;
|
||
tndb_.Delete;
|
||
except
|
||
MessageDLG('Dropping failed.'+crlf+'Maybe '''+tndb_.Text+''' is not a valid database-name.', mtError, [mbOK], 0)
|
||
end;
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
|
||
|
||
|
||
procedure TMDIChild.ShowVariablesAndProcesses(Sender: TObject);
|
||
|
||
procedure addLVitem( caption: String; commandCount: Int64; totalCount: Int64 );
|
||
var
|
||
n : TListItem;
|
||
tmpval : Double;
|
||
begin
|
||
n := ListCommandStats.Items.Add;
|
||
n.ImageIndex := 86;
|
||
caption := Copy( caption, 5, Length(caption) );
|
||
caption := StringReplace( caption, '_', ' ', [rfReplaceAll] );
|
||
n.Caption := caption;
|
||
// Total Frequency
|
||
n.Subitems.Add( FormatNumber( commandCount ) );
|
||
// Average per hour
|
||
uptime := max(uptime, 1);
|
||
tmpval := commandCount / ( uptime / 60 / 60 );
|
||
n.Subitems.Add( FormatNumber( tmpval, 1 ) );
|
||
// Average per second
|
||
tmpval := commandCount / uptime;
|
||
n.Subitems.Add( FormatNumber( tmpval, 1 ) );
|
||
// Percentage. Take care of division by zero errors and Int64's
|
||
if commandCount < 1 then
|
||
commandCount := 1;
|
||
if totalCount < 1 then
|
||
totalCount := 1;
|
||
tmpval := 100 / totalCount * commandCount;
|
||
n.Subitems.Add( FormatNumber( tmpval, 1 ) + ' %' );
|
||
end;
|
||
|
||
var
|
||
i : Integer;
|
||
n : TListItem;
|
||
questions : Int64;
|
||
ds : TDataSet;
|
||
begin
|
||
// Refresh variables and process-list
|
||
Screen.Cursor := crSQLWait;
|
||
|
||
// VARIABLES
|
||
ListVariables.Items.BeginUpdate;
|
||
ListVariables.Items.Clear;
|
||
|
||
ds := GetResults( 'SHOW VARIABLES', false );
|
||
for i:=1 to ds.RecordCount do
|
||
begin
|
||
n := ListVariables.Items.Add;
|
||
n.ImageIndex := 87;
|
||
n.Caption := ds.Fields[0].AsString;
|
||
n.Subitems.Add( ds.Fields[1].AsString );
|
||
ds.Next;
|
||
end;
|
||
|
||
// STATUS
|
||
uptime := 1; // avoids division by zero :)
|
||
questions := 1;
|
||
ds := GetResults( 'SHOW /*!50002 GLOBAL */ STATUS' );
|
||
for i:=1 to ds.RecordCount do
|
||
begin
|
||
if LowerCase( Copy( ds.Fields[0].AsString, 1, 4 ) ) <> 'com_' then
|
||
begin
|
||
n := ListVariables.Items.Add;
|
||
n.ImageIndex := 87;
|
||
n.Caption := ds.Fields[0].AsString;
|
||
n.Subitems.Add( ds.Fields[1].AsString );
|
||
if lowercase( ds.Fields[0].AsString ) = 'uptime' then
|
||
uptime := MakeInt(ds.Fields[1].AsString);
|
||
if lowercase( ds.Fields[0].AsString ) = 'questions' then
|
||
questions := MakeInt(ds.Fields[1].AsString);
|
||
end;
|
||
ds.Next;
|
||
end;
|
||
// Remove existing column-sort-images
|
||
// (TODO: auomatically invoke this method in TSortListView itself)
|
||
ListVariables.ClearSortColumnImages;
|
||
|
||
// Command-Statistics
|
||
ListCommandStats.Items.BeginUpdate;
|
||
ListCommandStats.Items.Clear;
|
||
addLVitem( ' All commands', questions, questions );
|
||
ds.First;
|
||
for i:=1 to ds.RecordCount do
|
||
begin
|
||
if LowerCase( Copy( ds.Fields[0].AsString, 1, 4 ) ) = 'com_' then
|
||
begin
|
||
addLVitem( ds.Fields[0].AsString, MakeInt(ds.Fields[1].AsString), questions );
|
||
end;
|
||
ds.Next;
|
||
end;
|
||
ListCommandStats.Items.EndUpdate;
|
||
// Sort 2nd column descending
|
||
ListCommandStats.ColClick( ListCommandStats.Columns[1] );
|
||
ListCommandStats.ColClick( ListCommandStats.Columns[1] );
|
||
|
||
TimerHostUptime.Enabled := true;
|
||
TimerHostUptimeTimer(self);
|
||
TimerHostUptime.OnTimer := TimerHostUptimeTimer;
|
||
|
||
ListVariables.Items.EndUpdate;
|
||
tabVariables.Caption := 'Variables (' + IntToStr(ListVariables.Items.Count) + ')';
|
||
Screen.Cursor := crDefault;
|
||
|
||
ShowProcesslist(self); // look at next procedure
|
||
end;
|
||
|
||
|
||
|
||
procedure TMDIChild.ShowProcessList(sender: TObject);
|
||
var
|
||
i,j : Integer;
|
||
n : TListItem;
|
||
ds : TDataSet;
|
||
begin
|
||
// No need to update if it's not visible.
|
||
if PageControlMain.ActivePage <> tabHost then exit;
|
||
if PageControlHost.ActivePage <> tabProcesslist then exit;
|
||
Screen.Cursor := crSQLWait;
|
||
try
|
||
ListProcesses.Items.BeginUpdate;
|
||
ListProcesses.Items.Clear;
|
||
debug('ShowProcessList()');
|
||
ds := GetResults('SHOW FULL PROCESSLIST', false, false);
|
||
for i:=1 to ds.RecordCount do
|
||
begin
|
||
n := ListProcesses.Items.Add;
|
||
n.Caption := ds.Fields[0].AsString;
|
||
if CompareText( ds.Fields[4].AsString, 'Killed') = 0 then
|
||
n.ImageIndex := 83 // killed
|
||
else
|
||
n.ImageIndex := 82; // running
|
||
for j := 1 to 7 do
|
||
n.Subitems.Add(ds.Fields[j].AsString);
|
||
ds.Next;
|
||
end;
|
||
ds.Close;
|
||
ListProcesses.Items.EndUpdate;
|
||
// Remove existing column-sort-images
|
||
// (TODO: auomatically invoke this method in TSortListView itself)
|
||
ListProcesses.ClearSortColumnImages;
|
||
tabProcessList.Caption := 'Process-List (' + IntToStr(ListProcesses.Items.Count) + ')';
|
||
except
|
||
on E: Exception do begin
|
||
LogSQL('Error loading process list (automatic refresh disabled): ' + e.Message);
|
||
TimerProcesslist.Enabled := false;
|
||
end;
|
||
end;
|
||
ListProcesses.Items.EndUpdate;
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.ListProcessesSelectItem(Sender: TObject; Item: TListItem;
|
||
Selected: Boolean);
|
||
begin
|
||
Kill1.Enabled := (ListProcesses.Selected <> nil) and (PageControlHost.ActivePage = tabProcessList);
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.KillProcess(Sender: TObject);
|
||
var t : Boolean;
|
||
begin
|
||
if ListProcesses.Selected.Caption = IntToStr( MySQLConn.Connection.GetThreadId ) then
|
||
MessageDlg('Fatal: Better not kill my own Process...', mtError, [mbok], 0)
|
||
else begin
|
||
t := TimerProcessList.Enabled;
|
||
TimerProcessList.Enabled := false; // prevent av (ListProcesses.selected...)
|
||
if MessageDlg('Kill Process '+ListProcesses.Selected.Caption+'?', mtConfirmation, [mbok,mbcancel], 0) = mrok then
|
||
begin
|
||
ExecUpdateQuery( 'KILL '+ListProcesses.Selected.Caption );
|
||
ShowVariablesAndProcesses(self);
|
||
end;
|
||
TimerProcessList.Enabled := t; // re-enable autorefresh timer
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.PageControlHostChange(Sender: TObject);
|
||
begin
|
||
// Show processlist if it's visible now but empty yet
|
||
if ListProcesses.Items.Count = 0 then
|
||
ShowProcessList( self );
|
||
ListProcessesSelectItem(self, nil, False);
|
||
end;
|
||
|
||
|
||
|
||
|
||
|
||
procedure TMDIChild.ExecSQLClick(Sender: TObject; Selection: Boolean=false; CurrentLine: Boolean=false);
|
||
var
|
||
SQL : TStringList;
|
||
i : Integer;
|
||
rowsaffected : Integer;
|
||
SQLstart : Integer;
|
||
SQLend : Integer;
|
||
SQLscriptstart : Integer;
|
||
SQLscriptend : Integer;
|
||
SQLTime : Double;
|
||
fieldcount : Integer;
|
||
recordcount : Integer;
|
||
ds : TDataSet;
|
||
term : String;
|
||
prevDb : String;
|
||
begin
|
||
if ( btnAltTerminator.Down ) then
|
||
begin
|
||
term := '//';
|
||
end
|
||
else
|
||
begin
|
||
term := ';';
|
||
end;
|
||
|
||
if ( CurrentLine ) then
|
||
begin
|
||
// Run current line
|
||
SQL := parseSQL( SynMemoQuery.LineText, term );
|
||
end
|
||
else
|
||
if ( Selection ) then
|
||
begin
|
||
// Run selection
|
||
SQL := parseSQL( SynMemoQuery.SelText, term );
|
||
end
|
||
else
|
||
begin
|
||
// Run all
|
||
SQL := parseSQL( SynMemoQuery.Text, term );
|
||
end;
|
||
|
||
if ( SQL.Count = 0 ) then
|
||
begin
|
||
LabelResultinfo.Caption := '(nothing to do)';
|
||
Exit;
|
||
end;
|
||
|
||
// Let EnsureActiveDatabase know that we're firing user queries.
|
||
prevDb := ActualDatabase;
|
||
ActualDatabase := EmptyStr;
|
||
UserQueryFired := true;
|
||
|
||
// Destroy old data set.
|
||
ds := DataSource2.DataSet;
|
||
DataSource2.DataSet := nil;
|
||
FreeAndNil( ds );
|
||
// set db-aware-component's properties..
|
||
DBMemo1.DataField := EmptyStr;
|
||
DBMemo1.DataSource := DataSource2;
|
||
EDBImage1.DataField := EmptyStr;
|
||
EDBImage1.DataSource := DataSource2;
|
||
|
||
SQLscriptstart := GetTickCount();
|
||
LabelResultinfo.Caption := EmptyStr;
|
||
|
||
ds := nil;
|
||
try
|
||
MainForm.showstatus( 'Initializing SQL...', 2, true );
|
||
Mainform.ExecuteQuery.Enabled := false;
|
||
Mainform.ExecuteSelection.Enabled := false;
|
||
|
||
if ( ActualDatabase <> EmptyStr ) then
|
||
begin
|
||
FMysqlConn.Connection.Database := ActualDatabase;
|
||
end;
|
||
|
||
rowsaffected := 0;
|
||
fieldcount := 0;
|
||
recordcount := 0;
|
||
ProgressBarQuery.Max := SQL.Count;
|
||
ProgressBarQuery.Position := 0;
|
||
ProgressBarQuery.Show();
|
||
|
||
MainForm.showstatus( 'Executing SQL...', 2, true );
|
||
for i := 0 to (SQL.Count - 1) do
|
||
begin
|
||
ProgressBarQuery.StepIt();
|
||
ProgressBarQuery.Repaint;
|
||
if ( sql[i] = EmptyStr ) then
|
||
begin
|
||
continue;
|
||
end;
|
||
// open last query with data-aware:
|
||
LabelResultinfo.Caption := EmptyStr;
|
||
// ok, let's rock
|
||
SQLstart := GetTickCount();
|
||
try
|
||
if ( ExpectResultSet( SQL[i] ) ) then
|
||
begin
|
||
ds := GetResults( SQL[i], false, false );
|
||
gridQuery.DataSource.DataSet := ds;
|
||
if ( ds <> nil ) then
|
||
begin
|
||
fieldcount := ds.Fieldcount;
|
||
recordcount := ds.Recordcount;
|
||
end
|
||
else
|
||
begin
|
||
fieldcount := 0;
|
||
recordcount := 0;
|
||
end;
|
||
rowsaffected := rowsaffected + TZQuery(ds).RowsAffected;
|
||
end
|
||
else
|
||
begin
|
||
rowsaffected := rowsaffected + ExecUpdateQuery( SQL[i], false, false );
|
||
fieldcount := 0;
|
||
recordcount := 0;
|
||
end;
|
||
except
|
||
on E:Exception do
|
||
begin
|
||
if (
|
||
( btnQueryStopOnErrors.Down ) or
|
||
( i = (SQL.Count - 1) )
|
||
) then
|
||
begin
|
||
Screen.Cursor := crDefault;
|
||
MessageDlg( E.Message, mtError, [mbOK], 0 );
|
||
ProgressBarQuery.Hide();
|
||
Mainform.ExecuteQuery.Enabled := true;
|
||
Mainform.ExecuteSelection.Enabled := true;
|
||
Break;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
SQLend := GetTickCount();
|
||
SQLTime := (SQLend - SQLstart) / 1000;
|
||
|
||
LabelResultinfo.Caption :=
|
||
FormatNumber( rowsaffected ) +' row(s) affected, '+
|
||
FormatNumber( fieldcount ) +' field(s) / '+
|
||
FormatNumber( recordcount ) +' record(s) in last result set.';
|
||
if ( SQL.Count = 1 ) then
|
||
begin
|
||
LabelResultinfo.Caption := LabelResultinfo.Caption +
|
||
' Query time: '+ FormatNumber( SQLTime, 3) +' sec.';
|
||
end;
|
||
end;
|
||
|
||
ProgressBarQuery.Hide();
|
||
Mainform.ExecuteQuery.Enabled := true;
|
||
Mainform.ExecuteSelection.Enabled := true;
|
||
// count chars:
|
||
SynMemoQuery.OnChange( Self );
|
||
|
||
if ( SQL.Count > 1 ) then
|
||
begin
|
||
SQLscriptend := GetTickCount();
|
||
SQLTime := (SQLscriptend - SQLscriptstart) / 1000;
|
||
LabelResultinfo.Caption := LabelResultinfo.Caption +' Batch time: '+
|
||
FormatNumber( SQLTime, 3 ) +' sec.';
|
||
end;
|
||
|
||
finally
|
||
// Avoid excessive GridHighlightChanged() when flicking controls.
|
||
viewingdata := true;
|
||
|
||
if ( ds <> nil ) then
|
||
begin
|
||
// Re-link to datasource
|
||
DataSource2.DataSet := ds;
|
||
// Flick controls so that column resizing will work (?)
|
||
TZQuery(ds).EnableControls();
|
||
TZQuery(ds).DisableControls();
|
||
// resize all columns, if they are more wide than Mainform.DefaultColWidth
|
||
if ( Mainform.DefaultColWidth <> 0 ) then
|
||
begin
|
||
for i:=0 to gridQuery.Columns.count-1 do
|
||
begin
|
||
if ( gridQuery.Columns[i].Width > Mainform.DefaultColWidth ) then
|
||
begin
|
||
gridQuery.Columns[i].Width := Mainform.DefaultColWidth;
|
||
end;
|
||
end;
|
||
end;
|
||
end
|
||
else
|
||
begin
|
||
// Avoid AV for mousewheel-scrolling in empty datagrid
|
||
DataSource2.DataSet := nil;
|
||
end;
|
||
// Ensure controls are in a valid state
|
||
ValidateControls();
|
||
viewingdata := false;
|
||
ActualDatabase := prevDb;
|
||
if ( ds <> nil ) then
|
||
begin
|
||
TZQuery(ds).EnableControls();
|
||
end;
|
||
Screen.Cursor := crDefault;
|
||
MainForm.ShowStatus( STATUS_MSG_READY, 2 );
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.ListColumnsSelectItem(Sender: TObject; Item: TListItem;
|
||
Selected: Boolean);
|
||
begin
|
||
// Clicked somewhere in the field-list of the "Table"-tabsheet
|
||
|
||
// some columns selected ?
|
||
if ListColumns.Selected <> nil then with ListColumns.Selected do
|
||
begin
|
||
btnTableDropField.Enabled := True;
|
||
DropField1.Enabled := True;
|
||
MenuEditField.Enabled := true;
|
||
btnTableEditField.enabled := true;
|
||
end
|
||
else
|
||
begin
|
||
btnTableDropField.Enabled := False;
|
||
DropField1.Enabled := false;
|
||
MenuEditField.Enabled := false;
|
||
btnTableEditField.enabled := false;
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.DropField(Sender: TObject);
|
||
var
|
||
tn : TTreeNode;
|
||
begin
|
||
// Drop Column
|
||
if ListColumns.Items.Count = 1 then
|
||
begin
|
||
if MessageDlg('Can''t drop the last Field - drop Table '+ActualTable+'?', mtConfirmation, [mbok,mbcancel], 0) = mrok then
|
||
begin
|
||
Screen.Cursor := crSQLWait;
|
||
ExecUpdateQuery( 'DROP TABLE '+mask(ActualTable) );
|
||
tn := DBTree.Selected;
|
||
DBTree.Selected := DBTree.Selected.Parent;
|
||
tn.Destroy;
|
||
RefreshActiveDbTableList;
|
||
ShowDBProperties(self);
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
end else
|
||
if MessageDlg('Drop field ' + ListColumns.Selected.Caption + ' ?', mtConfirmation, [mbok,mbcancel], 0) = mrok then
|
||
begin
|
||
ExecUpdateQuery( 'ALTER TABLE '+mask(ActualTable)+' DROP '+mask(ListColumns.Selected.Caption) );
|
||
ShowTableProperties(self);
|
||
end;
|
||
end;
|
||
|
||
|
||
{ Proposal about to insert a String into synmemo }
|
||
procedure TMDIChild.SynCompletionProposal1CodeCompletion(Sender: TObject;
|
||
var Value: String; Shift: TShiftState; Index: Integer; EndToken: Char);
|
||
begin
|
||
SynCompletionProposal1.Editor.UndoList.AddGroupBreak;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.SynCompletionProposal1AfterCodeCompletion(Sender: TObject;
|
||
const Value: String; Shift: TShiftState; Index: Integer; EndToken: Char);
|
||
begin
|
||
SynCompletionProposal1.Editor.UndoList.AddGroupBreak;
|
||
end;
|
||
|
||
|
||
{ Proposal-Combobox pops up }
|
||
procedure TMDIChild.SynCompletionProposal1Execute(Kind: TSynCompletionType;
|
||
Sender: TObject; var CurrentInput: String; var x, y: Integer;
|
||
var CanExecute: Boolean);
|
||
var
|
||
i,j,c,t : Integer;
|
||
tn, child : TTreeNode;
|
||
sql, tmpsql, kw : String;
|
||
keywords, tables : TStringList;
|
||
tablename : String;
|
||
|
||
procedure addTable( name: String );
|
||
begin
|
||
SynCompletionProposal1.InsertList.Add( name );
|
||
SynCompletionProposal1.ItemList.Add( '\hspace{2}\color{'+ColorToString(SynSQLSyn1.TableNameAttri.Foreground)+'}table\color{clWindowText}\column{}' + name );
|
||
end;
|
||
|
||
procedure addColumns( tablename: String );
|
||
var
|
||
dbname : String;
|
||
i : Integer;
|
||
ds : TDataSet;
|
||
begin
|
||
dbname := ActualDatabase;
|
||
if Pos( '.', tablename ) > -1 then
|
||
begin
|
||
dbname := Copy( tablename, 0, Pos( '.', tablename )-1 );
|
||
tablename := Copy( tablename, Pos( '.', tablename )+1, Length(tablename) );
|
||
end;
|
||
tablename := mask( tablename );
|
||
if dbname <> '' then
|
||
tablename := mask( dbname ) + '.' + tablename;
|
||
ds := getResults( 'SHOW COLUMNS FROM '+tablename, true, false );
|
||
if ds = nil then exit;
|
||
for i:=0 to ds.RecordCount-1 do
|
||
begin
|
||
SynCompletionProposal1.InsertList.Add( ds.FieldByName( 'Field' ).AsString );
|
||
SynCompletionProposal1.ItemList.Add( '\hspace{2}\color{'+ColorToString(clTeal)+'}column\color{clWindowText}\column{}' + ds.FieldByName( 'Field' ).AsString + '\style{-B} ' + ds.FieldByName( 'Type' ).AsString );
|
||
ds.Next;
|
||
end;
|
||
end;
|
||
|
||
begin
|
||
SynCompletionProposal1.InsertList.Clear;
|
||
SynCompletionProposal1.ItemList.Clear;
|
||
|
||
// Get column-names into the proposal (Daniel's Top1-wish...)
|
||
// when we write sql like "SELECT t.|col FROM table [AS] t"
|
||
// This is at no means a perfect solution which catches all kind of
|
||
// SQL statements. But for simple SQL it should work fine.
|
||
|
||
// 1. find the currently edited sql-statement around the cursor position in synmemo
|
||
j := Length(SynCompletionProposal1.Editor.Text);
|
||
c := 1024;
|
||
for i := SynCompletionProposal1.Editor.SelStart+1024 downto SynCompletionProposal1.Editor.SelStart-1024 do
|
||
begin
|
||
if i > j then
|
||
continue;
|
||
if i < 1 then
|
||
begin
|
||
c := SynCompletionProposal1.Editor.SelStart;
|
||
break;
|
||
end;
|
||
sql := SynCompletionProposal1.Editor.Text[i] + sql;
|
||
end;
|
||
sql := StringReplace( sql, CRLF, ' ', [rfReplaceall] );
|
||
sql := StringReplace( sql, #10, ' ', [rfReplaceall] );
|
||
sql := StringReplace( sql, #13, ' ', [rfReplaceall] );
|
||
// 2. find the position of tablenames after the cursor-position (c)
|
||
keywords := TStringList.Create;
|
||
keywords.CommaText := 'FROM,INTO,UPDATE';
|
||
t := -1;
|
||
for i := c to Length(sql) do // forward from cursor-position
|
||
begin
|
||
kw := Copy( sql, i, Pos( ' ', Copy( sql, i, Length(sql) ) )-1 );
|
||
if keywords.IndexOf( kw ) > -1 then
|
||
begin
|
||
t := i + Length( kw );
|
||
break;
|
||
end;
|
||
end;
|
||
if t = -1 then
|
||
begin
|
||
for i := c downto 0 do // and backwards from cursor-position
|
||
begin
|
||
kw := Copy( sql, i, Pos( ' ', Copy( sql, i, Length(sql) ) )-1 );
|
||
if keywords.IndexOf( kw ) > -1 then
|
||
begin
|
||
t := i + Length( kw );
|
||
break;
|
||
end;
|
||
end;
|
||
end;
|
||
// 3. find tablenames and aliases, compare them with previous token
|
||
tablename := '';
|
||
if t>-1 then
|
||
begin
|
||
tables := TStringList.Create;
|
||
tables.CommaText := StringReplace( Copy( sql, t, Length(sql)), ' ', ',', [rfReplaceall] );
|
||
for i := 0 to tables.Count - 1 do
|
||
begin
|
||
if (UpperCase(tables[i]) = 'AS') and (i > 0) and (i<tables.Count-1) then
|
||
begin
|
||
if (SynCompletionProposal1.PreviousToken = tables[i-1]) or
|
||
(SynCompletionProposal1.PreviousToken = tables[i+1]) then
|
||
begin
|
||
tablename := tables[i-1]; // Got it!
|
||
break;
|
||
end;
|
||
end;
|
||
end;
|
||
end;
|
||
if (tablename <> '') then
|
||
begin
|
||
// add columns to proposal
|
||
addColumns( tablename );
|
||
end
|
||
else if SynCompletionProposal1.PreviousToken <> '' then
|
||
begin
|
||
// assuming that previoustoken itself is a table
|
||
addColumns( SynCompletionProposal1.PreviousToken );
|
||
end;
|
||
|
||
|
||
if Length(CurrentInput) = 0 then // makes only sense if the user has typed "database."
|
||
begin
|
||
if OnlyDBs2.IndexOf( SynCompletionProposal1.PreviousToken ) > -1 then
|
||
begin
|
||
// Only display tables from specified db
|
||
Screen.Cursor := crHourGlass;
|
||
for i:=0 to DBTree.Items.Count-1 do
|
||
begin
|
||
tn := DBTree.Items[i];
|
||
if tn.Text = SynCompletionProposal1.PreviousToken then
|
||
begin
|
||
child := tn.getFirstChild;
|
||
for j:=0 to tn.Count-1 do
|
||
begin
|
||
addTable(child.Text);
|
||
child := tn.getNextChild(child);
|
||
end;
|
||
end;
|
||
end;
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
end;
|
||
|
||
if (SynCompletionProposal1.ItemList.count = 0) and (Length(CurrentInput)>0) then
|
||
begin
|
||
// Add databases
|
||
SynCompletionProposal1.InsertList.AddStrings( OnlyDBs2 );
|
||
SynCompletionProposal1.ItemList.AddStrings( OnlyDBs2 );
|
||
for i:=0 to SynCompletionProposal1.ItemList.count-1 do
|
||
SynCompletionProposal1.ItemList[i] := '\hspace{2}\color{'+ColorToString(SynSQLSyn1.TableNameAttri.Foreground)+'}database\color{clWindowText}\column{}' + SynCompletionProposal1.ItemList[i];
|
||
|
||
if ActualDatabase <> '' then
|
||
begin
|
||
// Add tables
|
||
for i:=0 to ListTables.Items.Count-1 do
|
||
begin
|
||
addTable( ListTables.Items[i].Caption );
|
||
end;
|
||
if Length(CurrentInput) = 0 then // assume that we have already a dbname in memo
|
||
SynCompletionProposal1.Position := OnlyDBs2.Count;
|
||
end;
|
||
|
||
// Add functions
|
||
for i := 0 to Length(MySQLFunctions) - 1 do
|
||
begin
|
||
// Don't display unsupported functions here
|
||
if MySqlFunctions[i].Version > mysql_version then
|
||
continue;
|
||
SynCompletionProposal1.InsertList.Add( MySQLFunctions[i].Name + MySQLFunctions[i].Declaration );
|
||
SynCompletionProposal1.ItemList.Add( '\hspace{2}\color{'+ColorToString(SynSQLSyn1.FunctionAttri.Foreground)+'}function\color{clWindowText}\column{}' + MySQLFunctions[i].Name + '\style{-B}' + MySQLFunctions[i].Declaration );
|
||
end;
|
||
|
||
// Add keywords
|
||
for i := 0 to MYSQL_KEYWORDS.Count - 1 do
|
||
begin
|
||
SynCompletionProposal1.InsertList.Add( MYSQL_KEYWORDS[i] );
|
||
SynCompletionProposal1.ItemList.Add( '\hspace{2}\color{'+ColorToString(SynSQLSyn1.KeyAttri.Foreground)+'}keyword\color{clWindowText}\column{}'+MYSQL_KEYWORDS[i] );
|
||
end;
|
||
|
||
end;
|
||
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.SynMemoQueryChange(Sender: TObject);
|
||
var somechars : Boolean;
|
||
begin
|
||
PanelCharsInQueryWindow.Caption := FormatByteNumber( SynMemoQuery.GetTextLen );
|
||
somechars := Length(SynMemoQuery.Text) > 0;
|
||
Mainform.ExecuteQuery.Enabled := somechars;
|
||
Mainform.ExecuteSelection.Enabled := SynMemoQuery.SelAvail;
|
||
Mainform.ExecuteLine.Enabled := SynMemoQuery.LineText <> '';
|
||
btnQuerySave.Enabled := somechars;
|
||
btnQuerySaveSnippet.Enabled := somechars;
|
||
end;
|
||
|
||
|
||
|
||
procedure TMDIChild.CreateTable(Sender: TObject);
|
||
begin
|
||
CreateTableWindow(Self);
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.TimerHostUptimeTimer(Sender: TObject);
|
||
var
|
||
days, hours, minutes, seconds : Integer;
|
||
msg: string;
|
||
begin
|
||
// Host-Uptime
|
||
days:= uptime div (60*60*24);
|
||
seconds := uptime mod (60*60*24);
|
||
hours := seconds div (60*60);
|
||
seconds := seconds mod (60*60);
|
||
minutes := seconds div 60;
|
||
seconds := seconds mod 60;
|
||
|
||
inc(uptime);
|
||
msg := Format('%d days, %.2d:%.2d:%.2d', [days,hours,minutes,seconds]);
|
||
if TimerHostUptime.Enabled then msg := Format(strHostRunning, [msg])
|
||
else msg := Format(strHostNotRunning, [msg]);
|
||
Panel4.Caption := msg;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.FormActivate(Sender: TObject);
|
||
begin
|
||
TimerConnected.OnTimer(self);
|
||
ValidateControls;
|
||
end;
|
||
|
||
procedure TMDIChild.FormDeactivate(Sender: TObject);
|
||
begin
|
||
ValidateControls( False );
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.FormShow(Sender: TObject);
|
||
begin
|
||
{ TODO : only load file when autoconnected ?? }
|
||
if (paramstr(1) <> '') and Main.loadsqlfile then
|
||
try
|
||
// load sql-file from paramstr
|
||
SynMemoQuery.Lines.LoadFromFile(paramstr(1));
|
||
Main.loadsqlfile := false;
|
||
except
|
||
MessageDLG('File could not be opened: ' + paramstr(1), mtError, [mbOK], 0);
|
||
end;
|
||
|
||
//TODO:
|
||
//ds.DisableControls;
|
||
FormResize( self );
|
||
end;
|
||
|
||
|
||
{ Edit field }
|
||
procedure TMDIChild.UpdateField(Sender: TObject);
|
||
var
|
||
fn : String;
|
||
fem : TFieldEditorMode;
|
||
begin
|
||
fn := '';
|
||
fem := femFieldAdd;
|
||
|
||
if ListColumns.Selected<>nil then
|
||
fn := ListColumns.Selected.Caption;
|
||
|
||
if fn<>'' then
|
||
fem := femFieldUpdate;
|
||
|
||
FieldEditorWindow (Self,fem,fn);
|
||
end;
|
||
|
||
|
||
{ Add new field }
|
||
procedure TMDIChild.MenuAddFieldClick(Sender: TObject);
|
||
begin
|
||
FieldEditorWindow (Self,femFieldAdd);
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.CreateDatabase(Sender: TObject);
|
||
var dbname : String;
|
||
begin
|
||
// Create new Database:
|
||
if InputQuery('Create new Database...', 'Database Name:', dbname) then
|
||
begin
|
||
Screen.Cursor := crSQLWait;
|
||
Try
|
||
ExecUpdateQuery( 'CREATE DATABASE ' + mask( dbname ) );
|
||
// Add DB to OnlyDBs-regkey if this is not empty
|
||
if OnlyDBs.Count > 0 then
|
||
begin
|
||
OnlyDBs.Add( dbname );
|
||
with TRegistry.Create do
|
||
begin
|
||
if OpenKey(REGPATH + '\Servers\' + FConn.Description, false) then
|
||
begin
|
||
WriteString( 'OnlyDBs', ImplodeStr( ';', OnlyDBs ) );
|
||
CloseKey;
|
||
end;
|
||
end;
|
||
end;
|
||
ActualDatabase := dbname;
|
||
ReadDatabasesAndTables(self);
|
||
except
|
||
MessageDLG('Creation failed.'+crlf+'Maybe '''+dbname+''' is not a valid database-name.', mtError, [mbOK], 0)
|
||
end;
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.MenuAdvancedPropertiesClick(Sender: TObject);
|
||
begin
|
||
tbl_properties_Window(self);
|
||
end;
|
||
|
||
|
||
{***
|
||
Rename table after checking the new name for invalid characters
|
||
}
|
||
procedure TMDIChild.ListTablesEdited(Sender: TObject; Item: TListItem;
|
||
var S: String);
|
||
var
|
||
i : Integer;
|
||
begin
|
||
|
||
// Try to rename, on any error abort and don't rename ListItem
|
||
try
|
||
ensureValidIdentifier( S );
|
||
// rename table
|
||
ExecUpdateQuery( 'ALTER TABLE ' + mask(Item.Caption) + ' RENAME ' + mask(S), False, False );
|
||
except
|
||
On E : Exception do
|
||
begin
|
||
MessageDlg( E.Message, mtError, [mbOK], 0 );
|
||
abort;
|
||
end;
|
||
end;
|
||
|
||
i := SynSQLSyn1.TableNames.IndexOf( Item.Caption );
|
||
if i > -1 then
|
||
SynSQLSyn1.TableNames[i] := S;
|
||
ActualTable := S;
|
||
RefreshActiveDbTableList;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.MenuRenameTableClick(Sender: TObject);
|
||
begin
|
||
// menuitem for edit table-name
|
||
ListTables.Selected.EditCaption;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.MenuViewBlobClick(Sender: TObject);
|
||
begin
|
||
PageControlBottom.ActivePage := tabBlobEditor;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.TimerConnectedTimer(Sender: TObject);
|
||
begin
|
||
if not TimerConnected.Enabled then begin
|
||
MainForm.showstatus('Disconnected.', 1);
|
||
exit;
|
||
end;
|
||
|
||
inc(time_connected);
|
||
|
||
// calculate and display connection-time
|
||
MainForm.showstatus( 'Connected: ' + FormatTimeNumber(time_connected), 1 );
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.Clear1Click(Sender: TObject);
|
||
begin
|
||
// clear
|
||
if SynMemoFilter.Focused then
|
||
SynMemoFilter.Lines.Clear
|
||
else
|
||
SynMemoQuery.Lines.Clear;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.MenuTableCommentClick(Sender: TObject);
|
||
begin
|
||
// table-comment
|
||
tablecommentWindow(self);
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.Clear2Click(Sender: TObject);
|
||
begin
|
||
// clear history-memo
|
||
Screen.Cursor := crHourglass;
|
||
SynMemoSQLLog.Lines.Clear;
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.EditQuery1Click(Sender: TObject);
|
||
begin
|
||
// take query from history to query-tab
|
||
SynMemoQuery.Text := SynMemoSQLLog.SelText;
|
||
PageControlMain.ActivePage := tabQuery;
|
||
pcChange(self);
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.ManageIndexes1Click(Sender: TObject);
|
||
begin
|
||
FieldEditorWindow (Self,femIndexEditor);
|
||
end;
|
||
|
||
procedure TMDIChild.Markall3Click(Sender: TObject);
|
||
begin
|
||
// select all in history
|
||
SynMemoSQLLog.SelectAll;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.More1Click(Sender: TObject);
|
||
begin
|
||
TableDiagnosticsWindow(Self);
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.MenuOptimizeClick(Sender: TObject);
|
||
var
|
||
i : Integer;
|
||
begin
|
||
// Optimize tables
|
||
Screen.Cursor := crHourGlass;
|
||
try
|
||
for i:=0 to ListTables.Items.Count - 1 do
|
||
begin
|
||
if ListTables.Items[i].Selected then
|
||
ExecUpdateQuery( 'OPTIMIZE TABLE ' + mask(ListTables.Items[i].Caption) );
|
||
end;
|
||
finally
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.MenuCheckClick(Sender: TObject);
|
||
var
|
||
i : Integer;
|
||
tables : String;
|
||
begin
|
||
// Check tables
|
||
Screen.Cursor := crHourGlass;
|
||
try
|
||
tables := '';
|
||
for i:=0 to ListTables.Items.Count - 1 do
|
||
if ListTables.Items[i].Selected then begin
|
||
if tables <> '' then
|
||
tables := tables + ', ';
|
||
tables := tables + mask(ListTables.Items[i].Caption);
|
||
end;
|
||
ExecUpdateQuery( 'CHECK TABLE ' + tables + ' QUICK' );
|
||
finally
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
end;
|
||
|
||
|
||
{**
|
||
Clear Query memo
|
||
}
|
||
procedure TMDIChild.menuclearClick(Sender: TObject);
|
||
var
|
||
memo : TSynMemo;
|
||
begin
|
||
// Clear SynMemo
|
||
if SynMemoFilter.Focused then
|
||
memo := SynMemoFilter
|
||
else
|
||
memo := SynMemoQuery;
|
||
// Make sure to add this step to SynMemo's undo history
|
||
memo.SelectAll;
|
||
memo.SelText := '';
|
||
memo.SelStart := 0;
|
||
memo.SelEnd := 0;
|
||
end;
|
||
|
||
procedure TMDIChild.MenuAnalyzeClick(Sender: TObject);
|
||
var
|
||
i : Integer;
|
||
tables : String;
|
||
begin
|
||
// Analyze tables
|
||
Screen.Cursor := crHourGlass;
|
||
try
|
||
tables := '';
|
||
for i:=0 to ListTables.Items.Count - 1 do
|
||
if ListTables.Items[i].Selected then begin
|
||
if tables <> '' then
|
||
tables := tables + ', ';
|
||
tables := tables + mask(ListTables.Items[i].Caption);
|
||
end;
|
||
ExecUpdateQuery( 'ANALYZE TABLE ' + tables );
|
||
finally
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.MenuRepairClick(Sender: TObject);
|
||
var
|
||
i : Integer;
|
||
tables : String;
|
||
begin
|
||
// Repair tables
|
||
Screen.Cursor := crHourGlass;
|
||
try
|
||
tables := '';
|
||
for i:=0 to ListTables.Items.Count - 1 do
|
||
if ListTables.Items[i].Selected then begin
|
||
if tables <> '' then
|
||
tables := tables + ', ';
|
||
tables := tables + mask(ListTables.Items[i].Caption);
|
||
end;
|
||
ExecUpdateQuery( 'REPAIR TABLE ' + tables + ' QUICK' );
|
||
finally
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.ListTablesDblClick(Sender: TObject);
|
||
begin
|
||
// table-doubleclick
|
||
if ListTables.Selected <> nil then begin
|
||
SelectTable(ActualDatabase, ListTables.Selected.Caption);
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.TimerConnectErrorCloseWindowTimer(Sender: TObject);
|
||
begin
|
||
// can't connect -> close MDI-Child
|
||
TimerConnectErrorCloseWindow.Enabled := false;
|
||
Mainform.Showstatus('', 1);
|
||
MainForm.ShowStatus( STATUS_MSG_READY, 2 );
|
||
close;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.gridDataTitleClick(Column: TColumn);
|
||
var
|
||
Grid : TSMDBGrid;
|
||
i : Integer;
|
||
existed : Boolean;
|
||
sortcol : TSMSortColumn;
|
||
sorting, reg_value : String;
|
||
reg : TRegistry;
|
||
begin
|
||
// column-title clicked -> generate "ORDER BY"
|
||
|
||
Grid := Column.Grid as TSMDBGrid;
|
||
Grid.DataSource.DataSet.DisableControls;
|
||
existed := false;
|
||
|
||
for i:=Grid.SortColumns.Count-1 downto 0 do
|
||
begin
|
||
sortcol := Grid.SortColumns[i];
|
||
if sortcol.FieldName <> Column.FieldName then
|
||
continue;
|
||
existed := true;
|
||
case sortcol.SortType of
|
||
stDescending : sortcol.SortType := stAscending;
|
||
stAscending : Grid.SortColumns.Delete(i);
|
||
stNone : sortcol.SortType := stDescending;
|
||
end;
|
||
end;
|
||
|
||
{add a new sorted column in list - ascending order}
|
||
if not existed then
|
||
begin
|
||
sortcol := Grid.SortColumns.Add;
|
||
sortcol.FieldName := column.FieldName;
|
||
sortcol.SortType := stDescending;
|
||
end;
|
||
|
||
Grid.DataSource.DataSet.EnableControls;
|
||
|
||
// Concat orderclause
|
||
sorting := '';
|
||
for i := 0 to ( gridData.SortColumns.Count - 1 ) do
|
||
begin
|
||
sortcol := gridData.SortColumns[i];
|
||
|
||
if ( sortcol.SortType <> stNone ) then
|
||
begin
|
||
if ( sorting <> '' ) then
|
||
begin
|
||
sorting := sorting + ', ';
|
||
end;
|
||
sorting := sorting + mask( sortcol.FieldName );
|
||
end;
|
||
|
||
case sortcol.SortType of
|
||
// stNone: ;
|
||
stAscending :
|
||
begin
|
||
sorting := sorting + ' DESC';
|
||
end;
|
||
stDescending:
|
||
begin
|
||
sorting := sorting + ' ASC';
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
// Write orderclause to registry
|
||
reg := TRegistry.Create();
|
||
reg.OpenKey( REGPATH + '\Servers\' + FConn.Description, true );
|
||
reg_value := 'ORDERCLAUSE_' + ActualDatabase + '.' + ActualTable;
|
||
if ( sorting <> '' ) then
|
||
begin
|
||
reg.WriteString( reg_value, sorting );
|
||
end
|
||
else
|
||
begin
|
||
if ( reg.ValueExists( reg_value ) ) then
|
||
reg.DeleteValue( reg_value );
|
||
end;
|
||
reg.CloseKey;
|
||
|
||
ViewData(self);
|
||
|
||
end;
|
||
|
||
|
||
|
||
procedure TMDIChild.Filter1Click(Sender: TObject);
|
||
begin
|
||
// Set WHERE-Filter
|
||
PageControlBottom.ActivePage := tabFilter;
|
||
SynMemoFilter.SetFocus;
|
||
end;
|
||
|
||
|
||
|
||
// open Find-Dialog
|
||
procedure TMDIChild.btnQueryFindClick(Sender: TObject);
|
||
begin
|
||
FindDialogQuery.execute;
|
||
// if something is selected search for that text
|
||
if SynMemoQuery.SelAvail and (SynMemoQuery.BlockBegin.Line = SynMemoQuery.BlockEnd.Line)
|
||
then
|
||
FindDialogQuery.FindText := SynMemoQuery.SelText
|
||
else
|
||
FindDialogQuery.FindText := SynMemoQuery.GetWordAtRowCol(SynMemoQuery.CaretXY);
|
||
end;
|
||
|
||
// open Replace-Dialog
|
||
procedure TMDIChild.btnQueryReplaceClick(Sender: TObject);
|
||
begin
|
||
ReplaceDialogQuery.execute;
|
||
// if something is selected search for that text
|
||
if SynMemoQuery.SelAvail and (SynMemoQuery.BlockBegin.Line = SynMemoQuery.BlockEnd.Line)
|
||
then
|
||
ReplaceDialogQuery.FindText := SynMemoQuery.SelText
|
||
else
|
||
ReplaceDialogQuery.FindText := SynMemoQuery.GetWordAtRowCol(SynMemoQuery.CaretXY);
|
||
end;
|
||
|
||
// Search-Dialog is searching...
|
||
procedure TMDIChild.FindDialogQueryFind(Sender: TObject);
|
||
var
|
||
Options: TSynSearchOptions;
|
||
Search: String;
|
||
begin
|
||
Search := FindDialogQuery.FindText;
|
||
Options := [];
|
||
if Sender is TReplaceDialog then
|
||
Include(Options, ssoEntireScope);
|
||
if not (frDown in FindDialogQuery.Options) then
|
||
Include(Options, ssoBackwards);
|
||
if frMatchCase in FindDialogQuery.Options then
|
||
Include(Options, ssoMatchCase);
|
||
if frWholeWord in FindDialogQuery.Options then
|
||
Include(Options, ssoWholeWord);
|
||
if SynMemoQuery.SearchReplace(Search, '', Options) = 0 then
|
||
begin
|
||
MessageBeep(MB_ICONASTERISK);
|
||
Mainform.ShowStatus( 'SearchText ''' + Search + ''' not found!', 0);
|
||
end;
|
||
end;
|
||
|
||
{ Find Text for replace-dialog }
|
||
procedure TMDIChild.ReplaceDialogQueryFind(Sender: TObject);
|
||
begin
|
||
FindDialogQuery.FindText := ReplaceDialogQuery.FindText;
|
||
FindDialogQueryFind( ReplaceDialogQuery );
|
||
end;
|
||
|
||
{ Replace Text with replace-dialog }
|
||
procedure TMDIChild.ReplaceDialogQueryReplace(Sender: TObject);
|
||
var
|
||
Options: TSynSearchOptions;
|
||
Search: String;
|
||
begin
|
||
Search := ReplaceDialogQuery.FindText;
|
||
Options := [ssoEntireScope]; // Do replaces always on entire scope, because the standard-dialog lacks of a down/up-option
|
||
if frReplaceAll in ReplaceDialogQuery.Options then
|
||
Include( Options, ssoReplaceAll );
|
||
if not (frDown in ReplaceDialogQuery.Options) then
|
||
Include(Options, ssoBackwards);
|
||
if frMatchCase in ReplaceDialogQuery.Options then
|
||
Include(Options, ssoMatchCase);
|
||
if frWholeWord in ReplaceDialogQuery.Options then
|
||
Include(Options, ssoWholeWord);
|
||
if frReplace in ReplaceDialogQuery.Options then // Replace instead of ReplaceAll is pressed
|
||
Include(Options, ssoReplace)
|
||
else
|
||
Include(Options, ssoReplaceAll);
|
||
if SynMemoQuery.SearchReplace( Search, ReplaceDialogQuery.ReplaceText, Options) = 0 then
|
||
begin
|
||
MessageBeep(MB_ICONASTERISK);
|
||
Mainform.ShowStatus( 'SearchText ''' + Search + ''' not found!', 0);
|
||
if ssoBackwards in Options then
|
||
SynMemoQuery.BlockEnd := SynMemoQuery.BlockBegin
|
||
else
|
||
SynMemoQuery.BlockBegin := SynMemoQuery.BlockEnd;
|
||
SynMemoQuery.CaretXY := SynMemoQuery.BlockBegin;
|
||
end;
|
||
end;
|
||
|
||
procedure TMDIChild.MenuLimitClick(Sender: TObject);
|
||
begin
|
||
// limit or not limit
|
||
mainform.CheckBoxLimit.Checked := not mainform.CheckBoxLimit.Checked;
|
||
viewdata(self);
|
||
end;
|
||
|
||
|
||
|
||
procedure TMDIChild.Delete1Click(Sender: TObject);
|
||
begin
|
||
// Delete record(s)
|
||
if gridData.SelectedRows.Count = 0 then begin
|
||
if MessageDLG('Delete 1 Record(s)?', mtConfirmation, [mbOK, mbCancel], 0) = mrOK then
|
||
GetVisualDataSet().Delete(); // unsafe ...
|
||
end else
|
||
if MessageDLG('Delete '+IntToStr(gridData.SelectedRows.count)+' Record(s)?', mtConfirmation, [mbOK, mbCancel], 0) = mrOK then
|
||
gridData.SelectedRows.Delete;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.QuickFilterClick(Sender: TObject);
|
||
var
|
||
filter,value,column : String;
|
||
menuitem : TMenuItem;
|
||
begin
|
||
// Set filter for "where..."-clause
|
||
value := gridData.SelectedField.AsString;
|
||
menuitem := (Sender as TMenuItem);
|
||
column := mask(gridData.SelectedField.FieldName);
|
||
if menuitem = QF1 then
|
||
filter := column + ' =' + ' ' + esc( value )
|
||
else if menuitem = QF2 then
|
||
filter := column + ' !=' + ' ' + esc( value )
|
||
else if menuitem = QF3 then
|
||
filter := column + ' >' + ' ' + esc( value )
|
||
else if menuitem = QF4 then
|
||
filter := column + ' <' + ' ' + esc( value )
|
||
else if menuitem = QF5 then
|
||
filter := column + ' LIKE' + ' ''' + esc( value, true ) + '%'''
|
||
else if menuitem = QF6 then
|
||
filter := column + ' LIKE' + ' ''%' + esc( value, true ) + ''''
|
||
else if menuitem = QF7 then
|
||
filter := column + ' LIKE' + ' ''%' + esc( value, true ) + '%'''
|
||
|
||
else if menuitem = QF8 then
|
||
begin
|
||
filter := InputBox('Specify filter-value...', column+' = ', 'Value');
|
||
if filter = 'Value' then
|
||
abort;
|
||
filter := column + ' = ''' + filter + '''';
|
||
end
|
||
|
||
else if menuitem = QF9 then
|
||
begin
|
||
filter := InputBox('Specify filter-value...', column+' != ', 'Value');
|
||
if filter = 'Value' then
|
||
abort;
|
||
filter := column + ' != ''' + filter + '''';
|
||
end
|
||
|
||
else if menuitem = QF10 then
|
||
begin
|
||
filter := InputBox('Specify filter-value...', column+' > ', 'Value');
|
||
if filter = 'Value' then
|
||
abort;
|
||
filter := column + ' > ''' + filter + '''';
|
||
end
|
||
|
||
else if menuitem = QF11 then
|
||
begin
|
||
filter := InputBox('Specify filter-value...', column+' < ', 'Value');
|
||
if filter = 'Value' then
|
||
abort;
|
||
filter := column + ' < ''' + filter + '''';
|
||
end
|
||
|
||
else if menuitem = QF12 then
|
||
begin
|
||
filter := InputBox('Specify filter-value...', column+' LIKE ', 'Value');
|
||
if filter = 'Value' then
|
||
abort;
|
||
filter := column + ' LIKE ''%' + filter + '%''';
|
||
end
|
||
|
||
// Filters with text from clipboard
|
||
else if (menuitem = QF13) or (menuitem = QF14) or (menuitem = QF15) or (menuitem = QF16) or (menuitem = QF17) then
|
||
begin
|
||
filter := menuitem.Caption;
|
||
end;
|
||
|
||
SynMemoFilter.Text := filter;
|
||
PageControlBottom.ActivePage := tabFilter;
|
||
SynMemoFilter.SetFocus;
|
||
SetFilter(self);
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.btnBlobWordWrapClick(Sender: TObject);
|
||
begin
|
||
// linebreaks
|
||
WordWrap := not WordWrap;
|
||
btnBlobWordWrap.Down := WordWrap;
|
||
if btnBlobWordWrap.Down then
|
||
DBMemo1.Scrollbars := ssVertical
|
||
else
|
||
DBMemo1.Scrollbars := ssBoth;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.btnDbPropertiesClick(Sender: TObject);
|
||
begin
|
||
if ListTables.Selected <> nil then
|
||
begin
|
||
SelectTable(ActualDatabase, ListTables.Selected.Caption);
|
||
end;
|
||
end;
|
||
|
||
procedure TMDIChild.btnDbViewDataClick(Sender: TObject);
|
||
begin
|
||
ShowTableData(ListTables.Selected.Caption);
|
||
end;
|
||
|
||
procedure TMDIChild.PageControlBlobEditorsChange(Sender: TObject);
|
||
begin
|
||
btnBlobCopy.Enabled := true;
|
||
btnBlobLoad.Enabled := not DBMemo1.ReadOnly;
|
||
btnBlobSave.Enabled := true;
|
||
if PageControlBlobEditors.ActivePage = tabBlobEditorText then
|
||
begin
|
||
// MEMO tab activated.
|
||
btnBlobWordWrap.Enabled := true;
|
||
end;
|
||
if PageControlBlobEditors.ActivePage = tabBlobEditorImage then
|
||
begin
|
||
// Image tab activated.
|
||
btnBlobWordWrap.Enabled := false;
|
||
end
|
||
end;
|
||
|
||
|
||
// Force a save of the user-edited contents of the BLOB editor.
|
||
procedure TMDIChild.SaveBlob;
|
||
begin
|
||
if not DBMemo1.Modified then exit;
|
||
if DBMemo1.ReadOnly then exit;
|
||
if Length(DBMemo1.DataField) = 0 then exit;
|
||
DBMemo1.DataSource.DataSet.Post;
|
||
//SendMessage(DBMemo1.Handle, CM_EXIT, 0, 0);
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.btnBlobSaveClick(Sender: TObject);
|
||
var
|
||
bf: Textfile;
|
||
grid: TSMDBGrid;
|
||
begin
|
||
// Todo: Weird fix, we probably shouldn't even be showing the button in the first place.
|
||
if Length(DBMemo1.DataField) = 0 then
|
||
begin
|
||
ShowMessage('Please choose a BLOB field in the data grid before using the "Save BLOB" function.');
|
||
exit;
|
||
end;
|
||
|
||
SaveBlob();
|
||
grid := ActiveGrid;
|
||
|
||
with TSaveDialog.Create(self) do begin
|
||
case PageControlBlobEditors.ActivePageIndex of
|
||
0 : begin
|
||
Filter := 'Text files (*.txt)|*.txt|All files (*.*)|*.*';
|
||
DefaultExt := 'txt';
|
||
end;
|
||
1 : begin
|
||
Filter := 'Bitmaps (*.bmp)|*.bmp|All files (*.*)|*.*';
|
||
DefaultExt := 'bmp';
|
||
end;
|
||
end;
|
||
FileName := grid.SelectedField.FieldName;
|
||
Options := [ofOverwritePrompt,ofEnableSizing];
|
||
if execute then try
|
||
Screen.Cursor := crHourGlass;
|
||
case PageControlBlobEditors.ActivePageIndex of
|
||
0 : begin
|
||
AssignFile(bf, filename);
|
||
Rewrite(bf);
|
||
Write(bf, GetVisualDataset().FieldByName(DBMemo1.DataField).AsString);
|
||
CloseFile(bf);
|
||
end;
|
||
1 : EDBImage1.Picture.SaveToFile(filename);
|
||
end;
|
||
Screen.Cursor := crDefault;
|
||
except
|
||
Screen.Cursor := crDefault;
|
||
messagedlg('File could not be saved', mterror, [mbok], 0);
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TMDIChild.btnBlobLoadClick(Sender: TObject);
|
||
begin
|
||
// Open BLOB-File
|
||
if not (DataSource1.State in [dsEdit, dsInsert]) then
|
||
try
|
||
DataSource1.Edit;
|
||
except
|
||
exit;
|
||
end;
|
||
|
||
with OpenDialog2 do begin
|
||
case PageControlBlobEditors.ActivePageIndex of
|
||
0 : Filter := 'Textfiles (*.txt)|*.txt|All files (*.*)|*.*';
|
||
1 : Filter := 'All Images (*.jpg, *.jpeg, *.bmp)|*.jpg;*.jpeg;*.bmp|JPEG (*.jpg, *.jpeg)|*.jpg;*.jpeg|Bitmap (*.bmp)|*.bmp|All files (*.*)|*.*';
|
||
end;
|
||
|
||
if execute then
|
||
case PageControlBlobEditors.ActivePageIndex of
|
||
0 : DBMemo1.Lines.LoadFromFile(filename);
|
||
1 : EDBImage1.Picture.LoadFromFile(filename);
|
||
end;
|
||
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.btnFilterLoadClick(Sender: TObject);
|
||
var
|
||
menuitem : Tmenuitem;
|
||
m,i : Integer;
|
||
_filename : String;
|
||
dontadd : Boolean;
|
||
begin
|
||
// open where-filter
|
||
With TOpenDialog.Create(self) do begin
|
||
Filter := 'Textfiles (*.txt)|*.txt|SQL-Files (*.sql)|*.sql|All files (*.*)|*.*';
|
||
if Execute and (Filename <> '') then begin
|
||
Screen.Cursor := crHourGlass;
|
||
try
|
||
SynMemoFilter.Lines.LoadFromFile(FileName);
|
||
except
|
||
MessageDLG('Error while reading file ''' + filename + '''', mtError, [mbOK], 0);
|
||
end;
|
||
Screen.Cursor := crDefault;
|
||
|
||
// don't get one filename more than one time
|
||
dontadd := false;
|
||
for m:=0 to popupFilterOpenFile.Items.Count-1 do begin
|
||
_filename := popupFilterOpenFile.Items[m].Caption;
|
||
i := 0;
|
||
while _filename[i] <> ' ' do
|
||
Inc( i );
|
||
_filename := Copy(_filename, i+1, Length(_filename));
|
||
_filename := Stringreplace(_filename, '&', '', [rfReplaceAll]);
|
||
if _filename = FileName then
|
||
dontadd := true;
|
||
end;
|
||
|
||
if not dontadd then begin
|
||
with TRegistry.Create do begin
|
||
openkey(REGPATH, true);
|
||
for i:=1 to 10 do begin
|
||
if not ValueExists('SQLWhereFile'+IntToStr(i)) then
|
||
break;
|
||
end;
|
||
while i > 1 do begin
|
||
WriteString('SQLWhereFile'+IntToStr(i), ReadString('SQLWhereFile'+IntToStr(i-1)));
|
||
dec(i);
|
||
end;
|
||
WriteString('SQLWhereFile1', FileName);
|
||
|
||
i := 1;
|
||
popupTreeView.Items.Clear;
|
||
while ValueExists('SQLWhereFile'+IntToStr(i)) do begin
|
||
menuitem := Tmenuitem.Create(self);
|
||
menuitem.Caption := IntToStr(popupFilterOpenFile.Items.count+1) + ' ' + ReadString('SQLWhereFile'+IntToStr(i));
|
||
menuitem.OnClick := LoadSQLWhereFile;
|
||
popupFilterOpenFile.Items.Add(menuitem);
|
||
Inc( i );
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TMDIChild.btnFilterSaveClick(Sender: TObject);
|
||
begin
|
||
// save where-filter
|
||
With TSaveDialog.Create(self) do begin
|
||
Filter := 'Textfiles (*.txt)|*.txt|SQL-Files (*.sql)|*.sql|All files (*.*)|*.*';
|
||
FileName := ActualTable;
|
||
Options := [ofOverwritePrompt,ofEnableSizing];
|
||
DefaultExt := 'txt';
|
||
if Execute and (Filename <> '') then
|
||
try
|
||
SynMemoFilter.Lines.SaveToFile(FileName);
|
||
except
|
||
MessageDLG('Error while reading file ''' + filename + '''', mtError, [mbOK], 0);
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TMDIChild.SetFilter(Sender: TObject);
|
||
var
|
||
where : String;
|
||
reg : TRegistry;
|
||
reg_value : String;
|
||
begin
|
||
// set filter for data-tab
|
||
where := trim(self.SynMemoFilter.Text);
|
||
|
||
// Store whereclause in Registry
|
||
reg := TRegistry.Create;
|
||
try
|
||
reg.openkey( REGPATH + '\Servers\' + FConn.Description, false );
|
||
reg_value := 'WHERECLAUSE_' + ActualDatabase + '.' + ActualTable;
|
||
if where <> '' then
|
||
reg.WriteString( reg_value, where )
|
||
else
|
||
if reg.ValueExists( reg_value ) then
|
||
reg.DeleteValue( reg_value );
|
||
finally
|
||
reg.Free;
|
||
end;
|
||
|
||
// store filter for browsing purpose:
|
||
if where <> '' then begin
|
||
if WhereFilters = nil then begin // Create filters-list
|
||
WhereFilters := TStringList.Create;
|
||
btnFilterPrevious.Enabled := true;
|
||
btnFilterNext.Enabled := true;
|
||
end;
|
||
if WhereFilters.IndexOf(where) > -1 then // Filter was previously used:
|
||
WhereFiltersIndex := WhereFilters.IndexOf(where)
|
||
else begin // New Filter:
|
||
WhereFilters.add(where);
|
||
WhereFiltersIndex := WhereFilters.count-1;
|
||
end;
|
||
ComboBoxWhereFilters.Items := WhereFilters;
|
||
ComboBoxWhereFilters.ItemIndex := WhereFiltersIndex;
|
||
end;
|
||
Filter1.checked := where <> '';
|
||
|
||
viewdata(self);
|
||
end;
|
||
|
||
procedure TMDIChild.ClearFilter(Sender: TObject);
|
||
begin
|
||
SynMemoFilter.Lines.Clear;
|
||
SetFilter(self);
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.LoadSQLWhereFile(Sender: TObject);
|
||
var
|
||
filename : String;
|
||
i : Integer;
|
||
begin
|
||
// Load SQLWhere File from Menuitem
|
||
Screen.Cursor := crHourGlass;
|
||
filename := (sender as TMenuItem).Caption;
|
||
i := 0;
|
||
while filename[i] <> ' ' do
|
||
Inc( i );
|
||
filename := Copy(filename, i+1, Length(filename));
|
||
filename := Stringreplace(filename, '&', '', [rfReplaceAll]);
|
||
|
||
try
|
||
SynMemoFilter.Lines.LoadFromFile(filename);
|
||
except
|
||
MessageDLG('Error while reading file ''' + filename + '''', mtError, [mbOK], 0);
|
||
end;
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
|
||
procedure TMDIChild.DropFilter1Click(Sender: TObject);
|
||
begin
|
||
// Drop Filter
|
||
ClearFilter(Self);
|
||
viewdata(self);
|
||
end;
|
||
|
||
procedure TMDIChild.MenuChangeTypeClick(Sender: TObject);
|
||
var
|
||
i : Integer;
|
||
tabletype : String;
|
||
begin
|
||
tabletype := (Sender as TMenuItem).Caption;
|
||
tabletype := StringReplace( tabletype, '&', '', [rfReplaceAll] ); // Remove Auto-Hotkey
|
||
for i:=0 to ListTables.Items.Count - 1 do
|
||
if ListTables.Items[i].Selected then
|
||
ExecUpdateQuery( 'ALTER TABLE ' + mask(ListTables.Items[i].Caption) + ' TYPE = ' + tabletype);
|
||
ShowDBProperties(self);
|
||
end;
|
||
|
||
procedure TMDIChild.MenuChangeTypeOtherClick(Sender: TObject);
|
||
var
|
||
i : Integer;
|
||
strtype : String;
|
||
begin
|
||
// change table-type:
|
||
if inputquery('Change table-type...','New table-type:', strtype) then begin
|
||
for i:=0 to ListTables.Items.Count - 1 do
|
||
if ListTables.Items[i].Selected then
|
||
ExecUpdateQuery( 'ALTER TABLE ' + mask(ListTables.Items[i].Caption) + ' TYPE = ' + strtype );
|
||
ShowDBProperties(self);
|
||
end;
|
||
end;
|
||
|
||
procedure TMDIChild.InsertRecord(Sender: TObject);
|
||
begin
|
||
viewdata(self);
|
||
GetVisualDataset().Insert;
|
||
end;
|
||
|
||
|
||
// select all tables
|
||
procedure TMDIChild.selectall1Click(Sender: TObject);
|
||
var i : Integer;
|
||
begin
|
||
for i:=0 to ListTables.Items.count-1 do
|
||
ListTables.Items[i].Selected := true;
|
||
end;
|
||
|
||
procedure TMDIChild.popupQueryPopup(Sender: TObject);
|
||
var
|
||
NotInFilterMemo,
|
||
somechars : Boolean;
|
||
begin
|
||
// Depending which SynMemo is focused, (de-)activate some menuitems
|
||
// The popupQuery is used in both Filter- and Query-Memo
|
||
NotInFilterMemo := Not SynMemoFilter.Focused;
|
||
|
||
MenuSetFilter.Visible := Not NotInFilterMemo;
|
||
|
||
MenuRun.Visible := NotInFilterMemo;
|
||
MenuRunSelection.Visible := NotInFilterMemo;
|
||
MenuRunLine.Visible := NotInFilterMemo;
|
||
|
||
MenuCopy.Visible := NotInFilterMemo;
|
||
MenuPaste.Visible := NotInFilterMemo;
|
||
|
||
MenuFind.Visible := NotInFilterMemo;
|
||
MenuReplace.Visible := NotInFilterMemo;
|
||
|
||
MenuLoad.Visible := NotInFilterMemo;
|
||
MenuInsertFileAtCursor.Visible := NotInFilterMemo;
|
||
MenuSave.Visible := NotInFilterMemo;
|
||
MenuSaveSelectionToFile.Visible := NotInFilterMemo;
|
||
MenuSaveAsSnippet.Visible := NotInFilterMemo;
|
||
MenuSaveSelectionAsSnippet.Visible := NotInFilterMemo;
|
||
|
||
if NotInFilterMemo then
|
||
begin
|
||
somechars := SynMemoQuery.GetTextLen > 0;
|
||
MenuRun.ShortCut := TextToShortCut('F9'); // Exec SQL with F9
|
||
MenuSetFilter.ShortCut := TextToShortCut('');
|
||
// Inserting file at cursor only makes sense with content
|
||
MenuInsertFileAtCursor.Enabled := somechars;
|
||
Menusave.Enabled := somechars;
|
||
MenuSaveSelectionToFile.Enabled := SynMemoQuery.SelAvail;
|
||
MenuSaveAsSnippet.Enabled := somechars;
|
||
MenuSaveSelectionAsSnippet.Enabled := SynMemoQuery.SelAvail;
|
||
end
|
||
else
|
||
begin
|
||
somechars := SynMemoFilter.GetTextLen > 0;
|
||
MenuRun.ShortCut := TextToShortCut('');
|
||
MenuSetFilter.ShortCut := TextToShortCut('F9'); // set Filter with F9
|
||
end;
|
||
MenuClear.Enabled := somechars;
|
||
end;
|
||
|
||
procedure TMDIChild.popupResultGridPopup(Sender: TObject);
|
||
begin
|
||
// data available?
|
||
// MainForm.Save2CSV.enabled :=
|
||
end;
|
||
|
||
procedure TMDIChild.gridDataColumnMoved(Sender: TObject; FromIndex,
|
||
ToIndex: Integer);
|
||
begin
|
||
// prevent OnTitleClick from being executed:
|
||
abort;
|
||
end;
|
||
|
||
procedure TMDIChild.gridMouseDown(Sender: TObject; Button: TMouseButton;
|
||
Shift: TShiftState; X, Y: Integer);
|
||
var
|
||
ed: TCustomEdit;
|
||
begin
|
||
if Button = mbRight then begin
|
||
// Select text in in-place editor to make popup menu copy and paste work.
|
||
(Sender as TDBGrid).EditorMode := true;
|
||
ed := TCustomEdit(FindControl(GetFocus()));
|
||
ed.SelectAll;
|
||
//debug('right-click catched, selected all text ''' + ed.SelText + '''.');
|
||
// Popup menu manually, mucking about with the grid causes it to stop doing it by itself.
|
||
if sender = gridData then begin
|
||
popupDataGrid.Popup(Mouse.CursorPos.X, Mouse.CursorPos.Y);
|
||
end else if sender = gridQuery then begin
|
||
popupResultGrid.Popup(Mouse.CursorPos.X, Mouse.CursorPos.Y);
|
||
end;
|
||
end else begin
|
||
inherited MouseDown(Button, Shift, X, Y);
|
||
end;
|
||
end;
|
||
|
||
procedure TMDIChild.controlsKeyUp(Sender: TObject; var Key: Word;
|
||
Shift: TShiftState);
|
||
begin
|
||
// Check for F1-pressed
|
||
if Key = VK_F1 then
|
||
CallSQLHelp( self )
|
||
|
||
// Simulate Ctrl+A-behaviour of common editors
|
||
else if ( Shift = [ssCtrl] ) and ( Key = Ord('A') ) then
|
||
begin
|
||
if Sender = DBMemo1 then
|
||
DBMemo1.SelectAll
|
||
else if Sender = EditDataSearch then
|
||
EditDataSearch.SelectAll;
|
||
end;
|
||
end;
|
||
|
||
procedure TMDIChild.Autoupdate1Click(Sender: TObject);
|
||
var
|
||
seconds : String;
|
||
secondsInt : Integer;
|
||
begin
|
||
// set interval for autorefresh-timer
|
||
seconds := IntToStr(TimerProcesslist.interval div 1000);
|
||
if inputquery('Auto-refresh processlist','Update list every ... seconds:', seconds) then begin
|
||
secondsInt := StrToIntDef(seconds, 0);
|
||
if secondsInt > 0 then begin
|
||
TimerProcesslist.Interval := secondsInt * 1000;
|
||
TimerProcesslist.Enabled := true;
|
||
EnableAutoRefresh.Checked := true;
|
||
DisableAutoRefresh.Checked := false;
|
||
end
|
||
else
|
||
MessageDLG('Seconds must be between 1 and ' + IntToStr(maxint) + '.', mtError, [mbOK], 0);
|
||
end;
|
||
end;
|
||
|
||
procedure TMDIChild.EnableAutoRefreshClick(Sender: TObject);
|
||
begin
|
||
// enable autorefresh-timer
|
||
TimerProcesslist.Enabled := true;
|
||
EnableAutoRefresh.Checked := true;
|
||
DisableAutoRefresh.Checked := false;
|
||
end;
|
||
|
||
procedure TMDIChild.DisableAutoRefreshClick(Sender: TObject);
|
||
begin
|
||
// enable autorefresh-timer
|
||
TimerProcesslist.Enabled := false;
|
||
EnableAutoRefresh.Checked := false;
|
||
DisableAutoRefresh.Checked := true;
|
||
end;
|
||
|
||
|
||
|
||
procedure TMDIChild.SynMemoQueryDragOver(Sender, Source: TObject; X,
|
||
Y: Integer; State: TDragState; var Accept: Boolean);
|
||
var
|
||
src : TControl;
|
||
begin
|
||
// dragging an object over the query-memo
|
||
src := Source as TControl;
|
||
// Accepting drag's from DBTree and QueryHelpers
|
||
Accept := (src = DBTree) or (src = lboxQueryHelpers);
|
||
// set x-position of cursor
|
||
SynMemoQuery.CaretX := (x - SynMemoQuery.Gutter.Width) div SynMemoQuery.CharWidth - 1 + SynMemoQuery.LeftChar;
|
||
// set y-position of cursor
|
||
SynMemoQuery.CaretY := y div SynMemoQuery.LineHeight + SynMemoQuery.TopLine;
|
||
if not SynMemoQuery.Focused then
|
||
SynMemoQuery.SetFocus;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.SynMemoQueryDragDrop(Sender, Source: TObject; X,
|
||
Y: Integer);
|
||
var
|
||
src : TControl;
|
||
Text : String;
|
||
LoadText : Boolean;
|
||
begin
|
||
// dropping a TTreeNode into the query-memo
|
||
SynMemoQuery.UndoList.AddGroupBreak;
|
||
src := Source as TControl;
|
||
Text := 'Error: Unspecified source control in drag''n drop operation!';
|
||
LoadText := True;
|
||
// Check for allowed controls as source has already
|
||
// been performed in OnDragOver. So, only do typecasting here.
|
||
if src is TTreeView then
|
||
begin
|
||
Text := (src as TTreeView).Selected.Text;
|
||
end
|
||
else if src = lboxQueryHelpers then
|
||
begin
|
||
Text := (src as TListBox).Items[(src as TListBox).ItemIndex];
|
||
if tabsetQueryHelpers.TabIndex = 3 then
|
||
begin
|
||
QueryLoad( DIRNAME_SNIPPETS + Text + '.sql', False );
|
||
LoadText := False;
|
||
end;
|
||
end;
|
||
// Only insert text if no previous action did the job.
|
||
// Should be false when dropping a snippet-file here
|
||
if LoadText then
|
||
SynMemoQuery.SelText := Text;
|
||
SynMemoQuery.UndoList.AddGroupBreak;
|
||
end;
|
||
|
||
|
||
|
||
procedure TMDIChild.SynMemoQueryDropFiles(Sender: TObject; X, Y: Integer;
|
||
AFiles: TStrings);
|
||
var
|
||
i : Integer;
|
||
begin
|
||
// one or more files from explorer or somewhere else was
|
||
// dropped onto the query-memo - let's load their contents:
|
||
for i:=0 to AFiles.Count-1 do
|
||
begin
|
||
if fileExists(AFiles[i]) then
|
||
begin
|
||
QueryLoad( AFiles[i], false );
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TMDIChild.SynMemoQueryKeyUp(Sender: TObject; var Key: Word;
|
||
Shift: TShiftState);
|
||
begin
|
||
SynMemoQuery.OnChange(self);
|
||
controlsKeyUp( Sender, Key, Shift );
|
||
end;
|
||
|
||
procedure TMDIChild.SynMemoQueryMouseUp(Sender: TObject; Button: TMouseButton;
|
||
Shift: TShiftState; X, Y: Integer);
|
||
begin
|
||
SynMemoQuery.OnChange(Self);
|
||
end;
|
||
|
||
procedure TMDIChild.popupHostPopup(Sender: TObject);
|
||
begin
|
||
MenuAutoupdate.Enabled := PageControlHost.ActivePage = tabProcessList;
|
||
end;
|
||
|
||
procedure TMDIChild.ListTablesEditing(Sender: TObject; Item: TListItem;
|
||
var AllowEdit: Boolean);
|
||
begin
|
||
// so that one can press DEL when editing
|
||
menudroptable.ShortCut := TextToShortCut('');
|
||
end;
|
||
|
||
|
||
|
||
procedure TMDIChild.Saveastextfile1Click(Sender: TObject);
|
||
begin
|
||
with TSaveDialog.Create(self) do begin
|
||
Filter := 'Textfiles (*.txt)|*.txt|All Files (*.*)|*.*';
|
||
DefaultExt := 'txt';
|
||
FilterIndex := 1;
|
||
Options := [ofOverwritePrompt,ofHideReadOnly,ofEnableSizing];
|
||
if Execute then begin
|
||
Screen.Cursor := crHourglass;
|
||
SynMemoSQLLog.Lines.SaveToFile(Filename);
|
||
Screen.Cursor := crdefault;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TMDIChild.popupTreeViewPopup(Sender: TObject);
|
||
begin
|
||
// toggle drop-items and remember right-clicked item
|
||
PopupMenuDropDatabase.Enabled := DBtree.Selected.Level = 1;
|
||
MainForm.DropTable.Enabled := DBtree.Selected.Level = 2;
|
||
DBRightClickSelectedItem := DBtree.Selected;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.btnQuerySaveSnippetClick(Sender: TObject);
|
||
var
|
||
snippetname : String;
|
||
sfile : Textfile;
|
||
mayChange : Boolean;
|
||
begin
|
||
// Save snippet
|
||
if InputQuery( 'Save snippet', 'Snippet name:', snippetname) then
|
||
begin
|
||
if Copy( snippetname, Length(snippetname)-4, 4 ) <> '.sql' then
|
||
snippetname := snippetname + '.sql';
|
||
// cleanup snippetname from special characters
|
||
snippetname := DIRNAME_SNIPPETS + goodfilename(snippetname);
|
||
if FileExists( snippetname ) then
|
||
begin
|
||
if MessageDlg( 'Overwrite existing snippet '+snippetname+'?', mtConfirmation, [mbOK, mbCancel], 0 ) <> mrOK then
|
||
exit;
|
||
end;
|
||
Screen.Cursor := crHourglass;
|
||
AssignFile( sfile, snippetname );
|
||
Rewrite( sfile );
|
||
// Save complete content or just the selected text,
|
||
// depending on the tag of calling control
|
||
case (Sender as TComponent).Tag of
|
||
0: Write( sfile, SynMemoQuery.Text );
|
||
1: Write( sfile, SynMemoQuery.SelText );
|
||
end;
|
||
CloseFile( sfile );
|
||
FillPopupQueryLoad;
|
||
if tabsetQueryHelpers.TabIndex = 3 then begin
|
||
// SQL Snippets selected in query helper, refresh list
|
||
mayChange := True; // Unused; satisfies callee parameter collection which is probably dictated by tabset.
|
||
tabsetQueryHelpersChange(Sender, 3, mayChange);
|
||
end;
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
end;
|
||
|
||
procedure TMDIChild.CallSQLHelp(Sender: TObject);
|
||
var
|
||
keyword : String;
|
||
begin
|
||
// Call SQL Help from various places
|
||
if mysql_version < 40100 then
|
||
exit;
|
||
|
||
keyword := '';
|
||
// Query-Tab
|
||
if SynMemoQuery.Focused then
|
||
keyword := SynMemoQuery.WordAtCursor
|
||
// LogSQL-Tab
|
||
else if SynMemoSQLLog.Focused then
|
||
keyword := SynMemoQuery.WordAtCursor
|
||
// Data-Tab
|
||
else if (PageControlMain.ActivePage = tabData)
|
||
and (-1 < gridData.Col)
|
||
and (gridData.Col <= ListColumns.Items.Count) then
|
||
begin
|
||
keyword := ListColumns.Items[gridData.Col-1].SubItems[0];
|
||
end
|
||
// Table-Tab
|
||
else if ListColumns.Focused and (ListColumns.Selected <> nil) then
|
||
begin
|
||
keyword := ListColumns.Selected.SubItems[0];
|
||
end
|
||
else if lboxQueryHelpers.Focused then
|
||
begin
|
||
// Makes only sense if one of the tabs "SQL fn" or "SQL kw" was selected
|
||
if tabsetQueryHelpers.TabIndex in [1,2] then
|
||
begin
|
||
keyword := lboxQueryHelpers.Items[lboxQueryHelpers.ItemIndex];
|
||
end;
|
||
end;
|
||
|
||
// Clean existing paranthesis, fx: char(64)
|
||
if Pos( '(', keyword ) > 0 then
|
||
begin
|
||
keyword := Copy( keyword, 1, Pos( '(', keyword )-1 );
|
||
end;
|
||
|
||
// Show the window
|
||
CallSQLHelpWithKeyword( keyword );
|
||
end;
|
||
|
||
|
||
|
||
{***
|
||
Show SQL Help window directly using a keyword
|
||
@param String SQL-keyword
|
||
@see FieldeditForm.btnDatatypeHelp
|
||
}
|
||
procedure TMDIChild.CallSQLHelpWithKeyword( keyword: String );
|
||
begin
|
||
// Set help-keyword and show window
|
||
SQLhelpWindow(self, keyword);
|
||
end;
|
||
|
||
procedure TMDIChild.ToolButton4Click(Sender: TObject);
|
||
begin
|
||
SaveBlob;
|
||
mainform.HTMLviewExecute(Sender);
|
||
end;
|
||
|
||
procedure TMDIChild.btnQuerySaveClick(Sender: TObject);
|
||
var
|
||
f : TextFile;
|
||
begin
|
||
// Save SQL
|
||
if SaveDialogSQLFile.Execute then
|
||
begin
|
||
Screen.Cursor := crHourGlass;
|
||
AssignFile(f, SaveDialogSQLFile.FileName);
|
||
Rewrite(f);
|
||
// Save complete content or just the selected text,
|
||
// depending on the tag of calling control
|
||
case (Sender as TComponent).Tag of
|
||
0: Write( f, SynMemoQuery.Text );
|
||
1: Write( f, SynMemoQuery.SelText );
|
||
end;
|
||
CloseFile(f);
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.popupQueryLoadClick( sender: TObject );
|
||
var
|
||
filename : String;
|
||
p : Integer;
|
||
begin
|
||
// Click on the popupQueryLoad
|
||
filename := (Sender as TMenuItem).Caption;
|
||
if Pos( '\', filename ) = 0 then
|
||
begin // assuming we load a snippet
|
||
filename := DIRNAME_SNIPPETS + filename + '.sql';
|
||
end
|
||
else
|
||
begin // assuming we load a file from the recent-list
|
||
p := Pos( ' ', filename ) + 1;
|
||
filename := Copy(filename, p, Length(filename));
|
||
end;
|
||
filename := Stringreplace(filename, '&', '', [rfReplaceAll]);
|
||
QueryLoad( filename );
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.btnQueryLoadClick(Sender: TObject);
|
||
begin
|
||
// Click on the btnQueryLoad
|
||
if OpenDialogSQLfile.Execute then
|
||
begin
|
||
QueryLoad( OpenDialogSQLfile.FileName );
|
||
end;
|
||
end;
|
||
|
||
|
||
{**
|
||
Insert SQL file at cursor
|
||
}
|
||
procedure TMDIChild.menuInsertFileAtCursorClick(Sender: TObject);
|
||
begin
|
||
if OpenDialogSQLfile.Execute then
|
||
begin
|
||
QueryLoad( OpenDialogSQLfile.FileName, False );
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.AddOrRemoveFromQueryLoadHistory( filename: String; AddIt: Boolean = true; CheckIfFileExists: Boolean = true );
|
||
var
|
||
i : Integer;
|
||
Values, newfilelist : TStringList;
|
||
reg : TRegistry;
|
||
savedfilename : String;
|
||
begin
|
||
// Add or remove filename to/from history, avoiding duplicates
|
||
|
||
reg := TRegistry.Create;
|
||
reg.openkey(REGPATH, true);
|
||
newfilelist := TStringList.create;
|
||
Values := TStringList.create;
|
||
reg.GetValueNames( Values );
|
||
|
||
// Add new filename
|
||
if AddIt then
|
||
begin
|
||
newfilelist.Add( filename );
|
||
end;
|
||
|
||
// Add all other filenames
|
||
for i:=0 to Values.Count-1 do
|
||
begin
|
||
if Pos( 'SQLFile', Values[i] ) <> 1 then
|
||
continue;
|
||
savedfilename := reg.ReadString( Values[i] );
|
||
reg.DeleteValue( Values[i] );
|
||
if CheckIfFileExists and (not FileExists( savedfilename )) then
|
||
continue;
|
||
if (savedfilename <> filename) and (newfilelist.IndexOf(savedfilename)=-1) then
|
||
newfilelist.add( savedfilename );
|
||
end;
|
||
|
||
// Save new list
|
||
for i := 0 to newfilelist.Count-1 do
|
||
begin
|
||
if i >= 20 then
|
||
break;
|
||
reg.WriteString( 'SQLFile'+IntToStr(i), newfilelist[i] );
|
||
end;
|
||
|
||
reg.Free;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.QueryLoad( filename: String; ReplaceContent: Boolean = true );
|
||
|
||
var
|
||
tmpstr, filecontent : String;
|
||
f : TextFile;
|
||
msgtext : String;
|
||
begin
|
||
// Ask for action when loading a big file
|
||
if _GetFileSize( filename ) > LOAD_SIZE then
|
||
begin
|
||
msgtext := 'The file you are about to load is bigger than '+FormatByteNumber(LOAD_SIZE, 0)+'.' + CRLF + CRLF +
|
||
'Do you want to just run the file to avoid loading it completely into the query-editor ( = memory ) ?' + CRLF + CRLF +
|
||
'Press' + CRLF +
|
||
' [Yes] to run the file without loading it into the editor' + CRLF +
|
||
' [No] to load the file into the query editor' + CRLF +
|
||
' [Cancel] to cancel file opening.';
|
||
case MessageDlg( msgtext, mtWarning, [mbYes, mbNo, mbCancel], 0 ) of
|
||
mrYes: // Run the file, don't load it into the editor
|
||
begin
|
||
RunSQLFileWindow( Self, filename );
|
||
// Add filename to history menu
|
||
if Pos( DIRNAME_SNIPPETS, filename ) = 0 then
|
||
AddOrRemoveFromQueryLoadHistory( filename, true );
|
||
// Don't load into editor
|
||
abort;
|
||
end;
|
||
|
||
mrNo:; // Do nothing, just load the file normally into the editor
|
||
|
||
mrCancel: // Cancel opening file
|
||
abort;
|
||
|
||
end;
|
||
end;
|
||
|
||
// Load file and add that to the undo-history of SynEdit.
|
||
// Normally we would do a simple SynMemo.Lines.LoadFromFile but
|
||
// this would prevent SynEdit from adding this step to the undo-history
|
||
// so we have to do it by replacing the SelText property
|
||
Screen.Cursor := crHourGlass;
|
||
try
|
||
AssignFile( f, filename );
|
||
Reset( f );
|
||
while not eof( f ) do
|
||
begin
|
||
Readln( f, tmpstr );
|
||
filecontent := filecontent + tmpstr + CRLF;
|
||
end;
|
||
except
|
||
on E: Exception do
|
||
begin
|
||
MessageDLG( 'Error while reading file ' + filename + ':' + CRLF + CRLF + E.Message, mtError, [mbOK], 0);
|
||
AddOrRemoveFromQueryLoadHistory( filename, false );
|
||
FillPopupQueryLoad;
|
||
Screen.Cursor := crDefault;
|
||
exit;
|
||
end;
|
||
end;
|
||
CloseFile( f );
|
||
|
||
SynCompletionProposal1.Editor.UndoList.AddGroupBreak;
|
||
SynMemoQuery.BeginUpdate;
|
||
if ReplaceContent then
|
||
begin
|
||
SynMemoQuery.SelectAll;
|
||
end;
|
||
SynMemoQuery.SelText := filecontent;
|
||
SynMemoQuery.SelStart := SynMemoQuery.SelEnd;
|
||
SynMemoQuery.EndUpdate;
|
||
SynMemoQueryChange( self );
|
||
|
||
if Pos( DIRNAME_SNIPPETS, filename ) = 0 then
|
||
AddOrRemoveFromQueryLoadHistory( filename, true );
|
||
FillPopupQueryLoad;
|
||
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.FillPopupQueryLoad;
|
||
var
|
||
i, j : Integer;
|
||
menuitem, snippetsfolder : TMenuItem;
|
||
snippets : TStringList;
|
||
sqlFilename : String;
|
||
begin
|
||
// Fill the popupQueryLoad menu
|
||
|
||
popupQueryLoad.Items.Clear;
|
||
|
||
// Snippets
|
||
snippets := getFilesFromDir( DIRNAME_SNIPPETS, '*.sql', true );
|
||
snippetsfolder := TMenuItem.Create( popupQueryLoad );
|
||
snippetsfolder.Caption := 'Snippets';
|
||
popupQueryLoad.Items.Add(snippetsfolder);
|
||
for i := 0 to snippets.Count - 1 do
|
||
begin
|
||
menuitem := TMenuItem.Create( snippetsfolder );
|
||
menuitem.Caption := snippets[i];
|
||
menuitem.OnClick := popupQueryLoadClick;
|
||
snippetsfolder.Add(menuitem);
|
||
end;
|
||
|
||
// Separator
|
||
menuitem := TMenuItem.Create( popupQueryLoad );
|
||
menuitem.Caption := '-';
|
||
popupQueryLoad.Items.Add(menuitem);
|
||
|
||
// Recent files
|
||
with TRegistry.Create do
|
||
begin
|
||
openkey(REGPATH, true);
|
||
j := 0;
|
||
for i:=0 to 19 do
|
||
begin
|
||
if not ValueExists('SQLFile'+IntToStr(i)) then
|
||
continue;
|
||
sqlFilename := ReadString( 'SQLFile'+IntToStr(i) );
|
||
inc(j);
|
||
menuitem := TMenuItem.Create( popupQueryLoad );
|
||
menuitem.Caption := IntToStr(j) + ' ' + sqlFilename;
|
||
menuitem.OnClick := popupQueryLoadClick;
|
||
popupQueryLoad.Items.Add(menuitem);
|
||
end;
|
||
Free;
|
||
end;
|
||
|
||
// Separator + "Remove absent files"
|
||
menuitem := TMenuItem.Create( popupQueryLoad );
|
||
menuitem.Caption := '-';
|
||
popupQueryLoad.Items.Add(menuitem);
|
||
menuitem := TMenuItem.Create( popupQueryLoad );
|
||
menuitem.Caption := 'Remove absent files';
|
||
menuitem.OnClick := PopupQueryLoadRemoveAbsentFiles;
|
||
popupQueryLoad.Items.Add(menuitem);
|
||
|
||
end;
|
||
|
||
procedure TMDIChild.PopupQueryLoadRemoveAbsentFiles( sender: TObject );
|
||
begin
|
||
AddOrRemoveFromQueryLoadHistory( '', false, true );
|
||
FillPopupQueryLoad;
|
||
end;
|
||
|
||
|
||
|
||
procedure TMDIChild.btnFilterPreviousClick(Sender: TObject);
|
||
begin
|
||
// Go to previous filter
|
||
if WhereFiltersIndex > 0 then begin
|
||
dec(WhereFiltersIndex);
|
||
ComboBoxWhereFilters.ItemIndex := WhereFiltersIndex;
|
||
SynMemoFilter.Text := WhereFilters[WhereFiltersIndex];
|
||
end;
|
||
end;
|
||
|
||
procedure TMDIChild.btnFilterNextClick(Sender: TObject);
|
||
begin
|
||
// Go to next filter
|
||
if WhereFiltersIndex < WhereFilters.count-1 then begin
|
||
inc(WhereFiltersIndex);
|
||
ComboBoxWhereFilters.ItemIndex := WhereFiltersIndex;
|
||
SynMemoFilter.Text := WhereFilters[WhereFiltersIndex];
|
||
end;
|
||
end;
|
||
|
||
procedure TMDIChild.ComboBoxWhereFiltersChange(Sender: TObject);
|
||
begin
|
||
WhereFiltersIndex := ComboBoxWhereFilters.ItemIndex;
|
||
SynMemoFilter.Text := ComboBoxWhereFilters.Items[ComboBoxWhereFilters.ItemIndex];
|
||
end;
|
||
|
||
procedure TMDIChild.btnQueryStopOnErrorsClick(Sender: TObject);
|
||
begin
|
||
StopOnErrors := not StopOnErrors;
|
||
btnQueryStopOnErrors.Down := StopOnErrors;
|
||
end;
|
||
|
||
procedure TMDIChild.btnTableViewDataClick(Sender: TObject);
|
||
begin
|
||
ShowTableData(ActualTable);
|
||
end;
|
||
|
||
procedure TMDIChild.btnUnsafeEditClick(Sender: TObject);
|
||
var
|
||
confirmed: Boolean;
|
||
msg: String;
|
||
begin
|
||
msg :=
|
||
'This will remove the first NUL character _and all text following it_.' + #13#10 +
|
||
'All newlines will be converted to CRLF format.' + #13#10 +
|
||
'Are you sure?';
|
||
confirmed := MessageDlg(msg, mtConfirmation, mbYesNo, 0) = mrYes;
|
||
if not confirmed then exit;
|
||
DBMemo1.ReadOnly := false;
|
||
DBMemo1.Color := clWindow;
|
||
btnUnsafeEdit.Enabled := false;
|
||
DBMemo1.SetFocus;
|
||
// Sets the grid in edit-state so posting won't ignore changes
|
||
gridData.DataSource.Edit;
|
||
end;
|
||
|
||
procedure TMDIChild.DBGridDblClick(Sender: TObject);
|
||
begin
|
||
// If grid is not empty...
|
||
if (Sender as TDBGrid).SelectedField <> nil then begin
|
||
// Set focus on DBMemo when user doubleclicks a (MEMO)-cell
|
||
if (sender as TSMDBGrid).SelectedField.IsBlob and (PageControlBlobEditors.ActivePage = tabBlobEditorText) then begin
|
||
PageControlBottom.ActivePage := tabBlobEditor;
|
||
DBMemo1.SetFocus;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TMDIChild.SaveDialogExportDataTypeChange(Sender: TObject);
|
||
begin
|
||
// Set default file-extension of saved file and options on the dialog to show
|
||
with SaveDialogExportData do begin
|
||
Case FilterIndex of
|
||
1 : begin
|
||
DefaultExt := 'csv';
|
||
VisibleOptions := voCSV;
|
||
end;
|
||
2 : begin
|
||
DefaultExt := 'html';
|
||
VisibleOptions := voHTML;
|
||
end;
|
||
3 : begin
|
||
DefaultExt := 'xml';
|
||
VisibleOptions := voHTML;
|
||
end;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TMDIChild.DBGridGetCellParams(Sender: TObject; Field: TField;
|
||
AFont: TFont; var Background: TColor; Highlight: Boolean);
|
||
begin
|
||
if (Sender as TDBGrid).SelectedRows.CurrentRowSelected then begin
|
||
background := clInfoBK;
|
||
afont.Color := clInfoText;
|
||
end;
|
||
if field.IsNull then background := mainform.DataNullBackground;
|
||
end;
|
||
|
||
procedure TMDIChild.DBMemo1Exit(Sender: TObject);
|
||
var
|
||
ds: TDataSource;
|
||
begin
|
||
// Save changes. Fixes issue #1538021.
|
||
ds := DBMemo1.DataSource;
|
||
if ds.State in [dsEdit, dsInsert] then begin
|
||
ds.DataSet.Post;
|
||
end;
|
||
end;
|
||
|
||
procedure TMDIChild.popupDataGridPopup(Sender: TObject);
|
||
var
|
||
y,m,d,h,i,s,ms : Word;
|
||
cpText, selectedColumn, value : String;
|
||
const
|
||
CLPBRD : String = 'CLIPBOARD';
|
||
begin
|
||
{DONE -oFrancisco -cData-browsing:Bugfix: [1650528] Access violation with F5}
|
||
if (gridData.SelectedField <> nil) then
|
||
begin
|
||
DataInsertDateTime.Enabled := gridData.SelectedField.DataType in [ftString, ftDatetime, ftDate, ftTime];
|
||
if DataInsertDateTime.Enabled then
|
||
begin
|
||
decodedate(now, y, m, d);
|
||
decodetime(now, h, i, s, ms);
|
||
DataDateTime.Caption := Format('%.4d-%.2d-%.2d %.2d:%.2d:%.2d', [y,m,d,h,i,s]);
|
||
DataDate.Caption := Format('%.4d-%.2d-%.2d', [y,m,d]);
|
||
DataTime.Caption := Format('%.2d:%.2d:%.2d', [h,i,s]);
|
||
DataTimestamp.caption := Format('%.4d%.2d%.2d%.2d%.2d%.2d', [y,m,d,h,i,s]);
|
||
DataYear.Caption := Format('%.4d', [y]);
|
||
end;
|
||
|
||
// Manipulate the Quick-filter menuitems
|
||
selectedColumn := mask(gridData.SelectedField.FieldName);
|
||
// 1. block: include selected columnname and value from datagrid in caption
|
||
value := sstr(gridData.SelectedField.AsString, 100);
|
||
QF1.Caption := selectedColumn + ' = ' + esc( value );
|
||
QF2.Caption := selectedColumn + ' != ' + esc( value );
|
||
QF3.Caption := selectedColumn + ' > ' + esc( value );
|
||
QF4.Caption := selectedColumn + ' < ' + esc( value );
|
||
QF5.Caption := selectedColumn + ' LIKE ''' + esc( value, true ) + '%''';
|
||
QF6.Caption := selectedColumn + ' LIKE ''%' + esc( value, true ) + '''';
|
||
QF7.Caption := selectedColumn + ' LIKE ''%' + esc( value, true ) + '%''';
|
||
|
||
// 2. block: include only selected columnname in caption
|
||
QF8.Caption := selectedColumn + ' = "..."';
|
||
QF9.Caption := selectedColumn + ' != "..."';
|
||
QF10.Caption := selectedColumn + ' > "..."';
|
||
QF11.Caption := selectedColumn + ' < "..."';
|
||
QF12.Caption := selectedColumn + ' LIKE "%...%"';
|
||
|
||
// 3. block: include selected columnname and clipboard-content in caption for one-click-filtering
|
||
cpText := Clipboard.AsText;
|
||
if Length(cpText) < 100 then
|
||
begin
|
||
QF13.Enabled := true; QF13.Caption := selectedColumn + ' = ' + esc( cpText );
|
||
QF14.Enabled := true; QF14.Caption := selectedColumn + ' != ' + esc( cpText );
|
||
QF15.Enabled := true; QF15.Caption := selectedColumn + ' > ' + esc( cpText );
|
||
QF16.Enabled := true; QF16.Caption := selectedColumn + ' < ' + esc( cpText );
|
||
QF17.Enabled := true; QF17.Caption := selectedColumn + ' LIKE ''%' + esc( cpText, true ) + '%''';
|
||
end
|
||
else
|
||
begin
|
||
QF13.Enabled := false; QF13.Caption := selectedColumn + ' = ' + CLPBRD;
|
||
QF14.Enabled := false; QF14.Caption := selectedColumn + ' != ' + CLPBRD;
|
||
QF15.Enabled := false; QF15.Caption := selectedColumn + ' > ' + CLPBRD;
|
||
QF16.Enabled := false; QF16.Caption := selectedColumn + ' < ' + CLPBRD;
|
||
QF17.Enabled := false; QF17.Caption := selectedColumn + ' LIKE %' + CLPBRD + '%';
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TMDIChild.InsertDate(Sender: TObject);
|
||
var d : String;
|
||
begin
|
||
// Insert date/time-value into table
|
||
d := (sender as TMenuItem).Caption;
|
||
delete(d, Pos('&', d), 1);
|
||
DataSource1.Edit;
|
||
gridData.SelectedField.AsString := d;
|
||
end;
|
||
|
||
procedure TMDIChild.btnBlobCopyClick(Sender: TObject);
|
||
begin
|
||
if dbmemo1.DataField = '' then exit;
|
||
SaveBlob;
|
||
case PageControlBlobEditors.ActivePageIndex of
|
||
0 : clipboard.astext := GetVisualDataset().FieldByName(DBMemo1.DataField).AsString;
|
||
1 : EDBImage1.CopyToClipboard;
|
||
end;
|
||
end;
|
||
|
||
procedure TMDIChild.setNULL1Click(Sender: TObject);
|
||
begin
|
||
if not (DataSource1.State in [dsEdit, dsInsert]) then
|
||
DataSource1.Edit;
|
||
gridData.SelectedField.Clear;
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.ZQueryGridAfterPost(DataSet: TDataSet);
|
||
var
|
||
affected_rows_str, msg : String;
|
||
affected_rows_int : Int64;
|
||
begin
|
||
affected_rows_int := FMysqlConn.Connection.GetAffectedRowsFromLastPost;
|
||
affected_rows_str := FormatNumber( affected_rows_int );
|
||
if affected_rows_int = 0 then
|
||
begin
|
||
LogSQL( 'Affected rows: ' + affected_rows_str );
|
||
// Refresh grid to show the user that no change has been applied
|
||
GetVisualDataset().Refresh;
|
||
msg := 'Warning: No row was affected by the last update.' + CRLF + CRLF
|
||
+ 'This is most likely caused by entering data which the MySQL-server has converted silently.' + CRLF
|
||
+ 'For example when you enter a date like "0000-01-01" (applies only to newer MySQL-versions).';
|
||
LogSQL( msg );
|
||
MessageDlg( msg, mtWarning, [mbOK], 0);
|
||
end;
|
||
if affected_rows_int > 1 then
|
||
begin
|
||
LogSQL( 'Affected rows: ' + affected_rows_str );
|
||
// Refresh grid to show the user which values the other records got
|
||
GetVisualDataset().Refresh;
|
||
msg := 'Warning: Consistency problem detected.' + CRLF + CRLF
|
||
+ 'The last query affected ' + affected_rows_str + ' rows, when it should have touched only 1 row!'
|
||
+ CRLF + CRLF
|
||
+ 'This is most likely caused by not having a primary key in the table''s definition.';
|
||
LogSQL( msg );
|
||
MessageDlg( msg, mtWarning, [mbOK], 0);
|
||
end;
|
||
|
||
// Display row count and filter-matchings above dbgrid
|
||
DisplayRowCountStats(DataSet);
|
||
end;
|
||
|
||
procedure TMDIChild.ZQueryGridBeforeClose(DataSet: TDataSet);
|
||
begin
|
||
// unassign data-aware controls
|
||
DBMemo1.DataField := '';
|
||
EDBImage1.DataField := '';
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.ExecUseQuery(db: string; HandleErrors: Boolean = false; DisplayErrors: Boolean = false);
|
||
begin
|
||
FConn.MysqlParams.Database := db;
|
||
ExecUpdateQuery('USE ' + mask(db), HandleErrors, DisplayErrors);
|
||
end;
|
||
|
||
|
||
{***
|
||
Execute a query without returning a resultset
|
||
The currently active connection is used
|
||
|
||
@param String The single SQL-query to be executed on the server
|
||
}
|
||
function TMDIChild.ExecUpdateQuery(sql: string; HandleErrors: Boolean = false; DisplayErrors: Boolean = false): Int64;
|
||
var
|
||
MysqlQuery : TMysqlQuery;
|
||
begin
|
||
Result := -1; // Silence compiler warning.
|
||
MysqlQuery := nil;
|
||
try
|
||
try
|
||
// Start query execution
|
||
MysqlQuery := RunThreadedQuery(sql);
|
||
Result := TZQuery(MysqlQuery.MysqlDataset).RowsAffected;
|
||
// Inspect query result code and log / notify user on failure
|
||
if MysqlQuery.Result in [MQR_CONNECT_FAIL,MQR_QUERY_FAIL] then
|
||
begin
|
||
raise Exception.Create(MysqlQuery.Comment);
|
||
end;
|
||
except
|
||
on E: Exception do begin
|
||
LogSQL( E.Message, True );
|
||
if DisplayErrors then MessageDlg( E.Message, mtError, [mbOK], 0 );
|
||
// Recreate exception, since we free it below the caller
|
||
// won't know what happened otherwise.
|
||
if not HandleErrors then raise THandledSQLError.Create(MysqlQuery.Comment);
|
||
Result := -1;
|
||
end;
|
||
end;
|
||
finally
|
||
// Cleanup the MysqlQuery object, we won't need it anymore
|
||
if MysqlQuery <> nil then FreeAndNil (MysqlQuery);
|
||
end;
|
||
end;
|
||
|
||
|
||
{***
|
||
Execute a query which may return a resultset. The caller is responsible for
|
||
freeing the MysqlQuery object and its Dataset member, only on returnvalue True.
|
||
The currently active connection is used
|
||
|
||
@param String The single SQL-query to be executed on the server
|
||
@return TMysqlQuery Containing the dataset and info data availability
|
||
}
|
||
function TMDIChild.ExecSelectQuery(sql: string; HandleErrors: Boolean = false; DisplayErrors: Boolean = false): TDataSet;
|
||
var
|
||
res: TMysqlQuery;
|
||
begin
|
||
res := nil;
|
||
try
|
||
// Start query execution
|
||
res := RunThreadedQuery(sql);
|
||
result := res.MysqlDataset;
|
||
// Inspect query result code and log / notify user on failure
|
||
if res.Result in [MQR_CONNECT_FAIL,MQR_QUERY_FAIL] then
|
||
begin
|
||
raise Exception.Create(res.Comment);
|
||
end;
|
||
except
|
||
on E: Exception do begin
|
||
LogSQL( E.Message, True );
|
||
if DisplayErrors then MessageDlg( E.Message, mtError, [mbOK], 0 );
|
||
if res <> nil then FreeAndNil(res);
|
||
if not HandleErrors then raise THandledSQLError.Create(E.Message);
|
||
Result := nil;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
|
||
{***
|
||
Executes a query.
|
||
}
|
||
function TMDIChild.GetResults( SQLQuery: String; HandleErrors: Boolean = false; DisplayErrors: Boolean = false ): TDataSet;
|
||
begin
|
||
result := ExecSelectQuery(SQLQuery, HandleErrors, DisplayErrors);
|
||
end;
|
||
|
||
|
||
{***
|
||
Execute a query and return String from column x
|
||
}
|
||
function TMDIChild.GetVar( SQLQuery: String; x: Integer = 0; HandleErrors: Boolean = false; DisplayErrors: Boolean = false) : String;
|
||
var
|
||
ds: TDataSet;
|
||
begin
|
||
ds := GetResults( SQLQuery, HandleErrors, DisplayErrors );
|
||
if ds = nil then exit;
|
||
Result := ds.Fields[x].AsString;
|
||
ds.Close;
|
||
end;
|
||
|
||
|
||
function TMDIChild.GetNamedVar( SQLQuery: String; x: String; HandleErrors: Boolean = false; DisplayErrors: Boolean = false) : String;
|
||
var
|
||
ds: TDataSet;
|
||
begin
|
||
ds := GetResults( SQLQuery, HandleErrors, DisplayErrors );
|
||
if ds = nil then exit;
|
||
Result := ds.Fields.FieldByName(x).AsString;
|
||
ds.Close;
|
||
end;
|
||
|
||
{***
|
||
This returns the dataset object that is currently visible to the user,
|
||
depending on with tabsheet is active.
|
||
|
||
@return TDataset if data/query tab is active, nil otherwise.
|
||
}
|
||
function TMDIChild.GetVisualDataset: TDataSet;
|
||
begin
|
||
|
||
case PageControlMain.ActivePageIndex of
|
||
3: Result := gridData.DataSource.DataSet;
|
||
4: Result := gridQuery.DataSource.DataSet;
|
||
else
|
||
Result := nil;
|
||
end;
|
||
end;
|
||
|
||
|
||
{***
|
||
Execute a query and return column x as Stringlist
|
||
|
||
@param String SQL query String
|
||
@param Integer 0-based column index in the resultset to return
|
||
@return TStringList
|
||
}
|
||
function TMDIChild.GetCol( SQLQuery: String; x: Integer = 0; HandleErrors: Boolean = false; DisplayErrors: Boolean = false ) : TStringList;
|
||
var
|
||
i: Integer;
|
||
ds: TDataSet;
|
||
begin
|
||
ds := GetResults( SQLQuery, HandleErrors, DisplayErrors);
|
||
Result := TStringList.create();
|
||
if ds = nil then exit;
|
||
for i := 0 to ds.RecordCount - 1 do
|
||
begin
|
||
Result.Add( ds.Fields[x].AsString );
|
||
ds.Next;
|
||
end;
|
||
ds.Close;
|
||
end;
|
||
|
||
|
||
{***
|
||
Event procedure handler for the ZSQLMonitor1 object
|
||
}
|
||
procedure TMDIChild.ZSQLMonitor1LogTrace(Sender: TObject;
|
||
Event: TZLoggingEvent);
|
||
begin
|
||
LogSQL( Trim( Event.Message ), (Event.Category <> lcExecute) );
|
||
end;
|
||
|
||
|
||
// Resize image to fit
|
||
procedure TMDIChild.ResizeImageToFit;
|
||
var
|
||
height_needed : Cardinal;
|
||
begin
|
||
// Abort zooming if it's not an image or image-tab is sized to minimum size
|
||
if (EDBImage1.Picture.Width < 1) or (ScrollBox1.Height < 1) then
|
||
exit;
|
||
|
||
// Use all height we can get, but only as much we need to display without zooming
|
||
if EDBImage1.Picture.Height > Scrollbox1.Height then
|
||
height_needed := Scrollbox1.Height
|
||
else
|
||
height_needed := EDBImage1.Picture.Height;
|
||
|
||
// Set size
|
||
EDBImage1.Height := height_needed;
|
||
EDBImage1.Width := MulDiv(height_needed, EDBImage1.Picture.Width, EDBImage1.Picture.Height);
|
||
|
||
MainForm.showstatus('Image: ' + IntToStr( EDBImage1.Picture.width)
|
||
+ ' x ' + IntToStr( EDBImage1.Picture.Height ) + ' pixel, '
|
||
+ 'zoomed to ' + IntToStr(Round( 100 / EDBImage1.Picture.Height * EDBImage1.Height )) + '%'
|
||
);
|
||
end;
|
||
|
||
procedure TMDIChild.RunAsyncPost(ds: TDeferDataSet);
|
||
begin
|
||
FQueryRunning := true;
|
||
try
|
||
try
|
||
CheckConnection;
|
||
except
|
||
on E: Exception do begin
|
||
raise Exception.Create('Failed to reconnect, giving up. (' + E.Message + ')');
|
||
end;
|
||
end;
|
||
FProgressForm := TFrmQueryProgress.Create(Self);
|
||
debug('RunThreadedQuery(): Launching asynchronous query.');
|
||
ExecPostAsync(FConn,nil,FProgressForm.Handle,ds);
|
||
WaitForQueryCompletion(FProgressForm);
|
||
finally
|
||
FQueryRunning := false;
|
||
end;
|
||
end;
|
||
|
||
{***
|
||
Run a query in a separate thread of execution on the current connection.
|
||
}
|
||
function TMDIChild.RunThreadedQuery(AQuery: String): TMysqlQuery;
|
||
begin
|
||
Result := nil;
|
||
if (Copy(AQuery, 1, 3) <> 'USE') then EnsureActiveDatabase;
|
||
// Indicate a querythread is active (only one thread allow at this moment)
|
||
FQueryRunning := true;
|
||
try
|
||
// Check if the connection of the current window is still alive
|
||
// Otherwise reconnect
|
||
try
|
||
CheckConnection;
|
||
except
|
||
on E: Exception do begin
|
||
raise Exception.Create('Failed to reconnect, giving up. (' + E.Message + ')');
|
||
end;
|
||
end;
|
||
|
||
// Create instance of the progress form (but don't show it yet)
|
||
FProgressForm := TFrmQueryProgress.Create(Self);
|
||
|
||
{ Launch a thread of execution that passes the query to the server
|
||
|
||
The progressform serves as receiver of the status
|
||
messages (WM_MYSQL_THREAD_NOTIFY) of the thread:
|
||
|
||
* After the thread starts it notifies the progressform (MQE_INITED)
|
||
(which calls ShowModal on itself)
|
||
* Waits for a completion message from the thread (MQE_FINISHED) to remove itself
|
||
* Set FQueryRunning to false
|
||
}
|
||
debug('RunThreadedQuery(): Launching asynchronous query.');
|
||
Result := ExecMysqlStatementAsync (AQuery,FConn,nil,FProgressForm.Handle,RunAsyncPost);
|
||
|
||
{ Repeatedly check if the query has finished by inspecting FQueryRunning
|
||
Allow repainting of user interface
|
||
}
|
||
WaitForQueryCompletion(FProgressForm);
|
||
finally
|
||
FQueryRunning := false;
|
||
end;
|
||
end;
|
||
|
||
procedure TMDIChild.Splitter2Moved(Sender: TObject);
|
||
begin
|
||
ResizeImageToFit;
|
||
end;
|
||
|
||
{***
|
||
Used to catch when the highlighted row in a grid changes.
|
||
Possibly the AfterScroll event would be better suited for the purpose?
|
||
*}
|
||
procedure TMDIChild.DataSourceDataChange(Sender: TObject; Field: TField);
|
||
begin
|
||
//debug('DataSourceDataChange()');
|
||
// Avoid calling when switching data source to another TDataset.
|
||
if not viewingdata then GridHighlightChanged(Sender);
|
||
end;
|
||
|
||
procedure TMDIChild.DBGridColEnter(Sender: TObject);
|
||
begin
|
||
//debug('DBGridColEnter()');
|
||
GridHighlightChanged(Sender);
|
||
end;
|
||
|
||
procedure TMDIChild.GridHighlightChanged(Sender: TObject);
|
||
var
|
||
grid: TSMDBGrid;
|
||
ds: TDataSource;
|
||
hasNull: Boolean;
|
||
hasOddNewlines: Boolean;
|
||
s: String;
|
||
begin
|
||
// Current highlighted row or column in a grid has changed.
|
||
// Happens twice (row, then column) if neither row nor column match previously selected field.
|
||
grid := ActiveGrid;
|
||
ds := grid.DataSource;
|
||
if grid.SelectedField = nil then exit;
|
||
debug('GridHighlightChanged');
|
||
|
||
if DBMemo1.DataSource <> ds then
|
||
begin
|
||
DBMemo1.DataField := '';
|
||
DBMemo1.DataSource := ds;
|
||
EDBImage1.DataField := '';
|
||
EDBImage1.DataSource := ds;
|
||
end;
|
||
|
||
if grid.SelectedField.IsBlob then
|
||
begin
|
||
// Connect the BLOB-components to this field
|
||
DBMemo1.DataField := grid.SelectedField.FieldName;
|
||
// Hack: If 8 bytes or less, don't try to interpret as an image.
|
||
// Fixes BIT fields, which EDBImage otherwise think contains icons.
|
||
if Length(grid.SelectedField.AsString) > 8 then begin
|
||
EDBImage1.DataField := grid.SelectedField.FieldName;
|
||
end;
|
||
|
||
// If user is already editing then we're here by accident.
|
||
if not DBMemo1.Focused then begin
|
||
// Disable text editor if there's binary data or odd newlines in the field,
|
||
// since the text editor may/will silently corrupt it if used.
|
||
hasNull := hasNullChar(DBMemo1.Field.AsString);
|
||
hasOddNewlines := hasIrregularNewlines(DBMemo1.Field.AsString);
|
||
DBMemo1.ReadOnly := hasNull or hasOddNewlines;
|
||
btnUnsafeEdit.Enabled := hasNull or hasOddNewlines;
|
||
if btnUnsafeEdit.Enabled then
|
||
begin
|
||
s := 'Cannot edit text because BLOB contains ';
|
||
if hasNull then s := s + 'NUL characters';
|
||
if hasOddNewlines then begin
|
||
if hasNull then s := s + ' and ';
|
||
s := s + 'non-CRLF newlines';
|
||
end;
|
||
s := s + '. Click here to remove NUL plus following text and convert all newlines to CRLF format.';
|
||
end
|
||
else
|
||
begin
|
||
s := 'No irregular characters found in BLOB. It is safe to edit this text.';
|
||
end;
|
||
btnUnsafeEdit.Hint := s;
|
||
end;
|
||
|
||
// Ensure visibility of the Blob-Editor
|
||
PageControlBottom.ActivePage := tabBlobEditor;
|
||
MenuViewBlob.Enabled := true;
|
||
|
||
// Detect if we have picture-data in this BLOB and
|
||
// if yes, bring the viewer in the BLOB-editor to the front
|
||
if grid.Focused then begin
|
||
if EDBImage1.Picture.Height > 0 then
|
||
begin
|
||
PageControlBlobEditors.ActivePage := tabBlobEditorImage;
|
||
end
|
||
else
|
||
begin
|
||
PageControlBlobEditors.ActivePage := tabBlobEditorText;
|
||
end;
|
||
end;
|
||
ResizeImageToFit;
|
||
|
||
end
|
||
else
|
||
begin
|
||
// No BLOB selected, so disconnect the Blob-components
|
||
// from any field
|
||
DBMemo1.ReadOnly := true;
|
||
DBMemo1.DataField := '';
|
||
EDBImage1.DataField := '';
|
||
MenuViewBlob.Enabled := false;
|
||
end;
|
||
|
||
// Indicate the ReadOnly-state of the BLOB to the user
|
||
if DBMemo1.ReadOnly then
|
||
begin
|
||
DBMemo1.Color := clInactiveCaptionText;
|
||
end
|
||
else
|
||
begin
|
||
DBMemo1.Color := clWindow;
|
||
end;
|
||
|
||
PageControlBlobEditorsChange(self);
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.ZQuery1EditError(DataSet: TDataSet; E: EDatabaseError;
|
||
var Action: TDataAction);
|
||
begin
|
||
LogSQL( E.Message, true );
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.FormResize(Sender: TObject);
|
||
begin
|
||
ListTables.Width := tabDatabase.Width - tlbDataLeft1.Width - tlbDataLeft1.Left;
|
||
ListTables.Height := tabDatabase.Height - pnlDatabaseTop.Height;
|
||
pnlTableList.Width := tabTable.Width - tlbTableLeft1.Width - tlbTableLeft1.Left;
|
||
pnlTableList.Height := tabTable.Height - pnlTableTop.Height;
|
||
end;
|
||
|
||
|
||
// Search with searchbox
|
||
procedure TMDIChild.ButtonDataSearchClick(Sender: TObject);
|
||
var
|
||
i : Integer;
|
||
where : String;
|
||
begin
|
||
if not gridData.DataSource.DataSet.Active then
|
||
exit;
|
||
where := '';
|
||
if EditDataSearch.text <> '' then
|
||
begin
|
||
for i:=0 to gridData.FieldCount-1 do
|
||
begin
|
||
if where <> '' then
|
||
where := where + CRLF + ' OR ';
|
||
where := where + mask(gridData.Fields[i].FieldName) + ' LIKE ''%' + esc( EditDataSearch.text, true ) + '%''';
|
||
end;
|
||
if CheckBoxDataSearch.Checked then
|
||
where := 'NOT (' + where + ')';
|
||
end;
|
||
|
||
SynMemoFilter.Text := where;
|
||
|
||
SetFilter(self);
|
||
end;
|
||
|
||
// Searchbox focused
|
||
procedure TMDIChild.EditDataSearchEnter(Sender: TObject);
|
||
begin
|
||
ButtonDataSearch.Default := true;
|
||
EditDataSearch.SelectAll;
|
||
end;
|
||
|
||
// Searchbox unfocused
|
||
procedure TMDIChild.EditDataSearchExit(Sender: TObject);
|
||
begin
|
||
ButtonDataSearch.Default := false;
|
||
end;
|
||
|
||
|
||
|
||
// Click on first menu-item in context-menu of tablelist-columns
|
||
procedure TMDIChild.MenuTablelistColumnsClick(Sender: TObject);
|
||
var
|
||
menuitem : TMenuItem;
|
||
TablelistColumnsList : TStringList;
|
||
begin
|
||
menuitem := (Sender as TMenuItem);
|
||
with TRegistry.Create do
|
||
try
|
||
openkey( REGPATH + '\Servers\' + FConn.Description, true );
|
||
case menuitem.Tag of
|
||
1 : // Toggle default-columns
|
||
WriteBool( 'TablelistDefaultColumns', not menuitem.Checked );
|
||
2 :
|
||
begin
|
||
if ValueExists( 'TablelistColumns' ) then
|
||
TablelistColumnsList := Explode( ',', ReadString( 'TablelistColumns' ) )
|
||
else
|
||
TablelistColumnsList := TStringList.Create;
|
||
if TablelistColumnsList.IndexOf( menuitem.Caption ) > -1 then
|
||
TablelistColumnsList.Delete( TablelistColumnsList.IndexOf( menuitem.Caption ) )
|
||
else
|
||
TablelistColumnsList.Add( menuitem.Caption );
|
||
WriteString( 'TablelistColumns', implodestr( ',', TablelistColumnsList ) );
|
||
end;
|
||
end;
|
||
free;
|
||
except
|
||
free;
|
||
MessageDlg( 'Error while writing to registry.', mtError, [mbOK], 0 );
|
||
exit;
|
||
end;
|
||
menuitem.Checked := not menuitem.Checked;
|
||
ShowDBProperties( self );
|
||
end;
|
||
|
||
|
||
// Rightclick on header of table grid
|
||
procedure TMDIChild.ListTablesColumnRightClick(Sender: TObject;
|
||
Column: TListColumn; Point: TPoint);
|
||
begin
|
||
popupDbGridHeader.Popup( Mouse.CursorPos.X, Mouse.CursorPos.Y );
|
||
end;
|
||
|
||
|
||
// Rightclick on database grid
|
||
procedure TMDIChild.ListTablesMouseDown(Sender: TObject;
|
||
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
|
||
begin
|
||
if button = mbright then
|
||
popupDbGrid.Popup( Mouse.CursorPos.X, Mouse.CursorPos.Y );
|
||
end;
|
||
|
||
|
||
|
||
function TMDIChild.mask(str: String) : String;
|
||
begin
|
||
result := maskSql(mysql_version, str);
|
||
end;
|
||
|
||
|
||
procedure TMDIChild.CheckConnection;
|
||
begin
|
||
if not FMysqlConn.IsAlive then begin
|
||
LogSQL('Connection failure detected. Trying to reconnect.', true);
|
||
TimerConnected.Enabled := false;
|
||
TimerConnectedTimer(self);
|
||
TimerHostUptime.Enabled := false;
|
||
TimerHostUptimeTimer(self);
|
||
// 1) CheckConnection is always called from
|
||
// within an FQueryRunning-enabled block.
|
||
// 2) PerformConnect (see below) will make calls
|
||
// that open an FQueryRunning block, causing an
|
||
// error message.
|
||
//
|
||
// Therefore, flick the state of the running
|
||
// flag before running PerformConnect().
|
||
FQueryRunning := false;
|
||
try
|
||
FMysqlConn.Connection.Reconnect;
|
||
PerformConnect;
|
||
finally
|
||
FQueryRunning := true;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
|
||
|
||
function TMDIChild.GetActiveGrid: TSMDBGrid;
|
||
begin
|
||
Result := nil;
|
||
if PageControlMain.ActivePage = tabData then Result := gridData;
|
||
if PageControlMain.ActivePage = tabQuery then Result := gridQuery;
|
||
end;
|
||
|
||
|
||
{***
|
||
Detect average row size and limit the number of rows fetched at
|
||
once if more than ~ 5 MB of data
|
||
}
|
||
function TMDIChild.GetCalculatedLimit( Table: String ): Int64;
|
||
var
|
||
AvgRowSize, RecordCount : Int64;
|
||
ds: TDataSet;
|
||
const
|
||
// how much overhead this application has per row
|
||
ROW_SIZE_OVERHEAD : Integer = 1150;
|
||
// average row size guess for mysql server < 5.0
|
||
ROW_SIZE_GUESS: Integer = 2048;
|
||
// round to nearest value when deciding limit
|
||
ROUNDING: Integer = 1000;
|
||
begin
|
||
result := -1;
|
||
try
|
||
ds := GetResults('SHOW TABLE STATUS LIKE ' + esc(Table));
|
||
if ds = nil then exit;
|
||
AvgRowSize := MakeInt( ds.FieldByName( 'Avg_row_length' ).AsString ) + ROW_SIZE_OVERHEAD;
|
||
RecordCount := MakeInt( ds.FieldByName( 'Rows' ).AsString );
|
||
if AvgRowSize * RecordCount > LOAD_SIZE then
|
||
begin
|
||
result := Trunc( LOAD_SIZE / AvgRowSize );
|
||
result := (Trunc(result / ROUNDING) + 1) * ROUNDING;
|
||
if result >= RecordCount then result := -1;
|
||
end;
|
||
finally
|
||
debug( 'GetCalculatedLimit: ' + formatnumber(result) );
|
||
end;
|
||
end;
|
||
|
||
|
||
{**
|
||
Column selection for datagrid
|
||
}
|
||
procedure TMDIChild.btnDataClick(Sender: TObject);
|
||
var
|
||
btn : TSpeedButton;
|
||
frm : TForm;
|
||
begin
|
||
btn := (Sender as TSpeedButton);
|
||
|
||
if btn.Down then
|
||
begin
|
||
// Create the desired form
|
||
if btn = btnColumnSelection then
|
||
frm := TColumnSelectionForm.Create(self)
|
||
else if btn = btnDataSorting then
|
||
frm := TDataSortingForm.Create(self)
|
||
else
|
||
frm := TForm.Create(self); // Dummy fallback, should never get created
|
||
|
||
// Position new form relative to btn's position
|
||
frm.Top := btn.ClientOrigin.Y + btn.Height;
|
||
frm.Left := btn.ClientOrigin.X;
|
||
|
||
// Display form and refresh data if needed
|
||
if frm.ShowModal = mrOK then
|
||
ViewData(self);
|
||
|
||
btn.Down := False;
|
||
end;
|
||
end;
|
||
|
||
|
||
{**
|
||
Tabset right to query-memo was clicked
|
||
}
|
||
procedure TMDIChild.tabsetQueryHelpersChange(Sender: TObject; NewTab: Integer;
|
||
var AllowChange: Boolean);
|
||
var
|
||
i : Integer;
|
||
begin
|
||
lboxQueryHelpers.Items.BeginUpdate;
|
||
lboxQueryHelpers.Items.Clear;
|
||
// By default sorted alpabetically
|
||
lboxQueryHelpers.Sorted := True;
|
||
// By default disable all items in popupmenu, enable them when needed
|
||
menuDeleteSnippet.Enabled := False;
|
||
menuHelp.Enabled := False;
|
||
|
||
case NewTab of
|
||
0: // Cols
|
||
begin
|
||
// Keep native order of columns
|
||
lboxQueryHelpers.Sorted := False;
|
||
for i := 0 to ListColumns.Items.Count - 1 do
|
||
begin
|
||
lboxQueryHelpers.Items.Add(ListColumns.Items[i].Caption);
|
||
end;
|
||
end;
|
||
|
||
1: // SQL functions
|
||
begin
|
||
// State of items in popupmenu
|
||
menuHelp.Enabled := True;
|
||
for i := 0 to Length(MySQLFunctions) - 1 do
|
||
begin
|
||
// Don't display unsupported functions here
|
||
if MySqlFunctions[i].Version > mysql_version then
|
||
continue;
|
||
lboxQueryHelpers.Items.Add( MySQLFunctions[i].Name + MySQLFunctions[i].Declaration );
|
||
end;
|
||
end;
|
||
|
||
2: // SQL keywords
|
||
begin
|
||
// State of items in popupmenu
|
||
menuHelp.Enabled := True;
|
||
lboxQueryHelpers.Items := MYSQL_KEYWORDS;
|
||
end;
|
||
|
||
3: // SQL Snippets
|
||
begin
|
||
// State of items in popupmenu
|
||
menuDeleteSnippet.Enabled := True;
|
||
lboxQueryHelpers.Items := getFilesFromDir( DIRNAME_SNIPPETS, '*.sql', true );
|
||
end;
|
||
|
||
end;
|
||
|
||
// Restore last selected item in tab
|
||
if (QueryHelpersSelectedItems[NewTab] > -1)
|
||
and (QueryHelpersSelectedItems[NewTab] < lboxQueryHelpers.Count) then
|
||
begin
|
||
lboxQueryHelpers.ItemIndex := QueryHelpersSelectedItems[NewTab];
|
||
end;
|
||
|
||
lboxQueryHelpers.Items.EndUpdate;
|
||
|
||
end;
|
||
|
||
|
||
|
||
{**
|
||
Insert string from listbox with query helpers into SQL
|
||
memo at doubleclick
|
||
}
|
||
procedure TMDIChild.lboxQueryHelpersDblClick(Sender: TObject);
|
||
var
|
||
itemtext : String;
|
||
begin
|
||
itemtext := lboxQueryHelpers.Items[lboxQueryHelpers.ItemIndex];
|
||
|
||
case tabsetQueryHelpers.TabIndex of
|
||
3: // Load snippet file <20>nto query-memo
|
||
QueryLoad( DIRNAME_SNIPPETS + itemtext + '.sql', False );
|
||
else // For all other tabs just insert the item from the list
|
||
SynMemoQuery.SelText := itemtext;
|
||
end;
|
||
|
||
SynMemoQuery.SetFocus;
|
||
end;
|
||
|
||
|
||
{**
|
||
Remember last used items in query helper tabs
|
||
}
|
||
procedure TMDIChild.lboxQueryHelpersClick(Sender: TObject);
|
||
begin
|
||
QueryHelpersSelectedItems[tabsetQueryHelpers.TabIndex] := lboxQueryHelpers.ItemIndex;
|
||
end;
|
||
|
||
|
||
{**
|
||
Insert function name from popupmenu to query memo
|
||
}
|
||
procedure TMDIChild.insertFunction(Sender: TObject);
|
||
var
|
||
f : String;
|
||
sm : TSynMemo;
|
||
begin
|
||
// Detect which memo is focused
|
||
if SynMemoFilter.Focused then
|
||
sm := SynMemoFilter
|
||
else
|
||
sm := SynMemoQuery;
|
||
// Restore function name from array
|
||
f := MySQLFunctions[TControl(Sender).tag].Name
|
||
+ MySQLFunctions[TControl(Sender).tag].Declaration;
|
||
sm.UndoList.AddGroupBreak;
|
||
sm.SelText := f;
|
||
sm.UndoList.AddGroupBreak;
|
||
if not SynMemoFilter.Focused then
|
||
SynMemoQueryChange(self);
|
||
end;
|
||
|
||
|
||
{**
|
||
Activate inline-item-editor of listColumns
|
||
}
|
||
procedure TMDIChild.menuRenameColumnClick(Sender: TObject);
|
||
begin
|
||
listColumns.Selected.EditCaption;
|
||
end;
|
||
|
||
|
||
{**
|
||
Rename a column name from within listColumns
|
||
}
|
||
procedure TMDIChild.ListColumnsEdited(Sender: TObject; Item: TListItem;
|
||
var S: string);
|
||
var
|
||
def : TDataSet;
|
||
sql_update, sql_null, sql_default, sql_extra, DefaultValue : String;
|
||
begin
|
||
// Try to rename, on any error abort and don't rename ListItem
|
||
try
|
||
ensureValidIdentifier( S );
|
||
|
||
// Fetch column definition
|
||
def := GetResults( 'SHOW COLUMNS FROM ' + mask(ActualTable) + ' LIKE ' + esc(Item.Caption), False, False );
|
||
|
||
// Check NOT NULL
|
||
sql_null := 'NULL ';
|
||
if UpperCase(def.FieldByName('Null').AsString) = 'NO' then
|
||
sql_null := 'NOT NULL ';
|
||
|
||
// Check default value, take care of non-literals / functions
|
||
sql_default := '';
|
||
DefaultValue := def.FieldByName('Default').AsString;
|
||
if DefaultValue <> '' then
|
||
begin
|
||
if (UpperCase(def.FieldByName('Type').AsString) <> 'TIMESTAMP') and (DefaultValue <> 'CURRENT_TIMESTAMP') then
|
||
DefaultValue := esc(DefaultValue);
|
||
sql_default := 'DEFAULT ' + DefaultValue + ' ';
|
||
end;
|
||
|
||
// Check extra options (auto_increment)
|
||
sql_extra := '';
|
||
if def.FieldByName('Extra').AsString <> '' then
|
||
sql_default := ' '+UpperCase(def.FieldByName('Extra').AsString) + ' ';
|
||
|
||
// Concat column definition
|
||
sql_update := 'ALTER TABLE ' + mask(ActualTable) +
|
||
' CHANGE ' + mask(Item.Caption) +
|
||
' ' + mask(S) + ' ' +
|
||
def.FieldByName('Type').AsString + ' ' +
|
||
sql_null +
|
||
sql_default +
|
||
sql_extra;
|
||
|
||
// Fire ALTER query
|
||
ExecUpdateQuery( sql_update, False, False );
|
||
except
|
||
On E : Exception do
|
||
begin
|
||
MessageDlg( E.Message, mtError, [mbOK], 0 );
|
||
abort;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
|
||
{**
|
||
Delete a snippet file
|
||
}
|
||
procedure TMDIChild.menuDeleteSnippetClick(Sender: TObject);
|
||
var
|
||
snippetfile : String;
|
||
mayChange : Boolean;
|
||
begin
|
||
// Don't do anything if no item was selected
|
||
if lboxQueryHelpers.ItemIndex = -1 then
|
||
abort;
|
||
|
||
snippetfile := DIRNAME_SNIPPETS + lboxQueryHelpers.Items[ lboxQueryHelpers.ItemIndex ] + '.sql';
|
||
if MessageDlg( 'Delete snippet file? ' + CRLF + snippetfile, mtConfirmation, [mbOk, mbCancel], 0) = mrOk then
|
||
begin
|
||
Screen.Cursor := crHourGlass;
|
||
if DeleteFile( snippetfile ) then
|
||
begin
|
||
// Refresh list with snippets
|
||
mayChange := True; // Unused; satisfies callee parameter collection which is probably dictated by tabset.
|
||
tabsetQueryHelpersChange( Sender, tabsetQueryHelpers.TabIndex, mayChange );
|
||
end
|
||
else
|
||
begin
|
||
Screen.Cursor := crDefault;
|
||
MessageDlg( 'Failed deleting ' + snippetfile, mtError, [mbOK], 0 );
|
||
end;
|
||
Screen.Cursor := crDefault;
|
||
end;
|
||
end;
|
||
|
||
|
||
end.
|
||
|
||
|
||
|