Skip to content

Commit

Permalink
feat: allow upsert() to skip initial SELECT query
Browse files Browse the repository at this point in the history
  • Loading branch information
liujimj committed Apr 6, 2022
1 parent c99b623 commit 04a4512
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 15 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ upsert(

Behaviour:

If `user` table already contains a record describing the input email, then the following query will be evaluted:
If `user` table already contains a record describing the input email, then the following query will be evaluated:

```sql
SELECT "id"
Expand All @@ -250,6 +250,7 @@ WHERE (
If `user` table does not contain a record describing the input email, then the following queries will be evaluated:

```sql
-- This initial query is skipped if `selectBeforeInsert: false` is set on options (defaults to true).
SELECT "id"
FROM "user"
WHERE (
Expand All @@ -262,7 +263,7 @@ ON CONFLICT ("email_address")
DO NOTHING
RETURNING "id";

-- This query will not be evaluted if the preceeding query returns result.
-- This query will not be evaluated if the preceding query returns result.
SELECT "id"
FROM "user"
WHERE (
Expand Down Expand Up @@ -310,7 +311,8 @@ upsert(

Behaviour:

If `user` table already contains a record describing the input email, then the following query will be evaluted:
If `user` table already contains a record describing the input email and `selectBeforeUpdate: true` (the default),
then the following query will be evaluated:

```sql
SELECT "id"
Expand All @@ -323,7 +325,8 @@ WHERE (

```

If `user` table does not contain a record describing the input email, then the following queries will be evaluated:
If `user` table does not contain a record describing the input email or `selectBeforeUpdate: false`, then the following
queries will be evaluated:

```sql
SELECT "id"
Expand Down
23 changes: 12 additions & 11 deletions src/routines/upsert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ type NamedValueBindingsType = {
};

type UpsertConfigurationType = {
readonly identifierName: string,
readonly identifierName?: string,
readonly selectBeforeUpdate?: boolean,
};

const log = Logger.child({
Expand All @@ -31,8 +32,9 @@ const normalizeNamedValueBindingName = (name: string): string => {
return snakeCase(name);
};

const defaultConfiguration = {
const defaultConfiguration: Required<UpsertConfigurationType> = {
identifierName: 'id',
selectBeforeUpdate: true,
};

export const upsert = async (
Expand Down Expand Up @@ -163,13 +165,12 @@ export const upsert = async (
WHERE
${whereClause}
`;
if (configuration.selectBeforeUpdate) {
const maybeId1 = await connection.maybeOneFirst(selectQuery);

let maybeId;

maybeId = await connection.maybeOneFirst(selectQuery);

if (maybeId) {
return maybeId;
if (maybeId1) {
return maybeId1;
}
}

if (updateClause) {
Expand All @@ -188,7 +189,7 @@ export const upsert = async (
`);
}

maybeId = await connection.maybeOneFirst(sql`
const maybeId2 = await connection.maybeOneFirst(sql`
INSERT INTO ${sql.identifier([
tableName,
])} (${columnIdentifiers})
Expand All @@ -197,8 +198,8 @@ export const upsert = async (
DO NOTHING
`);

if (maybeId) {
return maybeId;
if (maybeId2) {
return maybeId2;
}

return await connection.oneFirst(selectQuery);
Expand Down
34 changes: 34 additions & 0 deletions test/slonik-utilities/routines/upsert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,40 @@ test('uses unique constraint column name values and update column name values to
]);
});

test('skips initial SELECT if selectBeforeUpdate is false and update column names are defined', async (t) => {
const connection = createConnection();

connection.query.onCall(0).returns(1);

const recordId = await upsert(
connection,
'foo',
{
bar: 'baz',
qux: 'quux',
},
[
'bar',
],
{
selectBeforeUpdate: false,
},
);

t.is(recordId, 1);

t.is(connection.query.callCount, 1);

t.is(
normalizeQuery(connection.query.firstCall.args[0].sql),
'INSERT INTO "foo" ("bar", "qux") VALUES ($1, $2) ON CONFLICT ("bar") DO UPDATE SET "qux" = "excluded"."qux" RETURNING "id"',
);
t.deepEqual(connection.query.firstCall.args[0].values, [
'baz',
'quux',
]);
});

test('converts named value bindings to snake case (SELECT)', async (t) => {
const connection = createConnection();

Expand Down

0 comments on commit 04a4512

Please sign in to comment.