Thanks for getting back to me!

Assuming you meant to use lazy attributes in both the parent class and the 
subclass, it semi-worked in that I no longer see the missing column errors 
on the subclass. However, we still don't have typecasting and it seems I 
can't update the lazy loaded column from the subclass.

Here is my setup using the same DB schema as before

class Employee < Sequel::Model
  plugin :lazy_attributes, :bio
  plugin :class_table_inheritance, key: :kind
end

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

*Employee works as expected with typecasting and setting the lazy loaded 
attribute*

employee = Employee.create active: 0, bio: "Superclass bio"
puts "employee's biography: #{employee.bio}"



SQL

INSERT INTO "employees" ("active", "bio", "kind") VALUES (false, 
'Superclass bio', 'Employee') RETURNING "id"
SELECT "employees"."id", "employees"."kind", "employees"."active" FROM 
"employees" WHERE ("id" = 1) LIMIT 1
SELECT "employees"."bio" FROM "employees" WHERE ("id" = 1) LIMIT 1

*Manager still doesn't work quite right. The lazy loading works but bio is 
never inserted*

manager = Manager.create active: false, bio: "Manager bio"
manager.reload
puts %Q{manager's biography is "Manager bio": #{manager.bio == "Manager 
bio"}}

SQL

INSERT INTO "employees" ("kind", "active") VALUES ('Manager', false) 
RETURNING *
INSERT INTO "managers" ("id") VALUES (2) RETURNING *
SELECT "employees"."id", "employees"."kind", "employees"."active" FROM 
(SELECT "employees"."id", "employees"."kind", "employees"."active", 
"employees"."bio" FROM "employees" INNER JOIN "managers" ON 
("managers"."id" = "employees"."id")) AS "employees" WHERE ("id" = 2) LIMIT 
1
SELECT "employees"."bio" FROM (SELECT "employees"."id", "employees"."kind", 
"employees"."active", "employees"."bio" FROM "employees" INNER JOIN 
"managers" ON ("managers"."id" = "employees"."id")) AS "employees" WHERE 
("id" = 2) LIMIT 1


*Typecasting is also not working for the subclass*

Manager.create active: 0

ERROR -- : PG::DatatypeMismatch: ERROR:  column "active" is of type boolean 
but expression is of type integer
LINE 1: ..."employees" ("kind", "active") VALUES ('Manager', 0) RETURNI...  
                                                        ^
HINT:  You will need to rewrite or cast the expression.: INSERT INTO 
"employees" ("kind", "active") VALUES ('Manager', 0) RETURNING *
manager = Manager.create active: false, bio: "Manager bio"


I suspect it's because the schema and columns for Manager are missing the 
bio column.

irb(main):001:0> Manager.db_schema
=> {:id=>{}, :kind=>{}, :active=>{}}
irb(main):002:0> Manager.columns
=> [:id, :kind, :active]



Thanks for your help!


On Monday, October 18, 2021 at 8:38:44 PM UTC-7 Jeremy Evans wrote:

> On Mon, Oct 18, 2021 at 6:05 PM Sterling Brim-DeForest <
> [email protected]> 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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/sequel-talk/a47bcccf-ee83-49cc-848a-97a0350575f3n%40googlegroups.com.

Reply via email to