|
@@ -1,17 +1,19 @@
|
|
|
"""!
|
|
|
@package dbmgr.sqlbuilder
|
|
|
|
|
|
-@brief GRASS SQL Builder
|
|
|
+@brief GRASS SQL Select/Update Builder
|
|
|
|
|
|
Classes:
|
|
|
- - sqlbuilder::SQLFrame
|
|
|
+ - sqlbuilder::SQLBuilder
|
|
|
+ - sqlbuilder::SQLBuilderSelect
|
|
|
+ - sqlbuilder::SQLBuilderUpdate
|
|
|
|
|
|
Usage:
|
|
|
@code
|
|
|
python sqlbuilder.py vector_map
|
|
|
@endcode
|
|
|
|
|
|
-(C) 2007-2009, 2011 by the GRASS Development Team
|
|
|
+(C) 2007-2009, 2011-2012 by the GRASS Development Team
|
|
|
|
|
|
This program is free software under the GNU General Public License
|
|
|
(>=v2). Read the file COPYING that comes with GRASS for details.
|
|
@@ -19,6 +21,7 @@ This program is free software under the GNU General Public License
|
|
|
@author Jachym Cepicky <jachym.cepicky gmail.com> (original author)
|
|
|
@author Martin Landa <landa.martin gmail.com>
|
|
|
@author Hamish Bowman <hamish_b yahoo com>
|
|
|
+@author Refactoring, SQLBUilderUpdate by Stepan Turek <stepan.turek seznam.cz> (GSoC 2012, mentor: Martin Landa)
|
|
|
"""
|
|
|
|
|
|
import os
|
|
@@ -29,27 +32,25 @@ if __name__ == "__main__":
|
|
|
from core import globalvar
|
|
|
import wx
|
|
|
|
|
|
-from core.gcmd import RunCommand, GError
|
|
|
+from core.gcmd import RunCommand, GError, GMessage
|
|
|
from dbmgr.vinfo import createDbInfoDesc, VectorDBInfo
|
|
|
|
|
|
import grass.script as grass
|
|
|
|
|
|
-class SQLFrame(wx.Frame):
|
|
|
- """!SQL Frame class"""
|
|
|
- def __init__(self, parent, title, vectmap, id = wx.ID_ANY,
|
|
|
- layer = 1, qtype = "select", evtHandler = None):
|
|
|
-
|
|
|
+class SQLBuilder(wx.Frame):
|
|
|
+ """!SQLBuider class
|
|
|
+ Base class for classes, which builds SQL statements.
|
|
|
+ """
|
|
|
+ def __init__(self, parent, title, vectmap, modeChoices, id = wx.ID_ANY,
|
|
|
+ layer = 1):
|
|
|
wx.Frame.__init__(self, parent, id, title)
|
|
|
|
|
|
self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_sql.ico'),
|
|
|
wx.BITMAP_TYPE_ICO))
|
|
|
|
|
|
self.parent = parent
|
|
|
- self.evtHandler = evtHandler
|
|
|
-
|
|
|
- #
|
|
|
+
|
|
|
# variables
|
|
|
- #
|
|
|
self.vectmap = vectmap # fullname
|
|
|
if not "@" in self.vectmap:
|
|
|
self.vectmap = grass.find_file(self.vectmap, element = 'vector')['fullname']
|
|
@@ -61,25 +62,19 @@ class SQLFrame(wx.Frame):
|
|
|
self.tablename = self.dbInfo.GetTable(self.layer)
|
|
|
self.driver, self.database = self.dbInfo.GetDbSettings(self.layer)
|
|
|
|
|
|
- self.qtype = qtype # type of query: SELECT, UPDATE, DELETE, ...
|
|
|
self.colvalues = [] # array with unique values in selected column
|
|
|
-
|
|
|
- # set dialog title
|
|
|
- self.SetTitle(_("GRASS SQL Builder (%(type)s): vector map <%(map)s>") % \
|
|
|
- { 'type' : self.qtype.upper(), 'map' : self.vectmap })
|
|
|
|
|
|
self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
|
|
|
|
|
|
# statusbar
|
|
|
self.statusbar = self.CreateStatusBar(number=1)
|
|
|
- self.statusbar.SetStatusText(_("SQL statement not verified"), 0)
|
|
|
|
|
|
- self._doLayout()
|
|
|
+ self._doLayout(modeChoices)
|
|
|
|
|
|
- def _doLayout(self):
|
|
|
+ def _doLayout(self, modeChoices):
|
|
|
"""!Do dialog layout"""
|
|
|
|
|
|
- pagesizer = wx.BoxSizer(wx.VERTICAL)
|
|
|
+ self.pagesizer = wx.BoxSizer(wx.VERTICAL)
|
|
|
|
|
|
|
|
|
# dbInfo
|
|
@@ -102,10 +97,8 @@ class SQLFrame(wx.Frame):
|
|
|
self.text_sql = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY,
|
|
|
value = '', size = (-1, 50),
|
|
|
style=wx.TE_MULTILINE)
|
|
|
- if self.qtype.lower() == "select":
|
|
|
- self.text_sql.SetValue("SELECT * FROM %s" % self.tablename)
|
|
|
+
|
|
|
self.text_sql.SetInsertionPointEnd()
|
|
|
- self.text_sql.SetToolTipString(_("Example: %s") % "SELECT * FROM roadsmajor WHERE MULTILANE = 'no' OR OBJECTID < 10")
|
|
|
wx.CallAfter(self.text_sql.SetFocus)
|
|
|
|
|
|
sqlboxsizer.Add(item = self.text_sql, flag = wx.EXPAND)
|
|
@@ -115,15 +108,12 @@ class SQLFrame(wx.Frame):
|
|
|
#
|
|
|
self.btn_clear = wx.Button(parent = self.panel, id = wx.ID_CLEAR)
|
|
|
self.btn_clear.SetToolTipString(_("Set SQL statement to default"))
|
|
|
- self.btn_verify = wx.Button(parent = self.panel, id = wx.ID_ANY,
|
|
|
- label = _("Verify"))
|
|
|
- self.btn_verify.SetToolTipString(_("Verify SQL statement"))
|
|
|
self.btn_apply = wx.Button(parent = self.panel, id = wx.ID_APPLY)
|
|
|
self.btn_apply.SetToolTipString(_("Apply SQL statement and close the dialog"))
|
|
|
self.btn_close = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
|
|
|
self.btn_close.SetToolTipString(_("Close the dialog"))
|
|
|
|
|
|
- self.btn_lv = { 'is' : ['=', ],
|
|
|
+ self.btn_logic = { 'is' : ['=', ],
|
|
|
'isnot' : ['!=', ],
|
|
|
'like' : ['LIKE', ],
|
|
|
'gt' : ['>', ],
|
|
@@ -135,39 +125,41 @@ class SQLFrame(wx.Frame):
|
|
|
'and' : ['AND', ],
|
|
|
'brac' : ['()', ],
|
|
|
'prc' : ['%', ] }
|
|
|
-
|
|
|
- for key, value in self.btn_lv.iteritems():
|
|
|
- btn = wx.Button(parent = self.panel, id = wx.ID_ANY,
|
|
|
+
|
|
|
+ self.btn_logicpanel = wx.Panel(parent = self.panel, id = wx.ID_ANY)
|
|
|
+ for key, value in self.btn_logic.iteritems():
|
|
|
+ btn = wx.Button(parent = self.btn_logicpanel, id = wx.ID_ANY,
|
|
|
label = value[0])
|
|
|
- self.btn_lv[key].append(btn.GetId())
|
|
|
-
|
|
|
- buttonsizer = wx.FlexGridSizer(cols = 4, hgap = 5, vgap = 5)
|
|
|
- buttonsizer.Add(item = self.btn_clear)
|
|
|
- buttonsizer.Add(item = self.btn_verify)
|
|
|
- buttonsizer.Add(item = self.btn_apply)
|
|
|
- buttonsizer.Add(item = self.btn_close)
|
|
|
-
|
|
|
- buttonsizer2 = wx.GridBagSizer(5, 5)
|
|
|
- buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['is'][1]), pos = (0,0))
|
|
|
- buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['isnot'][1]), pos = (1,0))
|
|
|
- buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['like'][1]), pos = (2, 0))
|
|
|
-
|
|
|
- buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['gt'][1]), pos = (0, 1))
|
|
|
- buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['ge'][1]), pos = (1, 1))
|
|
|
- buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['or'][1]), pos = (2, 1))
|
|
|
-
|
|
|
- buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['lt'][1]), pos = (0, 2))
|
|
|
- buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['le'][1]), pos = (1, 2))
|
|
|
- buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['not'][1]), pos = (2, 2))
|
|
|
-
|
|
|
- buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['brac'][1]), pos = (0, 3))
|
|
|
- buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['prc'][1]), pos = (1, 3))
|
|
|
- buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['and'][1]), pos = (2, 3))
|
|
|
-
|
|
|
+ self.btn_logic[key].append(btn.GetId())
|
|
|
+
|
|
|
+ self.buttonsizer = wx.FlexGridSizer(cols = 4, hgap = 5, vgap = 5)
|
|
|
+ self.buttonsizer.Add(item = self.btn_clear)
|
|
|
+ self.buttonsizer.Add(item = self.btn_apply)
|
|
|
+ self.buttonsizer.Add(item = self.btn_close)
|
|
|
+
|
|
|
+ btn_logicsizer = wx.GridBagSizer(5, 5)
|
|
|
+ btn_logicsizer.Add(item = self.FindWindowById(self.btn_logic['is'][1]), pos = (0,0))
|
|
|
+ btn_logicsizer.Add(item = self.FindWindowById(self.btn_logic['isnot'][1]), pos = (1,0))
|
|
|
+ btn_logicsizer.Add(item = self.FindWindowById(self.btn_logic['like'][1]), pos = (2, 0))
|
|
|
+
|
|
|
+ btn_logicsizer.Add(item = self.FindWindowById(self.btn_logic['gt'][1]), pos = (0, 1))
|
|
|
+ btn_logicsizer.Add(item = self.FindWindowById(self.btn_logic['ge'][1]), pos = (1, 1))
|
|
|
+ btn_logicsizer.Add(item = self.FindWindowById(self.btn_logic['or'][1]), pos = (2, 1))
|
|
|
+
|
|
|
+ btn_logicsizer.Add(item = self.FindWindowById(self.btn_logic['lt'][1]), pos = (0, 2))
|
|
|
+ btn_logicsizer.Add(item = self.FindWindowById(self.btn_logic['le'][1]), pos = (1, 2))
|
|
|
+ btn_logicsizer.Add(item = self.FindWindowById(self.btn_logic['not'][1]), pos = (2, 2))
|
|
|
+
|
|
|
+ btn_logicsizer.Add(item = self.FindWindowById(self.btn_logic['brac'][1]), pos = (0, 3))
|
|
|
+ btn_logicsizer.Add(item = self.FindWindowById(self.btn_logic['prc'][1]), pos = (1, 3))
|
|
|
+ btn_logicsizer.Add(item = self.FindWindowById(self.btn_logic['and'][1]), pos = (2, 3))
|
|
|
+
|
|
|
+ self.btn_logicpanel.SetSizer(btn_logicsizer)
|
|
|
+
|
|
|
#
|
|
|
# list boxes (columns, values)
|
|
|
#
|
|
|
- hsizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
|
+ self.hsizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
|
|
|
|
columnsbox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
|
|
|
label = " %s " % _("Columns"))
|
|
@@ -178,32 +170,37 @@ class SQLFrame(wx.Frame):
|
|
|
columnsizer.Add(item = self.list_columns, proportion = 1,
|
|
|
flag = wx.EXPAND)
|
|
|
|
|
|
- radiosizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
|
- self.radio_cv = wx.RadioBox(parent = self.panel, id = wx.ID_ANY,
|
|
|
- label = " %s " % _("Add on double-click"),
|
|
|
- choices = [_("columns"), _("values")])
|
|
|
- self.radio_cv.SetSelection(1) # default 'values'
|
|
|
- radiosizer.Add(item = self.radio_cv, proportion = 1,
|
|
|
+ modesizer = wx.BoxSizer(wx.VERTICAL)
|
|
|
+
|
|
|
+
|
|
|
+ self.mode = wx.RadioBox(parent = self.panel, id = wx.ID_ANY,
|
|
|
+ label = " %s " % _("Interactive insertion"),
|
|
|
+ choices = modeChoices,
|
|
|
+ style = wx.RA_SPECIFY_COLS,
|
|
|
+ majorDimension = 1)
|
|
|
+
|
|
|
+ self.mode.SetSelection(1) # default 'values'
|
|
|
+ modesizer.Add(item = self.mode, proportion = 1,
|
|
|
flag = wx.ALIGN_CENTER_HORIZONTAL | wx.EXPAND, border = 5)
|
|
|
|
|
|
- columnsizer.Add(item = radiosizer, proportion = 0,
|
|
|
- flag = wx.TOP | wx.EXPAND, border = 5)
|
|
|
# self.list_columns.SetMinSize((-1,130))
|
|
|
# self.list_values.SetMinSize((-1,100))
|
|
|
|
|
|
- valuesbox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
|
|
|
+ self.valuespanel = wx.Panel(parent = self.panel, id = wx.ID_ANY)
|
|
|
+ valuesbox = wx.StaticBox(parent = self.valuespanel, id = wx.ID_ANY,
|
|
|
label = " %s " % _("Values"))
|
|
|
valuesizer = wx.StaticBoxSizer(valuesbox, wx.VERTICAL)
|
|
|
- self.list_values = wx.ListBox(parent = self.panel, id = wx.ID_ANY,
|
|
|
+ self.list_values = wx.ListBox(parent = self.valuespanel, id = wx.ID_ANY,
|
|
|
choices = self.colvalues,
|
|
|
style = wx.LB_MULTIPLE)
|
|
|
valuesizer.Add(item = self.list_values, proportion = 1,
|
|
|
- flag = wx.EXPAND)
|
|
|
-
|
|
|
- self.btn_unique = wx.Button(parent = self.panel, id = wx.ID_ANY,
|
|
|
+ flag = wx.EXPAND)
|
|
|
+ self.valuespanel.SetSizer(valuesizer)
|
|
|
+
|
|
|
+ self.btn_unique = wx.Button(parent = self.valuespanel, id = wx.ID_ANY,
|
|
|
label = _("Get all values"))
|
|
|
self.btn_unique.Enable(False)
|
|
|
- self.btn_uniquesample = wx.Button(parent = self.panel, id = wx.ID_ANY,
|
|
|
+ self.btn_uniquesample = wx.Button(parent = self.valuespanel, id = wx.ID_ANY,
|
|
|
label = _("Get sample"))
|
|
|
self.btn_uniquesample.Enable(False)
|
|
|
|
|
@@ -214,62 +211,64 @@ class SQLFrame(wx.Frame):
|
|
|
flag = wx.ALIGN_CENTER_HORIZONTAL)
|
|
|
|
|
|
valuesizer.Add(item = buttonsizer3, proportion = 0,
|
|
|
- flag = wx.TOP, border = 5)
|
|
|
+ flag = wx.TOP, border = 5)
|
|
|
|
|
|
# hsizer1.Add(wx.StaticText(self.panel,-1, "Unique values: "), border=0, proportion=1)
|
|
|
|
|
|
- hsizer.Add(item = columnsizer, proportion = 1,
|
|
|
+ self.hsizer.Add(item = columnsizer, proportion = 1,
|
|
|
flag = wx.EXPAND)
|
|
|
- hsizer.Add(item = valuesizer, proportion = 1,
|
|
|
+ self.hsizer.Add(item = self.valuespanel, proportion = 1,
|
|
|
flag = wx.EXPAND)
|
|
|
-
|
|
|
+
|
|
|
self.close_onapply = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
|
|
|
label = _("Close dialog on apply"))
|
|
|
self.close_onapply.SetValue(True)
|
|
|
|
|
|
- pagesizer.Add(item = databaseboxsizer,
|
|
|
+ self.pagesizer.Add(item = databaseboxsizer,
|
|
|
flag = wx.ALL | wx.EXPAND, border = 5)
|
|
|
- pagesizer.Add(item = hsizer, proportion = 1,
|
|
|
+ self.pagesizer.Add(item = modesizer, proportion = 0,
|
|
|
+ flag = wx.ALL, border = 5)
|
|
|
+ self.pagesizer.Add(item = self.hsizer, proportion = 1,
|
|
|
flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
|
|
|
- # pagesizer.Add(self.btn_uniqe,0,wx.ALIGN_LEFT|wx.TOP,border=5)
|
|
|
- # pagesizer.Add(self.btn_uniqesample,0,wx.ALIGN_LEFT|wx.TOP,border=5)
|
|
|
- pagesizer.Add(item = buttonsizer2, proportion = 0,
|
|
|
+ # self.pagesizer.Add(self.btn_uniqe,0,wx.ALIGN_LEFT|wx.TOP,border=5)
|
|
|
+ # self.pagesizer.Add(self.btn_uniqesample,0,wx.ALIGN_LEFT|wx.TOP,border=5)
|
|
|
+ self.pagesizer.Add(item = self.btn_logicpanel, proportion = 0,
|
|
|
flag = wx.ALIGN_CENTER_HORIZONTAL)
|
|
|
- pagesizer.Add(item = sqlboxsizer, proportion = 0,
|
|
|
+ self.pagesizer.Add(item = sqlboxsizer, proportion = 0,
|
|
|
flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 5)
|
|
|
- pagesizer.Add(item = buttonsizer, proportion = 0,
|
|
|
+ self.pagesizer.Add(item = self.buttonsizer, proportion = 0,
|
|
|
flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
|
|
|
- pagesizer.Add(item = self.close_onapply, proportion = 0,
|
|
|
+ self.pagesizer.Add(item = self.close_onapply, proportion = 0,
|
|
|
flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
|
|
|
|
|
|
#
|
|
|
# bindings
|
|
|
#
|
|
|
+ self.mode.Bind(wx.EVT_RADIOBOX, self.OnMode)
|
|
|
+ #self.text_sql.Bind(wx.EVT_ACTIVATE, self.OnTextSqlActivate)TODO
|
|
|
+
|
|
|
self.btn_unique.Bind(wx.EVT_BUTTON, self.OnUniqueValues)
|
|
|
self.btn_uniquesample.Bind(wx.EVT_BUTTON, self.OnSampleValues)
|
|
|
|
|
|
- for key, value in self.btn_lv.iteritems():
|
|
|
+ for key, value in self.btn_logic.iteritems():
|
|
|
self.FindWindowById(value[1]).Bind(wx.EVT_BUTTON, self.OnAddMark)
|
|
|
-
|
|
|
+
|
|
|
self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
|
|
|
self.btn_clear.Bind(wx.EVT_BUTTON, self.OnClear)
|
|
|
- self.btn_verify.Bind(wx.EVT_BUTTON, self.OnVerify)
|
|
|
self.btn_apply.Bind(wx.EVT_BUTTON, self.OnApply)
|
|
|
|
|
|
self.list_columns.Bind(wx.EVT_LISTBOX, self.OnAddColumn)
|
|
|
self.list_values.Bind(wx.EVT_LISTBOX, self.OnAddValue)
|
|
|
-
|
|
|
- self.text_sql.Bind(wx.EVT_TEXT, self.OnText)
|
|
|
-
|
|
|
+
|
|
|
self.panel.SetAutoLayout(True)
|
|
|
- self.panel.SetSizer(pagesizer)
|
|
|
- pagesizer.Fit(self.panel)
|
|
|
+ self.panel.SetSizer(self.pagesizer)
|
|
|
+ self.pagesizer.Fit(self.panel)
|
|
|
|
|
|
self.Layout()
|
|
|
- self.SetMinSize((660, 525))
|
|
|
+ self.SetSize((400, 525))
|
|
|
self.SetClientSize(self.panel.GetSize())
|
|
|
self.CenterOnParent()
|
|
|
-
|
|
|
+
|
|
|
def OnUniqueValues(self, event, justsample = False):
|
|
|
"""!Get unique values"""
|
|
|
vals = []
|
|
@@ -337,22 +336,128 @@ class SQLFrame(wx.Frame):
|
|
|
def OnAddMark(self, event):
|
|
|
"""!Add mark"""
|
|
|
mark = None
|
|
|
- for key, value in self.btn_lv.iteritems():
|
|
|
+ if self.btn_logicpanel and \
|
|
|
+ self.btn_logicpanel.IsShown():
|
|
|
+ btns = self.btn_logic
|
|
|
+ elif self.btn_arithmeticpanel and \
|
|
|
+ self.btn_arithmeticpanel.IsShown():
|
|
|
+ btns = self.btn_arithmetic
|
|
|
+
|
|
|
+ for key, value in btns.iteritems():
|
|
|
if event.GetId() == value[1]:
|
|
|
mark = value[0]
|
|
|
break
|
|
|
|
|
|
self._add(element = 'mark', value = mark)
|
|
|
|
|
|
+ def GetSQLStatement(self):
|
|
|
+ """!Return SQL statement"""
|
|
|
+ return self.text_sql.GetValue().strip().replace("\n"," ")
|
|
|
+
|
|
|
+ def OnClose(self, event):
|
|
|
+ """!Close button pressed"""
|
|
|
+ if self.evtHandler:
|
|
|
+ self.evtHandler(event = 'close')
|
|
|
+
|
|
|
+ self.Destroy()
|
|
|
+ event.Skip()
|
|
|
+
|
|
|
+class SQLBuilderSelect(SQLBuilder):
|
|
|
+ """!Class for building SELECT SQL statement"""
|
|
|
+ def __init__(self, parent, vectmap, id = wx.ID_ANY,
|
|
|
+ layer = 1, evtHandler = None):
|
|
|
+
|
|
|
+ self.evtHandler = evtHandler
|
|
|
+
|
|
|
+ # set dialog title
|
|
|
+ title = _("GRASS SQL Builder (%(type)s): vector map <%(map)s>") % \
|
|
|
+ { 'type' : "SELECT", 'map' : vectmap }
|
|
|
+
|
|
|
+ modeChoices = [_("Column to show (SELECT clause)"),
|
|
|
+ _("Constraint for query (WHERE clause)")]
|
|
|
+
|
|
|
+ SQLBuilder.__init__(self, parent, title, vectmap, id = wx.ID_ANY,
|
|
|
+ modeChoices = modeChoices, layer = layer)
|
|
|
+
|
|
|
+
|
|
|
+ def _doLayout(self, modeChoices):
|
|
|
+ """!Do dialog layout"""
|
|
|
+
|
|
|
+ SQLBuilder._doLayout(self, modeChoices)
|
|
|
+
|
|
|
+ self.text_sql.SetValue("SELECT * FROM %s" % self.tablename)
|
|
|
+ self.text_sql.SetToolTipString(_("Example: %s") % "SELECT * FROM roadsmajor WHERE MULTILANE = 'no' OR OBJECTID < 10")
|
|
|
+
|
|
|
+ self.btn_verify = wx.Button(parent = self.panel, id = wx.ID_ANY,
|
|
|
+ label = _("Verify"))
|
|
|
+ self.btn_verify.SetToolTipString(_("Verify SQL statement"))
|
|
|
+
|
|
|
+ self.buttonsizer.Insert(item = self.btn_verify, before = 1)
|
|
|
+
|
|
|
+ self.text_sql.Bind(wx.EVT_TEXT, self.OnText)
|
|
|
+ self.btn_verify.Bind(wx.EVT_BUTTON, self.OnVerify)
|
|
|
+
|
|
|
+ self.text_sql.SetInsertionPoint(self.text_sql.GetLastPosition())
|
|
|
+ self.statusbar.SetStatusText(_("SQL statement not verified"), 0)
|
|
|
+
|
|
|
+ def OnApply(self, event):
|
|
|
+ """Apply button pressed"""
|
|
|
+ if self.evtHandler:
|
|
|
+ self.evtHandler(event = 'apply')
|
|
|
+
|
|
|
+ if self.close_onapply.IsChecked():
|
|
|
+ self.Destroy()
|
|
|
+
|
|
|
+ event.Skip()
|
|
|
+
|
|
|
+ def OnClear(self, event):
|
|
|
+ """!Clear button pressed"""
|
|
|
+ self.text_sql.SetValue("SELECT * FROM %s" % self.tablename)
|
|
|
+
|
|
|
+ def OnMode(self, event):
|
|
|
+ """!Adjusts builder for chosen mode"""
|
|
|
+ if self.mode.GetSelection() == 0:
|
|
|
+ self.valuespanel.Hide()
|
|
|
+ self.btn_logicpanel.Hide()
|
|
|
+ elif self.mode.GetSelection() == 1:
|
|
|
+ self.valuespanel.Show()
|
|
|
+ self.btn_logicpanel.Show()
|
|
|
+ self.pagesizer.Layout()
|
|
|
+
|
|
|
+ def OnText(self, event):
|
|
|
+ """Query string changed"""
|
|
|
+ if len(self.text_sql.GetValue()) > 0:
|
|
|
+ self.btn_verify.Enable(True)
|
|
|
+ else:
|
|
|
+ self.btn_verify.Enable(False)
|
|
|
+
|
|
|
+ def OnVerify(self, event):
|
|
|
+ """!Verify button pressed"""
|
|
|
+ ret, msg = RunCommand('db.select',
|
|
|
+ getErrorMsg = True,
|
|
|
+ table = self.tablename,
|
|
|
+ sql = self.text_sql.GetValue(),
|
|
|
+ flags = 't',
|
|
|
+ driver = self.driver,
|
|
|
+ database = self.database)
|
|
|
+
|
|
|
+ if ret != 0 and msg:
|
|
|
+ self.statusbar.SetStatusText(_("SQL statement is not valid"), 0)
|
|
|
+ GError(parent = self,
|
|
|
+ message = _("SQL statement is not valid.\n\n%s") % msg)
|
|
|
+ else:
|
|
|
+ self.statusbar.SetStatusText(_("SQL statement is valid"), 0)
|
|
|
+
|
|
|
def _add(self, element, value):
|
|
|
"""!Add element to the query
|
|
|
|
|
|
@param element element to add (column, value)
|
|
|
"""
|
|
|
sqlstr = self.text_sql.GetValue()
|
|
|
+ curspos = self.text_sql.GetInsertionPoint()
|
|
|
newsqlstr = ''
|
|
|
if element == 'column':
|
|
|
- if self.radio_cv.GetSelection() == 0: # -> column
|
|
|
+ if self.mode.GetSelection() == 0: # -> column
|
|
|
idx1 = len('select')
|
|
|
idx2 = sqlstr.lower().find('from')
|
|
|
colstr = sqlstr[idx1:idx2].strip()
|
|
@@ -366,82 +471,240 @@ class SQLFrame(wx.Frame):
|
|
|
cols.append(value)
|
|
|
|
|
|
if len(cols) < 1:
|
|
|
- cols = ['*',]
|
|
|
-
|
|
|
- newsqlstr = 'SELECT ' + ','.join(cols) + ' ' + sqlstr[idx2:]
|
|
|
+ cols = ['*',]
|
|
|
+ newsqlstr = 'SELECT ' + ','.join(cols) + ' '
|
|
|
+ curspos = len(newsqlstr)
|
|
|
+ newsqlstr += sqlstr[idx2:]
|
|
|
else: # -> where
|
|
|
- newsqlstr = sqlstr
|
|
|
+ newsqlstr = ''
|
|
|
if sqlstr.lower().find('where') < 0:
|
|
|
- newsqlstr += ' WHERE'
|
|
|
-
|
|
|
+ newsqlstr += ' WHERE'
|
|
|
newsqlstr += ' ' + value
|
|
|
-
|
|
|
- elif element == 'value':
|
|
|
- newsqlstr = sqlstr + ' ' + value
|
|
|
- elif element == 'mark':
|
|
|
- newsqlstr = sqlstr + ' ' + value
|
|
|
+ curspos = self.text_sql.GetLastPosition() + len(newsqlstr)
|
|
|
+ newsqlstr = sqlstr + newsqlstr
|
|
|
+
|
|
|
+ elif element in ['value', 'mark']:
|
|
|
+ addstr = ' ' + value + ' '
|
|
|
+ newsqlstr = sqlstr[:curspos] + addstr + sqlstr[curspos:]
|
|
|
+ curspos += len(addstr)
|
|
|
|
|
|
if newsqlstr:
|
|
|
self.text_sql.SetValue(newsqlstr)
|
|
|
|
|
|
- def GetSQLStatement(self):
|
|
|
- """!Return SQL statement"""
|
|
|
- return self.text_sql.GetValue().strip().replace("\n"," ")
|
|
|
-
|
|
|
+ wx.CallAfter(self.text_sql.SetFocus)
|
|
|
+ self.text_sql.SetInsertionPoint(curspos)
|
|
|
+
|
|
|
def CloseOnApply(self):
|
|
|
"""!Return True if the dialog will be close on apply"""
|
|
|
return self.close_onapply.IsChecked()
|
|
|
-
|
|
|
- def OnText(self, event):
|
|
|
- """Query string changed"""
|
|
|
- if len(self.text_sql.GetValue()) > 0:
|
|
|
- self.btn_verify.Enable(True)
|
|
|
- else:
|
|
|
- self.btn_verify.Enable(False)
|
|
|
-
|
|
|
+
|
|
|
+class SQLBuilderUpdate(SQLBuilder):
|
|
|
+ """!Class for building UPDATE SQL statement"""
|
|
|
+ def __init__(self, parent, vectmap, id = wx.ID_ANY,
|
|
|
+ layer = 1, column = None):
|
|
|
+
|
|
|
+ self.column = column
|
|
|
+ # set dialog title
|
|
|
+ title = _("GRASS SQL Builder (%(type)s): vector map <%(map)s>") % \
|
|
|
+ { 'type' : "UPDATE", 'map' : vectmap }
|
|
|
+
|
|
|
+ modeChoices = [_("Column to set (SET clause)"),
|
|
|
+ _("Constraint for query (WHERE clause)"),
|
|
|
+ _("Calculate column value to set")]
|
|
|
+
|
|
|
+ SQLBuilder.__init__(self, parent, title, vectmap, id = wx.ID_ANY,
|
|
|
+ modeChoices = modeChoices, layer = layer)
|
|
|
+
|
|
|
+ def _doLayout(self, modeChoices):
|
|
|
+ """!Do dialog layout"""
|
|
|
+
|
|
|
+ SQLBuilder._doLayout(self, modeChoices)
|
|
|
+
|
|
|
+ self.initText = "UPDATE %s SET" % self.tablename
|
|
|
+ if self.column:
|
|
|
+ self.initText += " %s = " % self.column
|
|
|
+
|
|
|
+ self.text_sql.SetValue(self.initText)
|
|
|
+
|
|
|
+ self.btn_arithmetic = { 'eq' : ['=', ],
|
|
|
+ 'brac' : ['()',],
|
|
|
+ 'plus' : ['+', ],
|
|
|
+ 'minus' : ['-', ],
|
|
|
+ 'divide' : ['/', ],
|
|
|
+ 'multiply' : ['*', ]}
|
|
|
+
|
|
|
+ self.btn_arithmeticpanel = wx.Panel(parent = self.panel, id = wx.ID_ANY)
|
|
|
+
|
|
|
+ for key, value in self.btn_arithmetic.iteritems():
|
|
|
+ btn = wx.Button(parent = self.btn_arithmeticpanel, id = wx.ID_ANY,
|
|
|
+ label = value[0])
|
|
|
+ self.btn_arithmetic[key].append(btn.GetId())
|
|
|
+
|
|
|
+ btn_arithmeticsizer = wx.GridBagSizer(hgap = 5, vgap = 5)
|
|
|
+
|
|
|
+ btn_arithmeticsizer.Add(item = self.FindWindowById(self.btn_arithmetic['eq'][1]), pos = (0, 0))
|
|
|
+ btn_arithmeticsizer.Add(item = self.FindWindowById(self.btn_arithmetic['brac'][1]), pos = (1, 0))
|
|
|
+
|
|
|
+ btn_arithmeticsizer.Add(item = self.FindWindowById(self.btn_arithmetic['plus'][1]), pos = (0, 1))
|
|
|
+ btn_arithmeticsizer.Add(item = self.FindWindowById(self.btn_arithmetic['minus'][1]), pos = (1, 1))
|
|
|
+
|
|
|
+ btn_arithmeticsizer.Add(item = self.FindWindowById(self.btn_arithmetic['divide'][1]), pos = (0, 2))
|
|
|
+ btn_arithmeticsizer.Add(item = self.FindWindowById(self.btn_arithmetic['multiply'][1]), pos = (1, 2))
|
|
|
+
|
|
|
+ self.btn_arithmeticpanel.SetSizer(btn_arithmeticsizer)
|
|
|
+
|
|
|
+ self.pagesizer.Insert(item = self.btn_arithmeticpanel, before = 3,
|
|
|
+ proportion = 0, flag = wx.ALIGN_CENTER_HORIZONTAL)
|
|
|
+
|
|
|
+
|
|
|
+ self.funcpanel = wx.Panel(parent = self.panel, id = wx.ID_ANY)
|
|
|
+ self._initSqlFunctions()
|
|
|
+ funcsbox = wx.StaticBox(parent = self.funcpanel, id = wx.ID_ANY,
|
|
|
+ label = " %s " % _("Functions"))
|
|
|
+ funcsizer = wx.StaticBoxSizer(funcsbox, wx.VERTICAL)
|
|
|
+ self.list_func = wx.ListBox(parent = self.funcpanel, id = wx.ID_ANY,
|
|
|
+ choices = self.sqlFuncs['sqlite'].keys(),
|
|
|
+ style = wx.LB_SORT)
|
|
|
+
|
|
|
+ funcsizer.Add(item = self.list_func, proportion = 1,
|
|
|
+ flag = wx.EXPAND)
|
|
|
+
|
|
|
+ self.funcpanel.SetSizer(funcsizer)
|
|
|
+
|
|
|
+ self.hsizer.Insert(item = self.funcpanel, before = 2,
|
|
|
+ proportion = 1, flag = wx.EXPAND)
|
|
|
+
|
|
|
+ self.list_func.Bind(wx.EVT_LISTBOX, self.OnAddFunc)
|
|
|
+ for key, value in self.btn_arithmetic.iteritems():
|
|
|
+ self.FindWindowById(value[1]).Bind(wx.EVT_BUTTON, self.OnAddMark)
|
|
|
+ self.mode.SetSelection(0)
|
|
|
+ self.OnMode(None)
|
|
|
+ self.text_sql.SetInsertionPoint(self.text_sql.GetLastPosition())
|
|
|
+
|
|
|
def OnApply(self, event):
|
|
|
"""Apply button pressed"""
|
|
|
- if self.evtHandler:
|
|
|
- self.evtHandler(event = 'apply')
|
|
|
-
|
|
|
- if self.close_onapply.IsChecked():
|
|
|
- self.Destroy()
|
|
|
-
|
|
|
- event.Skip()
|
|
|
-
|
|
|
- def OnVerify(self, event):
|
|
|
- """!Verify button pressed"""
|
|
|
- ret, msg = RunCommand('db.select',
|
|
|
- getErrorMsg = True,
|
|
|
- table = self.tablename,
|
|
|
- sql = self.text_sql.GetValue(),
|
|
|
- flags = 't',
|
|
|
- driver = self.driver,
|
|
|
- database = self.database)
|
|
|
-
|
|
|
+
|
|
|
+ ret, msg = RunCommand('db.execute',
|
|
|
+ getErrorMsg = True,
|
|
|
+ parent = self,
|
|
|
+ stdin = self.text_sql.GetValue(),
|
|
|
+ input = '-',
|
|
|
+ driver = self.driver,
|
|
|
+ database = self.database)
|
|
|
+
|
|
|
if ret != 0 and msg:
|
|
|
- self.statusbar.SetStatusText(_("SQL statement is not valid"), 0)
|
|
|
- GError(parent = self,
|
|
|
- message = _("SQL statement is not valid.\n\n%s") % msg)
|
|
|
+ self.statusbar.SetStatusText(_("SQL statement was not applied"), 0)
|
|
|
+ #GError(parent = self,
|
|
|
+ # message = _("SQL statement can not be applied.\n\n%s") % msg)
|
|
|
else:
|
|
|
- self.statusbar.SetStatusText(_("SQL statement is valid"), 0)
|
|
|
-
|
|
|
+ self.statusbar.SetStatusText(_("SQL statement applied"), 0)
|
|
|
+
|
|
|
def OnClear(self, event):
|
|
|
"""!Clear button pressed"""
|
|
|
- if self.qtype.lower() == "select":
|
|
|
- self.text_sql.SetValue("SELECT * FROM %s" % self.tablename)
|
|
|
- else:
|
|
|
- self.text_sql.SetValue("")
|
|
|
-
|
|
|
- def OnClose(self, event):
|
|
|
- """!Close button pressed"""
|
|
|
- if self.evtHandler:
|
|
|
- self.evtHandler(event = 'close')
|
|
|
-
|
|
|
- self.Destroy()
|
|
|
-
|
|
|
- event.Skip()
|
|
|
+ self.text_sql.SetValue(self.initText)
|
|
|
+
|
|
|
+ def OnMode(self, event):
|
|
|
+ """!Adjusts builder for chosen mode"""
|
|
|
+ if self.mode.GetSelection() == 0:
|
|
|
+ self.valuespanel.Hide()
|
|
|
+ self.btn_logicpanel.Hide()
|
|
|
+ self.btn_arithmeticpanel.Hide()
|
|
|
+ self.funcpanel.Hide()
|
|
|
+ elif self.mode.GetSelection() == 1:
|
|
|
+ self.valuespanel.Show()
|
|
|
+ self.btn_logicpanel.Show()
|
|
|
+ self.btn_arithmeticpanel.Hide()
|
|
|
+ self.funcpanel.Hide()
|
|
|
+ elif self.mode.GetSelection() == 2:
|
|
|
+ self.valuespanel.Hide()
|
|
|
+ self.btn_logicpanel.Hide()
|
|
|
+ self.btn_arithmeticpanel.Show()
|
|
|
+ self.funcpanel.Show()
|
|
|
+ self.pagesizer.Layout()
|
|
|
+
|
|
|
+
|
|
|
+ def OnAddFunc(self, event):
|
|
|
+ """!Add function to the query"""
|
|
|
+
|
|
|
+ if self.driver == 'dbf':
|
|
|
+ GMessage(parent = self,
|
|
|
+ message = _("Dbf driver does not support usage of SQL functions."))
|
|
|
+ return
|
|
|
+
|
|
|
+ idx = self.list_func.GetSelections()
|
|
|
+ for i in idx:
|
|
|
+ func = self.sqlFuncs['sqlite'][self.list_func.GetString(i)][0]
|
|
|
+ self._add(element = 'func', value = func)
|
|
|
|
|
|
+
|
|
|
+ def _add(self, element, value):
|
|
|
+ """!Add element to the query
|
|
|
+
|
|
|
+ @param element element to add (column, value)
|
|
|
+ """
|
|
|
+ sqlstr = self.text_sql.GetValue()
|
|
|
+ curspos = self.text_sql.GetInsertionPoint()
|
|
|
+ newsqlstr = ''
|
|
|
+
|
|
|
+ if element in ['value', 'mark', 'func'] or \
|
|
|
+ (element == 'column' and self.mode.GetSelection() == 2):
|
|
|
+ addstr = ' ' + value + ' '
|
|
|
+ newsqlstr = sqlstr[:curspos] + addstr + sqlstr[curspos:]
|
|
|
+ curspos += len(addstr)
|
|
|
+ elif element == 'column':
|
|
|
+ if self.mode.GetSelection() == 0: # -> column
|
|
|
+ idx1 = sqlstr.lower().find('set') + len('set')
|
|
|
+ idx2 = sqlstr.lower().find('where')
|
|
|
+
|
|
|
+ if idx2 >= 0:
|
|
|
+ colstr = sqlstr[idx1:idx2].strip()
|
|
|
+ else:
|
|
|
+ colstr = sqlstr[idx1:].strip()
|
|
|
+
|
|
|
+ cols = [col.split('=')[0].strip() for col in colstr.split(',')]
|
|
|
+ if unicode(value) in cols:
|
|
|
+ self.text_sql.SetInsertionPoint(curspos)
|
|
|
+ wx.CallAfter(self.text_sql.SetFocus)
|
|
|
+ return
|
|
|
+ if colstr:
|
|
|
+ colstr += ','
|
|
|
+ colstr = ' ' + colstr
|
|
|
+ colstr += ' ' + value + '= '
|
|
|
+ newsqlstr = sqlstr[:idx1] + colstr
|
|
|
+ if idx2 >= 0:
|
|
|
+ newsqlstr += sqlstr[idx2:]
|
|
|
+ curspos = idx1 + len(colstr)
|
|
|
+
|
|
|
+ elif self.mode.GetSelection() == 1: # -> where
|
|
|
+ newsqlstr = ''
|
|
|
+ if sqlstr.lower().find('where') < 0:
|
|
|
+ newsqlstr += ' WHERE'
|
|
|
+ newsqlstr += ' ' + value
|
|
|
+ curspos = self.text_sql.GetLastPosition() + len(newsqlstr)
|
|
|
+ newsqlstr = sqlstr + newsqlstr
|
|
|
+
|
|
|
+ if newsqlstr:
|
|
|
+ self.text_sql.SetValue(newsqlstr)
|
|
|
+
|
|
|
+ wx.CallAfter(self.text_sql.SetFocus)
|
|
|
+ self.text_sql.SetInsertionPoint(curspos)
|
|
|
+
|
|
|
+ def _initSqlFunctions(self):
|
|
|
+
|
|
|
+ self.sqlFuncs = {}
|
|
|
+ # TODO add functions for other drivers
|
|
|
+ self.sqlFuncs['sqlite'] = {
|
|
|
+ 'ABS' : ['ABS()'],
|
|
|
+ 'LENGTH' : ['LENGTH()'],
|
|
|
+ 'LOWER' : ['LOWER()'],
|
|
|
+ 'LTRIM' : ['LTRIM(,)'],
|
|
|
+ 'MAX' : ['MAX()'],
|
|
|
+ 'MIN' : ['MIN()'],
|
|
|
+ 'RTRIM' : ['RTRIM(,)'],
|
|
|
+ 'SUBSTR' : ['SUBSTR (,[,])'],
|
|
|
+ 'TRIM' : ['TRIM (,)']
|
|
|
+ }
|
|
|
+
|
|
|
if __name__ == "__main__":
|
|
|
if len(sys.argv) != 2:
|
|
|
print >>sys.stderr, __doc__
|