Use IDENTITY column for table id columns
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
* Use IDENTITY column for id columns in the postgresql backend
|
||||
* Use no breaking space to format number and date
|
||||
* Fallback to code to format currency without symbol
|
||||
* Add sort method to order like ModelStorage.search
|
||||
|
||||
@@ -416,17 +416,22 @@ class Database(DatabaseInterface):
|
||||
|
||||
def nextid(self, connection, table):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT NEXTVAL(%s)", (table + '_id_seq',))
|
||||
cursor.execute(
|
||||
"SELECT NEXTVAL(pg_get_serial_sequence(%s, %s))", (table, 'id'))
|
||||
return cursor.fetchone()[0]
|
||||
|
||||
def setnextid(self, connection, table, value):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT SETVAL(%s, %s)", (table + '_id_seq', value))
|
||||
cursor.execute(
|
||||
"SELECT SETVAL(pg_get_serial_sequence(%s, %s), %s)",
|
||||
(table, 'id', value))
|
||||
|
||||
def currid(self, connection, table):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT pg_get_serial_sequence(%s, %s)", (table, 'id'))
|
||||
sequence_name, = cursor.fetchone()
|
||||
cursor.execute(SQL("SELECT last_value FROM {}").format(
|
||||
Identifier(table + '_id_seq')))
|
||||
Identifier(*sequence_name.split('.'))))
|
||||
return cursor.fetchone()[0]
|
||||
|
||||
def lock(self, connection, table):
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
CREATE SEQUENCE ir_configuration_id_seq;
|
||||
|
||||
CREATE TABLE ir_configuration (
|
||||
id INTEGER DEFAULT NEXTVAL('ir_configuration_id_seq') NOT NULL,
|
||||
id INTEGER GENERATED BY DEFAULT AS IDENTITY,
|
||||
language VARCHAR,
|
||||
hostname VARCHAR,
|
||||
PRIMARY KEY(id)
|
||||
);
|
||||
|
||||
CREATE SEQUENCE ir_model_id_seq;
|
||||
|
||||
CREATE TABLE ir_model (
|
||||
id INTEGER DEFAULT NEXTVAL('ir_model_id_seq') NOT NULL,
|
||||
id INTEGER GENERATED BY DEFAULT AS IDENTITY,
|
||||
model VARCHAR NOT NULL,
|
||||
name VARCHAR,
|
||||
info TEXT,
|
||||
@@ -18,10 +14,8 @@ CREATE TABLE ir_model (
|
||||
PRIMARY KEY(id)
|
||||
);
|
||||
|
||||
CREATE SEQUENCE ir_model_field_id_seq;
|
||||
|
||||
CREATE TABLE ir_model_field (
|
||||
id INTEGER DEFAULT NEXTVAL('ir_model_field_id_seq') NOT NULL,
|
||||
id INTEGER GENERATED BY DEFAULT AS IDENTITY,
|
||||
model INTEGER,
|
||||
name VARCHAR NOT NULL,
|
||||
relation VARCHAR,
|
||||
@@ -34,10 +28,8 @@ CREATE TABLE ir_model_field (
|
||||
FOREIGN KEY (model) REFERENCES ir_model(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE SEQUENCE ir_ui_view_id_seq;
|
||||
|
||||
CREATE TABLE ir_ui_view (
|
||||
id INTEGER DEFAULT NEXTVAL('ir_ui_view_id_seq') NOT NULL,
|
||||
id INTEGER GENERATED BY DEFAULT AS IDENTITY,
|
||||
model VARCHAR NOT NULL,
|
||||
"type" VARCHAR,
|
||||
data TEXT NOT NULL,
|
||||
@@ -46,10 +38,8 @@ CREATE TABLE ir_ui_view (
|
||||
PRIMARY KEY(id)
|
||||
);
|
||||
|
||||
CREATE SEQUENCE ir_ui_menu_id_seq;
|
||||
|
||||
CREATE TABLE ir_ui_menu (
|
||||
id INTEGER DEFAULT NEXTVAL('ir_ui_menu_id_seq') NOT NULL,
|
||||
id INTEGER GENERATED BY DEFAULT AS IDENTITY,
|
||||
parent INTEGER,
|
||||
name VARCHAR NOT NULL,
|
||||
icon VARCHAR,
|
||||
@@ -57,10 +47,8 @@ CREATE TABLE ir_ui_menu (
|
||||
FOREIGN KEY (parent) REFERENCES ir_ui_menu (id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE ir_translation_id_seq;
|
||||
|
||||
CREATE TABLE ir_translation (
|
||||
id INTEGER DEFAULT NEXTVAL('ir_translation_id_seq') NOT NULL,
|
||||
id INTEGER GENERATED BY DEFAULT AS IDENTITY,
|
||||
lang VARCHAR,
|
||||
src TEXT,
|
||||
name VARCHAR NOT NULL,
|
||||
@@ -72,10 +60,8 @@ CREATE TABLE ir_translation (
|
||||
PRIMARY KEY(id)
|
||||
);
|
||||
|
||||
CREATE SEQUENCE ir_lang_id_seq;
|
||||
|
||||
CREATE TABLE ir_lang (
|
||||
id INTEGER DEFAULT NEXTVAL('ir_lang_id_seq') NOT NULL,
|
||||
id INTEGER GENERATED BY DEFAULT AS IDENTITY,
|
||||
name VARCHAR NOT NULL,
|
||||
code VARCHAR NOT NULL,
|
||||
translatable BOOLEAN NOT NULL,
|
||||
@@ -85,10 +71,8 @@ CREATE TABLE ir_lang (
|
||||
PRIMARY KEY(id)
|
||||
);
|
||||
|
||||
CREATE SEQUENCE res_user_id_seq;
|
||||
|
||||
CREATE TABLE res_user (
|
||||
id INTEGER DEFAULT NEXTVAL('res_user_id_seq') NOT NULL,
|
||||
id INTEGER GENERATED BY DEFAULT AS IDENTITY,
|
||||
name VARCHAR NOT NULL,
|
||||
active BOOLEAN NOT NULL,
|
||||
login VARCHAR NOT NULL,
|
||||
@@ -100,18 +84,14 @@ ALTER TABLE res_user ADD CONSTRAINT res_user_login_key UNIQUE (login);
|
||||
|
||||
INSERT INTO res_user (id, login, password, name, active) VALUES (0, 'root', NULL, 'Root', False);
|
||||
|
||||
CREATE SEQUENCE res_group_id_seq;
|
||||
|
||||
CREATE TABLE res_group (
|
||||
id INTEGER DEFAULT NEXTVAL('res_group_id_seq') NOT NULL,
|
||||
id INTEGER GENERATED BY DEFAULT AS IDENTITY,
|
||||
name VARCHAR NOT NULL,
|
||||
PRIMARY KEY(id)
|
||||
);
|
||||
|
||||
CREATE SEQUENCE "res_user-res_group_id_seq";
|
||||
|
||||
CREATE TABLE "res_user-res_group" (
|
||||
id INTEGER DEFAULT NEXTVAL('res_user-res_group_id_seq') NOT NULL,
|
||||
id INTEGER GENERATED BY DEFAULT AS IDENTITY,
|
||||
"user" INTEGER NOT NULL,
|
||||
"group" INTEGER NOT NULL,
|
||||
FOREIGN KEY ("user") REFERENCES res_user (id) ON DELETE CASCADE,
|
||||
@@ -119,10 +99,8 @@ CREATE TABLE "res_user-res_group" (
|
||||
PRIMARY KEY(id)
|
||||
);
|
||||
|
||||
CREATE SEQUENCE ir_module_id_seq;
|
||||
|
||||
CREATE TABLE ir_module (
|
||||
id INTEGER DEFAULT NEXTVAL('ir_module_id_seq') NOT NULL,
|
||||
id INTEGER GENERATED BY DEFAULT AS IDENTITY,
|
||||
create_uid INTEGER NOT NULL,
|
||||
create_date TIMESTAMP WITHOUT TIME ZONE NOT NULL,
|
||||
write_date TIMESTAMP WITHOUT TIME ZONE,
|
||||
@@ -136,10 +114,8 @@ CREATE TABLE ir_module (
|
||||
|
||||
ALTER TABLE ir_module ADD CONSTRAINT ir_module_name_uniq UNIQUE (name);
|
||||
|
||||
CREATE SEQUENCE ir_module_dependency_id_seq;
|
||||
|
||||
CREATE TABLE ir_module_dependency (
|
||||
id INTEGER DEFAULT NEXTVAL('ir_module_dependency_id_seq') NOT NULL,
|
||||
id INTEGER GENERATED BY DEFAULT AS IDENTITY,
|
||||
create_uid INTEGER NOT NULL,
|
||||
create_date TIMESTAMP WITHOUT TIME ZONE NOT NULL,
|
||||
write_date TIMESTAMP WITHOUT TIME ZONE,
|
||||
@@ -152,10 +128,8 @@ CREATE TABLE ir_module_dependency (
|
||||
FOREIGN KEY (module) REFERENCES ir_module ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE SEQUENCE ir_cache_id_seq;
|
||||
|
||||
CREATE TABLE ir_cache (
|
||||
id INTEGER DEFAULT NEXTVAL('ir_cache_id_seq') NOT NULL,
|
||||
id INTEGER GENERATED BY DEFAULT AS IDENTITY,
|
||||
name VARCHAR NOT NULL,
|
||||
"timestamp" TIMESTAMP WITHOUT TIME ZONE
|
||||
);
|
||||
|
||||
@@ -28,11 +28,6 @@ class TableHandler(TableHandlerInterface):
|
||||
|
||||
transaction = Transaction()
|
||||
cursor = transaction.connection.cursor()
|
||||
# Create sequence if necessary
|
||||
if not transaction.database.sequence_exist(
|
||||
transaction.connection, self.sequence_name):
|
||||
transaction.database.sequence_create(
|
||||
transaction.connection, self.sequence_name)
|
||||
|
||||
# Create new table if necessary
|
||||
if not self.table_exist(self.table_name):
|
||||
@@ -51,50 +46,72 @@ class TableHandler(TableHandlerInterface):
|
||||
Identifier(self.table_name)),
|
||||
(model.__doc__,))
|
||||
|
||||
def migrate_to_identity(table, column):
|
||||
previous_seq_name = f"{table}_{column}_seq"
|
||||
cursor.execute(
|
||||
"SELECT NEXTVAL(%s)", (previous_seq_name,))
|
||||
next_val, = cursor.fetchone()
|
||||
cursor.execute(
|
||||
"SELECT seqincrement, seqmax, seqmin, seqcache "
|
||||
"FROM pg_sequence WHERE seqrelid = %s::regclass",
|
||||
(previous_seq_name,))
|
||||
increment, s_max, s_min, cache = cursor.fetchone()
|
||||
# Previously created sequences were setting bigint values for those
|
||||
# identity column mimic the type of the underlying column
|
||||
if (s_max > 2 ** 31 - 1
|
||||
and self._columns[column]['typname'] != 'int8'):
|
||||
s_max = 2 ** 31 - 1
|
||||
if (s_min < -(2 ** 31)
|
||||
and self._columns[column]['typname'] != 'int8'):
|
||||
s_min = -(2 ** 31)
|
||||
cursor.execute(
|
||||
SQL("ALTER TABLE {} ALTER COLUMN {} DROP DEFAULT").format(
|
||||
Identifier(table), Identifier(column)))
|
||||
cursor.execute(
|
||||
SQL("DROP SEQUENCE {}").format(
|
||||
Identifier(previous_seq_name)))
|
||||
cursor.execute(
|
||||
SQL("ALTER TABLE {} ALTER COLUMN {} "
|
||||
"ADD GENERATED BY DEFAULT AS IDENTITY").format(
|
||||
Identifier(table), Identifier(column)))
|
||||
cursor.execute(
|
||||
"SELECT pg_get_serial_sequence(%s, %s)", (table, column))
|
||||
serial_seq_name, = cursor.fetchone()
|
||||
cursor.execute(
|
||||
(f"ALTER SEQUENCE {serial_seq_name} INCREMENT BY %s "
|
||||
"MINVALUE %s MAXVALUE %s RESTART WITH %s CACHE %s"),
|
||||
(increment, s_min, s_max, next_val, cache))
|
||||
|
||||
update_definitions = False
|
||||
if 'id' not in self._columns:
|
||||
update_definitions = True
|
||||
if not self.history:
|
||||
cursor.execute(
|
||||
SQL(
|
||||
"ALTER TABLE {} ADD COLUMN id INTEGER "
|
||||
"DEFAULT nextval(%s) NOT NULL").format(
|
||||
Identifier(self.table_name)),
|
||||
(self.sequence_name,))
|
||||
cursor.execute(
|
||||
SQL('ALTER TABLE {} ADD PRIMARY KEY(id)')
|
||||
.format(Identifier(self.table_name)))
|
||||
"GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY").format(
|
||||
Identifier(self.table_name)))
|
||||
else:
|
||||
cursor.execute(
|
||||
SQL('ALTER TABLE {} ADD COLUMN id INTEGER')
|
||||
.format(Identifier(self.table_name)))
|
||||
self._update_definitions(columns=True)
|
||||
else:
|
||||
if not self.history and not self.__columns['id']['identity']:
|
||||
update_definitions = True
|
||||
migrate_to_identity(self.table_name, 'id')
|
||||
if self.history and '__id' not in self._columns:
|
||||
update_definitions = True
|
||||
cursor.execute(
|
||||
SQL(
|
||||
"ALTER TABLE {} ADD COLUMN __id INTEGER "
|
||||
"DEFAULT nextval(%s) NOT NULL").format(
|
||||
Identifier(self.table_name)),
|
||||
(self.sequence_name,))
|
||||
cursor.execute(
|
||||
SQL('ALTER TABLE {} ADD PRIMARY KEY(__id)')
|
||||
.format(Identifier(self.table_name)))
|
||||
"GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY").format(
|
||||
Identifier(self.table_name)))
|
||||
elif self.history:
|
||||
if not self.__columns['__id']['identity']:
|
||||
update_definitions = True
|
||||
migrate_to_identity(self.table_name, '__id')
|
||||
if update_definitions:
|
||||
self._update_definitions(columns=True)
|
||||
else:
|
||||
default = "nextval('%s'::regclass)" % self.sequence_name
|
||||
if self.history:
|
||||
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(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(columns=True)
|
||||
|
||||
@classmethod
|
||||
def table_exist(cls, table_name):
|
||||
@@ -111,11 +128,6 @@ class TableHandler(TableHandlerInterface):
|
||||
and not cls.table_exist(new_name)):
|
||||
cursor.execute(SQL('ALTER TABLE {} RENAME TO {}').format(
|
||||
Identifier(old_name), Identifier(new_name)))
|
||||
# Rename sequence
|
||||
old_sequence = old_name + '_id_seq'
|
||||
new_sequence = new_name + '_id_seq'
|
||||
transaction.database.sequence_rename(
|
||||
transaction.connection, old_sequence, new_sequence)
|
||||
# Rename history table
|
||||
old_history = old_name + "__history"
|
||||
new_history = new_name + "__history"
|
||||
@@ -151,16 +163,17 @@ class TableHandler(TableHandlerInterface):
|
||||
cursor.execute('SELECT '
|
||||
'column_name, udt_name, is_nullable, '
|
||||
'character_maximum_length, '
|
||||
'column_default '
|
||||
'column_default, is_identity '
|
||||
'FROM information_schema.columns '
|
||||
'WHERE table_name = %s AND table_schema = %s',
|
||||
(self.table_name, self.table_schema))
|
||||
for column, typname, nullable, size, default in cursor:
|
||||
for column, typname, nullable, size, default, identity in cursor:
|
||||
self.__columns[column] = {
|
||||
'typname': typname,
|
||||
'notnull': True if nullable == 'NO' else False,
|
||||
'size': size,
|
||||
'default': default,
|
||||
'identity': False if identity == 'NO' else True,
|
||||
}
|
||||
return self.__columns
|
||||
|
||||
|
||||
Reference in New Issue
Block a user