Use IDENTITY column for table id columns

This commit is contained in:
Nicolas Évrard
2023-03-13 17:59:29 +01:00
parent eef0c3d870
commit e18d604aa8
4 changed files with 77 additions and 84 deletions
+1
View File
@@ -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):
+13 -39
View File
@@ -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
);
+55 -42
View File
@@ -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