Skip to content

Commit

Permalink
Add lit method to expression builder (#600)
Browse files Browse the repository at this point in the history
  • Loading branch information
koskimas authored Jul 19, 2023
1 parent c3b9188 commit 895bea3
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 8 deletions.
31 changes: 31 additions & 0 deletions src/expression/expression-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import { SqlBool } from '../util/type-utils.js'
import { parseUnaryOperation } from '../parser/unary-operation-parser.js'
import {
ExtractTypeFromValueExpressionOrList,
parseSafeImmediateValue,
parseValueExpressionOrList,
} from '../parser/value-parser.js'
import { NOOP_QUERY_EXECUTOR } from '../query-executor/noop-query-executor.js'
Expand Down Expand Up @@ -420,6 +421,30 @@ export interface ExpressionBuilder<DB, TB extends keyof DB> {
value: VE
): ExpressionWrapper<DB, TB, ExtractTypeFromValueExpressionOrList<VE>>

/**
* Returns a literal value expression.
*
* Just like `val` but creates a literal value that gets merged in the SQL.
* To prevent SQL injections, only `boolean`, `number` and `null` values
* are accepted. If you need `string` or other literals, use `sql.lit` instead.
*
* ### Examples
*
* ```ts
* db.selectFrom('person')
* .select((eb) => eb.lit(1).as('one'))
* ```
*
* The generated SQL (PostgreSQL):
*
* ```sql
* select 1 as "one" from "person"
* ```
*/
lit<VE extends number | boolean | null>(
literal: VE
): ExpressionWrapper<DB, TB, VE>

/**
* @deprecated Use the expression builder as a function instead.
*
Expand Down Expand Up @@ -811,6 +836,12 @@ export function createExpressionBuilder<DB, TB extends keyof DB>(
return new ExpressionWrapper(parseValueExpressionOrList(value))
},

lit<VE extends number | boolean | null>(
value: VE
): ExpressionWrapper<DB, TB, VE> {
return new ExpressionWrapper(parseSafeImmediateValue(value))
},

// @deprecated
cmpr<
O extends SqlBool = SqlBool,
Expand Down
8 changes: 5 additions & 3 deletions test/node/src/expression.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,14 @@ for (const dialect of DIALECTS) {
first_name: eb.ref('last_name'),
last_name: eb.ref('first_name'),
}),
// Boolean literal
eb.lit(true),
])
)

testSql(query, dialect, {
postgres: {
sql: 'select "person".* from "person" where ((not "first_name" = $1 or "id" + $2 > $3 or "id" in ($4, $5, $6) or upper("first_name") = $7 or false) and exists (select "pet"."id" from "pet" where "pet"."owner_id" = "person"."id") and true and ("id" = $8 or "id" = $9 or "id" = $10 or "id" = $11) and ("id" = $12 and "first_name" = $13 and "last_name" = $14 and "marital_status" = $15) and ("id" = $16 or "id" = $17) and ("id" + $18) > $19 and ("first_name" = $20 and "last_name" = $21) and ("first_name" = "last_name" or "last_name" = "first_name"))',
sql: 'select "person".* from "person" where ((not "first_name" = $1 or "id" + $2 > $3 or "id" in ($4, $5, $6) or upper("first_name") = $7 or false) and exists (select "pet"."id" from "pet" where "pet"."owner_id" = "person"."id") and true and ("id" = $8 or "id" = $9 or "id" = $10 or "id" = $11) and ("id" = $12 and "first_name" = $13 and "last_name" = $14 and "marital_status" = $15) and ("id" = $16 or "id" = $17) and ("id" + $18) > $19 and ("first_name" = $20 and "last_name" = $21) and ("first_name" = "last_name" or "last_name" = "first_name") and true)',
parameters: [
'Jennifer',
1,
Expand All @@ -99,7 +101,7 @@ for (const dialect of DIALECTS) {
],
},
mysql: {
sql: 'select `person`.* from `person` where ((not `first_name` = ? or `id` + ? > ? or `id` in (?, ?, ?) or upper(`first_name`) = ? or false) and exists (select `pet`.`id` from `pet` where `pet`.`owner_id` = `person`.`id`) and true and (`id` = ? or `id` = ? or `id` = ? or `id` = ?) and (`id` = ? and `first_name` = ? and `last_name` = ? and `marital_status` = ?) and (`id` = ? or `id` = ?) and (`id` + ?) > ? and (`first_name` = ? and `last_name` = ?) and (`first_name` = `last_name` or `last_name` = `first_name`))',
sql: 'select `person`.* from `person` where ((not `first_name` = ? or `id` + ? > ? or `id` in (?, ?, ?) or upper(`first_name`) = ? or false) and exists (select `pet`.`id` from `pet` where `pet`.`owner_id` = `person`.`id`) and true and (`id` = ? or `id` = ? or `id` = ? or `id` = ?) and (`id` = ? and `first_name` = ? and `last_name` = ? and `marital_status` = ?) and (`id` = ? or `id` = ?) and (`id` + ?) > ? and (`first_name` = ? and `last_name` = ?) and (`first_name` = `last_name` or `last_name` = `first_name`) and true)',
parameters: [
'Jennifer',
1,
Expand All @@ -125,7 +127,7 @@ for (const dialect of DIALECTS) {
],
},
sqlite: {
sql: 'select "person".* from "person" where ((not "first_name" = ? or "id" + ? > ? or "id" in (?, ?, ?) or upper("first_name") = ? or false) and exists (select "pet"."id" from "pet" where "pet"."owner_id" = "person"."id") and true and ("id" = ? or "id" = ? or "id" = ? or "id" = ?) and ("id" = ? and "first_name" = ? and "last_name" = ? and "marital_status" = ?) and ("id" = ? or "id" = ?) and ("id" + ?) > ? and ("first_name" = ? and "last_name" = ?) and ("first_name" = "last_name" or "last_name" = "first_name"))',
sql: 'select "person".* from "person" where ((not "first_name" = ? or "id" + ? > ? or "id" in (?, ?, ?) or upper("first_name") = ? or false) and exists (select "pet"."id" from "pet" where "pet"."owner_id" = "person"."id") and true and ("id" = ? or "id" = ? or "id" = ? or "id" = ?) and ("id" = ? and "first_name" = ? and "last_name" = ? and "marital_status" = ?) and ("id" = ? or "id" = ?) and ("id" + ?) > ? and ("first_name" = ? and "last_name" = ?) and ("first_name" = "last_name" or "last_name" = "first_name") and true)',
parameters: [
'Jennifer',
1,
Expand Down
10 changes: 5 additions & 5 deletions test/node/src/where.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -671,25 +671,25 @@ for (const dialect of DIALECTS) {
const query = ctx.db
.selectFrom('person')
.selectAll()
.where(({ exists, selectFrom }) =>
.where(({ exists, selectFrom, lit }) =>
exists(
selectFrom('pet')
.select('pet.id')
.select(lit(1).as('exists'))
.whereRef('pet.owner_id', '=', 'person.id')
)
)

testSql(query, dialect, {
postgres: {
sql: 'select * from "person" where exists (select "pet"."id" from "pet" where "pet"."owner_id" = "person"."id")',
sql: 'select * from "person" where exists (select 1 as "exists" from "pet" where "pet"."owner_id" = "person"."id")',
parameters: [],
},
mysql: {
sql: 'select * from `person` where exists (select `pet`.`id` from `pet` where `pet`.`owner_id` = `person`.`id`)',
sql: 'select * from `person` where exists (select 1 as `exists` from `pet` where `pet`.`owner_id` = `person`.`id`)',
parameters: [],
},
sqlite: {
sql: 'select * from "person" where exists (select "pet"."id" from "pet" where "pet"."owner_id" = "person"."id")',
sql: 'select * from "person" where exists (select 1 as "exists" from "pet" where "pet"."owner_id" = "person"."id")',
parameters: [],
},
})
Expand Down
7 changes: 7 additions & 0 deletions test/typings/test-d/expression.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ function testExpressionBuilder(eb: ExpressionBuilder<Database, 'person'>) {
])
)

expectAssignable<Expression<1>>(eb.lit(1))
expectAssignable<Expression<boolean>>(eb.lit(true))
expectAssignable<Expression<null>>(eb.lit(null))

expectAssignable<Expression<SqlBool>>(
eb.and({
'person.age': 10,
Expand Down Expand Up @@ -127,4 +131,7 @@ function testExpressionBuilder(eb: ExpressionBuilder<Database, 'person'>) {

expectError(eb.or({ unknown_column: 'Jennifer' }))
expectError(eb.or({ age: 'wrong type' }))

// String literals are not allowed.
expectError(eb.lit('foobar'))
}

1 comment on commit 895bea3

@vercel
Copy link

@vercel vercel bot commented on 895bea3 Jul 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

kysely – ./

kysely.dev
www.kysely.dev
kysely-git-master-kysely-team.vercel.app
kysely-kysely-team.vercel.app

Please sign in to comment.