@livestore/livestore 0.0.0-snapshot-29dc6acb4ddfcb70ac29c4ae18419710d194e555 → 0.0.0-snapshot-669b49b56c8abe87f4e11263af7cbf506deab38e
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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/global-state.d.ts +1 -1
- package/dist/global-state.d.ts.map +1 -1
- package/dist/global-state.js +1 -1
- package/dist/global-state.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/{live-queries → reactiveQueries}/base-class.d.ts +4 -8
- package/dist/reactiveQueries/base-class.d.ts.map +1 -0
- package/dist/{live-queries → reactiveQueries}/base-class.js +0 -2
- package/dist/reactiveQueries/base-class.js.map +1 -0
- package/dist/{live-queries → reactiveQueries}/computed.d.ts +13 -4
- package/dist/reactiveQueries/computed.d.ts.map +1 -0
- package/dist/{live-queries → reactiveQueries}/computed.js +23 -4
- package/dist/reactiveQueries/computed.js.map +1 -0
- package/dist/{live-queries → reactiveQueries}/graphql.d.ts +8 -4
- package/dist/reactiveQueries/graphql.d.ts.map +1 -0
- package/dist/{live-queries → reactiveQueries}/graphql.js +16 -2
- package/dist/reactiveQueries/graphql.js.map +1 -0
- package/dist/reactiveQueries/sql.d.ts +49 -0
- package/dist/reactiveQueries/sql.d.ts.map +1 -0
- package/dist/reactiveQueries/sql.js +130 -0
- package/dist/reactiveQueries/sql.js.map +1 -0
- package/dist/reactiveQueries/sql.test.d.ts +2 -0
- package/dist/reactiveQueries/sql.test.d.ts.map +1 -0
- package/dist/reactiveQueries/sql.test.js +284 -0
- package/dist/reactiveQueries/sql.test.js.map +1 -0
- package/dist/row-query.d.ts +33 -0
- package/dist/row-query.d.ts.map +1 -0
- package/dist/row-query.js +80 -0
- package/dist/row-query.js.map +1 -0
- package/dist/store/create-store.d.ts +1 -1
- package/dist/store/create-store.d.ts.map +1 -1
- package/dist/store/devtools.d.ts +1 -1
- package/dist/store/devtools.d.ts.map +1 -1
- package/dist/store/devtools.js.map +1 -1
- package/dist/store/store-types.d.ts +2 -2
- package/dist/store/store-types.d.ts.map +1 -1
- package/dist/store/store.d.ts +3 -8
- package/dist/store/store.d.ts.map +1 -1
- package/dist/store/store.js +4 -32
- package/dist/store/store.js.map +1 -1
- package/dist/utils/tests/fixture.d.ts +132 -168
- package/dist/utils/tests/fixture.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/global-state.ts +1 -1
- package/src/index.ts +5 -8
- package/src/{live-queries → reactiveQueries}/base-class.ts +5 -10
- package/src/{live-queries → reactiveQueries}/computed.ts +29 -5
- package/src/{live-queries → reactiveQueries}/graphql.ts +21 -6
- package/src/reactiveQueries/sql.test.ts +308 -0
- package/src/reactiveQueries/sql.ts +226 -0
- package/src/row-query.ts +196 -0
- package/src/store/create-store.ts +1 -1
- package/src/store/devtools.ts +1 -1
- package/src/store/store-types.ts +2 -2
- package/src/store/store.ts +7 -44
- package/dist/live-queries/base-class.d.ts.map +0 -1
- package/dist/live-queries/base-class.js.map +0 -1
- package/dist/live-queries/computed.d.ts.map +0 -1
- package/dist/live-queries/computed.js.map +0 -1
- package/dist/live-queries/db.d.ts +0 -66
- package/dist/live-queries/db.d.ts.map +0 -1
- package/dist/live-queries/db.js +0 -199
- package/dist/live-queries/db.js.map +0 -1
- package/dist/live-queries/db.test.d.ts +0 -2
- package/dist/live-queries/db.test.d.ts.map +0 -1
- package/dist/live-queries/db.test.js +0 -117
- package/dist/live-queries/db.test.js.map +0 -1
- package/dist/live-queries/graphql.d.ts.map +0 -1
- package/dist/live-queries/graphql.js.map +0 -1
- package/dist/row-query-utils.d.ts +0 -17
- package/dist/row-query-utils.d.ts.map +0 -1
- package/dist/row-query-utils.js +0 -30
- package/dist/row-query-utils.js.map +0 -1
- package/src/live-queries/__snapshots__/db.test.ts.snap +0 -301
- package/src/live-queries/db.test.ts +0 -153
- package/src/live-queries/db.ts +0 -350
- package/src/row-query-utils.ts +0 -65
package/src/live-queries/db.ts
DELETED
|
@@ -1,350 +0,0 @@
|
|
|
1
|
-
import type { Bindable, QueryBuilder, QueryInfo } from '@livestore/common'
|
|
2
|
-
import {
|
|
3
|
-
getResultSchema,
|
|
4
|
-
isQueryBuilder,
|
|
5
|
-
prepareBindValues,
|
|
6
|
-
QueryBuilderAstSymbol,
|
|
7
|
-
replaceSessionIdSymbol,
|
|
8
|
-
} from '@livestore/common'
|
|
9
|
-
import { deepEqual, shouldNeverHappen } from '@livestore/utils'
|
|
10
|
-
import { Predicate, Schema, TreeFormatter } from '@livestore/utils/effect'
|
|
11
|
-
import * as otel from '@opentelemetry/api'
|
|
12
|
-
|
|
13
|
-
import { globalReactivityGraph } from '../global-state.js'
|
|
14
|
-
import type { Thunk } from '../reactive.js'
|
|
15
|
-
import { isThunk, NOT_REFRESHED_YET } from '../reactive.js'
|
|
16
|
-
import { makeExecBeforeFirstRun, rowQueryLabel } from '../row-query-utils.js'
|
|
17
|
-
import type { RefreshReason } from '../store/store-types.js'
|
|
18
|
-
import { getDurationMsFromSpan } from '../utils/otel.js'
|
|
19
|
-
import type { GetAtomResult, LiveQuery, QueryContext, ReactivityGraph } from './base-class.js'
|
|
20
|
-
import { LiveStoreQueryBase, makeGetAtomResult } from './base-class.js'
|
|
21
|
-
|
|
22
|
-
export type QueryInputRaw<TDecoded, TEncoded, TQueryInfo extends QueryInfo> = {
|
|
23
|
-
query: string
|
|
24
|
-
schema: Schema.Schema<TDecoded, TEncoded>
|
|
25
|
-
bindValues?: Bindable
|
|
26
|
-
/**
|
|
27
|
-
* Can be provided explicitly to slightly speed up initial query performance
|
|
28
|
-
*
|
|
29
|
-
* NOTE In the future we want to do this automatically at build time
|
|
30
|
-
*/
|
|
31
|
-
queriedTables?: Set<string>
|
|
32
|
-
queryInfo?: TQueryInfo
|
|
33
|
-
execBeforeFirstRun?: (ctx: QueryContext) => void
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export type QueryInput<TDecoded, TEncoded, TQueryInfo extends QueryInfo> =
|
|
37
|
-
| QueryInputRaw<TDecoded, TEncoded, TQueryInfo>
|
|
38
|
-
| QueryBuilder<TDecoded, any, any, TQueryInfo>
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* NOTE `query` is only supposed to read data. Don't use it to insert/update/delete data but use mutations instead.
|
|
42
|
-
*/
|
|
43
|
-
export const queryDb: {
|
|
44
|
-
<TResultSchema, TResult = TResultSchema, TQueryInfo extends QueryInfo = QueryInfo.None>(
|
|
45
|
-
queryInput:
|
|
46
|
-
| QueryInputRaw<TResultSchema, ReadonlyArray<any>, TQueryInfo>
|
|
47
|
-
| QueryBuilder<TResultSchema, any, any, TQueryInfo>,
|
|
48
|
-
options?: {
|
|
49
|
-
map?: (rows: TResultSchema) => TResult
|
|
50
|
-
/**
|
|
51
|
-
* Used for debugging / devtools
|
|
52
|
-
*/
|
|
53
|
-
label?: string
|
|
54
|
-
reactivityGraph?: ReactivityGraph
|
|
55
|
-
otelContext?: otel.Context
|
|
56
|
-
},
|
|
57
|
-
): LiveQuery<TResult, TQueryInfo>
|
|
58
|
-
// NOTE in this "thunk case", we can't directly derive label/queryInfo from the queryInput,
|
|
59
|
-
// so the caller needs to provide them explicitly otherwise queryInfo will be set to `None`,
|
|
60
|
-
// and label will be set during the query execution
|
|
61
|
-
<TResultSchema, TResult = TResultSchema, TQueryInfo extends QueryInfo = QueryInfo.None>(
|
|
62
|
-
queryInput:
|
|
63
|
-
| ((get: GetAtomResult) => QueryInputRaw<TResultSchema, ReadonlyArray<any>, TQueryInfo>)
|
|
64
|
-
| ((get: GetAtomResult) => QueryBuilder<TResultSchema, any, any, TQueryInfo>),
|
|
65
|
-
options?: {
|
|
66
|
-
map?: (rows: TResultSchema) => TResult
|
|
67
|
-
/**
|
|
68
|
-
* Used for debugging / devtools
|
|
69
|
-
*/
|
|
70
|
-
label?: string
|
|
71
|
-
reactivityGraph?: ReactivityGraph
|
|
72
|
-
queryInfo?: TQueryInfo
|
|
73
|
-
otelContext?: otel.Context
|
|
74
|
-
},
|
|
75
|
-
): LiveQuery<TResult, TQueryInfo>
|
|
76
|
-
} = (queryInput, options) =>
|
|
77
|
-
new LiveStoreDbQuery({
|
|
78
|
-
queryInput,
|
|
79
|
-
label: options?.label,
|
|
80
|
-
reactivityGraph: options?.reactivityGraph,
|
|
81
|
-
map: options?.map,
|
|
82
|
-
queryInfo: Predicate.hasProperty(options, 'queryInfo') ? (options.queryInfo as QueryInfo) : undefined,
|
|
83
|
-
otelContext: options?.otelContext,
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
/* An object encapsulating a reactive SQL query */
|
|
87
|
-
export class LiveStoreDbQuery<
|
|
88
|
-
TResultSchema,
|
|
89
|
-
TResult = TResultSchema,
|
|
90
|
-
TQueryInfo extends QueryInfo = QueryInfo.None,
|
|
91
|
-
> extends LiveStoreQueryBase<TResult, TQueryInfo> {
|
|
92
|
-
_tag: 'db' = 'db'
|
|
93
|
-
|
|
94
|
-
/** A reactive thunk representing the query text */
|
|
95
|
-
queryInput$: Thunk<QueryInput<TResultSchema, ReadonlyArray<any>, TQueryInfo>, QueryContext, RefreshReason> | undefined
|
|
96
|
-
|
|
97
|
-
/** A reactive thunk representing the query results */
|
|
98
|
-
results$: Thunk<TResult, QueryContext, RefreshReason>
|
|
99
|
-
|
|
100
|
-
label: string
|
|
101
|
-
|
|
102
|
-
queryInfo: TQueryInfo
|
|
103
|
-
|
|
104
|
-
protected reactivityGraph
|
|
105
|
-
|
|
106
|
-
private mapResult: (rows: TResultSchema) => TResult
|
|
107
|
-
|
|
108
|
-
constructor({
|
|
109
|
-
queryInput,
|
|
110
|
-
label: inputLabel,
|
|
111
|
-
reactivityGraph,
|
|
112
|
-
map,
|
|
113
|
-
queryInfo: inputQueryInfo,
|
|
114
|
-
otelContext,
|
|
115
|
-
}: {
|
|
116
|
-
label?: string
|
|
117
|
-
queryInput:
|
|
118
|
-
| QueryInput<TResultSchema, ReadonlyArray<any>, TQueryInfo>
|
|
119
|
-
| ((get: GetAtomResult, ctx: QueryContext) => QueryInput<TResultSchema, ReadonlyArray<any>, TQueryInfo>)
|
|
120
|
-
reactivityGraph?: ReactivityGraph
|
|
121
|
-
map?: (rows: TResultSchema) => TResult
|
|
122
|
-
queryInfo?: TQueryInfo
|
|
123
|
-
otelContext?: otel.Context
|
|
124
|
-
}) {
|
|
125
|
-
super()
|
|
126
|
-
|
|
127
|
-
let label = inputLabel ?? 'db(unknown)'
|
|
128
|
-
let queryInfo = inputQueryInfo ?? ({ _tag: 'None' } as TQueryInfo)
|
|
129
|
-
this.reactivityGraph = reactivityGraph ?? globalReactivityGraph
|
|
130
|
-
|
|
131
|
-
this.mapResult = map === undefined ? (rows: any) => rows as TResult : map
|
|
132
|
-
|
|
133
|
-
const schemaRef: { current: Schema.Schema<any, any> | undefined } = {
|
|
134
|
-
current:
|
|
135
|
-
typeof queryInput === 'function' ? undefined : isQueryBuilder(queryInput) ? undefined : queryInput.schema,
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const execBeforeFirstRunRef: { current: ((ctx: QueryContext, otelContext: otel.Context) => void) | undefined } = {
|
|
139
|
-
current: undefined,
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
type TQueryInputRaw = QueryInputRaw<any, any, QueryInfo>
|
|
143
|
-
|
|
144
|
-
let queryInputRaw$OrQueryInputRaw: TQueryInputRaw | Thunk<TQueryInputRaw, QueryContext, RefreshReason>
|
|
145
|
-
|
|
146
|
-
const fromQueryBuilder = (qb: QueryBuilder.Any, otelContext: otel.Context | undefined) => {
|
|
147
|
-
const qbRes = qb.asSql()
|
|
148
|
-
const schema = getResultSchema(qb) as Schema.Schema<TResultSchema, ReadonlyArray<any>>
|
|
149
|
-
const ast = qb[QueryBuilderAstSymbol]
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
queryInputRaw: {
|
|
153
|
-
query: qbRes.query,
|
|
154
|
-
schema,
|
|
155
|
-
bindValues: qbRes.bindValues,
|
|
156
|
-
queriedTables: new Set([ast.tableDef.sqliteDef.name]),
|
|
157
|
-
queryInfo: ast._tag === 'RowQuery' ? { _tag: 'Row', table: ast.tableDef, id: ast.id } : { _tag: 'None' },
|
|
158
|
-
} satisfies TQueryInputRaw,
|
|
159
|
-
label: ast._tag === 'RowQuery' ? rowQueryLabel(ast.tableDef, ast.id) : qb.toString(),
|
|
160
|
-
execBeforeFirstRun:
|
|
161
|
-
ast._tag === 'RowQuery'
|
|
162
|
-
? makeExecBeforeFirstRun({
|
|
163
|
-
table: ast.tableDef,
|
|
164
|
-
insertValues: ast.insertValues,
|
|
165
|
-
id: ast.id,
|
|
166
|
-
otelContext,
|
|
167
|
-
})
|
|
168
|
-
: undefined,
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (typeof queryInput === 'function') {
|
|
173
|
-
queryInputRaw$OrQueryInputRaw = this.reactivityGraph.makeThunk(
|
|
174
|
-
(get, setDebugInfo, ctx, otelContext) => {
|
|
175
|
-
const startMs = performance.now()
|
|
176
|
-
const queryInputResult = queryInput(makeGetAtomResult(get, otelContext ?? ctx.rootOtelContext), ctx)
|
|
177
|
-
const durationMs = performance.now() - startMs
|
|
178
|
-
|
|
179
|
-
let queryInputRaw: TQueryInputRaw
|
|
180
|
-
|
|
181
|
-
if (isQueryBuilder(queryInputResult)) {
|
|
182
|
-
const res = fromQueryBuilder(queryInputResult, otelContext)
|
|
183
|
-
queryInputRaw = res.queryInputRaw
|
|
184
|
-
// setting label dynamically here
|
|
185
|
-
this.label = res.label
|
|
186
|
-
execBeforeFirstRunRef.current = res.execBeforeFirstRun
|
|
187
|
-
} else {
|
|
188
|
-
queryInputRaw = queryInputResult
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
setDebugInfo({ _tag: 'computed', label: `${this.label}:queryInput`, query: queryInputRaw.query, durationMs })
|
|
192
|
-
|
|
193
|
-
schemaRef.current = queryInputRaw.schema
|
|
194
|
-
|
|
195
|
-
if (inputQueryInfo === undefined && queryInputRaw.queryInfo !== undefined) {
|
|
196
|
-
queryInfo = queryInputRaw.queryInfo as TQueryInfo
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return queryInputRaw
|
|
200
|
-
},
|
|
201
|
-
{
|
|
202
|
-
label: `${label}:query`,
|
|
203
|
-
meta: { liveStoreThunkType: 'db.query' },
|
|
204
|
-
// NOTE we're not checking the schema here as we assume the query string to always change when the schema might change
|
|
205
|
-
equal: (a, b) => a.query === b.query && deepEqual(a.bindValues, b.bindValues),
|
|
206
|
-
},
|
|
207
|
-
)
|
|
208
|
-
} else {
|
|
209
|
-
let queryInputRaw: TQueryInputRaw
|
|
210
|
-
if (isQueryBuilder(queryInput)) {
|
|
211
|
-
const res = fromQueryBuilder(queryInput, otelContext)
|
|
212
|
-
queryInputRaw = res.queryInputRaw
|
|
213
|
-
label = res.label
|
|
214
|
-
execBeforeFirstRunRef.current = res.execBeforeFirstRun
|
|
215
|
-
} else {
|
|
216
|
-
queryInputRaw = queryInput
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
schemaRef.current = queryInputRaw.schema
|
|
220
|
-
queryInputRaw$OrQueryInputRaw = queryInputRaw
|
|
221
|
-
|
|
222
|
-
// this.label = inputLabel ? this.label : `db(${})`
|
|
223
|
-
if (inputLabel === undefined && isQueryBuilder(queryInput)) {
|
|
224
|
-
const ast = queryInput[QueryBuilderAstSymbol]
|
|
225
|
-
if (ast._tag === 'RowQuery') {
|
|
226
|
-
label = `db(${rowQueryLabel(ast.tableDef, ast.id)})`
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if (inputQueryInfo === undefined && queryInputRaw.queryInfo !== undefined) {
|
|
231
|
-
queryInfo = queryInputRaw.queryInfo as TQueryInfo
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
const queriedTablesRef: { current: Set<string> | undefined } = { current: undefined }
|
|
236
|
-
|
|
237
|
-
const makeResultsEqual = (resultSchema: Schema.Schema<any, any>) => (a: TResult, b: TResult) =>
|
|
238
|
-
a === NOT_REFRESHED_YET || b === NOT_REFRESHED_YET ? false : Schema.equivalence(resultSchema)(a, b)
|
|
239
|
-
|
|
240
|
-
// NOTE we try to create the equality function eagerly as it might be expensive
|
|
241
|
-
// TODO also support derived equality for `map` (probably will depend on having an easy way to transform a schema without an `encode` step)
|
|
242
|
-
// This would mean dropping the `map` option
|
|
243
|
-
const resultsEqual =
|
|
244
|
-
map === undefined
|
|
245
|
-
? schemaRef.current === undefined
|
|
246
|
-
? (a: TResult, b: TResult) => makeResultsEqual(schemaRef.current!)(a, b)
|
|
247
|
-
: makeResultsEqual(schemaRef.current)
|
|
248
|
-
: undefined
|
|
249
|
-
|
|
250
|
-
const results$ = this.reactivityGraph.makeThunk<TResult>(
|
|
251
|
-
(get, setDebugInfo, queryContext, otelContext) =>
|
|
252
|
-
queryContext.otelTracer.startActiveSpan(
|
|
253
|
-
'db:...', // NOTE span name will be overridden further down
|
|
254
|
-
{},
|
|
255
|
-
otelContext ?? queryContext.rootOtelContext,
|
|
256
|
-
(span) => {
|
|
257
|
-
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
258
|
-
const { store } = queryContext
|
|
259
|
-
|
|
260
|
-
if (execBeforeFirstRunRef.current !== undefined) {
|
|
261
|
-
execBeforeFirstRunRef.current(queryContext, otelContext)
|
|
262
|
-
execBeforeFirstRunRef.current = undefined
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
const queryInputResult = isThunk(queryInputRaw$OrQueryInputRaw)
|
|
266
|
-
? (get(queryInputRaw$OrQueryInputRaw, otelContext) as TQueryInputRaw)
|
|
267
|
-
: (queryInputRaw$OrQueryInputRaw as TQueryInputRaw)
|
|
268
|
-
|
|
269
|
-
const sqlString = queryInputResult.query
|
|
270
|
-
const bindValues = queryInputResult.bindValues
|
|
271
|
-
|
|
272
|
-
if (queriedTablesRef.current === undefined) {
|
|
273
|
-
queriedTablesRef.current = store.syncDbWrapper.getTablesUsed(sqlString)
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
if (bindValues !== undefined) {
|
|
277
|
-
replaceSessionIdSymbol(bindValues, store.clientSession.coordinator.sessionId)
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// Establish a reactive dependency on the tables used in the query
|
|
281
|
-
for (const tableName of queriedTablesRef.current) {
|
|
282
|
-
const tableRef = store.tableRefs[tableName] ?? shouldNeverHappen(`No table ref found for ${tableName}`)
|
|
283
|
-
get(tableRef, otelContext)
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
span.setAttribute('sql.query', sqlString)
|
|
287
|
-
span.updateName(`db:${sqlString.slice(0, 50)}`)
|
|
288
|
-
|
|
289
|
-
const rawDbResults = store.syncDbWrapper.select<any>(sqlString, {
|
|
290
|
-
queriedTables: queriedTablesRef.current,
|
|
291
|
-
bindValues: bindValues ? prepareBindValues(bindValues, sqlString) : undefined,
|
|
292
|
-
otelContext,
|
|
293
|
-
})
|
|
294
|
-
|
|
295
|
-
span.setAttribute('sql.rowsCount', rawDbResults.length)
|
|
296
|
-
|
|
297
|
-
const parsedResult = Schema.decodeEither(schemaRef.current!)(rawDbResults)
|
|
298
|
-
|
|
299
|
-
if (parsedResult._tag === 'Left') {
|
|
300
|
-
const parseErrorStr = TreeFormatter.formatErrorSync(parsedResult.left)
|
|
301
|
-
const expectedSchemaStr = String(schemaRef.current!.ast)
|
|
302
|
-
const bindValuesStr = bindValues === undefined ? '' : `\nBind values: ${JSON.stringify(bindValues)}`
|
|
303
|
-
|
|
304
|
-
console.error(
|
|
305
|
-
`\
|
|
306
|
-
Error parsing SQL query result.
|
|
307
|
-
|
|
308
|
-
Query: ${sqlString}\
|
|
309
|
-
${bindValuesStr}
|
|
310
|
-
|
|
311
|
-
Expected schema: ${expectedSchemaStr}
|
|
312
|
-
|
|
313
|
-
Error: ${parseErrorStr}
|
|
314
|
-
|
|
315
|
-
Result:`,
|
|
316
|
-
rawDbResults,
|
|
317
|
-
)
|
|
318
|
-
return shouldNeverHappen(`Error parsing SQL query result: ${parsedResult.left}`)
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
const result = this.mapResult(parsedResult.right)
|
|
322
|
-
|
|
323
|
-
span.end()
|
|
324
|
-
|
|
325
|
-
const durationMs = getDurationMsFromSpan(span)
|
|
326
|
-
|
|
327
|
-
this.executionTimes.push(durationMs)
|
|
328
|
-
|
|
329
|
-
setDebugInfo({ _tag: 'db', label: `${label}:results`, query: sqlString, durationMs })
|
|
330
|
-
|
|
331
|
-
return result
|
|
332
|
-
},
|
|
333
|
-
),
|
|
334
|
-
{ label: `${label}:results`, meta: { liveStoreThunkType: 'db.result' }, equal: resultsEqual },
|
|
335
|
-
)
|
|
336
|
-
|
|
337
|
-
this.results$ = results$
|
|
338
|
-
|
|
339
|
-
this.label = label
|
|
340
|
-
this.queryInfo = queryInfo
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
destroy = () => {
|
|
344
|
-
if (this.queryInput$ !== undefined) {
|
|
345
|
-
this.reactivityGraph.destroyNode(this.queryInput$)
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
this.reactivityGraph.destroyNode(this.results$)
|
|
349
|
-
}
|
|
350
|
-
}
|
package/src/row-query-utils.ts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import type { PreparedBindValues, QueryInfo } from '@livestore/common'
|
|
2
|
-
import { SessionIdSymbol } from '@livestore/common'
|
|
3
|
-
import { DbSchema } from '@livestore/common/schema'
|
|
4
|
-
import { shouldNeverHappen } from '@livestore/utils'
|
|
5
|
-
import type * as otel from '@opentelemetry/api'
|
|
6
|
-
|
|
7
|
-
import type { LiveQuery, LiveQueryAny, QueryContext } from './live-queries/base-class.js'
|
|
8
|
-
import { computed } from './live-queries/computed.js'
|
|
9
|
-
|
|
10
|
-
export const rowQueryLabel = (table: DbSchema.TableDefBase, id: string | SessionIdSymbol | undefined) =>
|
|
11
|
-
`row:${table.sqliteDef.name}${id === undefined ? '' : id === SessionIdSymbol ? `:sessionId` : `:${id}`}`
|
|
12
|
-
|
|
13
|
-
export const deriveColQuery: {
|
|
14
|
-
<TQuery extends LiveQuery<any, QueryInfo.None>, TCol extends keyof TQuery['__result!'] & string>(
|
|
15
|
-
query$: TQuery,
|
|
16
|
-
colName: TCol,
|
|
17
|
-
): LiveQuery<TQuery['__result!'][TCol], QueryInfo.None>
|
|
18
|
-
<TQuery extends LiveQuery<any, QueryInfo.Row>, TCol extends keyof TQuery['__result!'] & string>(
|
|
19
|
-
query$: TQuery,
|
|
20
|
-
colName: TCol,
|
|
21
|
-
): LiveQuery<TQuery['__result!'][TCol], QueryInfo.Col>
|
|
22
|
-
} = (query$: LiveQueryAny, colName: string) => {
|
|
23
|
-
return computed((get) => get(query$)[colName], {
|
|
24
|
-
label: `deriveColQuery:${query$.label}:${colName}`,
|
|
25
|
-
queryInfo:
|
|
26
|
-
query$.queryInfo._tag === 'Row'
|
|
27
|
-
? { _tag: 'Col', table: query$.queryInfo.table, column: colName, id: query$.queryInfo.id }
|
|
28
|
-
: undefined,
|
|
29
|
-
}) as any
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export const makeExecBeforeFirstRun =
|
|
33
|
-
({
|
|
34
|
-
id,
|
|
35
|
-
insertValues,
|
|
36
|
-
table,
|
|
37
|
-
otelContext: otelContext_,
|
|
38
|
-
}: {
|
|
39
|
-
id?: string | SessionIdSymbol
|
|
40
|
-
insertValues?: any
|
|
41
|
-
table: DbSchema.TableDefBase
|
|
42
|
-
otelContext: otel.Context | undefined
|
|
43
|
-
}) =>
|
|
44
|
-
({ store }: QueryContext) => {
|
|
45
|
-
const otelContext = otelContext_ ?? store.otel.queriesSpanContext
|
|
46
|
-
|
|
47
|
-
if (table.options.isSingleton === false) {
|
|
48
|
-
const idStr = id === SessionIdSymbol ? store.sessionId : id!
|
|
49
|
-
const rowExists =
|
|
50
|
-
store.syncDbWrapper.select(`SELECT 1 FROM '${table.sqliteDef.name}' WHERE id = ?`, {
|
|
51
|
-
bindValues: [idStr] as any as PreparedBindValues,
|
|
52
|
-
}).length === 1
|
|
53
|
-
|
|
54
|
-
if (rowExists) return
|
|
55
|
-
|
|
56
|
-
if (DbSchema.tableHasDerivedMutations(table) === false) {
|
|
57
|
-
return shouldNeverHappen(
|
|
58
|
-
`Cannot insert row for table "${table.sqliteDef.name}" which does not have 'deriveMutations: true' set`,
|
|
59
|
-
)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// NOTE It's important that we only mutate and don't refresh here, as this function is called during a render
|
|
63
|
-
store.mutateWithoutRefresh(table.insert({ id, ...insertValues }), { otelContext, coordinatorMode: 'default' })
|
|
64
|
-
}
|
|
65
|
-
}
|