Skip to content

Commit 5533cec

Browse files
authored
Merge pull request #417 from dolthub/liuliu/add-pagination
Web, Graphql: add pagination to sql select
2 parents 19c01e5 + 099f8fd commit 5533cec

File tree

10 files changed

+206
-72
lines changed

10 files changed

+206
-72
lines changed

graphql-server/schema.gql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ type SqlSelect {
254254
refName: String!
255255
queryString: String!
256256
columns: [Column!]!
257-
rows: [Row!]!
257+
rows: RowList!
258258
queryExecutionStatus: QueryExecutionStatus!
259259
queryExecutionMessage: String!
260260
warnings: [String!]
@@ -350,8 +350,8 @@ type Query {
350350
doltSchemas(refName: String!, databaseName: String!, schemaName: String): [SchemaItem!]!
351351
views(refName: String!, databaseName: String!, schemaName: String): [SchemaItem!]!
352352
doltProcedures(databaseName: String!, refName: String!): [SchemaItem!]!
353-
sqlSelect(schemaName: String, refName: String!, databaseName: String!, queryString: String!): SqlSelect!
354-
sqlSelectForCsvDownload(schemaName: String, refName: String!, databaseName: String!, queryString: String!): String!
353+
sqlSelect(schemaName: String, refName: String!, databaseName: String!, queryString: String!, offset: Int): SqlSelect!
354+
sqlSelectForCsvDownload(schemaName: String, refName: String!, databaseName: String!, queryString: String!, offset: Int): String!
355355
status(databaseName: String!, refName: String!): [Status!]!
356356
table(tableName: String!, refName: String!, databaseName: String!, schemaName: String): Table!
357357
tableNames(schemaName: String, refName: String!, databaseName: String!, filterSystemTables: Boolean): TableNames!

graphql-server/src/sqlSelects/sqlSelect.model.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import * as column from "../columns/column.model";
33
import { RawRow } from "../queryFactory/types";
44
import * as row from "../rows/row.model";
55
import { QueryExecutionStatus } from "./sqlSelect.enums";
6+
import { ROW_LIMIT, getNextOffset } from "../utils";
7+
import { RowList } from "../rows/row.model";
68

79
@ObjectType()
810
export class SqlSelect {
@@ -21,8 +23,8 @@ export class SqlSelect {
2123
@Field(_type => [column.Column])
2224
columns: column.Column[];
2325

24-
@Field(_type => [row.Row])
25-
rows: row.Row[];
26+
@Field(_type => RowList)
27+
rows: RowList;
2628

2729
@Field(_type => QueryExecutionStatus)
2830
queryExecutionStatus: QueryExecutionStatus;
@@ -39,14 +41,15 @@ export function fromSqlSelectRow(
3941
refName: string,
4042
doltRows: RawRow | RawRow[] | undefined,
4143
queryString: string,
44+
offset: number,
4245
warnings?: string[],
4346
): SqlSelect {
4447
const res = {
4548
_id: `/databases/${databaseName}/refs/${refName}/queries/${queryString}`,
4649
databaseName,
4750
refName,
4851
queryString,
49-
rows: [],
52+
rows: { list: [] },
5053
columns: [],
5154
queryExecutionStatus: QueryExecutionStatus.Success,
5255
queryExecutionMessage: "",
@@ -71,11 +74,19 @@ export function fromSqlSelectRow(
7174
if (!doltRows.length) {
7275
return res;
7376
}
74-
75-
const rows: row.Row[] = doltRows.map(row.fromDoltRowRes);
77+
const rows: row.Row[] = doltRows
78+
.slice(offset, offset + ROW_LIMIT)
79+
.map(row.fromDoltRowRes);
7680
const columns: column.Column[] = Object.keys(doltRows[0]).map(c => {
7781
return { name: c, isPrimaryKey: false, type: "unknown" };
7882
});
7983

80-
return { ...res, columns, rows };
84+
return {
85+
...res,
86+
columns,
87+
rows: {
88+
list: rows,
89+
nextOffset: getNextOffset(doltRows.length, offset),
90+
},
91+
};
8192
}

graphql-server/src/sqlSelects/sqlSelect.resolver.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Args, ArgsType, Field, Query, Resolver } from "@nestjs/graphql";
1+
import { Args, ArgsType, Field, Int, Query, Resolver } from "@nestjs/graphql";
22
import { ConnectionProvider } from "../connections/connection.provider";
33
import { RawRows } from "../queryFactory/types";
44
import { getCellValue } from "../rows/row.model";
@@ -9,6 +9,9 @@ import { SqlSelect, fromSqlSelectRow } from "./sqlSelect.model";
99
export class SqlSelectArgs extends RefMaybeSchemaArgs {
1010
@Field()
1111
queryString: string;
12+
13+
@Field(_type => Int, { nullable: true })
14+
offset?: number;
1215
}
1316

1417
@Resolver(_of => SqlSelect)
@@ -18,12 +21,15 @@ export class SqlSelectResolver {
1821
@Query(_returns => SqlSelect)
1922
async sqlSelect(@Args() args: SqlSelectArgs): Promise<SqlSelect> {
2023
const conn = this.conn.connection();
24+
const offset = args.offset ?? 0;
2125
const res = await conn.getSqlSelect(args);
26+
2227
return fromSqlSelectRow(
2328
args.databaseName,
2429
args.refName,
2530
res.rows,
2631
args.queryString,
32+
offset,
2733
res.warnings,
2834
);
2935
}

web/renderer/components/FormSelectForRefs/tests/utils.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ export async function waitForVisibleValueToClick(
7171
).not.toBeInTheDocument(),
7272
);
7373

74-
expect(
75-
await screen.findByLabelText(`single-value-${selected}`),
76-
).toBeVisible();
74+
await waitFor(() => {
75+
expect(screen.getByLabelText(`single-value-${selected}`)).toBeVisible();
76+
});
7777

7878
await user.click(screen.getByText(selected));
7979
}

web/renderer/components/SchemaFragment/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export default function SchemaFragment(props: Props) {
8181
return (
8282
<QueryHandler
8383
result={res}
84-
render={data => <Inner {...props} rows={data.sqlSelect.rows} />}
84+
render={data => <Inner {...props} rows={data.sqlSelect.rows.list} />}
8585
/>
8686
);
8787
}

web/renderer/components/SqlDataTable/index.tsx

Lines changed: 28 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,75 @@
1-
import {
2-
ApolloClient,
3-
ApolloError,
4-
NormalizedCacheObject,
5-
} from "@apollo/client";
1+
import { ApolloClient } from "@apollo/client";
62
import { Inner as InnerDataTable } from "@components/DataTable";
73
import DataTableLayout from "@components/layouts/DataTableLayout";
84
import { Button, Loader } from "@dolthub/react-components";
95
import { useSessionQueryHistory } from "@dolthub/react-hooks";
10-
import {
11-
ColumnForSqlDataTableFragment,
12-
QueryExecutionStatus,
13-
RowForDataTableFragment,
14-
useSqlSelectForSqlDataTableQuery,
15-
} from "@gen/graphql-types";
166
import { SqlQueryParams } from "@lib/params";
177
import { useState } from "react";
188
import { Maybe } from "@dolthub/web-utils";
9+
import { ApolloErrorType } from "@lib/errors/types";
1910
import SqlMessage from "./SqlMessage";
2011
import { isReadOnlyDatabaseRevisionError } from "./SqlMessage/utils";
2112
import WorkingDiff from "./WorkingDiff";
2213
import css from "./index.module.css";
2314
import useSqlQuery from "./useSqlQuery";
15+
import useSqlSelectRows, { RowsState } from "./useSqlSelectRows";
2416

2517
type Props = {
2618
params: SqlQueryParams;
2719
};
2820

2921
type InnerProps = Props & {
30-
gqlError?: ApolloError;
31-
executionStatus?: QueryExecutionStatus;
32-
executionMessage?: string;
33-
rows?: RowForDataTableFragment[];
34-
columns?: ColumnForSqlDataTableFragment[];
35-
client: ApolloClient<NormalizedCacheObject>;
22+
fetchMore: () => Promise<void>;
23+
state: RowsState;
24+
hasMore: boolean;
25+
client: ApolloClient<any>;
26+
error?: ApolloErrorType;
3627
warnings?: Maybe<string[]>;
3728
};
3829

3930
function Inner(props: InnerProps) {
40-
const isMut = useSqlQuery(props.params, props.client, props.gqlError);
41-
const msg = <SqlMessage {...props} rowsLen={props.rows?.length ?? 0} />;
31+
const isMut = useSqlQuery(props.params, props.client, props.error);
32+
const msg = (
33+
<SqlMessage
34+
params={props.params}
35+
{...props.state}
36+
rowsLen={props.state.rows.length}
37+
/>
38+
);
4239
return (
4340
<>
4441
<DataTableLayout params={props.params}>
4542
<InnerDataTable
4643
params={props.params}
47-
rows={props.rows}
48-
columns={props.columns}
49-
loadMore={async () => {}}
44+
rows={props.state.rows}
45+
columns={props.state.cols}
46+
loadMore={props.fetchMore}
5047
message={msg}
5148
warnings={props.warnings}
49+
hasMore={props.hasMore}
5250
/>
5351
</DataTableLayout>
54-
{isMut && !isReadOnlyDatabaseRevisionError(props.gqlError) && (
52+
{isMut && !isReadOnlyDatabaseRevisionError(props.error) && (
5553
<WorkingDiff {...props} />
5654
)}
5755
</>
5856
);
5957
}
6058

6159
function Query(props: Props) {
62-
const { data, loading, error, client } = useSqlSelectForSqlDataTableQuery({
63-
variables: {
64-
databaseName: props.params.databaseName,
65-
refName: props.params.refName,
66-
queryString: props.params.q,
67-
schemaName: props.params.schemaName,
68-
},
69-
fetchPolicy: "cache-and-network",
70-
});
60+
const { state, fetchMore, hasMore, loading, client } = useSqlSelectRows(
61+
props.params,
62+
);
7163

7264
if (loading) return <Loader loaded={false} />;
7365

7466
return (
7567
<Inner
76-
gqlError={error}
77-
executionStatus={data?.sqlSelect.queryExecutionStatus}
78-
executionMessage={data?.sqlSelect.queryExecutionMessage}
79-
rows={data?.sqlSelect.rows}
80-
columns={data?.sqlSelect.columns}
81-
params={props.params}
68+
{...props}
69+
state={state}
70+
fetchMore={fetchMore}
71+
hasMore={hasMore}
8272
client={client}
83-
warnings={data?.sqlSelect.warnings}
8473
/>
8574
);
8675
}

web/renderer/components/SqlDataTable/queries.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,33 @@ export const SQL_SELECT_QUERY = gql`
1212
type
1313
sourceTable
1414
}
15+
fragment RowListForSqlDataTableRows on RowList {
16+
nextOffset
17+
list {
18+
...RowForSqlDataTable
19+
}
20+
}
1521
query SqlSelectForSqlDataTable(
1622
$databaseName: String!
1723
$refName: String!
1824
$queryString: String!
1925
$schemaName: String
26+
$offset: Int
2027
) {
2128
sqlSelect(
2229
databaseName: $databaseName
2330
refName: $refName
2431
queryString: $queryString
2532
schemaName: $schemaName
33+
offset: $offset
2634
) {
2735
queryExecutionStatus
2836
queryExecutionMessage
2937
columns {
3038
...ColumnForSqlDataTable
3139
}
3240
rows {
33-
...RowForSqlDataTable
41+
...RowListForSqlDataTableRows
3442
}
3543
warnings
3644
}

web/renderer/components/SqlDataTable/useSqlQuery.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
import {
2-
ApolloClient,
3-
ApolloError,
4-
NormalizedCacheObject,
5-
} from "@apollo/client";
1+
import { ApolloClient, NormalizedCacheObject } from "@apollo/client";
62
import { useSessionQueryHistory } from "@dolthub/react-hooks";
73
import useSqlParser from "@hooks/useSqlParser";
4+
import { ApolloErrorType } from "@lib/errors/types";
85
import { SqlQueryParams } from "@lib/params";
96
import { refetchUpdateDatabaseQueriesCacheEvict } from "@lib/refetchQueries";
107
import { databases } from "@lib/urls";
@@ -14,7 +11,7 @@ import { useEffect } from "react";
1411
export default function useSqlQuery(
1512
params: SqlQueryParams,
1613
client: ApolloClient<NormalizedCacheObject>,
17-
gqlError?: ApolloError,
14+
gqlError?: ApolloErrorType,
1815
): boolean {
1916
const { isMutation } = useSqlParser();
2017
const router = useRouter();

0 commit comments

Comments
 (0)