diff --git a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.CommonTesting/TestProtos/Fantasy.cs b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.CommonTesting/TestProtos/Fantasy.cs
new file mode 100644
index 000000000000..6df8a801164e
--- /dev/null
+++ b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.CommonTesting/TestProtos/Fantasy.cs
@@ -0,0 +1,51 @@
+//
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: fantasy.proto
+//
+#pragma warning disable 1591, 0612, 3021, 8981
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Fantasy {
+
+ /// Holder for reflection information generated from fantasy.proto
+ public static partial class FantasyReflection {
+
+ #region Descriptor
+ /// File descriptor for fantasy.proto
+ public static pbr::FileDescriptor Descriptor {
+ get { return descriptor; }
+ }
+ private static pbr::FileDescriptor descriptor;
+
+ static FantasyReflection() {
+ byte[] descriptorData = global::System.Convert.FromBase64String(
+ string.Concat(
+ "Cg1mYW50YXN5LnByb3RvEgdmYW50YXN5Kl8KDkNoYXJhY3RlckNsYXNzEh8K",
+ "G0NIQVJBQ1RFUl9DTEFTU19VTlNQRUNJRklFRBAAEgsKB1dBUlJJT1IQARII",
+ "CgRNQUdFEAISCQoFUk9HVUUQAxIKCgZDTEVSSUMQBEIKqgIHRmFudGFzeWIG",
+ "cHJvdG8z"));
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+ new pbr::FileDescriptor[] { },
+ new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Fantasy.CharacterClass), }, null, null));
+ }
+ #endregion
+
+ }
+ #region Enums
+ public enum CharacterClass {
+ [pbr::OriginalName("CHARACTER_CLASS_UNSPECIFIED")] Unspecified = 0,
+ [pbr::OriginalName("WARRIOR")] Warrior = 1,
+ [pbr::OriginalName("MAGE")] Mage = 2,
+ [pbr::OriginalName("ROGUE")] Rogue = 3,
+ [pbr::OriginalName("CLERIC")] Cleric = 4,
+ }
+
+ #endregion
+
+}
+
+#endregion Designer generated code
diff --git a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.CommonTesting/TestProtos/fantasy.proto b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.CommonTesting/TestProtos/fantasy.proto
new file mode 100644
index 000000000000..ba77127ca511
--- /dev/null
+++ b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.CommonTesting/TestProtos/fantasy.proto
@@ -0,0 +1,13 @@
+syntax = "proto3";
+
+package fantasy;
+
+option csharp_namespace = "Fantasy";
+
+enum CharacterClass {
+ CHARACTER_CLASS_UNSPECIFIED = 0;
+ WARRIOR = 1;
+ MAGE = 2;
+ ROGUE = 3;
+ CLERIC = 4;
+}
diff --git a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/KeysTests.cs b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/KeysTests.cs
index 275a90e807ee..ee18b328ff93 100644
--- a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/KeysTests.cs
+++ b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/KeysTests.cs
@@ -41,7 +41,8 @@ public void CreateKeyFromParameterCollection()
{ "", SpannerDbType.PgOid, 2 },
{ "", SpannerDbType.String, "test" },
{ "", SpannerDbType.Timestamp, new DateTime(2021, 9, 10, 9, 37, 10, DateTimeKind.Utc) },
- { "", SpannerDbType.Uuid, Guid.Parse("8f8c4746-17b1-4d9f-a634-58e11942095f") }
+ { "", SpannerDbType.Uuid, Guid.Parse("8f8c4746-17b1-4d9f-a634-58e11942095f") },
+ { "", SpannerDbType.Enum, Fantasy.CharacterClass.Warrior },
});
var actual = key.ToProtobuf(SpannerConversionOptions.Default);
@@ -63,7 +64,8 @@ public void CreateKeyFromParameterCollection()
Value.ForString("2"),
Value.ForString("test"),
Value.ForString("2021-09-10T09:37:10Z"),
- Value.ForString("8f8c4746-17b1-4d9f-a634-58e11942095f")
+ Value.ForString("8f8c4746-17b1-4d9f-a634-58e11942095f"),
+ Value.ForString("1")
}
};
Assert.Equal(expected, actual);
diff --git a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDataReader.cs b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDataReader.cs
index c117d6f868fe..a85f351544a3 100644
--- a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDataReader.cs
+++ b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDataReader.cs
@@ -250,6 +250,14 @@ public override long GetBytes(int ordinal, long fieldOffset, byte[] buffer, int
/// The value converted to a .
public SpannerDate GetSpannerDate(int i) => GetFieldValue(i);
+ ///
+ /// Gets the value of the specified column as a System.Enum.
+ ///
+ /// Enum type
+ /// The index of the column to retrieve.
+ /// The value converted to an enum, if defined.
+ public T GetEnum(int i) where T : System.Enum => GetFieldValue(i);
+
///
public override object GetValue(int i) => this[i];
diff --git a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDbType.ValueConversion.cs b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDbType.ValueConversion.cs
index 7ab24c9a2514..368b05323759 100644
--- a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDbType.ValueConversion.cs
+++ b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDbType.ValueConversion.cs
@@ -110,6 +110,11 @@ internal object ConvertToClrType(Value protobufValue, System.Type targetClrType,
return Guid.Parse(ConvertToClrTypeImpl(protobufValue, options));
}
+ if (targetClrType.IsEnum)
+ {
+ return System.Enum.ToObject(targetClrType, ConvertToClrTypeImpl(protobufValue, options));
+ }
+
return ConvertToClrTypeImpl(protobufValue, targetClrType, options);
}
@@ -306,6 +311,20 @@ internal Value ToProtobufValue(object value)
return Value.ForString(V1.Interval.Parse(stringValue).ToString());
}
throw new ArgumentException($"Interval parameters must be of type {typeof(Interval).FullName} or string");
+ case TypeCode.Enum:
+ if (value is string enumStr)
+ {
+ return Value.ForString(enumStr);
+ }
+ if (value is System.Enum or sbyte or short or int or long)
+ {
+ return Value.ForString(Convert.ToInt64(value, InvariantCulture).ToString(InvariantCulture));
+ }
+ if (value is byte or ushort or uint or ulong)
+ {
+ return Value.ForString(Convert.ToUInt64(value, InvariantCulture).ToString(InvariantCulture));
+ }
+ throw new ArgumentException($"Enum parameters must be of one of the following types: System.Enum, string, sbyte, short, int, long, byte, ushort, uint, ulong");
default:
throw new ArgumentOutOfRangeException(nameof(TypeCode), TypeCode, null);
}
diff --git a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDbType.cs b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDbType.cs
index 824a0e80c738..5f3658a16ed6 100644
--- a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDbType.cs
+++ b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerDbType.cs
@@ -111,6 +111,11 @@ public sealed partial class SpannerDbType
///
public static SpannerDbType Uuid { get; } = new SpannerDbType(TypeCode.Uuid);
+ ///
+ /// Representation of Spanner Protobuf Enum type.
+ ///
+ public static SpannerDbType Enum { get; } = new SpannerDbType(TypeCode.Enum);
+
private static readonly Dictionary s_simpleTypes
= new Dictionary
{
@@ -315,6 +320,10 @@ internal static SpannerDbType FromProtobufType(V1.Type type)
return new SpannerDbType(type.StructType.Fields.Select(f => new StructField(f.Name, FromProtobufType(f.Type))).ToList());
case TypeCode.Proto:
return new SpannerDbType(type.ProtoTypeFqn);
+ case TypeCode.Enum:
+ // This is handled here because equality logic prevent enum type working in s_simpleTypes,
+ // but we also don't want to carry arround the proto FQN since we dont use it
+ return Enum;
default:
return FromType(type);
}
diff --git a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerParameter.cs b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerParameter.cs
index 1cd188077355..9ef368b676c5 100644
--- a/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerParameter.cs
+++ b/apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerParameter.cs
@@ -150,7 +150,8 @@ internal object GetValidatedValue()
+ $"({nameof(SpannerDbType.Bool)}, {nameof(SpannerDbType.Int64)}, {nameof(SpannerDbType.Float32)} ,{nameof(SpannerDbType.Float64)},"
+ $" {nameof(SpannerDbType.Timestamp)}, {nameof(SpannerDbType.Date)}, {nameof(SpannerDbType.String)},"
+ $" {nameof(SpannerDbType.Bytes)}, {nameof(SpannerDbType.Json)}, {nameof(SpannerDbType.PgJsonb)}, {nameof(SpannerDbType.Numeric)},"
- + $" {nameof(SpannerDbType.PgNumeric)}, {nameof(SpannerDbType.PgOid)}), {nameof(SpannerDbType.Interval)}, {nameof(SpannerDbType.Uuid)}");
+ + $" {nameof(SpannerDbType.PgNumeric)}, {nameof(SpannerDbType.PgOid)}), {nameof(SpannerDbType.Interval)}, {nameof(SpannerDbType.Uuid)},"
+ + $" {nameof(SpannerDbType.Enum)}");
}
return Value;
}