Skip to content

Commit

Permalink
Fix RQB behavior for tables with same names in different schemas
Browse files Browse the repository at this point in the history
  • Loading branch information
dankochetov committed May 22, 2024
1 parent a78eefe commit ee97199
Show file tree
Hide file tree
Showing 24 changed files with 12,621 additions and 9,058 deletions.
1 change: 1 addition & 0 deletions changelogs/drizzle-orm/0.30.11.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- 🛠️ Fixed RQB behavior for tables with same names in different schemas
64 changes: 32 additions & 32 deletions drizzle-orm/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "drizzle-orm",
"version": "0.30.10",
"version": "0.30.11",
"description": "Drizzle ORM package for SQL databases",
"type": "module",
"scripts": {
Expand Down Expand Up @@ -147,39 +147,39 @@
}
},
"devDependencies": {
"@aws-sdk/client-rds-data": "^3.549.0",
"@cloudflare/workers-types": "^4.20230904.0",
"@aws-sdk/client-rds-data": "^3.569.0",
"@cloudflare/workers-types": "^4.20240502.0",
"@electric-sql/pglite": "^0.1.1",
"@libsql/client": "^0.5.6",
"@neondatabase/serverless": "^0.9.0",
"@op-engineering/op-sqlite": "^2.0.16",
"@opentelemetry/api": "^1.4.1",
"@libsql/client": "^0.6.0",
"@neondatabase/serverless": "^0.9.1",
"@op-engineering/op-sqlite": "^5.0.6",
"@opentelemetry/api": "^1.8.0",
"@originjs/vite-plugin-commonjs": "^1.0.3",
"@planetscale/database": "^1.16.0",
"@types/better-sqlite3": "^7.6.4",
"@types/node": "^20.2.5",
"@types/pg": "^8.10.1",
"@types/react": "^18.2.45",
"@types/sql.js": "^1.4.4",
"@planetscale/database": "^1.18.0",
"@types/better-sqlite3": "^7.6.10",
"@types/node": "^20.12.10",
"@types/pg": "^8.11.6",
"@types/react": "^18.3.1",
"@types/sql.js": "^1.4.9",
"@vercel/postgres": "^0.8.0",
"@xata.io/client": "^0.29.3",
"better-sqlite3": "^8.4.0",
"bun-types": "^0.6.6",
"cpy": "^10.1.0",
"expo-sqlite": "^13.2.0",
"knex": "^2.4.2",
"kysely": "^0.25.0",
"mysql2": "^3.3.3",
"pg": "^8.11.0",
"postgres": "^3.3.5",
"react": "^18.2.0",
"sql.js": "^1.8.0",
"sqlite3": "^5.1.2",
"tslib": "^2.5.2",
"tsx": "^3.12.7",
"vite-tsconfig-paths": "^4.2.0",
"vitest": "^0.31.4",
"zod": "^3.20.2",
"zx": "^7.2.2"
"@xata.io/client": "^0.29.4",
"better-sqlite3": "^9.6.0",
"bun-types": "^1.1.7",
"cpy": "^11.0.1",
"expo-sqlite": "^14.0.3",
"knex": "^3.1.0",
"kysely": "^0.27.3",
"mysql2": "^3.9.7",
"pg": "^8.11.5",
"postgres": "^3.4.4",
"react": "^18.3.1",
"sql.js": "^1.10.3",
"sqlite3": "^5.1.7",
"tslib": "^2.6.2",
"tsx": "^4.9.3",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^1.6.0",
"zod": "^3.23.7",
"zx": "^8.0.2"
}
}
8 changes: 4 additions & 4 deletions drizzle-orm/scripts/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ await fs.remove('dist.new');

