Skip to content

feat(sf_core): expose typed GetObjects metadata - #1339

Open
zeroshade wants to merge 2 commits into
snowflakedb:mainfrom
zeroshade:feat/sf-core-typed-metadata-tree
Open

zeroshade wants to merge 2 commits into
snowflakedb:mainfrom
zeroshade:feat/sf-core-typed-metadata-tree

Conversation

@zeroshade

Copy link
Copy Markdown
Contributor

Summary

  • add an owned, depth-aware GetObjects metadata tree to sf_core
  • expose DatabaseDriverV1::connection_get_objects_typed alongside the existing Arrow result-set API
  • make the existing connection_get_objects path encode its Arrow batch from the same typed tree, avoiding duplicate metadata-fetch logic
  • preserve existing catalog ordering and Arrow null/empty behavior at every GetObjects depth

API

The tree is represented by:

  • CatalogMetadata
  • DbSchemaMetadata
  • TableMetadata
  • ColumnDescriptor

Optional child vectors distinguish a depth cutoff (None) from a requested level with no matches (Some(Vec::new())).

Testing

  • cargo test -p sf_core --lib get_objects::tests — 41 passed
  • cargo test -p sf_core --lib — 1697 passed, 1 ignored
  • cargo check -p sf_core --all-targets
  • cargo clippy -p sf_core --lib
  • cargo doc -p sf_core --no-deps
  • cargo fmt --all -- --check
  • git diff --check

Copilot AI lite review requested due to automatic review settings August 11, 2026 21:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a typed, owned GetObjects metadata tree in sf_core and adds a new DatabaseDriverV1::connection_get_objects_typed API, while refactoring the existing connection_get_objects Arrow result-set path to reuse the same underlying metadata-fetching logic to avoid duplication and preserve existing ordering/null/empty semantics.

Changes:

  • Adds typed metadata tree structs (CatalogMetadata, DbSchemaMetadata, TableMetadata) and exposes them via the database_driver_v1 module exports.
  • Introduces DatabaseDriverV1::connection_get_objects_typed and refactors the legacy Arrow connection_get_objects to build batches from the typed tree.
  • Adds unit tests validating ordering and null/empty behavior when converting typed metadata to Arrow batches.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
sf_core/src/apis/database_driver_v1/mod.rs Re-exports new typed metadata structs so they’re available to API consumers.
sf_core/src/apis/database_driver_v1/get_objects.rs Implements typed metadata tree, new typed API entrypoint, refactors Arrow path to reuse typed tree, and adds tests for behavioral parity.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread sf_core/src/apis/database_driver_v1/get_objects.rs
Comment thread sf_core/src/apis/database_driver_v1/get_objects.rs Outdated
Copilot AI review requested due to automatic review settings August 11, 2026 22:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Suppressed comments (1)

sf_core/src/apis/database_driver_v1/get_objects.rs:240

  • The doc comment here says “A None child collection means the requested depth stopped at its parent”, but only CatalogMetadata::db_schemas and DbSchemaMetadata::tables are optional. TableMetadata::columns is always a Vec, so callers can’t interpret None/Some(Vec::new()) semantics for columns from this documentation as written. Please clarify that the None/empty distinction applies to the optional child vectors only.
    /// Unlike [`Self::connection_get_objects`], this API does not encode the
    /// metadata into an Arrow result-set handle. A `None` child collection
    /// means the requested depth stopped at its parent; `Some(Vec::new())`
    /// means that level was requested but matched no objects.

