{
  "markdown": "# spanalyzer\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/apstndb/spanalyzer.svg)](https://pkg.go.dev/github.com/apstndb/spanalyzer)\n\n`spanalyzer` is a Spanner analyzer framework: an experimental Go library and\nCLI toolkit for deriving Cloud Spanner GoogleSQL query result row types from\nSpanner DDL, and for building query analysis and code generation workflows on\ntop of that.\n\nThe current implementation parses Spanner DDL with\n[`github.com/cloudspannerecosystem/memefish`](https://github.com/cloudspannerecosystem/memefish)\nand analyzes queries with\n[`github.com/goccy/go-googlesql`](https://github.com/goccy/go-googlesql).\nThey are replaceable implementation details of the high-level `Analyzer` API.\nExperimental low-level escape hatches currently expose some of their types and\nare not a stable public contract.\n\nThis repository was previously named `go-googlesql-spanner-poc`.\n\nThe repository hosts five Go modules, split along dependency weight:\n\n- `github.com/apstndb/spanalyzer` — the analyzer framework: DDL catalog,\n  GoogleSQL analysis, type conversion, code generation planning, and the\n  lightweight CLIs (`spanner-analyzer`, `spanner-function-catalog`). Depends\n  on memefish and the GoogleSQL frontend, but not on container tooling.\n- [`github.com/apstndb/spanalyzer/plancontract`](plancontract) — a\n  lightweight nested module that normalizes raw `spannerpb.QueryPlan` values\n  (operator family classification, operator topology, plan digests) and\n  evaluates plan contracts against them. Its experimental\n  [`planvocab`](plancontract/planvocab) package detects new operator metadata\n  and child-link vocabulary against a generated, provenance-stamped catalog.\n  It works with plans obtained from Cloud Spanner, the emulator, Spanner Omni,\n  or saved artifacts, and depends only on the Spanner protos, CEL, and YAML —\n  not on the GoogleSQL frontend or containers.\n- [`github.com/apstndb/spanalyzer/cmd/spanner-query-gen`](cmd/spanner-query-gen)\n  — the query code generation CLI, including the Omni-backed `plan-report`\n  workflow and integration tests. This is where spanemuboost,\n  testcontainers, and the Docker client enter the dependency graph.\n- [`github.com/apstndb/spanalyzer/survey`](survey) — retained schema discovery,\n  DDL round-trip, and managed/Omni/Emulator evidence producers. It remains an\n  independently testable nested module; its unpublished legacy Git history is\n  represented by a checked import-provenance record rather than published.\n- [`github.com/apstndb/spanalyzer/tools`](tools) — developer-only Spanner\n  Omni probes (`spanner-query-plan-shape`, `optparam-plan-probe`), with the\n  same container-tooling dependencies.\n\nDevelopment across modules uses the committed `go.work` workspace.\n\n## Repository verification\n\nThe repository root owns read-only verification tasks across all five modules\nand the currently pinned target runtimes:\n\n```sh\nmise run lint               # all five Go modules; also used by CI and pre-push\nmise run verify-local       # modules, generated evidence, OKF, Markdown policy\nmise run verify-containers  # exact Emulator/Omni manifests; no evidence writes\nmise run verify-managed     # point-in-time checks against TEST_REAL_SPANNER_DATABASE\nmise run verify-current     # local + containers + managed\n```\n\nActivate the versioned pre-push hook once in each clone:\n\n```sh\nmise run hooks-install\n```\n\nThe hook runs the same `mise run lint` gate as CI and blocks a push when any\nmodule fails linting. Git does not activate repository hooks on clone, so the\ninstallation step is intentionally explicit. `git push --no-verify` can still\nbypass a local hook; CI remains the authoritative shared gate.\n\n[`runtime_targets.json`](runtime_targets.json) is the single source of truth\nfor the descriptive tags and platform-specific OCI manifest digests used by\nEmulator and Omni tests. Container check commands compare live catalog surfaces\nwith retained evidence and emit compact JSON; they do not create new captures.\nThe two Omni plan suites share one runtime in `verify-containers`, avoiding a\nsecond cold container startup.\n\n## Documentation map\n\n- [`knowledge/`](knowledge/index.md): the OKF v0.2 entry point for the\n  repository. It organizes architecture concepts, curated observations,\n  OKF-native research notes, and a\n  [complete tracked-Markdown inventory](knowledge/references/repository-documents.md),\n  plus a selective inventory of\n  [knowledge-bearing repository assets](knowledge/references/repository-assets.md),\n  while preserving one canonical body per topic.\n- This README: positioning, module layout, analyzer usage, and public\n  limitations.\n- [`AGENTS.md`](AGENTS.md): guidance for coding agents (commands, module\n  rules, testing layout).\n- [`TODO.md`](TODO.md): open follow-up work only; resolved history is\n  archived under [`research/archive/`](research/archive/).\n- [`cmd/spanner-query-gen/`](cmd/spanner-query-gen/README.md): the code\n  generator's UX ([README](cmd/spanner-query-gen/README.md)), intended\n  architecture ([DESIGN](cmd/spanner-query-gen/DESIGN.md)), drift status\n  ([IMPLEMENTATION_STATUS](cmd/spanner-query-gen/IMPLEMENTATION_STATUS.md)),\n  and the contract surface\n  ([PLAN_CONTRACTS](cmd/spanner-query-gen/PLAN_CONTRACTS.md)).\n- [`research/`](research/README.md): legacy non-normative observation notes and\n  design background. New substantive research is authored under\n  [`knowledge/research/`](knowledge/research/index.md).\n\n## Why plan inspection\n\nThe plan tooling in this repository — `plan-report`, plan contracts, the\n`plancontract` module, and the `spanner-query-plan-shape` probe — exists to\nmake careful execution plan inspection cheap, repeatable, and automatable.\nAs [Use The Index, Luke](https://use-the-index-luke.com/sql/testing-scalability/system-load)\nputs it:\n\n> Careful execution plan inspection yields more confidence than superficial\n> benchmarks. A full stress test is still worthwhile—but the costs are high.\n\nThe properties of real data are something only the application owner can\nknow, and constructing meaningful test data is hard. Many plan questions —\noperator choice, seekability, join elimination, hint effects, plan\nregressions across optimizer versions — can be answered from PLAN output\nalone, even against an empty database. The tooling therefore targets\nPLAN-only structural evidence: `plan-report` turns plans into reviewable\nartifacts, plan contracts turn inspection results into repeatable regression\nchecks, and the `plancontract` module applies the same normalization to\nplans obtained from any source. Performance claims beyond plan structure\nstill require PROFILE statistics over real data and load testing; the tools\nintentionally stop short of those.\n\nIn this document, \"GoogleSQL frontend\" refers to the analyzer and catalog\nlibrary formerly named ZetaSQL. \"Spanner GoogleSQL\" refers to\n[Cloud Spanner's SQL dialect](https://cloud.google.com/spanner/docs/reference/standard-sql/query-syntax).\nHistorical ZetaSQL names appear only when referring to upstream API names or\nrepositories that still use them.\n\n## Usage\n\n```sh\ngo run ./cmd/spanner-analyzer \\\n  --ddl testdata/order-proto-schema.sql \\\n  --proto-descriptors-file testdata/protos/order_descriptors.pb \\\n  --sql 'SELECT OrderInfo.order_number FROM Orders'\n```\n\nOutput excerpt:\n\n```yaml\nfields:\n- name: order_number\n  type:\n    code: STRING\n```\n\n`--proto-descriptors-file` accepts a Protocol Buffers `FileDescriptorSet` used\nto resolve types named by `CREATE PROTO BUNDLE` or `ALTER PROTO BUNDLE`. The\nflag is repeatable.\n\n`--ddl` is optional. Queries that only use built-in functions, parameters,\n`INFORMATION_SCHEMA`, or `SPANNER_SYS` can be analyzed without a schema file:\n\n```sh\ngo run ./cmd/spanner-analyzer \\\n  --sql 'SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES'\n```\n\nRegistered GoogleSQL frontend and Spanner function signatures can be dumped\nwith the dedicated function catalog command:\n\n```sh\ngo run ./cmd/spanner-function-catalog\n```\n\nUse `--verbose=false` to print only function names. `--ddl` and\n`--proto-descriptors-file` are also accepted when the catalog depends on schema\nobjects or proto descriptors.\n\nDeveloper-only probes live under `tools/`. For example,\n[`tools/spanner-query-plan-shape`](tools/spanner-query-plan-shape) starts\nSpanner Omni through [`spanemuboost`](https://github.com/apstndb/spanemuboost)\nand prints raw query plan node shapes for plan normalization work.\nExisting legacy investigation notes and review archives live under\n[`research/`](research/); new substantive research is authored under\n[`knowledge/research/`](knowledge/research/index.md). Both are non-normative\nsupporting material rather than the public CLI contract.\n\nThe repository includes the Protocol Buffers example from the\n[Cloud Spanner protocol buffers reference](https://cloud.google.com/spanner/docs/reference/standard-sql/protocol-buffers)\nunder `testdata/protos/`, including\n`order_protos.proto` and its compiled `order_descriptors.pb` descriptor set.\n\nNamed query parameters can be declared with `--param name=TYPE`. Positional\nparameters can be declared with repeatable `--positional-param TYPE`.\n\n```sh\ngo run ./cmd/spanner-analyzer \\\n  --sql 'SELECT @id AS id' \\\n  --param id=INT64\n```\n\nMore complex queries can mix aggregate functions, conditional expressions, and\nproto field access. The output is still the Cloud Spanner result row type, not\nquery data.\n\n```sh\ngo run ./cmd/spanner-analyzer \\\n  --ddl testdata/order-proto-schema.sql \\\n  --proto-descriptors-file testdata/protos/order_descriptors.pb \\\n  --sql '\n    SELECT\n      COUNT(*) AS order_count,\n      SUM(Id) AS id_sum,\n      AVG(Id) AS avg_id,\n      IF(COUNT(*) > 0, \"nonempty\", \"empty\") AS status,\n      CASE WHEN MAX(Id) >= 100 THEN \"large\" ELSE \"small\" END AS id_bucket,\n      COALESCE(MIN(OrderInfo.order_number), \"none\") AS first_order_number\n    FROM Orders'\n```\n\nOutput:\n\n```yaml\nfields:\n- name: order_count\n  type:\n    code: INT64\n- name: id_sum\n  type:\n    code: INT64\n- name: avg_id\n  type:\n    code: FLOAT64\n- name: status\n  type:\n    code: STRING\n- name: id_bucket\n  type:\n    code: STRING\n- name: first_order_number\n  type:\n    code: STRING\n```\n\n`--sql-mode expression` analyzes a single GoogleSQL expression and returns a\nsingle Spanner `Type` instead of a query result row type.\n\n```sh\ngo run ./cmd/spanner-analyzer \\\n  --sql-mode expression \\\n  --sql 'AI.SCORE(@prompt)' \\\n  --param 'prompt=STRING(MAX)'\n```\n\nOutput:\n\n```yaml\ncode: FLOAT64\n```\n\nPolymorphic functions resolve their return type from the argument type.\n\n```sh\ngo run ./cmd/spanner-analyzer \\\n  --sql-mode expression \\\n  --sql 'ARRAY_FIRST([1, 2, 3])'\n```\n\nOutput:\n\n```yaml\ncode: INT64\n```\n\n```sh\ngo run ./cmd/spanner-analyzer \\\n  --sql-mode expression \\\n  --sql 'ARRAY_FIRST([\"a\", \"b\"])'\n```\n\nOutput:\n\n```yaml\ncode: STRING\n```\n\nCloud Spanner `INFORMATION_SCHEMA` tables are registered as built-in catalog\ntables for analysis. They provide names and column types only; no row data is\nmaterialized. The built-in surface comes from the generated\n[`information_schema_manifest.json`](information_schema_manifest.json), whose\n[`projection source`](information_schema_projection_source.json) selects one\nexact point-in-time managed observation. Observed and rolling columns are\nprojected, while documented columns absent from that observation remain\nevidence metadata only. The observation is scoped to one database and does not\nclaim that managed Spanner has a fleet-wide or durable metadata version.\n\nValidate the selected capture, producer registry, projection source, and exact\ngenerated manifest bytes:\n\n```sh\n(cd tools && go run ./infoschema-survey-check)\n```\n\n```sh\ngo run ./cmd/spanner-analyzer \\\n  --sql 'SELECT TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, SPANNER_TYPE\n         FROM INFORMATION_SCHEMA.COLUMNS'\n```\n\nCloud Spanner `SPANNER_SYS` introspection tables are also registered as built-in\ncatalog tables. The built-in surface comes from the pinned, live-primary\n[`spanner_sys_manifest.json`](spanner_sys_manifest.json): only columns observed\nwith identical type and ordinal on both the recorded managed and Omni targets\nare projected, while known-absent entries and documentation conflicts remain\nevidence metadata. This is a pinned common surface, not a guarantee about every\ndeployment or future rollout. The tables are useful for type-checking monitoring\nqueries and statistics helpers such as `SPANNER_SYS.DISTRIBUTION_PERCENTILE`.\n\nValidate the manifest and reproduce its exact bytes from the retained\nin-repository survey exporter:\n\n```sh\n(cd tools && go run ./spannersys-survey-check)\n```\n\nThe immutable mapping from the unpublished legacy source snapshot to the\ninitial imported subtree is checked separately:\n\n```sh\n(cd tools && go run ./survey-import-check)\n```\n\n```sh\ngo run ./cmd/spanner-analyzer \\\n  --sql 'SELECT\n           INTERVAL_END,\n           TABLE_NAME,\n           READ_QUERY_COUNT\n         FROM SPANNER_SYS.TABLE_OPERATIONS_STATS_MINUTE'\n```\n\n```sh\ngo run ./cmd/spanner-analyzer \\\n  --sql 'SELECT\n           SPANNER_SYS.DISTRIBUTION_PERCENTILE(LATENCY_DISTRIBUTION[OFFSET(0)], 99.0) AS p99\n         FROM SPANNER_SYS.QUERY_STATS_TOTAL_10MINUTE'\n```\n\nThe Spanner lock statistics documentation uses a join between transaction and\nlock statistics tables. The same shape can be analyzed without DDL:\n\n```sh\ngo run ./cmd/spanner-analyzer \\\n  --sql 'SELECT\n           t.INTERVAL_END,\n           t.AVG_COMMIT_LATENCY_SECONDS,\n           l.TOTAL_LOCK_WAIT_SECONDS\n         FROM SPANNER_SYS.TXN_STATS_TOTAL_10MINUTE AS t\n         LEFT JOIN SPANNER_SYS.LOCK_STATS_TOTAL_10MINUTE AS l\n           ON t.INTERVAL_END = l.INTERVAL_END\n         ORDER BY t.INTERVAL_END'\n```\n\nThe CLI also exposes selected GoogleSQL analyzer options from\n`execute_query_tool`, including `--product-mode`, `--strict-name-resolution`,\n`--fold-literal-cast`, `--prune-unused-columns`, and\n`--parse-location-record-type`. The default `--product-mode` is `external`,\nmatching Cloud Spanner's public GoogleSQL dialect. `--mode=spanner_type` emits\nthe Cloud Spanner type protobuf as YAML by default. YAML output is produced by\nconverting the `protojson` result with\n[`github.com/goccy/go-yaml`](https://github.com/goccy/go-yaml). Use\n`--output json` or `--output textproto` to emit another protobuf format.\n\n```sh\ngo run ./cmd/spanner-analyzer \\\n  --sql-mode expression \\\n  --sql '1'\n```\n\nOutput:\n\n```yaml\ncode: INT64\n```\n\nJSON output is still available:\n\n```sh\ngo run ./cmd/spanner-analyzer \\\n  --sql-mode expression \\\n  --sql '1' \\\n  --output json\n```\n\nOutput:\n\n```json\n{\n  \"code\": \"INT64\"\n}\n```\n\n`--mode=go_struct` emits Go code for a struct that can receive query result\nrows. Use `--go-client=bigquery`, `--go-client=spanner`, or\n`--go-client=both` to choose struct tags and field types. The default is\n`both`, which emits both `bigquery` and `spanner` tags. In `both` mode the\ngenerator keeps one field per result column and emits a small `NullValue[T]`\nhelper so the same DTO can be loaded from BigQuery with `bigquery.ValueLoader`\nand from Spanner with `spanner.Decoder`.\n\n```sh\ngo run ./cmd/spanner-analyzer \\\n  --mode go_struct \\\n  --sql 'SELECT 1 AS n'\n```\n\nOutput excerpt:\n\n```go\npackage main\n\nimport (\n\t\"cloud.google.com/go/bigquery\"\n\t\"fmt\"\n)\n\ntype QueryRow struct {\n\tN NullValue[int64] `bigquery:\"n\" spanner:\"n\"`\n}\n\nfunc (r *QueryRow) Load(values []bigquery.Value, schema bigquery.Schema) error {\n\tif len(values) != len(schema) {\n\t\treturn fmt.Errorf(\"bigquery row has %d values for %d schema fields\", len(values), len(schema))\n\t}\n\tfor i, field := range schema {\n\t\tswitch field.Name {\n\t\tcase \"n\":\n\t\t\tif err := r.N.LoadBigQuery(values[i]); err != nil {\n\t\t\t\treturn fmt.Errorf(\"n: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\ntype NullValue[T any] struct {\n\tValue T\n\tValid bool\n}\n\nfunc (n NullValue[T]) IsNull() bool {\n\treturn !n.Valid\n}\n\nfunc (n *NullValue[T]) LoadBigQuery(value bigquery.Value) error {\n\treturn n.set(value)\n}\n\nfunc (n *NullValue[T]) DecodeSpanner(input interface{}) error {\n\treturn n.set(input)\n}\n\nfunc (n *NullValue[T]) set(value interface{}) error {\n\tif value == nil {\n\t\tvar zero T\n\t\tn.Value = zero\n\t\tn.Valid = false\n\t\treturn nil\n\t}\n\ttyped, ok := value.(T)\n\tif !ok {\n\t\treturn fmt.Errorf(\"cannot decode %T\", value)\n\t}\n\tn.Value = typed\n\tn.Valid = true\n\treturn nil\n}\n```\n\nBigQuery mode is also available. It analyzes BigQuery GoogleSQL queries against\n[BigQuery DDL](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language)\nand emits a BigQuery REST\n[`TableSchema`](https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#TableSchema)\nshaped result. The default mode for `--dialect=bigquery` is\n`--mode=bigquery_type`.\n\n```sh\ngo run ./cmd/spanner-analyzer \\\n  --dialect bigquery \\\n  --sql 'SELECT 1 AS n, [\"a\", \"b\"] AS tags'\n```\n\nOutput:\n\n```yaml\nfields:\n- name: \"n\"\n  type: INTEGER\n  mode: NULLABLE\n- name: tags\n  type: STRING\n  mode: REPEATED\n```\n\nBigQuery and Spanner use different GoogleSQL feature sets. For example,\nBigQuery mode accepts pipe query syntax, while the default Spanner mode rejects\nit.\n\n```sh\ngo run ./cmd/spanner-analyzer \\\n  --dialect bigquery \\\n  --sql 'FROM UNNEST([STRUCT(\"apples\" AS item, 2 AS sales, \"fruit\" AS category),\n                      STRUCT(\"carrots\", 8, \"vegetable\"),\n                      STRUCT(\"apples\", 7, \"fruit\")]) AS produce\n         |> WHERE category = \"fruit\"\n         |> AGGREGATE COUNT(*) AS num_items, SUM(sales) AS total_sales\n            GROUP BY item\n         |> ORDER BY item'\n```\n\nOutput:\n\n```yaml\nfields:\n- name: item\n  type: STRING\n  mode: NULLABLE\n- name: num_items\n  type: INTEGER\n  mode: NULLABLE\n- name: total_sales\n  type: INTEGER\n  mode: NULLABLE\n```\n\nBigQuery DDL is analyzed by the GoogleSQL frontend, so BigQuery table schemas\ncan use nested `STRUCT`, repeated `ARRAY`, `JSON`, `BIGNUMERIC`, and\n`RANGE<DATE|DATETIME|TIMESTAMP>` types.\n\n```sh\ngo run ./cmd/spanner-analyzer \\\n  --dialect bigquery \\\n  --ddl bigquery-schema.sql \\\n  --sql 'SELECT customer_id, profile, events FROM mydataset.customers'\n```\n\nFor\n[BigQuery-to-Spanner federated queries](https://cloud.google.com/bigquery/docs/spanner-federated-queries)\nthat use `EXTERNAL_QUERY`, pass BigQuery DDL and Spanner DDL for each BigQuery\nconnection ID. The analyzer provides a `EXTERNAL_QUERY` table-valued function\nthat returns the row type inferred from the inner Spanner query using the\nconnection-specific Spanner catalog.\n\nOnly the two-argument form `EXTERNAL_QUERY(connection, sql)` is analyzed. The\noptional third options argument is a hard error\n(`EXTERNAL_QUERY options argument is currently not supported for static\nanalysis`), because the analyzer cannot evaluate connection options\nstatically; remove it from the SQL under analysis.\n\n```sh\ngo run ./cmd/spanner-analyzer \\\n  --dialect bigquery \\\n  --ddl bigquery-schema.sql \\\n  --external-ddl my-project.us.example-db=spanner-schema.sql \\\n  --sql \"SELECT c.customer_id, rq.first_order_date\n         FROM mydataset.customers AS c\n         LEFT JOIN EXTERNAL_QUERY(\n           'my-project.us.example-db',\n           '''SELECT CustomerId AS customer_id, MIN(OrderDate) AS first_order_date\n              FROM Orders\n              GROUP BY CustomerId''') AS rq\n           ON rq.customer_id = c.customer_id\"\n```\n\nIf the Spanner schema uses `PROTO BUNDLE`, provide descriptor sets for the same\nconnection. Proto fields are available while analyzing that connection's inner\nSpanner SQL, but top-level `PROTO` values still cannot be returned through\nBigQuery `EXTERNAL_QUERY`.\n\n```sh\ngo run ./cmd/spanner-analyzer \\\n  --dialect bigquery \\\n  --external-ddl example-project.asia-northeast1.example-connection=testdata/order-proto-schema.sql \\\n  --external-proto-descriptors-file example-project.asia-northeast1.example-connection=testdata/protos/order_descriptors.pb \\\n  --sql \"SELECT * FROM EXTERNAL_QUERY(\n           'example-project.asia-northeast1.example-connection',\n           '''SELECT OrderInfo.order_number, OrderInfo.shipping_address.city FROM Orders''')\"\n```\n\nOutput:\n\n```yaml\nfields:\n- name: order_number\n  type: STRING\n  mode: NULLABLE\n- name: city\n  type: STRING\n  mode: NULLABLE\n```\n\n`--mode` is inspired by GoogleSQL\n[`execute_query`](https://github.com/google/googlesql/blob/master/execute_query.md)\nmodes. The default `--mode=spanner_type` returns the Cloud Spanner row type for\nquery mode, or a single Cloud Spanner type for expression mode. With\n`--dialect=bigquery`, the default `--mode=bigquery_type` returns a BigQuery\n`TableSchema` shaped schema. `--mode=parse` prints the parser AST,\n`--mode=analyze` prints the resolved AST debug string like GoogleSQL\n`execute_query` analyze mode, `--mode=unparse` prints parser AST converted\nback to SQL, and `--mode=go_struct` prints Go result struct code. Modes can be\ncomma-separated, for example `--mode=parse,analyze,spanner_type`.\n\nGoogleSQL is initialized once per process through `go-googlesql`.\n\nDialect feature presets start from GoogleSQL\n`EnableMaximumLanguageFeaturesForDevelopment()` and then disable features that\nare not available in the selected dialect. Use\n`--enable-maximum-development-language-features` to skip that blacklist and try\nraw development features, for example when validating a newly released Spanner\nfeature before this project has updated its preset.\n\n## Library components\n\nThe public API is intentionally split into composable steps:\n\n- `BuildSchemaCatalog` parses Spanner DDL into this project's Spanner schema\n  catalog.\n- `BuildGoogleSQLCatalogFromSpannerCatalog` and `BuildGoogleSQLCatalogFromDDL`\n  convert that schema into a GoogleSQL frontend catalog, analyzer options, and\n  type factory.\n- `BuildBigQueryGoogleSQLCatalogFromDDL` converts BigQuery DDL into a\n  GoogleSQL frontend catalog.\n- `GoogleSQLHelper` wraps parse, analyze, unparse, and resolved AST debug\n  operations against that catalog.\n- `RowTypeFromAnalyzerOutput`, `RowTypeFromResolvedQuery`, and\n  `TypeFromAnalyzerOutput` convert GoogleSQL analyzer results into Cloud\n  Spanner protobuf metadata.\n- `BigQueryTableSchemaFromAnalyzerOutput`,\n  `BigQueryTableSchemaFromResolvedQuery`, and\n  `BigQueryTableFieldSchemaFromGoogleSQLType` convert GoogleSQL analyzer types\n  into BigQuery REST `TableSchema` shaped metadata.\n- `Analyzer` remains a convenience wrapper that wires these components together\n  for the CLI-style row type use case.\n- `BigQueryAnalyzer` does the same for BigQuery `TableSchema` output.\n  Connection-specific Spanner analyzers can be attached with\n  `SetExternalQueryAnalyzers` to infer `EXTERNAL_QUERY` result schemas.\n\n## License\n\nThis project is licensed under the Apache License 2.0.\n\nThe source distribution does not vendor\n[`github.com/goccy/go-googlesql`](https://github.com/goccy/go-googlesql) or its\nembedded `googlesql.wasm` artifact. Binary distributions built from this\nproject do include that dependency transitively, so distributors should include\nthe relevant third-party license notices for at least:\n\n- [`github.com/goccy/go-googlesql`](https://github.com/goccy/go-googlesql) and\n  [`github.com/goccy/googlesql-wasm`](https://github.com/goccy/googlesql-wasm),\n  which are MIT licensed.\n- [`github.com/google/googlesql`](https://github.com/google/googlesql), the\n  GoogleSQL frontend embedded in that WASM artifact, which is Apache-2.0\n  licensed.\n\nIf future releases vendor dependencies or attach compiled binaries, add the\ncorresponding third-party license and NOTICE material to those release\nartifacts.\n\n## Limitations\n\n- `PROTO BUNDLE` support requires descriptor set files. DDL alone is not enough\n  to analyze proto fields.\n- Cloud Spanner and the\n  [Cloud Spanner emulator](https://github.com/GoogleCloudPlatform/cloud-spanner-emulator)\n  use the GoogleSQL frontend's native `MakeProtoType` and `MakeEnumType` APIs\n  with descriptors from the active proto bundle. This project now loads the\n  supplied descriptor set into the GoogleSQL frontend descriptor pool and uses\n  those native proto and enum types when building the analyzer catalog.\n- Proto and enum query outputs are converted back to Spanner row metadata when\n  possible, including nested proto fields selected as values.\n- Property graph DDL registers node and edge tables, labels, and direct column\n  property definitions through the `go-googlesql` `SimpleGraph*` constructors.\n  More advanced Spanner graph metadata, including arbitrary\n  property expressions and dynamic labels/properties, is still limited.\n- Some Spanner-specific functions are registered locally because they are not\n  included in the default `go-googlesql` builtin function set. This\n  includes commit timestamp, sequence, search, TOKENLIST, and AI helper\n  functions needed for query analysis.\n- `ML.PREDICT` is registered as an analyzer-only table-valued function. It\n  models schema only, returns model output columns followed by non-duplicated\n  input relation columns, and does not execute prediction logic. It supports\n  `ML.PREDICT`, `PREDICT`, and `SAFE.ML.PREDICT` names.\n- `TOKENLIST` is supported as an internal analysis type for search expressions,\n  but Cloud Spanner result sets cannot return `TOKENLIST`, so the Cloud Spanner\n  protobuf API has no `TypeCode` for it.\n- Named arguments for locally registered Spanner functions are normalized before\n  analysis because the current Go binding does not expose the GoogleSQL\n  frontend's argument name setters.\n- BigQuery mode currently registers ordinary BigQuery tables and views from DDL\n  but does not model every DDL side effect. Dataset DDL, indexes, and drops are\n  ignored because they do not change query result types in the current catalog\n  model.\n- BigQuery `bigquery_type` output is derived from resolved GoogleSQL types. It\n  can represent repeated and nested fields, but query result nullability is not\n  tracked, so non-repeated query output fields are emitted as `NULLABLE`.\n- BigQuery-to-Spanner federated queries use the `EXTERNAL_QUERY` table-valued\n  function. The current implementation identifies `EXTERNAL_QUERY` calls by\n  literal connection and SQL arguments and delegates row type inference to the\n  matching Spanner analyzer. It does not evaluate connection options,\n  permissions, PostgreSQL-dialect Spanner SQL, or non-literal dynamic SQL\n  expressions.\n",
  "bytes": 26206,
  "sha": "de87d6fba81cd72e91c297d725b410c4da2994e8ccc9504b958d93e5bf9c84b2",
  "repo_slug": "apstndb/spanalyzer",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/okf_apstndb_spanalyzer_knowledge_index_md_1babd1cb/readme"
}