working on data types tests;

refactoring in drivers and query runners;
This commit is contained in:
Zotov Dmitry 2017-07-03 17:01:43 +05:00
parent 848f559316
commit e7c516e7f4
22 changed files with 222 additions and 45 deletions

View File

@ -79,7 +79,7 @@ export function Column(typeOrOptions?: ((type?: any) => Function)|ColumnType|(Co
if (typeOrOptions instanceof Function) {
const reflectMetadataType = Reflect && (Reflect as any).getMetadata ? (Reflect as any).getMetadata("design:type", object, propertyName) : undefined;
const isArray = reflectMetadataType === Array || (options && options.array === true) ? true : false;
const isArray = reflectMetadataType === Array || (options && options.isArray === true) ? true : false;
const args: EmbeddedMetadataArgs = {
target: object.constructor,

View File

@ -39,6 +39,6 @@ export interface ColumnCommonOptions {
* Can be simply set to true or array length can be specified.
* Supported only by postgres.
*/
array?: boolean|string;
isArray?: boolean;
}

View File

@ -76,6 +76,6 @@ export interface ColumnOptions {
* Can be simply set to true or array length can be specified.
* Supported only by postgres.
*/
array?: boolean|string;
isArray?: boolean;
}

View File

@ -73,7 +73,7 @@ export interface Driver {
/**
* Transforms type of the given column to a database column type.
*/
normalizeType(column: { type?: ColumnType, length?: number, precision?: number, scale?: number, array?: string|boolean }): string;
normalizeType(column: { type?: ColumnType, length?: number, precision?: number, scale?: number, isArray?: boolean }): string;
/**
* Normalizes "default" value of the column.

View File

@ -164,7 +164,7 @@ export class MongoDriver implements Driver {
/**
* Creates a database type from a given column metadata.
*/
normalizeType(column: { type?: ColumnType, length?: number, precision?: number, scale?: number, array?: string|boolean }): string {
normalizeType(column: { type?: ColumnType, length?: number, precision?: number, scale?: number }): string {
throw new Error(`MongoDB is schema-less, not supported by this driver.`);
}

View File

@ -295,9 +295,9 @@ export class MysqlQueryRunner implements QueryRunner {
const columnSchema = new ColumnSchema();
columnSchema.name = dbColumn["COLUMN_NAME"];
const type = dbColumn["COLUMN_TYPE"].toLowerCase();
const endIndex = type.indexOf("(");
columnSchema.type = endIndex !== -1 ? type.substr(0, endIndex) : type;
const columnType = dbColumn["COLUMN_TYPE"].toLowerCase();
const endIndex = columnType.indexOf("(");
columnSchema.type = endIndex !== -1 ? columnType.substring(0, endIndex) : columnType;
columnSchema.default = dbColumn["COLUMN_DEFAULT"] !== null && dbColumn["COLUMN_DEFAULT"] !== undefined ? dbColumn["COLUMN_DEFAULT"] : undefined;
columnSchema.isNullable = dbColumn["IS_NULLABLE"] === "YES";
@ -312,13 +312,21 @@ export class MysqlQueryRunner implements QueryRunner {
|| columnSchema.type === "smallint" || columnSchema.type === "mediumint"
|| columnSchema.type === "bigint" || columnSchema.type === "year") {
const length = type.substr(type.indexOf("(") + 1, type.indexOf(")"));
const length = columnType.substring(columnType.indexOf("(") + 1, columnType.indexOf(")"));
columnSchema.length = parseInt(length);
} else {
} else {
columnSchema.length = dbColumn["CHARACTER_MAXIMUM_LENGTH"];
}
if (columnSchema.type === "enum") {
const colType = dbColumn["COLUMN_TYPE"];
const items = colType.substring(colType.indexOf("(") + 1, colType.indexOf(")")).split(",");
columnSchema.enum = (items as string[]).map(item => {
return item.substring(1, item.length - 1);
});
}
return columnSchema;
});

View File

@ -281,7 +281,7 @@ export class OracleDriver implements Driver {
/**
* Creates a database type from a given column metadata.
*/
normalizeType(column: { type?: ColumnType, length?: number, precision?: number, scale?: number, array?: string|boolean }): string {
normalizeType(column: { type?: ColumnType, length?: number, precision?: number, scale?: number, isArray?: boolean }): string {
let type = "";
if (column.type === Number) {
type += "integer";

View File

@ -228,7 +228,7 @@ export class PostgresDriver implements Driver {
|| columnMetadata.type === "timestamp without time zone") {
return DateUtils.mixedDateToUtcDatetimeString(value);
} else if (columnMetadata.type === "json" || columnMetadata.type === "jsonb") {
} else if (columnMetadata.type === "json" || columnMetadata.type === "jsonb" || columnMetadata.type === Object) {
return JSON.stringify(value);
} else if (columnMetadata.type === "simple-array") {
@ -301,7 +301,7 @@ export class PostgresDriver implements Driver {
/**
* Creates a database type from a given column metadata.
*/
normalizeType(column: { type?: ColumnType, length?: number, precision?: number, scale?: number, array?: string|boolean }): string {
normalizeType(column: { type?: ColumnType, length?: number, precision?: number, scale?: number, isArray?: boolean }): string {
let type = "";
if (column.type === Number) {
type += "integer";
@ -316,7 +316,7 @@ export class PostgresDriver implements Driver {
type += "boolean";
} else if (column.type === Object) {
type += "text";
type += "json";
} else if (column.type === "simple-array") {
type += "text";
@ -326,17 +326,49 @@ export class PostgresDriver implements Driver {
}
// normalize shortcuts
if (type === "int") {
if (type === "int" || type === "int4") {
type = "integer";
} else if (type === "int2") {
type = "smallint";
} else if (type === "int8") {
type = "bigint";
} else if (type === "decimal") {
type = "numeric";
} else if (type === "float8") {
type = "double precision";
} else if (type === "float4") {
type = "real";
} else if (type === "char") {
type = "character";
} else if (type === "varchar") {
type = "character varying";
} else if (type === "time") {
type = "time without time zone";
} else if (type === "timetz") {
type = "time with time zone";
} else if (type === "timestamptz") {
type = "timestamp with time zone";
} else if (type === "bool") {
type = "boolean";
} else if (type === "varbit") {
type = "bit varying";
} else if (type === "timestamp") {
type = "timestamp without time zone";
}
if (column.array) {
type += " ARRAY" + (typeof column.array === "string" ? column.array : "");
}
return type;
}

View File

@ -341,6 +341,11 @@ where constraint_type = 'PRIMARY KEY' AND c.table_schema = '${this.schemaName}'
columnSchema.isGenerated = isGenerated;
columnSchema.comment = ""; // dbColumn["COLUMN_COMMENT"];
columnSchema.isUnique = !!dbUniqueKeys.find(key => key["constraint_name"] === `uk_${dbColumn["table_name"]}_${dbColumn["column_name"]}`);
if (columnSchema.type === "array") {
columnSchema.isArray = true;
const type = dbColumn["udt_name"].substring(1);
columnSchema.type = this.connection.driver.normalizeType({type: type});
}
return columnSchema;
});

View File

@ -184,7 +184,7 @@ export class SqliteDriver implements Driver {
} else if (columnMetadata.type === "datetime") {
return DateUtils.mixedDateToUtcDatetimeString(value);
} else if (columnMetadata.type === "json") {
} else if (columnMetadata.type === Object) {
return JSON.stringify(value);
} else if (columnMetadata.type === "simple-array") {
@ -210,7 +210,7 @@ export class SqliteDriver implements Driver {
} else if (columnMetadata.type === "time") {
return DateUtils.mixedTimeToString(value);
} else if (columnMetadata.type === "json") {
} else if (columnMetadata.type === Object) {
return JSON.parse(value);
} else if (columnMetadata.type === "simple-array") {
@ -259,7 +259,7 @@ export class SqliteDriver implements Driver {
/**
* Creates a database type from a given column metadata.
*/
normalizeType(column: { type?: ColumnType, length?: number, precision?: number, scale?: number, array?: string|boolean }): string {
normalizeType(column: { type?: ColumnType, length?: number, precision?: number, scale?: number }): string {
let type = "";
if (column.type === Number || column.type === "int") {
type += "integer";
@ -270,6 +270,9 @@ export class SqliteDriver implements Driver {
} else if (column.type === Date) {
type += "datetime";
} else if ((column.type as any) === Buffer) {
type += "blob";
} else if (column.type === Boolean) {
type += "boolean";

View File

@ -294,7 +294,7 @@ export class SqlServerDriver implements Driver {
/**
* Creates a database type from a given column metadata.
*/
normalizeType(column: { type?: ColumnType, length?: number, precision?: number, scale?: number, array?: string|boolean }): string {
normalizeType(column: { type?: ColumnType, length?: number, precision?: number, scale?: number }): string {
let type = "";
if (column.type === Number) {
type += "int";

View File

@ -3,5 +3,5 @@ export interface DataTypeDefaults {
length?: number;
precision?: number;
scale?: number;
}
};
}

View File

@ -242,7 +242,7 @@ export class WebsqlDriver implements Driver {
/**
* Creates a database type from a given column metadata.
*/
normalizeType(column: { type?: ColumnType, length?: number, precision?: number, scale?: number, array?: string|boolean }): string {
normalizeType(column: { type?: ColumnType, length?: number, precision?: number, scale?: number }): string {
let type = "";
if (column.type === Number) {
type += "integer";

View File

@ -103,7 +103,7 @@ export class ColumnMetadata {
* Can be simply set to true or array length can be specified.
* Supported only by postgres.
*/
array?: boolean|string;
isArray?: boolean;
/**
* Gets full path to this column property (including column property name).
@ -216,8 +216,8 @@ export class ColumnMetadata {
this.precision = options.args.options.precision;
if (options.args.options.enum)
this.enum = options.args.options.enum;
if (options.args.options.array)
this.array = options.args.options.array;
if (options.args.options.isArray)
this.isArray = options.args.options.isArray;
if (options.args.mode) {
this.isVirtual = options.args.mode === "virtual";
this.isParentId = options.args.mode === "parentId";

View File

@ -45,6 +45,11 @@ export class ColumnSchema {
*/
isUnique: boolean = false;
/**
* Indicates if column stores array.
*/
isArray: boolean = false;
/**
* Column's comment.
*/
@ -126,27 +131,30 @@ export class ColumnSchema {
newColumnSchema.isGenerated = this.isGenerated;
newColumnSchema.isPrimary = this.isPrimary;
newColumnSchema.isUnique = this.isUnique;
newColumnSchema.isArray = this.isArray;
newColumnSchema.comment = this.comment;
return newColumnSchema;
}
getFullType(driver: Driver): string {
let type = this.type;
if (this.length) {
return this.type + "(" + this.length + ")";
type += "(" + this.length + ")";
} else if (this.precision && this.scale) {
return this.type + "(" + this.precision + "," + this.scale + ")";
} else if (this.precision) {
if (this.type === "real")
return this.type;
return this.type + "(" + this.precision + ")";
type += "(" + this.precision + "," + this.scale + ")";
} else if (this.precision && this.type !== "real") {
type += "(" + this.precision + ")";
} else if (this.scale) {
return this.type + "(" + this.scale + ")";
type += "(" + this.scale + ")";
} else if (driver.dataTypeDefaults && driver.dataTypeDefaults[this.type] && driver.dataTypeDefaults[this.type].length) {
type += "(" + driver.dataTypeDefaults[this.type].length + ")";
}
if (driver.dataTypeDefaults && driver.dataTypeDefaults[this.type] && driver.dataTypeDefaults[this.type].length)
return this.type + "(" + driver.dataTypeDefaults[this.type].length + ")";
if (this.isArray)
type += " array";
return this.type;
return type;
}
// -------------------------------------------------------------------------
@ -169,6 +177,7 @@ export class ColumnSchema {
columnSchema.type = normalizedType;
columnSchema.isPrimary = columnMetadata.isPrimary;
columnSchema.isUnique = columnMetadata.isUnique;
columnSchema.isArray = columnMetadata.isArray || false;
columnSchema.enum = columnMetadata.enum;
return columnSchema;
}

View File

@ -5,7 +5,7 @@ import {closeTestingConnections, createTestingConnections, reloadTestingDatabase
import {PostWithOptions} from "./entity/PostWithOptions";
import {PostWithoutTypes} from "./entity/PostWithoutTypes";
describe.only("database schema > column types > mysql", () => {
describe("database schema > column types > mysql", () => {
let connections: Connection[];
before(async () => {
@ -127,6 +127,9 @@ describe.only("database schema > column types > mysql", () => {
tableSchema!.findColumnByName("longblob")!.type.should.be.equal("longblob");
tableSchema!.findColumnByName("longtext")!.type.should.be.equal("longtext");
tableSchema!.findColumnByName("enum")!.type.should.be.equal("enum");
tableSchema!.findColumnByName("enum")!.enum![0].should.be.equal("A");
tableSchema!.findColumnByName("enum")!.enum![1].should.be.equal("B");
tableSchema!.findColumnByName("enum")!.enum![2].should.be.equal("C");
tableSchema!.findColumnByName("simpleArray")!.type.should.be.equal("text");
})));

View File

@ -3,6 +3,7 @@ import {Post} from "./entity/Post";
import {PostWithOptions} from "./entity/PostWithOptions";
import {Connection} from "../../../../../src/connection/Connection";
import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../../utils/test-utils";
import {PostWithoutTypes} from "./entity/PostWithoutTypes";
describe("database schema > column types > postgres", () => {
@ -192,7 +193,8 @@ describe("database schema > column types > postgres", () => {
tableSchema!.findColumnByName("uuid")!.type.should.be.equal("uuid");
tableSchema!.findColumnByName("xml")!.type.should.be.equal("xml");
tableSchema!.findColumnByName("json")!.type.should.be.equal("json");
tableSchema!.findColumnByName("array")!.type.should.be.equal("array");
tableSchema!.findColumnByName("array")!.type.should.be.equal("integer");
tableSchema!.findColumnByName("array")!.isArray!.should.be.true;
tableSchema!.findColumnByName("simpleArray")!.type.should.be.equal("text");
})));
@ -241,4 +243,35 @@ describe("database schema > column types > postgres", () => {
})));
it("all types should work correctly - persist and hydrate when types are not specified on columns", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(PostWithoutTypes);
const queryRunner = connection.createQueryRunner();
const tableSchema = await queryRunner.loadTableSchema("post_without_types");
await queryRunner.release();
const post = new PostWithoutTypes();
post.id = 1;
post.name = "Post";
post.bit = true;
post.datetime = new Date();
post.datetime.setMilliseconds(0);
post.object = { id: 1, name: "Post" };
await postRepository.save(post);
const loadedPost = (await postRepository.findOneById(1))!;
loadedPost.id.should.be.equal(post.id);
loadedPost.name.should.be.equal(post.name);
loadedPost.bit.should.be.equal(post.bit);
loadedPost.datetime.valueOf().should.be.equal(post.datetime.valueOf());
loadedPost.object.should.be.eql(post.object);
tableSchema!.findColumnByName("id")!.type.should.be.equal("integer");
tableSchema!.findColumnByName("name")!.type.should.be.equal("character varying");
tableSchema!.findColumnByName("bit")!.type.should.be.equal("boolean");
tableSchema!.findColumnByName("datetime")!.type.should.be.equal("timestamp without time zone");
tableSchema!.findColumnByName("object")!.type.should.be.equal("json");
})));
});

View File

@ -226,7 +226,7 @@ export class Post {
// Array Type
// -------------------------------------------------------------------------
@Column("int", { array: true })
@Column("int", { isArray: true })
array: number[];
// -------------------------------------------------------------------------

View File

@ -0,0 +1,23 @@
import {Entity} from "../../../../../../src/decorator/entity/Entity";
import {PrimaryColumn} from "../../../../../../src/decorator/columns/PrimaryColumn";
import {Column} from "../../../../../../src/decorator/columns/Column";
@Entity()
export class PostWithoutTypes {
@PrimaryColumn()
id: number;
@Column()
name: string;
@Column()
bit: boolean;
@Column()
datetime: Date;
@Column()
object: Object;
}

View File

@ -1,7 +1,8 @@
import "reflect-metadata";
import {Post} from "./entity/Post";
import {Connection} from "../../../../../src/connection/Connection";
import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../../utils/test-utils";
import {closeTestingConnections, createTestingConnections} from "../../../../utils/test-utils";
import {PostWithoutTypes} from "./entity/PostWithoutTypes";
describe("database schema > column types > sqlite", () => {
@ -11,10 +12,10 @@ describe("database schema > column types > sqlite", () => {
entities: [__dirname + "/entity/*{.js,.ts}"],
enabledDrivers: ["sqlite"],
schemaCreate: true,
dropSchemaOnConnection: true,
// dropSchemaOnConnection: true,
});
});
beforeEach(() => reloadTestingDatabases(connections));
// beforeEach(() => reloadTestingDatabases(connections));
after(() => closeTestingConnections(connections));
it("all types should work correctly - persist and hydrate", () => Promise.all(connections.map(async connection => {
@ -124,4 +125,38 @@ describe("database schema > column types > sqlite", () => {
})));
it("all types should work correctly - persist and hydrate when types are not specified on columns", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(PostWithoutTypes);
const queryRunner = connection.createQueryRunner();
const tableSchema = await queryRunner.loadTableSchema("post_without_types");
await queryRunner.release();
const post = new PostWithoutTypes();
post.id = 1;
post.name = "Post";
post.boolean = true;
post.blob = new Buffer("A");
post.datetime = new Date();
post.datetime.setMilliseconds(0);
post.object = { id: 1, name: "Post" };
await postRepository.save(post);
const loadedPost = (await postRepository.findOneById(1))!;
loadedPost.id.should.be.equal(post.id);
loadedPost.name.should.be.equal(post.name);
loadedPost.boolean.should.be.equal(post.boolean);
loadedPost.blob.toString().should.be.equal(post.blob.toString());
loadedPost.datetime.valueOf().should.be.equal(post.datetime.valueOf());
loadedPost.object.should.be.eql(post.object);
tableSchema!.findColumnByName("id")!.type.should.be.equal("integer");
tableSchema!.findColumnByName("name")!.type.should.be.equal("varchar");
tableSchema!.findColumnByName("boolean")!.type.should.be.equal("boolean");
tableSchema!.findColumnByName("blob")!.type.should.be.equal("blob");
tableSchema!.findColumnByName("datetime")!.type.should.be.equal("datetime");
tableSchema!.findColumnByName("object")!.type.should.be.equal("text");
})));
});

View File

@ -0,0 +1,26 @@
import {Entity} from "../../../../../../src/decorator/entity/Entity";
import {PrimaryColumn} from "../../../../../../src/decorator/columns/PrimaryColumn";
import {Column} from "../../../../../../src/decorator/columns/Column";
@Entity()
export class PostWithoutTypes {
@PrimaryColumn()
id: number;
@Column()
name: string;
@Column()
boolean: boolean;
@Column()
blob: Buffer;
@Column()
datetime: Date;
@Column()
object: Object;
}

View File

@ -4,7 +4,7 @@ import {Record} from "./entity/Record";
import {Connection} from "../../../src/connection/Connection";
import {closeTestingConnections, createTestingConnections} from "../../utils/test-utils";
describe("uuid type", () => {
describe.skip("uuid type", () => {
let connections: Connection[];
before(async () => {