Skip to content
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

AWS Data API fixes #2119

Merged
merged 53 commits into from
Apr 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
725dc93
[All] Added types and new config option to column and column-builder …
Angelelz Nov 14, 2023
c9ddcc8
[MySql] Added implementation for dialect
Angelelz Nov 14, 2023
fd726e2
[Pg] Added implementation for dialect
Angelelz Nov 14, 2023
d887c52
[SQLite] Added implementation for dialect
Angelelz Nov 14, 2023
13ec45d
[All] Improved performance of update builder in the dialect
Angelelz Nov 17, 2023
77f7005
[All] Fixed insert and update behavior
Angelelz Nov 19, 2023
5b82489
[All] Added integration tests
Angelelz Nov 19, 2023
fa3bd5e
[All] Added requested changes: fix issue ignoring null values on update
Angelelz Nov 24, 2023
5d2d4cb
fix: smallserial type
Jan 26, 2024
f284f00
Merge branch 'beta' into fix/smallserial-type
AndriiSherman Mar 9, 2024
3f49ddd
Merge branch 'beta' into fix/smallserial-type
AndriiSherman Mar 14, 2024
5553513
fix: aws data api doesn't support savepoint name as variable
hugo082 Feb 28, 2024
5847889
fix: await for the savepoint to be executed
hugo082 Feb 28, 2024
4d3a50e
fix: await for transaction to be executed before releasing the savepoint
hugo082 Feb 28, 2024
6aea59b
fix: transaction id propagation
hugo082 Feb 28, 2024
21e118b
tests: fix transaction testing
hugo082 Feb 28, 2024
6a96c90
fix: AwsDataApiSession match PgSession api
hugo082 Feb 28, 2024
b3a1ec1
tests: fixes
hugo082 Feb 28, 2024
eaab7c7
Merge branch 'main' into main
hugo082 Mar 4, 2024
995647f
Merge branch 'main' into main
hugo082 Mar 8, 2024
3dc3691
Merge branch 'main' into main
hugo082 Mar 19, 2024
d3cdf21
Merge branch 'main' into main
hugo082 Mar 20, 2024
795b22c
Merge branch 'main' into main
hugo082 Mar 21, 2024
9d789cf
Merge branch 'main' into feat-onupdate
AndriiSherman Mar 27, 2024
77763ae
Merge pull request #1509 from Angelelz/feat-onupdate
AndriiSherman Mar 27, 2024
5da5563
Merge branch 'main' into fix/smallserial-type
AndriiSherman Mar 27, 2024
c627b08
Add release notes for 0.30.5 release
AndriiSherman Mar 27, 2024
7cceb0b
Fix type errors
AndriiSherman Mar 27, 2024
fecb993
Add type tests for small serial
AndriiSherman Mar 27, 2024
8eabf2b
Merge branch 'main' into fix/smallserial-type
AndriiSherman Mar 27, 2024
4be8c03
Merge pull request #1836 from gabrielDonnantuoni/fix/smallserial-type
AndriiSherman Mar 27, 2024
5edac47
Add 0.30.5 release notes updates
AndriiSherman Mar 27, 2024
4a092cd
Fix typo in release notes
AndriiSherman Mar 27, 2024
3e821c4
Bump to 0.30.5
AndriiSherman Mar 27, 2024
f6de0d5
Fix xata tests
AndriiSherman Mar 27, 2024
085aaee
Merge branch 'main' into main
dankochetov Mar 27, 2024
a5523e9
Add pglite basic setup
AndriiSherman Mar 27, 2024
8efb725
Bump PGlite version and fix tests
samwillis Mar 28, 2024
c032cbd
Merge pull request #2079 from samwillis/fix-tests
AndriiSherman Mar 28, 2024
1af3622
Remove 1 check for dates in $onUpdate
AndriiSherman Mar 28, 2024
438fbaf
Add pglite release notes
AndriiSherman Mar 28, 2024
16d55e6
Fix imports
AndriiSherman Mar 28, 2024
fe8a0f7
Ignore pglite for cjs tests, as long as it's esm only
AndriiSherman Mar 28, 2024
dfa923a
Add XATA-API-KEY to feature releases
AndriiSherman Mar 28, 2024
0ddab65
Merge pull request #2081 from drizzle-team/feature/pglite
AndriiSherman Mar 28, 2024
f603677
Merge branch 'main' into main
hugo082 Mar 29, 2024
cb0c553
Add type parsers to vercel/postgres
AndriiSherman Apr 2, 2024
bb0f096
Bump to 0.30.7
AndriiSherman Apr 3, 2024
76eb060
Merge pull request #2103 from drizzle-team/vercel-postgres
AndriiSherman Apr 3, 2024
4a2a2de
Migrate Data API tests to Vitest, update Data API result for raw queries
dankochetov Apr 6, 2024
9c30efd
Merge branch 'main' into aws-data-api-fixes
dankochetov Apr 6, 2024
df2bd77
Exclude SST from type checks
dankochetov Apr 6, 2024
9b8a94f
Merge branch 'beta' into aws-data-api-fixes
dankochetov Apr 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions changelogs/drizzle-orm/0.30.5.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
- 🎉 Added custom schema support to enums in Postgres:
## New Features

