1import type * as bigquery from "@distilled.cloud/gcp/bigquery_v2";
2import * as Data from "effect/Data";
3import type * as Effect from "effect/Effect";
4import * as Binding from "../../Binding.ts";
5import type { RuntimeContext } from "../../RuntimeContext.ts";
6import type { Table } from "./Table.ts";
7
8/** A query job did not finish within the client's bounded wait. */
9export class QueryNotComplete extends Data.TaggedError(
10 "GCP.BigQuery.QueryNotComplete",
11)<{
12 jobId: string | undefined;
13}> {}
14
15export interface ListTableOptions {
16 /** Maximum rows per page. */
17 maxResults?: number;
18 /** Page token from a previous `list`. */
19 pageToken?: string;
20}
21
22export interface ListTableResult {
23 /** Rows decoded with the table schema (see {@link ReadTable}). */
24 rows: Record<string, unknown>[];
25 /** Pass to the next `list` call; `undefined` on the last page. */
26 nextPageToken: string | undefined;
27}
28
29/** Read-only client for one BigQuery table. */
30export interface ReadTableClient {
31 /**
32 * One page of table rows (`tabledata.list`), including rows still in the
33 * streaming buffer.
34 */
35 list(
36 options?: ListTableOptions,
37 ): Effect.Effect<
38 ListTableResult,
39 bigquery.GetTablesError | bigquery.ListTabledataError,
40 RuntimeContext
41 >;
42 /**
43 * Run a GoogleSQL query and return every result row. Unqualified table
44 * names resolve against the bound table's dataset; `params` become named
45 * parameters (`@name`) typed `STRING`, `INT64`/`FLOAT64`, or `BOOL`.
46 */
47 query(
48 sql: string,
49 params?: Record<string, string | number | boolean>,
50 ): Effect.Effect<
51 Record<string, unknown>[],
52 | bigquery.QueryJobsError
53 | bigquery.GetQueryResultsJobsError
54 | QueryNotComplete,
55 RuntimeContext
56 >;
57}
58
59/**
60 * Read access to a BigQuery {@link Table}: `list` rows and run `query`
61 * jobs. Grants `roles/bigquery.dataViewer` on the table and
62 * `roles/bigquery.jobUser` on the project (job creation is project-only).
63 *
64 * Rows are decoded with the schema: `INTEGER` → `number` (`bigint` beyond
65 * the safe range), `FLOAT` → `number`, `BOOLEAN` → `boolean`, `TIMESTAMP` →
66 * `Date`, `BYTES` → `Uint8Array`, `JSON` → parsed value, `RECORD` → object,
67 * `REPEATED` → array; `NUMERIC`, `DATE`, `DATETIME`, `TIME`, and `GEOGRAPHY`
68 * stay strings.
69 *
70 * ### Reading rows
71 * **Example:** Page through a table
72 * ```typescript
73 * const events = yield* GCP.BigQuery.ReadTable(table);
74 * const { rows, nextPageToken } = yield* events.list({ maxResults: 100 });
75 * // …provided with Effect.provide(GCP.BigQuery.ReadTableHttp)
76 * ```
77 *
78 * ### Querying
79 * **Example:** Parameterized query
80 * ```typescript
81 * const events = yield* GCP.BigQuery.ReadTable(table);
82 * const tableId = yield* table.tableId;
83 * return Effect.gen(function* () {
84 * const rows = yield* events.query(
85 * `SELECT id, score FROM \`${yield* tableId}\` WHERE score > @min`,
86 * { min: 10 },
87 * );
88 * });
89 * ```
90 *
91 * @binding
92 * @category BigQuery
93 */
94export interface ReadTable extends Binding.Service<
95 ReadTable,
96 "GCP.BigQuery.ReadTable",
97 (table: Table) => Effect.Effect<ReadTableClient>
98> {}
99
100export const ReadTable = Binding.Service<ReadTable>("GCP.BigQuery.ReadTable");