On Mon, Oct 18, 2021 at 6:05 PM Sterling Brim-DeForest <
s.brimdefor...@gmail.com> wrote:

> I'm running into issues using the lazy_attributes plugin with the
> class_table_inheritance plugin and am hoping to get some help with it.
>
> The parent table has a column that I want to lazily load. If I add the
> lazy_attributes plugin to the parent class I run into errors related to the
> lazy loaded column being missing. When I add it to the subclass then I lose
> typecasting for all columns when setting them through the subclass.
>
> I used postgres because sqlite is not very strict with typing and I wanted
> to use the same setup for both examples.
>
> setup:
>
> DB = Sequel.connect "postgres://user:password@host:port/database_name"
>
> DB.create_table :employees do
>   primary_key :id
>   column :kind, :text
>   column :active, :boolean
>   column :bio, :text
> end
>
> DB.create_table :managers do
>   foreign_key :id, :employees, null: false
>   primary_key [:id]
> end
>
> The base case without adding lazy_attributes manually works as expected
>
> class Employee < Sequel::Model
>   plugin :class_table_inheritance, key: :kind
> end
>
> class Manager < Employee
> end
>
> employee = Employee.create active: 0, bio: "Superclass bio"
> puts "employee's biography: #{employee.bio}"
> puts "Employee 'active' was typecast -> #{employee.active}"
>
> manager = Manager.create active: 0, bio: "Subclass bio"
> puts "manager's biography: #{manager.bio}"
> puts "Manager 'active' was typecast -> #{manager.active}"
>
>
> Adding lazy_attributes on bio to the parent class causes reading bio to
> raise an error: PG::UndefinedColumn: ERROR:  column employees.bio does
> not exist.
>
> class Employee < Sequel::Model
>   plugin :lazy_attributes, :bio
>   plugin :class_table_inheritance, key: :kind
> end
>
> class Manager < Employee
> end
>
> employee = Employee.create active: 0, bio: "Superclass bio"
> # superclass works
> puts "Employee 'active' was typecast -> #{employee.active}"
> puts "employee's biography: #{employee.bio}"
>
> manager = Manager.create active: 0, bio: "Subclass bio"
> puts "Manager 'active' was typecast -> #{manager.active}"
> # subclass breaks reading bio from DB
> puts "manager's biography: #{manager.bio}" # <- error raised here
>
>
> Adding it to the subclass instead means active won't be typecast on set
> for instances of the subclass (it raises PG::DatatypeMismatch: ERROR:
>  column "active" is of type boolean but expression is of type integer)
>
> class Employee < Sequel::Model
>   plugin :class_table_inheritance, key: :kind
> end
>
> class Manager < Employee
>   plugin :lazy_attributes, :bio
> end
>
> # Typecasting works on parent
> employee = Employee.create active: 0, bio: "Superclass bio"
> puts "employee's biography: #{employee.bio}"
> puts "Employee 'active' was typecast -> #{employee.active}"
>
> # Typecasting doesn't work on subclass
> manager = Manager.create active: 0, bio: "Subclass bio" # <- error raised
> here
> puts "manager's biography: #{manager.bio}"
> puts "Manager 'active' was typecast -> #{manager.active}"
>
>
> I'm not sure if this is a bug or user error. Any help would be much
> appreciated!
>

You cannot lazy load in the CTI subclass if you load the lazy_attributes
plugin the parent class, because the CTI subclass uses a subquery. This
isn't a bug, it's a result of how SQL works, in that you cannot select a
column that doesn't exist in what you are selecting from, and that with the
class_table_inheritance/lazy_attributes combination plugin, the subclass
selects from a subquery that doesn't include the column.

You should be able to work around this with the following code:

class Manager < Employee
  old_from = dataset.opts[:from][0]
  set_dataset
dataset.from(old_from.expression.select_append{employees[:bio]}.as(old_from.alias))
  lazy_attributes :bio
end

This works around the issue by re-adding the lazy load column to the
subquery SELECT, but still not including it in the main query SELECT.  The
SQL for the Manager dataset ends up being:

SELECT `employees`.`id` AS 'id', `employees`.`kind` AS 'kind',
`employees`.`active` AS 'active'
FROM (
  SELECT `employees`.`id`, `employees`.`kind`, `employees`.`active`,
`employees`.`bio`
  FROM `employees`
  INNER JOIN `managers` ON (`managers`.`id` = `employees`.`id`)
) AS 'employees'

Hopefully that works for you.  If not, please post back.

Thanks,
Jeremy

-- 
You received this message because you are subscribed to the Google Groups 
"sequel-talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sequel-talk+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/sequel-talk/CADGZSScOC155s5Mox85ge_%2B4xzUXeY1DYPyMq2apWuQP0ji_2w%40mail.gmail.com.

Reply via email to