```ts
import { pgSchema } from 'drizzle-orm/pg-core';
### 🎉 `$onUpdate` functionality for PostgreSQL, MySQL and SQLite

const mySchema = pgSchema('mySchema');
const colors = mySchema.enum('colors', ['red', 'green', 'blue']);
```
Adds a dynamic update value to the column.
The function will be called when the row is updated, and the returned value will be used as the column value if none is provided.
If no `default` (or `$defaultFn`) value is provided, the function will be called when the row is inserted as well, and the returned value will be used as the column value.

- 🐛 Split `where` clause in Postgres `.onConflictDoUpdate` method into `setWhere` and `targetWhere` clauses, to support both `where` cases in `on conflict ...` clause (#1628, #1302)
- 🐛 Fix query generation for `where` clause in Postgres `.onConflictDoNothing` method, as it was placed in a wrong spot (#1628)
> Note: This value does not affect the `drizzle-kit` behavior, it is only used at runtime in `drizzle-orm`.

```ts
const usersOnUpdate = pgTable('users_on_update', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
updateCounter: integer('update_counter').default(sql`1`).$onUpdateFn(() => sql`update_counter + 1`),
updatedAt: timestamp('updated_at', { mode: 'date', precision: 3 }).$onUpdate(() => new Date()),
alwaysNull: text('always_null').$type<string | null>().$onUpdate(() => null),
});
```

## Fixes

- [BUG]: insertions on columns with the smallserial datatype are not optional - #1848

Thanks @Angelelz and @gabrielDonnantuoni!
27 changes: 27 additions & 0 deletions changelogs/drizzle-orm/0.30.6.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## New Features

### 🎉 PGlite driver Support

PGlite is a WASM Postgres build packaged into a TypeScript client library that enables you to run Postgres in the browser, Node.js and Bun, with no need to install any other dependencies. It is only 2.6mb gzipped.

It can be used as an ephemeral in-memory database, or with persistence either to the file system (Node/Bun) or indexedDB (Browser).

Unlike previous "Postgres in the browser" projects, PGlite does not use a Linux virtual machine - it is simply Postgres in WASM.

Usage Example
```ts
import { PGlite } from '@electric-sql/pglite';
import { drizzle } from 'drizzle-orm/pglite';

// In-memory Postgres
const client = new PGlite();
const db = drizzle(client);

await db.select().from(users);
```
---
There are currently 2 limitations, that should be fixed on Pglite side:

- [Attempting to refresh a materialised view throws error](https://github.com/electric-sql/pglite/issues/63)

- [Attempting to SET TIME ZONE throws error](https://github.com/electric-sql/pglite/issues/62)
4 changes: 4 additions & 0 deletions changelogs/drizzle-orm/0.30.7.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## Bug fixes

- Add mappings for `@vercel/postgres` package
- Fix interval mapping for `neon` drivers - #1542
11 changes: 11 additions & 0 deletions changelogs/drizzle-orm/0.30.8.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
- 🎉 Added custom schema support to enums in Postgres:

```ts
import { pgSchema } from 'drizzle-orm/pg-core';

const mySchema = pgSchema('mySchema');
const colors = mySchema.enum('colors', ['red', 'green', 'blue']);
```

- 🐛 Split `where` clause in Postgres `.onConflictDoUpdate` method into `setWhere` and `targetWhere` clauses, to support both `where` cases in `on conflict ...` clause (#1628, #1302)
- 🐛 Fix query generation for `where` clause in Postgres `.onConflictDoNothing` method, as it was placed in a wrong spot (#1628)
3 changes: 2 additions & 1 deletion dprint.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"**/drizzle2/**/meta",
"**/*snapshot.json",
"**/_journal.json",
"**/tsup.config*.mjs"
"**/tsup.config*.mjs",
"**/.sst"
],
"plugins": [
"https://plugins.dprint.dev/typescript-0.83.0.wasm",
Expand Down
13 changes: 9 additions & 4 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.5",
"version": "0.30.7",
"description": "Drizzle ORM package for SQL databases",
"type": "module",
"scripts": {
Expand Down Expand Up @@ -45,6 +45,7 @@
"peerDependencies": {
"@aws-sdk/client-rds-data": ">=3",
"@cloudflare/workers-types": ">=3",
"@electric-sql/pglite": ">=0.1.1",
"@libsql/client": "*",
"@neondatabase/serverless": ">=0.1",
"@op-engineering/op-sqlite": ">=2",
Expand All @@ -54,7 +55,7 @@
"@types/pg": "*",
"@types/react": ">=18",
"@types/sql.js": "*",
"@vercel/postgres": "*",
"@vercel/postgres": ">=0.8.0",
"@xata.io/client": "*",
"better-sqlite3": ">=7",
"bun-types": "*",
Expand Down Expand Up @@ -140,11 +141,15 @@
},
"@types/react": {
"optional": true
},
"@electric-sql/pglite": {
"optional": true
}
},
"devDependencies": {
"@aws-sdk/client-rds-data": "^3.344.0",
"@aws-sdk/client-rds-data": "^3.549.0",
"@cloudflare/workers-types": "^4.20230904.0",
"@electric-sql/pglite": "^0.1.1",
"@libsql/client": "^0.5.6",
"@neondatabase/serverless": "^0.9.0",
"@op-engineering/op-sqlite": "^2.0.16",
Expand All @@ -156,7 +161,7 @@
"@types/pg": "^8.10.1",
"@types/react": "^18.2.45",
"@types/sql.js": "^1.4.4",
"@vercel/postgres": "^0.3.0",
"@vercel/postgres": "^0.8.0",
"@xata.io/client": "^0.29.3",
"better-sqlite3": "^8.4.0",
"bun-types": "^0.6.6",
Expand Down
16 changes: 13 additions & 3 deletions drizzle-orm/src/aws-data-api/pg/driver.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { entityKind } from '~/entity.ts';
import type { SQLWrapper } from '~/index.ts';
import type { Logger } from '~/logger.ts';
import { DefaultLogger } from '~/logger.ts';
import { PgDatabase } from '~/pg-core/db.ts';
import { PgDialect } from '~/pg-core/dialect.ts';
import type { PgRaw } from '~/pg-core/query-builders/raw.ts';
import {
createTableRelationsHelpers,
extractTablesRelationalConfig,
type RelationalSchemaConfig,
type TablesRelationalConfig,
} from '~/relations.ts';
import type { DrizzleConfig } from '~/utils.ts';
import type { AwsDataApiClient, AwsDataApiPgQueryResultHKT } from './session.ts';
import type { AwsDataApiClient, AwsDataApiPgQueryResult, AwsDataApiPgQueryResultHKT } from './session.ts';
import { AwsDataApiSession } from './session.ts';

export interface PgDriverOptions {
Expand All @@ -28,9 +30,17 @@ export interface DrizzleAwsDataApiPgConfig<
secretArn: string;
}

export type AwsDataApiPgDatabase<
export class AwsDataApiPgDatabase<
TSchema extends Record<string, unknown> = Record<string, never>,
> = PgDatabase<AwsDataApiPgQueryResultHKT, TSchema>;
> extends PgDatabase<AwsDataApiPgQueryResultHKT, TSchema> {
static readonly [entityKind]: string = 'AwsDataApiPgDatabase';

override execute<
TRow extends Record<string, unknown> = Record<string, unknown>,
>(query: SQLWrapper): PgRaw<AwsDataApiPgQueryResult<TRow>> {
return super.execute(query);
}
}

export class AwsPgDialect extends PgDialect {
static readonly [entityKind]: string = 'AwsPgDialect';
Expand Down
51 changes: 33 additions & 18 deletions drizzle-orm/src/aws-data-api/pg/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ export class AwsDataApiPreparedQuery<T extends PreparedQueryConfig> extends PgPr
async execute(placeholderValues: Record<string, unknown> | undefined = {}): Promise<T['execute']> {
const { fields, joinsNotNullableMap, customResultMapper } = this;

const rows = await this.values(placeholderValues) as unknown[][];
const result = await this.values(placeholderValues) as AwsDataApiPgQueryResult<unknown[]>;
if (!fields && !customResultMapper) {
return rows as T['execute'];
return result as T['execute'];
}
return customResultMapper
? customResultMapper(rows)
: rows.map((row) => mapResultRow<T['execute']>(fields!, row, joinsNotNullableMap));
? customResultMapper(result.rows!)
: result.rows!.map((row) => mapResultRow<T['execute']>(fields!, row, joinsNotNullableMap));
}

all(placeholderValues?: Record<string, unknown> | undefined): Promise<T['all']> {
Expand All @@ -83,16 +83,24 @@ export class AwsDataApiPreparedQuery<T extends PreparedQueryConfig> extends PgPr
if (!fields && !customResultMapper) {
const result = await client.send(rawQuery);
if (result.columnMetadata && result.columnMetadata.length > 0) {
return this.mapResultRows(result.records ?? [], result.columnMetadata);
const rows = this.mapResultRows(result.records ?? [], result.columnMetadata);
return {
...result,
rows,
};
}
return result.records ?? [];
return result;
}

const result = await client.send(rawQuery);
const rows = result.records?.map((row) => {
return row.map((field) => getValueFromDataApi(field));
}) ?? [];

return result.records?.map((row: any) => {
return row.map((field: Field) => getValueFromDataApi(field));
});
return {
...result,
rows,
};
}

/** @internal */
Expand Down Expand Up @@ -155,9 +163,10 @@ export class AwsDataApiSession<
prepareQuery<T extends PreparedQueryConfig = PreparedQueryConfig>(
query: QueryWithTypings,
fields: SelectedFieldsOrdered | undefined,
transactionId: string | undefined,
name: string | undefined,
isResponseInArrayMode: boolean,
customResultMapper?: (rows: unknown[][]) => T['execute'],
transactionId?: string,
): PgPreparedQuery<T> {
return new AwsDataApiPreparedQuery(
this.client,
Expand All @@ -166,7 +175,7 @@ export class AwsDataApiSession<
query.typings ?? [],
this.options,
fields,
transactionId,
transactionId ?? this.transactionId,
isResponseInArrayMode,
customResultMapper,
);
Expand All @@ -176,8 +185,10 @@ export class AwsDataApiSession<
return this.prepareQuery<PreparedQueryConfig & { execute: T }>(
this.dialect.sqlToQuery(query),
undefined,
this.transactionId,
undefined,
false,
undefined,
this.transactionId,
).execute();
}

Expand Down Expand Up @@ -208,21 +219,25 @@ export class AwsDataApiTransaction<
> extends PgTransaction<AwsDataApiPgQueryResultHKT, TFullSchema, TSchema> {
static readonly [entityKind]: string = 'AwsDataApiTransaction';

override transaction<T>(transaction: (tx: AwsDataApiTransaction<TFullSchema, TSchema>) => Promise<T>): Promise<T> {
override async transaction<T>(
transaction: (tx: AwsDataApiTransaction<TFullSchema, TSchema>) => Promise<T>,
): Promise<T> {
const savepointName = `sp${this.nestedIndex + 1}`;
const tx = new AwsDataApiTransaction(this.dialect, this.session, this.schema, this.nestedIndex + 1);
this.session.execute(sql`savepoint ${savepointName}`);
await this.session.execute(sql.raw(`savepoint ${savepointName}`));
try {
const result = transaction(tx);
this.session.execute(sql`release savepoint ${savepointName}`);
const result = await transaction(tx);
await this.session.execute(sql.raw(`release savepoint ${savepointName}`));
return result;
} catch (e) {
this.session.execute(sql`rollback to savepoint ${savepointName}`);
await this.session.execute(sql.raw(`rollback to savepoint ${savepointName}`));
throw e;
}
}
}

export type AwsDataApiPgQueryResult<T> = ExecuteStatementCommandOutput & { rows: T[] };

export interface AwsDataApiPgQueryResultHKT extends QueryResultHKT {
type: ExecuteStatementCommandOutput;
type: AwsDataApiPgQueryResult<any>;
}
21 changes: 21 additions & 0 deletions drizzle-orm/src/column-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export type ColumnBuilderRuntimeConfig<TData, TRuntimeConfig extends object = ob
notNull: boolean;
default: TData | SQL | undefined;
defaultFn: (() => TData | SQL) | undefined;
onUpdateFn: (() => TData | SQL) | undefined;
hasDefault: boolean;
primaryKey: boolean;
isUnique: boolean;
Expand Down Expand Up @@ -192,6 +193,26 @@ export abstract class ColumnBuilder<
*/
$default = this.$defaultFn;

/**
* Adds a dynamic update value to the column.
* The function will be called when the row is updated, and the returned value will be used as the column value if none is provided.
* If no `default` (or `$defaultFn`) value is provided, the function will be called when the row is inserted as well, and the returned value will be used as the column value.
*
* **Note:** This value does not affect the `drizzle-kit` behavior, it is only used at runtime in `drizzle-orm`.
*/
$onUpdateFn(
fn: () => (this['_'] extends { $type: infer U } ? U : this['_']['data']) | SQL,
): HasDefault<this> {
this.config.onUpdateFn = fn;
this.config.hasDefault = true;
return this as HasDefault<this>;
}

/**
* Alias for {@link $onUpdateFn}.
*/
$onUpdate = this.$onUpdateFn;

/**
* Adds a `primary key` clause to the column definition. This implicitly makes the column `not null`.
*
Expand Down
2 changes: 2 additions & 0 deletions drizzle-orm/src/column.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export abstract class Column<
readonly notNull: boolean;
readonly default: T['data'] | SQL | undefined;
readonly defaultFn: (() => T['data'] | SQL) | undefined;
readonly onUpdateFn: (() => T['data'] | SQL) | undefined;
readonly hasDefault: boolean;
readonly isUnique: boolean;
readonly uniqueName: string | undefined;
Expand All @@ -79,6 +80,7 @@ export abstract class Column<
this.notNull = config.notNull;
this.default = config.default;
this.defaultFn = config.defaultFn;
this.onUpdateFn = config.onUpdateFn;
this.hasDefault = config.hasDefault;
this.primary = config.primaryKey;
this.isUnique = config.isUnique;
Expand Down
35 changes: 22 additions & 13 deletions drizzle-orm/src/mysql-core/dialect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,20 +110,24 @@ export class MySqlDialect {
}

buildUpdateSet(table: MySqlTable, set: UpdateSet): SQL {
const setEntries = Object.entries(set);

const setSize = setEntries.length;
return sql.join(
setEntries
.flatMap(([colName, value], i): SQL[] => {
const col: MySqlColumn = table[Table.Symbol.Columns][colName]!;
const res = sql`${sql.identifier(col.name)} = ${value}`;
if (i < setSize - 1) {
return [res, sql.raw(', ')];
}
return [res];
}),
const tableColumns = table[Table.Symbol.Columns];

const columnNames = Object.keys(tableColumns).filter((colName) =>
set[colName] !== undefined || tableColumns[colName]?.onUpdateFn !== undefined
);

const setSize = columnNames.length;
return sql.join(columnNames.flatMap((colName, i) => {
const col = tableColumns[colName]!;

const value = set[colName] ?? sql.param(col.onUpdateFn!(), col);
const res = sql`${sql.identifier(col.name)} = ${value}`;

if (i < setSize - 1) {
return [res, sql.raw(', ')];
}
return [res];
}));
}

buildUpdateQuery({ table, set, where, returning, withList }: MySqlUpdateConfig): SQL {
Expand Down Expand Up @@ -423,6 +427,11 @@ export class MySqlDialect {
const defaultFnResult = col.defaultFn();
const defaultValue = is(defaultFnResult, SQL) ? defaultFnResult : sql.param(defaultFnResult, col);
valueList.push(defaultValue);
// eslint-disable-next-line unicorn/no-negated-condition
} else if (!col.default && col.onUpdateFn !== undefined) {
const onUpdateFnResult = col.onUpdateFn();
const newValue = is(onUpdateFnResult, SQL) ? onUpdateFnResult : sql.param(onUpdateFnResult, col);
valueList.push(newValue);
} else {
valueList.push(sql`default`);
}
Expand Down
Loading