sources:
  spanner-source:
    kind: spanner
    project: ${SPANNER_PROJECT}
    instance: ${SPANNER_INSTANCE}
    database: ${SPANNER_DATABASE}

tools:
  execute_sql:
    kind: spanner-execute-sql
    source: spanner-source
    description: Use this tool to execute DML SQL

  execute_sql_dql:
    kind: spanner-execute-sql
    source: spanner-source
    description: Use this tool to execute DQL SQL
    readOnly: true

  list_tables:
    kind: spanner-sql
    source: spanner-source
    readOnly: true
    description: "Lists detailed schema information (object type, columns, constraints, indexes) as JSON for user-created tables (ordinary or partitioned). Filters by a comma-separated list of names. If names are omitted, lists all tables in user schemas."
    statement: |
      WITH FilterTableNames AS (
        SELECT DISTINCT TRIM(name) AS TABLE_NAME
        FROM UNNEST(IF(@table_names = '' OR @table_names IS NULL, ['%'], SPLIT(@table_names, ','))) AS name
      ),

      -- 1. Table Information
      table_info_cte AS (
        SELECT
          T.TABLE_SCHEMA,
          T.TABLE_NAME,
          T.TABLE_TYPE,
          T.PARENT_TABLE_NAME, -- For interleaved tables
          T.ON_DELETE_ACTION -- For interleaved tables
        FROM INFORMATION_SCHEMA.TABLES AS T
        WHERE
          T.TABLE_SCHEMA = ''
          AND T.TABLE_TYPE = 'BASE TABLE'
          AND (EXISTS (SELECT 1 FROM FilterTableNames WHERE FilterTableNames.TABLE_NAME = '%') OR T.TABLE_NAME IN (SELECT TABLE_NAME FROM FilterTableNames))
      ),

      -- 2. Column Information (with JSON string for each column)
      columns_info_cte AS (
        SELECT
          C.TABLE_SCHEMA,
          C.TABLE_NAME,
          ARRAY_AGG(
            CONCAT(
              '{',
              '"column_name":"', IFNULL(C.COLUMN_NAME, ''), '",',
              '"data_type":"', IFNULL(C.SPANNER_TYPE, ''), '",',
              '"ordinal_position":', CAST(C.ORDINAL_POSITION AS STRING), ',',
              '"is_not_nullable":', IF(C.IS_NULLABLE = 'NO', 'true', 'false'), ',',
              '"column_default":', IF(C.COLUMN_DEFAULT IS NULL, 'null', CONCAT('"', C.COLUMN_DEFAULT, '"')),
              '}'
            ) ORDER BY C.ORDINAL_POSITION
          ) AS columns_json_array_elements
        FROM INFORMATION_SCHEMA.COLUMNS AS C
        WHERE EXISTS (SELECT 1 FROM table_info_cte TI WHERE C.TABLE_SCHEMA = TI.TABLE_SCHEMA AND C.TABLE_NAME = TI.TABLE_NAME)
        GROUP BY C.TABLE_SCHEMA, C.TABLE_NAME
      ),

      -- Helper CTE for aggregating constraint columns
      constraint_columns_agg_cte AS (
        SELECT
          CONSTRAINT_CATALOG,
          CONSTRAINT_SCHEMA,
          CONSTRAINT_NAME,
          ARRAY_AGG(CONCAT('"', COLUMN_NAME, '"') ORDER BY ORDINAL_POSITION) AS column_names_json_list
        FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
        GROUP BY CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME
      ),

      -- 3. Constraint Information (with JSON string for each constraint)
      constraints_info_cte AS (
        SELECT
          TC.TABLE_SCHEMA,
          TC.TABLE_NAME,
          ARRAY_AGG(
            CONCAT(
              '{',
              '"constraint_name":"', IFNULL(TC.CONSTRAINT_NAME, ''), '",',
              '"constraint_type":"', IFNULL(TC.CONSTRAINT_TYPE, ''), '",',
              '"constraint_definition":',
                CASE TC.CONSTRAINT_TYPE
                  WHEN 'CHECK' THEN IF(CC.CHECK_CLAUSE IS NULL, 'null', CONCAT('"', CC.CHECK_CLAUSE, '"'))
                  WHEN 'PRIMARY KEY' THEN CONCAT('"', 'PRIMARY KEY (', ARRAY_TO_STRING(COALESCE(KeyCols.column_names_json_list, []), ', '), ')', '"')
                  WHEN 'UNIQUE' THEN CONCAT('"', 'UNIQUE (', ARRAY_TO_STRING(COALESCE(KeyCols.column_names_json_list, []), ', '), ')', '"')
                  WHEN 'FOREIGN KEY' THEN CONCAT('"', 'FOREIGN KEY (', ARRAY_TO_STRING(COALESCE(KeyCols.column_names_json_list, []), ', '), ') REFERENCES ',
                                          IFNULL(RefKeyTable.TABLE_NAME, ''),
                                          ' (', ARRAY_TO_STRING(COALESCE(RefKeyCols.column_names_json_list, []), ', '), ')', '"')
                  ELSE 'null'
                END, ',',
              '"constraint_columns":[', ARRAY_TO_STRING(COALESCE(KeyCols.column_names_json_list, []), ','), '],',
              '"foreign_key_referenced_table":', IF(RefKeyTable.TABLE_NAME IS NULL, 'null', CONCAT('"', RefKeyTable.TABLE_NAME, '"')), ',',
              '"foreign_key_referenced_columns":[', ARRAY_TO_STRING(COALESCE(RefKeyCols.column_names_json_list, []), ','), ']',
              '}'
            ) ORDER BY TC.CONSTRAINT_NAME
          ) AS constraints_json_array_elements
        FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
        LEFT JOIN INFORMATION_SCHEMA.CHECK_CONSTRAINTS AS CC
          ON TC.CONSTRAINT_CATALOG = CC.CONSTRAINT_CATALOG AND TC.CONSTRAINT_SCHEMA = CC.CONSTRAINT_SCHEMA AND TC.CONSTRAINT_NAME = CC.CONSTRAINT_NAME
        LEFT JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS RC
          ON TC.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG AND TC.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA AND TC.CONSTRAINT_NAME = RC.CONSTRAINT_NAME
        LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS RefConstraint
          ON RC.UNIQUE_CONSTRAINT_CATALOG = RefConstraint.CONSTRAINT_CATALOG AND RC.UNIQUE_CONSTRAINT_SCHEMA = RefConstraint.CONSTRAINT_SCHEMA AND RC.UNIQUE_CONSTRAINT_NAME = RefConstraint.CONSTRAINT_NAME
        LEFT JOIN INFORMATION_SCHEMA.TABLES AS RefKeyTable
          ON RefConstraint.TABLE_CATALOG = RefKeyTable.TABLE_CATALOG AND RefConstraint.TABLE_SCHEMA = RefKeyTable.TABLE_SCHEMA AND RefConstraint.TABLE_NAME = RefKeyTable.TABLE_NAME
        LEFT JOIN constraint_columns_agg_cte AS KeyCols
          ON TC.CONSTRAINT_CATALOG = KeyCols.CONSTRAINT_CATALOG AND TC.CONSTRAINT_SCHEMA = KeyCols.CONSTRAINT_SCHEMA AND TC.CONSTRAINT_NAME = KeyCols.CONSTRAINT_NAME
        LEFT JOIN constraint_columns_agg_cte AS RefKeyCols
          ON RC.UNIQUE_CONSTRAINT_CATALOG = RefKeyCols.CONSTRAINT_CATALOG AND RC.UNIQUE_CONSTRAINT_SCHEMA = RefKeyCols.CONSTRAINT_SCHEMA AND RC.UNIQUE_CONSTRAINT_NAME = RefKeyCols.CONSTRAINT_NAME AND TC.CONSTRAINT_TYPE = 'FOREIGN KEY'
        WHERE EXISTS (SELECT 1 FROM table_info_cte TI WHERE TC.TABLE_SCHEMA = TI.TABLE_SCHEMA AND TC.TABLE_NAME = TI.TABLE_NAME)
        GROUP BY TC.TABLE_SCHEMA, TC.TABLE_NAME
      ),

      -- Helper CTE for aggregating index key columns (as JSON strings)
      index_key_columns_agg_cte AS (
        SELECT
          TABLE_CATALOG,
          TABLE_SCHEMA,
          TABLE_NAME,
          INDEX_NAME,
          ARRAY_AGG(
            CONCAT(
              '{"column_name":"', IFNULL(COLUMN_NAME, ''), '",',
              '"ordering":"', IFNULL(COLUMN_ORDERING, ''), '"}'
            ) ORDER BY ORDINAL_POSITION
          ) AS key_column_json_details
        FROM INFORMATION_SCHEMA.INDEX_COLUMNS
        WHERE ORDINAL_POSITION IS NOT NULL -- Key columns
        GROUP BY TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, INDEX_NAME
      ),

      -- Helper CTE for aggregating index storing columns (as JSON strings)
      index_storing_columns_agg_cte AS (
        SELECT
          TABLE_CATALOG,
          TABLE_SCHEMA,
          TABLE_NAME,
          INDEX_NAME,
          ARRAY_AGG(CONCAT('"', COLUMN_NAME, '"') ORDER BY COLUMN_NAME) AS storing_column_json_names
        FROM INFORMATION_SCHEMA.INDEX_COLUMNS
        WHERE ORDINAL_POSITION IS NULL -- Storing columns
        GROUP BY TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, INDEX_NAME
      ),

      -- 4. Index Information (with JSON string for each index)
      indexes_info_cte AS (
        SELECT
          I.TABLE_SCHEMA,
          I.TABLE_NAME,
          ARRAY_AGG(
            CONCAT(
              '{',
              '"index_name":"', IFNULL(I.INDEX_NAME, ''), '",',
              '"index_type":"', IFNULL(I.INDEX_TYPE, ''), '",',
              '"is_unique":', IF(I.IS_UNIQUE, 'true', 'false'), ',',
              '"is_null_filtered":', IF(I.IS_NULL_FILTERED, 'true', 'false'), ',',
              '"interleaved_in_table":', IF(I.PARENT_TABLE_NAME IS NULL, 'null', CONCAT('"', I.PARENT_TABLE_NAME, '"')), ',',
              '"index_key_columns":[', ARRAY_TO_STRING(COALESCE(KeyIndexCols.key_column_json_details, []), ','), '],',
              '"storing_columns":[', ARRAY_TO_STRING(COALESCE(StoringIndexCols.storing_column_json_names, []), ','), ']',
              '}'
            ) ORDER BY I.INDEX_NAME
          ) AS indexes_json_array_elements
        FROM INFORMATION_SCHEMA.INDEXES AS I
        LEFT JOIN index_key_columns_agg_cte AS KeyIndexCols
          ON I.TABLE_CATALOG = KeyIndexCols.TABLE_CATALOG AND I.TABLE_SCHEMA = KeyIndexCols.TABLE_SCHEMA AND I.TABLE_NAME = KeyIndexCols.TABLE_NAME AND I.INDEX_NAME = KeyIndexCols.INDEX_NAME
        LEFT JOIN index_storing_columns_agg_cte AS StoringIndexCols
          ON I.TABLE_CATALOG = StoringIndexCols.TABLE_CATALOG AND I.TABLE_SCHEMA = StoringIndexCols.TABLE_SCHEMA AND I.TABLE_NAME = StoringIndexCols.TABLE_NAME AND I.INDEX_NAME = StoringIndexCols.INDEX_NAME AND I.INDEX_TYPE = 'INDEX'
        WHERE EXISTS (SELECT 1 FROM table_info_cte TI WHERE I.TABLE_SCHEMA = TI.TABLE_SCHEMA AND I.TABLE_NAME = TI.TABLE_NAME)
        GROUP BY I.TABLE_SCHEMA, I.TABLE_NAME
      )

      -- Final SELECT to build the JSON output
      SELECT
        TI.TABLE_SCHEMA AS schema_name,
        TI.TABLE_NAME AS object_name,
        CONCAT(
          '{',
          '"schema_name":"', IFNULL(TI.TABLE_SCHEMA, ''), '",',
          '"object_name":"', IFNULL(TI.TABLE_NAME, ''), '",',
          '"object_type":"', IFNULL(TI.TABLE_TYPE, ''), '",',
          '"columns":[', ARRAY_TO_STRING(COALESCE(CI.columns_json_array_elements, []), ','), '],',
          '"constraints":[', ARRAY_TO_STRING(COALESCE(CONSI.constraints_json_array_elements, []), ','), '],',
          '"indexes":[', ARRAY_TO_STRING(COALESCE(II.indexes_json_array_elements, []), ','), '],',
          '}'
        ) AS object_details
      FROM table_info_cte AS TI
      LEFT JOIN columns_info_cte AS CI
        ON TI.TABLE_SCHEMA = CI.TABLE_SCHEMA AND TI.TABLE_NAME = CI.TABLE_NAME
      LEFT JOIN constraints_info_cte AS CONSI
        ON TI.TABLE_SCHEMA = CONSI.TABLE_SCHEMA AND TI.TABLE_NAME = CONSI.TABLE_NAME
      LEFT JOIN indexes_info_cte AS II
        ON TI.TABLE_SCHEMA = II.TABLE_SCHEMA AND TI.TABLE_NAME = II.TABLE_NAME
      ORDER BY TI.TABLE_SCHEMA, TI.TABLE_NAME;

    parameters:
      - name: table_names
        type: string
        description: "Optional: A comma-separated list of table names. If empty, details for all tables in user-accessible schemas will be listed."

toolsets:
  spanner-database-tools:
    - execute_sql
    - execute_sql_dql
    - list_tables
