Skip to content

Commit

Permalink
Add support for unordered parameters in the update function (#104)
Browse files Browse the repository at this point in the history
Fixes #92
  • Loading branch information
G4brym authored Jan 24, 2025
1 parent 15901e2 commit c67bd81
Show file tree
Hide file tree
Showing 3 changed files with 209 additions and 4 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "workers-qb",
"version": "1.9.0",
"version": "1.10.0",
"description": "Zero dependencies Query Builder for Cloudflare Workers",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
Expand Down
10 changes: 9 additions & 1 deletion src/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,14 @@ export class QueryBuilder<GenericResultWrapper, IsAsync extends boolean = true>
: 1
: 0

let whereString = this._where(params.where)

let parameterIndex = 1
if (whereString && whereString.match(/(?<!\d)\?(?!\d)/)) {
// if the user is using unnumbered parameters in where, replace '?' in whereString with numbered parameters
whereString = whereString.replace(/\?/g, () => `?${parameterIndex++}`)
}

const set: Array<string> = []
let index = 1
for (const [key, value] of Object.entries(params.data)) {
Expand All @@ -386,7 +394,7 @@ export class QueryBuilder<GenericResultWrapper, IsAsync extends boolean = true>
return (
`UPDATE ${this._onConflict(params.onConflict)}${params.tableName}
SET ${set.join(', ')}` +
this._where(params.where) +
whereString +
this._returning(params.returning)
)
}
Expand Down
201 changes: 199 additions & 2 deletions tests/unit/update.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { describe, expect, it } from 'vitest'
import { ConflictTypes } from '../../src/enums'
import { Raw } from '../../src/tools'
import { QuerybuilderTest } from '../utils'

Expand Down Expand Up @@ -177,7 +176,7 @@ describe('Update Builder', () => {
params: ['test', 345],
},
returning: ['id', 'field'],
onConflict: ConflictTypes.IGNORE,
onConflict: 'IGNORE',
})

