Class: ActiveRecord::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/activerecord_spanner_adapter/base.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

._buffer_record(values, method) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/activerecord_spanner_adapter/base.rb', line 116

def self._buffer_record values, method
  primary_key_value =
    if primary_key.is_a? Array
      _set_composite_primary_key_values primary_key, values
    else
      _set_single_primary_key_value primary_key, values
    end

   = TableMetadata.new self, arel_table
  columns, grpc_values = _create_grpc_values_for_insert , values

  write = Google::Cloud::Spanner::V1::Mutation::Write.new(
    table: arel_table.name,
    columns: columns,
    values: [grpc_values.list_value]
  )
  mutation = Google::Cloud::Spanner::V1::Mutation.new(
    "#{method}": write
  )

  connection.current_spanner_transaction.buffer mutation

  primary_key_value
end

._insert_record(values) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/activerecord_spanner_adapter/base.rb', line 46

def self._insert_record values
  return super unless buffered_mutations? || (primary_key && values.is_a?(Hash))

  return _buffer_record values, :insert if buffered_mutations?

  primary_key_value =
    if primary_key.is_a? Array
      _set_composite_primary_key_values primary_key, values
    else
      _set_single_primary_key_value primary_key, values
    end
  if ActiveRecord::VERSION::MAJOR >= 7
    im = Arel::InsertManager.new arel_table
    im.insert(values.transform_keys { |name| arel_table[name] })
  else
    im = arel_table.compile_insert _substitute_values(values)
  end
  connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
end

._set_composite_primary_key_values(primary_key, values) ⇒ Object



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/activerecord_spanner_adapter/base.rb', line 141

def self._set_composite_primary_key_values primary_key, values
  primary_key_value = []
  primary_key.each do |col|
    value = values[col]

    if !value && prefetch_primary_key?
      value =
        if ActiveRecord::VERSION::MAJOR >= 7
          ActiveModel::Attribute.from_database col, next_sequence_value, ActiveModel::Type::BigInteger.new
        else
          next_sequence_value
        end
      values[col] = value
    end
    if value.is_a? ActiveModel::Attribute
      value = value.value
    end
    primary_key_value.append value
  end
  primary_key_value
end

._set_single_primary_key_value(primary_key, values) ⇒ Object



163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/activerecord_spanner_adapter/base.rb', line 163

def self._set_single_primary_key_value primary_key, values
  primary_key_value = values[primary_key] || values[primary_key.to_sym]

  if !primary_key_value && prefetch_primary_key?
    primary_key_value = next_sequence_value
    if ActiveRecord::VERSION::MAJOR >= 7
      values[primary_key] = ActiveModel::Attribute.from_database primary_key, primary_key_value,
                                                                 ActiveModel::Type::BigInteger.new
    else
      values[primary_key] = primary_key_value
    end
  end
  primary_key_value
end

._upsert_record(values) ⇒ Object



66
67
68
# File 'lib/activerecord_spanner_adapter/base.rb', line 66

def self._upsert_record values
  _buffer_record values, :insert_or_update
end

.active_transaction?Boolean

Returns:

  • (Boolean)


189
190
191
192
# File 'lib/activerecord_spanner_adapter/base.rb', line 189

def self.active_transaction?
  current_transaction = connection.current_transaction
  !(current_transaction.nil? || current_transaction.is_a?(ConnectionAdapters::NullTransaction))
end

.buffered_mutations?Boolean

Returns:

  • (Boolean)


42
43
44
# File 'lib/activerecord_spanner_adapter/base.rb', line 42

def self.buffered_mutations?
  spanner_adapter? && connection&.current_spanner_transaction&.isolation == :buffered_mutations
end

.create(attributes = nil, &block) ⇒ Object



29
30
31
32
33
34
35
36
# File 'lib/activerecord_spanner_adapter/base.rb', line 29

def self.create attributes = nil, &block
  return super unless spanner_adapter?
  return super if active_transaction?

  transaction isolation: :buffered_mutations do
    return super
  end
end

.create!(attributes = nil, &block) ⇒ Object

