@subsquid/openreader 1.0.2 → 2.1.0
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/bin/main.js +1 -1
- package/lib/context.d.ts +14 -0
- package/lib/context.d.ts.map +1 -0
- package/lib/context.js +3 -0
- package/lib/context.js.map +1 -0
- package/lib/db.d.ts +23 -0
- package/lib/db.d.ts.map +1 -0
- package/lib/db.js +57 -0
- package/lib/db.js.map +1 -0
- package/{dist → lib}/dialect.d.ts +0 -0
- package/{dist → lib}/dialect.d.ts.map +0 -0
- package/{dist → lib}/dialect.js +0 -0
- package/{dist → lib}/dialect.js.map +0 -0
- package/lib/ir/args.d.ts +47 -0
- package/lib/ir/args.d.ts.map +1 -0
- package/lib/ir/args.js +3 -0
- package/lib/ir/args.js.map +1 -0
- package/lib/ir/connection.d.ts +30 -0
- package/lib/ir/connection.d.ts.map +1 -0
- package/lib/ir/connection.js +17 -0
- package/lib/ir/connection.js.map +1 -0
- package/lib/ir/fields.d.ts +22 -0
- package/lib/ir/fields.d.ts.map +1 -0
- package/lib/ir/fields.js +3 -0
- package/lib/ir/fields.js.map +1 -0
- package/lib/limit.size.d.ts +8 -0
- package/lib/limit.size.d.ts.map +1 -0
- package/lib/limit.size.js +107 -0
- package/lib/limit.size.js.map +1 -0
- package/{dist → lib}/main.d.ts +0 -0
- package/{dist → lib}/main.d.ts.map +0 -0
- package/lib/main.js +59 -0
- package/lib/main.js.map +1 -0
- package/{dist → lib}/model.d.ts +11 -1
- package/lib/model.d.ts.map +1 -0
- package/{dist → lib}/model.js +0 -0
- package/{dist → lib}/model.js.map +0 -0
- package/{dist/gql/schema.d.ts → lib/model.schema.d.ts} +2 -2
- package/lib/model.schema.d.ts.map +1 -0
- package/{dist/gql/schema.js → lib/model.schema.js} +62 -8
- package/lib/model.schema.js.map +1 -0
- package/{dist → lib}/model.tools.d.ts +0 -0
- package/{dist → lib}/model.tools.d.ts.map +0 -0
- package/{dist → lib}/model.tools.js +0 -0
- package/{dist → lib}/model.tools.js.map +0 -0
- package/{dist → lib/opencrud}/orderBy.d.ts +2 -5
- package/lib/opencrud/orderBy.d.ts.map +1 -0
- package/{dist → lib/opencrud}/orderBy.js +1 -1
- package/lib/opencrud/orderBy.js.map +1 -0
- package/lib/opencrud/schema.d.ts +31 -0
- package/lib/opencrud/schema.d.ts.map +1 -0
- package/lib/opencrud/schema.js +527 -0
- package/lib/opencrud/schema.js.map +1 -0
- package/lib/opencrud/tree.d.ts +8 -0
- package/lib/opencrud/tree.d.ts.map +1 -0
- package/lib/opencrud/tree.js +131 -0
- package/lib/opencrud/tree.js.map +1 -0
- package/lib/opencrud/where.d.ts +7 -0
- package/lib/opencrud/where.d.ts.map +1 -0
- package/lib/opencrud/where.js +141 -0
- package/lib/opencrud/where.js.map +1 -0
- package/{dist/gql → lib}/scalars/BigInt.d.ts +0 -0
- package/lib/scalars/BigInt.d.ts.map +1 -0
- package/{dist/gql → lib}/scalars/BigInt.js +1 -1
- package/lib/scalars/BigInt.js.map +1 -0
- package/{dist/gql → lib}/scalars/Bytes.d.ts +0 -0
- package/lib/scalars/Bytes.d.ts.map +1 -0
- package/{dist/gql → lib}/scalars/Bytes.js +2 -2
- package/lib/scalars/Bytes.js.map +1 -0
- package/{dist/gql → lib}/scalars/DateTime.d.ts +0 -0
- package/lib/scalars/DateTime.d.ts.map +1 -0
- package/{dist/gql → lib}/scalars/DateTime.js +1 -1
- package/lib/scalars/DateTime.js.map +1 -0
- package/{dist/gql → lib}/scalars/JSON.d.ts +0 -0
- package/lib/scalars/JSON.d.ts.map +1 -0
- package/{dist/gql → lib}/scalars/JSON.js +0 -0
- package/lib/scalars/JSON.js.map +1 -0
- package/{dist/gql → lib}/scalars/index.d.ts +0 -0
- package/lib/scalars/index.d.ts.map +1 -0
- package/{dist/gql → lib}/scalars/index.js +0 -0
- package/lib/scalars/index.js.map +1 -0
- package/lib/server.d.ts +42 -0
- package/lib/server.d.ts.map +1 -0
- package/lib/server.js +171 -0
- package/lib/server.js.map +1 -0
- package/lib/sql/cursor.d.ts +52 -0
- package/lib/sql/cursor.d.ts.map +1 -0
- package/lib/sql/cursor.js +234 -0
- package/lib/sql/cursor.js.map +1 -0
- package/lib/sql/mapping.d.ts +4 -0
- package/lib/sql/mapping.d.ts.map +1 -0
- package/lib/sql/mapping.js +71 -0
- package/lib/sql/mapping.js.map +1 -0
- package/lib/sql/printer.d.ts +37 -0
- package/lib/sql/printer.d.ts.map +1 -0
- package/lib/sql/printer.js +311 -0
- package/lib/sql/printer.js.map +1 -0
- package/lib/sql/query.d.ts +46 -0
- package/lib/sql/query.d.ts.map +1 -0
- package/lib/sql/query.js +134 -0
- package/lib/sql/query.js.map +1 -0
- package/lib/sql/util.d.ts +30 -0
- package/lib/sql/util.d.ts.map +1 -0
- package/lib/sql/util.js +75 -0
- package/lib/sql/util.js.map +1 -0
- package/lib/subscription.d.ts +18 -0
- package/lib/subscription.d.ts.map +1 -0
- package/lib/subscription.js +47 -0
- package/lib/subscription.js.map +1 -0
- package/{dist → lib}/test/basic.test.d.ts +0 -0
- package/{dist → lib}/test/basic.test.d.ts.map +0 -0
- package/{dist → lib}/test/basic.test.js +0 -0
- package/{dist → lib}/test/basic.test.js.map +0 -0
- package/{dist → lib}/test/connection.test.d.ts +0 -0
- package/{dist → lib}/test/connection.test.d.ts.map +0 -0
- package/{dist → lib}/test/connection.test.js +0 -0
- package/{dist → lib}/test/connection.test.js.map +0 -0
- package/{dist → lib}/test/fts.test.d.ts +0 -0
- package/{dist → lib}/test/fts.test.d.ts.map +0 -0
- package/{dist → lib}/test/fts.test.js +1 -1
- package/lib/test/fts.test.js.map +1 -0
- package/{dist → lib}/test/isNull.test.d.ts +0 -0
- package/{dist → lib}/test/isNull.test.d.ts.map +0 -0
- package/{dist → lib}/test/isNull.test.js +0 -0
- package/{dist → lib}/test/isNull.test.js.map +0 -0
- package/lib/test/limits.test.d.ts +2 -0
- package/lib/test/limits.test.d.ts.map +1 -0
- package/lib/test/limits.test.js +159 -0
- package/lib/test/limits.test.js.map +1 -0
- package/{dist → lib}/test/lists.test.d.ts +0 -0
- package/{dist → lib}/test/lists.test.d.ts.map +0 -0
- package/{dist → lib}/test/lists.test.js +0 -0
- package/{dist → lib}/test/lists.test.js.map +0 -0
- package/{dist → lib}/test/lookup.test.d.ts +0 -0
- package/{dist → lib}/test/lookup.test.d.ts.map +0 -0
- package/{dist → lib}/test/lookup.test.js +0 -0
- package/{dist → lib}/test/lookup.test.js.map +0 -0
- package/{dist → lib}/test/regressions.test.d.ts +0 -0
- package/{dist → lib}/test/regressions.test.d.ts.map +0 -0
- package/{dist → lib}/test/regressions.test.js +0 -0
- package/{dist → lib}/test/regressions.test.js.map +0 -0
- package/{dist → lib}/test/scalars.test.d.ts +0 -0
- package/{dist → lib}/test/scalars.test.d.ts.map +0 -0
- package/{dist → lib}/test/scalars.test.js +0 -0
- package/{dist → lib}/test/scalars.test.js.map +0 -0
- package/lib/test/setup.d.ts +17 -0
- package/lib/test/setup.d.ts.map +1 -0
- package/{dist → lib}/test/setup.js +18 -13
- package/lib/test/setup.js.map +1 -0
- package/lib/test/subscription.test.d.ts +2 -0
- package/lib/test/subscription.test.d.ts.map +1 -0
- package/lib/test/subscription.test.js +99 -0
- package/lib/test/subscription.test.js.map +1 -0
- package/{dist → lib}/test/tools.test.d.ts +0 -0
- package/{dist → lib}/test/tools.test.d.ts.map +0 -0
- package/{dist → lib}/test/tools.test.js +0 -0
- package/{dist → lib}/test/tools.test.js.map +0 -0
- package/{dist → lib}/test/typed-json.test.d.ts +0 -0
- package/{dist → lib}/test/typed-json.test.d.ts.map +0 -0
- package/{dist → lib}/test/typed-json.test.js +0 -0
- package/{dist → lib}/test/typed-json.test.js.map +0 -0
- package/{dist → lib}/test/unions.test.d.ts +0 -0
- package/{dist → lib}/test/unions.test.d.ts.map +0 -0
- package/{dist → lib}/test/unions.test.js +0 -0
- package/{dist → lib}/test/unions.test.js.map +0 -0
- package/{dist → lib}/test/where.test.d.ts +0 -0
- package/{dist → lib}/test/where.test.d.ts.map +0 -0
- package/{dist → lib}/test/where.test.js +0 -0
- package/{dist → lib}/test/where.test.js.map +0 -0
- package/{dist → lib}/tools.d.ts +0 -0
- package/{dist → lib}/tools.d.ts.map +0 -0
- package/{dist → lib}/tools.js +3 -3
- package/{dist → lib}/tools.js.map +1 -1
- package/lib/util/error-handling.d.ts +11 -0
- package/lib/util/error-handling.d.ts.map +1 -0
- package/lib/util/error-handling.js +42 -0
- package/lib/util/error-handling.js.map +1 -0
- package/lib/util/execute.d.ts +5 -0
- package/lib/util/execute.d.ts.map +1 -0
- package/lib/util/execute.js +28 -0
- package/lib/util/execute.js.map +1 -0
- package/lib/util/lazy-transaction.d.ts +10 -0
- package/lib/util/lazy-transaction.d.ts.map +1 -0
- package/lib/util/lazy-transaction.js +43 -0
- package/lib/util/lazy-transaction.js.map +1 -0
- package/lib/util/limit.d.ts +11 -0
- package/lib/util/limit.d.ts.map +1 -0
- package/lib/util/limit.js +39 -0
- package/lib/util/limit.js.map +1 -0
- package/lib/util/resolve-tree.d.ts +14 -0
- package/lib/util/resolve-tree.d.ts.map +1 -0
- package/lib/util/resolve-tree.js +52 -0
- package/lib/util/resolve-tree.js.map +1 -0
- package/{dist → lib/util}/util.d.ts +2 -3
- package/lib/util/util.d.ts.map +1 -0
- package/{dist → lib/util}/util.js +9 -13
- package/lib/util/util.js.map +1 -0
- package/package.json +18 -10
- package/src/context.ts +17 -0
- package/src/db.ts +46 -57
- package/src/ir/args.ts +85 -0
- package/src/ir/connection.ts +48 -0
- package/src/ir/fields.ts +40 -0
- package/src/limit.size.ts +130 -0
- package/src/main.ts +74 -42
- package/src/{gql/schema.ts → model.schema.ts} +72 -9
- package/src/model.ts +13 -1
- package/src/{orderBy.ts → opencrud/orderBy.ts} +3 -10
- package/src/opencrud/schema.ts +639 -0
- package/src/opencrud/tree.ts +144 -0
- package/src/opencrud/where.ts +141 -0
- package/src/{gql/scalars → scalars}/BigInt.ts +1 -1
- package/src/{gql/scalars → scalars}/Bytes.ts +4 -4
- package/src/{gql/scalars → scalars}/DateTime.ts +1 -1
- package/src/{gql/scalars → scalars}/JSON.ts +0 -0
- package/src/{gql/scalars → scalars}/index.ts +0 -0
- package/src/server.ts +175 -55
- package/src/sql/cursor.ts +291 -0
- package/src/sql/mapping.ts +66 -0
- package/src/sql/printer.ts +328 -0
- package/src/sql/query.ts +194 -0
- package/src/sql/util.ts +89 -0
- package/src/subscription.ts +46 -0
- package/src/test/fts.test.ts +1 -1
- package/src/test/limits.test.ts +163 -0
- package/src/test/setup.ts +16 -11
- package/src/test/subscription.test.ts +98 -0
- package/src/tools.ts +1 -1
- package/src/util/error-handling.ts +40 -0
- package/src/util/execute.ts +53 -0
- package/src/util/lazy-transaction.ts +49 -0
- package/src/util/limit.ts +34 -0
- package/src/util/resolve-tree.ts +65 -0
- package/src/{util.ts → util/util.ts} +9 -14
- package/dist/db.d.ts +0 -28
- package/dist/db.d.ts.map +0 -1
- package/dist/db.js +0 -69
- package/dist/db.js.map +0 -1
- package/dist/gql/opencrud.d.ts +0 -6
- package/dist/gql/opencrud.d.ts.map +0 -1
- package/dist/gql/opencrud.js +0 -326
- package/dist/gql/opencrud.js.map +0 -1
- package/dist/gql/scalars/BigInt.d.ts.map +0 -1
- package/dist/gql/scalars/BigInt.js.map +0 -1
- package/dist/gql/scalars/Bytes.d.ts.map +0 -1
- package/dist/gql/scalars/Bytes.js.map +0 -1
- package/dist/gql/scalars/DateTime.d.ts.map +0 -1
- package/dist/gql/scalars/DateTime.js.map +0 -1
- package/dist/gql/scalars/JSON.d.ts.map +0 -1
- package/dist/gql/scalars/JSON.js.map +0 -1
- package/dist/gql/scalars/index.d.ts.map +0 -1
- package/dist/gql/scalars/index.js.map +0 -1
- package/dist/gql/schema.d.ts.map +0 -1
- package/dist/gql/schema.js.map +0 -1
- package/dist/main.js +0 -44
- package/dist/main.js.map +0 -1
- package/dist/model.d.ts.map +0 -1
- package/dist/orderBy.d.ts.map +0 -1
- package/dist/orderBy.js.map +0 -1
- package/dist/queryBuilder.d.ts +0 -56
- package/dist/queryBuilder.d.ts.map +0 -1
- package/dist/queryBuilder.js +0 -733
- package/dist/queryBuilder.js.map +0 -1
- package/dist/relayConnection.d.ts +0 -37
- package/dist/relayConnection.d.ts.map +0 -1
- package/dist/relayConnection.js +0 -43
- package/dist/relayConnection.js.map +0 -1
- package/dist/requestedFields.d.ts +0 -33
- package/dist/requestedFields.d.ts.map +0 -1
- package/dist/requestedFields.js +0 -179
- package/dist/requestedFields.js.map +0 -1
- package/dist/resolver.d.ts +0 -9
- package/dist/resolver.d.ts.map +0 -1
- package/dist/resolver.js +0 -158
- package/dist/resolver.js.map +0 -1
- package/dist/server.d.ts +0 -22
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js +0 -96
- package/dist/server.js.map +0 -1
- package/dist/test/fts.test.js.map +0 -1
- package/dist/test/setup.d.ts +0 -14
- package/dist/test/setup.d.ts.map +0 -1
- package/dist/test/setup.js.map +0 -1
- package/dist/util.d.ts.map +0 -1
- package/dist/util.js.map +0 -1
- package/dist/where.d.ts +0 -9
- package/dist/where.d.ts.map +0 -1
- package/dist/where.js +0 -101
- package/dist/where.js.map +0 -1
- package/src/gql/opencrud.ts +0 -350
- package/src/queryBuilder.ts +0 -891
- package/src/relayConnection.ts +0 -80
- package/src/requestedFields.ts +0 -246
- package/src/resolver.ts +0 -201
- package/src/where.ts +0 -119
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {OrderBy, Where} from "./args"
|
|
2
|
+
import {FieldRequest} from "./fields"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export interface RelayConnectionRequest {
|
|
6
|
+
orderBy: OrderBy
|
|
7
|
+
where?: Where
|
|
8
|
+
first?: number
|
|
9
|
+
after?: string
|
|
10
|
+
edgeNode?: FieldRequest[]
|
|
11
|
+
edgeCursor?: boolean
|
|
12
|
+
pageInfo?: boolean
|
|
13
|
+
totalCount?: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
export interface RelayConnectionResponse {
|
|
18
|
+
edges?: RelayConnectionEdge[]
|
|
19
|
+
pageInfo?: Partial<RelayConnectionPageInfo>
|
|
20
|
+
totalCount?: number
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
export interface RelayConnectionEdge {
|
|
25
|
+
node?: unknown
|
|
26
|
+
cursor?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
export interface RelayConnectionPageInfo {
|
|
31
|
+
hasNextPage: boolean
|
|
32
|
+
hasPreviousPage: boolean
|
|
33
|
+
startCursor: string
|
|
34
|
+
endCursor: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
export function decodeRelayConnectionCursor(cursor: string): number | undefined {
|
|
39
|
+
if (!/^\d+$/.test(cursor)) return undefined
|
|
40
|
+
let val = parseInt(cursor, 10)
|
|
41
|
+
if (Number.isSafeInteger(val) && val > 0) return val
|
|
42
|
+
return undefined
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
export function encodeRelayConnectionCursor(val: number): string {
|
|
47
|
+
return '' + val
|
|
48
|
+
}
|
package/src/ir/fields.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EnumPropType,
|
|
3
|
+
FkPropType,
|
|
4
|
+
ListLookupPropType,
|
|
5
|
+
ListPropType,
|
|
6
|
+
LookupPropType,
|
|
7
|
+
ObjectPropType, Prop,
|
|
8
|
+
PropType,
|
|
9
|
+
ScalarPropType,
|
|
10
|
+
UnionPropType
|
|
11
|
+
} from "../model"
|
|
12
|
+
import {EntityListArguments} from "./args"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
export type FieldRequest = EntityListRequest | ObjectRequest | OpaqueRequest
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
type Base<T> = T extends PropType ? {
|
|
19
|
+
field: string
|
|
20
|
+
kind: T['kind']
|
|
21
|
+
type: T
|
|
22
|
+
prop: Prop
|
|
23
|
+
aliases: string[]
|
|
24
|
+
ifType?: string
|
|
25
|
+
index: number
|
|
26
|
+
} : never
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
export type EntityListRequest = Base<ListLookupPropType> & {
|
|
30
|
+
children: FieldRequest[]
|
|
31
|
+
args?: EntityListArguments
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
export type ObjectRequest = Base<FkPropType | LookupPropType | ObjectPropType | UnionPropType> & {
|
|
36
|
+
children: FieldRequest[]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
export type OpaqueRequest = Base<ScalarPropType | EnumPropType | ListPropType>
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import {unexpectedCase} from '@subsquid/util-internal'
|
|
2
|
+
import {Where} from './ir/args'
|
|
3
|
+
import {RelayConnectionRequest} from './ir/connection'
|
|
4
|
+
import {FieldRequest} from './ir/fields'
|
|
5
|
+
import {Model} from './model'
|
|
6
|
+
import {getEntity} from './model.tools'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
export function getSize(model: Model, fields: FieldRequest[]): number {
|
|
10
|
+
let total = 0
|
|
11
|
+
for (let req of fields) {
|
|
12
|
+
let size = getFieldSize(model, req)
|
|
13
|
+
if (Number.isFinite(size)) {
|
|
14
|
+
total += size * req.aliases.length
|
|
15
|
+
} else {
|
|
16
|
+
return Infinity
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return total
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
function getFieldSize(model: Model, req: FieldRequest): number {
|
|
24
|
+
switch(req.kind) {
|
|
25
|
+
case "scalar":
|
|
26
|
+
case "list":
|
|
27
|
+
return req.prop.byteWeight || 1
|
|
28
|
+
case "enum":
|
|
29
|
+
return 1
|
|
30
|
+
case "object":
|
|
31
|
+
case "fk":
|
|
32
|
+
case "lookup":
|
|
33
|
+
case "union":
|
|
34
|
+
return getSize(model, req.children) + 1
|
|
35
|
+
case "list-lookup":
|
|
36
|
+
return getEntityListSize(
|
|
37
|
+
model,
|
|
38
|
+
req.type.entity,
|
|
39
|
+
req.children,
|
|
40
|
+
Math.min(req.args?.limit ?? Infinity, req.prop.cardinality ?? Infinity),
|
|
41
|
+
req.args?.where
|
|
42
|
+
) + 1
|
|
43
|
+
default:
|
|
44
|
+
throw unexpectedCase()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
export function getEntityListSize(
|
|
50
|
+
model: Model,
|
|
51
|
+
entityName: string,
|
|
52
|
+
fields: FieldRequest[],
|
|
53
|
+
limit?: number,
|
|
54
|
+
where?: Where
|
|
55
|
+
): number {
|
|
56
|
+
let cardinality = Math.min(
|
|
57
|
+
getEntityCardinality(model, entityName),
|
|
58
|
+
limit ?? Infinity,
|
|
59
|
+
getWhereCardinality(where)
|
|
60
|
+
)
|
|
61
|
+
if (Number.isFinite(cardinality)) {
|
|
62
|
+
return cardinality * Math.max(getSize(model, fields), 1)
|
|
63
|
+
} else {
|
|
64
|
+
return Infinity
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
function getWhereCardinality(where?: Where): number {
|
|
70
|
+
if (where == null) return Infinity
|
|
71
|
+
switch(where.op) {
|
|
72
|
+
case 'AND': {
|
|
73
|
+
let min = Infinity
|
|
74
|
+
for (let co of where.args) {
|
|
75
|
+
min = Math.min(min, getWhereCardinality(co))
|
|
76
|
+
}
|
|
77
|
+
return min
|
|
78
|
+
}
|
|
79
|
+
case 'OR': {
|
|
80
|
+
if (where.args.length == 0) return Infinity
|
|
81
|
+
let max = 0
|
|
82
|
+
for (let co of where.args) {
|
|
83
|
+
max = Math.max(max, getWhereCardinality(co))
|
|
84
|
+
}
|
|
85
|
+
return max
|
|
86
|
+
}
|
|
87
|
+
case 'eq':
|
|
88
|
+
if (where.field == 'id') {
|
|
89
|
+
return 1
|
|
90
|
+
} else {
|
|
91
|
+
return Infinity
|
|
92
|
+
}
|
|
93
|
+
case 'in':
|
|
94
|
+
if (where.field == 'id') {
|
|
95
|
+
return where.values.length
|
|
96
|
+
} else {
|
|
97
|
+
return Infinity
|
|
98
|
+
}
|
|
99
|
+
default:
|
|
100
|
+
return Infinity
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
export function getRelaySize(model: Model, entityName: string, req: RelayConnectionRequest): number {
|
|
106
|
+
let total = 0
|
|
107
|
+
let limit = Math.min(
|
|
108
|
+
getEntityCardinality(model, entityName),
|
|
109
|
+
req.first ?? 100,
|
|
110
|
+
getWhereCardinality(req.where)
|
|
111
|
+
)
|
|
112
|
+
if (req.edgeNode) {
|
|
113
|
+
total += limit * Math.max(getSize(model, req.edgeNode), 1)
|
|
114
|
+
}
|
|
115
|
+
if (req.edgeCursor) {
|
|
116
|
+
total += limit
|
|
117
|
+
}
|
|
118
|
+
if (req.pageInfo) {
|
|
119
|
+
total += 4
|
|
120
|
+
}
|
|
121
|
+
if (req.totalCount) {
|
|
122
|
+
total += 1
|
|
123
|
+
}
|
|
124
|
+
return total
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
function getEntityCardinality(model: Model, entityName: string): number {
|
|
129
|
+
return getEntity(model, entityName).cardinality ?? Infinity
|
|
130
|
+
}
|
package/src/main.ts
CHANGED
|
@@ -1,53 +1,85 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import {createLogger} from '@subsquid/logger'
|
|
2
|
+
import {runProgram} from '@subsquid/util-internal'
|
|
3
|
+
import {nat, Url} from '@subsquid/util-internal-commander'
|
|
4
|
+
import {waitForInterruption} from '@subsquid/util-internal-http-server'
|
|
5
|
+
import {Command, Option} from 'commander'
|
|
6
|
+
import {Pool} from 'pg'
|
|
7
|
+
import {Dialect} from './dialect'
|
|
8
|
+
import {serve} from './server'
|
|
9
|
+
import {loadModel} from './tools'
|
|
6
10
|
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
let args = process.argv.slice(2)
|
|
12
|
+
const LOG = createLogger('sqd:openreader')
|
|
10
13
|
|
|
11
|
-
if (args.indexOf('--help') >= 0) {
|
|
12
|
-
help()
|
|
13
|
-
process.exit(1)
|
|
14
|
-
}
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
process.exit(1)
|
|
19
|
-
}
|
|
15
|
+
runProgram(async () => {
|
|
16
|
+
let program = new Command()
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
},
|
|
30
|
-
err => {
|
|
31
|
-
console.error(err)
|
|
32
|
-
process.exit(1)
|
|
33
|
-
}
|
|
18
|
+
program.description(`
|
|
19
|
+
GraphQL server for postgres-compatible databases
|
|
20
|
+
`.trim())
|
|
21
|
+
|
|
22
|
+
program.requiredOption('-s, --schema <file>', 'a path to a file or folder with database description')
|
|
23
|
+
program.requiredOption('-d, --db-url <url>', 'database connection string', Url(['postgres:']))
|
|
24
|
+
program.addOption(
|
|
25
|
+
new Option('-t, --db-type <type>', 'database type').choices(['postgres', 'cockroach']).default('postgres')
|
|
34
26
|
)
|
|
35
|
-
|
|
27
|
+
program.option('-p, --port <number>', 'port to listen on', nat, 3000)
|
|
28
|
+
program.option('--max-request-size <kb>', 'max request size in kilobytes', nat, 256)
|
|
29
|
+
program.option('--max-root-fields <count>', 'max number of root fields in a query', nat)
|
|
30
|
+
program.option('--max-response-size <nodes>', 'max response size measured in nodes', nat)
|
|
31
|
+
program.option('--sql-statement-timeout <ms>', 'sql statement timeout in ms', nat)
|
|
32
|
+
program.option('--subscriptions', 'enable gql subscriptions')
|
|
33
|
+
program.option('--subscription-poll-interval <ms>', 'subscription poll interval in ms', nat, 1000)
|
|
34
|
+
program.option('--subscription-sql-statement-timeout <ms>', 'sql statement timeout for polling queries', nat)
|
|
35
|
+
program.option('--subscription-max-response-size <nodes>', 'max response size measured in nodes', nat)
|
|
36
36
|
|
|
37
|
+
let opts = program.parse().opts() as {
|
|
38
|
+
schema: string
|
|
39
|
+
dbUrl: string
|
|
40
|
+
dbType: Dialect
|
|
41
|
+
port: number
|
|
42
|
+
maxRequestSize: number
|
|
43
|
+
maxRootFields?: number
|
|
44
|
+
maxResponseSize?: number
|
|
45
|
+
sqlStatementTimeout?: number
|
|
46
|
+
subscriptions?: boolean
|
|
47
|
+
subscriptionPollInterval: number
|
|
48
|
+
subscriptionSqlStatementTimeout?: number
|
|
49
|
+
subscriptionMaxResponseSize?: number
|
|
50
|
+
}
|
|
37
51
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
52
|
+
let model = loadModel(opts.schema)
|
|
53
|
+
|
|
54
|
+
let connection = new Pool({
|
|
55
|
+
connectionString: opts.dbUrl,
|
|
56
|
+
statement_timeout: opts.sqlStatementTimeout || undefined
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
let subscriptionConnection: Pool | undefined
|
|
60
|
+
if (opts.subscriptions) {
|
|
61
|
+
subscriptionConnection = new Pool({
|
|
62
|
+
connectionString: opts.dbUrl,
|
|
63
|
+
statement_timeout: opts.subscriptionSqlStatementTimeout || opts.sqlStatementTimeout || undefined
|
|
64
|
+
})
|
|
65
|
+
}
|
|
41
66
|
|
|
42
|
-
|
|
67
|
+
let server = await serve({
|
|
68
|
+
model,
|
|
69
|
+
dialect: opts.dbType,
|
|
70
|
+
connection,
|
|
71
|
+
port: opts.port,
|
|
72
|
+
log: LOG,
|
|
73
|
+
maxRequestSizeBytes: opts.maxRequestSize * 1024,
|
|
74
|
+
maxRootFields: opts.maxRootFields,
|
|
75
|
+
maxResponseNodes: opts.maxResponseSize,
|
|
76
|
+
subscriptions: opts.subscriptions,
|
|
77
|
+
subscriptionPollInterval: opts.subscriptionPollInterval,
|
|
78
|
+
subscriptionConnection,
|
|
79
|
+
subscriptionMaxResponseNodes: opts.subscriptionMaxResponseSize
|
|
80
|
+
})
|
|
43
81
|
|
|
44
|
-
|
|
82
|
+
LOG.info(`listening on port ${server.port}`)
|
|
45
83
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
DB_PASS
|
|
49
|
-
DB_HOST
|
|
50
|
-
DB_PORT
|
|
51
|
-
GRAPHQL_SERVER_PORT
|
|
52
|
-
`)
|
|
53
|
-
}
|
|
84
|
+
return waitForInterruption(server)
|
|
85
|
+
}, err => LOG.fatal(err))
|
|
@@ -18,8 +18,8 @@ import {
|
|
|
18
18
|
parse,
|
|
19
19
|
validateSchema
|
|
20
20
|
} from "graphql"
|
|
21
|
-
import {Index, Model, Prop, PropType} from "
|
|
22
|
-
import {validateModel} from "
|
|
21
|
+
import {Index, Model, Prop, PropType, Scalar} from "./model"
|
|
22
|
+
import {validateModel} from "./model.tools"
|
|
23
23
|
import {customScalars} from "./scalars"
|
|
24
24
|
|
|
25
25
|
|
|
@@ -29,6 +29,8 @@ const baseSchema = buildASTSchema(parse(`
|
|
|
29
29
|
directive @unique on FIELD_DEFINITION
|
|
30
30
|
directive @index(fields: [String!] unique: Boolean) on OBJECT | FIELD_DEFINITION
|
|
31
31
|
directive @fulltext(query: String!) on FIELD_DEFINITION
|
|
32
|
+
directive @cardinality(value: Int!) on OBJECT | FIELD_DEFINITION
|
|
33
|
+
directive @byteWeight(value: Float!) on FIELD_DEFINITION
|
|
32
34
|
directive @variant on OBJECT # legacy
|
|
33
35
|
directive @jsonField on OBJECT # legacy
|
|
34
36
|
scalar ID
|
|
@@ -75,11 +77,12 @@ function addEntityOrJsonObjectOrInterface(model: Model, type: GraphQLObjectType
|
|
|
75
77
|
let properties: Record<string, Prop> = {}
|
|
76
78
|
let interfaces: string[] = []
|
|
77
79
|
let indexes: Index[] = type instanceof GraphQLObjectType ? checkEntityIndexes(type) : []
|
|
80
|
+
let cardinality = checkEntityCardinality(type)
|
|
78
81
|
let description = type.description || undefined
|
|
79
82
|
|
|
80
83
|
switch(kind) {
|
|
81
84
|
case 'entity':
|
|
82
|
-
model[type.name] = {kind, properties, description, interfaces, indexes}
|
|
85
|
+
model[type.name] = {kind, properties, description, interfaces, indexes, ...cardinality}
|
|
83
86
|
break
|
|
84
87
|
case 'object':
|
|
85
88
|
model[type.name] = {kind, properties, description, interfaces}
|
|
@@ -120,6 +123,10 @@ function addEntityOrJsonObjectOrInterface(model: Model, type: GraphQLObjectType
|
|
|
120
123
|
let derivedFrom = checkDerivedFrom(type, f)
|
|
121
124
|
let index = checkFieldIndex(type, f)
|
|
122
125
|
let unique = index?.unique || false
|
|
126
|
+
let limits = {
|
|
127
|
+
...checkByteWeightDirective(type, f),
|
|
128
|
+
...checkCardinalityLimitDirective(type, f)
|
|
129
|
+
}
|
|
123
130
|
|
|
124
131
|
if (index) {
|
|
125
132
|
indexes.push(index)
|
|
@@ -137,10 +144,11 @@ function addEntityOrJsonObjectOrInterface(model: Model, type: GraphQLObjectType
|
|
|
137
144
|
properties[key] = {
|
|
138
145
|
type: wrapWithList(list.nulls, {
|
|
139
146
|
kind: 'scalar',
|
|
140
|
-
name: fieldType.name
|
|
147
|
+
name: fieldType.name as Scalar
|
|
141
148
|
}),
|
|
142
149
|
nullable,
|
|
143
|
-
description
|
|
150
|
+
description,
|
|
151
|
+
...limits
|
|
144
152
|
}
|
|
145
153
|
} else if (fieldType instanceof GraphQLEnumType) {
|
|
146
154
|
addEnum(model, fieldType)
|
|
@@ -150,7 +158,8 @@ function addEntityOrJsonObjectOrInterface(model: Model, type: GraphQLObjectType
|
|
|
150
158
|
name: fieldType.name
|
|
151
159
|
}),
|
|
152
160
|
nullable,
|
|
153
|
-
description
|
|
161
|
+
description,
|
|
162
|
+
...limits
|
|
154
163
|
}
|
|
155
164
|
} else if (fieldType instanceof GraphQLUnionType) {
|
|
156
165
|
addUnion(model, fieldType)
|
|
@@ -160,7 +169,8 @@ function addEntityOrJsonObjectOrInterface(model: Model, type: GraphQLObjectType
|
|
|
160
169
|
name: fieldType.name
|
|
161
170
|
}),
|
|
162
171
|
nullable,
|
|
163
|
-
description
|
|
172
|
+
description,
|
|
173
|
+
...limits
|
|
164
174
|
}
|
|
165
175
|
} else if (fieldType instanceof GraphQLObjectType) {
|
|
166
176
|
if (isEntityType(fieldType)) {
|
|
@@ -205,7 +215,8 @@ function addEntityOrJsonObjectOrInterface(model: Model, type: GraphQLObjectType
|
|
|
205
215
|
field: derivedFrom.field
|
|
206
216
|
},
|
|
207
217
|
nullable: false,
|
|
208
|
-
description
|
|
218
|
+
description,
|
|
219
|
+
...limits
|
|
209
220
|
}
|
|
210
221
|
break
|
|
211
222
|
default:
|
|
@@ -219,7 +230,8 @@ function addEntityOrJsonObjectOrInterface(model: Model, type: GraphQLObjectType
|
|
|
219
230
|
name: fieldType.name
|
|
220
231
|
}),
|
|
221
232
|
nullable,
|
|
222
|
-
description
|
|
233
|
+
description,
|
|
234
|
+
...limits
|
|
223
235
|
}
|
|
224
236
|
}
|
|
225
237
|
} else {
|
|
@@ -456,6 +468,57 @@ function checkDerivedFrom(type: GraphQLNamedType, f: GraphQLField<any, any>): {f
|
|
|
456
468
|
}
|
|
457
469
|
|
|
458
470
|
|
|
471
|
+
function checkEntityCardinality(type: GraphQLObjectType | GraphQLInterfaceType): {cardinality?: number} {
|
|
472
|
+
let directives = type.astNode?.directives?.filter(d => d.name.value == 'cardinality') || []
|
|
473
|
+
if (directives.length > 0 && !isEntityType(type)) {
|
|
474
|
+
throw new SchemaError(`@cardinality directive can be only applied to entities, but were applied to ${type.name}`)
|
|
475
|
+
}
|
|
476
|
+
if (directives.length > 1) throw new SchemaError(
|
|
477
|
+
`Multiple @cardinality directives where applied to ${type.name}`
|
|
478
|
+
)
|
|
479
|
+
if (directives.length == 0) return {}
|
|
480
|
+
let arg = assertNotNull(directives[0].arguments?.find(arg => arg.name.value == 'value'))
|
|
481
|
+
assert(arg.value.kind == 'IntValue')
|
|
482
|
+
let cardinality = parseInt(arg.value.value, 10)
|
|
483
|
+
if (cardinality < 0) throw new SchemaError(
|
|
484
|
+
`Incorrect @cardinality where applied to ${type.name}. Cardinality value must be positive.`
|
|
485
|
+
)
|
|
486
|
+
return {cardinality}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
function checkCardinalityLimitDirective(type: GraphQLNamedType, f: GraphQLField<any, any>): {cardinality?: number} {
|
|
491
|
+
let directives = f.astNode?.directives?.filter(d => d.name.value == 'cardinality') || []
|
|
492
|
+
if (directives.length > 1) throw new SchemaError(
|
|
493
|
+
`Multiple @cardinality directives where applied to ${type.name}.${f.name}`
|
|
494
|
+
)
|
|
495
|
+
if (directives.length == 0) return {}
|
|
496
|
+
let arg = assertNotNull(directives[0].arguments?.find(arg => arg.name.value == 'value'))
|
|
497
|
+
assert(arg.value.kind == 'IntValue')
|
|
498
|
+
let cardinality = parseInt(arg.value.value, 10)
|
|
499
|
+
if (cardinality < 0) throw new SchemaError(
|
|
500
|
+
`Incorrect @cardinality where applied to ${type.name}.${f.name}. Cardinality value must be positive.`
|
|
501
|
+
)
|
|
502
|
+
return {cardinality}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
function checkByteWeightDirective(type: GraphQLNamedType, f: GraphQLField<any, any>): {byteWeight?: number} {
|
|
507
|
+
let directives = f.astNode?.directives?.filter(d => d.name.value == 'byteWeight') || []
|
|
508
|
+
if (directives.length > 1) throw new SchemaError(
|
|
509
|
+
`Multiple @byteWeight directives where applied to ${type.name}.${f.name}`
|
|
510
|
+
)
|
|
511
|
+
if (directives.length == 0) return {}
|
|
512
|
+
let arg = assertNotNull(directives[0].arguments?.find(arg => arg.name.value == 'value'))
|
|
513
|
+
assert(arg.value.kind == 'FloatValue')
|
|
514
|
+
let byteWeight = parseFloat(arg.value.value)
|
|
515
|
+
if (byteWeight < 0) throw new SchemaError(
|
|
516
|
+
`Incorrect @byteWeight where applied to ${type.name}.${f.name}. Byte weight value must be positive.`
|
|
517
|
+
)
|
|
518
|
+
return {byteWeight}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
|
|
459
522
|
function unsupportedFieldTypeError(propName: string): Error {
|
|
460
523
|
return new SchemaError(`Property ${propName} has unsupported type`)
|
|
461
524
|
}
|
package/src/model.ts
CHANGED
|
@@ -9,6 +9,7 @@ export interface Entity extends TypeMeta {
|
|
|
9
9
|
properties: Record<Name, Prop>
|
|
10
10
|
interfaces?: Name[]
|
|
11
11
|
indexes?: Index[]
|
|
12
|
+
cardinality?: number
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
|
|
@@ -50,6 +51,14 @@ export interface Prop {
|
|
|
50
51
|
* Whether the values in the column must be unique. Applicable only to entities.
|
|
51
52
|
*/
|
|
52
53
|
unique?: boolean
|
|
54
|
+
/**
|
|
55
|
+
* Characteristic number of elements in a `list-lookup` or in a `list` of objects
|
|
56
|
+
*/
|
|
57
|
+
cardinality?: number
|
|
58
|
+
/**
|
|
59
|
+
* Relative byte size of a scalar value or scalar list
|
|
60
|
+
*/
|
|
61
|
+
byteWeight?: number
|
|
53
62
|
}
|
|
54
63
|
|
|
55
64
|
|
|
@@ -66,10 +75,13 @@ export type PropType =
|
|
|
66
75
|
|
|
67
76
|
export interface ScalarPropType {
|
|
68
77
|
kind: 'scalar'
|
|
69
|
-
name:
|
|
78
|
+
name: Scalar
|
|
70
79
|
}
|
|
71
80
|
|
|
72
81
|
|
|
82
|
+
export type Scalar = 'ID' | 'String' | 'Int' | 'Float' | 'Boolean' | 'DateTime' | 'BigInt' | 'JSON' | 'Bytes'
|
|
83
|
+
|
|
84
|
+
|
|
73
85
|
export interface EnumPropType {
|
|
74
86
|
kind: 'enum'
|
|
75
87
|
name: Name
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import assert from "assert"
|
|
2
|
-
import type {Model} from "
|
|
3
|
-
import {getUnionProps} from "
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export type SortOrder = 'ASC' | 'DESC'
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export interface OrderBy {
|
|
10
|
-
[field: string]: SortOrder | OrderBy
|
|
11
|
-
}
|
|
2
|
+
import type {Model} from "../model"
|
|
3
|
+
import {getUnionProps} from "../model.tools"
|
|
4
|
+
import {OrderBy} from "../ir/args"
|
|
12
5
|
|
|
13
6
|
|
|
14
7
|
/**
|