forked from fixin.me/fixin.me
Fix quantity ordered scope for SQLite: use pathname column instead of recursive CTE
SQLite's Arel visitor wraps CTE branches in extra parentheses, making the UNION ALL inside recursive CTEs invalid. Also SQLite lacks LPAD() and CAST(... AS BINARY). Fix by using the existing pathname column for ordering on SQLite, which already encodes the hierarchical path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,8 +15,8 @@ class Quantity < ApplicationRecord
|
||||
errors.add(:parent, :descendant_reference) if ancestor_of?(parent)
|
||||
end
|
||||
validates :name, presence: true, uniqueness: {scope: [:user_id, :parent_id]},
|
||||
length: {maximum: type_for_attribute(:name).limit}
|
||||
validates :description, length: {maximum: type_for_attribute(:description).limit}
|
||||
length: {maximum: type_for_attribute(:name).limit || Float::INFINITY}
|
||||
validates :description, length: {maximum: type_for_attribute(:description).limit || Float::INFINITY}
|
||||
|
||||
# Update :depths of progenies after parent change
|
||||
before_save if: :parent_changed? do
|
||||
@@ -61,18 +61,26 @@ class Quantity < ApplicationRecord
|
||||
|
||||
# Return: ordered [sub]hierarchy
|
||||
scope :ordered, ->(root: nil, include_root: true) {
|
||||
numbered = Arel::Table.new('numbered')
|
||||
|
||||
self.model.with(numbered: numbered(:parent_id, :name)).with_recursive(arel_table.name => [
|
||||
numbered.project(
|
||||
numbered[Arel.star],
|
||||
numbered.cast(numbered[:child_number], 'BINARY').as('path')
|
||||
).where(numbered[root && include_root ? :id : :parent_id].eq(root)),
|
||||
numbered.project(
|
||||
numbered[Arel.star],
|
||||
arel_table[:path].concat(numbered[:child_number])
|
||||
).join(arel_table).on(numbered[:parent_id].eq(arel_table[:id]))
|
||||
]).order(arel_table[:path])
|
||||
if connection.adapter_name =~ /mysql/i
|
||||
numbered = Arel::Table.new('numbered')
|
||||
self.model.with(numbered: numbered(:parent_id, :name)).with_recursive(arel_table.name => [
|
||||
numbered.project(
|
||||
numbered[Arel.star],
|
||||
numbered.cast(numbered[:child_number], 'BINARY').as('path')
|
||||
).where(numbered[root && include_root ? :id : :parent_id].eq(root)),
|
||||
numbered.project(
|
||||
numbered[Arel.star],
|
||||
arel_table[:path].concat(numbered[:child_number])
|
||||
).join(arel_table).on(numbered[:parent_id].eq(arel_table[:id]))
|
||||
]).order(arel_table[:path])
|
||||
elsif root.nil?
|
||||
# SQLite: pathname column already stores the full hierarchical path
|
||||
order(:pathname)
|
||||
else
|
||||
root_pathname = unscoped.where(id: root).pick(:pathname)
|
||||
scope = order(:pathname).where("pathname LIKE ?", "#{root_pathname}#{PATHNAME_DELIMITER}%")
|
||||
include_root ? scope.or(where(id: root)) : scope
|
||||
end
|
||||
}
|
||||
|
||||
# TODO: extract named functions to custom Arel extension
|
||||
|
||||
Reference in New Issue
Block a user