diff --git a/CHANGELOG b/CHANGELOG index 17332ccbf1..439113b41f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Use SQL sequence on PostgreSQL for ir.sequence * Allow to specify more than one interface * Use unoconv for report format conversion * Allow to use any Opendocument as report template diff --git a/COPYRIGHT b/COPYRIGHT index 6c994d4a71..231a7a99bc 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -2,6 +2,7 @@ Copyright (C) 2004-2008 Tiny SPRL. Copyright (C) 2007-2011 Cédric Krier. Copyright (C) 2007-2011 Bertrand Chenal. Copyright (C) 2008-2011 B2CK SPRL. +Copyright (C) 2011 Openlabs Technologies & Consulting (P) Ltd. 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 diff --git a/trytond/ir/sequence.py b/trytond/ir/sequence.py index 9371bb102b..70313202eb 100644 --- a/trytond/ir/sequence.py +++ b/trytond/ir/sequence.py @@ -8,6 +8,10 @@ from trytond.tools import datetime_strftime from trytond.pyson import In, Eval, Not from trytond.transaction import Transaction from trytond.pool import Pool +from trytond.config import CONFIG +from trytond.backend import TableHandler + +sql_sequence = CONFIG.options['db_type'] == 'postgresql' class SequenceType(ModelSQL, ModelView): @@ -24,6 +28,7 @@ class Sequence(ModelSQL, ModelView): "Sequence" _name = 'ir.sequence' _description = __doc__ + _strict = False name = fields.Char('Sequence Name', required=True, translate=True) code = fields.Selection('code_get', 'Sequence Code', required=True, states={ @@ -37,10 +42,12 @@ class Sequence(ModelSQL, ModelView): ('decimal timestamp', 'Decimal Timestamp'), ('hexadecimal timestamp', 'Hexadecimal Timestamp'), ], 'Type') - number_next = fields.Integer('Next Number', + number_next_internal = fields.Integer('Next Number', states={ 'invisible': Not(In(Eval('type'), ['incremental'])), }, depends=['type']) + number_next = fields.Function(number_next_internal, 'get_number_next', + 'set_number_next') number_increment = fields.Integer('Increment Number', states={ 'invisible': Not(In(Eval('type'), ['incremental'])), @@ -77,6 +84,26 @@ class Sequence(ModelSQL, ModelView): 'future_last_timestamp': 'Last Timestamp could not be in future!', }) + def init(self, module_name): + cursor = Transaction().cursor + table = TableHandler(cursor, self, module_name) + + # Migration from 2.0 rename number_next into number_next_internal + table.column_rename('number_next', 'number_next_internal') + + super(Sequence, self).init(module_name) + + # Migration from 2.0 create sql_sequence + if sql_sequence and not self._strict: + sequence_ids = self.search([]) + for sequence in self.browse(sequence_ids): + if sequence.type != 'incremental': + continue + if not TableHandler.sequence_exist(cursor, + self._sql_sequence_name(sequence)): + self.create_sql_sequence(sequence, + sequence.number_next_internal) + def default_active(self): return True @@ -104,6 +131,53 @@ class Sequence(ModelSQL, ModelView): def default_code(self): return Transaction().context.get('code', False) + def get_number_next(self, ids, name): + cursor = Transaction().cursor + result = {} + for sequence in self.browse(ids): + sql_name = self._sql_sequence_name(sequence) + if sql_sequence and not self._strict: + cursor.execute('SELECT ' + 'CASE WHEN NOT is_called THEN last_value ' + 'ELSE last_value + increment_by ' + 'END FROM "%s"' % sql_name) + value, = cursor.fetchone() + else: + value = sequence.number_next_internal + result[sequence.id] = value + return result + + def set_number_next(self, ids, name, value): + cursor = Transaction().cursor + super(Sequence, self).write(ids, { + 'number_next_internal': value, + }) + + def create(self, values): + sequence_id = super(Sequence, self).create(values) + if sql_sequence and not self._strict: + sequence = self.browse(sequence_id) + self.update_sql_sequence(sequence, values.get('number_next', + self.default_number_next())) + return sequence_id + + def write(self, ids, values): + result = super(Sequence, self).write(ids, values) + if sql_sequence and not self._strict: + ids = [ids] if isinstance(ids, (int, long)) else ids + sequences = self.browse(ids) + for sequence in sequences: + self.update_sql_sequence(sequence, values.get('number_next')) + return result + + def delete(self, ids): + if sql_sequence and not self._strict: + ids = [ids] if isinstance(ids, (int, long)) else ids + sequences = self.browse(ids) + for sequence in sequences: + self.delete_sql_sequence(sequence) + return super(Sequence, self).delete(ids) + def code_get(self): pool = Pool() sequence_type_obj = pool.get('ir.sequence.type') @@ -131,6 +205,47 @@ class Sequence(ModelSQL, ModelView): return False return True + def _sql_sequence_name(self, sequence): + 'Return SQL sequence name' + return '%s_%s' % (self._table, sequence.id) + + def create_sql_sequence(self, sequence, number_next=None): + 'Create the SQL sequence' + cursor = Transaction().cursor + if sequence.type != 'incremental': + return + if number_next is None: + number_next = sequence.number_next + cursor.execute('CREATE SEQUENCE "' + self._sql_sequence_name(sequence) + + '" INCREMENT BY %s START WITH %s', (sequence.number_increment, + number_next)) + + def update_sql_sequence(self, sequence, number_next=None): + 'Update the SQL sequence' + cursor = Transaction().cursor + exist = TableHandler.sequence_exist(cursor, + self._sql_sequence_name(sequence)) + if sequence.type != 'incremental': + if exist: + self.delete_sql_sequence(sequence) + return + if not exist: + self.create_sql_sequence(sequence, number_next) + return + if number_next is None: + number_next = sequence.number_next + cursor.execute('ALTER SEQUENCE "' + self._sql_sequence_name(sequence) + + '" INCREMENT BY %s RESTART WITH %s', (sequence.number_increment, + number_next)) + + def delete_sql_sequence(self, sequence): + 'Delete the SQL sequence' + cursor = Transaction().cursor + if sequence.type != 'incremental': + return + cursor.execute('DROP SEQUENCE "%s"' + % self._sql_sequence_name(sequence)) + def _process(self, string, date=None): pool = Pool() date_obj = pool.get('ir.date') @@ -151,13 +266,20 @@ class Sequence(ModelSQL, ModelView): def _get_sequence(self, sequence): if sequence.type == 'incremental': - #Pre-fetch number_next - number_next = sequence.number_next or 0 + if sql_sequence and not self._strict: + cursor = Transaction().cursor + cursor.execute('SELECT nextval(\'"%s"\')' + % self._sql_sequence_name(sequence)) + number_next, = cursor.fetchone() + else: + #Pre-fetch number_next + number_next = sequence.number_next_internal - with Transaction().set_user(0): - self.write(sequence.id, { - 'number_next': number_next + sequence.number_increment, - }) + with Transaction().set_user(0): + self.write(sequence.id, { + 'number_next_internal': (number_next + + sequence.number_increment), + }) return '%%0%sd' % sequence.padding % number_next elif sequence.type in ('decimal timestamp', 'hexadecimal timestamp'): timestamp = sequence.last_timestamp @@ -209,6 +331,7 @@ class SequenceStrict(Sequence): "Sequence Strict" _name = 'ir.sequence.strict' _description = __doc__ + _strict = True def get_id(self, clause): Transaction().cursor.lock(self._table)