internal static StatementResult CreateSingleColumnResultSet(V1.Type type, string col, params object[] values)
        {
            ResultSet rs = new ResultSet
            {
                Metadata = new ResultSetMetadata
                {
                    RowType = new StructType()
                },
            };

            rs.Metadata.RowType.Fields.Add(new StructType.Types.Field
            {
                Name = col,
                Type = type,
            });
            foreach (object val in values)
            {
                ListValue row = new ListValue();
                row.Values.Add(SpannerConverter.ToProtobufValue(type, val));
                rs.Rows.Add(row);
            }
            return(CreateQuery(rs));
        }
Пример #2
0
        internal static Value ToProtobufValue(V1.Type type, object value)
        {
            if (value == null || value is DBNull)
            {
                return(Value.ForNull());
            }

            switch (type.Code)
            {
            case TypeCode.Bytes:
                if (value is string s)
                {
                    return(new Value {
                        StringValue = s
                    });
                }
                if (value is byte[] bArray)
                {
                    return(new Value {
                        StringValue = Convert.ToBase64String(bArray)
                    });
                }
                throw new ArgumentException("TypeCode.Bytes only supports string and byte[]", nameof(value));

            case TypeCode.Bool:
                return(new Value {
                    BoolValue = Convert.ToBoolean(value)
                });

            case TypeCode.String:
                if (value is DateTime dateTime)
                {
                    // If the value is a DateTime, we always convert using XmlConvert.
                    // This allows us to convert back to a datetime reliably from the
                    // resulting string (so roundtrip works properly if the developer uses
                    // a string as a backing field for a datetime for whatever reason).
                    return(new Value {
                        StringValue = XmlConvert.ToString(dateTime, XmlDateTimeSerializationMode.Utc)
                    });
                }
                // All the other conversions will fail naturally, but let's make sure we don't convert structs
                // to strings.
                if (value is SpannerStruct)
                {
                    throw new ArgumentException("SpannerStruct cannot be used for string parameters", nameof(value));
                }
                return(new Value {
                    StringValue = Convert.ToString(value, InvariantCulture)
                });

            case TypeCode.Int64:
                return(new Value
                {
                    StringValue = Convert.ToInt64(value, InvariantCulture)
                                  .ToString(InvariantCulture)
                });

            case TypeCode.Float64:
                return(new Value {
                    NumberValue = Convert.ToDouble(value, InvariantCulture)
                });

            case TypeCode.Timestamp:
                return(new Value
                {
                    StringValue = XmlConvert.ToString(Convert.ToDateTime(value, InvariantCulture), XmlDateTimeSerializationMode.Utc)
                });

            case TypeCode.Date:
                return(new Value
                {
                    StringValue = StripTimePart(
                        XmlConvert.ToString(Convert.ToDateTime(value, InvariantCulture), XmlDateTimeSerializationMode.Utc))
                });

            case TypeCode.Array:
                if (value is IEnumerable enumerable)
                {
                    return(Value.ForList(
                               enumerable.Cast <object>()
                               .Select(x => ToProtobufValue(type.ArrayElementType, x)).ToArray()));
                }
                throw new ArgumentException("The given array instance needs to implement IEnumerable.");

            case TypeCode.Struct:
                throw new ArgumentException("Struct values are not supported");

            case TypeCode.Numeric:
                if (value is SpannerNumeric spannerNumeric)
                {
                    return(Value.ForString(spannerNumeric.ToString()));
                }
                if (value is string str)
                {
                    return(Value.ForString(SpannerNumeric.Parse(str).ToString()));
                }
                if (value is float || value is double || value is decimal)
                {
                    // We throw if there's a loss of precision. We could use
                    // LossOfPrecisionHandling.Truncate but GoogleSQL documentation requests to
                    // use half-away-from-zero rounding but the SpannerNumeric implementation
                    // truncates instead.
                    return(Value.ForString(SpannerNumeric.FromDecimal(
                                               Convert.ToDecimal(value, InvariantCulture), LossOfPrecisionHandling.Throw).ToString()));
                }
                if (value is sbyte || value is short || value is int || value is long)
                {
                    SpannerNumeric numericValue = Convert.ToInt64(value, InvariantCulture);
                    return(Value.ForString(numericValue.ToString()));
                }
                if (value is byte || value is ushort || value is uint || value is ulong)
                {
                    SpannerNumeric numericValue = Convert.ToUInt64(value, InvariantCulture);
                    return(Value.ForString(numericValue.ToString()));
                }
                throw new ArgumentException("Numeric parameters must be of type SpannerNumeric or string");

            default:
                throw new ArgumentOutOfRangeException(nameof(type.Code), type.Code, null);
            }
        }