In a view, I created a combined fields filter, which was supposed to be exposed to the users, allowing a "universal search" by keywords matching multiple fields of some content type.
An attempt to use the exposed filter results in an error:
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.field_editor_valuenode__field_journal.field_journal_target_idnode__field_note.f' at line 10: SELECT COUNT(*) AS expression FROM (SELECT 1 AS expression FROM {node_field_data} node_field_data LEFT JOIN {node__field_field_author} node__field_field_author ON node_field_data.nid = node__field_field_author.entity_id AND (node__field_field_author.deleted = :views_join_condition_0 AND node__field_field_author.langcode = node_field_data.langcode) LEFT JOIN {node__field_editor} node__field_editor ON node_field_data.nid = node__field_editor.entity_id AND (node__field_editor.deleted = :views_join_condition_2 AND node__field_editor.langcode = node_field_data.langcode) LEFT JOIN {node__field_journal} node__field_journal ON node_field_data.nid = node__field_journal.entity_id AND (node__field_journal.deleted = :views_join_condition_4 AND node__field_journal.langcode = node_field_data.langcode) LEFT JOIN {node__field_note} node__field_note ON node_field_data.nid = node__field_note.entity_id AND (node__field_note.deleted = :views_join_condition_6 AND node__field_note.langcode = node_field_data.langcode) LEFT JOIN {node__field_organization} node__field_organization ON node_field_data.nid = node__field_organization.entity_id AND (node__field_organization.deleted = :views_join_condition_8 AND node__field_organization.langcode = node_field_data.langcode) LEFT JOIN {node__field_publisher} node__field_publisher ON node_field_data.nid = node__field_publisher.entity_id AND (node__field_publisher.deleted = :views_join_condition_10 AND node__field_publisher.langcode = node_field_data.langcode) LEFT JOIN {node__field_school} node__field_school ON node_field_data.nid = node__field_school.entity_id AND (node__field_school.deleted = :views_join_condition_12 AND node__field_school.langcode = node_field_data.langcode) LEFT JOIN {node__field_titlehtml} node__field_titlehtml ON node_field_data.nid = node__field_titlehtml.entity_id AND (node__field_titlehtml.deleted = :views_join_condition_14 AND node__field_titlehtml.langcode = node_field_data.langcode) LEFT JOIN {node__field_abstract} node__field_abstract ON node_field_data.nid = node__field_abstract.entity_id AND (node__field_abstract.deleted = :views_join_condition_16 AND node__field_abstract.langcode = node_field_data.langcode) WHERE (( (node_field_data.status = :db_condition_placeholder_0) AND (node_field_data.type IN (:db_condition_placeholder_1)) AND( (CONCAT_WSnode_field_data.titlenode__field_field_author.field_field_author_valuenode__field_editor.field_editor_valuenode__field_journal.field_journal_target_idnode__field_note.field_note_valuenode__field_organization.field_organization_valuenode__field_publisher.field_publisher_valuenode__field_school.field_school_valuenode__field_titlehtml.field_titlehtml_valuenode__field_abstract.field_abstract_value LIKE :db_condition_placeholder_2 ESCAPE '\\') )))) subquery; Array ( [:db_condition_placeholder_0] => 1 [:db_condition_placeholder_1] => publication [:db_condition_placeholder_2] => %sdfs% [:views_join_condition_0] => 0 [:views_join_condition_2] => 0 [:views_join_condition_4] => 0 [:views_join_condition_6] => 0 [:views_join_condition_8] => 0 [:views_join_condition_10] => 0 [:views_join_condition_12] => 0 [:views_join_condition_14] => 0 [:views_join_condition_16] => 0 )
If I add only one field to the filter, the error looks a bit different:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'CONCAT_WSnode_field_data.title' in 'where clause': SELECT COUNT(*) AS expression FROM (SELECT 1 AS expression FROM {node_field_data} node_field_data WHERE (( (node_field_data.status = :db_condition_placeholder_0) AND (node_field_data.type IN (:db_condition_placeholder_1)) AND( (CONCAT_WSnode_field_data.title LIKE :db_condition_placeholder_2 ESCAPE '\\') )))) subquery; Array ( [:db_condition_placeholder_0] => 1 [:db_condition_placeholder_1] => publication [:db_condition_placeholder_2] => %sdf% )
But it seems that in both cases there are some spaces missing when constructing the query.
I experience this on a new website currently under construction. Started with Drupal 8.0.2, then upgraded to 8.0.3.
This happens when using the "Contains any word" operator, not when using "Contains"
Comment | File | Size | Author |
---|---|---|---|
#63 | interdiff.txt | 1.66 KB | slasher13 |
#63 | combine_filter_word-2664530-63.patch | 6.21 KB | slasher13 |
#53 | interdiff.txt | 1.29 KB | slasher13 |
#53 | combine_filter_word-2664530-53.patch | 5.28 KB | slasher13 |
#52 | combine_filter_word-2664530-52.patch | 5.18 KB | slasher13 |
Comments
Comment #2
LendudeMoving to the right queue.
Comment #3
LendudeDo you have an export of this View? This is not a standard View since it uses an expression and a subquery. Do you have some steps to reproduce this?
Comment #4
andrew.trebble CreditAttribution: andrew.trebble at Portage CyberTech commentedI'm experiencing the same behaviour. All the other filters I put on the view are working correctly but the combined field filter causes the same error.
Using Drupal 8.0.5.
To recreate it I just needed to create a view, add a few fields and then add an exposed combined field filter that searches on several of the fields. Also add a regular filter on a single field. The single field filter works fine by itself. If I put a search term into the the combined field filter it dies with the SQL error as vakorol reported.
Through a little testing I think I was able to narrow it down a little. When the operator on the combined filter is "Contains any word" then the filter causes the crash. If I set it to "is equal to" then the filter doesn't crash.
EDIT: "Contains" operator works as well. It seems to be the "Contains any word" operator that causes a problem for me. I have not tested the other operators.
Hope that helps.
Comment #5
Lendude@andrew.trebble Yup that was the bit that did it, thanks!
Comment #6
LendudeHere is a failing test for this.
The reason it fails is that the Combine filter doesn't override the opContainsWord() method, but haven't managed to get that to work yet.
Comment #8
LendudeNot sure if this is the most elegant way to fix this, but it's a fix.
Comment #9
LendudeBumping to major since this results in a fatal error.
Comment #10
andrew.trebble CreditAttribution: andrew.trebble at Portage CyberTech commentedPatch in #8 worked for me. Thanks Lendude.
Comment #11
dawehnerNice!
Comment #12
catchNeeds docs, even if it's just @inheritdoc
Comment #13
LendudeDocs added. @inheritdoc wouldn't work since the parent method doesn't have any docs either (nor do any of the other ops methods, we should fix that...).
8.1.x patch should work for 8.2.x too.
Comment #14
dawehnerThank you @Lendude!!
Comment #15
alexpottMissing @param documentation
db_link() is deprecated but there's no inject database to easily use so this looks okay to me.
Is all of this properly tested?
Comment #16
David4514 CreditAttribution: David4514 commentedI'm also experiencing the same problem. The patch in #13 works for me. Thanks @Lendude.
Comment #17
Lendude15.1 Added.
15.2 Cool.
15.3 I updated the test to contain a 'phrase', so the preg_match_all() double quote logic is tested. Taking a closer look at it, it's unclear to me what the preg_split() is supposed to be doing anyway. Since the first preg_match_all() already splits on ' ', I don't see what the preg_split() is supposed to accomplish. Taking it out leaves the tests green, so unless somebody can think of a search sting to turn it red, lets leave it out.
8.1.x patch should apply to 8.2.x too.
Comment #19
alexpottDiscussed with @xjm and @catch. We agree that this is major because this results in an exception through normal usage of views.
Comment #20
LendudeForgot age... no change in the 8.0 patch, but reposting to keep everything together.
Comment #21
LendudeComment #22
David4514 CreditAttribution: David4514 commentedOn an 8.1.x-dev system I applied the patch from #20. My view is only using the "Contains All Word" variety of the filter, but it seems to work as expected.
Comment #24
ChristianAdamski CreditAttribution: ChristianAdamski as a volunteer commentedHad the same issue on 8.1.2. #20 fixed it.
Comment #25
catchThis needs a comment.
Why not just trim?
Comment #26
Lendude@catch thanks for the feedback! So something like this?
Comment #27
dawehnerNice improvement!
Comment #28
catchCommitted/pushed to 8.2.x and cherry-picked to 8.1.x, thanks!
Gave review credit to andrew.trebble (post commit) for the clear steps to reproduce.
Comment #31
xjmI think this issue may have broken postgres.
Comment #32
xjmTestbot doesn't like patches on fixed issues now apparently. Uploaded at #2756595: test issue.
The relevant CI fail: https://www.drupal.org/pift-ci-job/346519
Comment #35
xjmConfirmed; it fails on Postgres. I reverted the commit in both 8.2.x and 8.1.x.
Comment #36
LendudeWell that will teach me to add tests for other databases when messing with views query building.....thanks for the revert @xjm!
I know next to nothing about Postgres, so no idea how to fix this.
For people familiar with postgres but not views, this is the final query that is build by the test:
I assume the problem stems from using a CONCAT_WS inside a WHERE clause, but *shrug*
Comment #39
LendudeBleh, Postgres is case sensitive, well at least I now know how to run tests on postgres locally.....
This passes locally on postgres now.
Comment #40
dawehnerIts nice that the combined filter is able to filter something like this now.
Nitpick: maybe add a comma before 'when', so its clear "when encapsulated by double words" is just for sequences.
Potential follow-up: Replace
db_like
withDatabase::getConnection()->escapeLike($string)
whereever possible, in views.Comment #41
LendudeFixed comment by removing 'when' altogether, I think this is even clearer then adding the comma.
opened follow-up #2784739: Fix PostgreSQL operator in views
Comment #42
Lendudenow with an actual patch....
Comment #43
dawehnerNice!
Comment #46
LendudeUnrelated fails
Comment #47
catchThe postgres driver maps LIKE to ILIKE for normal condition queries: https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Database%...
We shouldn't introduce driver-specific logic here, but the behaviour should be the same IMO.
Having said that I see this a lot in StringFilter and Combine, so might need to be a follow-up bug report to look at all of those queries?
Comment #48
LendudeI updated the follow-up #2784739: Fix PostgreSQL operator in views to also fix the hardcoded LIKE and NOT LIKE instances. Since both mapConditionOperator and escapeLike would require the connection it's probably easiest to tackle both at the same time.
@catch would that address your concerns?
Comment #49
slasher13Did you mean that?
addresses #40.2 and revert changes from #39
Comment #52
slasher13Comment #53
slasher13Comment #54
LendudeI think this addresses the concerns catch had in #47, in the follow-up #2784739: Fix PostgreSQL operator in views we should update everything to use this pattern.
Tests are passing on all databases, so this looks good to me. Nice!
Comment #55
slasher13Comment #56
Anonymous (not verified) CreditAttribution: Anonymous commentedWhy do we use a complex expression as:
preg_match_all('/ (-?)("[^"]+"|[^" ]+)/i', ' ' . $this->value, $matches, PREG_SET_ORDER);
instead
without:
Comment #57
LendudeBecause I just copy/pasted it from
\Drupal\views\Plugin\views\filter\StringFilter::opContainsWord
and figured "if it ain't broke....."I think from a sanity point of view they should use the same regex and changing StringFilter is out of scope here, maybe open a follow-up if this lands and update both?
Comment #58
Anonymous (not verified) CreditAttribution: Anonymous commented@Lendude, thank you for answer and great work! And if we will refactoring it, I think that the next regexp will be enough:
/".+?"|\S+/
Also, maybe add description about "related words" ability to UI form build filter value. Because it is not quite clear to me, though I am ashamed to admit this :)
Comment #59
alexpottI think it is in scope to ensure that \Drupal\views\Plugin\views\filter\Combine::opContainsWord() uses the same regex as \Drupal\views\Plugin\views\filter\StringFilter::opContainsWord() because we're implementing an override of that method. The simplest thing to do is just add the regex as a class constant to StringFilter and then use it in both methods.
Everything else looks great and it is fantastic to be building up our views test coverage.
Comment #60
Anonymous (not verified) CreditAttribution: Anonymous commentedMaybe static method?
Example usage:
Comment #61
Lendude@vaplas Lets not add methods, that would make this an API change and push this to 8.3.x (or 8.4.x). I like the idea, I'm just not thrilled about the impact it has on fixing this. Lets just get this working.
I'd say just go with what @alexpott suggested, add the regex as a class contant to \Drupal\views\Plugin\views\filter\StringFilter and use that. Clean, concise, no BC breakage.
Comment #62
Anonymous (not verified) CreditAttribution: Anonymous commented@Lendude, I totally agree with you! I just wanted to know THE SECRET of the regexp :) Sorry!
Comment #63
slasher13addresses #59
Comment #64
Anonymous (not verified) CreditAttribution: Anonymous commentedAnd I also found something. The difference between the two patches reminiscent instructions "How to draw owl" :)
PS.
But, perhaps, the great @gordon just secretly used it (-?) to identify specific queries in Commerce :)
Comment #65
LendudeFeedback from #59 has been addressed, still passes on all supported databases, nice!
Comment #66
slasher13Comment #69
catchCommitted/pushed to 8.3.x and cherry-picked to 8.2.x, thanks!