@fabrika/platform-node 0.9.0-dev.3 → 0.9.0-dev.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/sql-postgres.ts +14 -8
- package/types/src/sql-postgres.d.ts +4 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fabrika/platform-node",
|
|
3
|
-
"version": "0.9.0-dev.
|
|
3
|
+
"version": "0.9.0-dev.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -38,6 +38,6 @@
|
|
|
38
38
|
"test": "bun test"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@fabrika/platform": "0.9.0-dev.
|
|
41
|
+
"@fabrika/platform": "0.9.0-dev.4"
|
|
42
42
|
}
|
|
43
43
|
}
|
package/src/sql-postgres.ts
CHANGED
|
@@ -26,8 +26,11 @@
|
|
|
26
26
|
// already coerce `undefined` → null, so this matches what the services are tested against.
|
|
27
27
|
// * SQLite-only SQL still fails, obviously: `unixepoch()` does not exist in Postgres. Several
|
|
28
28
|
// statements in `@fabrika/control` and `@fabrika/iam` use it today.
|
|
29
|
-
// *
|
|
30
|
-
//
|
|
29
|
+
// * NO NAMED PREPARED STATEMENTS. A Zerops PostgreSQL service hands one server session to the IAM, Control and
|
|
30
|
+
// Operations processes in turn, so a statement prepared before a migration's ADD COLUMN outlives the
|
|
31
|
+
// process that prepared it, and `SELECT *` then fails with `cached plan must not change result type`
|
|
32
|
+
// (0A000). The same happens to one client that runs a migration between two reads. `connect` therefore
|
|
33
|
+
// defaults to `{ prepare: false }`: every statement is parsed where it runs.
|
|
31
34
|
|
|
32
35
|
import type { SqlDatabase, SqlQueryResult, SqlRunResult, SqlStatement } from '@fabrika/platform'
|
|
33
36
|
import { rewritePlaceholders, type RewrittenSql } from './placeholders'
|
|
@@ -36,7 +39,7 @@ import { rewritePlaceholders, type RewrittenSql } from './placeholders'
|
|
|
36
39
|
const MUTATING_COMMANDS = new Set(['INSERT', 'UPDATE', 'DELETE', 'MERGE'])
|
|
37
40
|
|
|
38
41
|
/** What a bound value may be. Anything else is a programming error and throws at bind time, as D1 does. */
|
|
39
|
-
export type SqlBindValue = string | number | bigint | boolean |
|
|
42
|
+
export type SqlBindValue = string | number | bigint | boolean | Uint8Array | null
|
|
40
43
|
|
|
41
44
|
/**
|
|
42
45
|
* One prepared statement. IMMUTABLE: `bind()` returns a NEW statement carrying the values and leaves
|
|
@@ -99,11 +102,11 @@ export class PostgresDatabase implements SqlDatabase {
|
|
|
99
102
|
constructor(private readonly sql: Bun.SQL) {}
|
|
100
103
|
|
|
101
104
|
/**
|
|
102
|
-
* Open a pooled client. The URL is a CREDENTIAL — it is
|
|
103
|
-
* error message raised here.
|
|
105
|
+
* Open a pooled client that prepares no named statement (see the header). The URL is a CREDENTIAL — it is
|
|
106
|
+
* never logged, and never included in an error message raised here.
|
|
104
107
|
*/
|
|
105
|
-
static connect(url: string, options: Omit<Bun.SQL.PostgresOrMySQLOptions, 'url' | 'adapter'> = {}): PostgresDatabase {
|
|
106
|
-
return new PostgresDatabase(new Bun.SQL({ ...options, url, adapter: 'postgres' }))
|
|
108
|
+
static connect(url: string, options: Omit<Bun.SQL.PostgresOrMySQLOptions, 'url' | 'adapter' | 'prepare'> = {}): PostgresDatabase {
|
|
109
|
+
return new PostgresDatabase(new Bun.SQL({ ...options, prepare: false, url, adapter: 'postgres' }))
|
|
107
110
|
}
|
|
108
111
|
|
|
109
112
|
prepare(sql: string): SqlStatement {
|
|
@@ -198,11 +201,14 @@ function toBindValue(value: unknown): SqlBindValue {
|
|
|
198
201
|
|| typeof value === 'number'
|
|
199
202
|
|| typeof value === 'bigint'
|
|
200
203
|
|| typeof value === 'boolean'
|
|
201
|
-
|| value instanceof Date
|
|
202
204
|
|| value instanceof Uint8Array
|
|
203
205
|
) {
|
|
204
206
|
return value
|
|
205
207
|
}
|
|
208
|
+
// Without a prepared statement Bun sends a parameter as text, and a Date as `Date#toString()`, which Postgres refuses.
|
|
209
|
+
if (value instanceof Date) {
|
|
210
|
+
return value.toISOString()
|
|
211
|
+
}
|
|
206
212
|
if (value instanceof ArrayBuffer) {
|
|
207
213
|
return new Uint8Array(value)
|
|
208
214
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SqlDatabase, SqlQueryResult, SqlRunResult, SqlStatement } from '@fabrika/platform';
|
|
2
2
|
/** What a bound value may be. Anything else is a programming error and throws at bind time, as D1 does. */
|
|
3
|
-
export type SqlBindValue = string | number | bigint | boolean |
|
|
3
|
+
export type SqlBindValue = string | number | bigint | boolean | Uint8Array | null;
|
|
4
4
|
/**
|
|
5
5
|
* One prepared statement. IMMUTABLE: `bind()` returns a NEW statement carrying the values and leaves
|
|
6
6
|
* the receiver untouched, which is D1's contract and what call sites rely on when they prepare once
|
|
@@ -33,10 +33,10 @@ export declare class PostgresDatabase implements SqlDatabase {
|
|
|
33
33
|
private readonly rewrites;
|
|
34
34
|
constructor(sql: Bun.SQL);
|
|
35
35
|
/**
|
|
36
|
-
* Open a pooled client. The URL is a CREDENTIAL — it is
|
|
37
|
-
* error message raised here.
|
|
36
|
+
* Open a pooled client that prepares no named statement (see the header). The URL is a CREDENTIAL — it is
|
|
37
|
+
* never logged, and never included in an error message raised here.
|
|
38
38
|
*/
|
|
39
|
-
static connect(url: string, options?: Omit<Bun.SQL.PostgresOrMySQLOptions, 'url' | 'adapter'>): PostgresDatabase;
|
|
39
|
+
static connect(url: string, options?: Omit<Bun.SQL.PostgresOrMySQLOptions, 'url' | 'adapter' | 'prepare'>): PostgresDatabase;
|
|
40
40
|
prepare(sql: string): SqlStatement;
|
|
41
41
|
/**
|
|
42
42
|
* Run every statement in ONE transaction, in order. A throw rolls the whole batch back. The transaction is sent by
|