depth: i32,
) -> Result<RecordBatch, ApiError> {
match depth {
DEPTH_CATALOGS => build_catalogs_batch(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why can't build_catalogs_batch and analogous methods work on constructed metadata tree instead of requiring to parse it to non-typed strings?

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CatalogMetadata {
pub catalog_name: String,
pub db_schemas: Option<Vec<DbSchemaMetadata>>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there doesn't seem to be any difference in handling None vs handling empty vector here - maybe we can equate the two and drop the option whatsoever

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DbSchemaMetadata {
pub db_schema_name: String,
pub tables: Option<Vec<TableMetadata>>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there doesn't seem to be any difference in handling None vs handling empty vector here - maybe we can equate the two and drop the option whatsoever

/// `None` at column depth because `SHOW COLUMNS` does not return table type.
pub table_type: Option<String>,
/// Columns are empty when the requested depth stops at tables.
pub columns: Vec<ColumnDescriptor>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there doesn't seem to be any difference in handling None vs handling empty vector here - maybe we can equate the two and drop the option whatsoever

pub table_name: String,
/// `Some("TABLE")` or `Some("VIEW")` when table metadata was requested;
/// `None` at column depth because `SHOW COLUMNS` does not return table type.
pub table_type: Option<String>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add concrete enum to represent Table or View variants.

/// metadata into an Arrow result-set handle. A `None` child collection
/// means the requested depth stopped at its parent; `Some(Vec::new())`
/// means that level was requested but matched no objects.
pub async fn connection_get_objects_typed(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new public api method has zero test coverage

snowflake-copybara Bot pushed a commit that referenced this pull request Sep 5, 2026
## Stack

SNOW-2912540 typed session parameters, merge order:

1. [#1339](https://github.com/snowflake-eng/drivers/pull/1339) proto +
`sf_core` (merged)
2. [#1340](https://github.com/snowflake-eng/drivers/pull/1340) Python —
typed `SessionParametersProxy` (base of #1341)
3. [#1341](https://github.com/snowflake-eng/drivers/pull/1341) JDBC —
typed `ParametersRegistry` (**this PR's base**)
4. **This PR** — ODBC typed `ConfigSetting` reads
5. [#1629](https://github.com/snowflake-eng/drivers/pull/1629) drop the
deprecated string wire fields (stacked on this branch)

## Summary

- The independent session-parameter read sites (autocommit,
decimal-as-int, big-number-as-string, max-varchar-size, array-bind
threshold, metadata-context bool) plus the TZ-offset-format cache now
read `ConnectionGetParameterResponse.typed_value` (`ConfigSetting`)
instead of parsing a re-derived dispring.
- Three shared helpers (`config_setting_bool` / `config_setting_u64` /
`config_setting_string`) live in `api::utils` so those call sites do not
each duplicate the oneof match. Native variants are preferred;
`string_value` remains a fallback. Non-string variants still collapse to
`None` for string-typed parameters such as `TIMESTAMP_TZ_OUTPUT_FORMAT`.
- Not doing the full `ParametersRegistry`-style consolidation (dedup of
the near-identical RPC-fetch helpers across `connection.rs` /
`statement.rs` / `catalog.rs`). That is independent of the typing fix.

Merge-resolution restores after catching up to #1341 (from
[#1656](https://github.com/snowflake-eng/drivers/pull/1656)):

- `select_binding_mode` again requires `threshold > 0` before CSV/stage
binding, so `CLIENT_STAGE_ARRAY_BINDING_THRESHOLD=0` stays on inline
JSON (same as Python).
- `SQLGetTypeInfo` IRD schema again tags string columns as
`SQL_WVARCHAR` and `INTERVAL_PRECISION` as `SQL_SMALLINT`, with the
corresponding `type_info_tests` restored.

## Test plan

- [x] `cargo test -p odbc --lib` — helpers plus restored
`type_info_tests` (`string_columns_are_tagged_wvarchar`,
`interval_precision_is_smallint_num_prec_radix_is_integer`)
- [x] `cargo clippy -p odbc` / `cargo fmt -p odbc` on changed files
- [ ] CI `odbc_tests` on this branch after the merge-fix (the three e2e
cases that failed on the pre-fix run: all-NULL row with threshold 0;
GetTypeInfo WVARCHAR / SMALLINT IRD)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Filip Pawłowski <sfc-gh-fpawlowski@users.noreply.github.com>
GitOrigin-RevId: 51ab345
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants