Skip to content

feat(spanner): add uuid type support#422

Open
devgony wants to merge 4 commits intoyoshidan:mainfrom
devgony:support-uuid
Open

feat(spanner): add uuid type support#422
devgony wants to merge 4 commits intoyoshidan:mainfrom
devgony:support-uuid

Conversation

@devgony
Copy link

@devgony devgony commented Jan 24, 2026

Summary

  • Add optional uuid feature to support reading/writing UUID types from Spanner
  • The emulator supports UUID natively since v1.5.46 (Dec 2024), but the Rust SDK lacked TryFromValue implementation
  • Add Row::field() and Row::field_by_name() getters to expose column metadata

Changes

UUID Support

  • Add uuid crate as optional dependency with uuid feature flag
  • Implement TryFromValue for uuid::Uuid (parse from StringValue with TypeCode validation)
  • Implement ToKind for uuid::Uuid (serialize to StringValue with TypeCode::Uuid)
  • Add unit tests for both serialization and deserialization

Field Metadata Access

  • Add Row::field(column_index) → returns Option<&Field>
  • Add Row::field_by_name(column_name) → returns Option<&Field>

Usage

UUID

[dependencies]
google-cloud-spanner = { package="gcloud-spanner", version="1.6.1", features = ["uuid"] }
// Reading UUID from Spanner
let uuid: uuid::Uuid = row.column_by_name("uuid_col")?;

// Writing UUID to Spanner
stmt.add_param("id", &uuid);

Field Metadata (for ORM/dynamic type handling)

use google_cloud_googleapis::spanner::v1::TypeCode;

let field = row.field(idx).unwrap();
let type_code = field.r#type.as_ref().map(|t| t.code).unwrap_or(0);

match type_code {
    x if x == TypeCode::Bytes as i32 => {
        let bytes: Vec<u8> = row.column(idx)?;
    }
    x if x == TypeCode::String as i32 => {
        let s: String = row.column(idx)?;
    }
    x if x == TypeCode::Array as i32 => {
        // Check element type
        let elem_code = field.r#type.as_ref()
            .and_then(|t| t.array_element_type.as_deref())
            .map(|t| t.code);
        // Handle based on element type
    }
    x if x == TypeCode::Json as i32 => {
        let json: serde_json::Value = row.column(idx)?;
    }
    _ => { /* ... */ }
}

Why these changes?

Why StringValue for UUID?

Spanner gRPC protocol uses google.protobuf.Value which has no UUID-specific kind. All complex types (INT64, NUMERIC, BYTES, DATE, UUID, etc.) are encoded as string_value. The TypeCode::Uuid is schema metadata, not wire format.

Why TypeCode validation in UUID?

Without validation, attempting to read a UUID from a non-UUID column (e.g., STRING) would try to parse any string as UUID, causing confusing errors. Now it fails fast with a clear type mismatch error.

Why Field getters?

Several Spanner types share the same wire format (e.g., BYTES and STRING both use StringValue). ORM layers need to inspect TypeCode to determine the correct Rust type. The field getters expose this metadata without breaking the existing API.

Add optional uuid feature to support reading/writing UUID types
from Spanner. The emulator supports UUID since v1.5.46, but the
Rust SDK lacked TryFromValue implementation.

- Add uuid crate as optional dependency with 'uuid' feature flag
- Implement TryFromValue for uuid::Uuid (parse from StringValue)
- Implement ToKind for uuid::Uuid (serialize to StringValue with TypeCode::Uuid)
- Add tests for both serialization and deserialization
Prevent UUID parsing from being attempted on non-UUID columns.
Now TryFromValue for uuid::Uuid checks TypeCode::Uuid before parsing,
returning a clear error if the column type doesn't match.
Expose Field metadata to allow callers to inspect TypeCode before
reading values. Useful for:
- Distinguishing BYTES vs STRING (both use StringValue)
- Checking array element types
- Validating JSON column types
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant