diff options
| author | Barry Warsaw | 2015-12-16 10:38:04 -0500 |
|---|---|---|
| committer | Barry Warsaw | 2015-12-16 14:52:56 -0500 |
| commit | ff84c2bb24555d4ae6276b1dca7071b440172bdb (patch) | |
| tree | 116baf4b4c5663efea47346bd7e67c6e3e18d997 | |
| parent | b414461832e4da232096904f2630b07a8caf6b15 (diff) | |
| download | mailman-ff84c2bb24555d4ae6276b1dca7071b440172bdb.tar.gz mailman-ff84c2bb24555d4ae6276b1dca7071b440172bdb.tar.zst mailman-ff84c2bb24555d4ae6276b1dca7071b440172bdb.zip | |
| -rw-r--r-- | src/mailman/database/alembic/versions/47294d3a604_pendable_indexes.py | 35 | ||||
| -rw-r--r-- | src/mailman/database/tests/test_migrations.py | 4 | ||||
| -rw-r--r-- | src/mailman/interfaces/pending.py | 8 | ||||
| -rw-r--r-- | src/mailman/model/docs/pending.rst | 7 | ||||
| -rw-r--r-- | src/mailman/model/pending.py | 8 |
5 files changed, 29 insertions, 33 deletions
diff --git a/src/mailman/database/alembic/versions/47294d3a604_pendable_indexes.py b/src/mailman/database/alembic/versions/47294d3a604_pendable_indexes.py index 775294290..6b627dc77 100644 --- a/src/mailman/database/alembic/versions/47294d3a604_pendable_indexes.py +++ b/src/mailman/database/alembic/versions/47294d3a604_pendable_indexes.py @@ -11,10 +11,11 @@ Create Date: 2015-12-02 11:46:47.295174 # revision identifiers, used by Alembic. revision = '47294d3a604' -down_revision = '33bc0099223' +down_revision = '3e09bb4a5dc' import json import sqlalchemy as sa + from alembic import op @@ -22,7 +23,7 @@ TYPE_CLUES = { 'member_id': 'probe', 'token_owner': 'subscription', '_mod_message_id': 'data', -} + } pended_table = sa.sql.table( 'pended', @@ -37,6 +38,7 @@ keyvalue_table = sa.sql.table( sa.sql.column('pended_id', sa.Integer), ) + def upgrade(): op.create_index( op.f('ix_pended_expiration_date'), 'pended', ['expiration_date'], @@ -57,15 +59,17 @@ def upgrade(): kv_type = [kv for kv in keyvalues if kv['key'] == 'type'] if kv_type: # Convert existing type keys from JSON to plain text. - kv_type = kv_type[0] # the (pended_id, key) tuple is unique. + # The (pended_id, key) tuple is unique. + kv_type = kv_type[0] try: new_value = json.loads(kv_type['value']) except ValueError: - pass # New-style entry (or already converted). + # New-style entry (or already converted). + pass else: connection.execute(keyvalue_table.update().where( keyvalue_table.c.id == kv_type['id'] - ).values(value=new_value)) + ).values(value=new_value)) else: # Detect the type and add the corresponding type key. keys = [kv['key'] for kv in keyvalues] @@ -93,26 +97,7 @@ def downgrade(): connection.execute(keyvalue_table.update().where( keyvalue_table.c.id == keyvalue['id'] ).values(value=json.dumps(keyvalue['value']))) - # Remove indexes - op.drop_index(op.f('ix_pendedkeyvalue_value'), table_name='pendedkeyvalue') - op.drop_index(op.f('ix_pendedkeyvalue_key'), table_name='pendedkeyvalue') - op.drop_index(op.f('ix_pended_token'), table_name='pended') - op.drop_index(op.f('ix_pended_expiration_date'), table_name='pended') - # Data migration. - connection = op.get_bind() - # Remove the introduced type keys. - connection.execute(keyvalue_table.delete().where(sa.and_( - keyvalue_table.c.key == 'type', - keyvalue_table.c.value.in_(TYPE_CLUES.values()) - ))) - # Convert the other type keys to JSON. - keyvalues = connection.execute(keyvalue_table.select().where( - keyvalue_table.c.key == 'type')).fetchall() - for keyvalue in keyvalues: - connection.execute(keyvalue_table.update().where( - keyvalue_table.c.id == keyvalue['id'] - ).values(value=json.dumps(keyvalue['value']))) - # Remove indexes + # Remove indexes. op.drop_index(op.f('ix_pendedkeyvalue_value'), table_name='pendedkeyvalue') op.drop_index(op.f('ix_pendedkeyvalue_key'), table_name='pendedkeyvalue') op.drop_index(op.f('ix_pended_token'), table_name='pended') diff --git a/src/mailman/database/tests/test_migrations.py b/src/mailman/database/tests/test_migrations.py index f446213e5..823c94652 100644 --- a/src/mailman/database/tests/test_migrations.py +++ b/src/mailman/database/tests/test_migrations.py @@ -109,6 +109,7 @@ class TestMigrations(unittest.TestCase): # - one is a moderation request # - one is a held message # - one is a registration request in the new format + # # The first three used to have no 'type' key and must be properly # typed, the held message used to have a type key, but in JSON, and # must be converted. @@ -144,7 +145,8 @@ class TestMigrations(unittest.TestCase): config.db.store.execute(keyvalue_table.insert().values([ {'pended_id': 1, 'key': 'member_id', 'value': 'test-value'}, {'pended_id': 2, 'key': 'token_owner', 'value': 'test-value'}, - {'pended_id': 3, 'key': '_mod_message_id', 'value': 'test-value'}, + {'pended_id': 3, 'key': '_mod_message_id', + 'value': 'test-value'}, {'pended_id': 4, 'key': 'type', 'value': '"held message"'}, {'pended_id': 5, 'key': 'type', 'value': 'registration'}, ])) diff --git a/src/mailman/interfaces/pending.py b/src/mailman/interfaces/pending.py index 152946b66..59890ee03 100644 --- a/src/mailman/interfaces/pending.py +++ b/src/mailman/interfaces/pending.py @@ -37,7 +37,13 @@ from zope.interface import Interface, Attribute class IPendable(Interface): """A pendable object.""" - PEND_TYPE = Attribute("""The type of this pendable.""") + PEND_TYPE = Attribute( + """The type of this pendable. + + Subclasses must define this attribute, and it must be a unique string; + it's used to efficiently search for all pendables of the given type. + The PEND_TYPE "type" is reserved. + """) def keys(): """The keys of the pending event data, all of which are strings.""" diff --git a/src/mailman/model/docs/pending.rst b/src/mailman/model/docs/pending.rst index 9c21a09eb..a871297b6 100644 --- a/src/mailman/model/docs/pending.rst +++ b/src/mailman/model/docs/pending.rst @@ -45,9 +45,10 @@ There's exactly one entry in the pendings database now. You can *confirm* the pending, which means returning the `IPendable` structure (as a dictionary) from the database that matches the token. -All `IPendable` classes have a `PEND_TYPE` attribute which must be a string. It -is used to identify and query pendables in the database, and will be returned -as the `type` key in the dictionary. +All `IPendable` classes have a `PEND_TYPE` attribute which must be a string. +It is used to identify and query pendables in the database, and will be +returned as the `type` key in the dictionary. Thus `type` is a reserved key +and pendables may not otherwise set it. If the token isn't in the database, None is returned. diff --git a/src/mailman/model/pending.py b/src/mailman/model/pending.py index 95c88c015..1973820d0 100644 --- a/src/mailman/model/pending.py +++ b/src/mailman/model/pending.py @@ -35,7 +35,7 @@ from mailman.database.transaction import dbconnection from mailman.interfaces.pending import ( IPendable, IPended, IPendedKeyValue, IPendings) from mailman.utilities.datetime import now -from sqlalchemy import and_, Column, DateTime, ForeignKey, Integer, Unicode +from sqlalchemy import Column, DateTime, ForeignKey, Integer, Unicode, and_ from sqlalchemy.orm import aliased, relationship from zope.interface import implementer from zope.interface.verify import verifyObject @@ -139,9 +139,11 @@ class Pendings: 'Unexpected token count: {0}'.format(pendings.count())) pending = pendings[0] pendable = UnpendedPendable() - # Iter on PendedKeyValue entries that are associated with the pending - # object's ID. Watch out for type conversions. + # Iterate on PendedKeyValue entries that are associated with the + # pending object's ID. Watch out for type conversions. for keyvalue in pending.key_values: + # The `type` key is special and served. It is not JSONified. See + # the IPendable interface for details. if keyvalue.key == 'type': value = keyvalue.value else: |
