relstorage/relstorage/adapters/mysql/schema.py

272 lines
9.5 KiB
Python

##############################################################################
#
# Copyright (c) 2009 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Database schema installers
"""
from __future__ import absolute_import
from ..interfaces import ISchemaInstaller
from ..schema import AbstractSchemaInstaller
from relstorage._compat import db_binary_to_bytes
from ZODB.POSException import StorageError
from zope.interface import implementer
@implementer(ISchemaInstaller)
class MySQLSchemaInstaller(AbstractSchemaInstaller):
database_type = 'mysql'
def _to_native_str(self, value):
# Almost all drivers return CHAR/VARCHAR as
# bytes or possibly str. mysql connector/python, though,
# will return them as unicode on Py2 if not properly configured.
# If properly configured, it will return them as bytearray.
# This doesn't seem configurable.
# sigh.
value = db_binary_to_bytes(value)
if not isinstance(value, str):
return value.decode('ascii')
return value
def get_database_name(self, cursor):
cursor.execute("SELECT DATABASE()")
for (name,) in cursor:
return self._to_native_str(name)
def list_tables(self, cursor):
cursor.execute("SHOW TABLES")
return [self._to_native_str(name)
for (name,) in cursor.fetchall()]
def list_sequences(self, cursor):
return []
def check_compatibility(self, cursor, tables):
super(MySQLSchemaInstaller, self).check_compatibility(cursor, tables)
stmt = "SHOW TABLE STATUS LIKE 'object_state'"
cursor.execute(stmt)
for row in cursor:
for col_index, col in enumerate(cursor.description):
if col[0].lower() == 'engine':
engine = row[col_index]
if not isinstance(engine, str):
engine = engine.decode('ascii')
if engine.lower() != 'innodb':
raise StorageError(
"The object_state table must use the InnoDB "
"engine, but it is using the %s engine." % engine)
def _create_commit_lock(self, cursor):
return
def _create_pack_lock(self, cursor):
return
def _create_transaction(self, cursor):
if self.keep_history:
stmt = """
CREATE TABLE transaction (
tid BIGINT NOT NULL PRIMARY KEY,
packed BOOLEAN NOT NULL DEFAULT FALSE,
empty BOOLEAN NOT NULL DEFAULT FALSE,
username BLOB NOT NULL,
description BLOB NOT NULL,
extension BLOB
) ENGINE = InnoDB;
"""
self.runner.run_script(cursor, stmt)
def _create_new_oid(self, cursor):
stmt = """
CREATE TABLE new_oid (
zoid BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT
) ENGINE = InnoDB;
"""
self.runner.run_script(cursor, stmt)
def _create_object_state(self, cursor):
if self.keep_history:
stmt = """
CREATE TABLE object_state (
zoid BIGINT NOT NULL,
tid BIGINT NOT NULL REFERENCES transaction,
PRIMARY KEY (zoid, tid),
CHECK (tid > 0),
prev_tid BIGINT NOT NULL REFERENCES transaction,
md5 CHAR(32) CHARACTER SET ascii,
state_size BIGINT NOT NULL,
state LONGBLOB
) ENGINE = InnoDB;
CREATE INDEX object_state_tid ON object_state (tid);
CREATE INDEX object_state_prev_tid ON object_state (prev_tid);
"""
else:
stmt = """
CREATE TABLE object_state (
zoid BIGINT NOT NULL PRIMARY KEY,
tid BIGINT NOT NULL,
CHECK (tid > 0),
state_size BIGINT NOT NULL,
state LONGBLOB
) ENGINE = InnoDB;
CREATE INDEX object_state_tid ON object_state (tid);
"""
self.runner.run_script(cursor, stmt)
def _create_blob_chunk(self, cursor):
if self.keep_history:
stmt = """
CREATE TABLE blob_chunk (
zoid BIGINT NOT NULL,
tid BIGINT NOT NULL,
chunk_num BIGINT NOT NULL,
PRIMARY KEY (zoid, tid, chunk_num),
chunk LONGBLOB NOT NULL
) ENGINE = InnoDB;
CREATE INDEX blob_chunk_lookup ON blob_chunk (zoid, tid);
ALTER TABLE blob_chunk ADD CONSTRAINT blob_chunk_fk
FOREIGN KEY (zoid, tid)
REFERENCES object_state (zoid, tid)
ON DELETE CASCADE;
"""
else:
stmt = """
CREATE TABLE blob_chunk (
zoid BIGINT NOT NULL,
chunk_num BIGINT NOT NULL,
PRIMARY KEY (zoid, chunk_num),
tid BIGINT NOT NULL,
chunk LONGBLOB NOT NULL
) ENGINE = InnoDB;
CREATE INDEX blob_chunk_lookup ON blob_chunk (zoid);
ALTER TABLE blob_chunk ADD CONSTRAINT blob_chunk_fk
FOREIGN KEY (zoid)
REFERENCES object_state (zoid)
ON DELETE CASCADE;
"""
self.runner.run_script(cursor, stmt)
def _create_current_object(self, cursor):
if self.keep_history:
stmt = """
CREATE TABLE current_object (
zoid BIGINT NOT NULL PRIMARY KEY,
tid BIGINT NOT NULL,
FOREIGN KEY (zoid, tid)
REFERENCES object_state (zoid, tid)
) ENGINE = InnoDB;
CREATE INDEX current_object_tid ON current_object (tid);
"""
self.runner.run_script(cursor, stmt)
def _create_object_ref(self, cursor):
if self.keep_history:
stmt = """
CREATE TABLE object_ref (
zoid BIGINT NOT NULL,
tid BIGINT NOT NULL,
to_zoid BIGINT NOT NULL,
PRIMARY KEY (tid, zoid, to_zoid)
) ENGINE = InnoDB;
"""
else:
stmt = """
CREATE TABLE object_ref (
zoid BIGINT NOT NULL,
to_zoid BIGINT NOT NULL,
tid BIGINT NOT NULL,
PRIMARY KEY (zoid, to_zoid)
) ENGINE = InnoDB;
"""
self.runner.run_script(cursor, stmt)
def _create_object_refs_added(self, cursor):
if self.keep_history:
stmt = """
CREATE TABLE object_refs_added (
tid BIGINT NOT NULL PRIMARY KEY
) ENGINE = InnoDB;
"""
else:
stmt = """
CREATE TABLE object_refs_added (
zoid BIGINT NOT NULL PRIMARY KEY,
tid BIGINT NOT NULL
) ENGINE = InnoDB;
"""
self.runner.run_script(cursor, stmt)
def _create_pack_object(self, cursor):
stmt = """
CREATE TABLE pack_object (
zoid BIGINT NOT NULL PRIMARY KEY,
keep BOOLEAN NOT NULL,
keep_tid BIGINT NOT NULL,
visited BOOLEAN NOT NULL DEFAULT FALSE
) ENGINE = InnoDB;
CREATE INDEX pack_object_keep_zoid ON pack_object (keep, zoid);
"""
self.runner.run_script(cursor, stmt)
def _create_pack_state(self, cursor):
if self.keep_history:
stmt = """
CREATE TABLE pack_state (
tid BIGINT NOT NULL,
zoid BIGINT NOT NULL,
PRIMARY KEY (tid, zoid)
) ENGINE = InnoDB;"""
self.runner.run_script(cursor, stmt)
def _create_pack_state_tid(self, cursor):
if self.keep_history:
stmt = """
CREATE TABLE pack_state_tid (
tid BIGINT NOT NULL PRIMARY KEY
) ENGINE = InnoDB;
"""
self.runner.run_script(cursor, stmt)
# Temp tables are created in a session-by-session basis
def _create_temp_store(self, _cursor):
return
def _create_temp_blob_chunk(self, _cursor):
return
def _create_temp_pack_visit(self, _cursor):
return
def _create_temp_undo(self, _cursor):
return
def _init_after_create(self, cursor):
if self.keep_history:
stmt = """
INSERT INTO transaction (tid, username, description)
VALUES (0, 'system', 'special transaction for object creation');
"""
self.runner.run_script(cursor, stmt)
def _reset_oid(self, cursor):
stmt = "TRUNCATE new_oid;"
self.runner.run_script(cursor, stmt)