Convert into hash too long data name

Some back-ends have constraint on the length of data name, they usually
silently truncate them, but this can create collision. The solution is to hash
the name when it is too long.

issue4620
issue6494
review37381003
This commit is contained in:
Cédric Krier committed 2017-05-12 12:00:44 +02:00
1 parent b1fbc675f1
commit 4520afedd3
5 files changed
+44 -32

No files matched your search

+10 -8
View File
@@ -11,6 +11,7 @@ VARCHAR_SIZE_RE = re.compile('VARCHAR\(([0-9]+)\)')
class TableHandler(TableHandlerInterface):
namedatalen = 64
def __init__(self, model, module_name=None, history=False):
super(TableHandler, self).__init__(model,
@@ -247,7 +248,8 @@ class TableHandler(TableHandlerInterface):
def add_fk(self, column_name, reference, on_delete=None):
if on_delete is None:
on_delete = 'SET NULL'
conname = '%s_%s_fkey' % (self.table_name, column_name)
conname = self.convert_name(
'%s_%s_fkey' % (self.table_name, column_name))
if conname in self._fkeys:
self.drop_fk(column_name)
cursor = Transaction().connection.cursor()
@@ -259,7 +261,8 @@ class TableHandler(TableHandlerInterface):
self._update_definitions(constraints=True)
def drop_fk(self, column_name, table=None):
conname = '%s_%s_fkey' % (self.table_name, column_name)
conname = self.convert_name(
'%s_%s_fkey' % (self.table_name, column_name))
if conname not in self._fkeys:
return
cursor = Transaction().connection.cursor()
@@ -270,10 +273,9 @@ class TableHandler(TableHandlerInterface):
def index_action(self, column_name, action='add', table=None):
if isinstance(column_name, basestring):
column_name = [column_name]
index_name = ((table or self.table_name) + "_" + '_'.join(column_name)
+ "_index")
# Index name length is limited to 64
index_name = index_name[:64]
index_name = self.convert_name(
((table or self.table_name) + "_" + '_'.join(column_name) +
"_index"))
for k in column_name:
if k in self._columns:
@@ -348,7 +350,7 @@ class TableHandler(TableHandlerInterface):
raise Exception('Not null action not supported!')
def add_constraint(self, ident, constraint, exception=False):
ident = self.table_name + "_" + ident
ident = self.convert_name(self.table_name + "_" + ident)
if ident in self._constraints:
# This constrain already exists
return
@@ -371,7 +373,7 @@ class TableHandler(TableHandlerInterface):
self._update_definitions(constraints=True)
def drop_constraint(self, ident, exception=False, table=None):
ident = (table or self.table_name) + "_" + ident
ident = self.convert_name((table or self.table_name) + "_" + ident)
if ident not in self._constraints:
return
+16 -21
View File
@@ -13,6 +13,7 @@ VARCHAR_SIZE_RE = re.compile('VARCHAR\(([0-9]+)\)')
class TableHandler(TableHandlerInterface):
namedatalen = 64
def __init__(self, model, module_name=None, history=False):
super(TableHandler, self).__init__(model,
@@ -314,17 +315,14 @@ class TableHandler(TableHandlerInterface):
on_delete = 'SET NULL'
cursor = Transaction().connection.cursor()
name = self.table_name + '_' + column_name + '_fkey'
cursor.execute('SELECT 1 '
'FROM information_schema.key_column_usage '
'WHERE table_name = %s AND table_schema = %s '
'AND constraint_name = %s',
(self.table_name, self.table_schema, name))
add = False
if not cursor.rowcount:
add = True
elif self._fk_deltypes.get(column_name) != on_delete:
self.drop_fk(column_name)
name = self.convert_name(self.table_name + '_' + column_name + '_fkey')
if name in self._constraints:
if self._fk_deltypes.get(column_name) != on_delete:
self.drop_fk(column_name)
add = True
else:
add = False
else:
add = True
if add:
cursor.execute('ALTER TABLE "' + self.table_name + '" '
@@ -340,16 +338,13 @@ class TableHandler(TableHandlerInterface):
def index_action(self, column_name, action='add', table=None):
if isinstance(column_name, basestring):
column_name = [column_name]
index_name = ((table or self.table_name) + "_" + '_'.join(column_name)
+ "_index")
if self._indexes:
test_index_name = index_name[:max(map(len, self._indexes))]
else:
test_index_name = index_name
index_name = self.convert_name(
((table or self.table_name) + "_" + '_'.join(column_name) +
"_index"))
with Transaction().connection.cursor() as cursor:
if action == 'add':
if test_index_name in self._indexes:
if index_name in self._indexes:
return
cursor.execute('CREATE INDEX "' + index_name + '" '
'ON "' + self.table_name + '" ( '
@@ -361,7 +356,7 @@ class TableHandler(TableHandlerInterface):
self.module_name) != self.module_name):
return
if test_index_name in self._indexes:
if index_name in self._indexes:
cursor.execute('DROP INDEX "%s" ' % (index_name,))
self._update_definitions(indexes=True)
else:
@@ -407,7 +402,7 @@ class TableHandler(TableHandlerInterface):
raise Exception('Not null action not supported!')
def add_constraint(self, ident, constraint, exception=False):
ident = self.table_name + "_" + ident
ident = self.convert_name(self.table_name + "_" + ident)
if ident in self._constraints:
# This constrain already exist
return
@@ -429,7 +424,7 @@ class TableHandler(TableHandlerInterface):
self._update_definitions(constraints=True)
def drop_constraint(self, ident, exception=False, table=None):
ident = (table or self.table_name) + "_" + ident
ident = self.convert_name((table or self.table_name) + "_" + ident)
if ident not in self._constraints:
return
cursor = Transaction().connection.cursor()
+2 -1
View File
@@ -248,7 +248,8 @@ class TableHandler(TableHandlerInterface):
def index_action(self, column_name, action='add', table=None):
if isinstance(column_name, basestring):
column_name = [column_name]
index_name = self.table_name + "_" + '_'.join(column_name) + "_index"
index_name = self.convert_name(
self.table_name + "_" + '_'.join(column_name) + "_index")
cursor = Transaction().connection.cursor()
if action == 'add':
+13
View File
@@ -1,11 +1,13 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import hashlib
class TableHandlerInterface(object):
'''
Define generic interface to handle database table
'''
namedatalen = None
def __init__(self, model, module_name=None, history=False):
'''
@@ -204,3 +206,14 @@ class TableHandlerInterface(object):
:param cascade: a boolean to add "CASCADE" to the delete query
'''
raise NotImplementedError
@classmethod
def convert_name(cls, name):
'''
Convert data name in respect of namedatalen.
:param name: the data name
'''
if cls.namedatalen and len(name) >= cls.namedatalen:
name = hashlib.sha256(name).hexdigest()[:cls.namedatalen - 1]
return name
+3 -2
View File
@@ -261,6 +261,7 @@ class ModelSQL(ModelStorage):
def __raise_integrity_error(
cls, exception, values, field_names=None, transaction=None):
pool = Pool()
TableHandler = backend.get('TableHandler')
if field_names is None:
field_names = cls._fields.keys()
if transaction is None:
@@ -293,10 +294,10 @@ class ModelSQL(ModelStorage):
cls.raise_user_error('foreign_model_missing',
error_args=error_args)
for name, _, error in cls._sql_constraints:
if name in str(exception):
if TableHandler.convert_name(name) in str(exception):
cls.raise_user_error(error)
for name, error in cls._sql_error_messages.iteritems():
if name in str(exception):
if TableHandler.convert_name(name) in str(exception):
cls.raise_user_error(error)
@classmethod