kysely-org / kysely

A type-safe typescript SQL query builder
https://kysely.dev
MIT License
10.41k stars 266 forks source link

Support `ONLY` keyword and descendants #234

Open ooflorent opened 1 year ago

ooflorent commented 1 year ago

When using PostgresSQL's inheritance, one may want to execute a SELECT using the ONLY keyword. For example:

SELECT name, elevation
    FROM ONLY cities
    WHERE elevation > 500;

Or being explicit about querying the descendants:

SELECT name, elevation
    FROM cities*
    WHERE elevation > 500;

How can kysely achieve this without going full raw? Ideally, a few helpers could solve this issue. Here below are some API suggestions:

  1. selectOnlyFrom(tableName: string)
  2. selectFrom(tableName: string, options: { only?: boolean, descendants?: boolean })
  3. Using something from sql
igalklebanov commented 1 year ago

Hey 👋

Generally,

Don't use table inheritance. If you think you want to, use foreign keys instead. Why not? Table inheritance was a part of a fad wherein the database was closely coupled to object-oriented code. It turned out that coupling things that closely didn't actually produce the desired results. When should you? Never …almost. Now that table partitioning is done natively, that common use case for table inheritance has been replaced by a native feature that handles tuple routing, etc., without bespoke code. One of the very few exceptions would be temporal_tables extension if you are in a pinch and want to use that for row versioning in place of a lacking SQL 2011 support. Table inheritance will provide a small shortcut instead of using UNION ALL to get both historical as well as current rows. Even then you ought to be wary of caveats while working with parent table.

postgresql wiki


You can try the following:

import type { TableExpression } from "kysely"

function only<TB extends keyof DB>(table: TB): TableExpression<DB, TB> {
  return sql`only ${sql.table(table)}`
}

function descendants<TB extends keyof DB>(table: TB): TableExpression<DB, TB> {
  return sql`${sql.raw(table)}*`
}

const rows = await kysely
  .selectFrom(descendants("cities"))
  .where("elevation", ">", 500)
  .execute()

const moreRows = await kysely
  .selectFrom(only("cities"))
  .where("elevation", ">", 500)
  .execute()

playground link


Regarding the suggestions:

  1. looks least harmful, but wording should be selectFromOnly to align with the output.
  2. too "javascripty" for Kysely.