[Checkins] SVN: relstorage/branches/postgres_blob_oid/relstorage/adapters/ Update PostgreSQL schema to switch ZODB BLOBS from BYTEA to OID.
Martijn Pieters
mj at zopatista.com
Sun Jun 12 09:18:54 EDT 2011
Log message for revision 121922:
Update PostgreSQL schema to switch ZODB BLOBS from BYTEA to OID.
Included is a AFTER DELETE trigger that unlinks the OID data when the blob_chunk row is deleted.
Changed:
U relstorage/branches/postgres_blob_oid/relstorage/adapters/mover.py
U relstorage/branches/postgres_blob_oid/relstorage/adapters/schema.py
-=-
Modified: relstorage/branches/postgres_blob_oid/relstorage/adapters/mover.py
===================================================================
--- relstorage/branches/postgres_blob_oid/relstorage/adapters/mover.py 2011-06-12 12:02:37 UTC (rev 121921)
+++ relstorage/branches/postgres_blob_oid/relstorage/adapters/mover.py 2011-06-12 13:18:54 UTC (rev 121922)
@@ -392,7 +392,7 @@
CREATE TEMPORARY TABLE temp_blob_chunk (
zoid BIGINT NOT NULL,
chunk_num BIGINT NOT NULL,
- chunk BYTEA
+ chunk OID
) ON COMMIT DROP;
CREATE UNIQUE INDEX temp_blob_chunk_key
ON temp_blob_chunk (zoid, chunk_num);
Modified: relstorage/branches/postgres_blob_oid/relstorage/adapters/schema.py
===================================================================
--- relstorage/branches/postgres_blob_oid/relstorage/adapters/schema.py 2011-06-12 12:02:37 UTC (rev 121921)
+++ relstorage/branches/postgres_blob_oid/relstorage/adapters/schema.py 2011-06-12 13:18:54 UTC (rev 121922)
@@ -104,7 +104,7 @@
tid BIGINT NOT NULL,
chunk_num BIGINT NOT NULL,
PRIMARY KEY (zoid, tid, chunk_num),
- chunk BYTEA NOT NULL
+ chunk OID NOT NULL
);
CREATE INDEX blob_chunk_lookup ON blob_chunk (zoid, tid);
ALTER TABLE blob_chunk ADD CONSTRAINT blob_chunk_fk
@@ -394,6 +394,27 @@
CREATE SEQUENCE zoid_seq;
"""
+postgresql_history_preserving_plpgsql = """
+CREATE OR REPLACE FUNCTION blob_chunk_delete_trigger() RETURNS TRIGGER
+AS $blob_chunk_delete_trigger$
+ -- Version: %s
+ -- Unlink large object data file after blob_chunck row deletion
+ BEGIN
+ PERFORM lo_unlink(OLD.chunk);
+ RETURN NULL;
+ END;
+$blob_chunk_delete_trigger$ LANGUAGE plpgsql;
+/
+
+DROP TRIGGER IF EXISTS blob_chunk_delete ON blob_chunk;
+/
+CREATE TRIGGER blob_chunk_delete
+ AFTER DELETE ON blob_chunk
+ FOR EACH ROW
+ EXECUTE PROCEDURE blob_chunk_delete_trigger();
+/
+""" % relstorage_op_version
+
oracle_history_preserving_plsql = """
CREATE OR REPLACE PACKAGE relstorage_op AS
TYPE numlist IS TABLE OF NUMBER(20) INDEX BY BINARY_INTEGER;
@@ -494,7 +515,7 @@
chunk_num BIGINT NOT NULL,
PRIMARY KEY (zoid, chunk_num),
tid BIGINT NOT NULL,
- chunk BYTEA NOT NULL
+ chunk OID NOT NULL
);
CREATE INDEX blob_chunk_lookup ON blob_chunk (zoid);
ALTER TABLE blob_chunk ADD CONSTRAINT blob_chunk_fk
@@ -674,6 +695,8 @@
CREATE SEQUENCE zoid_seq;
"""
+postgresql_history_free_plpgsql = postgresql_history_preserving_plpgsql
+
oracle_history_free_plsql = """
CREATE OR REPLACE PACKAGE relstorage_op AS
TYPE numlist IS TABLE OF NUMBER(20) INDEX BY BINARY_INTEGER;
@@ -885,6 +908,41 @@
connmanager, runner, keep_history)
self.locker = locker
+ def prepare(self):
+ """Create the database schema if it does not already exist."""
+ def callback(conn, cursor):
+ tables = self.list_tables(cursor)
+ if not 'object_state' in tables:
+ self.create(cursor)
+ else:
+ self.check_compatibility(cursor, tables)
+ self.update_schema(cursor, tables)
+ triggers = self.list_triggers(cursor)
+ if triggers.get('blob_chunk_delete_trigger') != relstorage_op_version:
+ self.install_triggers(cursor)
+ triggers = self.list_triggers(cursor)
+ if triggers.get('blob_chunk_delete_trigger') != relstorage_op_version:
+ raise AssertionError(
+ "Could not get version information after "
+ "installing the blob_chunk_delete_trigger trigger.")
+ self.connmanager.open_and_call(callback)
+
+ def install_triggers(self, cursor):
+ """Install the PL/pgSQL triggers"""
+ if self.keep_history:
+ plpgsql = postgresql_history_preserving_plpgsql
+ else:
+ plpgsql = postgresql_history_free_plpgsql
+
+ lines = []
+ for line in plpgsql.splitlines():
+ if line.strip() == '/':
+ # end of a statement
+ cursor.execute('\n'.join(lines))
+ lines = []
+ elif line.strip():
+ lines.append(line)
+
def create(self, cursor):
"""Create the database tables."""
super(PostgreSQLSchemaInstaller, self).create(cursor)
@@ -899,7 +957,25 @@
cursor.execute("SELECT relname FROM pg_class WHERE relkind = 'S'")
return [name for (name,) in cursor]
+ def list_triggers(self, cursor):
+ """Returns {trigger name: version}. version may be None."""
+ stmt = """
+ SELECT proname, prosrc
+ FROM pg_catalog.pg_namespace n
+ JOIN pg_catalog.pg_proc p ON pronamespace = n.oid
+ JOIN pg_catalog.pg_type t ON prorettype = t.oid
+ WHERE nspname = 'public' AND typname = 'trigger'
+ """
+ cursor.execute(stmt)
+ res = {}
+ for (name, text) in cursor:
+ version = None
+ match = re.search(r'Version:\s*([0-9a-zA-Z.]+)', text)
+ if match is not None:
+ version = match.group(1)
+ res[name.lower()] = version
+
class MySQLSchemaInstaller(AbstractSchemaInstaller):
implements(ISchemaInstaller)
More information about the checkins
mailing list