diff --git a/trytond/backend/sqlite/database.py b/trytond/backend/sqlite/database.py index 60746c31c6..0f2123f5a2 100644 --- a/trytond/backend/sqlite/database.py +++ b/trytond/backend/sqlite/database.py @@ -23,6 +23,7 @@ except ImportError: from sqlite3 import OperationalError as DatabaseOperationalError QUOTE_SEPARATION = re.compile(r"(.*?)('.*?')", re.DOTALL) EXTRACT_PATTERN = re.compile(r'EXTRACT\s*\(\s*(\S*)\s+FROM', re.I) +POSITION_PATTERN = re.compile(r'POSITION\s*\(([^\)]*)\s+IN', re.I) def extract(lookup_type, date): @@ -87,6 +88,15 @@ def split_part(text, delimiter, count): return (text.split(delimiter) + [''] * (count - 1))[count - 1] +def position(substring, string): + if string is None: + return + try: + return string.index(substring) + 1 + except ValueError: + return 0 + + def replace(text, pattern, replacement): return str(text).replace(pattern, replacement) @@ -122,6 +132,7 @@ class Database(DatabaseInterface): self._conn.create_function('extract', 2, extract) self._conn.create_function('date_trunc', 2, date_trunc) self._conn.create_function('split_part', 3, split_part) + self._conn.create_function('position', 2, position) if sqlite.sqlite_version_info < (3, 3, 14): self._conn.create_function('replace', 3, replace) return self @@ -278,6 +289,7 @@ class Cursor(CursorInterface): def execute(self, sql, params=None): buf = "" + sql = re.sub(POSITION_PATTERN, r'POSITION(\1,', sql) for nquote, quote in QUOTE_SEPARATION.findall(sql + "''"): nquote = nquote.replace('?', '??') nquote = nquote.replace('%s', '?') diff --git a/trytond/ir/translation.py b/trytond/ir/translation.py index 91bf4f206f..3f87940f3f 100644 --- a/trytond/ir/translation.py +++ b/trytond/ir/translation.py @@ -144,7 +144,7 @@ class Translation(ModelSQL, ModelView): def search_model(cls, name, clause): cursor = Transaction().cursor cursor.execute('SELECT id FROM "%s" ' - 'WHERE split_part(name, \',\', 1) %s %%s' % + 'WHERE SUBSTR(name, 1, POSITION(\',\' IN name) - 1) %s %%s' % (cls._table, clause[1]), (clause[2],)) return [('id', 'in', [x[0] for x in cursor.fetchall()])] diff --git a/trytond/model/fields/property.py b/trytond/model/fields/property.py index 74e30a88c6..182c3c6efc 100644 --- a/trytond/model/fields/property.py +++ b/trytond/model/fields/property.py @@ -90,7 +90,8 @@ class Property(Function): #Fetch res ids that comply with the domain cursor.execute( 'SELECT CAST(' - 'SPLIT_PART("' + Property._table + '".res,\',\',2) ' + 'SUBSTR("' + Property._table + '".res, ' + 'POSITION(\',\' IN "' + Property._table + '".res) + 1) ' 'AS INTEGER), ' '"' + Property._table + '".id ' 'FROM "' + Property._table + '" ' @@ -135,7 +136,7 @@ class Property(Function): #Fetch the res ids that doesn't use the default value cursor.execute( - "SELECT cast(split_part(res,',',2) as integer) " + "SELECT CAST(SUBSTR(res, POSITION(',' IN res) + 1) AS integer) " 'FROM "' + Property._table + '"' 'WHERE ' + property_query + ' AND res is not null', property_val) @@ -160,23 +161,22 @@ class Property(Function): if sql_type == 'NUMERIC': operator = 'CAST(%s AS NUMERIC)' + value = "SUBSTR(value, POSITION(',' IN value) + 1)" # All negative clauses will be negated later if clause[1] in ('in', 'not in'): operator = operator % '%%s' - return ("(CAST(SPLIT_PART(value,',',2) AS %s) IN (" + return ("(CAST(" + value + " AS %s) IN (" + ",".join((operator,) * len(clause[2])) + ")) ") % sql_type elif ((clause[2] is False or clause[2] is None) and clause[1] in ['=', '!=']): - return "((cast(split_part(value,',',2) as %s) IS NULL " \ - ") = %%s) " % sql_type + return "((CAST(" + value + " AS %s) IS NULL) = %%s) " % sql_type elif clause[1] in ['not like', 'not ilike']: - return "(cast(split_part(value,',',2) as %s) %s %s) " % \ + return "(CAST(" + value + " AS %s) %s %s) " % \ (sql_type, clause[1].split()[1], operator) elif clause[1] == '!=': - return "(cast(split_part(value,',',2) as %s) = %s) " % \ - (sql_type, operator) + return "(CAST(" + value + " AS %s) = %s) " % (sql_type, operator) else: - return "(cast(split_part(value,',',2) as %s) %s %s) " % \ + return "(CAST(" + value + " AS %s) %s %s) " % \ (sql_type, clause[1], operator) @staticmethod diff --git a/trytond/model/modelsql.py b/trytond/model/modelsql.py index b0f75abf22..d438b82273 100644 --- a/trytond/model/modelsql.py +++ b/trytond/model/modelsql.py @@ -1269,8 +1269,9 @@ class ModelSQL(ModelStorage): sql_type = FIELDS[ cls.id._type].sql_type(cls.id)[0] query = ('SELECT id FROM "' + cls._table + '" ' - 'WHERE CAST(SPLIT_PART(' - '"' + fargs[0] + '", \',\', 2) ' + 'WHERE CAST(SUBSTR("' + fargs[0] + '", ' + 'POSITION(\',\' IN "' + fargs[0] + '")' + + '+ 1) ' 'AS ' + sql_type + ') ' 'IN (' + in_query[0] + ') ' 'AND "' + fargs[0] + '" ilike %s') @@ -1295,8 +1296,9 @@ class ModelSQL(ModelStorage): cls.id._type].sql_type(cls.id)[0] where = ' AND "' + origin + '" LIKE %s' where_args = [cls.__name__ + ',%'] - origin = ('CAST(SPLIT_PART("%s", \',\', 2) AS %s)' - % (origin, sql_type)) + origin = ('CAST(SUBSTR("%s", ' + 'POSITION(\',\' IN "%s") + 1) AS %s)' + % (origin, origin, sql_type)) else: origin = '"%s"' % origin where = '' @@ -1336,8 +1338,9 @@ class ModelSQL(ModelStorage): rev_field = Field._fields[field.field] if rev_field._type == 'reference': sql_type = FIELDS[cls.id._type].sql_type(cls.id)[0] - select = ('CAST(SPLIT_PART("%s", \',\', 2) AS %s)' - % (field.field, sql_type)) + select = ('CAST(SUBSTR("%s", ' + 'POSITION(\',\' IN "%s") + 1) AS %s)' + % (field.field, field.field, sql_type)) where = ' AND "' + field.field + '" LIKE %s' where_args = [cls.__name__ + ',%'] else: @@ -1431,8 +1434,9 @@ class ModelSQL(ModelStorage): origin_field = Relation._fields[field.origin] if origin_field._type == 'reference': sql_type = FIELDS[cls.id._type].sql_type(cls.id)[0] - select = ('CAST(SPLIT_PART("%s", \',\', 2) AS %s)' - % (field.origin, sql_type)) + select = ('CAST(SUBSTR("%s", ' + 'POSITION(\',\' IN "%s") + 1) AS %s)' + % (field.origin, field.origin, sql_type)) where = ' AND "' + field.origin + '" LIKE %s' where_args = [cls.__name__ + ',%'] else: