|
1 | 1 | import type { ActiveFilter } from '@conar/shared/filters' |
2 | 2 | import { type } from 'arktype' |
| 3 | +import { sql } from 'kysely' |
3 | 4 | import { createQuery } from '../query' |
4 | 5 | import { buildWhere } from './rows' |
5 | 6 |
|
6 | 7 | export const totalQuery = createQuery({ |
7 | | - type: type('string | number | bigint | undefined').pipe(v => v !== undefined ? Number(v) : undefined), |
| 8 | + type: type({ |
| 9 | + count: 'number', |
| 10 | + isEstimated: 'boolean', |
| 11 | + }), |
8 | 12 | query: ({ |
9 | 13 | schema, |
10 | 14 | table, |
11 | 15 | filters, |
12 | | - }: { schema: string, table: string, filters?: ActiveFilter[] }) => ({ |
| 16 | + exact, |
| 17 | + }: { |
| 18 | + schema: string |
| 19 | + table: string |
| 20 | + filters?: ActiveFilter[] |
| 21 | + exact?: boolean |
| 22 | + }) => ({ |
13 | 23 | postgres: async (db) => { |
| 24 | + if (!exact && !filters?.length) { |
| 25 | + const estimate = await db |
| 26 | + .withSchema('pg_catalog') |
| 27 | + .selectFrom('pg_catalog.pg_class') |
| 28 | + .innerJoin('pg_catalog.pg_namespace', 'pg_catalog.pg_namespace.oid', 'pg_catalog.pg_class.relnamespace') |
| 29 | + .select('pg_catalog.pg_class.reltuples as count') |
| 30 | + .where('pg_catalog.pg_namespace.nspname', '=', schema) |
| 31 | + .where('pg_catalog.pg_class.relname', '=', table) |
| 32 | + .executeTakeFirst() |
| 33 | + |
| 34 | + if (estimate && estimate.count !== null && estimate.count >= 0) { |
| 35 | + return { |
| 36 | + count: Math.round(estimate.count), |
| 37 | + isEstimated: true, |
| 38 | + } |
| 39 | + } |
| 40 | + } |
| 41 | + |
14 | 42 | const query = await db |
15 | 43 | .withSchema(schema) |
16 | 44 | .withTables<{ [table]: Record<string, unknown> }>() |
17 | 45 | .selectFrom(table) |
18 | 46 | .select(db.fn.countAll().as('total')) |
19 | 47 | .$if(filters !== undefined, qb => qb.where(eb => buildWhere(eb, filters!))) |
20 | | - .execute() |
| 48 | + .executeTakeFirst() |
21 | 49 |
|
22 | | - return query[0]?.total |
| 50 | + return { count: Number(query?.total ?? 0), isEstimated: false } |
23 | 51 | }, |
24 | 52 | mysql: async (db) => { |
| 53 | + if (!exact && !filters?.length) { |
| 54 | + const estimate = await db |
| 55 | + .withSchema('information_schema') |
| 56 | + .selectFrom('information_schema.TABLES') |
| 57 | + .select('TABLE_ROWS as count') |
| 58 | + .where('TABLE_SCHEMA', '=', schema) |
| 59 | + .where('TABLE_NAME', '=', table) |
| 60 | + .executeTakeFirst() |
| 61 | + |
| 62 | + if (estimate && estimate.count !== null && estimate.count >= 0) { |
| 63 | + return { count: estimate.count, isEstimated: true } |
| 64 | + } |
| 65 | + } |
| 66 | + |
25 | 67 | const query = await db |
26 | 68 | .withSchema(schema) |
27 | 69 | .withTables<{ [table]: Record<string, unknown> }>() |
28 | 70 | .selectFrom(table) |
29 | 71 | .select(db.fn.countAll().as('total')) |
30 | 72 | .$if(filters !== undefined, qb => qb.where(eb => buildWhere(eb, filters!))) |
31 | | - .execute() |
| 73 | + .executeTakeFirst() |
32 | 74 |
|
33 | | - return query[0]?.total |
| 75 | + return { count: Number(query?.total ?? 0), isEstimated: false } |
34 | 76 | }, |
| 77 | + |
35 | 78 | mssql: async (db) => { |
36 | 79 | const query = await db |
37 | 80 | .withSchema(schema) |
38 | 81 | .withTables<{ [table]: Record<string, unknown> }>() |
39 | 82 | .selectFrom(table) |
40 | 83 | .select(db.fn.countAll().as('total')) |
41 | 84 | .$if(filters !== undefined, qb => qb.where(eb => buildWhere(eb, filters!))) |
42 | | - .execute() |
| 85 | + .executeTakeFirst() |
43 | 86 |
|
44 | | - return query[0]?.total |
| 87 | + return { |
| 88 | + count: Number(query?.total ?? 0), |
| 89 | + isEstimated: false, |
| 90 | + } |
45 | 91 | }, |
| 92 | + |
46 | 93 | clickhouse: async (db) => { |
| 94 | + if (!exact && !filters?.length) { |
| 95 | + const estimate = await db |
| 96 | + .withSchema('system') |
| 97 | + .selectFrom('system.parts') |
| 98 | + .select(db.fn.sum(sql.ref('rows')).as('count')) |
| 99 | + .where('database', '=', schema) |
| 100 | + .where('table', '=', table) |
| 101 | + .where('active', '=', 1) |
| 102 | + .executeTakeFirst() |
| 103 | + |
| 104 | + if (estimate && Number(estimate.count) >= 0) { |
| 105 | + return { count: Number(estimate.count), isEstimated: true } |
| 106 | + } |
| 107 | + } |
| 108 | + |
47 | 109 | const query = await db |
48 | 110 | .withSchema(schema) |
49 | 111 | .withTables<{ [table]: Record<string, unknown> }>() |
50 | 112 | .selectFrom(table) |
51 | 113 | .select(db.fn.countAll().as('total')) |
52 | 114 | .$if(filters !== undefined, qb => qb.where(eb => buildWhere(eb, filters!))) |
53 | | - .execute() |
| 115 | + .executeTakeFirst() |
54 | 116 |
|
55 | | - return query[0]?.total |
| 117 | + return { count: Number(query?.total ?? 0), isEstimated: false } |
56 | 118 | }, |
57 | 119 | }), |
58 | 120 | }) |
0 commit comments