Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/demo/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const tg = await typegres({ type: "pglite" });
const pets = values(
{ species: Text.new("cat"), age: Float8.new(2), id: Int8.new(1n) },
{ species: Text.new("dog"), age: Float8.new(3), id: Int8.new(2n) },
{ species: Text.new("dog"), age: Float8.new(10), id: Int8.new(3n) }
{ species: Text.new("dog"), age: Float8.new(10), id: Int8.new(3n) },
);

const example1 = await pets
Expand Down Expand Up @@ -41,7 +41,7 @@ console.log("Example 2", example2);
const people = values(
{ id: Int8.new(1n), name: Text.new("Alice"), petId: Int8.new(1n) },
{ id: Int8.new(2n), name: Text.new("Bob"), petId: Int8.new(2n) },
{ id: Int8.new(3n), name: Text.new("Charlie"), petId: Int8.new(3n) }
{ id: Int8.new(3n), name: Text.new("Charlie"), petId: Int8.new(3n) },
);

const example3 = await people
Expand Down Expand Up @@ -69,8 +69,8 @@ const homework = await values({
.power(2)
["+"](
exp(Float8.new(0)["-"](y.power(2)))["*"](
sqrt(abs(tan(x["*"](y)))["+"](1))
)
sqrt(abs(tan(x["*"](y)))["+"](1)),
),
),
}))
.debug()
Expand Down
2 changes: 1 addition & 1 deletion src/gen/gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export const asType = (
if (runtime) {
return `Types.Setof.ofSchema({${columns.join(", ")}})`;
} else {
return `Types.Setof<{from: {${columns.join(", ")}}}>`;
return `Types.Setof<{from: Types.FromItem<{${columns.join(", ")}}>}>`;
}
}

Expand Down
109 changes: 51 additions & 58 deletions src/query/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import {
} from "./values";
import { Context, QueryAlias, SelectableExpression } from "../expression";
import type { Typegres } from "../db";
import { FromItem, Joins } from "./from-item";
import invariant from "tiny-invariant";
import { dummyDb } from "../test/db";

export const Generated = Symbol("Generated");

Expand Down Expand Up @@ -45,7 +48,9 @@ type TableSchemaToRowLike<T extends TableSchema> = {
};

type Database<DB extends DbSchema> = {
[t in keyof DB]: Table<{ from: TableSchemaToRowLike<DB[t]> }>;
[t in keyof DB]: Table<{
from: FromItem<TableSchemaToRowLike<DB[t]>, Joins>;
}>;
};

