rado 0.1.64 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,62 +1,70 @@
1
- import { Column, PrimaryKey } from './Column';
1
+ import { Column, ColumnData, OptionalColumn, PrimaryColumn, PrimaryKey } from './Column';
2
2
  import { Cursor } from './Cursor';
3
- import { EV, Expr } from './Expr';
4
- import { Fields } from './Fields';
5
- import { Index } from './Index';
6
- import { Schema } from './Schema';
3
+ import { EV } from './Expr';
4
+ import { Index, IndexData } from './Index';
7
5
  import { Selection } from './Selection';
8
- import { Update } from './Update';
9
- export declare class Table<T> extends Cursor.SelectMultiple<Table.Normalize<T>> {
10
- [Selection.__tableType](): Table.Normalize<T>;
11
- constructor(schema: Schema);
12
- fetch(id: EV<T extends {
13
- id: Column.IsPrimary<infer V, any>;
14
- } ? V : never>): Cursor.SelectSingle<Table.Normalize<T> | undefined>;
15
- insertOne(record: Table.Insert<T>): Cursor<Table.Normalize<T>>;
16
- insertAll(data: Array<Table.Insert<T>>): Cursor.InsertValues;
17
- set(data: Update<Table.Normalize<T>>): Cursor.Update<Table.Normalize<T>>;
18
- delete(): Cursor.Delete;
19
- createTable(): Cursor.CreateTable;
20
- as(alias: string): this;
21
- alias(): Record<string, this>;
22
- get(name: string): Expr<any>;
23
- toExpr(): Expr<Table.Normalize<T>>;
24
- schema(): Schema;
6
+ interface TableDefinition {
25
7
  }
8
+ export interface TableData {
9
+ name: string;
10
+ alias?: string;
11
+ definition: TableDefinition;
12
+ columns: Record<string, ColumnData>;
13
+ meta(): {
14
+ indexes: Record<string, IndexData>;
15
+ };
16
+ }
17
+ interface TableProto<Definition> {
18
+ (conditions: {
19
+ [K in keyof Definition]?: Definition[K] extends Column<infer V> ? EV<V> : never;
20
+ }): Cursor.TableSelect<Definition>;
21
+ (...conditions: Array<EV<boolean>>): Cursor.TableSelect<Definition>;
22
+ }
23
+ declare class TableProto<Definition> {
24
+ [Selection.__tableType](): Table.Select<Definition>;
25
+ get [table.data](): TableData;
26
+ [table.meta](): TableMeta;
27
+ get name(): unknown;
28
+ get length(): unknown;
29
+ get call(): unknown;
30
+ get apply(): unknown;
31
+ get bind(): unknown;
32
+ get prototype(): unknown;
33
+ }
34
+ export type Table<Definition> = Definition & TableProto<Definition>;
26
35
  export declare namespace Table {
27
- type Intersection<A, B> = A & B extends infer U ? {
28
- [P in keyof U]: EV<U[P]>;
29
- } : never;
30
- type OptionalKeys<T> = {
31
- [K in keyof T]: null extends T[K] ? K : T[K] extends Column.IsPrimary<any, any> ? K : T[K] extends Column.IsOptional<any> ? K : never;
32
- }[keyof T];
33
- type RequiredKeys<T> = {
34
- [K in keyof T]: null extends T[K] ? never : T[K] extends Column.IsPrimary<any, any> ? never : T[K] extends Column.IsOptional<any> ? never : K;
35
- }[keyof T];
36
- type Optionals<T> = {
37
- [K in keyof T]?: T[K] extends Column.IsOptional<infer V> ? V : T[K] extends Column.IsPrimary<infer V, infer K> ? PrimaryKey<V, K> : T[K];
36
+ export type Of<Row> = Table<{
37
+ [K in keyof Row]: Column<Row[K]>;
38
+ }>;
39
+ export type Select<Definition> = {
40
+ [K in keyof Definition as Definition[K] extends Column<any> ? K : never]: Definition[K] extends PrimaryColumn<infer T, infer K> ? PrimaryKey<T, K> : Definition[K] extends Column<infer T> ? T : never;
38
41
  };
39
- export type Insert<T> = Intersection<Optionals<Pick<T, OptionalKeys<T>>>, Pick<T, RequiredKeys<T>>>;
40
- export type Normalize<T> = {
41
- [K in keyof T]: T[K] extends Column.IsOptional<infer V> ? V : T[K] extends Column.IsPrimary<infer V, infer K> ? PrimaryKey<V, K> : T[K];
42
+ export type Update<Definition> = Partial<{
43
+ [K in keyof Definition as Definition[K] extends Column<any> ? K : never]: Definition[K] extends Column<infer T> ? EV<T> : never;
44
+ }>;
45
+ type IsOptional<K> = K extends PrimaryColumn<any, any> ? true : K extends OptionalColumn<any> ? true : K extends Column<infer V> ? null extends V ? true : false : never;
46
+ export type Insert<Definition> = {
47
+ [K in keyof Definition as true extends IsOptional<Definition[K]> ? K : never]?: Definition[K] extends Column<infer V> ? EV<V> : never;
48
+ } & {
49
+ [K in keyof Definition as false extends IsOptional<Definition[K]> ? K : never]: Definition[K] extends Column<infer V> ? EV<V> : never;
42
50
  };
43
- export type Infer<T> = T extends Table<infer U> ? Normalize<U> : never;
44
51
  export {};
45
52
  }
46
- export type TableOptions<T, R> = {
47
- name: string;
48
- alias?: string;
49
- columns: {
50
- [K in keyof T]: Column<T[K]>;
51
- };
52
- indexes?: (this: Fields<T>) => Record<string, Index>;
53
+ export interface TableMeta {
54
+ indexes?: Record<string, Index>;
55
+ }
56
+ type Definition<T> = {
57
+ [K in keyof T as K extends string ? K : never]: Column<any> | (() => any);
53
58
  };
54
- export declare function table<T extends {}, R>(options: TableOptions<T, R>): Table<T> & Fields<T>;
59
+ interface Define<T> {
60
+ new (): T;
61
+ }
62
+ type Blueprint<T> = Definition<T>;
63
+ export type table<T> = T extends Table<infer D> ? Table.Select<D> : never;
64
+ export declare function createTable<Definition>(data: TableData): Table<Definition>;
65
+ export declare function table<T extends Blueprint<T>>(define: Record<string, T | Define<T>>): Table<T>;
55
66
  export declare namespace table {
56
- export type infer<T> = Table.Infer<T>;
57
- type Extensions<T> = {
58
- [key: string]: (this: T, ...args: any[]) => any;
59
- };
60
- export function extend<T extends Table<any>, E extends Extensions<T>>(target: T, extensions: E): T & E;
61
- export {};
67
+ const data: unique symbol;
68
+ const meta: unique symbol;
62
69
  }
70
+ export {};
@@ -1,130 +1,175 @@
1
1
  // src/define/Table.ts
2
+ import {
3
+ Column
4
+ } from "./Column.js";
2
5
  import { Cursor } from "./Cursor.js";
3
- import { Expr, ExprData } from "./Expr.js";
4
- import { Query } from "./Query.js";
6
+ import { BinOpType, Expr, ExprData } from "./Expr.js";
5
7
  import { Selection } from "./Selection.js";
6
8
  import { Target } from "./Target.js";
7
- var Table = class extends Cursor.SelectMultiple {
9
+ var {
10
+ keys,
11
+ entries,
12
+ fromEntries,
13
+ getOwnPropertyDescriptors,
14
+ assign,
15
+ getPrototypeOf,
16
+ setPrototypeOf,
17
+ getOwnPropertyDescriptor
18
+ } = Object;
19
+ var { ownKeys } = Reflect;
20
+ var TableProto = class {
8
21
  [Selection.__tableType]() {
9
22
  throw "assert";
10
23
  }
11
- constructor(schema) {
12
- super(
13
- Query.Select({
14
- from: Target.Table(schema),
15
- selection: ExprData.Row(Target.Table(schema))
16
- })
17
- );
18
- Object.defineProperty(this, "schema", {
19
- enumerable: false,
20
- value: () => schema
21
- });
22
- if (schema.columns)
23
- for (const column of Object.keys(schema.columns)) {
24
- Object.defineProperty(this, column, {
25
- enumerable: true,
26
- get: () => this.get(column)
27
- });
28
- }
29
- return new Proxy(this, {
30
- get(target, key) {
31
- return key in target ? target[key] : target.get(key);
32
- }
33
- });
34
- }
35
- fetch(id) {
36
- return this.where(this.get("id").is(id)).first();
37
- }
38
- insertOne(record) {
39
- return new Cursor(
40
- Query.Insert({
41
- into: this.schema(),
42
- data: [record],
43
- selection: ExprData.Row(Target.Table(this.schema())),
44
- singleResult: true
45
- })
46
- );
24
+ get [table.data]() {
25
+ throw "assert";
47
26
  }
48
- insertAll(data) {
49
- return new Cursor.Insert(this.schema()).values(...data);
27
+ [table.meta]() {
28
+ throw "assert";
50
29
  }
51
- set(data) {
52
- return new Cursor.Update(
53
- Query.Update({
54
- table: this.schema()
55
- })
56
- ).set(data);
30
+ // Clear the Function prototype, not sure if there's a better way
31
+ // as mapped types (Omit) will remove the callable signature. We define them
32
+ // in a class getter since it's the only way to also mark them as non-enumarable
33
+ // Seems open: Microsoft/TypeScript#27575
34
+ get name() {
35
+ throw "assert";
57
36
  }
58
- delete() {
59
- return new Cursor.Delete(Query.Delete({ table: this.schema() }));
37
+ get length() {
38
+ throw "assert";
60
39
  }
61
- createTable() {
62
- return new Cursor.CreateTable(this.schema());
40
+ get call() {
41
+ throw "assert";
63
42
  }
64
- as(alias) {
65
- return new Table({ ...this.schema(), alias });
43
+ get apply() {
44
+ throw "assert";
66
45
  }
67
- alias() {
68
- return new Proxy(
69
- {},
70
- {
71
- get: (_, key) => {
72
- return this.as(key);
73
- }
74
- }
75
- );
46
+ get bind() {
47
+ throw "assert";
76
48
  }
77
- get(name) {
78
- return new Expr(ExprData.Field(this.toExpr().expr, name));
49
+ get prototype() {
50
+ throw "assert";
79
51
  }
80
- toExpr() {
81
- return new Expr(
82
- ExprData.Row(Target.Table(this.schema()))
83
- );
52
+ };
53
+ function keysOf(input) {
54
+ const methods = [];
55
+ while (input = getPrototypeOf(input)) {
56
+ const keys2 = ownKeys(input);
57
+ for (const key of keys2)
58
+ if (typeof key === "string")
59
+ methods.push(key);
84
60
  }
85
- schema() {
86
- throw new Error("Not implemented");
61
+ return methods;
62
+ }
63
+ function createTable(data) {
64
+ const target = Target.Table(data);
65
+ const call = {
66
+ [data.name]: function(...args) {
67
+ const isConditionalRecord = args.length === 1 && typeof args[0] === "object" && !(args[0] instanceof Expr);
68
+ const conditions = isConditionalRecord ? entries(args[0]).map(([key, value]) => {
69
+ const column = data.columns[key];
70
+ if (!column)
71
+ throw new Error(`Column ${key} not found`);
72
+ return new Expr(
73
+ ExprData.BinOp(
74
+ BinOpType.Equals,
75
+ ExprData.Field(ExprData.Row(target), key),
76
+ ExprData.create(value)
77
+ )
78
+ );
79
+ }) : args;
80
+ return new Cursor.TableSelect(data, conditions);
81
+ }
82
+ }[data.name];
83
+ const cols = keys(data.columns);
84
+ const hasKeywords = cols.concat(keysOf(data.definition)).some((name) => name in Function);
85
+ const expressions = fromEntries(
86
+ cols.map((name) => [
87
+ name,
88
+ new Expr(ExprData.Field(ExprData.Row(target), name))
89
+ ])
90
+ );
91
+ const toExpr = () => new Expr(ExprData.Row(target));
92
+ const ownKeys2 = ["prototype", ...cols];
93
+ let res;
94
+ if (!hasKeywords) {
95
+ res = assign(call, expressions, { [table.data]: data });
96
+ setPrototypeOf(call, getPrototypeOf(data.definition));
97
+ } else {
98
+ let get2 = function(key) {
99
+ return expressions[key] || data.definition[key];
100
+ };
101
+ var get = get2;
102
+ res = new Proxy(call, {
103
+ get(target2, key) {
104
+ if (key === table.data)
105
+ return data;
106
+ if (key === Expr.toExpr)
107
+ return toExpr;
108
+ return get2(key);
109
+ },
110
+ ownKeys(target2) {
111
+ return ownKeys2;
112
+ },
113
+ getOwnPropertyDescriptor(target2, key) {
114
+ if (key === "prototype")
115
+ return getOwnPropertyDescriptor(target2, key);
116
+ return {
117
+ value: get2(key),
118
+ enumerable: true,
119
+ configurable: true
120
+ };
121
+ }
122
+ });
87
123
  }
88
- };
89
- function table(options) {
90
- const schema = {
91
- ...options,
92
- columns: Object.fromEntries(
93
- Object.entries(options.columns).map(([key, column]) => {
124
+ return res;
125
+ }
126
+ function table(define) {
127
+ const names = keys(define);
128
+ if (names.length !== 1)
129
+ throw new Error("Table must have a single name");
130
+ const name = names[0];
131
+ const target = define[name];
132
+ const definition = "prototype" in target ? new target() : target;
133
+ const columns = definition;
134
+ const res = createTable({
135
+ name,
136
+ definition,
137
+ columns: fromEntries(
138
+ entries(getOwnPropertyDescriptors(columns)).map(([name2, descriptor]) => {
139
+ const column = columns[name2];
140
+ if (!(column instanceof Column))
141
+ throw new Error(`Property ${name2} is not a column`);
94
142
  const { data } = column;
95
- return [key, { ...data, name: data.name || key }];
143
+ return [
144
+ name2,
145
+ {
146
+ ...data,
147
+ name: data.name || name2,
148
+ enumerable: descriptor.enumerable
149
+ }
150
+ ];
96
151
  })
97
152
  ),
98
- indexes: {}
99
- };
100
- const indexes = Object.fromEntries(
101
- Object.entries(
102
- options.indexes ? options.indexes.call(
103
- new Expr(ExprData.Row(Target.Table(schema)))
104
- ) : {}
105
- ).map(([key, index]) => {
106
- const indexName = `${schema.name}.${key}`;
107
- return [indexName, { name: indexName, ...index.data }];
108
- })
109
- );
110
- return new Table({
111
- ...schema,
112
- indexes
153
+ meta() {
154
+ const createMeta = res[table.meta];
155
+ const meta = createMeta ? createMeta.apply(res) : {};
156
+ return {
157
+ indexes: fromEntries(
158
+ entries(meta?.indexes || {}).map(([key, index]) => {
159
+ const indexName = `${name}.${key}`;
160
+ return [indexName, { name: indexName, ...index.data }];
161
+ })
162
+ )
163
+ };
164
+ }
113
165
  });
166
+ return res;
114
167
  }
115
168
  ((table2) => {
116
- function extend(target, extensions) {
117
- return new Proxy(target, {
118
- get(target2, key) {
119
- if (key === "as")
120
- return (...args) => extend(target2.as(...args), extensions);
121
- return key in extensions ? extensions[key].bind(target2) : target2[key];
122
- }
123
- });
124
- }
125
- table2.extend = extend;
169
+ table2.data = Symbol("data");
170
+ table2.meta = Symbol("meta");
126
171
  })(table || (table = {}));
127
172
  export {
128
- Table,
173
+ createTable,
129
174
  table
130
175
  };
@@ -1,6 +1,6 @@
1
1
  import { ExprData } from './Expr';
2
2
  import { Query as QueryData } from './Query';
3
- import { Schema } from './Schema';
3
+ import { TableData } from './Table';
4
4
  export declare enum TargetType {
5
5
  Expr = "Expr",
6
6
  Query = "Query",
@@ -23,9 +23,9 @@ export declare namespace Target {
23
23
  function Query(query: QueryData, alias?: string): Query;
24
24
  interface Table {
25
25
  type: TargetType.Table;
26
- table: Schema;
26
+ table: TableData;
27
27
  }
28
- function Table(table: Schema): Table;
28
+ function Table(table: TableData): Table;
29
29
  interface Join {
30
30
  type: TargetType.Join;
31
31
  left: Target;
@@ -34,5 +34,5 @@ export declare namespace Target {
34
34
  on: ExprData;
35
35
  }
36
36
  function Join(left: Target, right: Target, joinType: 'left' | 'inner', on: ExprData): Join;
37
- function source(from: Target): Schema | undefined;
37
+ function source(from: Target): TableData | undefined;
38
38
  }
package/dist/index.d.ts CHANGED
@@ -13,7 +13,6 @@ export * from './define/Schema';
13
13
  export * from './define/Selection';
14
14
  export * from './define/Table';
15
15
  export * from './define/Target';
16
- export * from './define/Update';
17
16
  export type { Driver } from './lib/Driver';
18
17
  export type { FormatContext, Formatter } from './lib/Formatter';
19
18
  export type { Sanitizer } from './lib/Sanitizer';
package/dist/index.js CHANGED
@@ -14,5 +14,4 @@ export * from "./define/Schema.js";
14
14
  export * from "./define/Selection.js";
15
15
  export * from "./define/Table.js";
16
16
  export * from "./define/Target.js";
17
- export * from "./define/Update.js";
18
17
  export * from "./lib/SqlError.js";
@@ -0,0 +1,3 @@
1
+ export declare class Callable extends Function {
2
+ constructor(fn: Function);
3
+ }
@@ -0,0 +1,14 @@
1
+ // src/lib/Callable.ts
2
+ var Callable = class extends Function {
3
+ constructor(fn) {
4
+ super();
5
+ return new Proxy(this, {
6
+ apply(_, thisArg, input) {
7
+ return fn.apply(thisArg, input);
8
+ }
9
+ });
10
+ }
11
+ };
12
+ export {
13
+ Callable
14
+ };
@@ -4,6 +4,7 @@ import { Expr, ExprData } from "../define/Expr.js";
4
4
  import { ParamData } from "../define/Param.js";
5
5
  import { Query, QueryType } from "../define/Query.js";
6
6
  import { Schema } from "../define/Schema.js";
7
+ import { table } from "../define/Table.js";
7
8
  var Callable = class extends Function {
8
9
  constructor(fn) {
9
10
  super();
@@ -80,8 +81,8 @@ var SyncDriver = class extends DriverBase {
80
81
  }
81
82
  migrateSchema(...tables) {
82
83
  const queries = [];
83
- for (const table of Object.values(tables)) {
84
- const schema = table.schema();
84
+ for (const current of Object.values(tables)) {
85
+ const schema = current[table.data];
85
86
  const localSchema = this.schemaInstructions(schema.name);
86
87
  if (!localSchema) {
87
88
  queries.push(...Schema.create(schema).queries);
@@ -186,8 +187,8 @@ var AsyncDriver = class extends DriverBase {
186
187
  }
187
188
  async migrateSchema(...tables) {
188
189
  const queries = [];
189
- for (const table of Object.values(tables)) {
190
- const schema = table.schema();
190
+ for (const current of Object.values(tables)) {
191
+ const schema = current[table.data];
191
192
  const localSchema = await this.schemaInstructions(schema.name);
192
193
  if (!localSchema) {
193
194
  queries.push(...Schema.create(schema).queries);
@@ -22,6 +22,7 @@ export interface FormatContext {
22
22
  formatAsInsert?: boolean;
23
23
  formatAsIn?: boolean;
24
24
  topLevel?: boolean;
25
+ selectAsColumns?: boolean;
25
26
  }
26
27
  type FormatSubject = (stmt: Statement, mkSubject: () => void) => void;
27
28
  export interface CompileOptions extends Partial<FormatContext>, StatementOptions {
@@ -43,6 +44,7 @@ export declare abstract class Formatter implements Sanitizer {
43
44
  formatAsInsert?: boolean | undefined;
44
45
  formatAsIn?: boolean | undefined;
45
46
  topLevel?: boolean | undefined;
47
+ selectAsColumns?: boolean | undefined;
46
48
  skipNewlines?: boolean | undefined;
47
49
  };
48
50
  format<T>(ctx: FormatContext, query: Query<T>): Statement;
@@ -54,6 +56,7 @@ export declare abstract class Formatter implements Sanitizer {
54
56
  formatCreateIndex(ctx: FormatContext, query: Query.CreateIndex): Statement;
55
57
  formatDropIndex(ctx: FormatContext, query: Query.DropIndex): Statement;
56
58
  formatAlterTable(ctx: FormatContext, query: Query.AlterTable): Statement;
59
+ formatDropTable(ctx: FormatContext, query: Query.DropTable): Statement;
57
60
  formatTransaction(ctx: FormatContext, { op, id }: Query.Transaction): Statement;
58
61
  formatBatch(ctx: FormatContext, { queries }: Query.Batch): Statement;
59
62
  formatRaw(ctx: FormatContext, { strings, params }: Query.Raw): Statement;
@@ -61,12 +61,14 @@ var Formatter = class {
61
61
  return this.formatDelete(ctx, query);
62
62
  case QueryType.CreateTable:
63
63
  return this.formatCreateTable(ctx, query);
64
+ case QueryType.AlterTable:
65
+ return this.formatAlterTable(ctx, query);
66
+ case QueryType.DropTable:
67
+ return this.formatDropTable(ctx, query);
64
68
  case QueryType.CreateIndex:
65
69
  return this.formatCreateIndex(ctx, query);
66
70
  case QueryType.DropIndex:
67
71
  return this.formatDropIndex(ctx, query);
68
- case QueryType.AlterTable:
69
- return this.formatAlterTable(ctx, query);
70
72
  case QueryType.Transaction:
71
73
  return this.formatTransaction(ctx, query);
72
74
  case QueryType.Batch:
@@ -96,18 +98,28 @@ var Formatter = class {
96
98
  }
97
99
  formatInsert(ctx, query) {
98
100
  const { stmt } = ctx;
99
- const columns = Object.values(query.into.columns);
101
+ const columns = Object.values(query.into.columns).map((c) => c.name);
100
102
  stmt.add("INSERT INTO").addIdentifier(query.into.name);
101
103
  if (query.into.alias)
102
104
  stmt.raw("AS").addIdentifier(query.into.alias);
103
105
  for (const column of stmt.call(columns))
104
- this.formatString(ctx, column.name);
105
- stmt.add("VALUES");
106
- if (query.data.length === 0)
107
- stmt.add("()");
108
- else
106
+ this.formatString(ctx, column);
107
+ if (query.data) {
108
+ stmt.add("VALUES").space();
109
109
  for (const row of stmt.separate(query.data))
110
110
  this.formatInsertRow(ctx, query.into.columns, row);
111
+ }
112
+ if (query.select) {
113
+ stmt.space();
114
+ this.formatSelect(
115
+ {
116
+ ...ctx,
117
+ topLevel: false,
118
+ selectAsColumns: true
119
+ },
120
+ query.select
121
+ );
122
+ }
111
123
  if (query.selection) {
112
124
  stmt.add("RETURNING").space();
113
125
  this.formatSelection(ctx, query.selection, formatAsResultObject);
@@ -183,17 +195,33 @@ var Formatter = class {
183
195
  }
184
196
  formatAlterTable(ctx, query) {
185
197
  const { stmt } = ctx;
186
- stmt.add("ALTER TABLE").addIdentifier(query.table.name);
198
+ stmt.add("ALTER TABLE").addIdentifier(
199
+ query.renameTable ? query.renameTable.from : query.table.name
200
+ );
187
201
  if (query.addColumn) {
188
202
  stmt.add("ADD COLUMN").space();
189
203
  this.formatColumn(ctx, query.addColumn);
190
204
  } else if (query.dropColumn) {
191
205
  stmt.add("DROP COLUMN").addIdentifier(query.dropColumn);
206
+ } else if (query.renameColumn) {
207
+ stmt.add("RENAME COLUMN");
208
+ stmt.addIdentifier(query.renameColumn.from).add("TO");
209
+ stmt.addIdentifier(query.renameColumn.to);
210
+ } else if (query.renameTable) {
211
+ stmt.add("RENAME TO").addIdentifier(query.table.name);
192
212
  } else if (query.alterColumn) {
193
213
  throw new Error(`Not available in this formatter: alter column`);
194
214
  }
195
215
  return stmt;
196
216
  }
217
+ formatDropTable(ctx, query) {
218
+ const { stmt } = ctx;
219
+ stmt.add("DROP TABLE");
220
+ if (query.ifExists)
221
+ stmt.add("IF EXISTS");
222
+ stmt.addIdentifier(query.table.name);
223
+ return stmt;
224
+ }
197
225
  formatTransaction(ctx, { op, id }) {
198
226
  const { stmt } = ctx;
199
227
  switch (op) {
@@ -393,10 +421,10 @@ var Formatter = class {
393
421
  const { stmt, forceInline } = ctx;
394
422
  if (!limit && !offset && !singleResult)
395
423
  return stmt;
396
- stmt.newline().raw("LIMIT");
424
+ stmt.newline().raw("LIMIT").space();
397
425
  this.formatValue(ctx, singleResult ? 1 : limit);
398
426
  if (offset && offset > 0) {
399
- stmt.raw("OFFSET");
427
+ stmt.add("OFFSET").space();
400
428
  this.formatValue(ctx, offset);
401
429
  }
402
430
  return stmt;
@@ -414,7 +442,8 @@ var Formatter = class {
414
442
  formatSubject(stmt, mkSubject);
415
443
  else
416
444
  this.formatExpr(ctx, selection);
417
- stmt.add("AS").addIdentifier("result");
445
+ if (!ctx.selectAsColumns)
446
+ stmt.add("AS").addIdentifier("result");
418
447
  return stmt;
419
448
  }
420
449
  formatExprJson(ctx, expr) {
@@ -579,12 +608,12 @@ var Formatter = class {
579
608
  return stmt.closeParenthesis();
580
609
  }
581
610
  if (expr.query.singleResult) {
582
- stmt.raw("json").openParenthesis().openParenthesis();
583
- this.format(ctx, expr.query);
584
- return stmt.closeParenthesis().closeParenthesis();
611
+ stmt.openParenthesis();
612
+ this.format({ ...ctx, topLevel: true }, expr.query);
613
+ return stmt.closeParenthesis().raw(`->'$.result'`);
585
614
  }
586
- stmt.openParenthesis().raw("SELECT json_group_array(json(result))").newline().raw("FROM").space().openParenthesis();
587
- this.format(ctx, expr.query);
615
+ stmt.openParenthesis().raw(`SELECT json_group_array(result->'$.result')`).newline().raw("FROM").space().openParenthesis();
616
+ this.format({ ...ctx, topLevel: true }, expr.query);
588
617
  return stmt.closeParenthesis().closeParenthesis();
589
618
  case ExprType.Row:
590
619
  switch (expr.target.type) {
@@ -615,12 +644,21 @@ var Formatter = class {
615
644
  this.formatExpr({ ...ctx, formatAsJson: true }, expr.b);
616
645
  return stmt.closeParenthesis();
617
646
  case ExprType.Record:
618
- stmt.identifier("json_object");
619
- for (const [key, value] of stmt.call(Object.entries(expr.fields))) {
647
+ if (ctx.selectAsColumns) {
648
+ const inner = { ...ctx, selectAsColumns: false };
649
+ for (const [key, value] of stmt.separate(
650
+ Object.entries(expr.fields)
651
+ )) {
652
+ this.formatExprJson(inner, value);
653
+ }
654
+ return stmt;
655
+ }
656
+ stmt.identifier("json_object").openParenthesis();
657
+ for (const [key, value] of stmt.separate(Object.entries(expr.fields))) {
620
658
  this.formatString(ctx, key).raw(", ");
621
659
  this.formatExprJson(ctx, value);
622
660
  }
623
- return stmt;
661
+ return stmt.closeParenthesis();
624
662
  case ExprType.Filter: {
625
663
  const { target, condition } = expr;
626
664
  switch (target.type) {