await Promise.all([
(async () => {
await $`tsup`;
await $`tsup`.stdio('pipe', 'pipe', 'pipe');
})(),
(async () => {
await $`tsc -p tsconfig.dts.json`;
await $`tsc -p tsconfig.dts.json`.stdio('pipe', 'pipe', 'pipe');
await cpy('dist-dts/**/*.d.ts', 'dist.new', {
rename: (basename) => basename.replace(/\.d\.ts$/, '.d.cts'),
});
Expand All @@ -64,8 +64,8 @@ await Promise.all([
]);

await Promise.all([
$`tsup src/version.ts --no-config --dts --format esm --outDir dist.new`,
$`tsup src/version.ts --no-config --dts --format cjs --outDir dist.new`,
$`tsup src/version.ts --no-config --dts --format esm --outDir dist.new`.stdio('pipe', 'pipe', 'pipe'),
$`tsup src/version.ts --no-config --dts --format cjs --outDir dist.new`.stdio('pipe', 'pipe', 'pipe'),
]);

await $`scripts/fix-imports.ts`;
Expand Down
4 changes: 2 additions & 2 deletions drizzle-orm/src/d1/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ function d1ToRawMapping(results: any) {
}

export class D1PreparedQuery<T extends PreparedQueryConfig = PreparedQueryConfig> extends SQLitePreparedQuery<
{ type: 'async'; run: D1Result; all: T['all']; get: T['get']; values: T['values']; execute: T['execute'] }
{ type: 'async'; run: D1Response; all: T['all']; get: T['get']; values: T['values']; execute: T['execute'] }
> {
static readonly [entityKind]: string = 'D1PreparedQuery';

Expand Down Expand Up @@ -177,7 +177,7 @@ export class D1PreparedQuery<T extends PreparedQueryConfig = PreparedQueryConfig
this.stmt = stmt;
}

run(placeholderValues?: Record<string, unknown>): Promise<D1Result> {
run(placeholderValues?: Record<string, unknown>): Promise<D1Response> {
const params = fillPlaceholders(this.query.params, placeholderValues ?? {});
this.logger.logQuery(this.query.sql, params);
return this.stmt.bind(...params).run();
Expand Down
6 changes: 3 additions & 3 deletions drizzle-orm/src/mysql-core/dialect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
} from '~/relations.ts';
import { Param, type QueryWithTypings, SQL, sql, type SQLChunk, View } from '~/sql/sql.ts';
import { Subquery } from '~/subquery.ts';
import { getTableName, Table } from '~/table.ts';
import { getTableName, getTableUniqueName, Table } from '~/table.ts';
import { orderSelectedFields, type UpdateSet } from '~/utils.ts';
import { and, DrizzleError, eq, type Name, ViewBaseConfig } from '../index.ts';
import { MySqlColumn } from './columns/common.ts';
Expand Down Expand Up @@ -612,7 +612,7 @@ export class MySqlDialect {
} of selectedRelations
) {
const normalizedRelation = normalizeRelation(schema, tableNamesMap, relation);
const relationTableName = relation.referencedTable[Table.Symbol.Name];
const relationTableName = getTableUniqueName(relation.referencedTable);
const relationTableTsName = tableNamesMap[relationTableName]!;
const relationTableAlias = `${tableAlias}_${selectedRelationTsKey}`;
const joinOn = and(
Expand Down Expand Up @@ -909,7 +909,7 @@ export class MySqlDialect {
} of selectedRelations
) {
const normalizedRelation = normalizeRelation(schema, tableNamesMap, relation);
const relationTableName = relation.referencedTable[Table.Symbol.Name];
const relationTableName = getTableUniqueName(relation.referencedTable);
const relationTableTsName = tableNamesMap[relationTableName]!;
const relationTableAlias = `${tableAlias}_${selectedRelationTsKey}`;
const joinOn = and(
Expand Down
4 changes: 2 additions & 2 deletions drizzle-orm/src/pg-core/dialect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import {
type SQLChunk,
} from '~/sql/sql.ts';
import { Subquery } from '~/subquery.ts';
import { getTableName, Table } from '~/table.ts';
import { getTableName, getTableUniqueName, Table } from '~/table.ts';
import { orderSelectedFields, type UpdateSet } from '~/utils.ts';
import { ViewBaseConfig } from '~/view-common.ts';
import type { PgSession } from './session.ts';
Expand Down Expand Up @@ -1218,7 +1218,7 @@ export class PgDialect {
} of selectedRelations
) {
const normalizedRelation = normalizeRelation(schema, tableNamesMap, relation);
const relationTableName = relation.referencedTable[Table.Symbol.Name];
const relationTableName = getTableUniqueName(relation.referencedTable);
const relationTableTsName = tableNamesMap[relationTableName]!;
const relationTableAlias = `${tableAlias}_${selectedRelationTsKey}`;
const joinOn = and(
Expand Down
12 changes: 11 additions & 1 deletion drizzle-orm/src/pg-core/schema.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { entityKind, is } from '~/entity.ts';
import type { SQLWrapper } from '~/index.ts';
import { SQL, sql } from '~/index.ts';
import type { pgEnum } from './columns/enum.ts';
import { pgEnumWithSchema } from './columns/enum.ts';
import { type PgTableFn, pgTableWithSchema } from './table.ts';
import { type pgMaterializedView, pgMaterializedViewWithSchema, type pgView, pgViewWithSchema } from './view.ts';

export class PgSchema<TName extends string = string> {
export class PgSchema<TName extends string = string> implements SQLWrapper {
static readonly [entityKind]: string = 'PgSchema';
constructor(
public readonly schemaName: TName,
Expand All @@ -25,6 +27,14 @@ export class PgSchema<TName extends string = string> {
enum: typeof pgEnum = ((name, values) => {
return pgEnumWithSchema(name, values, this.schemaName);
});

getSQL(): SQL {
return new SQL([sql.identifier(this.schemaName)]);
}

shouldOmitSQLParens(): boolean {
return true;
}
}

export function isPgSchema(obj: unknown): obj is PgSchema {
Expand Down
10 changes: 5 additions & 5 deletions drizzle-orm/src/relations.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type AnyTable, type InferModelFromColumns, isTable, Table } from '~/table.ts';
import { type AnyTable, getTableUniqueName, type InferModelFromColumns, isTable, Table } from '~/table.ts';
import { type AnyColumn, Column } from './column.ts';
import { entityKind, is } from './entity.ts';
import { PrimaryKeyBuilder } from './pg-core/primary-keys.ts';
Expand Down Expand Up @@ -430,7 +430,7 @@ export function extractTablesRelationalConfig<
const tablesConfig: TablesRelationalConfig = {};
for (const [key, value] of Object.entries(schema)) {
if (isTable(value)) {
const dbName = value[Table.Symbol.Name];
const dbName = getTableUniqueName(value);
const bufferedRelations = relationsBuffer[dbName];
tableNamesMap[dbName] = key;
tablesConfig[key] = {
Expand Down Expand Up @@ -462,7 +462,7 @@ export function extractTablesRelationalConfig<
}
}
} else if (is(value, Relations)) {
const dbName: string = value.table[Table.Symbol.Name];
const dbName = getTableUniqueName(value.table);
const tableName = tableNamesMap[dbName];
const relations: Record<string, Relation> = value.config(
configHelpers(value.table),
Expand Down Expand Up @@ -561,7 +561,7 @@ export function normalizeRelation(
};
}

const referencedTableTsName = tableNamesMap[relation.referencedTable[Table.Symbol.Name]];
const referencedTableTsName = tableNamesMap[getTableUniqueName(relation.referencedTable)];
if (!referencedTableTsName) {
throw new Error(
`Table "${relation.referencedTable[Table.Symbol.Name]}" not found in schema`,
Expand All @@ -574,7 +574,7 @@ export function normalizeRelation(
}

const sourceTable = relation.sourceTable;
const sourceTableTsName = tableNamesMap[sourceTable[Table.Symbol.Name]];
const sourceTableTsName = tableNamesMap[getTableUniqueName(sourceTable)];
if (!sourceTableTsName) {
throw new Error(
`Table "${sourceTable[Table.Symbol.Name]}" not found in schema`,
Expand Down
15 changes: 9 additions & 6 deletions drizzle-orm/src/sql/sql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export interface QueryWithTypings extends Query {
*/
export interface SQLWrapper {
getSQL(): SQL;
shouldOmitSQLParens?(): boolean;
}

export function isSQLWrapper(value: unknown): value is SQLWrapper {
Expand Down Expand Up @@ -209,15 +210,15 @@ export class SQL<T = unknown> implements SQLWrapper {
}

let typings: QueryTypingsValue[] | undefined;
if (prepareTyping !== undefined) {
if (prepareTyping) {
typings = [prepareTyping(chunk.encoder)];
}

return { sql: escapeParam(paramStartIndex.value++, mappedValue), params: [mappedValue], typings };
}

if (is(chunk, Placeholder)) {
return { sql: escapeParam(paramStartIndex.value++, chunk), params: [chunk] };
return { sql: escapeParam(paramStartIndex.value++, chunk), params: [chunk], typings: ['none'] };
}

if (is(chunk, SQL.Aliased) && chunk.fieldAlias !== undefined) {
Expand All @@ -244,6 +245,9 @@ export class SQL<T = unknown> implements SQLWrapper {
}

if (isSQLWrapper(chunk)) {
if (chunk.shouldOmitSQLParens?.()) {
return this.buildQueryFromSourceParams([chunk.getSQL()], config);
}
return this.buildQueryFromSourceParams([
new StringChunk('('),
chunk.getSQL(),
Expand Down Expand Up @@ -437,11 +441,10 @@ export type SQLChunk =

export function sql<T>(strings: TemplateStringsArray, ...params: any[]): SQL<T>;
/*
The type of `params` is specified as `SQLSourceParam[]`, but that's slightly incorrect -
The type of `params` is specified as `SQLChunk[]`, but that's slightly incorrect -
in runtime, users won't pass `FakePrimitiveParam` instances as `params` - they will pass primitive values
which will be wrapped in `Param` using `buildChunksFromParam(...)`. That's why the overload
specify `params` as `any[]` and not as `SQLSourceParam[]`. This type is used to make our lives easier and
the type checker happy.
which will be wrapped in `Param`. That's why the overload specifies `params` as `any[]` and not as `SQLSourceParam[]`.
This type is used to make our lives easier and the type checker happy.
*/
export function sql(strings: TemplateStringsArray, ...params: SQLChunk[]): SQL {
const queryChunks: SQLChunk[] = [];
Expand Down
6 changes: 3 additions & 3 deletions drizzle-orm/src/sqlite-core/dialect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { SQLiteColumn } from '~/sqlite-core/columns/index.ts';
import type { SQLiteDeleteConfig, SQLiteInsertConfig, SQLiteUpdateConfig } from '~/sqlite-core/query-builders/index.ts';
import { SQLiteTable } from '~/sqlite-core/table.ts';
import { Subquery } from '~/subquery.ts';
import { getTableName, Table } from '~/table.ts';
import { getTableName, getTableUniqueName, Table } from '~/table.ts';
import { orderSelectedFields, type UpdateSet } from '~/utils.ts';
import { ViewBaseConfig } from '~/view-common.ts';
import type {
Expand Down Expand Up @@ -584,7 +584,7 @@ export abstract class SQLiteDialect {
} of selectedRelations
) {
const normalizedRelation = normalizeRelation(schema, tableNamesMap, relation);
const relationTableName = relation.referencedTable[Table.Symbol.Name];
const relationTableName = getTableUniqueName(relation.referencedTable);
const relationTableTsName = tableNamesMap[relationTableName]!;
const relationTableAlias = `${tableAlias}_${selectedRelationTsKey}`;
// const relationTable = schema[relationTableTsName]!;
Expand Down Expand Up @@ -778,7 +778,7 @@ export class SQLiteAsyncDialect extends SQLiteDialect {

async migrate(
migrations: MigrationMeta[],
session: SQLiteSession<'async', unknown, any, TablesRelationalConfig>,
session: SQLiteSession<'async', any, any, any>,
config?: string | MigrationConfig,
): Promise<void> {
const migrationsTable = config === undefined
Expand Down
4 changes: 4 additions & 0 deletions drizzle-orm/src/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ export function getTableName<T extends Table>(table: T): T['_']['name'] {
return table[TableName];
}

export function getTableUniqueName<T extends Table>(table: T): `${T['_']['schema']}.${T['_']['name']}` {
return `${table[Schema] ?? 'public'}.${table[TableName]}`;
}

export type MapColumnName<TName extends string, TColumn extends Column, TDBColumNames extends boolean> =
TDBColumNames extends true ? TColumn['_']['name']
: TName;
Expand Down
5 changes: 2 additions & 3 deletions drizzle-orm/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,11 @@ export type UpdateSet = Record<string, SQL | Param | null | undefined>;

export type OneOrMany<T> = T | T[];

export type Update<T, TUpdate> = Simplify<
export type Update<T, TUpdate> =
& {
[K in Exclude<keyof T, keyof TUpdate>]: T[K];
}
& TUpdate
>;
& TUpdate;

export type Simplify<T> =
& {
Expand Down
38 changes: 38 additions & 0 deletions drizzle-orm/tests/relation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { expect, test } from 'vitest';

import { pgSchema, pgTable } from '~/pg-core/index.ts';
import { createTableRelationsHelpers, extractTablesRelationalConfig } from '~/relations.ts';

test('tables with same name in different schemas', () => {
const folder = pgSchema('folder');
const schema = {
folder: {
usersInFolder: folder.table('users', {}),
},
public: {
users: pgTable('users', {}),
},
};

const relationalSchema = {
...Object.fromEntries(
Object.entries(schema)
.flatMap(([key, val]) => {
// have unique keys across schemas

const mappedTableEntries = Object.entries(val).map((tableEntry) => {
return [`__${key}__.${tableEntry[0]}`, tableEntry[1]];
});

return mappedTableEntries;
}),
),
};

const relationsConfig = extractTablesRelationalConfig(
relationalSchema,
createTableRelationsHelpers,
);

expect(Object.keys(relationsConfig)).toHaveLength(2);
});
2 changes: 1 addition & 1 deletion drizzle-orm/type-tests/mysql/set-operators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ const exceptAll2Test = await exceptAll(
db.select({
userId: newYorkers.userId,
cityId: newYorkers.cityId,
}).from(newYorkers).leftJoin(newYorkers, sql``),
}).from(newYorkers).leftJoin(users, sql``),
);

Expect<Equal<{ userId: number; cityId: number | null }[], typeof exceptAll2Test>>;
Expand Down
2 changes: 1 addition & 1 deletion drizzle-orm/type-tests/pg/set-operators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ const exceptAll2Test = await exceptAll(
db.select({
userId: newYorkers.userId,
cityId: newYorkers.cityId,
}).from(newYorkers).leftJoin(newYorkers, sql``),
}).from(newYorkers).leftJoin(users, sql``),
);

Expect<Equal<{ userId: number; cityId: number | null }[], typeof exceptAll2Test>>;
Expand Down
Loading

0 comments on commit ee97199

Please sign in to comment.