Skip to content

Commit 62b49e1

Browse files
committed
feat: bulk inserts/upserts w/ column defaults
1 parent 76a9a94 commit 62b49e1

File tree

2 files changed

+44
-13
lines changed

2 files changed

+44
-13
lines changed

src/PostgrestQueryBuilder.ts

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -113,24 +113,31 @@ export default class PostgrestQueryBuilder<
113113
*
114114
* `"estimated"`: Uses exact count for low numbers and planned count for high
115115
* numbers.
116+
*
117+
* @param options.defaultToNull - Make missing fields default to `null`.
118+
* Otherwise, use the default value for the column.
116119
*/
117120
insert<Row extends Relation extends { Insert: unknown } ? Relation['Insert'] : never>(
118121
values: Row | Row[],
119122
{
120123
count,
124+
defaultToNull = true,
121125
}: {
122126
count?: 'exact' | 'planned' | 'estimated'
127+
defaultToNull?: boolean
123128
} = {}
124129
): PostgrestFilterBuilder<Schema, Relation['Row'], null> {
125130
const method = 'POST'
126131

127132
const prefersHeaders = []
128-
const body = values
133+
if (this.headers['Prefer']) {
134+
prefersHeaders.push(this.headers['Prefer'])
135+
}
129136
if (count) {
130137
prefersHeaders.push(`count=${count}`)
131138
}
132-
if (this.headers['Prefer']) {
133-
prefersHeaders.unshift(this.headers['Prefer'])
139+
if (!defaultToNull) {
140+
prefersHeaders.push('missing=default')
134141
}
135142
this.headers['Prefer'] = prefersHeaders.join(',')
136143

@@ -147,7 +154,7 @@ export default class PostgrestQueryBuilder<
147154
url: this.url,
148155
headers: this.headers,
149156
schema: this.schema,
150-
body,
157+
body: values,
151158
fetch: this.fetch,
152159
allowEmpty: false,
153160
} as unknown as PostgrestBuilder<null>)
@@ -185,30 +192,37 @@ export default class PostgrestQueryBuilder<
185192
*
186193
* `"estimated"`: Uses exact count for low numbers and planned count for high
187194
* numbers.
195+
*
196+
* @param options.defaultToNull - Make missing fields default to `null`.
197+
* Otherwise, use the default value for the column.
188198
*/
189199
upsert<Row extends Relation extends { Insert: unknown } ? Relation['Insert'] : never>(
190200
values: Row | Row[],
191201
{
192202
onConflict,
193203
ignoreDuplicates = false,
194204
count,
205+
defaultToNull = true,
195206
}: {
196207
onConflict?: string
197208
ignoreDuplicates?: boolean
198209
count?: 'exact' | 'planned' | 'estimated'
210+
defaultToNull?: boolean
199211
} = {}
200212
): PostgrestFilterBuilder<Schema, Relation['Row'], null> {
201213
const method = 'POST'
202214

203215
const prefersHeaders = [`resolution=${ignoreDuplicates ? 'ignore' : 'merge'}-duplicates`]
204216

205217
if (onConflict !== undefined) this.url.searchParams.set('on_conflict', onConflict)
206-
const body = values
218+
if (this.headers['Prefer']) {
219+
prefersHeaders.push(this.headers['Prefer'])
220+
}
207221
if (count) {
208222
prefersHeaders.push(`count=${count}`)
209223
}
210-
if (this.headers['Prefer']) {
211-
prefersHeaders.unshift(this.headers['Prefer'])
224+
if (!defaultToNull) {
225+
prefersHeaders.push('missing=default')
212226
}
213227
this.headers['Prefer'] = prefersHeaders.join(',')
214228

@@ -217,7 +231,7 @@ export default class PostgrestQueryBuilder<
217231
url: this.url,
218232
headers: this.headers,
219233
schema: this.schema,
220-
body,
234+
body: values,
221235
fetch: this.fetch,
222236
allowEmpty: false,
223237
} as unknown as PostgrestBuilder<null>)
@@ -254,21 +268,20 @@ export default class PostgrestQueryBuilder<
254268
): PostgrestFilterBuilder<Schema, Relation['Row'], null> {
255269
const method = 'PATCH'
256270
const prefersHeaders = []
257-
const body = values
271+
if (this.headers['Prefer']) {
272+
prefersHeaders.push(this.headers['Prefer'])
273+
}
258274
if (count) {
259275
prefersHeaders.push(`count=${count}`)
260276
}
261-
if (this.headers['Prefer']) {
262-
prefersHeaders.unshift(this.headers['Prefer'])
263-
}
264277
this.headers['Prefer'] = prefersHeaders.join(',')
265278

266279
return new PostgrestFilterBuilder({
267280
method,
268281
url: this.url,
269282
headers: this.headers,
270283
schema: this.schema,
271-
body,
284+
body: values,
272285
fetch: this.fetch,
273286
allowEmpty: false,
274287
} as unknown as PostgrestBuilder<null>)

test/basic.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,24 @@ describe("insert, update, delete with count: 'exact'", () => {
10781078
`)
10791079
})
10801080

1081+
test('bulk insert with column defaults', async () => {
1082+
let res = await postgrest
1083+
.from('channels')
1084+
.insert([{ id: 100 }, { slug: 'test-slug' }], { defaultToNull: false })
1085+
.select()
1086+
.rollback()
1087+
expect(res).toMatchInlineSnapshot()
1088+
})
1089+
1090+
test('bulk upsert with column defaults', async () => {
1091+
let res = await postgrest
1092+
.from('channels')
1093+
.upsert([{ id: 1 }, { slug: 'test-slug' }], { defaultToNull: false })
1094+
.select()
1095+
.rollback()
1096+
expect(res).toMatchInlineSnapshot()
1097+
})
1098+
10811099
test("update with count: 'exact'", async () => {
10821100
let res = await postgrest
10831101
.from('messages')

0 commit comments

Comments
 (0)