diff --git a/google-cloud-spanner/lib/google/cloud/spanner/batch_client.rb b/google-cloud-spanner/lib/google/cloud/spanner/batch_client.rb index da22247a23a0..b12ea5d18288 100644 --- a/google-cloud-spanner/lib/google/cloud/spanner/batch_client.rb +++ b/google-cloud-spanner/lib/google/cloud/spanner/batch_client.rb @@ -272,6 +272,80 @@ def load_partition serialized_partition Partition.load serialized_partition end + ## + # Creates a configuration object ({Fields}) that may be provided to + # queries or used to create STRUCT objects. (The STRUCT will be + # represented by the {Data} class.) See {Client#execute} and/or + # {Fields#struct}. + # + # For more information, see [Data Types - Constructing a + # STRUCT](https://cloud.google.com/spanner/docs/data-types#constructing-a-struct). + # + # @param [Array, Hash] types Accepts an array or hash types. + # + # Arrays can contain just the type value, or a sub-array of the + # field's name and type value. Hash keys must contain the field name + # as a `Symbol` or `String`, or the field position as an `Integer`. + # Hash values must contain the type value. If a Hash is used the + # fields will be created using the same order as the Hash keys. + # + # Supported type values incude: + # + # * `:BOOL` + # * `:BYTES` + # * `:DATE` + # * `:FLOAT64` + # * `:INT64` + # * `:STRING` + # * `:TIMESTAMP` + # * `Array` - Lists are specified by providing the type code in an + # array. For example, an array of integers are specified as + # `[:INT64]`. + # * {Fields} - Nested Structs are specified by providing a Fields + # object. + # + # @return [Fields] The fields of the given types. + # + # @example Create a STRUCT value with named fields using Fields object: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # + # batch_client = spanner.batch_client "my-instance", "my-database" + # + # named_type = batch_client.fields( + # { id: :INT64, name: :STRING, active: :BOOL } + # ) + # named_data = named_type.struct( + # { id: 42, name: nil, active: false } + # ) + # + # @example Create a STRUCT value with anonymous field names: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # + # batch_client = spanner.batch_client "my-instance", "my-database" + # + # anon_type = batch_client.fields [:INT64, :STRING, :BOOL] + # anon_data = anon_type.struct [42, nil, false] + # + # @example Create a STRUCT value with duplicate field names: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # + # batch_client = spanner.batch_client "my-instance", "my-database" + # + # dup_type = batch_client.fields( + # [[:x, :INT64], [:x, :STRING], [:x, :BOOL]] + # ) + # dup_data = dup_type.struct [42, nil, false] + # + def fields types + Fields.new types + end + ## # Creates a Spanner Range. This can be used in place of a Ruby Range # when needing to exclude the beginning value. diff --git a/google-cloud-spanner/lib/google/cloud/spanner/batch_snapshot.rb b/google-cloud-spanner/lib/google/cloud/spanner/batch_snapshot.rb index 621c934e8200..103d2e7aa9e3 100644 --- a/google-cloud-spanner/lib/google/cloud/spanner/batch_snapshot.rb +++ b/google-cloud-spanner/lib/google/cloud/spanner/batch_snapshot.rb @@ -112,23 +112,35 @@ def timestamp # placeholder consists of "@" followed by the parameter name. # Parameter names consist of any combination of letters, numbers, and # underscores. - # @param [Integer] partition_size_bytes The desired data size for each - # partition generated. This is only a hint. The actual size of each - # partition may be smaller or larger than this size request. - # @param [Integer] max_partitions The desired maximum number of - # partitions to return. For example, this may be set to the number of - # workers available. This is only a hint and may provide different - # results based on the request. # @param [Hash] params SQL parameters for the query string. The # parameter placeholders, minus the "@", are the the hash keys, and # the literal values are the hash values. If the query string contains # something like "WHERE id > @msg_id", then the params must contain # something like `:msg_id => 1`. + # + # Ruby types are mapped to Spanner types as follows: + # + # | Spanner | Ruby | Notes | + # |-------------|----------------|---| + # | `BOOL` | `true`/`false` | | + # | `INT64` | `Integer` | | + # | `FLOAT64` | `Float` | | + # | `STRING` | `String` | | + # | `DATE` | `Date` | | + # | `TIMESTAMP` | `Time`, `DateTime` | | + # | `BYTES` | `File`, `IO`, `StringIO`, or similar | | + # | `ARRAY` | `Array` | Nested arrays are not supported. | + # | `STRUCT` | `Hash`, {Data} | | + # + # See [Data + # types](https://cloud.google.com/spanner/docs/data-definition-language#data_types). + # + # See [Data Types - Constructing a + # STRUCT](https://cloud.google.com/spanner/docs/data-types#constructing-a-struct). # @param [Hash] types Types of the SQL parameters in `params`. It is not # always possible for Cloud Spanner to infer the right SQL type from a - # value in `params`. In these cases, the `types` hash can be used to - # specify the exact SQL type for some or all of the SQL query - # parameters. + # value in `params`. In these cases, the `types` hash must be used to + # specify the SQL type for these values. # # The keys of the hash should be query string parameter placeholders, # minus the "@". The values of the hash should be Cloud Spanner type @@ -141,13 +153,20 @@ def timestamp # * `:INT64` # * `:STRING` # * `:TIMESTAMP` - # - # Arrays are specified by providing the type code in an array. For - # example, an array of integers are specified as `[:INT64]`. - # - # Structs are not yet supported in query parameters. + # * `Array` - Lists are specified by providing the type code in an + # array. For example, an array of integers are specified as + # `[:INT64]`. + # * {Fields} - Types for STRUCT values (`Hash`/{Data} objects) are + # specified using a {Fields} object. # # Types are optional. + # @param [Integer] partition_size_bytes The desired data size for each + # partition generated. This is only a hint. The actual size of each + # partition may be smaller or larger than this size request. + # @param [Integer] max_partitions The desired maximum number of + # partitions to return. For example, this may be set to the number of + # workers available. This is only a hint and may provide different + # results based on the request. # # @return [Array] The partitions # created by the query partition. @@ -342,23 +361,6 @@ def close ## # Executes a SQL query. # - # Arguments can be passed using `params`, Ruby types are mapped to - # Spanner types as follows: - # - # | Spanner | Ruby | Notes | - # |-------------|----------------|---| - # | `BOOL` | `true`/`false` | | - # | `INT64` | `Integer` | | - # | `FLOAT64` | `Float` | | - # | `STRING` | `String` | | - # | `DATE` | `Date` | | - # | `TIMESTAMP` | `Time`, `DateTime` | | - # | `BYTES` | `File`, `IO`, `StringIO`, or similar | | - # | `ARRAY` | `Array` | Nested arrays are not supported. | - # - # See [Data - # types](https://cloud.google.com/spanner/docs/data-definition-language#data_types). - # # @param [String] sql The SQL query string. See [Query # syntax](https://cloud.google.com/spanner/docs/query-syntax). # @@ -371,11 +373,30 @@ def close # the literal values are the hash values. If the query string contains # something like "WHERE id > @msg_id", then the params must contain # something like `:msg_id => 1`. + # + # Ruby types are mapped to Spanner types as follows: + # + # | Spanner | Ruby | Notes | + # |-------------|----------------|---| + # | `BOOL` | `true`/`false` | | + # | `INT64` | `Integer` | | + # | `FLOAT64` | `Float` | | + # | `STRING` | `String` | | + # | `DATE` | `Date` | | + # | `TIMESTAMP` | `Time`, `DateTime` | | + # | `BYTES` | `File`, `IO`, `StringIO`, or similar | | + # | `ARRAY` | `Array` | Nested arrays are not supported. | + # | `STRUCT` | `Hash`, {Data} | | + # + # See [Data + # types](https://cloud.google.com/spanner/docs/data-definition-language#data_types). + # + # See [Data Types - Constructing a + # STRUCT](https://cloud.google.com/spanner/docs/data-types#constructing-a-struct). # @param [Hash] types Types of the SQL parameters in `params`. It is not # always possible for Cloud Spanner to infer the right SQL type from a - # value in `params`. In these cases, the `types` hash can be used to - # specify the exact SQL type for some or all of the SQL query - # parameters. + # value in `params`. In these cases, the `types` hash must be used to + # specify the SQL type for these values. # # The keys of the hash should be query string parameter placeholders, # minus the "@". The values of the hash should be Cloud Spanner type @@ -388,11 +409,11 @@ def close # * `:INT64` # * `:STRING` # * `:TIMESTAMP` - # - # Arrays are specified by providing the type code in an array. For - # example, an array of integers are specified as `[:INT64]`. - # - # Structs are not yet supported in query parameters. + # * `Array` - Lists are specified by providing the type code in an + # array. For example, an array of integers are specified as + # `[:INT64]`. + # * {Fields} - Types for STRUCT values (`Hash`/{Data} objects) are + # specified using a {Fields} object. # # Types are optional. # @return [Google::Cloud::Spanner::Results] The results of the query @@ -419,8 +440,72 @@ def close # batch_snapshot = batch_client.batch_snapshot # # results = batch_snapshot.execute "SELECT * FROM users " \ - # "WHERE active = @active", - # params: { active: true } + # "WHERE active = @active", + # params: { active: true } + # + # results.rows.each do |row| + # puts "User #{row[:id]} is #{row[:name]}" + # end + # + # @example Query with a SQL STRUCT query parameter as a Hash: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # batch_client = spanner.batch_client "my-instance", "my-database" + # batch_snapshot = batch_client.batch_snapshot + # + # user_hash = { id: 1, name: "Charlie", active: false } + # + # results = batch_snapshot.execute "SELECT * FROM users WHERE " \ + # "ID = @user_struct.id " \ + # "AND name = @user_struct.name " \ + # "AND active = @user_struct.active", + # params: { user_struct: user_hash } + # + # results.rows.each do |row| + # puts "User #{row[:id]} is #{row[:name]}" + # end + # + # @example Specify the SQL STRUCT type using Fields object: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # batch_client = spanner.batch_client "my-instance", "my-database" + # batch_snapshot = batch_client.batch_snapshot + # + # user_type = batch_client.fields( + # { id: :INT64, name: :STRING, active: :BOOL } + # ) + # user_hash = { id: 1, name: nil, active: false } + # + # results = batch_snapshot.execute "SELECT * FROM users WHERE " \ + # "ID = @user_struct.id " \ + # "AND name = @user_struct.name " \ + # "AND active = @user_struct.active", + # params: { user_struct: user_hash }, + # types: { user_struct: user_type } + # + # results.rows.each do |row| + # puts "User #{row[:id]} is #{row[:name]}" + # end + # + # @example Or, query with a SQL STRUCT as a typed Data object: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # batch_client = spanner.batch_client "my-instance", "my-database" + # batch_snapshot = batch_client.batch_snapshot + # + # user_type = batch_client.fields( + # { id: :INT64, name: :STRING, active: :BOOL } + # ) + # user_data = user_type.struct id: 1, name: nil, active: false + # + # results = batch_snapshot.execute "SELECT * FROM users WHERE " \ + # "ID = @user_struct.id " \ + # "AND name = @user_struct.name " \ + # "AND active = @user_struct.active", + # params: { user_struct: user_data } # # results.rows.each do |row| # puts "User #{row[:id]} is #{row[:name]}" diff --git a/google-cloud-spanner/lib/google/cloud/spanner/client.rb b/google-cloud-spanner/lib/google/cloud/spanner/client.rb index 081d5e0f61db..4ea8d226ea4d 100644 --- a/google-cloud-spanner/lib/google/cloud/spanner/client.rb +++ b/google-cloud-spanner/lib/google/cloud/spanner/client.rb @@ -98,23 +98,6 @@ def database ## # Executes a SQL query. # - # Arguments can be passed using `params`, Ruby types are mapped to - # Spanner types as follows: - # - # | Spanner | Ruby | Notes | - # |-------------|----------------|---| - # | `BOOL` | `true`/`false` | | - # | `INT64` | `Integer` | | - # | `FLOAT64` | `Float` | | - # | `STRING` | `String` | | - # | `DATE` | `Date` | | - # | `TIMESTAMP` | `Time`, `DateTime` | | - # | `BYTES` | `File`, `IO`, `StringIO`, or similar | | - # | `ARRAY` | `Array` | Nested arrays are not supported. | - # - # See [Data - # types](https://cloud.google.com/spanner/docs/data-definition-language#data_types). - # # @param [String] sql The SQL query string. See [Query # syntax](https://cloud.google.com/spanner/docs/query-syntax). # @@ -127,11 +110,30 @@ def database # the literal values are the hash values. If the query string contains # something like "WHERE id > @msg_id", then the params must contain # something like `:msg_id => 1`. + # + # Ruby types are mapped to Spanner types as follows: + # + # | Spanner | Ruby | Notes | + # |-------------|----------------|---| + # | `BOOL` | `true`/`false` | | + # | `INT64` | `Integer` | | + # | `FLOAT64` | `Float` | | + # | `STRING` | `String` | | + # | `DATE` | `Date` | | + # | `TIMESTAMP` | `Time`, `DateTime` | | + # | `BYTES` | `File`, `IO`, `StringIO`, or similar | | + # | `ARRAY` | `Array` | Nested arrays are not supported. | + # | `STRUCT` | `Hash`, {Data} | | + # + # See [Data + # types](https://cloud.google.com/spanner/docs/data-definition-language#data_types). + # + # See [Data Types - Constructing a + # STRUCT](https://cloud.google.com/spanner/docs/data-types#constructing-a-struct). # @param [Hash] types Types of the SQL parameters in `params`. It is not # always possible for Cloud Spanner to infer the right SQL type from a - # value in `params`. In these cases, the `types` hash can be used to - # specify the exact SQL type for some or all of the SQL query - # parameters. + # value in `params`. In these cases, the `types` hash must be used to + # specify the SQL type for these values. # # The keys of the hash should be query string parameter placeholders, # minus the "@". The values of the hash should be Cloud Spanner type @@ -144,11 +146,11 @@ def database # * `:INT64` # * `:STRING` # * `:TIMESTAMP` - # - # Arrays are specified by providing the type code in an array. For - # example, an array of integers are specified as `[:INT64]`. - # - # Structs are not yet supported in query parameters. + # * `Array` - Lists are specified by providing the type code in an + # array. For example, an array of integers are specified as + # `[:INT64]`. + # * {Fields} - Types for STRUCT values (`Hash`/{Data} objects) are + # specified using a {Fields} object. # # Types are optional. # @param [Hash] single_use Perform the read with a single-use snapshot @@ -234,6 +236,66 @@ def database # puts "User #{row[:id]} is #{row[:name]}" # end # + # @example Query with a SQL STRUCT query parameter as a Hash: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # + # db = spanner.client "my-instance", "my-database" + # + # user_hash = { id: 1, name: "Charlie", active: false } + # + # results = db.execute "SELECT * FROM users WHERE " \ + # "ID = @user_struct.id " \ + # "AND name = @user_struct.name " \ + # "AND active = @user_struct.active", + # params: { user_struct: user_hash } + # + # results.rows.each do |row| + # puts "User #{row[:id]} is #{row[:name]}" + # end + # + # @example Specify the SQL STRUCT type using Fields object: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # + # db = spanner.client "my-instance", "my-database" + # + # user_type = db.fields id: :INT64, name: :STRING, active: :BOOL + # user_hash = { id: 1, name: nil, active: false } + # + # results = db.execute "SELECT * FROM users WHERE " \ + # "ID = @user_struct.id " \ + # "AND name = @user_struct.name " \ + # "AND active = @user_struct.active", + # params: { user_struct: user_hash }, + # types: { user_struct: user_type } + # + # results.rows.each do |row| + # puts "User #{row[:id]} is #{row[:name]}" + # end + # + # @example Or, query with a SQL STRUCT as a typed Data object: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # + # db = spanner.client "my-instance", "my-database" + # + # user_type = db.fields id: :INT64, name: :STRING, active: :BOOL + # user_data = user_type.struct id: 1, name: nil, active: false + # + # results = db.execute "SELECT * FROM users WHERE " \ + # "ID = @user_struct.id " \ + # "AND name = @user_struct.name " \ + # "AND active = @user_struct.active", + # params: { user_struct: user_data } + # + # results.rows.each do |row| + # puts "User #{row[:id]} is #{row[:name]}" + # end + # def execute sql, params: nil, types: nil, single_use: nil validate_single_use_args! single_use ensure_service! @@ -855,24 +917,72 @@ def snapshot strong: nil, timestamp: nil, read_timestamp: nil, end ## - # @private - # Creates fields object from types. + # Creates a configuration object ({Fields}) that may be provided to + # queries or used to create STRUCT objects. (The STRUCT will be + # represented by the {Data} class.) See {Client#execute} and/or + # {Fields#struct}. + # + # For more information, see [Data Types - Constructing a + # STRUCT](https://cloud.google.com/spanner/docs/data-types#constructing-a-struct). # - # @param [Array, Hash] types Accepts an array of types, array of type - # pairs, hash of positional types, hash of named types. + # @param [Array, Hash] types Accepts an array or hash types. + # + # Arrays can contain just the type value, or a sub-array of the + # field's name and type value. Hash keys must contain the field name + # as a `Symbol` or `String`, or the field position as an `Integer`. + # Hash values must contain the type value. If a Hash is used the + # fields will be created using the same order as the Hash keys. + # + # Supported type values incude: + # + # * `:BOOL` + # * `:BYTES` + # * `:DATE` + # * `:FLOAT64` + # * `:INT64` + # * `:STRING` + # * `:TIMESTAMP` + # * `Array` - Lists are specified by providing the type code in an + # array. For example, an array of integers are specified as + # `[:INT64]`. + # * {Fields} - Nested Structs are specified by providing a Fields + # object. # # @return [Fields] The fields of the given types. # - # @example + # @example Create a STRUCT value with named fields using Fields object: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # + # db = spanner.client "my-instance", "my-database" + # + # named_type = db.fields( + # { id: :INT64, name: :STRING, active: :BOOL } + # ) + # named_data = named_type.struct( + # { id: 42, name: nil, active: false } + # ) + # + # @example Create a STRUCT value with anonymous field names: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # + # db = spanner.client "my-instance", "my-database" + # + # anon_type = db.fields [:INT64, :STRING, :BOOL] + # anon_data = anon_type.struct [42, nil, false] + # + # @example Create a STRUCT value with duplicate field names: # require "google/cloud/spanner" # # spanner = Google::Cloud::Spanner.new # # db = spanner.client "my-instance", "my-database" - # user_fields = db.fields id: :INT64, name: :STRING, active: :BOOL # - # db.update "users", [user_fields.data(1, "Charlie", false), - # user_fields.data(2, "Harvey", true)] + # dup_type = db.fields [[:x, :INT64], [:x, :STRING], [:x, :BOOL]] + # dup_data = dup_type.struct [42, nil, false] # def fields types Fields.new types diff --git a/google-cloud-spanner/lib/google/cloud/spanner/data.rb b/google-cloud-spanner/lib/google/cloud/spanner/data.rb index 7fb635e6af22..aea5be3245ad 100644 --- a/google-cloud-spanner/lib/google/cloud/spanner/data.rb +++ b/google-cloud-spanner/lib/google/cloud/spanner/data.rb @@ -42,7 +42,8 @@ module Spanner # class Data ## - # Returns the names and values of the data as an array of field objects. + # Returns the configuration object ({Fields}) of the names and types of + # the data. # # @return [Array] An array containing name and value pairs. # diff --git a/google-cloud-spanner/lib/google/cloud/spanner/fields.rb b/google-cloud-spanner/lib/google/cloud/spanner/fields.rb index e35b647883c6..574eb73a0a69 100644 --- a/google-cloud-spanner/lib/google/cloud/spanner/fields.rb +++ b/google-cloud-spanner/lib/google/cloud/spanner/fields.rb @@ -41,7 +41,65 @@ module Spanner # class Fields ## - # @private + # Creates {Fields} object from types. See {Client#fields}. + # + # This object can be used to create {Data} objects by providing values + # that match the field types. See {Fields#struct}. + # + # See [Data Types - Constructing a + # STRUCT](https://cloud.google.com/spanner/docs/data-types#constructing-a-struct). + # + # @param [Array, Hash] types Accepts an array or hash types. + # + # Arrays can contain just the type value, or a sub-array of the + # field's name and type value. Hash keys must contain the field name + # as a `Symbol` or `String`, or the field position as an `Integer`. + # Hash values must contain the type value. If a Hash is used the + # fields will be created using the same order as the Hash keys. + # + # Supported type values incude: + # + # * `:BOOL` + # * `:BYTES` + # * `:DATE` + # * `:FLOAT64` + # * `:INT64` + # * `:STRING` + # * `:TIMESTAMP` + # * `Array` - Lists are specified by providing the type code in an + # array. For example, an array of integers are specified as + # `[:INT64]`. + # * {Fields} - Nested Structs are specified by providing a Fields + # object. + # + # @return [Fields] The fields of the given types. + # + # @example Create a STRUCT value with named fields using Fields object: + # require "google/cloud/spanner" + # + # named_type = Google::Cloud::Spanner::Fields.new( + # { id: :INT64, name: :STRING, active: :BOOL } + # ) + # named_data = named_type.struct( + # { id: 42, name: nil, active: false } + # ) + # + # @example Create a STRUCT value with anonymous field names: + # require "google/cloud/spanner" + # + # anon_type = Google::Cloud::Spanner::Fields.new( + # [:INT64, :STRING, :BOOL] + # ) + # anon_data = anon_type.struct [42, nil, false] + # + # @example Create a STRUCT value with duplicate field names: + # require "google/cloud/spanner" + # + # dup_type = Google::Cloud::Spanner::Fields.new( + # [[:x, :INT64], [:x, :STRING], [:x, :BOOL]] + # ) + # dup_data = dup_type.struct [42, nil, false] + # def initialize types types = types.to_a if types.is_a? Hash @@ -146,12 +204,61 @@ def [] key # rubocop:disable all ## - # Creates a new Data object given the data values matching the fields. + # Creates a new {Data} object given the data values matching the fields. # Can be provided as either an Array of values, or a Hash where the hash # keys match the field name or match the index position of the field. # + # For more information, see [Data Types - Constructing a + # STRUCT](https://cloud.google.com/spanner/docs/data-types#constructing-a-struct). + # + # @param [Array, Hash] data Accepts an array or hash data values. + # + # Arrays can contain just the data value, nested arrays will be + # treated as lists of values. Values must be provided in the same + # order as the fields, and there is no way to associate values to the + # field names. + # + # Hash keys must contain the field name as a `Symbol` or `String`, or + # the field position as an `Integer`. Hash values must contain the + # data value. Hash values will be matched to the fields, so they don't + # need to match the same order as the fields. + # # @return [Data] A new Data object. # + # @example Create a STRUCT value with named fields using Fields object: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # + # db = spanner.client "my-instance", "my-database" + # + # named_type = db.fields( + # { id: :INT64, name: :STRING, active: :BOOL } + # ) + # named_data = named_type.struct( + # { id: 42, name: nil, active: false } + # ) + # + # @example Create a STRUCT value with anonymous field names: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # + # db = spanner.client "my-instance", "my-database" + # + # anon_type = db.fields [:INT64, :STRING, :BOOL] + # anon_data = anon_type.struct [42, nil, false] + # + # @example Create a STRUCT value with duplicate field names: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # + # db = spanner.client "my-instance", "my-database" + # + # dup_type = db.fields [[:x, :INT64], [:x, :STRING], [:x, :BOOL]] + # dup_data = dup_type.struct [42, nil, false] + # def struct data # create local copy of types so they are parsed just once. cached_types = types diff --git a/google-cloud-spanner/lib/google/cloud/spanner/results.rb b/google-cloud-spanner/lib/google/cloud/spanner/results.rb index f2b3cf1ec7f2..6b7768122582 100644 --- a/google-cloud-spanner/lib/google/cloud/spanner/results.rb +++ b/google-cloud-spanner/lib/google/cloud/spanner/results.rb @@ -51,7 +51,8 @@ def timestamp end ## - # Returns the field names and types for the rows in the returned data. + # Returns the configuration object ({Fields}) of the names and types of + # the rows in the returned data. # # @return [Fields] The fields of the returned data. # diff --git a/google-cloud-spanner/lib/google/cloud/spanner/session.rb b/google-cloud-spanner/lib/google/cloud/spanner/session.rb index e16aa83310c7..595e81492cd9 100644 --- a/google-cloud-spanner/lib/google/cloud/spanner/session.rb +++ b/google-cloud-spanner/lib/google/cloud/spanner/session.rb @@ -93,23 +93,6 @@ def path ## # Executes a SQL query. # - # Arguments can be passed using `params`, Ruby types are mapped to - # Spanner types as follows: - # - # | Spanner | Ruby | Notes | - # |-------------|----------------|---| - # | `BOOL` | `true`/`false` | | - # | `INT64` | `Integer` | | - # | `FLOAT64` | `Float` | | - # | `STRING` | `String` | | - # | `DATE` | `Date` | | - # | `TIMESTAMP` | `Time`, `DateTime` | | - # | `BYTES` | `File`, `IO`, `StringIO`, or similar | | - # | `ARRAY` | `Array` | Nested arrays are not supported. | - # - # See [Data - # types](https://cloud.google.com/spanner/docs/data-definition-language#data_types). - # # @param [String] sql The SQL query string. See [Query # syntax](https://cloud.google.com/spanner/docs/query-syntax). # @@ -122,11 +105,30 @@ def path # the literal values are the hash values. If the query string contains # something like "WHERE id > @msg_id", then the params must contain # something like `:msg_id => 1`. + # + # Ruby types are mapped to Spanner types as follows: + # + # | Spanner | Ruby | Notes | + # |-------------|----------------|---| + # | `BOOL` | `true`/`false` | | + # | `INT64` | `Integer` | | + # | `FLOAT64` | `Float` | | + # | `STRING` | `String` | | + # | `DATE` | `Date` | | + # | `TIMESTAMP` | `Time`, `DateTime` | | + # | `BYTES` | `File`, `IO`, `StringIO`, or similar | | + # | `ARRAY` | `Array` | Nested arrays are not supported. | + # | `STRUCT` | `Hash`, {Data} | | + # + # See [Data + # types](https://cloud.google.com/spanner/docs/data-definition-language#data_types). + # + # See [Data Types - Constructing a + # STRUCT](https://cloud.google.com/spanner/docs/data-types#constructing-a-struct). # @param [Hash] types Types of the SQL parameters in `params`. It is not # always possible for Cloud Spanner to infer the right SQL type from a - # value in `params`. In these cases, the `types` hash can be used to - # specify the exact SQL type for some or all of the SQL query - # parameters. + # value in `params`. In these cases, the `types` hash must be used to + # specify the SQL type for these values. # # The keys of the hash should be query string parameter placeholders, # minus the "@". The values of the hash should be Cloud Spanner type @@ -139,11 +141,11 @@ def path # * `:INT64` # * `:STRING` # * `:TIMESTAMP` - # - # Arrays are specified by providing the type code in an array. For - # example, an array of integers are specified as `[:INT64]`. - # - # Structs are not yet supported in query parameters. + # * `Array` - Lists are specified by providing the type code in an + # array. For example, an array of integers are specified as + # `[:INT64]`. + # * {Fields} - Types for STRUCT values (`Hash`/{Data} objects) are + # specified using a {Fields} object. # # Types are optional. # @param [Google::Spanner::V1::TransactionSelector] transaction The @@ -180,6 +182,66 @@ def path # puts "User #{row[:id]} is #{row[:name]}" # end # + # @example Query with a SQL STRUCT query parameter as a Hash: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # + # db = spanner.client "my-instance", "my-database" + # + # user_hash = { id: 1, name: "Charlie", active: false } + # + # results = db.execute "SELECT * FROM users WHERE " \ + # "ID = @user_struct.id " \ + # "AND name = @user_struct.name " \ + # "AND active = @user_struct.active", + # params: { user_struct: user_hash } + # + # results.rows.each do |row| + # puts "User #{row[:id]} is #{row[:name]}" + # end + # + # @example Specify the SQL STRUCT type using Fields object: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # + # db = spanner.client "my-instance", "my-database" + # + # user_type = db.fields id: :INT64, name: :STRING, active: :BOOL + # user_hash = { id: 1, name: nil, active: false } + # + # results = db.execute "SELECT * FROM users WHERE " \ + # "ID = @user_struct.id " \ + # "AND name = @user_struct.name " \ + # "AND active = @user_struct.active", + # params: { user_struct: user_hash }, + # types: { user_struct: user_type } + # + # results.rows.each do |row| + # puts "User #{row[:id]} is #{row[:name]}" + # end + # + # @example Or, query with a SQL STRUCT as a typed Data object: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # + # db = spanner.client "my-instance", "my-database" + # + # user_type = db.fields id: :INT64, name: :STRING, active: :BOOL + # user_data = user_type.struct id: 1, name: nil, active: false + # + # results = db.execute "SELECT * FROM users WHERE " \ + # "ID = @user_struct.id " \ + # "AND name = @user_struct.name " \ + # "AND active = @user_struct.active", + # params: { user_struct: user_struct } + # + # results.rows.each do |row| + # puts "User #{row[:id]} is #{row[:name]}" + # end + # def execute sql, params: nil, types: nil, transaction: nil, partition_token: nil ensure_service! diff --git a/google-cloud-spanner/lib/google/cloud/spanner/snapshot.rb b/google-cloud-spanner/lib/google/cloud/spanner/snapshot.rb index c4ad0fe24431..7e842a23a2a2 100644 --- a/google-cloud-spanner/lib/google/cloud/spanner/snapshot.rb +++ b/google-cloud-spanner/lib/google/cloud/spanner/snapshot.rb @@ -63,23 +63,6 @@ def timestamp ## # Executes a SQL query. # - # Arguments can be passed using `params`, Ruby types are mapped to - # Spanner types as follows: - # - # | Spanner | Ruby | Notes | - # |-------------|----------------|---| - # | `BOOL` | `true`/`false` | | - # | `INT64` | `Integer` | | - # | `FLOAT64` | `Float` | | - # | `STRING` | `String` | | - # | `DATE` | `Date` | | - # | `TIMESTAMP` | `Time`, `DateTime` | | - # | `BYTES` | `File`, `IO`, `StringIO`, or similar | | - # | `ARRAY` | `Array` | Nested arrays are not supported. | - # - # See [Data - # types](https://cloud.google.com/spanner/docs/data-definition-language#data_types). - # # @param [String] sql The SQL query string. See [Query # syntax](https://cloud.google.com/spanner/docs/query-syntax). # @@ -92,11 +75,30 @@ def timestamp # the literal values are the hash values. If the query string contains # something like "WHERE id > @msg_id", then the params must contain # something like `:msg_id => 1`. + # + # Ruby types are mapped to Spanner types as follows: + # + # | Spanner | Ruby | Notes | + # |-------------|----------------|---| + # | `BOOL` | `true`/`false` | | + # | `INT64` | `Integer` | | + # | `FLOAT64` | `Float` | | + # | `STRING` | `String` | | + # | `DATE` | `Date` | | + # | `TIMESTAMP` | `Time`, `DateTime` | | + # | `BYTES` | `File`, `IO`, `StringIO`, or similar | | + # | `ARRAY` | `Array` | Nested arrays are not supported. | + # | `STRUCT` | `Hash`, {Data} | | + # + # See [Data + # types](https://cloud.google.com/spanner/docs/data-definition-language#data_types). + # + # See [Data Types - Constructing a + # STRUCT](https://cloud.google.com/spanner/docs/data-types#constructing-a-struct). # @param [Hash] types Types of the SQL parameters in `params`. It is not # always possible for Cloud Spanner to infer the right SQL type from a - # value in `params`. In these cases, the `types` hash can be used to - # specify the exact SQL type for some or all of the SQL query - # parameters. + # value in `params`. In these cases, the `types` hash must be used to + # specify the SQL type for these values. # # The keys of the hash should be query string parameter placeholders, # minus the "@". The values of the hash should be Cloud Spanner type @@ -109,11 +111,11 @@ def timestamp # * `:INT64` # * `:STRING` # * `:TIMESTAMP` - # - # Arrays are specified by providing the type code in an array. For - # example, an array of integers are specified as `[:INT64]`. - # - # Structs are not yet supported in query parameters. + # * `Array` - Lists are specified by providing the type code in an + # array. For example, an array of integers are specified as + # `[:INT64]`. + # * {Fields} - Types for STRUCT values (`Hash`/{Data} objects) are + # specified using a {Fields} object. # # Types are optional. # @return [Google::Cloud::Spanner::Results] The results of the query @@ -149,6 +151,69 @@ def timestamp # end # end # + # @example Query with a SQL STRUCT query parameter as a Hash: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # db = spanner.client "my-instance", "my-database" + # + # db.snapshot do |snp| + # user_hash = { id: 1, name: "Charlie", active: false } + # + # results = snp.execute "SELECT * FROM users WHERE " \ + # "ID = @user_struct.id " \ + # "AND name = @user_struct.name " \ + # "AND active = @user_struct.active", + # params: { user_struct: user_hash } + # + # results.rows.each do |row| + # puts "User #{row[:id]} is #{row[:name]}" + # end + # end + # + # @example Specify the SQL STRUCT type using Fields object: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # db = spanner.client "my-instance", "my-database" + # + # db.snapshot do |snp| + # user_type = snp.fields id: :INT64, name: :STRING, active: :BOOL + # user_hash = { id: 1, name: nil, active: false } + # + # results = snp.execute "SELECT * FROM users WHERE " \ + # "ID = @user_struct.id " \ + # "AND name = @user_struct.name " \ + # "AND active = @user_struct.active", + # params: { user_struct: user_hash }, + # types: { user_struct: user_type } + # + # results.rows.each do |row| + # puts "User #{row[:id]} is #{row[:name]}" + # end + # end + # + # @example Or, query with a SQL STRUCT as a typed Data object: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # db = spanner.client "my-instance", "my-database" + # + # db.snapshot do |snp| + # user_type = snp.fields id: :INT64, name: :STRING, active: :BOOL + # user_data = user_type.struct id: 1, name: nil, active: false + # + # results = snp.execute "SELECT * FROM users WHERE " \ + # "ID = @user_struct.id " \ + # "AND name = @user_struct.name " \ + # "AND active = @user_struct.active", + # params: { user_struct: user_data } + # + # results.rows.each do |row| + # puts "User #{row[:id]} is #{row[:name]}" + # end + # end + # def execute sql, params: nil, types: nil ensure_session! @@ -203,6 +268,84 @@ def read table, columns, keys: nil, index: nil, limit: nil transaction: tx_selector end + ## + # Creates a configuration object ({Fields}) that may be provided to + # queries or used to create STRUCT objects. (The STRUCT will be + # represented by the {Data} class.) See {Client#execute} and/or + # {Fields#struct}. + # + # For more information, see [Data Types - Constructing a + # STRUCT](https://cloud.google.com/spanner/docs/data-types#constructing-a-struct). + # + # See [Data Types - Constructing a + # STRUCT](https://cloud.google.com/spanner/docs/data-types#constructing-a-struct). + # + # @param [Array, Hash] types Accepts an array or hash types. + # + # Arrays can contain just the type value, or a sub-array of the + # field's name and type value. Hash keys must contain the field name + # as a `Symbol` or `String`, or the field position as an `Integer`. + # Hash values must contain the type value. If a Hash is used the + # fields will be created using the same order as the Hash keys. + # + # Supported type values incude: + # + # * `:BOOL` + # * `:BYTES` + # * `:DATE` + # * `:FLOAT64` + # * `:INT64` + # * `:STRING` + # * `:TIMESTAMP` + # * `Array` - Lists are specified by providing the type code in an + # array. For example, an array of integers are specified as + # `[:INT64]`. + # * {Fields} - Nested Structs are specified by providing a Fields + # object. + # + # @return [Fields] The fields of the given types. + # + # @example Create a STRUCT value with named fields using Fields object: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # db = spanner.client "my-instance", "my-database" + # + # db.snapshot do |snp| + # named_type = snp.fields( + # { id: :INT64, name: :STRING, active: :BOOL } + # ) + # named_data = named_type.struct( + # { id: 42, name: nil, active: false } + # ) + # end + # + # @example Create a STRUCT value with anonymous field names: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # db = spanner.client "my-instance", "my-database" + # + # db.snapshot do |snp| + # anon_type = snp.fields [:INT64, :STRING, :BOOL] + # anon_data = anon_type.struct [42, nil, false] + # end + # + # @example Create a STRUCT value with duplicate field names: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # db = spanner.client "my-instance", "my-database" + # + # db.snapshot do |snp| + # dup_type = snp.fields [[:x, :INT64], [:x, :STRING], [:x, :BOOL]] + # dup_data = dup_type.struct [42, nil, false] + # end + # + def fields types + Fields.new types + end + ## # Creates a Cloud Spanner Range. This can be used in place of a Ruby # Range when needing to exclude the beginning value. diff --git a/google-cloud-spanner/lib/google/cloud/spanner/transaction.rb b/google-cloud-spanner/lib/google/cloud/spanner/transaction.rb index f01d162059d3..1d8d8f1bb2e8 100644 --- a/google-cloud-spanner/lib/google/cloud/spanner/transaction.rb +++ b/google-cloud-spanner/lib/google/cloud/spanner/transaction.rb @@ -92,23 +92,6 @@ def transaction_id ## # Executes a SQL query. # - # Arguments can be passed using `params`, Ruby types are mapped to - # Spanner types as follows: - # - # | Spanner | Ruby | Notes | - # |-------------|----------------|---| - # | `BOOL` | `true`/`false` | | - # | `INT64` | `Integer` | | - # | `FLOAT64` | `Float` | | - # | `STRING` | `String` | | - # | `DATE` | `Date` | | - # | `TIMESTAMP` | `Time`, `DateTime` | | - # | `BYTES` | `File`, `IO`, `StringIO`, or similar | | - # | `ARRAY` | `Array` | Nested arrays are not supported. | - # - # See [Data - # types](https://cloud.google.com/spanner/docs/data-definition-language#data_types). - # # @param [String] sql The SQL query string. See [Query # syntax](https://cloud.google.com/spanner/docs/query-syntax). # @@ -121,11 +104,30 @@ def transaction_id # the literal values are the hash values. If the query string contains # something like "WHERE id > @msg_id", then the params must contain # something like `:msg_id => 1`. + # + # Ruby types are mapped to Spanner types as follows: + # + # | Spanner | Ruby | Notes | + # |-------------|----------------|---| + # | `BOOL` | `true`/`false` | | + # | `INT64` | `Integer` | | + # | `FLOAT64` | `Float` | | + # | `STRING` | `String` | | + # | `DATE` | `Date` | | + # | `TIMESTAMP` | `Time`, `DateTime` | | + # | `BYTES` | `File`, `IO`, `StringIO`, or similar | | + # | `ARRAY` | `Array` | Nested arrays are not supported. | + # | `STRUCT` | `Hash`, {Data} | | + # + # See [Data + # types](https://cloud.google.com/spanner/docs/data-definition-language#data_types). + # + # See [Data Types - Constructing a + # STRUCT](https://cloud.google.com/spanner/docs/data-types#constructing-a-struct). # @param [Hash] types Types of the SQL parameters in `params`. It is not # always possible for Cloud Spanner to infer the right SQL type from a - # value in `params`. In these cases, the `types` hash can be used to - # specify the exact SQL type for some or all of the SQL query - # parameters. + # value in `params`. In these cases, the `types` hash must be used to + # specify the SQL type for these values. # # The keys of the hash should be query string parameter placeholders, # minus the "@". The values of the hash should be Cloud Spanner type @@ -138,11 +140,11 @@ def transaction_id # * `:INT64` # * `:STRING` # * `:TIMESTAMP` - # - # Arrays are specified by providing the type code in an array. For - # example, an array of integers are specified as `[:INT64]`. - # - # Structs are not yet supported in query parameters. + # * `Array` - Lists are specified by providing the type code in an + # array. For example, an array of integers are specified as + # `[:INT64]`. + # * {Fields} - Types for STRUCT values (`Hash`/{Data} objects) are + # specified using a {Fields} object. # # Types are optional. # @return [Google::Cloud::Spanner::Results] The results of the query @@ -177,6 +179,69 @@ def transaction_id # end # end # + # @example Query with a SQL STRUCT query parameter as a Hash: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # db = spanner.client "my-instance", "my-database" + # + # db.transaction do |tx| + # user_hash = { id: 1, name: "Charlie", active: false } + # + # results = tx.execute "SELECT * FROM users WHERE " \ + # "ID = @user_struct.id " \ + # "AND name = @user_struct.name " \ + # "AND active = @user_struct.active", + # params: { user_struct: user_hash } + # + # results.rows.each do |row| + # puts "User #{row[:id]} is #{row[:name]}" + # end + # end + # + # @example Specify the SQL STRUCT type using Fields object: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # db = spanner.client "my-instance", "my-database" + # + # db.transaction do |tx| + # user_type = tx.fields id: :INT64, name: :STRING, active: :BOOL + # user_hash = { id: 1, name: nil, active: false } + # + # results = tx.execute "SELECT * FROM users WHERE " \ + # "ID = @user_struct.id " \ + # "AND name = @user_struct.name " \ + # "AND active = @user_struct.active", + # params: { user_struct: user_hash }, + # types: { user_struct: user_type } + # + # results.rows.each do |row| + # puts "User #{row[:id]} is #{row[:name]}" + # end + # end + # + # @example Or, query with a SQL STRUCT as a typed Data object: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # db = spanner.client "my-instance", "my-database" + # + # db.transaction do |tx| + # user_type = tx.fields id: :INT64, name: :STRING, active: :BOOL + # user_data = user_type.struct id: 1, name: nil, active: false + # + # results = tx.execute "SELECT * FROM users WHERE " \ + # "ID = @user_struct.id " \ + # "AND name = @user_struct.name " \ + # "AND active = @user_struct.active", + # params: { user_struct: user_data } + # + # results.rows.each do |row| + # puts "User #{row[:id]} is #{row[:name]}" + # end + # end + # def execute sql, params: nil, types: nil ensure_session! @@ -468,6 +533,81 @@ def fields_for table execute("SELECT * FROM #{table} WHERE 1 = 0").fields end + ## + # Creates a configuration object ({Fields}) that may be provided to + # queries or used to create STRUCT objects. (The STRUCT will be + # represented by the {Data} class.) See {Client#execute} and/or + # {Fields#struct}. + # + # For more information, see [Data Types - Constructing a + # STRUCT](https://cloud.google.com/spanner/docs/data-types#constructing-a-struct). + # + # @param [Array, Hash] types Accepts an array or hash types. + # + # Arrays can contain just the type value, or a sub-array of the + # field's name and type value. Hash keys must contain the field name + # as a `Symbol` or `String`, or the field position as an `Integer`. + # Hash values must contain the type value. If a Hash is used the + # fields will be created using the same order as the Hash keys. + # + # Supported type values incude: + # + # * `:BOOL` + # * `:BYTES` + # * `:DATE` + # * `:FLOAT64` + # * `:INT64` + # * `:STRING` + # * `:TIMESTAMP` + # * `Array` - Lists are specified by providing the type code in an + # array. For example, an array of integers are specified as + # `[:INT64]`. + # * {Fields} - Nested Structs are specified by providing a Fields + # object. + # + # @return [Fields] The fields of the given types. + # + # @example Create a STRUCT value with named fields using Fields object: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # db = spanner.client "my-instance", "my-database" + # + # db.transaction do |tx| + # named_type = tx.fields( + # { id: :INT64, name: :STRING, active: :BOOL } + # ) + # named_data = named_type.struct( + # { id: 42, name: nil, active: false } + # ) + # end + # + # @example Create a STRUCT value with anonymous field names: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # db = spanner.client "my-instance", "my-database" + # + # db.transaction do |tx| + # anon_type = tx.fields [:INT64, :STRING, :BOOL] + # anon_data = anon_type.struct [42, nil, false] + # end + # + # @example Create a STRUCT value with duplicate field names: + # require "google/cloud/spanner" + # + # spanner = Google::Cloud::Spanner.new + # db = spanner.client "my-instance", "my-database" + # + # db.transaction do |tx| + # dup_type = tx.fields [[:x, :INT64], [:x, :STRING], [:x, :BOOL]] + # dup_data = dup_type.struct [42, nil, false] + # end + # + def fields types + Fields.new types + end + ## # Creates a Cloud Spanner Range. This can be used in place of a Ruby # Range when needing to exclude the beginning value. diff --git a/google-cloud-spanner/support/doctest_helper.rb b/google-cloud-spanner/support/doctest_helper.rb index 082a2a80a101..2ea49b378b7f 100644 --- a/google-cloud-spanner/support/doctest_helper.rb +++ b/google-cloud-spanner/support/doctest_helper.rb @@ -244,20 +244,7 @@ def mock_spanner 5.times do mock.expect :begin_transaction, tx_resp, ["session-name", Google::Spanner::V1::TransactionOptions, Hash] end - mock.expect :execute_streaming_sql, results_enum, ["session-name", "SELECT * FROM users", Hash] - mock.expect :commit, commit_resp, ["session-name", Array, Hash] - end - end - - doctest.before "Google::Cloud::Spanner::BatchSnapshot#execute@Query using query parameters:" do - mock_spanner do |mock, mock_instances, mock_databases| - 20.times do - mock.expect :create_session, session_grpc, ["projects/my-project/instances/my-instance/databases/my-database", Hash] - end - 5.times do - mock.expect :begin_transaction, tx_resp, ["session-name", Google::Spanner::V1::TransactionOptions, Hash] - end - mock.expect :execute_streaming_sql, results_enum, ["session-name", "SELECT * FROM users WHERE active = @active", Hash] + mock.expect :execute_streaming_sql, results_enum, ["session-name", String, Hash] mock.expect :commit, commit_resp, ["session-name", Array, Hash] end end @@ -426,16 +413,7 @@ def mock_spanner 20.times do mock.expect :create_session, session_grpc, ["projects/my-project/instances/my-instance/databases/my-database", Hash] end - mock.expect :execute_streaming_sql, results_enum, ["session-name", "SELECT * FROM users", Hash] - end - end - - doctest.before "Google::Cloud::Spanner::Client#execute@Query using query parameters:" do - mock_spanner do |mock, mock_instances, mock_databases| - 20.times do - mock.expect :create_session, session_grpc, ["projects/my-project/instances/my-instance/databases/my-database", Hash] - end - mock.expect :execute_streaming_sql, results_enum, ["session-name", "SELECT * FROM users WHERE active = @active", Hash] + mock.expect :execute_streaming_sql, results_enum, ["session-name", String, Hash] end end @@ -672,20 +650,7 @@ def mock_spanner 5.times do mock.expect :begin_transaction, tx_resp, ["session-name", Google::Spanner::V1::TransactionOptions, Hash] end - mock.expect :execute_streaming_sql, results_enum, ["session-name", "SELECT * FROM users", Hash] - mock.expect :commit, commit_resp, ["session-name", Array, Hash] - end - end - - doctest.before "Google::Cloud::Spanner::Snapshot#execute@Query using query parameters:" do - mock_spanner do |mock, mock_instances, mock_databases| - 20.times do - mock.expect :create_session, session_grpc, ["projects/my-project/instances/my-instance/databases/my-database", Hash] - end - 5.times do - mock.expect :begin_transaction, tx_resp, ["session-name", Google::Spanner::V1::TransactionOptions, Hash] - end - mock.expect :execute_streaming_sql, results_enum, ["session-name", "SELECT * FROM users WHERE active = @active", Hash] + mock.expect :execute_streaming_sql, results_enum, ["session-name", String, Hash] mock.expect :commit, commit_resp, ["session-name", Array, Hash] end end @@ -748,20 +713,7 @@ def mock_spanner 5.times do mock.expect :begin_transaction, tx_resp, ["session-name", Google::Spanner::V1::TransactionOptions, Hash] end - mock.expect :execute_streaming_sql, results_enum, ["session-name", "SELECT * FROM users", Hash] - mock.expect :commit, commit_resp, ["session-name", Array, Hash] - end - end - - doctest.before "Google::Cloud::Spanner::Transaction#execute@Query using query parameters:" do - mock_spanner do |mock, mock_instances, mock_databases| - 20.times do - mock.expect :create_session, session_grpc, ["projects/my-project/instances/my-instance/databases/my-database", Hash] - end - 5.times do - mock.expect :begin_transaction, tx_resp, ["session-name", Google::Spanner::V1::TransactionOptions, Hash] - end - mock.expect :execute_streaming_sql, results_enum, ["session-name", "SELECT * FROM users WHERE active = @active", Hash] + mock.expect :execute_streaming_sql, results_enum, ["session-name", String, Hash] mock.expect :commit, commit_resp, ["session-name", Array, Hash] end end