Skip to content

Commit

Permalink
feat: bulk inserts/upserts w/ column defaults
Browse files Browse the repository at this point in the history
  • Loading branch information
soedirgo committed Apr 14, 2023
1 parent 76a9a94 commit 62b49e1
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 13 deletions.
39 changes: 26 additions & 13 deletions src/PostgrestQueryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,24 +113,31 @@ export default class PostgrestQueryBuilder<
*
* `"estimated"`: Uses exact count for low numbers and planned count for high
* numbers.
*
* @param options.defaultToNull - Make missing fields default to `null`.
* Otherwise, use the default value for the column.
*/
insert<Row extends Relation extends { Insert: unknown } ? Relation['Insert'] : never>(
values: Row | Row[],
{
count,
defaultToNull = true,
}: {
count?: 'exact' | 'planned' | 'estimated'
defaultToNull?: boolean
} = {}
): PostgrestFilterBuilder<Schema, Relation['Row'], null> {
const method = 'POST'

const prefersHeaders = []
const body = values
if (this.headers['Prefer']) {
prefersHeaders.push(this.headers['Prefer'])
}
if (count) {
prefersHeaders.push(`count=${count}`)
}
if (this.headers['Prefer']) {
prefersHeaders.unshift(this.headers['Prefer'])
if (!defaultToNull) {
prefersHeaders.push('missing=default')
}
this.headers['Prefer'] = prefersHeaders.join(',')

Expand All @@ -147,7 +154,7 @@ export default class PostgrestQueryBuilder<
url: this.url,
headers: this.headers,
schema: this.schema,
body,
body: values,
fetch: this.fetch,
allowEmpty: false,
} as unknown as PostgrestBuilder<null>)
Expand Down Expand Up @@ -185,30 +192,37 @@ export default class PostgrestQueryBuilder<
*
* `"estimated"`: Uses exact count for low numbers and planned count for high
* numbers.
*
* @param options.defaultToNull - Make missing fields default to `null`.
* Otherwise, use the default value for the column.
*/
upsert<Row extends Relation extends { Insert: unknown } ? Relation['Insert'] : never>(
values: Row | Row[],
{
onConflict,
ignoreDuplicates = false,
count,
defaultToNull = true,
}: {
onConflict?: string
ignoreDuplicates?: boolean
count?: 'exact' | 'planned' | 'estimated'
defaultToNull?: boolean
} = {}
): PostgrestFilterBuilder<Schema, Relation['Row'], null> {
const method = 'POST'

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

if (onConflict !== undefined) this.url.searchParams.set('on_conflict', onConflict)
const body = values
if (this.headers['Prefer']) {
prefersHeaders.push(this.headers['Prefer'])
}
if (count) {
prefersHeaders.push(`count=${count}`)
}
if (this.headers['Prefer']) {
prefersHeaders.unshift(this.headers['Prefer'])
if (!defaultToNull) {
prefersHeaders.push('missing=default')
}
this.headers['Prefer'] = prefersHeaders.join(',')

Expand All @@ -217,7 +231,7 @@ export default class PostgrestQueryBuilder<
url: this.url,
headers: this.headers,
schema: this.schema,
body,
body: values,
fetch: this.fetch,
allowEmpty: false,
} as unknown as PostgrestBuilder<null>)
Expand Down Expand Up @@ -254,21 +268,20 @@ export default class PostgrestQueryBuilder<
): PostgrestFilterBuilder<Schema, Relation['Row'], null> {
const method = 'PATCH'
const prefersHeaders = []
const body = values
if (this.headers['Prefer']) {
prefersHeaders.push(this.headers['Prefer'])
}
if (count) {
prefersHeaders.push(`count=${count}`)
}
if (this.headers['Prefer']) {
prefersHeaders.unshift(this.headers['Prefer'])
}
this.headers['Prefer'] = prefersHeaders.join(',')

return new PostgrestFilterBuilder({
method,
url: this.url,
headers: this.headers,
schema: this.schema,
body,
body: values,
fetch: this.fetch,
allowEmpty: false,
} as unknown as PostgrestBuilder<null>)
Expand Down
18 changes: 18 additions & 0 deletions test/basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,24 @@ describe("insert, update, delete with count: 'exact'", () => {
`)
})

test('bulk insert with column defaults', async () => {
let res = await postgrest
.from('channels')
.insert([{ id: 100 }, { slug: 'test-slug' }], { defaultToNull: false })
.select()
.rollback()
expect(res).toMatchInlineSnapshot()
})

test('bulk upsert with column defaults', async () => {
let res = await postgrest
.from('channels')
.upsert([{ id: 1 }, { slug: 'test-slug' }], { defaultToNull: false })
.select()
.rollback()
expect(res).toMatchInlineSnapshot()
})

test("update with count: 'exact'", async () => {
let res = await postgrest
.from('messages')
Expand Down

0 comments on commit 62b49e1

Please sign in to comment.