Skip to content

[sqlalchemy-spanner]: _designate_type() raises KeyError on unrecognized Spanner column types instead of falling back to NullType #18839

Open

Description

@rocksnow1942

Determine this is the right repository

  • I determined this is the correct repository in which to report this bug.

Summary of the issue

SpannerDialect._designate_type() performs an unguarded _type_map[str_repr] lookup and raises KeyError whenever a reflected table (or any table in the same schema during get_multi_columns()) contains a Spanner column type not in _type_map (such as INTERVAL, PROTO<...>, ENUM<...>, or STRUCT<...>, as well as UUID and TOKENLIST prior to 1.20.0).

Environment details

  • OS: Linux
  • Python version: 3.13
  • sqlalchemy-spanner version: 1.17.2 and main / 1.20.1 (eb8a76628adf10093959600071a18f50285c2ecf)
  • sqlalchemy version: 2.0.52

Problem description

In SpannerDialect._designate_type:

    def _designate_type(self, str_repr):
        if str_repr.startswith("STRING"):
            end = str_repr.index(")")
            size = int_from_size(str_repr[7:end])
            return _type_map["STRING"](length=size)
        elif str_repr.startswith("BYTES"):
            end = str_repr.index(")")
            size = int_from_size(str_repr[6:end])
            return _type_map["BYTES"](length=size)
        elif str_repr.startswith("ARRAY"):
            inner_type_str = str_repr[6:-1]
            inner_type = self._designate_type(inner_type_str)
            return _type_map["ARRAY"](inner_type)
        else:
            return _type_map[str_repr]

While UUID (#17913) and TOKENLIST (#17911) were added to _type_map in 1.20.0, _designate_type() still raises an unhandled KeyError for any other valid Spanner column type not in _type_map (e.g. INTERVAL, PROTO<...>, ENUM<...>, STRUCT<...>). Because get_multi_columns() reflects all tables in a schema when filter_names=None (or when MetaData.reflect() reflects a schema), a single table with an unrecognized column type crashes reflection for the entire schema.

In SQLAlchemy's built-in dialects (e.g., PostgreSQL, MySQL, SQLite, Oracle), unrecognized column types during reflection emit sa.util.warn(f"Did not recognize type '{str_repr}' ...") and return sa.types.NullType(). (See also #16622.)

Reproduction steps: code

from google.cloud.sqlalchemy_spanner.sqlalchemy_spanner import SpannerDialect

dialect = SpannerDialect()
for spanner_type in ("INTERVAL", "PROTO<my.pkg.Message>", "ENUM<my.pkg.Status>"):
    try:
        dialect._designate_type(spanner_type)
    except KeyError as e:
        print(f"{spanner_type} -> KeyError: {e}")

Actual output

INTERVAL -> KeyError: 'INTERVAL'
PROTO<my.pkg.Message> -> KeyError: 'PROTO<my.pkg.Message>'
ENUM<my.pkg.Status> -> KeyError: 'ENUM<my.pkg.Status>'

Expected output

_designate_type() maps supported Spanner types and falls back to sa.types.NullType() with sa.util.warn(...) for unrecognized column types instead of raising KeyError.

Activity

  1. added a commit that references this issue on Oct 24, 2026
    21375ae
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions