From f701f6974f1ab92e28e6b892a8abdf4fc2c9c51a Mon Sep 17 00:00:00 2001 From: Maia Everett Date: Sat, 9 Nov 2019 16:33:36 +0700 Subject: [PATCH 1/6] Add joinColumn and inverseJoinColumn to @JoinTable properties (fixes #183) --- src/drivers/AbstractDriver.ts | 23 +++++++++++++++++++---- src/entity.mst | 2 +- src/models/RelationInfo.ts | 4 ++++ 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/drivers/AbstractDriver.ts b/src/drivers/AbstractDriver.ts index 171444b..5188d62 100644 --- a/src/drivers/AbstractDriver.ts +++ b/src/drivers/AbstractDriver.ts @@ -91,10 +91,15 @@ export default abstract class AbstractDriver { ); manyToManyEntities.forEach(entity => { let relations: RelationInfo[] = []; - relations = entity.Columns.reduce( - (prev: RelationInfo[], curr) => prev.concat(curr.relations), - relations - ); + const joinColumnMap = new Map(); + + entity.Columns.forEach(column => { + column.relations.forEach(relation => { + joinColumnMap.set(relation.relatedTable, column.tsName); + relations.push(relation); + }); + }); + const namesOfRelatedTables = relations .map(v => v.relatedTable) .filter((v, i, s) => s.indexOf(v) === i); @@ -138,6 +143,11 @@ export default abstract class AbstractDriver { col1Rel.isOwner = true; col1Rel.ownerColumn = firstRelatedTable; + col1Rel.joinColumn = joinColumnMap.get(namesOfRelatedTables[0]); + col1Rel.inverseJoinColumn = joinColumnMap.get( + namesOfRelatedTables[1] + ); + column1.relations.push(col1Rel); relatedTable1.Columns.push(column1); @@ -148,6 +158,11 @@ export default abstract class AbstractDriver { col2Rel.relatedTable = firstRelatedTable; col2Rel.relatedColumn = secondRelatedTable; + col2Rel.joinColumn = joinColumnMap.get(namesOfRelatedTables[1]); + col2Rel.inverseJoinColumn = joinColumnMap.get( + namesOfRelatedTables[0] + ); + col2Rel.relationType = "ManyToMany"; col2Rel.isOwner = false; column2.relations.push(col2Rel); diff --git a/src/entity.mst b/src/entity.mst index 49bd95f..8b333e8 100644 --- a/src/entity.mst +++ b/src/entity.mst @@ -27,7 +27,7 @@ import {BaseEntity,Column,Entity,Index,JoinColumn,JoinTable,ManyToMany,ManyToOne {{printPropertyVisibility}}{{toPropertyName tsName}}{{strictMode}}:{{tsType}}{{#options/nullable}} | null{{/options/nullable}}; {{/relations}}{{#relations}} @{{relationType}}(()=>{{toEntityName relatedTable}}, ({{toPropertyName relatedTable}}: {{toEntityName relatedTable}})=>{{toPropertyName relatedTable}}.{{#if isOwner}}{{toPropertyName ownerColumn}},{ {{#../options/primary}}primary:true,{{/../options/primary}}{{^../options/nullable}} nullable:false,{{/../options/nullable}}{{#actionOnDelete}}onDelete: '{{.}}',{{/actionOnDelete}}{{#actionOnUpdate}}onUpdate: '{{.}}'{{/actionOnUpdate}} }{{else}}{{toPropertyName relatedColumn}}{{#if (or actionOnDelete actionOnUpdate ) }},{ {{#actionOnDelete}}onDelete: '{{.}}' ,{{/actionOnDelete}}{{#actionOnUpdate}}onUpdate: '{{.}}'{{/actionOnUpdate}} }{{/if}}{{/if}}){{#isOwner}} - {{#if isManyToMany}}@JoinTable({ name:'{{ ../options/name}}'}){{else}}@JoinColumn({ name:'{{ ../options/name}}'}){{/if}}{{/isOwner}} + {{#if isManyToMany}}@JoinTable({ name:'{{ ../options/name}}'{{#if joinColumn}},joinColumn:{name:'{{joinColumn}}'}{{/if}}{{#if inverseJoinColumn}},inverseJoinColumn:{name:'{{inverseJoinColumn}}'}{{/if}} }){{else}}@JoinColumn({ name:'{{ ../options/name}}'}){{/if}}{{/isOwner}} {{#if (or isOneToMany isManyToMany)}}{{printPropertyVisibility}}{{toPropertyName ../tsName}}{{strictMode}}:{{toLazy (concat (toEntityName relatedTable) "[]")}}; {{else}}{{printPropertyVisibility}}{{toPropertyName ../tsName}}{{strictMode}}:{{toLazy (concat (toEntityName relatedTable) ' | null')}}; {{/if}} diff --git a/src/models/RelationInfo.ts b/src/models/RelationInfo.ts index efaa10e..d9a0b17 100644 --- a/src/models/RelationInfo.ts +++ b/src/models/RelationInfo.ts @@ -11,6 +11,10 @@ export default class RelationInfo { public ownerColumn: string; + public joinColumn?: string; + + public inverseJoinColumn?: string; + public actionOnDelete: | "RESTRICT" | "CASCADE" From da8fe038c1ff5e39b3be94195fd588c08241eac6 Mon Sep 17 00:00:00 2001 From: Kononnable Date: Sun, 10 Nov 2019 20:56:48 +0100 Subject: [PATCH 2/6] fix tslint issue --- src/drivers/AbstractDriver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/AbstractDriver.ts b/src/drivers/AbstractDriver.ts index 5188d62..a74045a 100644 --- a/src/drivers/AbstractDriver.ts +++ b/src/drivers/AbstractDriver.ts @@ -90,7 +90,7 @@ export default abstract class AbstractDriver { }).length === entity.Columns.length ); manyToManyEntities.forEach(entity => { - let relations: RelationInfo[] = []; + const relations: RelationInfo[] = []; const joinColumnMap = new Map(); entity.Columns.forEach(column => { From 7af1a9eefe64d66eb927ac312c3713139176fd04 Mon Sep 17 00:00:00 2001 From: 0xflotus <0xflotus@gmail.com> Date: Sun, 10 Nov 2019 21:26:47 +0100 Subject: [PATCH 3/6] fixed small error --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 80bddc0..24f0c87 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Options: [default: "./output"] -s, --schema Schema name to create model from. Only for mssql and postgres. You can pass multiple values - separted by comma eg. -s scheme1,scheme2,scheme3 + separated by comma eg. -s scheme1,scheme2,scheme3 --ssl [boolean] [default: false] ``` ### Examples From 4e800df27da6a1940f33fff8a9044af27bad9cbe Mon Sep 17 00:00:00 2001 From: Gabriel Arias Date: Sun, 1 Dec 2019 18:06:54 -0300 Subject: [PATCH 4/6] Geography type added in postgres driver. Solving error when this type appear. --- src/drivers/PostgresDriver.ts | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/drivers/PostgresDriver.ts b/src/drivers/PostgresDriver.ts index 9dbb560..fb3d8a3 100644 --- a/src/drivers/PostgresDriver.ts +++ b/src/drivers/PostgresDriver.ts @@ -29,9 +29,11 @@ export default class PostgresDriver extends AbstractDriver { TABLE_SCHEMA: string; TABLE_NAME: string; DB_NAME: string; - }[] = (await this.Connection.query( - `SELECT table_schema as "TABLE_SCHEMA",table_name as "TABLE_NAME", table_catalog as "DB_NAME" FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE' AND table_schema in (${schema}) ` - )).rows; + }[] = ( + await this.Connection.query( + `SELECT table_schema as "TABLE_SCHEMA",table_name as "TABLE_NAME", table_catalog as "DB_NAME" FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE' AND table_schema in (${schema}) ` + ) + ).rows; return response; }; @@ -52,8 +54,9 @@ export default class PostgresDriver extends AbstractDriver { isidentity: string; isunique: string; enumvalues: string | null; - }[] = (await this.Connection - .query(`SELECT table_name,column_name,udt_name,column_default,is_nullable, + }[] = ( + await this.Connection + .query(`SELECT table_name,column_name,udt_name,column_default,is_nullable, data_type,character_maximum_length,numeric_precision,numeric_scale, case when column_default LIKE 'nextval%' then 'YES' else 'NO' end isidentity, (SELECT count(*) @@ -74,7 +77,8 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name ) enumValues FROM INFORMATION_SCHEMA.COLUMNS c where table_schema in (${schema}) - order by ordinal_position`)).rows; + order by ordinal_position`) + ).rows; entities.forEach(ent => { response .filter(filterVal => filterVal.table_name === ent.tsEntityName) @@ -370,6 +374,7 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name switch (udtName) { case "citext": case "hstore": + case "geography": case "geometry": ret.sqlType = udtName; break; @@ -407,7 +412,8 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name columnname: string; is_unique: number; is_primary_key: number; - }[] = (await this.Connection.query(`SELECT + }[] = ( + await this.Connection.query(`SELECT c.relname AS tablename, i.relname as indexname, f.attname AS columnname, @@ -430,7 +436,8 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name AND n.nspname in (${schema}) AND f.attnum > 0 AND i.oid<>0 - ORDER BY c.relname,f.attname;`)).rows; + ORDER BY c.relname,f.attname;`) + ).rows; entities.forEach(ent => { response .filter(filterVal => filterVal.tablename === ent.tsEntityName) @@ -477,7 +484,8 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name onupdate: "RESTRICT" | "CASCADE" | "SET NULL" | "NO ACTION"; object_id: string; // Distinct because of note in https://www.postgresql.org/docs/9.1/information-schema.html - }[] = (await this.Connection.query(`SELECT DISTINCT + }[] = ( + await this.Connection.query(`SELECT DISTINCT con.relname AS tablewithforeignkey, att.attnum as fk_partno, att2.attname AS foreignkeycolumn, @@ -516,7 +524,8 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name AND att2.attrelid = con.conrelid AND att2.attnum = con.parent AND rc.constraint_name= con.conname AND constraint_catalog=current_database() AND rc.constraint_schema=nspname - `)).rows; + `) + ).rows; const relationsTemp: RelationTempInfo[] = [] as RelationTempInfo[]; response.forEach(resp => { let rels = relationsTemp.find( From d8448fd5ed344234309570c6c5100cdda1d97b2d Mon Sep 17 00:00:00 2001 From: Kononnable Date: Sun, 15 Dec 2019 21:41:44 +0100 Subject: [PATCH 5/6] test for postgres geography column type --- test/integration/entityTypes/postgres/entity/Post.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/integration/entityTypes/postgres/entity/Post.ts b/test/integration/entityTypes/postgres/entity/Post.ts index 1f1cc56..b715176 100644 --- a/test/integration/entityTypes/postgres/entity/Post.ts +++ b/test/integration/entityTypes/postgres/entity/Post.ts @@ -186,4 +186,7 @@ export class Post { @Column("daterange") daterange: string; + + @Column("geography") + geography: string; } From f5a9da288061a4ca6fecbd5b157648c90bb34853 Mon Sep 17 00:00:00 2001 From: Kononnable Date: Sun, 15 Dec 2019 22:39:09 +0100 Subject: [PATCH 6/6] ignore PostGIS table on schema generation --- CHANGELOG.md | 3 ++- src/drivers/PostgresDriver.ts | 29 +++++++++++------------------ 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3a3b6d..08134e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,8 @@ ## Unreleased * change default case conversions for generated files (#196) -* enum type safety #205 +* enum type safety (#205) +* postgress geography type support (#232) ## 0.3.5 diff --git a/src/drivers/PostgresDriver.ts b/src/drivers/PostgresDriver.ts index fb3d8a3..2662b89 100644 --- a/src/drivers/PostgresDriver.ts +++ b/src/drivers/PostgresDriver.ts @@ -29,11 +29,10 @@ export default class PostgresDriver extends AbstractDriver { TABLE_SCHEMA: string; TABLE_NAME: string; DB_NAME: string; - }[] = ( - await this.Connection.query( - `SELECT table_schema as "TABLE_SCHEMA",table_name as "TABLE_NAME", table_catalog as "DB_NAME" FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE' AND table_schema in (${schema}) ` - ) - ).rows; + }[] = (await this.Connection.query( + `SELECT table_schema as "TABLE_SCHEMA",table_name as "TABLE_NAME", table_catalog as "DB_NAME" FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_TYPE='BASE TABLE' AND table_schema in (${schema}) AND table_name<>'spatial_ref_sys'` + )).rows; return response; }; @@ -54,9 +53,8 @@ export default class PostgresDriver extends AbstractDriver { isidentity: string; isunique: string; enumvalues: string | null; - }[] = ( - await this.Connection - .query(`SELECT table_name,column_name,udt_name,column_default,is_nullable, + }[] = (await this.Connection + .query(`SELECT table_name,column_name,udt_name,column_default,is_nullable, data_type,character_maximum_length,numeric_precision,numeric_scale, case when column_default LIKE 'nextval%' then 'YES' else 'NO' end isidentity, (SELECT count(*) @@ -77,8 +75,7 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name ) enumValues FROM INFORMATION_SCHEMA.COLUMNS c where table_schema in (${schema}) - order by ordinal_position`) - ).rows; + order by ordinal_position`)).rows; entities.forEach(ent => { response .filter(filterVal => filterVal.table_name === ent.tsEntityName) @@ -412,8 +409,7 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name columnname: string; is_unique: number; is_primary_key: number; - }[] = ( - await this.Connection.query(`SELECT + }[] = (await this.Connection.query(`SELECT c.relname AS tablename, i.relname as indexname, f.attname AS columnname, @@ -436,8 +432,7 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name AND n.nspname in (${schema}) AND f.attnum > 0 AND i.oid<>0 - ORDER BY c.relname,f.attname;`) - ).rows; + ORDER BY c.relname,f.attname;`)).rows; entities.forEach(ent => { response .filter(filterVal => filterVal.tablename === ent.tsEntityName) @@ -484,8 +479,7 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name onupdate: "RESTRICT" | "CASCADE" | "SET NULL" | "NO ACTION"; object_id: string; // Distinct because of note in https://www.postgresql.org/docs/9.1/information-schema.html - }[] = ( - await this.Connection.query(`SELECT DISTINCT + }[] = (await this.Connection.query(`SELECT DISTINCT con.relname AS tablewithforeignkey, att.attnum as fk_partno, att2.attname AS foreignkeycolumn, @@ -524,8 +518,7 @@ WHERE "n"."nspname" = table_schema AND "t"."typname"=udt_name AND att2.attrelid = con.conrelid AND att2.attnum = con.parent AND rc.constraint_name= con.conname AND constraint_catalog=current_database() AND rc.constraint_schema=nspname - `) - ).rows; + `)).rows; const relationsTemp: RelationTempInfo[] = [] as RelationTempInfo[]; response.forEach(resp => { let rels = relationsTemp.find(