[Not ready for merge] Here is an attempt to add support of User-defined types to Exposed. It was kind of experiment, and I cannot say that I am completely satisfied with the result, so it would be nice to discuss it and probably change the direction.
Concept
The problem I started with is the improvement of enumeration support. One part of it is native Postgres enumerations.
Postgres enum (and other user-defined types) can be created with sql command (create type <name> ...), has its own type name, and can be used among different tables. So the main conceptual idea of the feature is to make a ddl aware column type that should be responsible for being created inside a database.
I tried 2 options:
Adding UserDefinedColumnType<T>(val name: String) : ColumnType<T>(), DdlAware which has own name (the name of the type inside database), and which should implement DdlAware methods.
Creating independent classes structure for such types like UserDefinedType(val name: String): DdlAware and ColumnTypes that would consume such types.
I stopped with the first variant because it looks less complex in general, and because ColumnTypes are also kind of just types without connection to certain table columns, and there is no problem to create a ColumnType and then register it in several tables.
From the usage perspective, it looks like:
enum class FooBar { FOO, BAR }
val fooBarEnumType = pgEnumerationType<FooBar>("test_postgres_enumeration_foobar") // PGEnumColumnType(name, T::class)
val tester = object : IntIdTable() {
val value = enumeration("value", fooBarEnumType)
}
User can create a type and use it in different tables. SchmeUtils will create required types if it's necessary (but not delete after). For the table above the following sql would be created:
CREATE TYPE test_postgres_enumeration_foobar AS ENUM ('FOO', 'BAR')
CREATE TABLE IF NOT EXISTS "testpostgresenumeration$tester$1" (
id SERIAL PRIMARY KEY,
"value" test_postgres_enumeration_foobar NOT NULL
)
in UserDefinedTypesTests class there are example of usage postgres enums, postgres ranges, and oracle varrays.
Concerns
My biggest concern now is that the feature is too vendor-specific and even for the current examples requires specific methods from underlying db drivers.
Even for the current example, I had to add to the core package postgres and oracle packages (libs.postgre, libs.oracle1). It's needed because for postgres types it was necessary to use PGEnum, and for oracle ARRAY (via createOracleArray() method of OracleConnection)
I had to pass through PreparedStatementApi interface the original statement (val statement: PreparedStatement) or connection of it, because creating oracle array requires unwrapped OracleConnection
Possible solution
The first option is to continue anyway. Even if it's not for 80% of users, it can be useful for some of them, and we can add support for more user-defined types.
It's also a variant to avoid creating of the types by ourself and expect that user will create it by himself (with exec() for example), and pass the name of a custom type to the column during describing table. But it will not solve the problem of specific drivers usage (we still need to convert values to PGObject and so on).
Make it inside vendor-specific package if we introduce them in the future. If the vendor-specific packages are created in the future, and these packages will know about driver interfaces, this feature can be implemented there too.
And we can say that such a feature is not the thing needed for the library on the API stabilization way.
Other user-defined types
There are several examples of custom types that are not covered with PR.
One of the most complicated ones that have own columns like tables. For example in Postgres it's composite types:
CREATE TYPE custom AS (
value VARCHAR(15)
);
or object types in oracle:
CREATE TYPE custom AS OBJECT (
value VARCHAR2(50)
);
Another examples:
postgres domain types CREATE DOMAIN positive_integer AS INT CHECK (VALUE > 0); or similar from sql server CREATE TYPE Age FROM INT NOT NULL CHECK (Age >= 0 AND Age <= 130);
[Not ready for merge] Here is an attempt to add support of User-defined types to Exposed. It was kind of experiment, and I cannot say that I am completely satisfied with the result, so it would be nice to discuss it and probably change the direction.
Concept
The problem I started with is the improvement of enumeration support. One part of it is native Postgres enumerations.
Postgres enum (and other user-defined types) can be created with sql command (
create type <name> ...
), has its own type name, and can be used among different tables. So the main conceptual idea of the feature is to make a ddl aware column type that should be responsible for being created inside a database.I tried 2 options:
UserDefinedColumnType<T>(val name: String) : ColumnType<T>(), DdlAware
which has ownname
(the name of the type inside database), and which should implementDdlAware
methods.UserDefinedType(val name: String): DdlAware
and ColumnTypes that would consume such types.I stopped with the first variant because it looks less complex in general, and because
ColumnType
s are also kind of just types without connection to certain table columns, and there is no problem to create a ColumnType and then register it in several tables.From the usage perspective, it looks like:
User can create a type and use it in different tables.
SchmeUtils
will create required types if it's necessary (but not delete after). For the table above the following sql would be created:in
UserDefinedTypesTests
class there are example of usage postgres enums, postgres ranges, and oracle varrays.Concerns
My biggest concern now is that the feature is too vendor-specific and even for the current examples requires specific methods from underlying db drivers.
Even for the current example, I had to add to the core package postgres and oracle packages (
libs.postgre
,libs.oracle1
). It's needed because for postgres types it was necessary to usePGEnum
, and for oracleARRAY
(viacreateOracleArray()
method ofOracleConnection
)I had to pass through
PreparedStatementApi
interface the original statement (val statement: PreparedStatement) or connection of it, because creating oracle array requires unwrappedOracleConnection
Possible solution
The first option is to continue anyway. Even if it's not for 80% of users, it can be useful for some of them, and we can add support for more user-defined types.
It's also a variant to avoid creating of the types by ourself and expect that user will create it by himself (with
exec()
for example), and pass the name of a custom type to the column during describing table. But it will not solve the problem of specific drivers usage (we still need to convert values to PGObject and so on).Make it inside vendor-specific package if we introduce them in the future. If the vendor-specific packages are created in the future, and these packages will know about driver interfaces, this feature can be implemented there too.
And we can say that such a feature is not the thing needed for the library on the API stabilization way.
Other user-defined types
There are several examples of custom types that are not covered with PR.
One of the most complicated ones that have own columns like tables. For example in Postgres it's composite types:
or object types in oracle:
Another examples:
CREATE DOMAIN positive_integer AS INT CHECK (VALUE > 0);
or similar from sql serverCREATE TYPE Age FROM INT NOT NULL CHECK (Age >= 0 AND Age <= 130);