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:
1 parent
b1fbc675f1
commit
4520afedd3
5 files changed
+44
-32
No files matched your search
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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':
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user