| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967 |
- import { afterAll, beforeAll, describe, expect, test } from 'vitest'
- import pgMeta from '../src/index'
- import { rawSql } from '../src/pg-format'
- import { cleanupRoot, createTestDatabase } from './db/utils'
- beforeAll(async () => {
- // Any global setup if needed
- })
- afterAll(async () => {
- await cleanupRoot()
- })
- const withTestDatabase = (
- name: string,
- fn: (db: Awaited<ReturnType<typeof createTestDatabase>>) => Promise<void>
- ) => {
- test(name, async () => {
- const db = await createTestDatabase()
- try {
- await fn(db)
- } finally {
- await db.cleanup()
- }
- })
- }
- withTestDatabase('list columns', async ({ executeQuery }) => {
- const { sql, zod } = await pgMeta.columns.list()
- const res = zod.parse(await executeQuery(sql))
- const userIdColumn = res.find(({ name }) => name === 'user-id')
- expect(userIdColumn).toMatchInlineSnapshot(
- {
- id: expect.stringMatching(/^\d+\.3$/),
- table_id: expect.any(Number),
- },
- `
- {
- "check": null,
- "comment": null,
- "data_type": "bigint",
- "default_value": null,
- "enums": [],
- "format": "int8",
- "format_schema": "pg_catalog",
- "id": StringMatching /\\^\\\\d\\+\\\\\\.3\\$/,
- "identity_generation": null,
- "is_generated": false,
- "is_identity": false,
- "is_nullable": false,
- "is_unique": false,
- "is_updatable": true,
- "name": "user-id",
- "ordinal_position": 3,
- "schema": "public",
- "table": "todos",
- "table_id": Any<Number>,
- }
- `
- )
- })
- withTestDatabase('list columns from a single table', async ({ executeQuery }) => {
- // Create test table
- await executeQuery('CREATE TABLE t (c1 text, c2 text)')
- // Get the table ID directly
- const tableId = Number(
- (
- await executeQuery(
- "SELECT oid FROM pg_class WHERE relname = 't' AND relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'public')"
- )
- )[0].oid
- )
- const testTable = { id: tableId, name: 't', schema: 'public' }
- // List columns
- const { sql, zod } = await pgMeta.columns.list({ tableId: testTable.id })
- const res = zod.parse(await executeQuery(sql))
- expect(res).toMatchInlineSnapshot(
- [
- {
- id: expect.stringMatching(/^\d+\.\d+$/),
- table_id: expect.any(Number),
- },
- {
- id: expect.stringMatching(/^\d+\.\d+$/),
- table_id: expect.any(Number),
- },
- ],
- `
- [
- {
- "check": null,
- "comment": null,
- "data_type": "text",
- "default_value": null,
- "enums": [],
- "format": "text",
- "format_schema": "pg_catalog",
- "id": StringMatching /\\^\\\\d\\+\\\\\\.\\\\d\\+\\$/,
- "identity_generation": null,
- "is_generated": false,
- "is_identity": false,
- "is_nullable": true,
- "is_unique": false,
- "is_updatable": true,
- "name": "c1",
- "ordinal_position": 1,
- "schema": "public",
- "table": "t",
- "table_id": Any<Number>,
- },
- {
- "check": null,
- "comment": null,
- "data_type": "text",
- "default_value": null,
- "enums": [],
- "format": "text",
- "format_schema": "pg_catalog",
- "id": StringMatching /\\^\\\\d\\+\\\\\\.\\\\d\\+\\$/,
- "identity_generation": null,
- "is_generated": false,
- "is_identity": false,
- "is_nullable": true,
- "is_unique": false,
- "is_updatable": true,
- "name": "c2",
- "ordinal_position": 2,
- "schema": "public",
- "table": "t",
- "table_id": Any<Number>,
- },
- ]
- `
- )
- })
- withTestDatabase('list columns with included schemas', async ({ executeQuery }) => {
- const { sql, zod } = await pgMeta.columns.list({
- includedSchemas: ['public'],
- })
- const res = zod.parse(await executeQuery(sql))
- expect(res.length).toBeGreaterThan(0)
- res.forEach((column) => {
- expect(column.schema).toBe('public')
- })
- })
- withTestDatabase('list columns with excluded schemas', async ({ executeQuery }) => {
- const { sql, zod } = await pgMeta.columns.list({
- excludedSchemas: ['public'],
- })
- const res = zod.parse(await executeQuery(sql))
- res.forEach((column) => {
- expect(column.schema).not.toBe('public')
- })
- })
- withTestDatabase(
- 'list columns with excluded schemas and include System Schemas',
- async ({ executeQuery }) => {
- const { sql, zod } = await pgMeta.columns.list({
- excludedSchemas: ['public'],
- includeSystemSchemas: true,
- })
- const res = zod.parse(await executeQuery(sql))
- expect(res.length).toBeGreaterThan(0)
- res.forEach((column) => {
- expect(column.schema).not.toBe('public')
- })
- }
- )
- withTestDatabase('retrieve, create, update, delete column', async ({ executeQuery }) => {
- // Create test table using pure SQL
- await executeQuery('CREATE TABLE t ()')
- // Create column
- const { sql: createColumnSql } = await pgMeta.columns.create({
- schema: 'public',
- table: 't',
- name: 'c',
- type: { name: 'int2' },
- default_value: 42,
- comment: 'foo',
- })
- await executeQuery(createColumnSql)
- // Retrieve and verify created column
- const { sql: retrieveSql, zod: retrieveZod } = await pgMeta.columns.retrieve({
- schema: 'public',
- table: 't',
- name: 'c',
- })
- let column = retrieveZod.parse((await executeQuery(retrieveSql))[0])
- expect(column).toMatchInlineSnapshot(
- {
- id: expect.stringMatching(/^\d+\.1$/),
- table_id: expect.any(Number),
- },
- `
- {
- "check": null,
- "comment": "foo",
- "data_type": "smallint",
- "default_value": "42",
- "enums": [],
- "format": "int2",
- "format_schema": "pg_catalog",
- "id": StringMatching /\\^\\\\d\\+\\\\\\.1\\$/,
- "identity_generation": null,
- "is_generated": false,
- "is_identity": false,
- "is_nullable": true,
- "is_unique": false,
- "is_updatable": true,
- "name": "c",
- "ordinal_position": 1,
- "schema": "public",
- "table": "t",
- "table_id": Any<Number>,
- }
- `
- )
- // Update column
- const { sql: updateSql } = await pgMeta.columns.update(column!, {
- name: 'c1',
- type: { name: 'int4' },
- drop_default: true,
- is_identity: true,
- identity_generation: 'ALWAYS',
- is_nullable: false,
- comment: 'bar',
- })
- await executeQuery(updateSql)
- // Verify updated column
- const { sql: retrieveUpdatedSql, zod: retrieveUpdatedZod } = await pgMeta.columns.retrieve({
- schema: 'public',
- table: 't',
- name: 'c1',
- })
- column = retrieveUpdatedZod.parse((await executeQuery(retrieveUpdatedSql))[0])
- expect(column).toMatchInlineSnapshot(
- {
- id: expect.stringMatching(/^\d+\.1$/),
- table_id: expect.any(Number),
- },
- `
- {
- "check": null,
- "comment": "bar",
- "data_type": "integer",
- "default_value": null,
- "enums": [],
- "format": "int4",
- "format_schema": "pg_catalog",
- "id": StringMatching /\\^\\\\d\\+\\\\\\.1\\$/,
- "identity_generation": "ALWAYS",
- "is_generated": false,
- "is_identity": true,
- "is_nullable": false,
- "is_unique": false,
- "is_updatable": true,
- "name": "c1",
- "ordinal_position": 1,
- "schema": "public",
- "table": "t",
- "table_id": Any<Number>,
- }
- `
- )
- // Remove column
- const { sql: removeSql } = await pgMeta.columns.remove(column!)
- await executeQuery(removeSql)
- // Verify column was removed
- const { sql: retrieveRemovedSql, zod: retrieveRemovedZod } = await pgMeta.columns.retrieve({
- schema: 'public',
- table: 't',
- name: 'c1',
- })
- const removedColumn = retrieveRemovedZod.parse((await executeQuery(retrieveRemovedSql))[0])
- expect(removedColumn).toBeUndefined()
- })
- withTestDatabase('enum column with quoted name', async ({ executeQuery }) => {
- await executeQuery('CREATE TYPE "T" AS ENUM (\'v\'); CREATE TABLE t ( c "T" );')
- const { sql, zod } = await pgMeta.columns.list()
- const res = zod.parse(await executeQuery(sql))
- expect(res.find(({ table }) => table === 't')).toMatchInlineSnapshot(
- {
- id: expect.stringMatching(/^\d+\.1$/),
- table_id: expect.any(Number),
- },
- `
- {
- "check": null,
- "comment": null,
- "data_type": "USER-DEFINED",
- "default_value": null,
- "enums": [
- "v",
- ],
- "format": "T",
- "format_schema": "public",
- "id": StringMatching /\\^\\\\d\\+\\\\\\.1\\$/,
- "identity_generation": null,
- "is_generated": false,
- "is_identity": false,
- "is_nullable": true,
- "is_unique": false,
- "is_updatable": true,
- "name": "c",
- "ordinal_position": 1,
- "schema": "public",
- "table": "t",
- "table_id": Any<Number>,
- }
- `
- )
- })
- withTestDatabase('primary key column', async ({ executeQuery }) => {
- // Create test table using pure SQL
- await executeQuery('CREATE TABLE t ()')
- // Create column with primary key
- const { sql: createColumnSql } = await pgMeta.columns.create({
- schema: 'public',
- table: 't',
- name: 'c',
- type: { name: 'int2' },
- is_primary_key: true,
- })
- await executeQuery(createColumnSql)
- // Verify primary key using pure SQL
- const primaryKeyResult = await executeQuery(`
- SELECT a.attname
- FROM pg_index i
- JOIN pg_attribute a ON a.attrelid = i.indrelid
- AND a.attnum = ANY(i.indkey)
- WHERE i.indrelid = 't'::regclass
- AND i.indisprimary;
- `)
- expect(primaryKeyResult).toMatchInlineSnapshot(`
- [
- {
- "attname": "c",
- },
- ]
- `)
- })
- withTestDatabase('unique column', async ({ executeQuery }) => {
- // Create test table using pure SQL
- await executeQuery('CREATE TABLE t ()')
- // Create column with unique constraint
- const { sql: createColumnSql } = await pgMeta.columns.create({
- schema: 'public',
- table: 't',
- name: 'c',
- type: { name: 'int2' },
- is_unique: true,
- })
- await executeQuery(createColumnSql)
- // Verify unique constraint using pure SQL
- const uniqueResult = await executeQuery(`
- SELECT a.attname
- FROM pg_index i
- JOIN pg_constraint c ON c.conindid = i.indexrelid
- JOIN pg_attribute a ON a.attrelid = i.indrelid
- AND a.attnum = ANY(i.indkey)
- WHERE i.indrelid = 't'::regclass
- AND i.indisunique;
- `)
- expect(uniqueResult).toMatchInlineSnapshot(`
- [
- {
- "attname": "c",
- },
- ]
- `)
- })
- describe('array column', async () => {
- const db = await createTestDatabase()
- await db.executeQuery('CREATE TABLE t ()')
- afterAll(async () => {
- await db.cleanup()
- })
- test.concurrent.for([
- // numerical types
- { type: 'int2', etype: '_int2' },
- { type: 'int4', etype: '_int4' },
- { type: 'int8', etype: '_int8' },
- { type: 'float4', etype: '_float4' },
- { type: 'float8', etype: '_float8' },
- { type: 'numeric', etype: '_numeric' },
- // json types
- { type: 'json', etype: '_json' },
- { type: 'jsonb', etype: '_jsonb' },
- // text types
- { type: 'text', etype: '_text' },
- { type: 'varchar', etype: '_varchar' },
- // datetime types
- { type: 'timestamp', etype: '_timestamp' },
- { type: 'timestamptz', etype: '_timestamptz' },
- { type: 'date', etype: '_date' },
- { type: 'time', etype: '_time' },
- { type: 'timetz', etype: '_timetz' },
- // other types
- { type: 'uuid', etype: '_uuid' },
- { type: 'bool', etype: '_bool' },
- { type: 'bytea', etype: '_bytea' },
- ])('$type[] -> $etype', async (c, { expect, task }) => {
- const id = { schema: 'public', table: 't', name: `c${task.id}` }
- // Create column with default value
- const { sql } = pgMeta.columns.create({
- ...id,
- type: { name: c.type, isArray: true },
- default_value: null,
- })
- await db.executeQuery(sql)
- // Retrieve and verify the created column
- const expected = pgMeta.columns.retrieve(id)
- const result = await db.executeQuery(expected.sql)
- const column = expected.zod.parse(result[0])
- expect(column).toStrictEqual({
- ...id,
- data_type: 'ARRAY',
- default_value: null,
- format: c.etype,
- format_schema: 'pg_catalog',
- id: expect.stringMatching(/^\d+\.\d+$/),
- ordinal_position: expect.any(Number),
- table_id: expect.any(Number),
- check: null,
- comment: null,
- enums: [],
- identity_generation: null,
- is_generated: false,
- is_identity: false,
- is_nullable: true,
- is_unique: false,
- is_updatable: true,
- })
- })
- })
- describe('column with default value', async () => {
- const db = await createTestDatabase()
- await db.executeQuery('CREATE TABLE t ()')
- afterAll(async () => {
- await db.cleanup()
- })
- test.concurrent.for([
- // numerical types
- { type: 'int2', value: 0, etype: 'smallint', evalue: '0' },
- { type: 'int4', value: 1, etype: 'integer', evalue: '1' },
- { type: 'int8', value: -1, etype: 'bigint', evalue: `'-1'::integer` },
- { type: 'float4', value: 0.1, etype: 'real', evalue: '0.1' },
- { type: 'float8', value: -0.1, etype: 'double precision', evalue: `'-0.1'::numeric` },
- { type: 'numeric', value: 1e2, etype: 'numeric', evalue: '100' },
- // json types
- {
- type: 'json',
- value: { a: 0, b: '1', c: true },
- etype: 'json',
- evalue: `'{\"a\": 0, \"b\": \"1\", \"c\": true}'::jsonb`,
- },
- // json array must be stringified, otherwise it will be converted to pg array literal
- { type: 'jsonb', value: JSON.stringify([null]), etype: 'jsonb', evalue: `'[null]'::jsonb` },
- // text types
- { type: 'text', value: `quote's`, etype: 'text', evalue: `'quote''s'::text` },
- { type: 'varchar', value: '\n', etype: 'character varying', evalue: `'\n'::character varying` },
- // datetime types
- {
- type: 'timestamp',
- value: `now() - INTERVAL '1 day'`,
- etype: 'timestamp without time zone',
- evalue: `(now() - '1 day'::interval)`,
- exp: true,
- },
- {
- type: 'timestamptz',
- value: 'NOW()',
- etype: 'timestamp with time zone',
- evalue: 'now()',
- exp: true,
- },
- { type: 'date', value: '2025-05-09', etype: 'date', evalue: `'2025-05-09'::date` },
- {
- type: 'time',
- value: '11:22:33',
- etype: 'time without time zone',
- evalue: `'11:22:33'::time without time zone`,
- },
- {
- type: 'timetz',
- value: '11:22:33+0800',
- etype: 'time with time zone',
- evalue: `'11:22:33+08'::time with time zone`,
- },
- // other types
- {
- type: 'uuid',
- value: 'gen_random_uuid()',
- etype: 'uuid',
- evalue: 'gen_random_uuid()',
- exp: true,
- },
- { type: 'bool', value: true, etype: 'boolean', evalue: 'true' },
- // https://www.postgresql.org/docs/current/datatype-binary.html#DATATYPE-BINARY-BYTEA-ESCAPE-FORMAT
- { type: 'bytea', value: `\\000`, etype: 'bytea', evalue: `'\\x00'::bytea` },
- ])('$type -> $value', async (c, { expect, task }) => {
- const id = { schema: 'public', table: 't', name: `c${task.id}` }
- // Create column with default value
- const { sql } = pgMeta.columns.create(
- c.exp
- ? {
- ...id,
- type: { name: c.type },
- default_value_format: 'expression',
- default_value: rawSql(String(c.value)),
- }
- : { ...id, type: { name: c.type }, default_value_format: 'literal', default_value: c.value }
- )
- await db.executeQuery(sql)
- // Retrieve and verify the created column
- const expected = pgMeta.columns.retrieve(id)
- const result = await db.executeQuery(expected.sql)
- const column = expected.zod.parse(result[0])
- expect(column).toStrictEqual({
- ...id,
- data_type: c.etype,
- default_value: c.evalue,
- format: c.type,
- format_schema: 'pg_catalog',
- id: expect.stringMatching(/^\d+\.\d+$/),
- ordinal_position: expect.any(Number),
- table_id: expect.any(Number),
- check: null,
- comment: null,
- enums: [],
- identity_generation: null,
- is_generated: false,
- is_identity: false,
- is_nullable: true,
- is_unique: false,
- is_updatable: true,
- })
- })
- })
- // https://github.com/supabase/supabase/issues/3553
- withTestDatabase('alter column to type with uppercase', async ({ executeQuery }) => {
- // Setup: Create table and type
- await executeQuery('CREATE TABLE t ()')
- await executeQuery('CREATE TYPE "T" AS ENUM ()')
- // Create and then update column
- const { sql: createSql } = await pgMeta.columns.create({
- schema: 'public',
- table: 't',
- name: 'c',
- type: { name: 'text' },
- is_unique: false,
- })
- await executeQuery(createSql)
- // Get column ID
- const { sql: retrieveSql, zod: retrieveZod } = await pgMeta.columns.retrieve({
- schema: 'public',
- table: 't',
- name: 'c',
- })
- const column = retrieveZod.parse((await executeQuery(retrieveSql))[0])
- // Update column
- const { sql: updateSql } = await pgMeta.columns.update(column!, { type: { name: 'T' } })
- await executeQuery(updateSql)
- // Verify updated column
- const { sql: verifySQL, zod: verifyZod } = await pgMeta.columns.retrieve({
- schema: 'public',
- table: 't',
- name: 'c',
- })
- const updatedColumn = verifyZod.parse((await executeQuery(verifySQL))[0])
- expect(updatedColumn).toMatchInlineSnapshot(
- {
- id: expect.stringMatching(/^\d+\.1$/),
- table_id: expect.any(Number),
- },
- `
- {
- "check": null,
- "comment": null,
- "data_type": "USER-DEFINED",
- "default_value": null,
- "enums": [],
- "format": "T",
- "format_schema": "public",
- "id": StringMatching /\\^\\\\d\\+\\\\\\.1\\$/,
- "identity_generation": null,
- "is_generated": false,
- "is_identity": false,
- "is_nullable": true,
- "is_unique": false,
- "is_updatable": true,
- "name": "c",
- "ordinal_position": 1,
- "schema": "public",
- "table": "t",
- "table_id": Any<Number>,
- }
- `
- )
- })
- withTestDatabase('enums are populated in enum array columns', async ({ executeQuery }) => {
- // Setup: Create type and table
- await executeQuery(`CREATE TYPE test_enum AS ENUM ('a')`)
- await executeQuery('CREATE TABLE t ()')
- // Create column
- const { sql: createSql } = pgMeta.columns.create({
- schema: 'public',
- table: 't',
- name: 'c',
- type: { name: 'test_enum', isArray: true },
- })
- await executeQuery(createSql)
- // Verify created column
- const { sql: verifySql, zod: verifyZod } = pgMeta.columns.retrieve({
- schema: 'public',
- table: 't',
- name: 'c',
- })
- const column = verifyZod.parse((await executeQuery(verifySql))[0])
- expect(column).toMatchInlineSnapshot(
- {
- id: expect.stringMatching(/^\d+\.1$/),
- table_id: expect.any(Number),
- },
- `
- {
- "check": null,
- "comment": null,
- "data_type": "ARRAY",
- "default_value": null,
- "enums": [
- "a",
- ],
- "format": "_test_enum",
- "format_schema": "public",
- "id": StringMatching /\\^\\\\d\\+\\\\\\.1\\$/,
- "identity_generation": null,
- "is_generated": false,
- "is_identity": false,
- "is_nullable": true,
- "is_unique": false,
- "is_updatable": true,
- "name": "c",
- "ordinal_position": 1,
- "schema": "public",
- "table": "t",
- "table_id": Any<Number>,
- }
- `
- )
- })
- withTestDatabase('drop with cascade', async ({ executeQuery }) => {
- // Setup table with generated column
- await executeQuery(`
- create table public.t (
- id int8 primary key,
- t_id int8 generated always as (id) stored
- );
- `)
- // Retrieve column
- const { sql: retrieveSql, zod: retrieveZod } = pgMeta.columns.retrieve({
- schema: 'public',
- table: 't',
- name: 'id',
- })
- const column = retrieveZod.parse((await executeQuery(retrieveSql))[0])
- // Remove column with cascade
- const { sql: removeSql } = pgMeta.columns.remove(column!, { cascade: true })
- await executeQuery(removeSql)
- // Verify original column was removed
- const { sql: verifyIdSql, zod: verifyIdZod } = pgMeta.columns.retrieve({
- schema: 'public',
- table: 't',
- name: 'id',
- })
- const removedColumn = verifyIdZod.parse((await executeQuery(verifyIdSql))[0])
- expect(removedColumn).toBeUndefined()
- // Verify dependent column was also removed
- const { sql: verifyTIdSql, zod: verifyTIdZod } = pgMeta.columns.retrieve({
- schema: 'public',
- table: 't',
- name: 't_id',
- })
- const dependentColumn = verifyTIdZod.parse((await executeQuery(verifyTIdSql))[0])
- expect(dependentColumn).toBeUndefined()
- })
- withTestDatabase('column with multiple checks', async ({ executeQuery }) => {
- // Setup table with multiple check constraints
- await executeQuery('create table t(c int8 check (c != 0) check (c != -1))')
- // List columns
- const { sql, zod } = pgMeta.columns.list()
- const res = zod.parse(await executeQuery(sql))
- const columns = res
- .filter((c) => c.schema === 'public' && c.table === 't')
- .map(({ id, table_id, ...c }) => c)
- expect(columns).toMatchInlineSnapshot(`
- [
- {
- "check": "c <> 0",
- "comment": null,
- "data_type": "bigint",
- "default_value": null,
- "enums": [],
- "format": "int8",
- "format_schema": "pg_catalog",
- "identity_generation": null,
- "is_generated": false,
- "is_identity": false,
- "is_nullable": true,
- "is_unique": false,
- "is_updatable": true,
- "name": "c",
- "ordinal_position": 1,
- "schema": "public",
- "table": "t",
- },
- ]
- `)
- })
- withTestDatabase('column with multiple unique constraints', async ({ executeQuery }) => {
- // Setup table with multiple unique constraints
- await executeQuery(`create table t(c int8 unique); alter table t add unique (c);`)
- // List columns
- const { sql, zod } = await pgMeta.columns.list()
- const res = zod.parse(await executeQuery(sql))
- const columns = res
- .filter((c) => c.schema === 'public' && c.table === 't')
- .map(({ id, table_id, ...c }) => c)
- expect(columns).toMatchInlineSnapshot(`
- [
- {
- "check": null,
- "comment": null,
- "data_type": "bigint",
- "default_value": null,
- "enums": [],
- "format": "int8",
- "format_schema": "pg_catalog",
- "identity_generation": null,
- "is_generated": false,
- "is_identity": false,
- "is_nullable": true,
- "is_unique": true,
- "is_updatable": true,
- "name": "c",
- "ordinal_position": 1,
- "schema": "public",
- "table": "t",
- },
- ]
- `)
- })
- withTestDatabase('dropping column checks', async ({ executeQuery }) => {
- // Setup table with check constraint
- await executeQuery(`create table public.t(c int8 check (c != 0))`)
- // Retrieve column
- const { sql: retrieveSql, zod: retrieveZod } = await pgMeta.columns.retrieve({
- schema: 'public',
- table: 't',
- name: 'c',
- })
- const column = retrieveZod.parse((await executeQuery(retrieveSql))[0])
- // Update column to remove check
- const { sql: updateSql } = await pgMeta.columns.update(column!, { check: null })
- await executeQuery(updateSql)
- // Verify updated column
- const { sql: verifySql, zod: verifyZod } = await pgMeta.columns.retrieve({
- schema: 'public',
- table: 't',
- name: 'c',
- })
- const updatedColumn = verifyZod.parse((await executeQuery(verifySql))[0])
- expect(updatedColumn!.check).toMatchInlineSnapshot(`null`)
- })
- withTestDatabase('column with fully-qualified type', async ({ executeQuery }) => {
- // Setup: Create table and type in separate schema
- await executeQuery(`
- create table public.t();
- create schema s;
- create type s.my_type as enum ();
- `)
- // Create column with fully-qualified type
- const { sql: createColumnSql } = await pgMeta.columns.create({
- schema: 'public',
- table: 't',
- name: 'c',
- type: { schema: 's', name: 'my_type' },
- })
- await executeQuery(createColumnSql)
- // Retrieve and verify the created column
- const { sql: retrieveSql, zod: retrieveZod } = await pgMeta.columns.retrieve({
- schema: 'public',
- table: 't',
- name: 'c',
- })
- const column = retrieveZod.parse((await executeQuery(retrieveSql))[0])
- expect(column).toMatchInlineSnapshot(
- {
- id: expect.stringMatching(/^\d+\.1$/),
- table_id: expect.any(Number),
- },
- `
- {
- "check": null,
- "comment": null,
- "data_type": "USER-DEFINED",
- "default_value": null,
- "enums": [],
- "format": "my_type",
- "format_schema": "s",
- "id": StringMatching /\\^\\\\d\\+\\\\\\.1\\$/,
- "identity_generation": null,
- "is_generated": false,
- "is_identity": false,
- "is_nullable": true,
- "is_unique": false,
- "is_updatable": true,
- "name": "c",
- "ordinal_position": 1,
- "schema": "public",
- "table": "t",
- "table_id": Any<Number>,
- }
- `
- )
- })
- withTestDatabase('format_schema for built-in type', async ({ executeQuery }) => {
- await executeQuery(`create table t (c int4)`)
- const { sql, zod } = pgMeta.columns.retrieve({ schema: 'public', table: 't', name: 'c' })
- const column = zod.parse((await executeQuery(sql))[0])
- expect(column?.format).toBe('int4')
- expect(column?.format_schema).toBe('pg_catalog')
- })
- withTestDatabase('format_schema for public-schema enum', async ({ executeQuery }) => {
- await executeQuery(`create type public_enum as enum ('a'); create table t (c public_enum);`)
- const { sql, zod } = pgMeta.columns.retrieve({ schema: 'public', table: 't', name: 'c' })
- const column = zod.parse((await executeQuery(sql))[0])
- expect(column?.format).toBe('public_enum')
- expect(column?.format_schema).toBe('public')
- })
- withTestDatabase('format_schema for non-public-schema enum', async ({ executeQuery }) => {
- await executeQuery(`
- create schema s;
- create type s.my_enum as enum ('a');
- create table t (c s.my_enum);
- `)
- const { sql, zod } = pgMeta.columns.retrieve({ schema: 'public', table: 't', name: 'c' })
- const column = zod.parse((await executeQuery(sql))[0])
- expect(column?.format).toBe('my_enum')
- expect(column?.format_schema).toBe('s')
- })
- withTestDatabase('format_schema for array of non-public-schema enum', async ({ executeQuery }) => {
- await executeQuery(`
- create schema s;
- create type s.my_enum as enum ('a');
- create table t (c s.my_enum[]);
- `)
- const { sql, zod } = pgMeta.columns.retrieve({ schema: 'public', table: 't', name: 'c' })
- const column = zod.parse((await executeQuery(sql))[0])
- expect(column?.data_type).toBe('ARRAY')
- expect(column?.format).toBe('_my_enum')
- expect(column?.format_schema).toBe('s')
- })
- withTestDatabase(
- 'format_schema for domain over non-public-schema base type',
- async ({ executeQuery }) => {
- await executeQuery(`
- create schema s;
- create domain s.my_domain as int4;
- create table t (c s.my_domain);
- `)
- const { sql, zod } = pgMeta.columns.retrieve({ schema: 'public', table: 't', name: 'c' })
- const column = zod.parse((await executeQuery(sql))[0])
- // Domain's `format` resolves to the base type, so `format_schema` follows the base type.
- expect(column?.format).toBe('int4')
- expect(column?.format_schema).toBe('pg_catalog')
- }
- )
|