Skip to content

Commit

Permalink
Merge pull request #614 from drizzle-team/beta
Browse files Browse the repository at this point in the history
  • Loading branch information
dankochetov authored May 24, 2023
2 parents 474f1a3 + 1f1c16b commit 06ff652
Show file tree
Hide file tree
Showing 38 changed files with 1,533 additions and 313 deletions.
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@
<h1>Drizzle ORM <a href=""><img alt="npm" src="https://img.shields.io/npm/v/drizzle-orm?label="></a></h1>
<img alt="npm" src="https://img.shields.io/npm/dm/drizzle-orm">
<img alt="npm bundle size" src="https://img.shields.io/bundlephobia/min/drizzle-orm">
<a href="https://discord.gg/yfjTbVXMW4"><img alt="Discord" src="https://img.shields.io/discord/1043890932593987624?label=Discord"></a>
<a href="https://discord.gg/yfjTbVXMW4" target="_blank"><img alt="Discord" src="https://img.shields.io/discord/1043890932593987624?label=Discord"></a>
<img alt="License" src="https://img.shields.io/npm/l/drizzle-orm">
<h6><i>If you know SQL, you know Drizzle ORM</i></h6>
<hr />
</div>

Drizzle ORM is a TypeScript ORM for SQL databases designed with maximum type safety in mind. It comes with a [drizzle-kit](https://github.com/drizzle-team/drizzle-kit-mirror) CLI companion for automatic SQL migrations generation. Drizzle ORM is meant to be a library, not a framework. It stays as an opt-in solution all the time at any levels.

The ORM main philosophy is "If you know SQL, you know Drizzle ORM". We follow the SQL-like syntax whenever possible, are strongly typed ground up and fail at compile time, not in runtime.
The ORM's main philosophy is "If you know SQL, you know Drizzle ORM". We follow the SQL-like syntax whenever possible, are strongly typed ground up, and fail at compile time, not in runtime.

Drizzle ORM is being battle-tested on production projects by multiple teams 🚀 Give it a try and let us know if you have any questions or feedback on [Discord](https://discord.gg/yfjTbVXMW4).

## Feature list
## Features

- Full type safety
- [Smart automated migrations generation](https://github.com/drizzle-team/drizzle-kit-mirror)
Expand All @@ -28,7 +27,7 @@ Drizzle ORM is being battle-tested on production projects by multiple teams 🚀

## Documentation

Check the full documenation on [the website](https://orm.drizzle.team)
Check the full documentation on [the website](https://orm.drizzle.team)

## Supported databases

Expand Down
3 changes: 3 additions & 0 deletions changelogs/drizzle-orm/0.26.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- 🐛 Fixed including multiple relations on the same level in RQB (#599)
- 🐛 Updated migrators for relational queries support (#601)
- 🐛 Fixed invoking .findMany() without arguments
2 changes: 1 addition & 1 deletion drizzle-orm/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "drizzle-orm",
"version": "0.26.0",
"version": "0.26.1",
"description": "Drizzle ORM package for SQL databases",
"type": "module",
"scripts": {
Expand Down
5 changes: 4 additions & 1 deletion drizzle-orm/src/aws-data-api/pg/migrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import type { MigrationConfig } from '~/migrator';
import { readMigrationFiles } from '~/migrator';
import type { AwsDataApiPgDatabase } from './driver';

export async function migrate(db: AwsDataApiPgDatabase, config: string | MigrationConfig) {
export async function migrate<TSchema extends Record<string, unknown>>(
db: AwsDataApiPgDatabase<TSchema>,
config: string | MigrationConfig,
) {
const migrations = readMigrationFiles(config);
await db.dialect.migrate(migrations, db.session);
}
7 changes: 5 additions & 2 deletions drizzle-orm/src/better-sqlite3/migrator.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import type { MigrationConfig} from '~/migrator';
import type { MigrationConfig } from '~/migrator';
import { readMigrationFiles } from '~/migrator';
import type { BetterSQLite3Database } from './driver';

export function migrate(db: BetterSQLite3Database, config: string | MigrationConfig) {
export function migrate<TSchema extends Record<string, unknown>>(
db: BetterSQLite3Database<TSchema>,
config: string | MigrationConfig,
) {
const migrations = readMigrationFiles(config);
db.dialect.migrate(migrations, db.session);
}
2 changes: 0 additions & 2 deletions drizzle-orm/src/better-sqlite3/session.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { Database, RunResult, Statement } from 'better-sqlite3';
import util from 'node:util';
import type { Logger } from '~/logger';
import { NoopLogger } from '~/logger';
import { type RelationalSchemaConfig, type TablesRelationalConfig } from '~/relations';
Expand Down Expand Up @@ -101,7 +100,6 @@ export class PreparedQuery<T extends PreparedQueryConfig = PreparedQueryConfig>

const rows = this.values(placeholderValues);
if (customResultMapper) {
console.log('rows:', util.inspect(rows, { depth: null, colors: true }));
return customResultMapper(rows) as T['all'];
}
return rows.map((row) => mapResultRow(fields!, row, joinsNotNullableMap));
Expand Down
7 changes: 5 additions & 2 deletions drizzle-orm/src/bun-sqlite/migrator.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import type { MigrationConfig} from '~/migrator';
import type { MigrationConfig } from '~/migrator';
import { readMigrationFiles } from '~/migrator';
import type { BunSQLiteDatabase } from './driver';

export function migrate(db: BunSQLiteDatabase, config: string | MigrationConfig) {
export function migrate<TSchema extends Record<string, unknown>>(
db: BunSQLiteDatabase<TSchema>,
config: string | MigrationConfig,
) {
const migrations = readMigrationFiles(config);
db.dialect.migrate(migrations, db.session);
}
7 changes: 5 additions & 2 deletions drizzle-orm/src/d1/migrator.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import type { MigrationConfig} from '~/migrator';
import type { MigrationConfig } from '~/migrator';
import { readMigrationFiles } from '~/migrator';
import type { DrizzleD1Database } from './driver';

export async function migrate(db: DrizzleD1Database, config: string | MigrationConfig) {
export async function migrate<TSchema extends Record<string, unknown>>(
db: DrizzleD1Database<TSchema>,
config: string | MigrationConfig,
) {
const migrations = readMigrationFiles(config);
await db.dialect.migrate(migrations, db.session);
}
2 changes: 1 addition & 1 deletion drizzle-orm/src/d1/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export class PreparedQuery<T extends PreparedQueryConfig = PreparedQueryConfig>
if (!fields && !customResultMapper) {
const params = fillPlaceholders(this.params, placeholderValues ?? {});
logger.logQuery(queryString, params);
return stmt.bind(...params).all().then(({ results }) => results!);
return stmt.bind(...params).all().then(({ results }) => results![0]);
}

const rows = await this.values(placeholderValues);
Expand Down
5 changes: 4 additions & 1 deletion drizzle-orm/src/libsql/migrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import type { MigrationConfig } from '~/migrator';
import { readMigrationFiles } from '~/migrator';
import type { LibSQLDatabase } from './driver';

export function migrate(db: LibSQLDatabase, config: MigrationConfig) {
export function migrate<TSchema extends Record<string, unknown>>(
db: LibSQLDatabase<TSchema>,
config: MigrationConfig,
) {
const migrations = readMigrationFiles(config);
return db.dialect.migrate(migrations, db.session);
}
2 changes: 1 addition & 1 deletion drizzle-orm/src/libsql/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export class PreparedQuery<T extends PreparedQueryConfig = PreparedQueryConfig>
const params = fillPlaceholders(this.params, placeholderValues ?? {});
logger.logQuery(queryString, params);
const stmt: InStatement = { sql: queryString, args: params as InArgs };
return (tx ? tx.execute(stmt) : client.execute(stmt)).then(({ rows }) => rows.map((row) => normalizeRow(row)));
return (tx ? tx.execute(stmt) : client.execute(stmt)).then(({ rows }) => normalizeRow(rows[0]));
}

const rows = await this.values(placeholderValues);
Expand Down
151 changes: 75 additions & 76 deletions drizzle-orm/src/mysql-core/dialect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,18 +397,7 @@ export class MySqlDialect {

return {
tableTsKey: tableConfig.tsName,
sql: this.buildSelectQuery({
table,
fields: {},
fieldsFlat: selectionEntries.map(([, c]) => ({
path: [c.name],
field: c as AnyMySqlColumn,
})),
groupBy: [],
orderBy: [],
joins: [],
withList: [],
}),
sql: table,
selection,
};
}
Expand Down Expand Up @@ -480,10 +469,36 @@ export class MySqlDialect {
fieldsSelection[key] = value;
}

let where;
if (config.where) {
const whereSql = typeof config.where === 'function' ? config.where(aliasedFields, operators) : config.where;
where = whereSql && mapColumnsInSQLToAlias(whereSql, tableAlias);
}

const groupBy = (tableConfig.primaryKey.length ? tableConfig.primaryKey : Object.values(tableConfig.columns)).map(
(c) => aliasedTableColumn(c as AnyMySqlColumn, tableAlias),
);

let orderByOrig = typeof config.orderBy === 'function'
? config.orderBy(aliasedFields, orderByOperators)
: config.orderBy ?? [];
if (!Array.isArray(orderByOrig)) {
orderByOrig = [orderByOrig];
}
const orderBy = orderByOrig.map((orderByValue) => {
if (orderByValue instanceof Column) {
return aliasedTableColumn(orderByValue, tableAlias) as AnyMySqlColumn;
}
return mapColumnsInSQLToAlias(orderByValue, tableAlias);
});

const builtRelations: { key: string; value: BuildRelationalQueryResult }[] = [];
const joins: JoinsValue[] = [];
const builtRelationFields: SelectedFieldsOrdered = [];

let result;

let selectedRelationIndex = 0;
for (const { key: selectedRelationKey, value: selectedRelationValue } of selectedRelations) {
let relation: Relation | undefined;
for (const [relationKey, relationValue] of Object.entries(tableConfig.relations)) {
Expand Down Expand Up @@ -513,9 +528,20 @@ export class MySqlDialect {
);
builtRelations.push({ key: selectedRelationKey, value: builtRelation });

joins.push({
table: new Subquery(builtRelation.sql, {}, relationAlias),
alias: selectedRelationKey,
let relationWhere;
if (typeof selectedRelationValue === 'object' && selectedRelationValue.limit) {
const field = sql`${sql.identifier(relationAlias)}.${sql.identifier('__drizzle_row_number')}`;
relationWhere = and(
relationWhere,
or(and(sql`${field} <= ${selectedRelationValue.limit}`), sql`(${field} is null)`),
);
}

const join: JoinsValue = {
table: builtRelation.sql instanceof Table
? aliasedTable(builtRelation.sql as AnyMySqlTable, relationAlias)
: new Subquery(builtRelation.sql, {}, relationAlias),
alias: relationAlias,
on: and(
...normalizedRelation.fields.map((field, i) =>
eq(
Expand All @@ -525,7 +551,7 @@ export class MySqlDialect {
),
),
joinType: 'left',
});
};

const elseField = sql`json_arrayagg(json_array(${
sql.join(
Expand All @@ -541,10 +567,41 @@ export class MySqlDialect {
sql.join(normalizedRelation.references.map((c) => aliasedTableColumn(c, relationAlias)), sql.raw(' or '))
}) = 0, '[]', ${elseField})`.as(selectedRelationKey);

builtRelationFields.push({
const builtRelationField = {
path: [selectedRelationKey],
field,
};

result = this.buildSelectQuery({
table: result ? new Subquery(result, {}, tableAlias) : aliasedTable(table, tableAlias),
fields: {},
fieldsFlat: [
...Object.entries(tableConfig.columns).map(([tsKey, column]) => ({
path: [tsKey],
field: aliasedTableColumn(column, tableAlias) as AnyMySqlColumn,
})),
...(selectedRelationIndex === selectedRelations.length - 1
? selectedExtras.map(({ key, value }) => ({
path: [key],
field: value,
}))
: []),
...builtRelationFields.map(({ path, field }) => ({
path,
field: sql`${sql.identifier(tableAlias)}.${sql.identifier((field as SQL.Aliased).fieldAlias)}`,
})),
builtRelationField,
],
where: relationWhere,
groupBy,
orderBy: selectedRelationIndex === selectedRelations.length - 1 ? orderBy : [],
joins: [join],
withList: [],
});

joins.push(join);
builtRelationFields.push(builtRelationField);
selectedRelationIndex++;
}

const finalFieldsSelection: SelectedFieldsOrdered = Object.entries(fieldsSelection).map(([key, value]) => {
Expand All @@ -554,24 +611,6 @@ export class MySqlDialect {
};
});

const initialWhere = and(
...selectedRelations.filter(({ key }) => {
const relation = config.with?.[key];
return typeof relation === 'object' && (relation as DBQueryConfig<'many'>).limit !== undefined;
}).map(({ key }) => {
const field = sql`${sql.identifier(`${tableAlias}_${key}`)}.${sql.identifier('__drizzle_row_number')}`;
const value = config.with![key] as DBQueryConfig<'many'>;
const cond = or(and(sql`${field} <= ${value.limit}`), sql`(${field} is null)`);
return cond;
}),
);

const groupBy = (builtRelationFields.length
? (tableConfig.primaryKey.length ? tableConfig.primaryKey : Object.values(tableConfig.columns)).map((c) =>
aliasedTableColumn(c, tableAlias)
)
: []) as AnyMySqlColumn[];

const finalFieldsFlat: SelectedFieldsOrdered = isRoot
? [
...finalFieldsSelection.map(({ path, field }) => ({
Expand Down Expand Up @@ -605,30 +644,6 @@ export class MySqlDialect {
});
}

const initialFieldsFlat: SelectedFieldsOrdered = [
{
path: [],
field: sql`${sql.identifier(tableAlias)}.*`,
},
...selectedExtras.map(({ key, value }) => ({
path: [key],
field: value,
})),
...builtRelationFields,
];

let orderByOrig = typeof config.orderBy === 'function'
? config.orderBy(aliasedFields, orderByOperators)
: config.orderBy ?? [];
if (!Array.isArray(orderByOrig)) {
orderByOrig = [orderByOrig];
}
const orderBy = orderByOrig.map((orderByValue) => {
if (orderByValue instanceof Column) {
return aliasedTableColumn(orderByValue, tableAlias) as AnyMySqlColumn;
}
return mapColumnsInSQLToAlias(orderByValue, tableAlias);
});
if (!isRoot && !config.limit && orderBy.length > 0) {
finalFieldsFlat.push({
path: ['__drizzle_row_number'],
Expand All @@ -653,24 +668,8 @@ export class MySqlDialect {
}
}

let result = this.buildSelectQuery({
table: aliasedTable(table, tableAlias),
fields: {},
fieldsFlat: initialFieldsFlat,
where: initialWhere,
groupBy,
orderBy: [],
joins,
withList: [],
});

let where;
if (config.where) {
const whereSql = typeof config.where === 'function' ? config.where(aliasedFields, operators) : config.where;
where = whereSql && mapColumnsInSQLToAlias(whereSql, tableAlias);
}
result = this.buildSelectQuery({
table: new Subquery(result, {}, tableAlias),
table: result ? new Subquery(result, {}, tableAlias) : aliasedTable(table, tableAlias),
fields: {},
fieldsFlat: finalFieldsFlat,
where,
Expand Down
5 changes: 3 additions & 2 deletions drizzle-orm/src/mysql-core/query-builders/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
type TableRelationalConfig,
type TablesRelationalConfig,
} from '~/relations';
import { type SQL } from '~/sql';
import { type KnownKeysOnly } from '~/utils';
import { type MySqlDialect } from '../dialect';
import {
Expand Down Expand Up @@ -42,7 +43,7 @@ export class RelationalQueryBuilder<
this.tableConfig,
this.dialect,
this.session,
config ? (config as DBQueryConfig<'many', true>) : true,
config ? (config as DBQueryConfig<'many', true>) : {},
'many',
);
}
Expand Down Expand Up @@ -97,7 +98,7 @@ export class MySqlRelationalQuery<
true,
);

const builtQuery = this.dialect.sqlToQuery(query.sql);
const builtQuery = this.dialect.sqlToQuery(query.sql as SQL);
return this.session.prepareQuery(
builtQuery,
undefined,
Expand Down
5 changes: 4 additions & 1 deletion drizzle-orm/src/mysql2/migrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import type { MigrationConfig } from '~/migrator';
import { readMigrationFiles } from '~/migrator';
import type { MySql2Database } from './driver';

export async function migrate(db: MySql2Database, config: MigrationConfig) {
export async function migrate<TSchema extends Record<string, unknown>>(
db: MySql2Database<TSchema>,
config: MigrationConfig,
) {
const migrations = readMigrationFiles(config);
await db.dialect.migrate(migrations, db.session, config);
}
Loading

0 comments on commit 06ff652

Please sign in to comment.