Skip to content
This repository was archived by the owner on Mar 18, 2022. It is now read-only.

Commit 19e2179

Browse files
hauaupleerock
authored andcommitted
feat: add set datatype support for MySQL/MariaDB (typeorm#4538)
Set possible values defined using existing enum column option. Sets are implemented as arrays. Closes: typeorm#2779
1 parent 5c311ed commit 19e2179

8 files changed

Lines changed: 129 additions & 2 deletions

File tree

docs/entities.md

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ or
294294
`bit`, `int`, `integer`, `tinyint`, `smallint`, `mediumint`, `bigint`, `float`, `double`,
295295
`double precision`, `dec`, `decimal`, `numeric`, `fixed`, `bool`, `boolean`, `date`, `datetime`,
296296
`timestamp`, `time`, `year`, `char`, `nchar`, `national char`, `varchar`, `nvarchar`, `national varchar`,
297-
`text`, `tinytext`, `mediumtext`, `blob`, `longtext`, `tinyblob`, `mediumblob`, `longblob`, `enum`,
297+
`text`, `tinytext`, `mediumtext`, `blob`, `longtext`, `tinyblob`, `mediumblob`, `longblob`, `enum`, `set`,
298298
`json`, `binary`, `varbinary`, `geometry`, `point`, `linestring`, `polygon`, `multipoint`, `multilinestring`,
299299
`multipolygon`, `geometrycollection`
300300

@@ -390,6 +390,52 @@ export class User {
390390
}
391391
```
392392

393+
### `set` column type
394+
395+
`set` column type is supported by `mariadb` and `mysql`. There are various possible column definitions:
396+
397+
Using typescript enums:
398+
```typescript
399+
export enum UserRole {
400+
ADMIN = "admin",
401+
EDITOR = "editor",
402+
GHOST = "ghost"
403+
}
404+
405+
@Entity()
406+
export class User {
407+
408+
@PrimaryGeneratedColumn()
409+
id: number;
410+
411+
@Column({
412+
type: "set",
413+
enum: UserRole,
414+
default: [UserRole.GHOST, UserRole.EDITOR]
415+
})
416+
roles: UserRole[]
417+
418+
}
419+
```
420+
421+
Using array with `set` values:
422+
```typescript
423+
export type UserRoleType = "admin" | "editor" | "ghost",
424+
425+
@Entity()
426+
export class User {
427+
428+
@PrimaryGeneratedColumn()
429+
id: number;
430+
431+
@Column({
432+
type: "set",
433+
enum: ["admin", "editor", "ghost"],
434+
default: ["ghost", "editor"]
435+
})
436+
roles: UserRoleType[]
437+
}
438+
```
393439

394440
### `simple-array` column type
395441

src/decorator/columns/Column.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ export function Column(type: "enum", options?: ColumnCommonOptions & ColumnEnumO
7070
*/
7171
export function Column(type: "simple-enum", options?: ColumnCommonOptions & ColumnEnumOptions): Function;
7272

73+
/**
74+
* Column decorator is used to mark a specific class property as a table column.
75+
* Only properties decorated with this decorator will be persisted to the database when entity be saved.
76+
*/
77+
export function Column(type: "set", options?: ColumnCommonOptions & ColumnEnumOptions): Function;
78+
7379
/**
7480
* Column decorator is used to mark a specific class property as a table column.
7581
* Only properties decorated with this decorator will be persisted to the database when entity be saved.

src/driver/mysql/MysqlDriver.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ export class MysqlDriver implements Driver {
120120
"longblob",
121121
"longtext",
122122
"enum",
123+
"set",
123124
"binary",
124125
"varbinary",
125126
// json data type
@@ -459,6 +460,9 @@ export class MysqlDriver implements Driver {
459460

460461
} else if (columnMetadata.type === "enum" || columnMetadata.type === "simple-enum") {
461462
return "" + value;
463+
464+
} else if (columnMetadata.type === "set") {
465+
return DateUtils.simpleArrayToString(value);
462466
}
463467

464468
return value;
@@ -498,6 +502,8 @@ export class MysqlDriver implements Driver {
498502
&& columnMetadata.enum.indexOf(parseInt(value)) >= 0) {
499503
// convert to number if that exists in possible enum options
500504
value = parseInt(value);
505+
} else if (columnMetadata.type === "set") {
506+
value = DateUtils.stringToSimpleArray(value);
501507
}
502508

503509
if (columnMetadata.transformer)
@@ -564,6 +570,10 @@ export class MysqlDriver implements Driver {
564570
return `'${defaultValue}'`;
565571
}
566572

573+
if ((columnMetadata.type === "set") && defaultValue !== undefined) {
574+
return `'${DateUtils.simpleArrayToString(defaultValue)}'`;
575+
}
576+
567577
if (typeof defaultValue === "number") {
568578
return "" + defaultValue;
569579

src/driver/mysql/MysqlQueryRunner.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1333,7 +1333,7 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner {
13331333
tableColumn.scale = parseInt(dbColumn["NUMERIC_SCALE"]);
13341334
}
13351335

1336-
if (tableColumn.type === "enum" || tableColumn.type === "simple-enum") {
1336+
if (tableColumn.type === "enum" || tableColumn.type === "simple-enum" || tableColumn.type === "set") {
13371337
const colType = dbColumn["COLUMN_TYPE"];
13381338
const items = colType.substring(colType.indexOf("(") + 1, colType.indexOf(")")).split(",");
13391339
tableColumn.enum = (items as string[]).map(item => {

src/driver/types/ColumnTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ export type SimpleColumnType =
161161

162162
// other types
163163
|"enum" // mysql, postgres
164+
|"set" // mysql
164165
|"cidr" // postgres
165166
|"inet" // postgres, cockroachdb
166167
|"macaddr"// postgres
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Column, Entity, PrimaryGeneratedColumn } from "../../../../src";
2+
import { Role } from "../set";
3+
4+
@Entity("post")
5+
export class Post {
6+
7+
@PrimaryGeneratedColumn()
8+
id: number;
9+
10+
@Column("set", {
11+
default: [Role.Admin, Role.Developer],
12+
enum: Role
13+
})
14+
roles: Role[];
15+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import "reflect-metadata";
2+
import { Connection } from "../../../src/connection/Connection";
3+
import { closeTestingConnections, createTestingConnections } from "../../utils/test-utils";
4+
import { Post } from "./entity/Post";
5+
import { expect } from "chai";
6+
import { Role } from "./set";
7+
8+
describe("github issues > #2779 Could we add support for the MySQL/MariaDB SET data type?", () => {
9+
10+
let connections: Connection[];
11+
before(async () => {
12+
connections = await createTestingConnections({
13+
entities: [__dirname + "/entity/*{.js,.ts}"],
14+
enabledDrivers: ["mariadb", "mysql"],
15+
schemaCreate: true,
16+
dropSchema: true,
17+
});
18+
});
19+
after(() => closeTestingConnections(connections));
20+
21+
it("should create column with SET datatype", () => Promise.all(connections.map(async connection => {
22+
23+
const queryRunner = connection.createQueryRunner();
24+
const table = await queryRunner.getTable("post");
25+
table!.findColumnByName("roles")!.type.should.be.equal("set");
26+
await queryRunner.release();
27+
28+
})));
29+
30+
it("should persist and hydrate sets", () => Promise.all(connections.map(async connection => {
31+
32+
const targetValue = [Role.Support, Role.Developer];
33+
34+
const post = new Post();
35+
post.roles = targetValue;
36+
await connection.manager.save(post);
37+
post.roles.should.be.deep.equal(targetValue);
38+
39+
const loadedPost = await connection.manager.findOne(Post);
40+
expect(loadedPost).not.to.be.undefined;
41+
loadedPost!.roles.should.be.deep.equal(targetValue);
42+
})));
43+
44+
});

test/github-issues/2779/set.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export enum Role {
2+
Admin = "Admin",
3+
Support = "Support",
4+
Developer = "Developer"
5+
}

0 commit comments

Comments
 (0)