Skip to content

Commit c725a96

Browse files
authored
Merge pull request #232 from AthennaIO/develop
feat: support knex raw on values
2 parents 68e099a + 4d65567 commit c725a96

6 files changed

Lines changed: 118 additions & 4 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@athenna/database",
3-
"version": "5.50.0",
3+
"version": "5.51.0",
44
"description": "The Athenna database handler for SQL/NoSQL.",
55
"license": "MIT",
66
"author": "João Lenon <lenon@athenna.io>",

src/database/drivers/BaseKnexDriver.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,9 +520,26 @@ export class BaseKnexDriver extends Driver<Knex, Knex.QueryBuilder> {
520520

521521
/**
522522
* Verify if a value should be serialized before persisting.
523+
*
524+
* Knex `Raw` instances must NEVER be stringified: they hold an internal
525+
* reference to the database client (and its connection pool, which contains
526+
* Node.js `Timeout` objects with circular references), so calling
527+
* `JSON.stringify` on them throws `TypeError: Converting circular structure
528+
* to JSON`. Knex marks every `Raw` instance with `isRawInstance = true` and
529+
* relies on it internally during query compilation, so we trust the same
530+
* marker here. The same precaution applies to Knex `QueryBuilder` instances
531+
* (subqueries) which expose `isQueryBuilder = true`.
523532
*/
524533
private shouldStringifyJsonValue(value: any) {
525-
return !!value && (Is.Array(value) || Is.Object(value))
534+
if (!value) {
535+
return false
536+
}
537+
538+
if (value.isRawInstance || value.isQueryBuilder) {
539+
return false
540+
}
541+
542+
return Is.Array(value) || Is.Object(value)
526543
}
527544

528545
/**

tests/unit/drivers/MySqlDriverTest.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,33 @@ export default class MySqlDriverTest {
898898
])
899899
}
900900

901+
@Test()
902+
public async shouldBeAbleToUpdateDataUsingARawValueWithoutThrowingCircularStructureError({ assert }: Context) {
903+
await this.driver.table('users').create({ id: '1', name: 'Robert Kiyosaki' })
904+
905+
await assert.doesNotReject(() =>
906+
this.driver
907+
.table('users')
908+
.where('id', '1')
909+
.update({ name: this.driver.raw("'Warren Buffet'") } as any)
910+
)
911+
912+
const result = await this.driver.table('users').where('id', '1').find()
913+
914+
assert.containSubset(result, { id: '1', name: 'Warren Buffet' })
915+
}
916+
917+
@Test()
918+
public async shouldBeAbleToCreateDataUsingARawValueWithoutThrowingCircularStructureError({ assert }: Context) {
919+
await assert.doesNotReject(() =>
920+
this.driver.table('users').create({ id: '1', name: this.driver.raw("'Robert Kiyosaki'") } as any)
921+
)
922+
923+
const result = await this.driver.table('users').where('id', '1').find()
924+
925+
assert.containSubset(result, { id: '1', name: 'Robert Kiyosaki' })
926+
}
927+
901928
@Test()
902929
public async shouldBeAbleToDeleteDataUsingDeleteMethod({ assert }: Context) {
903930
const data = { id: '1', name: 'Robert Kiyosaki' }

tests/unit/drivers/PostgresDriverTest.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,33 @@ export default class PostgresDriverTest {
897897
])
898898
}
899899

900+
@Test()
901+
public async shouldBeAbleToUpdateDataUsingARawValueWithoutThrowingCircularStructureError({ assert }: Context) {
902+
await this.driver.table('users').create({ id: '1', name: 'Robert Kiyosaki' })
903+
904+
await assert.doesNotReject(() =>
905+
this.driver
906+
.table('users')
907+
.where('id', '1')
908+
.update({ name: this.driver.raw("'Warren Buffet'") } as any)
909+
)
910+
911+
const result = await this.driver.table('users').where('id', '1').find()
912+
913+
assert.containSubset(result, { id: '1', name: 'Warren Buffet' })
914+
}
915+
916+
@Test()
917+
public async shouldBeAbleToCreateDataUsingARawValueWithoutThrowingCircularStructureError({ assert }: Context) {
918+
await assert.doesNotReject(() =>
919+
this.driver.table('users').create({ id: '1', name: this.driver.raw("'Robert Kiyosaki'") } as any)
920+
)
921+
922+
const result = await this.driver.table('users').where('id', '1').find()
923+
924+
assert.containSubset(result, { id: '1', name: 'Robert Kiyosaki' })
925+
}
926+
900927
@Test()
901928
public async shouldBeAbleToDeleteDataUsingDeleteMethod({ assert }: Context) {
902929
const data = { id: '1', name: 'Robert Kiyosaki' }

tests/unit/drivers/SqliteDriverTest.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,49 @@ export default class SqliteDriverTest {
899899
])
900900
}
901901

902+
@Test()
903+
public async shouldBeAbleToUpdateDataUsingARawValueWithoutThrowingCircularStructureError({ assert }: Context) {
904+
await this.driver.table('users').create({ id: '1', name: 'Robert Kiyosaki' })
905+
906+
await assert.doesNotReject(() =>
907+
this.driver
908+
.table('users')
909+
.where('id', '1')
910+
.update({ name: this.driver.raw("'Warren Buffet'") } as any)
911+
)
912+
913+
const result = await this.driver.table('users').where('id', '1').find()
914+
915+
assert.containSubset(result, { id: '1', name: 'Warren Buffet' })
916+
}
917+
918+
@Test()
919+
public async shouldBeAbleToCreateDataUsingARawValueWithoutThrowingCircularStructureError({ assert }: Context) {
920+
await assert.doesNotReject(() =>
921+
this.driver.table('users').create({ id: '1', name: this.driver.raw("'Robert Kiyosaki'") } as any)
922+
)
923+
924+
const result = await this.driver.table('users').where('id', '1').find()
925+
926+
assert.containSubset(result, { id: '1', name: 'Robert Kiyosaki' })
927+
}
928+
929+
@Test()
930+
public async shouldBeAbleToCreateOrUpdateUsingARawValueWithoutThrowingCircularStructureError({ assert }: Context) {
931+
await this.driver.table('users').create({ id: '1', name: 'Robert Kiyosaki' })
932+
933+
await assert.doesNotReject(() =>
934+
this.driver
935+
.table('users')
936+
.where('id', '1')
937+
.createOrUpdate({ id: '1', name: this.driver.raw("'Warren Buffet'") } as any)
938+
)
939+
940+
const result = await this.driver.table('users').where('id', '1').find()
941+
942+
assert.containSubset(result, { id: '1', name: 'Warren Buffet' })
943+
}
944+
902945
@Test()
903946
public async shouldBeAbleToDeleteDataUsingDeleteMethod({ assert }: Context) {
904947
const data = { id: '1', name: 'Robert Kiyosaki' }

0 commit comments

Comments
 (0)