feat(effect-drizzle-sqlite): add vendored sqlite adapter (#28547)
This commit is contained in:
@@ -0,0 +1,349 @@
|
||||
/* oxlint-disable */
|
||||
import type * as Effect from "effect/Effect"
|
||||
import { applyEffectWrapper, type QueryEffectHKTBase } from "drizzle-orm/effect-core/query-effect"
|
||||
import { entityKind, is } from "drizzle-orm/entity"
|
||||
import type { SelectResultFields } from "drizzle-orm/query-builders/select.types"
|
||||
import type { RunnableQuery } from "drizzle-orm/runnable-query"
|
||||
import type { Query, SQLWrapper } from "drizzle-orm/sql/sql"
|
||||
import { Param, SQL, sql } from "drizzle-orm/sql/sql"
|
||||
import type { SQLiteDialect } from "drizzle-orm/sqlite-core/dialect"
|
||||
import type { IndexColumn } from "drizzle-orm/sqlite-core/indexes"
|
||||
import type {
|
||||
SQLiteInsertConfig,
|
||||
SQLiteInsertSelectQueryBuilder,
|
||||
SQLiteInsertValue,
|
||||
} from "drizzle-orm/sqlite-core/query-builders/insert"
|
||||
import type { SelectedFieldsFlat } from "drizzle-orm/sqlite-core/query-builders/select.types"
|
||||
import type { PreparedQueryConfig } from "drizzle-orm/sqlite-core/session"
|
||||
import { SQLiteTable } from "drizzle-orm/sqlite-core/table"
|
||||
import { extractUsedTable } from "drizzle-orm/sqlite-core/utils"
|
||||
import type { Subquery } from "drizzle-orm/subquery"
|
||||
import { type DrizzleTypeError, haveSameKeys } from "drizzle-orm/utils"
|
||||
import type { SQLiteColumn } from "drizzle-orm/sqlite-core/columns/common"
|
||||
import { QueryBuilder } from "drizzle-orm/sqlite-core/query-builders/query-builder"
|
||||
import type { SQLiteUpdateSetSource } from "drizzle-orm/sqlite-core/query-builders/update"
|
||||
import { getTableColumnsRuntime, mapUpdateSet, orderSelectedFields } from "../../internal/drizzle-utils"
|
||||
import type { SQLiteEffectPreparedQuery, SQLiteEffectSession } from "./session"
|
||||
|
||||
export type SQLiteEffectInsertWithout<
|
||||
T extends AnySQLiteEffectInsert,
|
||||
TDynamic extends boolean,
|
||||
K extends keyof T & string,
|
||||
> = TDynamic extends true
|
||||
? T
|
||||
: Omit<
|
||||
SQLiteEffectInsertBase<
|
||||
T["_"]["table"],
|
||||
T["_"]["runResult"],
|
||||
T["_"]["returning"],
|
||||
TDynamic,
|
||||
T["_"]["excludedMethods"] | K,
|
||||
T["_"]["effectHKT"]
|
||||
>,
|
||||
T["_"]["excludedMethods"] | K
|
||||
>
|
||||
|
||||
export type SQLiteEffectInsertReturning<
|
||||
T extends AnySQLiteEffectInsert,
|
||||
TDynamic extends boolean,
|
||||
TSelectedFields extends SelectedFieldsFlat,
|
||||
> = SQLiteEffectInsertWithout<
|
||||
SQLiteEffectInsertBase<
|
||||
T["_"]["table"],
|
||||
T["_"]["runResult"],
|
||||
SelectResultFields<TSelectedFields>,
|
||||
TDynamic,
|
||||
T["_"]["excludedMethods"],
|
||||
T["_"]["effectHKT"]
|
||||
>,
|
||||
TDynamic,
|
||||
"returning"
|
||||
>
|
||||
|
||||
export type SQLiteEffectInsertReturningAll<
|
||||
T extends AnySQLiteEffectInsert,
|
||||
TDynamic extends boolean,
|
||||
> = SQLiteEffectInsertWithout<
|
||||
SQLiteEffectInsertBase<
|
||||
T["_"]["table"],
|
||||
T["_"]["runResult"],
|
||||
T["_"]["table"]["$inferSelect"],
|
||||
TDynamic,
|
||||
T["_"]["excludedMethods"],
|
||||
T["_"]["effectHKT"]
|
||||
>,
|
||||
TDynamic,
|
||||
"returning"
|
||||
>
|
||||
|
||||
export type SQLiteEffectInsertDynamic<T extends AnySQLiteEffectInsert> = SQLiteEffectInsert<
|
||||
T["_"]["table"],
|
||||
T["_"]["runResult"],
|
||||
T["_"]["returning"],
|
||||
T["_"]["effectHKT"]
|
||||
>
|
||||
|
||||
export type SQLiteEffectInsertOnConflictDoUpdateConfig<T extends AnySQLiteEffectInsert> = {
|
||||
target: IndexColumn | IndexColumn[]
|
||||
/** @deprecated - use either `targetWhere` or `setWhere` */
|
||||
where?: SQL
|
||||
targetWhere?: SQL
|
||||
setWhere?: SQL
|
||||
set: SQLiteUpdateSetSource<T["_"]["table"]>
|
||||
}
|
||||
|
||||
export type SQLiteEffectInsertExecute<T extends AnySQLiteEffectInsert> = T["_"]["returning"] extends undefined
|
||||
? T["_"]["runResult"]
|
||||
: T["_"]["returning"][]
|
||||
|
||||
export type SQLiteEffectInsertPrepare<
|
||||
T extends AnySQLiteEffectInsert,
|
||||
TEffectHKT extends QueryEffectHKTBase = T["_"]["effectHKT"],
|
||||
> = SQLiteEffectPreparedQuery<
|
||||
PreparedQueryConfig & {
|
||||
run: T["_"]["runResult"]
|
||||
all: T["_"]["returning"] extends undefined
|
||||
? DrizzleTypeError<".all() cannot be used without .returning()">
|
||||
: T["_"]["returning"][]
|
||||
get: T["_"]["returning"] extends undefined
|
||||
? DrizzleTypeError<".get() cannot be used without .returning()">
|
||||
: T["_"]["returning"]
|
||||
values: T["_"]["returning"] extends undefined
|
||||
? DrizzleTypeError<".values() cannot be used without .returning()">
|
||||
: any[][]
|
||||
execute: SQLiteEffectInsertExecute<T>
|
||||
},
|
||||
TEffectHKT
|
||||
>
|
||||
|
||||
export type SQLiteEffectInsert<
|
||||
TTable extends SQLiteTable = SQLiteTable,
|
||||
TRunResult = unknown,
|
||||
TReturning = any,
|
||||
TEffectHKT extends QueryEffectHKTBase = QueryEffectHKTBase,
|
||||
> = SQLiteEffectInsertBase<TTable, TRunResult, TReturning, true, never, TEffectHKT>
|
||||
|
||||
export type AnySQLiteEffectInsert = SQLiteEffectInsertBase<any, any, any, any, any, any>
|
||||
|
||||
export class SQLiteEffectInsertBuilder<
|
||||
TTable extends SQLiteTable,
|
||||
TRunResult,
|
||||
TEffectHKT extends QueryEffectHKTBase = QueryEffectHKTBase,
|
||||
> {
|
||||
static readonly [entityKind]: string = "SQLiteEffectInsertBuilder"
|
||||
|
||||
constructor(
|
||||
protected table: TTable,
|
||||
protected session: SQLiteEffectSession<TEffectHKT, TRunResult, any>,
|
||||
protected dialect: SQLiteDialect,
|
||||
private withList?: Subquery[],
|
||||
) {}
|
||||
|
||||
values(
|
||||
value: SQLiteInsertValue<TTable>,
|
||||
): SQLiteEffectInsertBase<TTable, TRunResult, undefined, false, never, TEffectHKT>
|
||||
values(
|
||||
values: SQLiteInsertValue<TTable>[],
|
||||
): SQLiteEffectInsertBase<TTable, TRunResult, undefined, false, never, TEffectHKT>
|
||||
values(
|
||||
values: SQLiteInsertValue<TTable> | SQLiteInsertValue<TTable>[],
|
||||
): SQLiteEffectInsertBase<TTable, TRunResult, undefined, false, never, TEffectHKT> {
|
||||
values = Array.isArray(values) ? values : [values]
|
||||
if (values.length === 0) {
|
||||
throw new Error("values() must be called with at least one value")
|
||||
}
|
||||
const mappedValues = values.map((entry) => {
|
||||
const result: Record<string, Param | SQL> = {}
|
||||
const cols = getTableColumnsRuntime(this.table)
|
||||
for (const colKey of Object.keys(entry)) {
|
||||
const colValue = entry[colKey as keyof typeof entry]
|
||||
result[colKey] = is(colValue, SQL) ? colValue : new Param(colValue, cols[colKey])
|
||||
}
|
||||
return result
|
||||
})
|
||||
|
||||
return new SQLiteEffectInsertBase(this.table, mappedValues, this.session, this.dialect, this.withList)
|
||||
}
|
||||
|
||||
select(
|
||||
selectQuery: (qb: QueryBuilder) => SQLiteInsertSelectQueryBuilder<TTable>,
|
||||
): SQLiteEffectInsertBase<TTable, TRunResult, undefined, false, never, TEffectHKT>
|
||||
select(
|
||||
selectQuery: (qb: QueryBuilder) => SQL,
|
||||
): SQLiteEffectInsertBase<TTable, TRunResult, undefined, false, never, TEffectHKT>
|
||||
select(selectQuery: SQL): SQLiteEffectInsertBase<TTable, TRunResult, undefined, false, never, TEffectHKT>
|
||||
select(
|
||||
selectQuery: SQLiteInsertSelectQueryBuilder<TTable>,
|
||||
): SQLiteEffectInsertBase<TTable, TRunResult, undefined, false, never, TEffectHKT>
|
||||
select(
|
||||
selectQuery:
|
||||
| SQL
|
||||
| SQLiteInsertSelectQueryBuilder<TTable>
|
||||
| ((qb: QueryBuilder) => SQLiteInsertSelectQueryBuilder<TTable> | SQL),
|
||||
): SQLiteEffectInsertBase<TTable, TRunResult, undefined, false, never, TEffectHKT> {
|
||||
const select = typeof selectQuery === "function" ? selectQuery(new QueryBuilder()) : selectQuery
|
||||
|
||||
if (!is(select, SQL) && !haveSameKeys(getTableColumnsRuntime(this.table), select._.selectedFields)) {
|
||||
throw new Error(
|
||||
"Insert select error: selected fields are not the same or are in a different order compared to the table definition",
|
||||
)
|
||||
}
|
||||
|
||||
return new SQLiteEffectInsertBase(this.table, select, this.session, this.dialect, this.withList, true)
|
||||
}
|
||||
}
|
||||
|
||||
export interface SQLiteEffectInsertBase<
|
||||
TTable extends SQLiteTable,
|
||||
TRunResult,
|
||||
TReturning = undefined,
|
||||
TDynamic extends boolean = false,
|
||||
_TExcludedMethods extends string = never,
|
||||
TEffectHKT extends QueryEffectHKTBase = QueryEffectHKTBase,
|
||||
> extends SQLWrapper,
|
||||
RunnableQuery<TReturning extends undefined ? TRunResult : TReturning[], "sqlite">,
|
||||
Effect.Effect<
|
||||
TReturning extends undefined ? TRunResult : TReturning[],
|
||||
TEffectHKT["error"],
|
||||
TEffectHKT["context"]
|
||||
> {
|
||||
readonly _: {
|
||||
readonly dialect: "sqlite"
|
||||
readonly table: TTable
|
||||
readonly resultType: "async"
|
||||
readonly runResult: TRunResult
|
||||
readonly returning: TReturning
|
||||
readonly dynamic: TDynamic
|
||||
readonly excludedMethods: _TExcludedMethods
|
||||
readonly result: TReturning extends undefined ? TRunResult : TReturning[]
|
||||
readonly effectHKT: TEffectHKT
|
||||
}
|
||||
}
|
||||
|
||||
export class SQLiteEffectInsertBase<
|
||||
TTable extends SQLiteTable,
|
||||
TRunResult,
|
||||
TReturning = undefined,
|
||||
TDynamic extends boolean = false,
|
||||
_TExcludedMethods extends string = never,
|
||||
TEffectHKT extends QueryEffectHKTBase = QueryEffectHKTBase,
|
||||
>
|
||||
implements RunnableQuery<TReturning extends undefined ? TRunResult : TReturning[], "sqlite">, SQLWrapper
|
||||
{
|
||||
static readonly [entityKind]: string = "SQLiteEffectInsert"
|
||||
|
||||
/** @internal */
|
||||
config: SQLiteInsertConfig<TTable>
|
||||
|
||||
constructor(
|
||||
private table: TTable,
|
||||
values: SQLiteInsertConfig["values"],
|
||||
private effectSession: SQLiteEffectSession<TEffectHKT, TRunResult, any>,
|
||||
private effectDialect: SQLiteDialect,
|
||||
withList?: Subquery[],
|
||||
select?: boolean,
|
||||
) {
|
||||
this.config = { table, values: values as any, withList, select }
|
||||
}
|
||||
|
||||
returning(): SQLiteEffectInsertReturningAll<this, TDynamic>
|
||||
returning<TSelectedFields extends SelectedFieldsFlat>(
|
||||
fields: TSelectedFields,
|
||||
): SQLiteEffectInsertReturning<this, TDynamic, TSelectedFields>
|
||||
returning(
|
||||
fields: SelectedFieldsFlat = getTableColumnsRuntime(this.config.table),
|
||||
): SQLiteEffectInsertWithout<AnySQLiteEffectInsert, TDynamic, "returning"> {
|
||||
this.config.returning = orderSelectedFields<SQLiteColumn>(fields)
|
||||
return this as any
|
||||
}
|
||||
|
||||
onConflictDoNothing(config: { target?: IndexColumn | IndexColumn[]; where?: SQL } = {}): this {
|
||||
if (!this.config.onConflict) this.config.onConflict = []
|
||||
|
||||
if (config.target === undefined) {
|
||||
this.config.onConflict.push(sql` on conflict do nothing`)
|
||||
return this
|
||||
}
|
||||
|
||||
const targetSql = Array.isArray(config.target) ? sql`${config.target}` : sql`${[config.target]}`
|
||||
const whereSql = config.where ? sql` where ${config.where}` : sql``
|
||||
this.config.onConflict.push(sql` on conflict ${targetSql} do nothing${whereSql}`)
|
||||
return this
|
||||
}
|
||||
|
||||
onConflictDoUpdate(config: SQLiteEffectInsertOnConflictDoUpdateConfig<this>): this {
|
||||
if (config.where && (config.targetWhere || config.setWhere)) {
|
||||
throw new Error(
|
||||
'You cannot use both "where" and "targetWhere"/"setWhere" at the same time - "where" is deprecated, use "targetWhere" or "setWhere" instead.',
|
||||
)
|
||||
}
|
||||
|
||||
if (!this.config.onConflict) this.config.onConflict = []
|
||||
|
||||
const whereSql = config.where ? sql` where ${config.where}` : undefined
|
||||
const targetWhereSql = config.targetWhere ? sql` where ${config.targetWhere}` : undefined
|
||||
const setWhereSql = config.setWhere ? sql` where ${config.setWhere}` : undefined
|
||||
const targetSql = Array.isArray(config.target) ? sql`${config.target}` : sql`${[config.target]}`
|
||||
const setSql = this.effectDialect.buildUpdateSet(
|
||||
this.config.table,
|
||||
mapUpdateSet(this.config.table, config.set as SQLiteUpdateSetSource<TTable>),
|
||||
)
|
||||
this.config.onConflict.push(
|
||||
sql` on conflict ${targetSql}${targetWhereSql} do update set ${setSql}${whereSql}${setWhereSql}`,
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
getSQL(): SQL {
|
||||
return this.effectDialect.buildInsertQuery(this.config)
|
||||
}
|
||||
|
||||
toSQL(): Query {
|
||||
return this.effectDialect.sqlToQuery(this.getSQL())
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_prepare(isOneTimeQuery = true): SQLiteEffectInsertPrepare<this, TEffectHKT> {
|
||||
return this.effectSession[isOneTimeQuery ? "prepareOneTimeQuery" : "prepareQuery"](
|
||||
this.effectDialect.sqlToQuery(this.getSQL()),
|
||||
this.config.returning,
|
||||
this.config.returning ? "all" : "run",
|
||||
undefined,
|
||||
{
|
||||
type: "insert",
|
||||
tables: extractUsedTable(this.config.table),
|
||||
},
|
||||
) as SQLiteEffectInsertPrepare<this, TEffectHKT>
|
||||
}
|
||||
|
||||
prepare(): SQLiteEffectInsertPrepare<this, TEffectHKT> {
|
||||
return this._prepare(false)
|
||||
}
|
||||
|
||||
run: ReturnType<this["prepare"]>["run"] = (placeholderValues) => {
|
||||
return this._prepare().run(placeholderValues)
|
||||
}
|
||||
|
||||
all: ReturnType<this["prepare"]>["all"] = (placeholderValues) => {
|
||||
return this._prepare().all(placeholderValues)
|
||||
}
|
||||
|
||||
get: ReturnType<this["prepare"]>["get"] = (placeholderValues) => {
|
||||
return this._prepare().get(placeholderValues)
|
||||
}
|
||||
|
||||
values: ReturnType<this["prepare"]>["values"] = (placeholderValues) => {
|
||||
return this._prepare().values(placeholderValues)
|
||||
}
|
||||
|
||||
execute: ReturnType<this["prepare"]>["execute"] = (placeholderValues) => {
|
||||
return this._prepare().execute(placeholderValues)
|
||||
}
|
||||
|
||||
$dynamic(): SQLiteEffectInsertDynamic<this> {
|
||||
return this as any
|
||||
}
|
||||
}
|
||||
|
||||
applyEffectWrapper(SQLiteEffectInsertBase)
|
||||
Reference in New Issue
Block a user