Creates an object (or multiple objects) and saves it to the database. This method will use mutations instead of DML if there is no active transaction, or if the active transaction has been created with the option isolation: :buffered_mutations.



20
21
22
23
24
25
26
27
# File 'lib/activerecord_spanner_adapter/base.rb', line 20

def self.create! attributes = nil, &block
  return super unless spanner_adapter?
  return super if active_transaction?

  transaction isolation: :buffered_mutations do
    return super
  end
end

.delete_allObject

Deletes all records of this class. This method will use mutations instead of DML if there is no active transaction, or if the active transaction has been created with the option isolation: :buffered_mutations.



180
181
182
183
184
185
186
187
# File 'lib/activerecord_spanner_adapter/base.rb', line 180

def self.delete_all
  return super unless spanner_adapter?
  return super if active_transaction?

  transaction isolation: :buffered_mutations do
    return super
  end
end

.insert_all(_attributes, _returning: nil, _unique_by: nil) ⇒ Object

Raises:

  • (NotImplementedError)


70
71
72
# File 'lib/activerecord_spanner_adapter/base.rb', line 70

def self.insert_all _attributes, _returning: nil, _unique_by: nil
  raise NotImplementedError, "Cloud Spanner does not support skip_duplicates."
end

.insert_all!(attributes, returning: nil) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/activerecord_spanner_adapter/base.rb', line 74

def self.insert_all! attributes, returning: nil
  return super unless spanner_adapter?
  return super if active_transaction? && !buffered_mutations?

  # This might seem inefficient, but is actually not, as it is only buffering a mutation locally.
  # The mutations will be sent as one batch when the transaction is committed.
  if active_transaction?
    attributes.each do |record|
      _insert_record record
    end
  else
    transaction isolation: :buffered_mutations do
      attributes.each do |record|
        _insert_record record
      end
    end
  end
end

.spanner_adapter?Boolean

Returns:

  • (Boolean)


38
39
40
# File 'lib/activerecord_spanner_adapter/base.rb', line 38

def self.spanner_adapter?
  connection.adapter_name == "spanner"
end

.unwrap_attribute(attr_or_value) ⇒ Object



194
195
196
197
198
199
200
# File 'lib/activerecord_spanner_adapter/base.rb', line 194

def self.unwrap_attribute attr_or_value
  if attr_or_value.is_a? ActiveModel::Attribute
    attr_or_value.value
  else
    attr_or_value
  end
end

.upsert_all(attributes, returning: nil, unique_by: nil) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/activerecord_spanner_adapter/base.rb', line 93

def self.upsert_all attributes, returning: nil, unique_by: nil
  return super unless spanner_adapter?
  if active_transaction? && !buffered_mutations?
    raise NotImplementedError, "Cloud Spanner does not support upsert using DML. " \
                               "Use upsert outside a transaction block or in a transaction " \
                               "block with isolation: :buffered_mutations"
  end

  # This might seem inefficient, but is actually not, as it is only buffering a mutation locally.
  # The mutations will be sent as one batch when the transaction is committed.
  if active_transaction?
    attributes.each do |record|
      _upsert_record record
    end
  else
    transaction isolation: :buffered_mutations do
      attributes.each do |record|
        _upsert_record record
      end
    end
  end
end

Instance Method Details

#destroyObject

Deletes the object in the database. This method will use mutations instead of DML if there is no active transaction, or if the active transaction has been created with the option isolation: :buffered_mutations.



217
218
219
220
221
222
223
224
# File 'lib/activerecord_spanner_adapter/base.rb', line 217

def destroy
  return super unless self.class.spanner_adapter?
  return super if self.class.active_transaction?

  transaction isolation: :buffered_mutations do
    return super
  end
end

#update(attributes) ⇒ Object

Updates the given attributes of the object in the database. This method will use mutations instead of DML if there is no active transaction, or if the active transaction has been created with the option isolation: :buffered_mutations.



205
206
207
208
209
210
211
212
# File 'lib/activerecord_spanner_adapter/base.rb', line 205

def update attributes
  return super unless self.class.spanner_adapter?
  return super if self.class.active_transaction?

  transaction isolation: :buffered_mutations do
    return super
  end
end