expect(result.query).toEqual(
Expand Down Expand Up @@ -208,4 +207,202 @@ describe('Update Builder', () => {
expect(result.arguments).toEqual(['test', 345, 'test_update', 123])
expect(result.fetchType).toEqual('ALL')
})

it('update one field with one where and verify arguments - unnumbered parameters', async () => {
const result = new QuerybuilderTest().update({
tableName: 'testTable',
data: {
my_field: 'test_data',
},
where: {
conditions: 'field = ?',
params: ['test_where'],
},
})

expect(result.query).toEqual('UPDATE testTable SET my_field = ?2 WHERE field = ?1')
expect(result.arguments).toEqual(['test_where', 'test_data'])
expect(result.fetchType).toEqual('ALL')
})

it('update with Raw sql values - unnumbered parameters', async () => {
const result = new QuerybuilderTest().update({
tableName: 'testTable',
data: {
my_field: 'test_data',
updated_at: new Raw('CURRENT_TIMESTAMP'),
another: '123',
},
where: {
conditions: 'field = ?',
params: ['test_where'],
},
})

expect(result.query).toEqual(
'UPDATE testTable SET my_field = ?2, updated_at = CURRENT_TIMESTAMP, another = ?3 WHERE field = ?1'
)
expect(result.arguments).toEqual(['test_where', 'test_data', '123'])
expect(result.fetchType).toEqual('ALL')
})

it('update one field with one where without returning - unnumbered parameters', async () => {
const result = new QuerybuilderTest().update({
tableName: 'testTable',
data: {
my_field: 'test_update',
},
where: {
conditions: 'field = ?',
params: ['test'],
},
})

expect(result.query).toEqual('UPDATE testTable SET my_field = ?2 WHERE field = ?1')
expect(result.arguments).toEqual(['test', 'test_update'])
expect(result.fetchType).toEqual('ALL')
})

it('update multiple field with one where without returning - unnumbered parameters', async () => {
const result = new QuerybuilderTest().update({
tableName: 'testTable',
data: {
my_field: 'test_update',
another: 123,
},
where: {
conditions: 'field = ?',
params: ['test'],
},
})

expect(result.query).toEqual('UPDATE testTable SET my_field = ?2, another = ?3 WHERE field = ?1')
expect(result.arguments).toEqual(['test', 'test_update', 123])
expect(result.fetchType).toEqual('ALL')
})

it('update multiple field with multiple where without returning - unnumbered parameters', async () => {
const result = new QuerybuilderTest().update({
tableName: 'testTable',
data: {
my_field: 'test_update',
another: 123,
},
where: {
conditions: ['field = ?', 'id = ?'],
params: ['test', 345],
},
})

expect(result.query).toEqual('UPDATE testTable SET my_field = ?3, another = ?4 WHERE (field = ?1) AND (id = ?2)')
expect(result.arguments).toEqual(['test', 345, 'test_update', 123])
expect(result.fetchType).toEqual('ALL')
})

it('update multiple field with multiple where with one returning - unnumbered parameters', async () => {
const result = new QuerybuilderTest().update({
tableName: 'testTable',
data: {
my_field: 'test_update',
another: 123,
},
where: {
conditions: ['field = ?', 'id = ?'],
params: ['test', 345],
},
returning: 'id',
})

expect(result.query).toEqual(
'UPDATE testTable SET my_field = ?3, another = ?4 WHERE (field = ?1) AND (id = ?2) RETURNING id'
)
expect(result.arguments).toEqual(['test', 345, 'test_update', 123])
expect(result.fetchType).toEqual('ALL')
})

it('update multiple field with multiple where with multiple returning - unnumbered parameters', async () => {
const result = new QuerybuilderTest().update({
tableName: 'testTable',
data: {
my_field: 'test_update',
another: 123,
},
where: {
conditions: ['field = ?', 'id = ?'],
params: ['test', 345],
},
returning: ['id', 'field'],
})

expect(result.query).toEqual(
'UPDATE testTable SET my_field = ?3, another = ?4 WHERE (field = ?1) AND (id = ?2) RETURNING id, field'
)
expect(result.arguments).toEqual(['test', 345, 'test_update', 123])
expect(result.fetchType).toEqual('ALL')
})

it('update on conflict ignore - unnumbered parameters', async () => {
const result = new QuerybuilderTest().update({
tableName: 'testTable',
data: {
my_field: 'test_update',
another: 123,
},
where: {
conditions: ['field = ?', 'id = ?'],
params: ['test', 345],
},
returning: ['id', 'field'],
onConflict: 'IGNORE',
})

expect(result.query).toEqual(
'UPDATE OR IGNORE testTable SET my_field = ?3, another = ?4 WHERE (field = ?1) AND (id = ?2) RETURNING id, field'
)
expect(result.arguments).toEqual(['test', 345, 'test_update', 123])
expect(result.fetchType).toEqual('ALL')
})

it('update on conflict replace - unnumbered parameters', async () => {
const result = new QuerybuilderTest().update({
tableName: 'testTable',
data: {
my_field: 'test_update',
another: 123,
},
where: {
conditions: ['field = ?', 'id = ?'],
params: ['test', 345],
},
returning: ['id', 'field'],
onConflict: 'REPLACE',
})

expect(result.query).toEqual(
'UPDATE OR REPLACE testTable SET my_field = ?3, another = ?4 WHERE (field = ?1) AND (id = ?2) RETURNING id, field'
)
expect(result.arguments).toEqual(['test', 345, 'test_update', 123])
expect(result.fetchType).toEqual('ALL')
})

it('update multiple field with multiple where without returning - unnumbered parameters - complex case', async () => {
const result = new QuerybuilderTest().update({
tableName: 'testTable',
data: {
my_field: 'test_update',
another: 123,
third_field: 'third value',
},
where: {
conditions: ['field = ? AND another_field = ?', 'id = ?'],
params: ['test', 'another_test', 345],
},
})

expect(result.query).toEqual(
'UPDATE testTable SET my_field = ?4, another = ?5, third_field = ?6 WHERE (field = ?1 AND another_field = ?2) AND (id = ?3)'
)
expect(result.arguments).toEqual(['test', 'another_test', 345, 'test_update', 123, 'third value'])
expect(result.fetchType).toEqual('ALL')
})
})

0 comments on commit c67bd81

Please sign in to comment.