-
-
Notifications
You must be signed in to change notification settings - Fork 565
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat: Optional database aliases for columns in table declarations #2750
base: beta
Are you sure you want to change the base?
Conversation
thanks @L-Mario564! a few comments before I start reviewing:
|
This one should be changed and I guess we can release it |
@AndriiSherman Done. |
@L-Mario564 we've discussed a bit more updates, so we can make this release a full one and ensure all parts of both the ORM and kit are working well Things, that should be done: Create a new setting in the Drizzle instance that will define custom casing mappings for column names. We can start with // In this case, all keys will be automatically mapped to snake_case for database aliases
const db = drizzle(client, { casing: 'snake_case' }) // In this case, all keys will be automatically mapped to camelCase for database aliases
const db = drizzle(client, { casing: 'camelCase' }) // In this case it will work the same as now in your PR
const db = drizzle(client) As long as this setting resides in a DB instance, the casing mapping should be applied to each query, which may have a significant performance impact. We would need to have a cache with an object mapping a table to an object that represents the column key and database alias to be used. For example: { usersTable: { firstName: first_name } }. When the DB object is created, this cache is empty, but after the first usage of a table in any query, the cache will be updated and filled with the data. Make sure to test scenarios for queries with joins and, most importantly, for such queries: db.select({ customKey: users.firstName, customeKey2: users.lastName }).from(users) also one important use case would be db.select({ customKey: sql`${users.firstName}` }).from(users) In this case we should provide this cache to sql template chunks mapper, so we can reuse names from it. It would be awesome to have it in this way:
class Casing {
private cache: Record<string, Record<string,string>> = {}
constructor(){}
getCasing(name: string){
// find this column in cache and retrn it
// return name if nothing was found
}
fillCache(table: AnyTable) {
// if no table was in cache
// go through all table columns and fill them inside this.cache
// if table was in cache - return void
}
}
// inside PgDatabase(or other databases) class we would need to make
class PgDatabase {
constructor(
private casing: Casing = new Casing()
) {
}
} and then this object can be passed to needed parts of a query lifecycle Another change should be added to Drizzle-Kit, but let's first add all the changes and features mentioned above. Then I'll guide you through the needed changes for Drizzle-Kit (it will be just about introspection and a new config value) |
I've done some changes based on @AndriiSherman's comment. const db = drizzle(client, {
casing: /* 'snake_case': map columns to snake case. 'camelCase': map columns to camel case. undefined: don't do any mapping */
}); Upon providing a value for the above setting, expect a query to look like: const users = pgTable('users', {
id: serial().primaryKey(),
firstName: text().notNull(),
age: integer('AGE').notNull(),
});
const db = drizzle(client, { casing: 'snake_case' });
db.select({ firstName: users.firstName, age: users.age }).from(users);
// select "first_name", "AGE" from "users" const users = pgTable('users', {
id: serial().primaryKey(),
first_name: text().notNull(),
age: integer('AGE').notNull(),
});
const db = drizzle(client, { casing: 'camelCase' });
db.select({ first_name: users.first_name, age: users.age }).from(users);
// select "firstName", "AGE" from "users" In both examples To improve query performance, each dialect instance stores a cache for the mapped values. It's stored in the dialect instead of the DB instance since const qb = new QueryBuilder({ casing: 'snake_case' });
qb.select().from(posts);
const db = drizzle(client, { casing: 'snake_case' });
db.select().from(users);
// Cache for qb: { 'public.posts.postTitle': 'post_title' }
// Cache for db: { 'public.users.firstName': 'first_name' } Query builders in callbacks don't suffer from this issue since those stem from the DB instance. const sq = db.$with('sq').as((qb) => qb.select().from(posts));
db.with(sq).select().from(users);
// One cache that looks like: { 'public.posts.postTitle': 'post_title', 'public.users.firstName': 'first_name' } On a smaller note, you can also now define column types without imports: const users = pgTable('users', (t) => ({
id: t.serial().primaryKey(),
firstName: t.text().notNull(),
age: t.integer().notNull(),
})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a few comments, not big ones, just needed clarifications
This PR aims to address one of the items defined in the roadmap for v1.
You can now define columns in tables without specifying a database alias. Works for all dialects.
This:
Is now the same as this:
Some misc. notes for this PR:
line
andpoint
PG column types into thepostgis_extension
folder (previously in the maincolumns
folder).config
in custom types (for all dialects) are now required to be an object (Record<string, any>
).PgBigIntConfig
,PgBigSerialConfig
,PgGeometryConfig
interfaces/types.PgDateConfig
,PgNumericConfig
interfaces/types.MySqlBigIntConfig
.(There might be more changes not noted here).