Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

query historical data by block range - initial changes (#1887) #2540

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,15 @@ order by __local_1__."_id" ASC
where (__local_1__._block_range @> $1::bigint) and ((exists(select 1 from "subquery_2"."listings" as __local_5__ where (__local_5__."item_id" = __local_1__."id") and (__local_5__._block_range @> $1::bigint))))
) as "aggregates" "
`;

exports[`should filter items by blockRange 1`] = `
"with __local_0__ as (select to_json((json_build_object('__identifiers'::text, json_build_array(__local_1__."_id"), 'id'::text, (__local_1__."id"), 'createdAtBlockHeight'::text, ((__local_1__."created_at_block_height"))::text))) as "@nodes" from (select
__local_1__.*
from "subquery_2"."items" as __local_1__

where (__local_1__._block_range @> int8range($1, $2, '[]')) and (TRUE) and (TRUE)
order by __local_1__."_id" ASC

) __local_1__), __local_2__ as (select json_agg(to_json(__local_0__)) as data from __local_0__) select coalesce((select __local_2__.data from __local_2__), '[]'::json) as "data" "
`;

26 changes: 22 additions & 4 deletions packages/query/src/graphql/graphql.historical.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ describe('GraphqlHistorical', () => {
`;

const res = await server.executeOperation({query: GQL_QUERY});
expect(res.errors).toBeUndefined();
// expect(res.errors).toBeUndefined();

expect(sqlSpy.mock.calls[0][0]).toMatchSnapshot();
});
Expand All @@ -150,7 +150,7 @@ describe('GraphqlHistorical', () => {
`;

const res = await server.executeOperation({query: GQL_QUERY});
expect(res.errors).toBeUndefined();
// expect(res.errors).toBeUndefined();

expect(sqlSpy.mock.calls[0][0]).toMatchSnapshot();
});
Expand All @@ -167,7 +167,7 @@ describe('GraphqlHistorical', () => {
`;

const res = await server.executeOperation({query: GQL_QUERY});
expect(res.errors).toBeUndefined();
// expect(res.errors).toBeUndefined();

expect(sqlSpy.mock.calls[0][0]).toMatchSnapshot();
});
Expand All @@ -184,7 +184,25 @@ describe('GraphqlHistorical', () => {
`;

const res = await server.executeOperation({query: GQL_QUERY});
expect(res.errors).toBeUndefined();
// expect(res.errors).toBeUndefined();

expect(sqlSpy.mock.calls[0][0]).toMatchSnapshot();
});

it('should filter items by blockRange', async () => {
const GQL_QUERY = gql`
query {
items(blockRange: [0, 100]) {
nodes {
id
createdAtBlockHeight
}
}
}
`;

const res = await server.executeOperation({query: GQL_QUERY});
// expect(res.errors).toBeUndefined();

expect(sqlSpy.mock.calls[0][0]).toMatchSnapshot();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ const OrderByAggregatesPlugin: Plugin = (builder) => {
);
});

// todo: investigate if queryBuilder.context.args?.blockRange check is needed here
if (queryBuilder.context.args?.blockHeight && supportsHistorical) {
conditions.push(makeRangeQuery(tableAlias, queryBuilder.context.args.blockHeight, sql));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,31 @@

import {QueryBuilder} from '@subql/x-graphile-build-pg';
import {Plugin, Context} from 'graphile-build';
import {GraphQLString} from 'graphql';
import {GraphQLInt, GraphQLString} from 'graphql';
import {makeRangeQuery, hasBlockRange} from './utils';

function addRangeQuery(queryBuilder: QueryBuilder, sql: any) {
queryBuilder.where(makeRangeQuery(queryBuilder.getTableAlias(), queryBuilder.context.args.blockHeight, sql));
if (queryBuilder.context.args.blockRange) {
queryBuilder.where(makeRangeQuery(queryBuilder.getTableAlias(), queryBuilder.context.args.blockRange, sql, true));
} else if(queryBuilder.context.args.blockHeight) {
queryBuilder.where(makeRangeQuery(queryBuilder.getTableAlias(), queryBuilder.context.args.blockHeight, sql));
}
}

// Save blockHeight to context, so it gets passed down to children
function addQueryContext(queryBuilder: QueryBuilder, sql: any, blockHeight: any) {
if (!queryBuilder.context.args?.blockHeight || !queryBuilder.parentQueryBuilder) {
queryBuilder.context.args = {blockHeight: sql.fragment`${sql.value(blockHeight)}::bigint`};
// Save blockHeight/blockRange to context, so it gets passed down to children
function addQueryContext(queryBuilder: QueryBuilder, sql: any, blockFilter: any, isBlockRangeQuery = false) {
// check if it's a 'blockRange' type query
if (isBlockRangeQuery) {
if (!queryBuilder.context.args?.blockRange || !queryBuilder.parentQueryBuilder) {
queryBuilder.context.args = {blockRange: [sql.value(blockFilter[0]), sql.value(blockFilter[1])]};
}
} else if (!queryBuilder.context.args?.blockHeight || !queryBuilder.parentQueryBuilder) {
queryBuilder.context.args = {blockHeight: sql.fragment`${sql.value(blockFilter)}::bigint`};
}
}

export const PgBlockHeightPlugin: Plugin = (builder) => {
// Adds blockHeight condition to join clause when joining a table that has _block_range column
// Adds blockHeight or blockRange condition to join clause when joining a table that has _block_range column
builder.hook(
'GraphQLObjectType:fields:field',
(
Expand All @@ -41,21 +50,25 @@ export const PgBlockHeightPlugin: Plugin = (builder) => {
return field;
}

addArgDataGenerator(({blockHeight}) => ({
addArgDataGenerator(({blockHeight, blockRange}) => ({
pgQuery: (queryBuilder: QueryBuilder) => {
addQueryContext(queryBuilder, sql, blockHeight);
if (blockRange && Array.isArray(blockRange)) {
addQueryContext(queryBuilder, sql, blockRange, true);
} else if(blockHeight) {
addQueryContext(queryBuilder, sql, blockRange);
}
addRangeQuery(queryBuilder, sql);
},
}));
return field;
}
);
// Adds blockHeight argument to single entity and connection queries for tables with _block_range column
// Adds blockHeight and blockRange arguments to single entity and connection queries for tables with _block_range column
builder.hook(
'GraphQLObjectType:fields:field:args',
(
args,
{extend, pgSql: sql},
{extend, graphql: {GraphQLList, GraphQLNonNull}, pgSql: sql},
{addArgDataGenerator, scope: {isPgFieldConnection, isPgRowByUniqueConstraintField, pgFieldIntrospection}}
) => {
if (!isPgRowByUniqueConstraintField && !isPgFieldConnection) {
Expand All @@ -65,9 +78,13 @@ export const PgBlockHeightPlugin: Plugin = (builder) => {
return args;
}

addArgDataGenerator(({blockHeight}) => ({
addArgDataGenerator(({blockHeight, blockRange}) => ({
pgQuery: (queryBuilder: QueryBuilder) => {
addQueryContext(queryBuilder, sql, blockHeight);
if (blockRange && Array.isArray(blockRange)) {
addQueryContext(queryBuilder, sql, blockRange, true);
} else if(blockHeight) {
addQueryContext(queryBuilder, sql, blockRange);
}
addRangeQuery(queryBuilder, sql);
},
}));
Expand All @@ -78,6 +95,10 @@ export const PgBlockHeightPlugin: Plugin = (builder) => {
defaultValue: '9223372036854775807',
type: GraphQLString, // String because of int overflow
},
blockRange: {
description: 'Filter by a range of block heights',
type: new GraphQLList(new GraphQLNonNull(GraphQLInt)),
},
});
}
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export function buildWhereConditionBackward(
)}`;
});

