Open ilyakooo0 opened 5 years ago
Can you give an example of what you mean by hinder? I guess you're talking about the constraint
schemas1 ~ Alter sch (Create tab ('Table (constraints :=> columns)) schema0) schemas0
in createTable
. It's an equality constraint so it doesn't act much differently from inlining, but schemas1
is used in 2 places so it makes the type a little clearer I think.
I mean that there isn't a way (at least I couldn't think of it) to describe the Definition
of two different tables separately and then compose them into one Definition
.
If the two tables are independent, then they will both come from an empty schema. in this case the composition would have to be roughly:
cat a b -> cat a c -> cat a (b + c)
Which is more akin to a special case of &&&
from Arrow
.
Furthermore, a table definition could depend on a different specific table being defined.
You can use the DDL type families to be polymorphic in the initial schema.
createUser
:: Has "public" db schema
=> Definition db (Alter "public" (Create "user" UserTable schema) db)
I think providing something akin to this might be useful (of course not hard-coded to the "public" schema):
type SchemumCreation (s :: Symbol) (table :: SchemumType) =
forall db schema.
Has "public" db schema =>
Definition db (Alter "public" (Create s table schema) db)
I think that's less clear. It wouldn't be as obvious createTable
is a Definition
. And it's not a very general tool, whereas the DDL type families are very useful and general.
Related: I am getting Could not deduce: AllNotNull
with the SchemumCreation
from the previous comment.
I think what I have written might not be strict enough somehow.
(I have a primaryKey
constraint)
If you have a foreign key in your definition then the column(s) it references must be unique and not null. You should specify that your primary key in the other table is not null.
type TasksTable =
'Table
( '[ "pk_task_payload" :=> PrimaryKey '["task", "payload"]
]
:=> '[ "task" ::: 'NoDef :=> 'NotNull (PG Text),
"payload" ::: 'NoDef :=> 'NotNull (PG (Jsonb Value)),
"start_time" ::: 'NoDef :=> 'NotNull (PG UTCTime),
"initial_start_time" ::: 'NoDef :=> 'NotNull (PG UTCTime)
]
)
createTasksTable ::
Has "public" db schema =>
Definition db (Alter "public" (Create "tasks" TasksTable schema) db)
createTasksTable =
createTable
#tasks
( notNullable text `as` #task
:* notNullable jsonb `as` #payload
:* notNullable timestampWithTimeZone `as` #start_time
:* notNullable timestampWithTimeZone `as` #initial_start_time
)
( primaryKey (#task :* #payload) `as` #pk_task_payload
)
This gives me:
• Could not deduce: AllNotNull
'["task" ::: field1, "payload" ::: field]
The exact same code works when it is placed in a constant of type
Definition (Public '[]) (Public '[ "tasks" ::: TasksTable ])
interesting...clearly GHC's having trouble inferring field1
and field2
. It should be able to infer them from the HasAll
constraint but it can't because it's not fully applying the type families yet. Is there a big reason to want the definitions split up and polymorphic?
Just decomposition.
Having one multi-thousand line file with every table and view definition and migration doesn't feel like a terribly good practice and feels like it will hinder maintainability and composability. (Compared to every definition/migration defining its own dependancies and separated into module).
For example define two executables with their own sets of tables, which overlap. Can't think of a good way except for copy-pasting.
In an ideal scenario every function could explicitly specify which tables it relies on and not depend on the whole schema.
You can still decompose monomorphically though. In my project I have modules like V1.hs
, V2.hs
, etc. Each has their own Schemas
type and a migration from the previous one.
You mean explicitly defining some midpoint schema and splitting the definition along that schema?
basically, yes. Although I don't do one for each table, but just introduce a new Vn.hs
each time I need to change the schema. So the first schema I defined was relatively large
Not ideal, but seems reasonable.
Thanks
You can of course break apart that first one however you like. As for maintainability though, I never change the old Vn.hs
s, just introduce a new one.
Currently the types of definition functions like
createTable
constrain the resulting schema, rather than deriving it from the initial schema and the definition itself, which kind of hinders the ability to decompose a schema definition into independentDefinition
s.