const table = (name: string, columns: TableSchema) => {
Expand All @@ -69,64 +74,68 @@ type InsertColumns<R extends RowLike> = {
};

export class Table<Q extends Query> extends Setof<Q> {
constructor(
public rawFromExpr: RawTableReferenceExpression,
public fromAlias: QueryAlias,
public joinAliases: Record<string, QueryAlias>,
public query: Q,
public fromRow: RowLike,
) {
super(rawFromExpr, fromAlias, joinAliases, query, fromRow);
constructor(public query: Q) {
super(query);
}

static of<R extends RowLike>(fromRow: R) {
return class extends Table<{
from: R;
from: FromItem<R, Joins>;
select: R;
wheres: undefined;
groupBy: undefined;
}> {
static new(fromExpr: RawTableReferenceExpression) {
const alias = new QueryAlias(fromExpr.table);
return new Table<{
from: R;
}>(
fromExpr,
alias,
{},
{
from: aliasRowLike(alias, fromRow),
},
fromRow,
);
from: FromItem<R, Joins>;
}>({
from: new FromItem(fromExpr, alias, {}, aliasRowLike(alias, fromRow)),
});
}
};
}

rawFromExpr() {
const rawFromExpr = this.query.from.rawFromExpr;
invariant(
rawFromExpr instanceof RawTableReferenceExpression,
"Raw from expression must be a table reference",
);
return rawFromExpr;
}

insert<
Q2 extends
| {
from: RowLike;
from: FromItem<RowLike, Joins>;
select: ResultType<Q2> extends RowLike
? InsertColumns<ResultType<Q2>>
: never;
}
| {
from: ResultType<Q2> extends RowLike
? InsertColumns<ResultType<Q2>>
: never;
from: FromItem<
ResultType<Q2> extends RowLike
? InsertColumns<ResultType<Q2>>
: never,
Joins
>;
},
>(expr: Setof<Q2>) {
const keys = sql.join(
Object.keys(expr.query.from).toSorted().map(sql.ref),
Object.keys(expr.query.from.from).toSorted().map(sql.ref),
sql.raw(", "),
);
const statement = sql`
INSERT INTO ${sql.ref(this.rawFromExpr.table)} (${keys})
(${expr.compile(Context.new().withReference(this.rawFromExpr.table))})
INSERT INTO ${sql.ref(this.rawFromExpr().table)} (${keys})
(${expr.compile(Context.new().withReference(this.rawFromExpr().table))})
RETURNING *
`;
return {
debug() {
console.log("Debugging query:", statement.compile(dummyDb));
return this;
},
execute: async (tg: Typegres) => {
const kysely = (tg as any)._internal;
try {
Expand All @@ -146,8 +155,8 @@ export class Table<Q extends Query> extends Setof<Q> {

update<
A extends {
where: (t: Q["from"]) => Bool<0 | 1>;
from?: (t: Q["from"]) => Setof<Query>;
where: (t: Q["from"]["from"]) => Bool<0 | 1>;
from?: (t: Q["from"]["from"]) => Setof<Query>;
},
>(arg: A) {
return new UpdateBuilder<Q, A>(this, arg);
Expand All @@ -157,8 +166,8 @@ export class Table<Q extends Query> extends Setof<Q> {
class UpdateBuilder<
Q extends Query,
A extends {
where: (t: Q["from"]) => Bool<0 | 1>;
from?: (t: Q["from"]) => Setof<Query>;
where: (t: Q["from"]["from"]) => Bool<0 | 1>;
from?: (t: Q["from"]["from"]) => Setof<Query>;
},
> {
constructor(
Expand All @@ -168,19 +177,19 @@ class UpdateBuilder<

set(
setCb: (
t: Q["from"],
...f: A["from"] extends (t: Q["from"]) => infer R
t: Q["from"]["from"],
...f: A["from"] extends (t: Q["from"]["from"]) => infer R
? R extends Setof<Query>
? SelectArgs<R["query"]>
: []
: []
) => Partial<Q["from"]>,
) => Partial<Q["from"]["from"]>,
) {
const builder = this;
return {
async execute(tg: Typegres) {
const kysely = (tg as any)._internal;
const alias = new QueryAlias(builder.table.rawFromExpr.table);
const alias = new QueryAlias(builder.table.rawFromExpr().table);
const asAlias = aliasRowLike(
alias,
resultType(builder.table.query) as RowLike,
Expand All @@ -196,43 +205,27 @@ class UpdateBuilder<
(where) => where !== undefined,
);
const { wheres: _fromWheres, ...rest } = from?.query ?? {};
const fromWithoutWheres =
from &&
"from" in rest &&
new Setof(
from.rawFromExpr,
from.fromAlias,
from.joinAliases,
rest,
from.fromRow,
);
const fromWithoutWheres = from && "from" in rest && rest.from;

const set = setCb(
asAlias,
...((builder.arg.from && from ? from.toSelectArgs() : []) as any),
);

const ctx = Context.new().withAliases([
alias,
...(from ? [from.fromAlias, ...Object.values(from.joinAliases)] : []),
]);
const [ctx, from2, joins] = fromWithoutWheres
? fromWithoutWheres.compile(Context.new().withAliases([alias]))
: [Context.new().withAliases([alias]), null, null];

const statement = sql`
UPDATE ${sql.ref(builder.table.rawFromExpr.table)}
UPDATE ${sql.ref(builder.table.rawFromExpr().table)}
SET ${sql.join(
Object.entries(set).map(
([key, value]) =>
sql`${sql.ref(key)} = ${(value as Any).toExpression().compile(ctx)}`,
),
sql.raw(", "),
)}
${
fromWithoutWheres
? sql`FROM ${fromWithoutWheres.compile(ctx)} AS ${sql.ref(
from.fromAlias.name,
)}`
: sql``
}
${from2 && joins ? sql`FROM ${from2} ${joins}` : sql``}
${
wheres.length
? sql`WHERE ${sql.join(
Expand All @@ -241,7 +234,7 @@ class UpdateBuilder<
)}`
: sql``
}
RETURNING ${sql.ref(builder.table.rawFromExpr.table)}.*
RETURNING ${sql.ref(builder.table.rawFromExpr().table)}.*
`;
try {
const res = await kysely.executeQuery(statement.compile(kysely));
Expand Down
Loading