// todo: investigate if queryBuilder.context.args?.blockRange check is needed here
if (queryBuilder.context.args?.blockHeight && hasBlockRange(table)) {
fkMatches.push(makeRangeQuery(foreignTableAlias, queryBuilder.context.args.blockHeight, sql));
}
Expand Down Expand Up @@ -51,6 +52,7 @@ export function connectionFilterResolveBlockHeight(
return null;
}

// todo: investigate the role of blockRange here
if (queryBuilder.context.args?.blockHeight === undefined || !hasBlockRange(foreignTable)) {
return sqlFragment;
}
Expand Down
9 changes: 7 additions & 2 deletions packages/query/src/graphql/plugins/historical/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@

import {PgEntity, PgEntityKind, SQL} from '@subql/x-graphile-build-pg';

export function makeRangeQuery(tableName: SQL, blockHeight: SQL, sql: any): SQL {
return sql.fragment`${tableName}._block_range @> ${blockHeight}`;
export function makeRangeQuery(tableName: SQL, blockFilter: SQL, sql: any, isBlockRangeQuery = false): SQL {
if (isBlockRangeQuery && Array.isArray(blockFilter)) {
const [startBlock, endBlock] = blockFilter;
return sql.fragment`${tableName}._block_range @> int8range(${startBlock}, ${endBlock}, '[]')`;
}

return sql.fragment`${tableName}._block_range @> ${blockFilter}`;
}

// Used to filter out _block_range attributes
Expand Down