def append_table(src_con: "SimpleSQLite", dst_con: "SimpleSQLite", table_name: str) -> bool:
    """
    Append a table from source database to destination database.

    :param SimpleSQLite src_con: Connection to the source database.
    :param SimpleSQLite dst_con: Connection to the destination database.
    :param str table_name: Table name to append.
    :return: |True| if the append operation succeed.
    :rtype: bool
    :raises simplesqlite.TableNotFoundError:
        |raises_verify_table_existence|
    :raises ValueError:
        If attributes of the table are different from each other.
    """

    logger.debug(
        "append table: src={src_db}.{src_tbl}, dst={dst_db}.{dst_tbl}".format(
            src_db=src_con.database_path,
            src_tbl=table_name,
            dst_db=dst_con.database_path,
            dst_tbl=table_name,
        )
    )

    src_con.verify_table_existence(table_name)
    dst_con.validate_access_permission(["w", "a"])

    if dst_con.has_table(table_name):
        src_attrs = src_con.fetch_attr_names(table_name)
        dst_attrs = dst_con.fetch_attr_names(table_name)
        if src_attrs != dst_attrs:
            raise ValueError(
                dedent(
                    """
                    source and destination attribute is different from each other
                    src: {}
                    dst: {}
                    """.format(
                        src_attrs, dst_attrs
                    )
                )
            )

    primary_key, index_attrs, type_hints = extract_table_metadata(src_con, table_name)

    dst_con.create_table_from_tabledata(
        src_con.select_as_tabledata(table_name, type_hints=type_hints),
        primary_key=primary_key,
        index_attrs=index_attrs,
    )

    return True def copy_table(
    src_con: "SimpleSQLite",
    dst_con: "SimpleSQLite",
    src_table_name: str,
    dst_table_name: str,
    is_overwrite: bool = True,
) -> bool:
    """
    Copy a table from source to destination.

    :param SimpleSQLite src_con: Connection to the source database.
    :param SimpleSQLite dst_con: Connection to the destination database.
    :param str src_table_name: Source table name to copy.
    :param str dst_table_name: Destination table name.
    :param bool is_overwrite: If |True|, overwrite existing table.
    :return: |True| if the copy operation succeed.
    :rtype: bool
    :raises simplesqlite.TableNotFoundError:
        |raises_verify_table_existence|
    :raises ValueError:
        If attributes of the table are different from each other.
    """

    logger.debug(
        "copy table: src={src_db}.{src_tbl}, dst={dst_db}.{dst_tbl}".format(
            src_db=src_con.database_path,
            src_tbl=src_table_name,
            dst_db=dst_con.database_path,
            dst_tbl=dst_table_name,
        )
    )

    src_con.verify_table_existence(src_table_name)
    dst_con.validate_access_permission(["w", "a"])

    if dst_con.has_table(dst_table_name):
        if is_overwrite:
            dst_con.drop_table(dst_table_name)
        else:
            logger.error(
                "failed to copy table: the table already exists "
                "(src_table={}, dst_table={})".format(src_table_name, dst_table_name)
            )
            return False

    primary_key, index_attrs, _ = extract_table_metadata(src_con, src_table_name)

    result = src_con.select(select="*", table_name=src_table_name)
    if result is None:
        return False

    dst_con.create_table_from_data_matrix(
        dst_table_name,
        src_con.fetch_attr_names(src_table_name),
        result.fetchall(),
        primary_key=primary_key,
        index_attrs=index_attrs,
    )

    return True def connect_memdb(max_workers: Optional[int] = None) -> SimpleSQLite:
    """
    :return: Instance of an in memory database.
    :rtype: SimpleSQLite

    :Example:
        :ref:`example-connect-sqlite-db-mem`
    """

    return SimpleSQLite(MEMORY_DB_NAME, "w", max_workers=max_workers) class DatabaseError(sqlite3.DatabaseError):
