OperatorBase.py 5.24 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
##############################################################################
#
# Copyright (c) 2002-2006 Nexedi SARL and Contributors. All Rights Reserved.
# Copyright (c) 2007-2009 Nexedi SA and Contributors. All Rights Reserved.
#                    Jean-Paul Smets-Solanes <jp@nexedi.com>
#                    Vincent Pelletier <vincent@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################

from zLOG import LOG
from Products.ZSQLCatalog.Interface.IOperator import IOperator
from Interface.Verify import verifyClass
from Products.ZSQLCatalog.SQLCatalog import profiler_decorator

@profiler_decorator
def escapeString(value):
  # Inspired from ERP5Type/Utils:sqlquote, but this product must not depend on it.
  return "'" + value.replace('\\', '\\\\').replace("'", "''") + "'"

@profiler_decorator
def valueFloatRenderer(value):
  if isinstance(value, basestring):
    value = float(value.replace(' ', ''))
  return repr(value)

@profiler_decorator
def valueDateTimeRenderer(value):
  return '"%s"' % (value.toZone('UTC').ISO(), )

@profiler_decorator
def valueDefaultRenderer(value):
  LOG('OperatorBase', 0, 'Unhandled value class: %s (%r). Converted to string and escaped.' % (value.__class__.__name__, value))
  return escapeString(str(value))

@profiler_decorator
def valueNoneRenderer(value):
  return 'NULL'

value_renderer = {
  'int': str,
  'long': str,
  'float': valueFloatRenderer,
  'DateTime': valueDateTimeRenderer,
  'NoneType': valueNoneRenderer,
}

value_search_text_renderer = {
  'DateTime': str,
}

@profiler_decorator
def valueDefaultSearchTextRenderer(value):
  """
    This is just repr, but always surrounding text strings with doublequotes.
  """
  if isinstance(value, basestring):
    result = '"%s"' % (value.replace('\\', '\\\\').replace('"', '\\"'), )
  else:
    result = repr(value)
  return result

@profiler_decorator
def columnFloatRenderer(column, format=None):
  if format is not None:
    if '.' in format:
      format = format.replace(' ', '')
      column = "TRUNCATE(%s, %s)" % (column, len(format.split('.')[-1]))
  return column

@profiler_decorator
def columnDefaultRenderer(column, format=None):
  return column

column_renderer = {
  'float': columnFloatRenderer
}

class OperatorBase(object):

  __implements__ = IOperator

103
  def __init__(self, operator, operator_search_text=None):
104
    self.operator = operator
105 106 107
    if operator_search_text is None:
      operator_search_text = operator
    self.operator_search_text = operator_search_text
108 109 110 111

  def getOperator(self):
    return self.operator

112 113 114
  def getOperatorSearchText(self):
    return self.operator_search_text

115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
  @profiler_decorator
  def _render(self, column, value):
    """
      Render given column and value for use in SQL.
      Value is rendered to convert it to SQL-friendly value.
      Column is rendered to include possible cast code.

      column (string)
        Column on which the value will be matched
      value (see _renderValue)
        Value to render.
    """
    if isinstance(value, dict):
      type = value['type']
      column = column_renderer.get(type, columnDefaultRenderer)(column, format=value['format'])
      value = value_renderer.get(type, valueDefaultRenderer)(value['query'])
    else:
      value = self._renderValue(value)
    return column, value

  @profiler_decorator
  def _renderValue(self, value):
    """
      Render given value as string.

      value (int, float, long, DateTime, string, None)
        Value to render as a string for use in SQL (quoted, escaped).
    """
    if isinstance(value, basestring):
      value = escapeString(value)
    else:
      value = value_renderer.get(value.__class__.__name__, valueDefaultRenderer)(value)
    return value

  @profiler_decorator
  def asSearchText(self, value):
    return value_search_text_renderer.get(value.__class__.__name__, valueDefaultSearchTextRenderer)(value)

  def asSQLExpression(self, column, value_list, only_group_columns):
    raise NotImplementedError, 'This method must be overloaded by a subclass ' \
      'to be able to get an SQL representation of this operator.'

  def __repr__(self):
    return '<%s(%r) at %s>' % (self.__class__.__name__, self.getOperator(), id(self))

verifyClass(IOperator, OperatorBase)