typeorm: JoinTable query generates incorrectly when entity doesn't match standard naming conventions

Steps to reproduce or a small repository showing the problem:

  1. Create two entities that don’t follow the naming convention (ex. Thing in table things and Tag in table tags)

  2. Create a join table for them (thing_tags) (note that due to the departure from naming convention earlier, this is also a departure from naming convention)

  3. Create a @ManyToMany relation using @JoinTable inside Thing as follows:

  @ManyToMany(type => Tag, {eager: true})
  @JoinTable({
    name: "thing_tags",
    joinColumn: {name: "thing_id", referencedColumnName: "id"}
    inverseJoinColumn: {name: "tag_id", referencedColumnName: "id"}
  })
  tags: Tag[];

When trying to retrieve a Thing, the query is just barely wrong:

SELECT Thing_tags_relation_id.tag_id AS "tag_id", Thing_tags_relation_id.thing_id AS "thing_id" FROM "tags" "tags" INNER JOIN "thing_tags" "Thing_tags_relation_id" ON (Thing_tags_relation_id.thing_id = $1 AND Thing_tags_relation_id.tag_id = "tags"."id") ORDER BY Thing_tags_relation_id.tag_id ASC, Thing_tags_relation_id.thing_id ASC;
ERROR:  missing FROM-clause entry for table "thing_tags_relation_id"

The problem here is that Thing_tags_relation_id is not quoted everywhere it’s mentioned. If I take the query to postgres, and add quotes around every unquoted Thing_tags_relation_id, the query works great:

db_test=# SELECT "Thing_tags_relation_id".tag_id AS "tag_id", "Thing_tags_relation_id".thing_id AS "thing_id" FROM "tags" "tags" INNER JOIN "thing_tags" "Thing_tags_relation_id" ON ("Thing_tags_relation_id".thing_id = 1743 AND "Thing_tags_relation_id".tag_id = "tags". "id") ORDER BY "Thing_tags_relation_id".tag_id ASC, "Thing_tags_relation_id".thing_id ASC;
tag_id | thing_id
--------+-----------
(0 rows)

PS I personally would love a parametrizedQuery parameter in error reports that prints the complete query with params filled out (exactly as it would have been sent).

As always thanks for all the work on typeorm, it is 100% improving the development lives of me and my team 😃

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 3
  • Comments: 32 (10 by maintainers)

Most upvoted comments

Started implementing the above. Seems to fix this specific problem - running against the test suite to verify it doesn’t break any other cases.

There’s a number of places that require refactoring, though, because they use referencedColumn and don’t know the relation they’re associated with.

In some cases, the code paths are RELYING on this behavior so they don’t crash. (They just did the wrong thing before)

Was thinking it through finding all the related issues.

Path forward can be as such -

  • referencedColumn has to be marked deprecated. No matter what, this just doesn’t work, the data modelling doesn’t match up with reality.
  • Created a Map or object on the column and map from the relation property name to the referenced column. Goes from target of current column to the relations property.
  • Set the relational column’s propertyName to something that makes sense 'cause right now it doesn’t - it overlaps.
  • Whenever we need a referenced column we will need to specify for WHAT we need it from - or explicitly pick one at random. No more implicit randomness here.
  • Whatever the last set mapped column is, we set that to the deprecated reference column for now.
  • Add a helper in the relation metadata to emit tuples of joins & references.
  • Refactor all the code to use one of the multiple mechanisms available to use the correct reference.

😭 There goes my hopes of a one line fix.

Started implementing the above. Seems to fix this specific problem - running against the test suite to verify it doesn’t break any other cases.

There’s a number of places that require refactoring, though, because they use referencedColumn and don’t know the relation they’re associated with.

In some cases, the code paths are RELYING on this behavior so they don’t crash. (They just did the wrong thing before)

Any update on this?

Ok, after digging around some more I realized why I am getting this incorrect query. It’s basically because typeorm assumes that a single column will always only be used to reference a single table with a specific referenced column. If you now have a fooId relation that refers to foo.id and somefoolinktable.foo_id by setting up custom JoinColumn decorations with referencedColumnNames you get an incorrect query because one relation will override the referencedColumn of fooId that was previously written by the other.

I’m not sure whether this actually is what the original issue poster was experiencing through another pathway.

Does it sound like it would make sense to change the referencedColumns of ColumnMetadata to be ColumnMetadata Array instead of just single metadata? I think I will try my hand at making this change but just wanted to check if someone already knew whether it makes sense.

Are there any suggested workarounds for this? (Specifically ones using find, and not createQueryBuilder?)

I can provide a full repro, but it sounds like the issue is well understood. Basically, in our case, a ProjectRole has a User and a Project, through user_id and a project_id, respectively, and it also has a ProjectLabel, with a multi-keyJoinColumn of:

[
    { name: 'user_id', referencedColumnName: 'userId' },
    { name: 'project_id', referencedColumnName: 'projectId' },
]

Here is the relevant section of the generated query for our issue:

SELECT ...
FROM "projectrole" "ProjectRole"
       LEFT JOIN "users" "ProjectRole__user"
              ON ProjectRole__user.userid = "projectrole"."user_id"
       LEFT JOIN "project" "ProjectRole__project"
              ON ProjectRole__project.projectid =
                 "projectrole"."project_id"
       LEFT JOIN "userprojectlabel" "ProjectRole__label"
              ON "ProjectRole__label"."user_id" =
                 "projectrole"."user_id"
                 AND "ProjectRole__label"."project_id" =
                     "projectrole"."project_id") "distinctAlias"
...

Here, ON ProjectRole__user.userid should be ON "ProjectRole__user"."id", and ON ProjectRole__project.projectid should be ON "ProjectRole__project"."id". Also, note that if I remove the (multi-column) ProjectLabel relation, this problem goes away.

It definitely seems like the case of having multiple join columns with the same name, but different join tables.

Just ran into this as well. @pleerock could we get this reopened?