Я работаю над кодом Ruby, который должен создавать первичные ключи в существующих таблицах базы данных MySQL. Он должен обнаруживать и исправлять дубликаты, чтобы обеспечить успешное создание индекса, используя следующий алгоритм:
# 1. Query
ALTER TABLE foo ADD PRIMARY KEY (id)
# 2. Handle exception:
Duplicate entry '3761290' for key 'PRIMARY' (Mysql::Error)
# 3. Query:
SELECT COUNT(1) FROM TABLE foo WHERE id = 3761290
# 4. (Assuming 5 rows were returned from the previous query) Query:
DELETE FROM TABLE foo WHERE id = 3761290 LIMIT 4 OFFSET 1
# 5. retry ALTER TABLE query
Тест выглядит примерно так:
def test_create_primary_key
table = 'foo'
db = flexmock
db.should_receive(:prepare).
with("ALTER TABLE #{table} ADD PRIMARY KEY (id)").
twice.
and_raise(Mysql::Error, "Duplicate entry '3761290' for key 'PRIMARY'")
db.should_receive(:prepare).
with("SELECT COUNT(1) FROM #{table} WHERE id = ?").
once.
and_return(MockStatement.new [ [5] ])
db.should_receive(:prepare).
with("DELETE FROM #{table} WHERE id = ? LIMIT 4 OFFSET 1").
once.
and_return(MockStatement.new [ [5] ])
indexer = Indexer.new :database_handle => db
indexer.create_indexes table
end
Проблема заключается в том, что код будет выполняться в бесконечном цикле (если только он не имеет условия максимального повторения, что вполне может быть выполнено), поскольку он продолжит получать исключение из базы данных FlexMock.
В идеале макет должен быть в состоянии вызвать исключение в первый раз, а затем вернуть действительный дескриптор оператора во второй раз. Блочная форма #with может сработать, но я бы хотел сделать это чисто, если это возможно.
Есть идеи?