mirror of
https://github.com/apache/superset.git
synced 2026-08-04 13:02:41 +00:00
110 lines
4.0 KiB
Python
110 lines
4.0 KiB
Python
# Licensed to the Apache Software Foundation (ASF) under one
|
|
# or more contributor license agreements. See the NOTICE file
|
|
# distributed with this work for additional information
|
|
# regarding copyright ownership. The ASF licenses this file
|
|
# to you 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.
|
|
"""The capture path stays quiet only for the pre-migration missing-table
|
|
race; every other operational failure must classify as loggable. These
|
|
tests pin the driver-specific shapes the predicate reads, per supported
|
|
metadata database."""
|
|
|
|
from typing import Any
|
|
|
|
from sqlalchemy.exc import OperationalError, ProgrammingError
|
|
|
|
from superset.versioning.db_errors import is_missing_table_error
|
|
|
|
|
|
class _FakeDriverError(Exception):
|
|
"""Stands in for a DBAPI driver exception, carrying whichever
|
|
attributes the real driver would set."""
|
|
|
|
def __init__(self, *args: Any, **attrs: Any) -> None:
|
|
super().__init__(*args)
|
|
for name, value in attrs.items():
|
|
setattr(self, name, value)
|
|
|
|
|
|
def _wrap(
|
|
exc_cls: type, driver_error: Exception
|
|
) -> OperationalError | ProgrammingError:
|
|
return exc_cls("SELECT 1", {}, driver_error)
|
|
|
|
|
|
def test_postgres_undefined_table_is_missing_table() -> None:
|
|
# psycopg2 stamps pgcode; UndefinedTable is SQLSTATE 42P01.
|
|
error = _wrap(
|
|
ProgrammingError,
|
|
_FakeDriverError('relation "version_changes" does not exist', pgcode="42P01"),
|
|
)
|
|
assert is_missing_table_error(error) is True
|
|
|
|
|
|
def test_postgres_deadlock_is_not_missing_table() -> None:
|
|
# Deadlock is SQLSTATE 40P01 — same OperationalError class on the
|
|
# SQLAlchemy side, and the case the old class-based swallow lost.
|
|
error = _wrap(
|
|
OperationalError,
|
|
_FakeDriverError("deadlock detected", pgcode="40P01"),
|
|
)
|
|
assert is_missing_table_error(error) is False
|
|
|
|
|
|
def test_psycopg3_sqlstate_spelling_is_recognized() -> None:
|
|
# psycopg 3 exposes the code as `sqlstate`, not `pgcode`; a driver
|
|
# swap must not silently widen the swallow back to the whole class.
|
|
error = _wrap(
|
|
ProgrammingError,
|
|
_FakeDriverError('relation "version_changes" does not exist', sqlstate="42P01"),
|
|
)
|
|
assert is_missing_table_error(error) is True
|
|
|
|
|
|
def test_mysql_no_such_table_is_missing_table() -> None:
|
|
# mysqlclient raises with (errno, message) args; 1146 is
|
|
# "Table ... doesn't exist".
|
|
error = _wrap(
|
|
ProgrammingError,
|
|
_FakeDriverError(1146, "Table 'superset.version_changes' doesn't exist"),
|
|
)
|
|
assert is_missing_table_error(error) is True
|
|
|
|
|
|
def test_mysql_lock_wait_timeout_is_not_missing_table() -> None:
|
|
error = _wrap(
|
|
OperationalError,
|
|
_FakeDriverError(1205, "Lock wait timeout exceeded"),
|
|
)
|
|
assert is_missing_table_error(error) is False
|
|
|
|
|
|
def test_sqlite_no_such_table_is_missing_table() -> None:
|
|
# SQLite's OperationalError carries no code; the message is the signal.
|
|
error = _wrap(OperationalError, _FakeDriverError("no such table: version_changes"))
|
|
assert is_missing_table_error(error) is True
|
|
|
|
|
|
def test_sqlite_locked_database_is_not_missing_table() -> None:
|
|
error = _wrap(OperationalError, _FakeDriverError("database is locked"))
|
|
assert is_missing_table_error(error) is False
|
|
|
|
|
|
def test_connection_drop_with_no_code_is_not_missing_table() -> None:
|
|
# A dropped connection often surfaces with no driver code at all.
|
|
error = _wrap(
|
|
OperationalError,
|
|
_FakeDriverError("server closed the connection unexpectedly"),
|
|
)
|
|
assert is_missing_table_error(error) is False
|