ITADN

Issue with indexes already existing or duplicating

#1234Closedivanthewebber 创建于 2025-12-11
I
ivanthewebbercommented
I am getting an error trying to apply the [sqlalchemy example ](https://github.com/dbfixtures/pytest-postgresql?tab=readme-ov-file#using-sqlalchemy-to-initialise-basic-database-state). Specifically it seems that it's trying to create an index again. The relevant fixtures look like this: ```python import psycopg def load_database(**kwargs): """ Initialize the database template for unit tests. """ connection = psycopg.connect(**kwargs) with connection.cursor() as cur: cur.execute("CREATE EXTENSION IF NOT EXISTS pg_trgm;") connection.commit() connection.close() engine = create_engine(f"postgresql+psycopg2://{kwargs['user']}:@{kwargs['host']}:{kwargs['port']}/{kwargs['dbname']}") Base.metadata.create_all(engine) session = scoped_session(sessionmaker(bind=engine)) # NOTE add anything else shared by all tests to session here session.commit() postgresql_proc = factories.postgresql_proc(load=[load_database]) # postgresql is a fixture for postgresql_proc # it has function scope postgresql = factories.postgresql('postgresql_proc') @pytest.fixture def db_session(postgresql) -> Generator[OrmSession, None, None]: """ Function-scoped database transaction. All data in the transaction is rolled back between test classes. """ connection = f'postgresql+psycopg2://{postgresql.info.user}:@{postgresql.info.host}:{postgresql.info.port}/{postgresql.info.dbname}' engine = create_engine(connection) session = scoped_session( sessionmaker(autocommit=False, autoflush=False, bind=engine) ) yield session session.close() ``` My understanding is that load_database will be used to create a template all tests will start from. ``` ______________ ERROR at setup of test_my_test _______________ self = <sqlalchemy.engine.base.Connection object at 0xffff8bfe1540> dialect = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0xffff8bfe1780> context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0xffff8bf752d0> statement = <sqlalchemy.dialects.postgresql.base.PGDDLCompiler object at 0xffff8bfe0c40> parameters = [immutabledict({})] def _exec_single_context( self, dialect: Dialect, context: ExecutionContext, statement: Union[str, Compiled], parameters: Optional[_AnyMultiExecuteParams], ) -> CursorResult[Any]: """continue the _execute_context() method for a single DBAPI cursor.execute() or cursor.executemany() call. """ if dialect.bind_typing is BindTyping.SETINPUTSIZES: generic_setinputsizes = context._prepare_set_input_sizes() if generic_setinputsizes: try: dialect.do_set_input_sizes( context.cursor, generic_setinputsizes, context ) except BaseException as e: self._handle_dbapi_exception( e, str(statement), parameters, None, context ) cursor, str_statement, parameters = ( context.cursor, context.statement, context.parameters, ) effective_parameters: Optional[_AnyExecuteParams] if not context.executemany: effective_parameters = parameters[0] else: effective_parameters = parameters if self._has_events or self.engine._has_events: for fn in self.dispatch.before_cursor_execute: str_statement, effective_parameters = fn( self, cursor, str_statement, effective_parameters, context, context.executemany, ) if self._echo: self._log_info(str_statement) stats = context._get_cache_stats() if not self.engine.hide_parameters: self._log_info( "[%s] %r", stats, sql_util._repr_params( effective_parameters, batches=10, ismulti=context.executemany, ), ) else: self._log_info( "[%s] [SQL parameters hidden due to hide_parameters=True]", stats, ) evt_handled: bool = False try: if context.execute_style is ExecuteStyle.EXECUTEMANY: effective_parameters = cast( "_CoreMultiExecuteParams", effective_parameters ) if self.dialect._has_events: for fn in self.dialect.dispatch.do_executemany: if fn( cursor, str_statement, effective_parameters, context, ): evt_handled = True break if not evt_handled: self.dialect.do_executemany( cursor, str_statement, effective_parameters, context, ) elif not effective_parameters and context.no_parameters: if self.dialect._has_events: for fn in self.dialect.dispatch.do_execute_no_params: if fn(cursor, str_statement, context): evt_handled = True break if not evt_handled: self.dialect.do_execute_no_params( cursor, str_statement, context ) else: effective_parameters = cast( "_CoreSingleExecuteParams", effective_parameters ) if self.dialect._has_events: for fn in self.dialect.dispatch.do_execute: if fn( cursor, str_statement, effective_parameters, context, ): evt_handled = True break if not evt_handled: > self.dialect.do_execute( cursor, str_statement, effective_parameters, context ) /usr/local/lib/python3.10/site-packages/sqlalchemy/engine/base.py:1967: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0xffff8bfe1780> cursor = <cursor object at 0xffff8b9f86d0; closed: -1> statement = 'CREATE INDEX ix_user_distribution_settings_category ON user_distribution_settings (category)' parameters = immutabledict({}) context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0xffff8bf752d0> def do_execute(self, cursor, statement, parameters, context=None): > cursor.execute(statement, parameters) E psycopg2.errors.DuplicateTable: relation "ix_user_distribution_settings_category" already exists /usr/local/lib/python3.10/site-packages/sqlalchemy/engine/default.py:941: DuplicateTable The above exception was the direct cause of the following exception: fixturedef = <FixtureDef argname='postgresql' scope='function' baseid='tests'> request = <SubRequest 'postgresql' for <Function test__check_user_contact_groups>> @pytest.hookimpl(wrapper=True) def pytest_fixture_setup(fixturedef: FixtureDef, request) -> object | None: asyncio_mode = _get_asyncio_mode(request.config) if not _is_asyncio_fixture_function(fixturedef.func): if asyncio_mode == Mode.STRICT: # Ignore async fixtures without explicit asyncio mark in strict mode # This applies to pytest_trio fixtures, for example return (yield) if not _is_coroutine_or_asyncgen(fixturedef.func): > return (yield) /usr/local/lib/python3.10/site-packages/pytest_asyncio/plugin.py:735: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /usr/local/lib/python3.10/site-packages/pytest_postgresql/factories/client.py:53: in postgresql_factory proc_fixture: Union[PostgreSQLExecutor, NoopExecutor] = request.getfixturevalue( /usr/local/lib/python3.10/site-packages/pytest_asyncio/plugin.py:735: in pytest_fixture_setup return (yield) /usr/local/lib/python3.10/site-packages/pytest_postgresql/factories/process.py:184: in postgresql_proc_fixture janitor.load(load_element) /usr/local/lib/python3.10/site-packages/pytest_postgresql/janitor.py:121: in load _loader( tests/conftest.py:305: in load_database Base.metadata.create_all(engine) /usr/local/lib/python3.10/site-packages/sqlalchemy/sql/schema.py:5868: in create_all bind._run_ddl_visitor( /usr/local/lib/python3.10/site-packages/sqlalchemy/engine/base.py:3253: in _run_ddl_visitor conn._run_ddl_visitor(visitorcallable, element, **kwargs) /usr/local/lib/python3.10/site-packages/sqlalchemy/engine/base.py:2459: in _run_ddl_visitor visitorcallable(self.dialect, self, **kwargs).traverse_single(element) /usr/local/lib/python3.10/site-packages/sqlalchemy/sql/visitors.py:664: in traverse_single return meth(obj, **kw) /usr/local/lib/python3.10/site-packages/sqlalchemy/sql/ddl.py:918: in visit_metadata self.traverse_single( /usr/local/lib/python3.10/site-packages/sqlalchemy/sql/visitors.py:664: in traverse_single return meth(obj, **kw) /usr/local/lib/python3.10/site-packages/sqlalchemy/sql/ddl.py:960: in visit_table self.traverse_single(index, create_ok=True) /usr/local/lib/python3.10/site-packages/sqlalchemy/sql/visitors.py:664: in traverse_single return meth(obj, **kw) /usr/local/lib/python3.10/site-packages/sqlalchemy/sql/ddl.py:997: in visit_index CreateIndex(index)._invoke_with(self.connection) /usr/local/lib/python3.10/site-packages/sqlalchemy/sql/ddl.py:314: in _invoke_with return bind.execute(self) /usr/local/lib/python3.10/site-packages/sqlalchemy/engine/base.py:1418: in execute return meth( /usr/local/lib/python3.10/site-packages/sqlalchemy/sql/ddl.py:180: in _execute_on_connection return connection._execute_ddl( /usr/local/lib/python3.10/site-packages/sqlalchemy/engine/base.py:1529: in _execute_ddl ret = self._execute_context( /usr/local/lib/python3.10/site-packages/sqlalchemy/engine/base.py:1846: in _execute_context return self._exec_single_context( /usr/local/lib/python3.10/site-packages/sqlalchemy/engine/base.py:1986: in _exec_single_context self._handle_dbapi_exception( /usr/local/lib/python3.10/site-packages/sqlalchemy/engine/base.py:2355: in _handle_dbapi_exception raise sqlalchemy_exception.with_traceback(exc_info[2]) from e /usr/local/lib/python3.10/site-packages/sqlalchemy/engine/base.py:1967: in _exec_single_context self.dialect.do_execute( _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0xffff8bfe1780> cursor = <cursor object at 0xffff8b9f86d0; closed: -1> statement = 'CREATE INDEX ix_user_distribution_settings_category ON user_distribution_settings (category)' parameters = immutabledict({}) context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0xffff8bf752d0> def do_execute(self, cursor, statement, parameters, context=None): > cursor.execute(statement, parameters) E sqlalchemy.exc.ProgrammingError: (psycopg2.errors.DuplicateTable) relation "ix_user_distribution_settings_category" already exists E E [SQL: CREATE INDEX ix_user_distribution_settings_category ON user_distribution_settings (category)] E (Background on this error at: https://sqlalche.me/e/20/f405) /usr/local/lib/python3.10/site-packages/sqlalchemy/engine/default.py:941: ProgrammingError ```
关闭于 2025-12-17 9 条评论