1 #include "SqlCtrl.h"
2 #include "SqlDlg.h"
3 #include <Report/Report.h>
4 #include <CodeEditor/CodeEditor.h>
5 
6 namespace Upp {
7 
8 #define IMAGEFILE <SqlCtrl/SqlCtrl.iml>
9 #define IMAGECLASS SqlConsoleImg
10 #include <Draw/iml.h>
11 
scmpw(const byte * s,const char * w)12 static bool scmpw(const byte *s, const char *w) {
13 	for(;;) {
14 		if(*w == '\0')
15 			return *s == ' ' || *s == '\t';
16 		if(ToUpper(*s++) != ToUpper(*w++))
17 			return false;
18 	}
19 }
20 
21 static const int MAX_NESTING_LEVEL = 10;
22 static const int PROGRESS_RANGE = 1000000000;
23 
SqlRunScript(int dialect,Stream & script_stream,Gate<String> executor,Gate<int,int> progress,Index<String> & script_file_names,int progress_min,int progress_max)24 bool SqlRunScript(int dialect, Stream& script_stream,
25 	Gate<String> executor, Gate<int, int> progress,
26 	Index<String>& script_file_names, int progress_min, int progress_max)
27 {
28 	int line_number = 1;
29 	for(int c; (c = script_stream.Term()) >= 0;) {
30 		if(c <= ' ') {
31 			if(c == '\n')
32 				line_number++;
33 			script_stream.Get();
34 		}
35 		else if(c == '@') {
36 			script_stream.Get();
37 			int64 start = script_stream.GetPos() - 1;
38 			bool rel = false;
39 			if(script_stream.Term() == '@') {
40 				script_stream.Get();
41 				rel = true;
42 			}
43 			StringBuffer subscript_fn;
44 			while((c = script_stream.Get()) >= 0 && c != '\n')
45 				subscript_fn.Cat((char)c);
46 			int e = subscript_fn.GetLength();
47 			while(e > 0 && subscript_fn[e - 1] <= ' ')
48 				e--;
49 			String fn(~subscript_fn, e);
50 			fn = NormalizePath(fn, GetFileDirectory(script_file_names[rel ? script_file_names.GetCount() - 1 : 0]));
51 			String norm = fn;
52 #if !PLATFORM_PATH_HAS_CASE
53 			norm = ToLower(norm);
54 #endif
55 			if(script_file_names.Find(norm) >= 0)
56 				throw Exc(NFormat(t_("circular script inclusion of %s at %s:%d"),
57 					fn, script_file_names.Top(), line_number));
58 			if(script_file_names.GetCount() >= MAX_NESTING_LEVEL)
59 				throw Exc(NFormat(t_("script nesting too deep (%d levels)"), script_file_names.GetCount()));
60 			int64 end = script_stream.GetPos();
61 
62 			int new_min = progress_min + (int)((progress_max - progress_min) * start / script_stream.GetSize());
63 			int new_max = progress_min + (int)((progress_max - progress_min) * end / script_stream.GetSize());
64 
65 			FileIn fi;
66 			if(!fi.Open(fn))
67 				throw Exc(NFormat(t_("error opening script file '%s'"), fn));
68 
69 			script_file_names.Add(norm);
70 			if(!SqlRunScript(dialect, fi, executor, progress, script_file_names, new_min, new_max))
71 				return false;
72 			script_file_names.Drop();
73 		}
74 		else {
75 			StringBuffer statement;
76 			bool exec = false;
77 			bool body = false;
78 			bool chr = false;
79 
80 			while(!script_stream.IsEof() && !exec) {
81 				String line = script_stream.GetLine();
82 				line_number++;
83 
84 				int l = line.GetLength();
85 				if(l && line[0] == '/' && !body && !chr)
86 					exec = true;
87 				else
88 				if(l && line[0] == '.' && body && !chr)
89 					body = false;
90 				else {
91 					if(statement.GetLength() && !chr)
92 						statement.Cat(' ');
93 					bool spc = true;
94 					bool create = false;
95 					for(const byte *s = (const byte *)(const char *)line; *s; s++) {
96 						if(s[0] == '-' && s[1] == '-' && !chr) break;
97 						if(*s == '\'') chr = !chr;
98 						if(!chr && spc && scmpw(s, "create"))
99 							create = true;
100 						if(!chr && spc && (scmpw(s, "begin") || scmpw(s, "declare") ||
101 						   create && (scmpw(s, "procedure") || scmpw(s, "function") || scmpw(s, "trigger"))
102 						))
103 							body = true;
104 						if(*s == ';' && !chr && !body) {
105 							exec = true;
106 							break;
107 						}
108 						if(*s > ' ' || chr) {
109 							statement.Cat(*s);
110 							spc = false;
111 						}
112 						else
113 						if(*s == '\t' || *s == ' ') {
114 							if(!spc) statement.Cat(' ');
115 							spc = true;
116 						}
117 						else
118 							spc = false;
119 					}
120 					if(chr) statement.Cat("\r\n");
121 				}
122 			}
123 			if(progress((int)(progress_min + script_stream.GetPos()
124 				* (progress_max - progress_min) / script_stream.GetSize()), PROGRESS_RANGE))
125 				throw AbortExc();
126 			if(!executor(statement))
127 				return false;
128 		}
129 	}
130 	return true;
131 }
132 
SqlRunScript(int dialect,Stream & script_stream,const String & file_name,Gate<String> executor,Gate<int,int> progress)133 bool SqlRunScript(int dialect, Stream& script_stream, const String& file_name,
134                   Gate<String> executor, Gate<int, int> progress)
135 {
136 	Index<String> script_file_names;
137 	String fn = NormalizePath(file_name);
138 #if !PLATFORM_PASH_HAS_CASE
139 	fn = ToLower(fn);
140 #endif
141 	script_file_names.Add(fn);
142 	return SqlRunScript(dialect, script_stream, executor, progress, script_file_names, 0, PROGRESS_RANGE);
143 }
144 
145 void RunDlgSqlExport(Sql& cursor, String command, String tablename);
146 
147 class SqlConsole : public TopWindow {
148 public:
149 	virtual bool Key(dword key, int count);
150 	virtual void Serialize(Stream& s);
151 
152 	void ListMenu(Bar& bar);
153 	void ListPrintRow();
154 	void ListPrintList();
155 	void ListExport();
156 
157 protected:
158 	Sql                        cursor;
159 	String                     lastquery;
160 	Splitter                   lires, vsplit;
161 	ArrayCtrl                  list;
162 	StaticRect                 rec_err;
163 	DocEdit                    errortext;
164 	ArrayCtrl                  record;
165 	ArrayCtrl                  trace;
166 	CodeEditor                 command;
167 	Button                     execute;
168 	Button                     schema;
169 	Button                     csv;
170 	ParentCtrl                 command_pane;
171 //	CallbackSet                hide;
172 	Vector<int>                cw;
173 	Vector<bool>               visible;
174 	Vector<bool>               lob;
175 	String                     LastDir;
176 	Label                      info1, info2;
177 
178 	enum {
179 		NORMAL,
180 		RERUN,
181 		SCRIPT,
182 		QUIET,
183 	};
184 
185 	void    ColSize();
186 	void    Hide(int i);
187 	void    Record();
188 	void    Execute(int type = NORMAL);
189 	void    TraceToCommand();
190 	void    TraceToExecute();
191 	void    ListToCommand(ArrayCtrl *list);
192 	void    SaveTrace();
193 	void    RunScript(bool quiet);
194 	void    TraceMenu(Bar& menu);
ObjectTree()195 	void    ObjectTree() { SQLObjectTree(cursor.GetSession()); }
196 	void    Csv();
197 
198 	class Exec;
199 	friend class Exec;
200 	class Exec : public StatementExecutor {
201 	public:
202 		typedef Exec CLASSNAME;
Exec(bool quiet)203 		Exec(bool quiet) : quiet(quiet) {}
204 		SqlConsole *me;
205 		bool quiet;
Execute(const String & stmt)206 		virtual bool Execute(const String& stmt) {
207 			me->command <<= stmt; me->Sync(); me->Execute(quiet ? QUIET : SCRIPT); return true;
208 		}
GateExec(String stmt)209 		bool GateExec(String stmt) { return Execute(stmt); }
operator Gate<String>()210 		operator Gate<String> ()   { return THISBACK(GateExec); }
211 
212 	};
213 
214 	void ViewRecord();
215 
216 public:
217 	typedef SqlConsole CLASSNAME;
218 
219 	void    Perform();
220 
221 	void    AddInfo(Ctrl& tgt, Label& info, const char *txt);
222 
223 	SqlConsole(SqlSession& session);
224 };
225 
Execute(int type)226 void SqlConsole::Execute(int type) {
227 	list.Reset();
228 	list.HeaderObject().Absolute().Moving();
229 	visible.Clear();
230 	lob.Clear();
231 	record.Clear();
232 	String s = ~command;
233 	while(*s.Last() == ';')
234 		s.Trim(s.GetLength() - 1);
235 	int ms0 = msecs();
236 	cursor.ClearError();
237 	lastquery = s;
238 	String ttl = s;
239 	ttl.Replace("\t", " ");
240 	ttl.Replace("\n", " ");
241 	ttl.Replace("\r", "");
242 	info1.Remove();
243 	info2.Remove();
244 	if(!cursor.Execute(s)) {
245 	error:
246 		record.Hide();
247 		errortext.Show();
248 		list.AddColumn(t_("Error"));
249 		String err = cursor.GetLastError();
250 		errortext <<= err;
251 		list.Add(err);
252 		trace.Add(s, err, "");
253 		trace.GoEnd();
254 		Title((ttl + " - " + err).ToWString());
255 		return;
256 	}
257 	if(type == QUIET)
258 		return;
259 	bool onecol = cursor.GetColumns() == 1;
260 	record.Show(!onecol);
261 	errortext.Show(onecol);
262 	int ms1 = msecs();
263 	cw.SetCount(cursor.GetColumns());
264 	visible.SetCount(cw.GetCount(), true);
265 	int margins;
266 	for(int i = 0; i < cursor.GetColumns(); i++) {
267 		const SqlColumnInfo& ci = cursor.GetColumnInfo(i);
268 		String n = ToLower(ci.name);
269 		list.AddColumn(n);
270 		list.HeaderTab(i).WhenAction = THISBACK1(Hide, i);
271 		margins = HorzLayoutZoom(2) + 2 * list.HeaderTab(i).GetMargin();
272 		cw[i] = GetTextSize(n, StdFont()).cx + margins;
273 		record.Add(n, Null);
274 		lob.Add(ci.type == -1 || ci.type == -2); // !! BLOB / CLOB hack
275 	}
276 	Progress pi;
277 	pi.SetText(t_("Fetched %d line(s)"));
278 	while(cursor.Fetch()) {
279 		Vector<Value> row = cursor.GetRow();
280 		for(int i = 0; i < cursor.GetColumns(); i++)
281 		{
282 			if(lob[i])
283 			{
284 				String temp;
285 				cursor.GetColumn(i, temp);
286 				row[i] = temp;
287 			}
288 			cw[i] = max(cw[i], GetTextSize(StdFormat(row[i]), StdFont()).cx + margins);
289 			cw[i] = min(cw[i], list.GetSize().cx / 3);
290 		}
291 		list.Add(row);
292 		if(pi.StepCanceled()) break;
293 	}
294 	if(cw.GetCount() && cursor.WasError()) {
295 		list.Reset();
296 		goto error;
297 	}
298 	visible.SetCount(list.GetColumnCount(), true);
299 	ColSize();
300 	if(list.GetCount() > 0)
301 		list.SetCursor(0);
302 	Title(NFormat(t_("%s (%d rows)"), ttl, list.GetCount()));
303 	String rrows = Format(t_("%d rows"), max(list.GetCount(), cursor.GetRowsProcessed()));
304 	String rms = Format(t_("%d ms"), ms1 - ms0);
305 	if(type == RERUN && trace.IsCursor()) {
306 		trace.Set(1, rrows);
307 		trace.Set(2, rms);
308 	}
309 	else {
310 		trace.Add(s, rrows, rms);
311 		trace.GoEnd();
312 	}
313 	command.Remove(0, command.GetLength());
314 	command.SetSelection(0, 0);
315 }
316 
ColSize()317 void SqlConsole::ColSize() {
318 	int maxw = list.GetSize().cx;
319 	int wx = 0;
320 	for(int i = 0; i < list.GetColumnCount(); i++)
321 		if(visible[i]) {
322 			int w = min(maxw, cw[i]);
323 			wx += w;
324 			list.HeaderObject().SetTabRatio(i, w);
325 			list.HeaderObject().ShowTab(i);
326 		}
327 		else
328 			list.HeaderObject().HideTab(i);
329 }
330 
Hide(int i)331 void SqlConsole::Hide(int i) {
332 	if(i < visible.GetCount())
333 		visible[i] = false;
334 	ColSize();
335 }
336 
Record()337 void SqlConsole::Record() {
338 	if(list.GetIndexCount() == 1)
339 		errortext <<= StdFormat(list.Get(0));
340 	for(int i = 0; i < list.GetIndexCount(); i++)
341 		record.Set(i, 1, list.IsCursor() ? list.Get(i) : Value());
342 }
343 
Key(dword key,int count)344 bool SqlConsole::Key(dword key, int count) {
345 	switch(key) {
346 	case K_F5:
347 		Execute();
348 		return true;
349 	case K_CTRL_R:
350 		RunScript(false);
351 		return true;
352 	case K_CTRL_Q:
353 		RunScript(true);
354 		return true;
355 	case K_CTRL_S:
356 		SaveTrace();
357 		return true;
358 	}
359 	return TopWindow::Key(key, count);
360 }
361 
Serialize(Stream & s)362 void SqlConsole::Serialize(Stream& s) {
363 	int version = 0;
364 	s / version;
365 	s.Magic();
366 	Rect r = GetRect();
367 	s % r;
368 	SetRect(r);
369 	vsplit.Serialize(s);
370 	record.SerializeHeader(s);
371 	lires.Serialize(s);
372 	trace.SerializeHeader(s);
373 	if(s.IsLoading())
374 		trace.Clear();
375 	Vector<ValueArray> ar;
376 	for(int i = 0; i < trace.GetCount(); i++)
377 		ar.Add(trace.GetArray(i));
378 	s % ar;
379 	if(s.IsLoading()) {
380 		for(int i = 0; i < ar.GetCount(); i++)
381 			trace.SetArray(i, ar[i]);
382 		trace.GoEnd();
383 	}
384 	if(version >= 1)
385 		s % LastDir;
386 	s.Magic();
387 }
388 
Perform()389 void SqlConsole::Perform() {
390 	const char cfg[] = "SqlConsole.cfg";
391 	LoadFromFile(*this, cfg);
392 	Title(t_("SQL Commander"));
393 	Icon(SqlConsoleImg::database_edit(), SqlConsoleImg::SqlConsoleIconLarge());
394 	Sizeable();
395 	Zoomable();
396 	ActiveFocus(command);
397 	Run();
398 	cursor.ClearError();
399 	StoreToFile(*this, cfg);
400 }
401 
TraceToCommand()402 void SqlConsole::TraceToCommand() {
403 	if(trace.IsCursor()) {
404 		command.SetData(trace.Get(0));
405 		command.SetCursor(command.GetLength());
406 	}
407 }
408 
ListToCommand(ArrayCtrl * l)409 void SqlConsole::ListToCommand(ArrayCtrl *l)
410 {
411 	int c = l->GetClickColumn();
412 	if(GetCtrl() && l->IsCursor() && c >= 0 && c < l->GetColumnCount()) {
413 		command.Paste(AsString(l->Get(c)).ToWString());
414 		command.SetFocus();
415 	}
416 }
417 
TraceToExecute()418 void SqlConsole::TraceToExecute() {
419 	Execute(RERUN);
420 }
421 
SaveTrace()422 void SqlConsole::SaveTrace() {
423 	FileSel fsel;
424 	fsel.ActiveDir(LastDir);
425 	fsel.DefaultExt("sql");
426 	fsel.Type(t_("SQL scripts (*.sql)"), "*.sql");
427 	fsel.AllFilesType();
428 	if(!fsel.ExecuteSaveAs(t_("Save trace as"))) return;
429 	FileOut out(~fsel);
430 	if(!out) return;
431 	LastDir = GetFileDirectory(~fsel);
432 	for(int i = 0; i < trace.GetCount(); i++) {
433 		out.Put((String)trace.Get(i, 0));
434 		out.Put(";\n");
435 	}
436 }
437 
RunScript(bool quiet)438 void SqlConsole::RunScript(bool quiet) {
439 //	UPP::RunScript runscript = cursor.GetSession().GetRunScript();
440 //	if(!runscript) {
441 //		Exclamation(t_("Database connection doesn't support running scripts."));
442 //		return;
443 //	}
444 	FileSel fsel;
445 	fsel.ActiveDir(LastDir);
446 	fsel.DefaultExt("sql");
447 	fsel.Type(t_("SQL scripts (*.sql)"), "*.sql");
448 	fsel.AllFilesType();
449 	if(!fsel.ExecuteOpen(t_("Run script"))) return;
450 	Exec exec(quiet);
451 	exec.me = this;
452 	LastDir = GetFileDirectory(~fsel);
453 	Progress progress(t_("Executing script"));
454 	FileIn fi;
455 	if(!fi.Open(~fsel)) {
456 		Exclamation(NFormat(t_("Cannot open file [* \1%s\1]."), ~fsel));
457 		return;
458 	}
459 	try {
460 		SqlRunScript(cursor.GetDialect(), fi, ~fsel, exec, progress);
461 	}
462 	catch(Exc e) {
463 		ShowExc(e);
464 	}
465 }
466 
TraceMenu(Bar & menu)467 void SqlConsole::TraceMenu(Bar& menu) {
468 	menu.Add(t_("Save as script.."), THISBACK(SaveTrace)).Key(K_CTRL_S);
469 	menu.Add(t_("Run script.."), THISBACK1(RunScript, false)).Key(K_CTRL_R);
470 	menu.Add(t_("Run script quietly.."), THISBACK1(RunScript, true)).Key(K_CTRL_Q);
471 }
472 
ListMenu(Bar & bar)473 void SqlConsole::ListMenu(Bar& bar)
474 {
475 	bar.Add(t_("Print record"), THISBACK(ListPrintRow));
476 	bar.Add(t_("Print list"), THISBACK(ListPrintList));
477 	bar.Add(t_("Export..."), THISBACK(ListExport));
478 }
479 
Csv()480 void SqlConsole::Csv()
481 {
482 	SelectSaveFile("Csv\t*.csv", list.AsCsv());
483 }
484 
ListPrintRow()485 void SqlConsole::ListPrintRow()
486 {
487 	String qtf;
488 	qtf << "[A1 ";
489 	if(!IsNull(lastquery))
490 		qtf << "[4* \1" << lastquery << "\1]&&";
491 	for(int i = 0; i < record.GetCount(); i++) {
492 		qtf << (i ? "--" : "++") << "::10@(240.240.240) \1" << StdFormat(record.Get(i, 0)) << "\1"
493 			"||::30@(255.255.255) \1" << StdFormat(record.Get(i, 1)) << "\1";
494 	}
495 	qtf << "++";
496 	Report report;
497 	report << qtf;
498 	UPP::Perform(report);
499 }
500 
ListPrintList()501 void SqlConsole::ListPrintList()
502 {
503 	String qtf;
504 	qtf << "[A1 ";
505 	if(!IsNull(lastquery))
506 		qtf << "[4* \1" << lastquery << "\1]&&";
507 	for(int i = 0; i < record.GetCount(); i++)
508 		qtf << (i ? "||" : "++") << "::@(240.240.240) [* \1"
509 		<< StdFormat(record.Get(i, 0)) << "\1]";
510 	for(int i = 0; i < list.GetCount(); i++)
511 		for(int j = 0; j < list.GetIndexCount(); j++)
512 			qtf << (j ? "||" : "--") << "::@(255.255.255) \1" << StdFormat(list.Get(i, j)) << '\1';
513 	qtf << "++";
514 	Report report;
515 	report << qtf;
516 	UPP::Perform(report);
517 }
518 
ListExport()519 void SqlConsole::ListExport()
520 {
521 	RunDlgSqlExport(cursor, lastquery, Null);
522 }
523 
524 struct SqlValueViewDlg : public WithSqlValueViewLayout<TopWindow> {
525 	typedef SqlValueViewDlg CLASSNAME;
526 
527 	String value;
528 
529 	void Sync();
530 	void Save();
531 
532 	SqlValueViewDlg();
533 };
534 
Sync()535 void SqlValueViewDlg::Sync()
536 {
537 	if(~format) {
538 		StringStream ss;
539 		HexDumpData(ss, ~value, value.GetLength(), false, 1000000);
540 		text <<= ss.GetResult();
541 	}
542 	else
543 		text <<= value;
544 }
545 
Save()546 void SqlValueViewDlg::Save()
547 {
548 	SelectSaveFile("File\t*.*", value);
549 }
550 
SqlValueViewDlg()551 SqlValueViewDlg::SqlValueViewDlg()
552 {
553 	CtrlLayout(*this, "");
554 	text.SetFont(Monospace(GetStdFont().GetHeight()));
555 	format <<= THISBACK(Sync);
556 	save <<= THISBACK(Save);
557 	format = 0;
558 }
559 
SqlViewValue(const String & title,const String & value)560 void SqlViewValue(const String& title, const String& value)
561 {
562 	SqlValueViewDlg dlg;
563 	dlg.Title(title);
564 	dlg.value = value;
565 	dlg.Sync();
566 	dlg.Execute();
567 }
568 
ViewRecord()569 void SqlConsole::ViewRecord()
570 {
571 	if(!record.IsCursor())
572 		return;
573 	SqlViewValue(record.Get(0), AsString(record.Get(1)));
574 }
575 
AddInfo(Ctrl & tgt,Label & info,const char * txt)576 void SqlConsole::AddInfo(Ctrl& tgt, Label& info, const char *txt)
577 {
578 	info = txt;
579 	tgt.Add(info.SizePos());
580 //	tgt.Add(info.BottomPos(0, GetStdFontCy()).RightPos(0, GetTextSize(txt, StdFont()).cx));
581 }
582 
SqlConsole(SqlSession & session)583 SqlConsole::SqlConsole(SqlSession& session)
584 : cursor(session)
585 {
586 	int ecy = EditField::GetStdHeight();
587 	lires.Horz(list, rec_err);
588 	rec_err << record.SizePos() << errortext.SizePos();
589 	errortext.SetReadOnly();
590 	errortext.SetFont(Courier(12));
591 	errortext.Hide();
592 	vsplit.Vert(lires, trace);
593 	vsplit.Add(command_pane);
594 	vsplit.SetPos(6500);
595 	vsplit.SetPos(8500, 1);
596 	lires.SetPos(7000);
597 	record.AddColumn(t_("Column"), 5);
598 	record.AddColumn(t_("Value"), 10);
599 	record.WhenLeftDouble = THISBACK(ViewRecord);
600 	record.WhenLeftClick = THISBACK1(ListToCommand, &record);
601 	AddInfo(record, info1, "\1[g= Use [@B Ctrl+Click] to copy data into SQL&[@B DoubleClick] for detailed view.");
602 	trace.AddColumn(t_("Command"), 8);
603 	trace.AddColumn(t_("Result"), 1);
604 	trace.AddColumn(t_("Duration"), 1);
605 	trace.WhenLeftClick = THISBACK(TraceToCommand);
606 	trace.WhenLeftDouble = THISBACK(TraceToExecute);
607 	trace.WhenBar = THISBACK(TraceMenu);
608 	trace.NoWantFocus();
609 	list.WhenSel = THISBACK(Record);
610 	list.WhenLeftClick = THISBACK1(ListToCommand, &list);
611 	list.WhenBar = THISBACK(ListMenu);
612 	list.HeaderObject().Absolute();
613 	AddInfo(list, info2, "\1[g= Use [@B Ctrl+Click] to copy data into SQL.");
614 	Add(vsplit.SizePos());
615 	command.SetFont(Courier(GetStdFontCy()));
616 	command_pane.Add(command.VSizePos().HSizePos(0, HorzLayoutZoom(100)));
617 	ecy = max(24, ecy);
618 	command_pane.Add(execute.TopPos(0, ecy).RightPos(4, HorzLayoutZoom(90)));
619 	command_pane.Add(schema.TopPos(ecy + 4, ecy).RightPos(4, HorzLayoutZoom(90)));
620 	command_pane.Add(csv.TopPos(2 * ecy + 4, ecy).RightPos(4, HorzLayoutZoom(90)));
621 	command.Highlight("sql");
622 	schema.SetLabel(t_("&Schema"));
623 	schema <<= THISBACK(ObjectTree);
624 	schema.SetImage(SqlConsoleImg::bricks());
625 	execute <<= THISBACK1(Execute, NORMAL);
626 	execute.SetImage(SqlConsoleImg::lightning());
627 	execute.SetLabel(t_("Execute (F5)"));
628 	csv <<= THISBACK(Csv);
629 	csv.SetLabel(t_("Export.."));
630 	csv.SetImage(SqlConsoleImg::database_save());
631 	ActiveFocus(command);
632 }
633 
SQLCommander(SqlSession & session)634 void SQLCommander(SqlSession& session) {
635 	SqlConsole con(session);
636 	con.Perform();
637 }
638 
IsSqlConsoleActive__()639 bool IsSqlConsoleActive__()
640 {
641 	return dynamic_cast<SqlConsole *>(Ctrl::GetActiveWindow());
642 }
643 
644 }
645