#include "SqlCtrl.h" #include NAMESPACE_UPP #define IMAGEFILE #define IMAGECLASS SqlConsoleImg #include void RunDlgSqlExport(Sql& cursor, String command, String tablename); class MacroSet { struct Macro { int n; String text; Vector param; void Serialize(Stream& s) { s / n % text % param; } }; ArrayMap macro; static bool Spaces(const char *&s); static String GetId(const char *&s); static bool IsId(int c) { return c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '_'; } String Expand(const char *s, Index& used) const; public: void Serialize(Stream& s); bool Define(const char *s); String Expand(const char *s) const; int GetCount() const { return macro.GetCount(); } String Get(int i) const; }; bool MacroSet::Spaces(const char *&s) { if(*s != ' ' && *s != '\t') return false; while(*s == ' ' || *s == '\t') s++; return true; } String MacroSet::GetId(const char *&s) { String result; while(*s >= 'A' && *s <= 'Z' || *s >= 'a' && *s <= 'z' || *s >= '0' && *s <= '9' || *s == '_') result.Cat(*s++); return result; } void MacroSet::Serialize(Stream& s) { int version = 0; s % version; int n = macro.GetCount(); s / n; if(s.IsLoading()) { macro.Clear(); for(int i = 0; i < n; i++) { String key; Macro value; s % key; s % macro.Add(key); } } else for(int i = 0; i < n; i++) { String key; Macro value; key = macro.GetKey(i); value = macro[i]; s % key % value; } } bool MacroSet::Define(const char *s) { if(!IsId(*s)) return false; String name = GetId(s); Index param; Spaces(s); if(*s == '(') { s++; Spaces(s); if(*s != ')') for(;;) { Spaces(s); if(!IsId(*s)) return false; if(param.GetCount() >= 16) return false; param.Add(GetId(s)); Spaces(s); if(*s == ')') break; if(*s != ',') return false; s++; } s++; } Spaces(s); Macro& m = macro.GetAdd(name); m.n = param.GetCount(); m.param <<= param.GetKeys(); m.text.Clear(); while(*s) { if(IsId(*s)) { String name = GetId(s); int i = param.Find(name); if(i >= 0) m.text.Cat(i + 16); else m.text.Cat(name); } else { if(*s >= 16 && *s < 32) m.text.Cat(' '); else m.text.Cat(*s); s++; } } return true; } String MacroSet::Expand(const char *s, Index& used) const { String text; while(*s) if(IsId(*s)) { String name = GetId(s); int i = macro.Find(name); if(i >= 0 && used.Find(i) < 0) { const Macro& m = macro[i]; Vector param; const char *t = s; Spaces(s); if(*s == '(') { int level = 0; String p; for(;;) { s++; if((*s == ',' || *s == ')') && level == 0) { p = TrimRight(TrimLeft(p)); // p.TrimLeft(); // p.TrimRight(); param.Add(Expand(p)); p.Clear(); } else p.Cat(*s); if(*s == '(') level++; if(*s == ')') { if(level <= 0) { s++; break; } level--; } } } else s = t; param.SetCount(16); Index newused(used, 1); newused.Add(i); String mt = Expand(m.text, newused); for(const char *q = mt; *q; q++) if(*q >= 16 && *q < 32) text.Cat(param[*q - 16]); else text.Cat(*q); } else text.Cat(name); } else text.Cat(*s++); return text; } String MacroSet::Expand(const char *s) const { Index temp; return Expand(s, temp); } String MacroSet::Get(int i) const { const Macro& m = macro[i]; String s = macro.GetKey(i); if(m.param.GetCount()) { s.Cat('('); for(i = 0; i < m.param.GetCount(); i++) { if(i) s.Cat(", "); s.Cat(m.param[i]); } s.Cat(')'); } s.Cat(" "); s.Cat(Expand(s)); return s; } class SqlConsole : public TopWindow { public: virtual bool Key(dword key, int count); virtual void Serialize(Stream& s); void ListMenu(Bar& bar); void ListPrintRow(); void ListPrintList(); void ListExport(); protected: Sql cursor; String lastquery; Splitter lires, vsplit; ArrayCtrl list; StaticRect rec_err; DocEdit errortext; ArrayCtrl record; ArrayCtrl trace; WithDropChoice command; Button schema; // CallbackSet hide; Vector cw; Vector visible; Vector lob; String LastDir; MacroSet macroset; enum { NORMAL, RERUN, SCRIPT, QUIET, }; void ColSize(); void Hide(int i); void Record(); void Execute(int type = NORMAL); void TraceToCommand(); void TraceToExecute(); void ListToCommand(); void SaveTrace(); void RunScript(bool quiet); void TraceMenu(Bar& menu); void MacroList(); void ObjectTree() { SQLObjectTree(cursor.GetSession()); } class Exec; friend class Exec; class Exec : public StatementExecutor { public: Exec(bool quiet) : quiet(quiet) {} SqlConsole *me; bool quiet; virtual bool Execute(const String& stmt) { me->command <<= stmt; me->Sync(); me->Execute(quiet ? QUIET : SCRIPT); return true; } }; public: typedef SqlConsole CLASSNAME; void Perform(); SqlConsole(SqlSession& session); }; void SqlConsole::MacroList() { list.Reset(); record.Clear(); list.AddColumn(t_("Defined macros")); for(int i = 0; i < macroset.GetCount(); i++) list.Add('#' + macroset.Get(i)); } void SqlConsole::Execute(int type) { list.Reset(); visible.Clear(); lob.Clear(); record.Clear(); String s = command.GetText().ToString(); if(*s == '#') { if(s[1] == '\0') { MacroList(); command.Clear(); return; } list.AddColumn(t_("Macro definition")); if(macroset.Define(~s + 1)) { list.Add("OK"); trace.Add(s, t_("Macro OK"), ""); if(type == NORMAL) command.AddHistory(); command.Clear(); } else { list.Add(t_("Invalid macro")); trace.Add(s, t_("Invalid"), ""); } return; } s = TrimRight(macroset.Expand(s)); // s.TrimRight(); while(*s.Last() == ';') s.Trim(s.GetLength() - 1); int ms0 = GetTickCount(); cursor.ClearError(); lastquery = s; if(!cursor.Execute(s)) { error: record.Hide(); errortext.Show(); list.AddColumn(t_("Error")); String err = cursor.GetLastError(); errortext <<= err; list.Add(err); trace.Add(s, err, ""); trace.GoEnd(); Title((s + " - " + err).ToWString()); return; } if(type == QUIET) return; bool onecol = cursor.GetColumns() == 1; record.Show(!onecol); errortext.Show(onecol); int ms1 = GetTickCount(); cw.SetCount(cursor.GetColumns()); visible.SetCount(cw.GetCount(), true); for(int i = 0; i < cursor.GetColumns(); i++) { const SqlColumnInfo& ci = cursor.GetColumnInfo(i); list.AddColumn(ci.name); list.HeaderTab(i).WhenAction = THISBACK1(Hide, i); cw[i] = GetTextSize(ci.name, StdFont()).cx + 2 * list.HeaderTab(i).GetMargin(); record.Add(ci.name, Null); lob.Add(ci.type == -1 || ci.type == -2); // !! BLOB / CLOB hack } Progress pi; pi.SetText(t_("Fetched %d line(s)")); while(cursor.Fetch()) { Vector row = cursor.GetRow(); for(int i = 0; i < cursor.GetColumns(); i++) { if(lob[i]) { String temp; cursor.GetColumn(i, temp); row[i] = temp; } cw[i] = max(cw[i], GetTextSize(StdFormat(row[i]), StdFont()).cx + 2 * list.HeaderTab(i).GetMargin()); } list.Add(row); if(pi.StepCanceled()) break; } if(cursor.GetColumns() && cursor.WasError()) { list.Reset(); goto error; } visible.SetCount(cursor.GetColumns(), true); ColSize(); if(list.GetCount() > 0) list.SetCursor(0); Title(NFormat(t_("%s (%d rows)"), s, pi.GetPos())); String rrows = Format(t_("%d rows"), max(list.GetCount(), cursor.GetRowsProcessed())); String rms = Format(t_("%d ms"), ms1 - ms0); if(type == RERUN && trace.IsCursor()) { trace.Set(1, rrows); trace.Set(2, rms); } else { trace.Add(s, rrows, rms); trace.GoEnd(); } if(type == NORMAL) command.AddHistory(); command.Clear(); } void SqlConsole::ColSize() { int maxw = 18 * StdFont().Info().GetAveWidth(); int maxx = list.GetSize().cx; int wx = 0; for(int i = 0; i < cursor.GetColumns(); i++) if(visible[i]) { int w = min(maxw, cw[i]); wx += w; if(wx < maxx) { list.HeaderObject().SetTabRatio(i, w); list.HeaderObject().ShowTab(i); } else list.HeaderObject().HideTab(i); } else list.HeaderObject().HideTab(i); } void SqlConsole::Hide(int i) { if(i < visible.GetCount()) visible[i] = false; ColSize(); } void SqlConsole::Record() { if(list.GetIndexCount() == 1) errortext <<= StdFormat(list.Get(0)); for(int i = 0; i < list.GetIndexCount(); i++) record.Set(i, 1, list.Get(i)); } bool SqlConsole::Key(dword key, int count) { switch(key) { case K_ENTER: Execute(); return true; case K_CTRL_R: RunScript(false); return true; case K_CTRL_Q: RunScript(true); return true; case K_CTRL_S: SaveTrace(); return true; case K_CTRL_M: MacroList(); return true; } if(key >= ' ' && key < 256 || key == K_RIGHT || key == K_LEFT || key == K_ALT_DOWN) { command.SetFocus(); command.Key(key, count); return true; } return TopWindow::Key(key, count); } void SqlConsole::Serialize(Stream& s) { int version = 2; s / version; s.Magic(); Rect r = GetRect(); s % r; SetRect(r); vsplit.Serialize(s); record.SerializeHeader(s); lires.Serialize(s); command.SerializeList(s); trace.SerializeHeader(s); if(version >= 1) s % LastDir; if(version >= 2) s % macroset; } void SqlConsole::Perform() { const char cfg[] = "SqlConsole.cfg"; LoadFromFile(*this, cfg); Title(t_("SQL Commander")); Icon(SqlConsoleImg::SqlConsoleIconSmall(), SqlConsoleImg::SqlConsoleIconLarge()); Sizeable(); Zoomable(); ActiveFocus(command); Run(); cursor.ClearError(); StoreToFile(*this, cfg); } void SqlConsole::TraceToCommand() { if(trace.IsCursor()) command.SetData(trace.Get(0)); } void SqlConsole::ListToCommand() { int c = list.GetEditColumn(); if(list.IsCursor() && c >= 0 && c < list.GetIndexCount()) command.Insert(StdFormat(list.Get(c)).ToWString()); command.SetFocus(); } void SqlConsole::TraceToExecute() { Execute(RERUN); } void SqlConsole::SaveTrace() { FileSelector fsel; fsel.ActiveDir(LastDir); fsel.DefaultExt("sql"); fsel.Type(t_("SQL scripts (*.sql)"), "*.sql"); fsel.AllFilesType(); if(!fsel.ExecuteSaveAs(t_("Save trace as"))) return; FileOut out(~fsel); if(!out) return; LastDir = GetFileDirectory(~fsel); for(int i = 0; i < trace.GetCount(); i++) { out.Put((String)trace.Get(i, 0)); out.Put(";\n"); } } void SqlConsole::RunScript(bool quiet) { UPP::RunScript runscript = cursor.GetSession().GetRunScript(); if(!runscript) { Exclamation(t_("Database connection doesn't support running scripts.")); return; } FileSelector fsel; fsel.ActiveDir(LastDir); fsel.DefaultExt("sql"); fsel.Type(t_("SQL scripts (*.sql)"), "*.sql"); fsel.AllFilesType(); if(!fsel.ExecuteOpen(t_("Run script"))) return; Exec exec(quiet); exec.me = this; LastDir = GetFileDirectory(~fsel); Progress progress(t_("Executing script")); (*runscript)(LoadFile(~fsel), exec, progress); } void SqlConsole::TraceMenu(Bar& menu) { menu.Add(t_("Save as script.."), THISBACK(SaveTrace)).Key(K_CTRL_S); menu.Add(t_("Run script.."), THISBACK1(RunScript, false)).Key(K_CTRL_R); menu.Add(t_("Run script quietly.."), THISBACK1(RunScript, true)).Key(K_CTRL_Q); menu.Add(t_("List macros.."), THISBACK(MacroList)).Key(K_CTRL_M); } void SqlConsole::ListMenu(Bar& bar) { bar.Add(t_("Print record"), THISBACK(ListPrintRow)); bar.Add(t_("Print list"), THISBACK(ListPrintList)); bar.Add(t_("Export..."), THISBACK(ListExport)); } void SqlConsole::ListPrintRow() { String qtf; qtf << "[A1 "; if(!IsNull(lastquery)) qtf << "[4* \1" << lastquery << "\1]&&"; for(int i = 0; i < record.GetCount(); i++) { qtf << (i ? "--" : "++") << "::10@(240.240.240) \1" << StdFormat(record.Get(i, 0)) << "\1" "||::30@(255.255.255) \1" << StdFormat(record.Get(i, 1)) << "\1"; } qtf << "++"; Report report; report << qtf; UPP::Perform(report); } void SqlConsole::ListPrintList() { String qtf; qtf << "[A1 "; if(!IsNull(lastquery)) qtf << "[4* \1" << lastquery << "\1]&&"; for(int i = 0; i < record.GetCount(); i++) qtf << (i ? "||" : "++") << "::@(240.240.240) [* \1" << StdFormat(record.Get(i, 0)) << "\1]"; for(int i = 0; i < list.GetCount(); i++) for(int j = 0; j < list.GetIndexCount(); j++) qtf << (j ? "||" : "--") << "::@(255.255.255) \1" << StdFormat(list.Get(i, j)) << '\1'; qtf << "++"; Report report; report << qtf; UPP::Perform(report); } void SqlConsole::ListExport() { RunDlgSqlExport(cursor, lastquery, Null); } SqlConsole::SqlConsole(SqlSession& session) : cursor(session) { int ecy = EditField::GetStdHeight(); lires.Horz(list, rec_err); rec_err << record.SizePos() << errortext.SizePos(); errortext.SetReadOnly(); errortext.SetFont(Courier(12)); errortext.Hide(); vsplit.Vert(lires, trace); record.NoCursor(); record.AddColumn(t_("Column"), 5); record.AddColumn(t_("Value"), 10); trace.AddColumn(t_("Command"), 8); trace.AddColumn(t_("Result"), 1); trace.AddColumn(t_("Duration"), 1); trace.WhenLeftClick = THISBACK(TraceToCommand); trace.WhenLeftDouble = THISBACK(TraceToExecute); trace.WhenBar = THISBACK(TraceMenu); list.WhenEnterRow = THISBACK(Record); list.WhenLeftDouble = THISBACK(ListToCommand); list.WhenBar = THISBACK(ListMenu); Add(vsplit.VSizePos(0, ecy + 4).HSizePos(0, 0)); Add(command.BottomPos(0, ecy).HSizePos(0, 90)); Add(schema.BottomPos(0, ecy).RightPos(0, 80)); schema.SetLabel(t_("&Schema")); schema <<= THISBACK(ObjectTree); // hide = callback(this, &SqlConsole::Hide); // Size sz = Stock.GetScreenSize(); // SetRect((sz.cx - 600) / 2, (sz.cy - 400) / 2, 600, 400); ActiveFocus(command); } void SQLCommander(SqlSession& session) { SqlConsole con(session); con.Perform(); } END_UPP_NAMESPACE