class NullDatabaseConnectionError(DatabaseError):
class TableNotFoundError(DatabaseError):
class AttributeNotFoundError(DatabaseError):
class SqlSyntaxError(Exception):
class OperationalError(sqlite3.OperationalError):
    def message(self) -> Optional[str]:
    def __init__(self, *args, **kwargs) -> None: def validate_attr_name(name: str) -> None:
    """
    :param str name: Name to validate.
    :raises NameValidationError: |raises_validate_attr_name|
    """

    try:
        validate_sqlite_attr_name(name)
    except ValidationError as e:
        raise NameValidationError(e) def validate_table_name(name: str) -> None:
    """
    :param str name: Table name to validate.
    :raises NameValidationError: |raises_validate_table_name|
    """

    try:
        validate_sqlite_table_name(name)
    except ValidationError as e:
        if e.reason == ErrorReason.RESERVED_NAME and e.reusable_name:
            pass
        else:
            raise NameValidationError(e) TEST_TABLE_NAME = "test_table" @pytest.fixture
def con_empty(tmpdir):
    p = tmpdir.join("tmp_empty.db")
    return SimpleSQLite(str(p), "w") @pytest.fixture
def con_mix(tmpdir):
    p = tmpdir.join("tmp_mixed_data.db")
    con = SimpleSQLite(str(p), "w")

    con.create_table_from_data_matrix(
        TEST_TABLE_NAME, ["attr_i", "attr_f", "attr_s"], [[1, 2.2, "aa"], [3, 4.4, "bb"]]
    )
    con.execute_query(f"CREATE VIEW view1 AS SELECT attr_a, attr_b FROM {TEST_TABLE_NAME}")
    con.commit()

    return con @pytest.fixture
def con_null(tmpdir):
    p = tmpdir.join("tmp_null.db")
    con = SimpleSQLite(str(p), "w")
    con.close()

    return con @pytest.fixture
def con_profile(tmpdir):
    p = tmpdir.join("tmp_profile.db")
    con = SimpleSQLite(str(p), "w", profile=True)

    con.create_table_from_data_matrix(TEST_TABLE_NAME, ["attr_a", "attr_b"], [[1, 2], [3, 4]])
    con.commit()

    return con @pytest.fixture
def con_ro(tmpdir):
    p = tmpdir.join("tmp_readonly.db")
    con = SimpleSQLite(str(p), "w")

    con.create_table_from_data_matrix(TEST_TABLE_NAME, ["attr_a", "attr_b"], [[1, 2], [3, 4]])
    con.close()
    con.connect(str(p), "r")

    return con
import pytest
from simplesqlite import (
    NameValidationError,
    NullDatabaseConnectionError,
    append_table,
    connect_memdb,
    copy_table,
)
from simplesqlite._func import validate_attr_name, validate_table_name
from .fixture import (  # noqa: W0611
    TEST_TABLE_NAME,
    con_empty,
    con_mix,
    con_null,
    con_profile,
    con_ro,
)
"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""





class Test_validate_table_name:
    @pytest.mark.parametrize(["value"], [["valid_table_name"], ["table_"], ["%CPU"]])
    def test_normal(self, value):
        validate_table_name(value)

    @pytest.mark.parametrize(
        ["value", "expected"],
        [
            [None, NameValidationError],
            ["", NameValidationError],
            ["table", NameValidationError],
            ["TABLE", NameValidationError],
            ["Table", NameValidationError],
        ],
    )
    def test_exception(self, value, expected):
        with pytest.raises(expected):
            validate_table_name(value)


class Test_validate_attr_name:
    @pytest.mark.parametrize(["value"], [["valid_attr_name"], ["attr_"], ["%CPU"]])
    def test_normal(self, value):
        validate_attr_name(value)

    @pytest.mark.parametrize(
        ["value", "expected"],
        [
            [None, NameValidationError],
            ["", NameValidationError],
            ["table", NameValidationError],
            ["TABLE", NameValidationError],
            ["Table", NameValidationError],
        ],
    )
    def test_exception(self, value, expected):
        with pytest.raises(expected):
            validate_attr_name(value)


class Test_append_table:

    def test_normal(self, con_mix, con_empty):