Ver código fonte

wxGUI/DbMgr: major refactoring done by Stepan Turek (GSoC 2012)
initial version of Field Calculator (SQL Update Builder)


git-svn-id: https://svn.osgeo.org/grass/grass/trunk@52641 15284696-431f-4ddb-bdfa-cd5b030d7da7

Martin Landa 12 anos atrás
pai
commit
b9543e9172
3 arquivos alterados com 3653 adições e 3116 exclusões
  1. 3159 0
      gui/wxpython/dbmgr/base.py
  2. 76 2961
      gui/wxpython/dbmgr/manager.py
  3. 418 155
      gui/wxpython/dbmgr/sqlbuilder.py

Diferenças do arquivo suprimidas por serem muito extensas
+ 3159 - 0
gui/wxpython/dbmgr/base.py


Diferenças do arquivo suprimidas por serem muito extensas
+ 76 - 2961
gui/wxpython/dbmgr/manager.py


+ 418 - 155
gui/wxpython/dbmgr/sqlbuilder.py

@@ -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__