Commit 85359ca0 authored by Alex Kalderimis's avatar Alex Kalderimis

Simplify representation of columns

Using the column attribute object is better than a pair
of (name, column), since the column includes the name.
parent 65d0b8be
...@@ -15,6 +15,20 @@ module Gitlab ...@@ -15,6 +15,20 @@ module Gitlab
# #
# The requirements are that the table must have an ID column used to # The requirements are that the table must have an ID column used to
# identify the rows to be updated. # identify the rows to be updated.
#
# Usage:
#
# mapping = {
# issue_a => { title: 'This title', relative_position: 100 },
# issue_b => { title: 'That title', relative_position: 173 }
# }
#
# ::Gitlab::Database::SetAll.set_all(%i[title relative_position], mapping)
#
# Note that this is a very low level tool, and operates on the raw column
# values. Enums/state fields must be translated into their underlying
# representations, for example, and no hooks will be called.
#
module SetAll module SetAll
COMMA = ', ' COMMA = ', '
...@@ -30,7 +44,7 @@ module Gitlab ...@@ -30,7 +44,7 @@ module Gitlab
@table_name = model.table_name @table_name = model.table_name
@connection = model.connection @connection = model.connection
@columns = ([:id] + columns).map { |c| [c, model.column_for_attribute(c)] } @columns = ([:id] + columns).map { |c| model.column_for_attribute(c) }
@mapping = mapping @mapping = mapping
end end
...@@ -38,7 +52,7 @@ module Gitlab ...@@ -38,7 +52,7 @@ module Gitlab
mapping.flat_map do |k, v| mapping.flat_map do |k, v|
obj_id = k.try(:id) || k obj_id = k.try(:id) || k
v = v.merge(id: obj_id) v = v.merge(id: obj_id)
columns.map { |c| query_attribute(c, k, v) } columns.map { |c| query_attribute(c, k, v.with_indifferent_access) }
end end
end end
...@@ -51,10 +65,9 @@ module Gitlab ...@@ -51,10 +65,9 @@ module Gitlab
end end
def query_attribute(column, key, values) def query_attribute(column, key, values)
column_name = column.first value = values[column.name]
value = values[column_name] key[column.name] = value if key.try(:id) # optimistic update
key[column_name] = value if key.try(:id) # optimistic update ActiveRecord::Relation::QueryAttribute.from_user(nil, value, ActiveModel::Type.lookup(column.type))
ActiveRecord::Relation::QueryAttribute.from_user(nil, value, ActiveModel::Type.lookup(column.second.type))
end end
def values def values
...@@ -65,7 +78,7 @@ module Gitlab ...@@ -65,7 +78,7 @@ module Gitlab
binds = columns.map do |c| binds = columns.map do |c|
bind = "$#{counter += 1}" bind = "$#{counter += 1}"
# PG is not great at inferring types - help it for the first row. # PG is not great at inferring types - help it for the first row.
bind += "::#{c.second.sql_type}" unless typed bind += "::#{c.sql_type}" unless typed
bind bind
end end
typed = true typed = true
...@@ -75,7 +88,7 @@ module Gitlab ...@@ -75,7 +88,7 @@ module Gitlab
end end
def sql def sql
column_names = columns.map(&:first) column_names = columns.map(&:name)
cte_columns = column_names.map do |c| cte_columns = column_names.map do |c|
connection.quote_column_name("cte_#{c}") connection.quote_column_name("cte_#{c}")
end end
...@@ -90,7 +103,7 @@ module Gitlab ...@@ -90,7 +103,7 @@ module Gitlab
end end
def update! def update!
log_name = "SetAll #{table_name} #{columns.drop(1).map(&:first)}:#{mapping.size}" log_name = "SetAll #{table_name} #{columns.drop(1).map(&:name)}:#{mapping.size}"
if no_prepared_statement? if no_prepared_statement?
# A workaround for https://github.com/rails/rails/issues/24893 # A workaround for https://github.com/rails/rails/issues/24893
# When prepared statements are prevented (such as when using the # When prepared statements are prevented (such as when using the
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment