Changes "key" column in image_properties to "name".

As with all things migration, this was a pain in the ass.

MySQL doesn't support RENAME INDEX, so a custom SQL migration
file for it was needed.

Likewise, SQLite doesn't support either RENAME INDEX or
ALTER TABLE CHANGE COLUMN, so it also needed a custom migration
script.
This commit is contained in:
jaypipes@gmail.com 2011-04-04 15:41:34 -04:00
parent 2a4a88330c
commit c14827491b
9 changed files with 221 additions and 16 deletions

View File

@ -243,22 +243,22 @@ def _set_properties_for_image(context, image_ref, properties, session=None):
"""
orig_properties = {}
for prop_ref in image_ref.properties:
orig_properties[prop_ref.key] = prop_ref
orig_properties[prop_ref.name] = prop_ref
for key, value in properties.iteritems():
for name, value in properties.iteritems():
prop_values = {'image_id': image_ref.id,
'key': key,
'name': name,
'value': value}
if key in orig_properties:
prop_ref = orig_properties[key]
if name in orig_properties:
prop_ref = orig_properties[name]
image_property_update(context, prop_ref, prop_values,
session=session)
else:
image_property_create(context, prop_values, session=session)
for key in orig_properties.keys():
if not key in properties:
prop_ref = orig_properties[key]
for name in orig_properties.keys():
if not name in properties:
prop_ref = orig_properties[name]
image_property_delete(context, prop_ref, session=session)

View File

@ -0,0 +1,93 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack LLC.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from migrate.changeset import *
from sqlalchemy import *
from sqlalchemy.sql import and_, not_
from glance.registry.db.migrate_repo.schema import (
Boolean, DateTime, Integer, String, Text, from_migration_import)
def get_images_table(meta):
"""
No changes to the image properties table from 002...
"""
(get_images_table,) = from_migration_import(
'004_add_checksum', ['get_images_table'])
images = get_images_table(meta)
return images
def get_image_properties_table(meta):
"""
Returns the Table object for the image_properties table that
corresponds to the image_properties table definition of this version.
"""
(get_images_table,) = from_migration_import(
'004_add_checksum', ['get_images_table'])
images = get_images_table(meta)
image_properties = Table('image_properties', meta,
Column('id', Integer(), primary_key=True, nullable=False),
Column('image_id', Integer(), ForeignKey('images.id'), nullable=False,
index=True),
Column('name', String(255), nullable=False),
Column('value', Text()),
Column('created_at', DateTime(), nullable=False),
Column('updated_at', DateTime()),
Column('deleted_at', DateTime()),
Column('deleted', Boolean(), nullable=False, default=False,
index=True),
UniqueConstraint('image_id', 'name'),
mysql_engine='InnoDB',
useexisting=True)
return image_properties
def upgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
(get_image_properties_table,) = from_migration_import(
'004_add_checksum', ['get_image_properties_table'])
image_properties = get_image_properties_table(meta)
index = Index('ix_image_properties_image_id_get',
image_properties.c.image_id,
image_properties.c.key)
index.rename('ix_image_properties_image_id_name')
image_properties = get_image_properties_table(meta)
image_properties.columns['key'].alter(name="name")
def downgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
image_properties = get_image_properties_table(meta)
index = Index('ix_image_properties_image_id_name',
image_properties.c.image_id,
image_properties.c.name)
index.rename('ix_image_properties_image_id_key')
image_properties.columns['name'].alter(name="key")

View File

@ -0,0 +1,11 @@
/*
* This file is necessary because MySQL does not support
* renaming indexes.
*/
DROP INDEX ix_image_properties_image_id_name ON image_properties;
/* Rename the `key` column to `name` */
ALTER TABLE image_properties
CHANGE COLUMN name `key` VARCHAR(255) NOT NULL;
CREATE UNIQUE INDEX ix_image_properties_image_id_key ON image_properties (image_id, key);

View File

@ -0,0 +1,11 @@
/*
* This file is necessary because MySQL does not support
* renaming indexes.
*/
DROP INDEX ix_image_properties_image_id_key ON image_properties;
/* Rename the `key` column to `name` */
ALTER TABLE image_properties
CHANGE COLUMN `key` name VARCHAR(255) NOT NULL;
CREATE UNIQUE INDEX ix_image_properties_image_id_name ON image_properties (image_id, name);

View File

@ -0,0 +1,46 @@
/*
* This is necessary because SQLite does not support
* RENAME INDEX or ALTER TABLE CHANGE COLUMN.
*/
BEGIN TRANSACTION;
CREATE TEMPORARY TABLE image_properties_backup (
id INTEGER NOT NULL,
image_id INTEGER NOT NULL,
key VARCHAR(255) NOT NULL,
value TEXT,
created_at DATETIME NOT NULL,
updated_at DATETIME,
deleted_at DATETIME,
deleted BOOLEAN NOT NULL,
PRIMARY KEY (id)
);
INSERT INTO image_properties_backup
SELECT id, image_id, name, value, created_at, updated_at, deleted_at, deleted
FROM image_properties;
DROP TABLE image_properties;
CREATE TABLE image_properties (
id INTEGER NOT NULL,
image_id INTEGER NOT NULL,
key VARCHAR(255) NOT NULL,
value TEXT,
created_at DATETIME NOT NULL,
updated_at DATETIME,
deleted_at DATETIME,
deleted BOOLEAN NOT NULL,
PRIMARY KEY (id),
CHECK (deleted IN (0, 1)),
UNIQUE (image_id, key),
FOREIGN KEY(image_id) REFERENCES images (id)
);
CREATE INDEX ix_image_properties_name ON image_properties (name);
INSERT INTO image_properties (id, image_id, key, value, created_at, updated_at, deleted_at, deleted)
SELECT id, image_id, name, value, created_at, updated_at, deleted_at, deleted
FROM image_properties_backup;
DROP TABLE image_properties_backup;
COMMIT;

View File

@ -0,0 +1,46 @@
/*
* This is necessary because SQLite does not support
* RENAME INDEX or ALTER TABLE CHANGE COLUMN.
*/
BEGIN TRANSACTION;
CREATE TEMPORARY TABLE image_properties_backup (
id INTEGER NOT NULL,
image_id INTEGER NOT NULL,
name VARCHAR(255) NOT NULL,
value TEXT,
created_at DATETIME NOT NULL,
updated_at DATETIME,
deleted_at DATETIME,
deleted BOOLEAN NOT NULL,
PRIMARY KEY (id)
);
INSERT INTO image_properties_backup
SELECT id, image_id, key, value, created_at, updated_at, deleted_at, deleted
FROM image_properties;
DROP TABLE image_properties;
CREATE TABLE image_properties (
id INTEGER NOT NULL,
image_id INTEGER NOT NULL,
name VARCHAR(255) NOT NULL,
value TEXT,
created_at DATETIME NOT NULL,
updated_at DATETIME,
deleted_at DATETIME,
deleted BOOLEAN NOT NULL,
PRIMARY KEY (id),
CHECK (deleted IN (0, 1)),
UNIQUE (image_id, name),
FOREIGN KEY(image_id) REFERENCES images (id)
);
CREATE INDEX ix_image_properties_name ON image_properties (name);
INSERT INTO image_properties (id, image_id, name, value, created_at, updated_at, deleted_at, deleted)
SELECT id, image_id, name, value, created_at, updated_at, deleted_at, deleted
FROM image_properties_backup;
DROP TABLE image_properties_backup;
COMMIT;

View File

@ -110,13 +110,11 @@ class Image(BASE, ModelBase):
class ImageProperty(BASE, ModelBase):
"""Represents an image properties in the datastore"""
__tablename__ = 'image_properties'
__table_args__ = (UniqueConstraint('image_id', 'key'), {})
__table_args__ = (UniqueConstraint('image_id', 'name'), {})
id = Column(Integer, primary_key=True)
image_id = Column(Integer, ForeignKey('images.id'), nullable=False)
image = relationship(Image, backref=backref('properties'))
# FIXME(sirp): KEY is a reserved word in SQL, might be a good idea to
# rename this column
key = Column(String(255), index=True, nullable=False)
name = Column(String(255), index=True, nullable=False)
value = Column(Text)

View File

@ -199,7 +199,7 @@ def make_image_dict(image):
# TODO(sirp): should this be a dict, or a list of dicts?
# A plain dict is more convenient, but list of dicts would provide
# access to created_at, etc
properties = dict((p['key'], p['value'])
properties = dict((p['name'], p['value'])
for p in image['properties'] if not p['deleted'])
image_dict = _fetch_attrs(image, db_api.IMAGE_ATTRS)

View File

@ -285,7 +285,7 @@ def stub_out_registry_db_image_api(stubs):
'checksum': None,
'size': 13,
'location': "swift://user:passwd@acct/container/obj.tar.0",
'properties': [{'key': 'type',
'properties': [{'name': 'type',
'value': 'kernel',
'deleted': False}]},
{'id': 2,
@ -330,7 +330,7 @@ def stub_out_registry_db_image_api(stubs):
if 'properties' in values.keys():
for k, v in values['properties'].items():
p = {}
p['key'] = k
p['name'] = k
p['value'] = v
p['deleted'] = False
p['created_at'] = datetime.datetime.utcnow()
@ -356,7 +356,7 @@ def stub_out_registry_db_image_api(stubs):
if 'properties' in values.keys():
for k, v in values['properties'].items():
p = {}
p['key'] = k
p['name'] = k
p['value'] = v
p['deleted'] = False
p['created_at'] = datetime.datetime.utcnow()