changeset 33faabc6ec78 in trytond:default
details: https://hg.tryton.org/trytond?cmd=changeset&node=33faabc6ec78
description:
Use singleton for TableHandler
issue11301
review366861002
diffstat:
CHANGELOG | 2 +
trytond/backend/postgresql/table.py | 86 +++++++++++++++++++-----------------
trytond/backend/sqlite/table.py | 59 +++++++++++-------------
trytond/backend/table.py | 16 ++++++-
trytond/model/modelsql.py | 2 +-
5 files changed, 90 insertions(+), 75 deletions(-)
diffs (357 lines):
diff -r 33c8f05556b3 -r 33faabc6ec78 CHANGELOG
--- a/CHANGELOG Sat May 07 10:58:45 2022 +0200
+++ b/CHANGELOG Sat May 07 11:35:14 2022 +0200
@@ -1,3 +1,5 @@
+* Use singleton for TableHandler
+
Version 6.4.0 - 2022-05-02
* Bug fixes (see mercurial logs for details)
* Use unittest discover
diff -r 33c8f05556b3 -r 33faabc6ec78 trytond/backend/postgresql/table.py
--- a/trytond/backend/postgresql/table.py Sat May 07 10:58:45 2022 +0200
+++ b/trytond/backend/postgresql/table.py Sat May 07 11:35:14 2022 +0200
@@ -17,13 +17,12 @@
class TableHandler(TableHandlerInterface):
namedatalen = 64
- def __init__(self, model, module_name=None, history=False):
- super(TableHandler, self).__init__(model,
- module_name=module_name, history=history)
- self._columns = {}
- self._constraints = []
- self._fk_deltypes = {}
- self._indexes = []
+ def _init(self, model, history=False):
+ super()._init(model, history=history)
+ self.__columns = None
+ self.__constraints = None
+ self.__fk_deltypes = None
+ self.__indexes = None
transaction = Transaction()
cursor = transaction.connection.cursor()
@@ -50,7 +49,6 @@
Identifier(self.table_name)),
(model.__doc__,))
- self._update_definitions(columns=True)
if 'id' not in self._columns:
if not self.history:
cursor.execute(
@@ -77,6 +75,7 @@
cursor.execute(
SQL('ALTER TABLE {} ADD PRIMARY KEY(__id)')
.format(Identifier(self.table_name)))
+ self._update_definitions(columns=True)
else:
default = "nextval('%s'::regclass)" % self.sequence_name
if self.history:
@@ -86,13 +85,14 @@
"ALTER __id SET DEFAULT nextval(%s::regclass)")
.format(Identifier(self.table_name)),
(self.sequence_name,))
+ self._update_definitions(columns=True)
if self._columns['id']['default'] != default:
cursor.execute(
SQL("ALTER TABLE {} "
"ALTER id SET DEFAULT nextval(%s::regclass)")
.format(Identifier(self.table_name)),
(self.sequence_name,))
- self._update_definitions()
+ self._update_definitions(columns=True)
@staticmethod
def table_exist(table_name):
@@ -140,13 +140,11 @@
'Unable to rename column %s on table %s to %s.',
old_name, self.table_name, new_name)
- def _update_definitions(self,
- columns=None, constraints=None, indexes=None):
- if columns is None and constraints is None and indexes is None:
- columns = constraints = indexes = True
- cursor = Transaction().connection.cursor()
- if columns:
- self._columns = {}
+ @property
+ def _columns(self):
+ if self.__columns is None:
+ cursor = Transaction().connection.cursor()
+ self.__columns = {}
# Fetch columns definitions from the table
cursor.execute('SELECT '
'column_name, udt_name, is_nullable, '
@@ -156,20 +154,24 @@
'WHERE table_name = %s AND table_schema = %s',
(self.table_name, self.table_schema))
for column, typname, nullable, size, default in cursor:
- self._columns[column] = {
+ self.__columns[column] = {
'typname': typname,
'notnull': True if nullable == 'NO' else False,
'size': size,
'default': default,
}
+ return self.__columns
- if constraints:
+ @property
+ def _constraints(self):
+ if self.__constraints is None:
+ cursor = Transaction().connection.cursor()
# fetch constraints for the table
cursor.execute('SELECT constraint_name '
'FROM information_schema.table_constraints '
'WHERE table_name = %s AND table_schema = %s',
(self.table_name, self.table_schema))
- self._constraints = [c for c, in cursor]
+ self.__constraints = [c for c, in cursor]
# add nonstandard exclude constraint
cursor.execute('SELECT c.conname '
@@ -183,8 +185,13 @@
"AND r.relkind IN ('r', 'p') "
'AND r.relname = %s AND nr.nspname = %s',
(self.table_name, self.table_schema))
- self._constraints.extend((c for c, in cursor))
+ self.__constraints.extend((c for c, in cursor))
+ return self.__constraints
+ @property
+ def _fk_deltypes(self):
+ if self.__fk_deltypes is None:
+ cursor = Transaction().connection.cursor()
cursor.execute('SELECT k.column_name, r.delete_rule '
'FROM information_schema.key_column_usage AS k '
'JOIN information_schema.referential_constraints AS r '
@@ -192,9 +199,13 @@
'AND r.constraint_name = k.constraint_name '
'WHERE k.table_name = %s AND k.table_schema = %s',
(self.table_name, self.table_schema))
- self._fk_deltypes = dict(cursor)
+ self.__fk_deltypes = dict(cursor)
+ return self.__fk_deltypes
- if indexes:
+ @property
+ def _indexes(self):
+ if self.__indexes is None:
+ cursor = Transaction().connection.cursor()
# Fetch indexes defined for the table
cursor.execute("SELECT cl2.relname "
"FROM pg_index ind "
@@ -203,17 +214,20 @@
"JOIN pg_class cl2 on (cl2.oid = ind.indexrelid) "
"WHERE cl.relname = %s AND n.nspname = %s",
(self.table_name, self.table_schema))
- self._indexes = [l[0] for l in cursor]
+ self.__indexes = [l[0] for l in cursor]
+ return self.__indexes
- @property
- def _field2module(self):
- cursor = Transaction().connection.cursor()
- cursor.execute('SELECT f.name, f.module '
- 'FROM ir_model_field f '
- 'JOIN ir_model m on (f.model=m.id) '
- 'WHERE m.model = %s',
- (self.object_name,))
- return dict(cursor)
+ def _update_definitions(self,
+ columns=None, constraints=None, indexes=None):
+ if columns is None and constraints is None and indexes is None:
+ columns = constraints = indexes = True
+ if columns:
+ self.__columns = None
+ if constraints:
+ self.__constraints = None
+ self.__fk_deltypes = None
+ if indexes:
+ self.__indexes = None
def alter_size(self, column_name, column_type):
cursor = Transaction().connection.cursor()
@@ -427,11 +441,6 @@
params)
self._update_definitions(indexes=True)
elif action == 'remove':
- if len(columns) == 1 and isinstance(columns[0], str):
- if self._field2module.get(columns[0],
- self.module_name) != self.module_name:
- return
-
if index_name in self._indexes:
cursor.execute(SQL('DROP INDEX {}').format(
Identifier(index_name)))
@@ -469,9 +478,6 @@
elif action == 'remove':
if not self._columns[column_name]['notnull']:
return
- if (self._field2module.get(column_name, self.module_name)
- != self.module_name):
- return
cursor.execute(
SQL('ALTER TABLE {} ALTER COLUMN {} DROP NOT NULL')
.format(
diff -r 33c8f05556b3 -r 33faabc6ec78 trytond/backend/sqlite/table.py
--- a/trytond/backend/sqlite/table.py Sat May 07 10:58:45 2022 +0200
+++ b/trytond/backend/sqlite/table.py Sat May 07 11:35:14 2022 +0200
@@ -4,6 +4,7 @@
import logging
import re
import warnings
+from weakref import WeakKeyDictionary
from trytond.backend.table import TableHandlerInterface
from trytond.transaction import Transaction
@@ -21,13 +22,12 @@
class TableHandler(TableHandlerInterface):
- def __init__(self, model, module_name=None, history=False):
- super(TableHandler, self).__init__(model,
- module_name=module_name, history=history)
- self._columns = {}
- self._constraints = []
- self._fk_deltypes = {}
- self._indexes = []
+ __handlers = WeakKeyDictionary()
+
+ def _init(self, model, history=False):
+ super()._init(model, history=history)
+ self.__columns = None
+ self.__indexes = None
self._model = model
cursor = Transaction().connection.cursor()
@@ -143,14 +143,12 @@
'Unable to rename column %s on table %s to %s.',
old_name, self.table_name, new_name)
- def _update_definitions(self, columns=None, indexes=None):
- if columns is None and indexes is None:
- columns = indexes = True
- cursor = Transaction().connection.cursor()
- # Fetch columns definitions from the table
- if columns:
+ @property
+ def _columns(self):
+ if self.__columns is None:
+ cursor = Transaction().connection.cursor()
cursor.execute('PRAGMA table_info("' + self.table_name + '")')
- self._columns = {}
+ self.__columns = {}
for _, column, type_, notnull, hasdef, _ in cursor:
column = re.sub(r'^\"|\"$', '', column)
match = re.match(r'(\w+)(\((.*?)\))?', type_)
@@ -160,30 +158,32 @@
else:
typname = type_.upper()
size = None
- self._columns[column] = {
+ self.__columns[column] = {
'notnull': notnull,
'hasdef': hasdef,
'size': size,
'typname': typname,
}
+ return self.__columns
- # Fetch indexes defined for the table
- if indexes:
+ @property
+ def _indexes(self):
+ if self.__indexes is None:
+ cursor = Transaction().connection.cursor()
try:
cursor.execute('PRAGMA index_list("' + self.table_name + '")')
except IndexError: # There is sometimes IndexError
cursor.execute('PRAGMA index_list("' + self.table_name + '")')
- self._indexes = [l[1] for l in cursor]
+ self.__indexes = [l[1] for l in cursor]
+ return self.__indexes
- @property
- def _field2module(self):
- cursor = Transaction().connection.cursor()
- cursor.execute('SELECT f.name, f.module '
- 'FROM ir_model_field f '
- 'JOIN ir_model m on (f.model=m.id) '
- 'WHERE m.model = ?',
- (self.object_name,))
- return dict(cursor)
+ def _update_definitions(self, columns=None, indexes=None):
+ if columns is None and indexes is None:
+ columns = indexes = True
+ if columns:
+ self.__columns = None
+ if indexes:
+ self.__indexes = None
def alter_size(self, column_name, column_type):
self._recreate_table({column_name: {'size': column_type}})
@@ -324,11 +324,6 @@
params)
self._update_definitions(indexes=True)
elif action == 'remove':
- if len(columns) == 1 and isinstance(columns[0], str):
- if self._field2module.get(columns[0],
- self.module_name) != self.module_name:
- return
-
if index_name in self._indexes:
cursor.execute('DROP INDEX %s'
% (_escape_identifier(index_name),))
diff -r 33c8f05556b3 -r 33faabc6ec78 trytond/backend/table.py
--- a/trytond/backend/table.py Sat May 07 10:58:45 2022 +0200
+++ b/trytond/backend/table.py Sat May 07 11:35:14 2022 +0200
@@ -1,6 +1,9 @@
# 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
+from weakref import WeakKeyDictionary
+
+from trytond.transaction import Transaction
class TableHandlerInterface(object):
@@ -8,8 +11,18 @@
Define generic interface to handle database table
'''
namedatalen = None
+ __handlers = WeakKeyDictionary()
- def __init__(self, model, module_name=None, history=False):
+ def __new__(cls, model, history=False):
+ transaction = Transaction()
+ handlers = cls.__handlers.setdefault(transaction, {})
+ key = (model.__name__, history)
+ if key not in handlers:
+ instance = handlers[key] = super().__new__(cls)
+ instance._init(model, history=history)
+ return handlers[key]
+
+ def _init(self, model, history=False):
'''
:param model: the Model linked to the table
:param module_name: the module name
@@ -25,7 +38,6 @@
self.sequence_name = self.table_name + '___id_seq'
else:
self.sequence_name = self.table_name + '_id_seq'
- self.module_name = module_name
self.history = history
@staticmethod
diff -r 33c8f05556b3 -r 33faabc6ec78 trytond/model/modelsql.py
--- a/trytond/model/modelsql.py Sat May 07 10:58:45 2022 +0200
+++ b/trytond/model/modelsql.py Sat May 07 11:35:14 2022 +0200
@@ -211,7 +211,7 @@
@classmethod
def __table_handler__(cls, module_name=None, history=False):
- return backend.TableHandler(cls, module_name, history=history)
+ return backend.TableHandler(cls, history=history)
@classmethod
def __register__(cls, module_name):