Problem/Motivation
Entity IDs can be either a string or an int. Both are acceptable, and even tested for (EntityTestStringID.php). Postgres cannot join across different datatypes. So, when a View lists a relationship between two tables, and the shared identifier stores one as a string and the other as an integer, the query causes an error:
SQLSTATE[42883]: Undefined function: 7 ERROR: operator does not exist: bigint = character varying
Proposed resolution
Cast both condition values to strings.
| Comment | File | Size | Author |
|---|
Issue fork drupal-3079534
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
jcandan commentedThis fix casts the left and right join condition values as TEXT.
> Note: I personally think that the condition statement modified in this patch should go through
pgsqldriver, and it be cast there. But I was not sure what the best implementation would be.Comment #3
jcandan commentedComment #6
davidferlay commentedFaced that issue, this patch seems to solve it entirely when using PostgreSQL
But it breaks site when patch is used with MySQL, meaning codebase is coupled with infra, which is not cool and should be avoided
Comment #7
davidferlay commentedComment #8
andypostComment #9
davidferlay commentedComment #12
jakegibs617 commentedI did see initial success using the patch from #2 but later I saw very steep costs to performance due to this. This is applied to all views which can be dangerous. And it caused one of my views which originally might take up to 7 seconds to load to result in over 15 minutes to load.
Without the patch: Execution Time: 2214.311 ms, cost=52932.30
With the patch and a functional index: Execution Time: 7849.606 ms but the cost=220466848333415508344832.00 (way to big)
Comment #14
mr_scumbagUpdated this patch for Drupal 9.4.1
Comment #15
priya.chat commentedHi All, Updated the patch #14 with few minor changes.
Comment #17
priya.chat commentedTried to the syntax error by corrected the cast operator as getting syntax error in the previous patch.
Comment #20
prhuang commentedAdded a CAST as CHAR to solve the issue.
Comment #21
joel_osc commentedComment #22
prhuang commentedRemoved the trailing spaces.
Comment #24
luisnicg commentedComplementing #12
In addition to performance issues, I got duplicated rows in different views, for example, I got duplicated results after filtering users by mail in /admin/people.
I would recommend validating the database type before doing the CAST, since this only happens with "pgsql", then validate the type of the field (if entity type exists) and validate if the field doesn't have "id" as sub-string (assuming all ids are INT)
I solve my specific issue with this https://www.drupal.org/project/flag/issues/2929733#comment-12836956
Comment #25
luisnicg commentedBased on my previous comment I've created this patch.
Comment #26
luisnicg commentedI have placed strrpos params wrong, patch was updated.
Comment #27
xdequinzeThe last patch didn't work for me, using Drupal 9.5.11.
I updated to make it working.
Comment #28
liam morlandThe patch in #27 fixes this for me in Drupal 10.2. It also applies to Drupal 11.
Comment #29
liam morlandThis improved patch casts to
textinstead ofbigint. This fixes additional failures I was having.I have created a merge request with the patches in 27 and 29.
Comment #31
liam morlandI have updated the tests so that they pass with the changes.
Comment #32
joseph.olstad@Liam Morland, while the tests are passing on the merge request, it's not apparent which database system is being tested. It would be good to just make sure that MariaDB/Mysql/Sqlite are also passing and also all versions of PostgreSQL. With the MR test, it wasn't obvious which setup was being tested (maybe I'm missing something?).
Comment #33
liam morlandI have run the tests on all the platforms available in the pipelines. They have all passed. If you click on the pipeline number in the MR, you can see the full results.
Comment #34
lendudeCan we use \Drupal\views\Plugin\views\join\CastedIntFieldJoin for this and avoid adding pgsql specific code to base plugins?
Comment #35
liam morlandIt looks like it will be deprecated: #3376464: [PP-1] Deprecate and remove Drupal\views\Plugin\views\join\CastedIntFieldJoin.
By its name, it sounds like it would cast to
int. The merge request has it casting totextbecause entity identifiers are not always integers.Comment #36
liam morlandCurrent changes without the test updates.
Comment #37
smustgrave commentedHiding patches for clarity.
Leaving in NR as seems desire is/was to move pgsql specific code out of base
Comment #38
liam morlandWhere would you suggest this code be put?
Comment #39
joseph.olstad@smustgrave , the suggested approach to move pgsql specific code out of base is marked to be deprecated.
see comment #35
#3079534-35: Views JOIN condition fails in PostgreSQL when comparing entity identifiers of different datatypes
Comment #40
lendudeWell if there is a use for it, we can always undeprecate it (or in this case, never deprecate it in the first place), or if it needs a string version of this we should add a string version and put it in the pgsql module where this seems to belong (is PostgreSQL the exception here or is MySQL? How does SQLite handle this?)
Also, I think the general consensus is that int joins are more performant than string joins, so what performance impact does casting everything to a string have? We should check.
How is this handled outside Views? It's not like Views is the only place you can make joins. Is there maybe some method we could/should add to the database drivers that handles this?
I would like to see test coverage added that actually shows we need this, so something that would fail without this fix. All the modified test coverage changes only show that the ':text' is added and this doesn't break existing queries.
Comment #41
liam morlandAt the moment, Postgres is the exception, though I think that it is doing it more correct under the SQL standard. I don't know what SQLite does.
I ran into this problem as described in #2864440: PostgreSQL problems on entity_id of type varchar with flagging and flag_count tables. Other related issues describe how this has surfaced.
The problem is that
idcolumns can be either string or integer. If there is a way to of knowing which it is then it could use integer casting when that is possible.Comment #43
orkutmuratyilmazI've tried different patches and the Merge Requests (as patches) on a D11.3.3, PG17 installation. Unfortunately, none of them worked. Do I miss something?
Comment #44
liam morlandThere is a patch for this for
flagmodule in #2929733: SQL query error on PostgreSQL 9.5 when try to list Bookmarks.Comment #45
joseph.olstadThis change is reasonable IMHO and the tests have been adjusted. Client of mine is migrating from MySQL to pgsql for several reasons and we'll appreciate this improvement.
Would be good if this fix made it into 11.4.
Comment #46
alexpottThis solution around
str_contains($this->field, 'id')does not feel correct. Firstly it can produce both false positives and false negatives. For example the uuid field or any field containing the string id. Also, casting an indexed integer column to ::text in a JOIN … ON condition makes PostgreSQL unable to use the index on that column. For large tables (e.g., node_field_data, users_field_data) this could turn an index seek into a full table scan.We need to compare the columns and only add ::text cast to the necessary column. However we don't have an API to introspect the table schema so we'll need to add one. Ideally we should file fixes in modules that cause this problem - ie. in the flagging case we should add an integer column to the table flagging_count table in in the case where we join to an entity with an integer ID we should join to that column.