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.