Skip to content

Commit 87c448e

Browse files
dosachadosacha
andauthored
feat: support array column type option (#1653)
Co-authored-by: dosacha <dosacha@dosachaui-MacBookAir.local>
1 parent 7d994e3 commit 87c448e

4 files changed

Lines changed: 142 additions & 22 deletions

File tree

docs/src/migrations/columns.md

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,49 @@ The `createTable` and `addColumns` methods both take a `columns` argument that s
66
It is an object (key/value) where each key is the name of the column,
77
and the value is another object that defines the options for the column.
88

9-
| Option | Type | Description |
10-
| ----------------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------- |
11-
| `type` | `string` | Data type (use normal postgres types) |
12-
| `collation` | `string` | Collation of data type |
13-
| `unique` | `boolean` | Set to true to add a unique constraint on this column |
14-
| `primaryKey` | `boolean` | Set to true to make this column the primary key |
15-
| `notNull` | `boolean` | Set to true to make this column not null |
16-
| `default` | `string` | Adds DEFAULT clause for column. Accepts null, a literal value, or a `pgm.func()` expression. |
17-
| `check` | `string` | SQL for a check constraint for this column |
18-
| `references` | [Name](/migrations/#type) or `string` | A table name that this column is a foreign key to |
19-
| `referencesConstraintName` | `string` | Name of the created constraint |
20-
| `referencesConstraintComment` | `string` | Comment on the created constraint |
21-
| `onDelete` | `string` | Adds ON DELETE constraint for a reference column |
22-
| `onUpdate` | `string` | Adds ON UPDATE constraint for a reference column |
23-
| `match` | `string` | `FULL` or `SIMPLE` |
24-
| `deferrable` | `boolean` | Flag for deferrable column constraint |
25-
| `deferred` | `boolean` | Flag for initially deferred deferrable column constraint |
26-
| `comment` | `string` | Adds comment on column |
27-
| `expressionGenerated` | `string` | Expression to compute column value |
28-
| `sequenceGenerated` | `object` | Creates identity column see [sequence options section](sequences.md#sequence-options) |
29-
| `precedence` | `string` | `ALWAYS` or `BY DEFAULT` |
9+
| Option | Type | Description |
10+
| ----------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------- |
11+
| `type` | `string` | Data type (use normal postgres types) |
12+
| `array` | `boolean` or `number` | Defines the column as a PostgreSQL array type. Use `true` for `ARRAY` or a number for `ARRAY[n]`. |
13+
| `collation` | `string` | Collation of data type |
14+
| `unique` | `boolean` | Set to true to add a unique constraint on this column |
15+
| `primaryKey` | `boolean` | Set to true to make this column the primary key |
16+
| `notNull` | `boolean` | Set to true to make this column not null |
17+
| `default` | `string` | Adds DEFAULT clause for column. Accepts null, a literal value, or a `pgm.func()` expression. |
18+
| `check` | `string` | SQL for a check constraint for this column |
19+
| `references` | [Name](/migrations/#type) or `string` | A table name that this column is a foreign key to |
20+
| `referencesConstraintName` | `string` | Name of the created constraint |
21+
| `referencesConstraintComment` | `string` | Comment on the created constraint |
22+
| `onDelete` | `string` | Adds ON DELETE constraint for a reference column |
23+
| `onUpdate` | `string` | Adds ON UPDATE constraint for a reference column |
24+
| `match` | `string` | `FULL` or `SIMPLE` |
25+
| `deferrable` | `boolean` | Flag for deferrable column constraint |
26+
| `deferred` | `boolean` | Flag for initially deferred deferrable column constraint |
27+
| `comment` | `string` | Adds comment on column |
28+
| `expressionGenerated` | `string` | Expression to compute column value |
29+
| `sequenceGenerated` | `object` | Creates identity column see [sequence options section](sequences.md#sequence-options) |
30+
| `precedence` | `string` | `ALWAYS` or `BY DEFAULT` |
3031

3132
## Data types & Convenience Shorthand
3233

3334
Data type strings will be passed through directly to postgres, so write types as you would if you were writing the
3435
queries by hand.
3536

37+
**PostgreSQL array columns can be defined with the `array` option:**
38+
39+
```ts
40+
pgm.addColumns('myTable', {
41+
tags: { type: PgType.TEXT, array: true },
42+
payByQuarter: { type: PgType.INTEGER, array: 4 },
43+
});
44+
```
45+
46+
String-based PostgreSQL array type declarations continue to work as before:
47+
48+
```ts
49+
pgm.addColumns('myTable', { tags: { type: 'text[]' } });
50+
```
51+
3652
**There are some aliases on types to make things more foolproof:** _(int, string, float, double, datetime, bool)_
3753

3854
**There is a shorthand to pass only the type instead of an option object:**

src/operations/tables/shared.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ export type SequenceGeneratedOptions = {
3535
export interface ColumnDefinition extends Partial<ReferencesOptions> {
3636
type: string;
3737

38+
array?: boolean | number;
39+
3840
collation?: string;
3941

4042
unique?: boolean;
@@ -219,6 +221,7 @@ export function parseColumns(
219221
columns: Object.entries(columnsWithOptions).map(([columnName, options]) => {
220222
const {
221223
type,
224+
array,
222225
collation,
223226
default: defaultValue,
224227
unique,
@@ -302,7 +305,14 @@ export function parseColumns(
302305
const constraintsStr =
303306
constraints.length > 0 ? ` ${constraints.join(' ')}` : '';
304307

305-
const sType = typeof type === 'object' ? mOptions.literal(type) : type;
308+
const baseType = typeof type === 'object' ? mOptions.literal(type) : type;
309+
310+
const sType =
311+
array === true
312+
? `${baseType} ARRAY`
313+
: typeof array === 'number'
314+
? `${baseType} ARRAY[${array}]`
315+
: baseType;
306316

307317
return `${mOptions.literal(columnName)} ${sType}${constraintsStr}`;
308318
}),

test/operations/columns/addColumns.spec.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,77 @@ describe('operations', () => {
6464
);
6565
});
6666

67+
it('should support unbounded array column type option', () => {
68+
const statement = addColumnsFn('transactions', {
69+
tags: {
70+
type: PgType.TEXT,
71+
array: true,
72+
},
73+
});
74+
75+
expect(statement).toBeTypeOf('string');
76+
expect(statement).toBe(`ALTER TABLE "transactions"
77+
ADD "tags" text ARRAY;`);
78+
});
79+
80+
it('should pass numeric array dimensions through to SQL', () => {
81+
const arrayDimension = 17;
82+
83+
const statement = addColumnsFn('transactions', {
84+
scores: {
85+
type: PgType.INTEGER,
86+
array: arrayDimension,
87+
},
88+
});
89+
90+
expect(statement).toBeTypeOf('string');
91+
expect(statement).toBe(`ALTER TABLE "transactions"
92+
ADD "scores" integer ARRAY[${arrayDimension}];`);
93+
});
94+
95+
it('should support array option for different column types', () => {
96+
const statement = addColumnsFn('transactions', {
97+
ids: {
98+
type: PgType.UUID,
99+
array: true,
100+
},
101+
aliases: {
102+
type: 'varchar(30)',
103+
array: true,
104+
},
105+
});
106+
107+
expect(statement).toBeTypeOf('string');
108+
expect(statement).toBe(`ALTER TABLE "transactions"
109+
ADD "ids" uuid ARRAY,
110+
ADD "aliases" varchar(30) ARRAY;`);
111+
});
112+
113+
it('should keep string-based array type declarations unchanged', () => {
114+
const statement = addColumnsFn('transactions', {
115+
tags: {
116+
type: `${PgType.TEXT}[]`,
117+
},
118+
});
119+
120+
expect(statement).toBeTypeOf('string');
121+
expect(statement).toBe(`ALTER TABLE "transactions"
122+
ADD "tags" text[];`);
123+
});
124+
125+
it('should ignore false array option', () => {
126+
const statement = addColumnsFn('transactions', {
127+
description: {
128+
type: PgType.TEXT,
129+
array: false,
130+
},
131+
});
132+
133+
expect(statement).toBeTypeOf('string');
134+
expect(statement).toBe(`ALTER TABLE "transactions"
135+
ADD "description" text;`);
136+
});
137+
67138
describe('reverse', () => {
68139
it('should contain a reverse function', () => {
69140
expect(addColumnsFn.reverse).toBeTypeOf('function');

test/operations/tables/createTable.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,29 @@ COMMENT ON CONSTRAINT "fk_col_b" ON "my_table_name" IS $pga$fk b comment$pga$;`,
728728
}
729729
);
730730

731+
it('should support array column type options', () => {
732+
const arrayDimension = 8;
733+
734+
const statement = createTableFn('arrays', {
735+
tags: {
736+
type: PgType.TEXT,
737+
array: true,
738+
},
739+
scores: {
740+
type: PgType.INTEGER,
741+
array: arrayDimension,
742+
},
743+
});
744+
745+
expect(statement).toBeTypeOf('string');
746+
expect(statement).toBe(
747+
`CREATE TABLE "arrays" (
748+
"tags" text ARRAY,
749+
"scores" integer ARRAY[${arrayDimension}]
750+
);`
751+
);
752+
});
753+
731754
describe('reverse', () => {
732755
it('should contain a reverse function', () => {
733756
expect(createTableFn.reverse).toBeTypeOf('function');

0 commit comments

Comments
 (0)