The classcore/modules/views/src/Plugin/views/filter/Combine.php creates a CONCAT_WS() expression from an array of fields. When the array has only one field element, the separator will not be added. Instead, we can just return the field. MS SQL Server requires at least three arguments for CONCAT_WS() for this reason.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Beakerboy created an issue. See original summary.

Beakerboy’s picture

Status: Active » Needs review
FileSize
1.24 KB

Patch which uses the CONCAT_WS expression, only when there are multiple fields to concatenate together.

Beakerboy’s picture

I should probably change it to:

if ($count < 2)

because an empty field array makes just as little sense, and would probably yield an error with the existing code.

Beakerboy’s picture

Testing a combine filter with no fields in the array.

daffie’s picture

Status: Needs review » Needs work
  1. I think that a comment should be added to make clear why we do the if-statement with $count == 1.
  2. The added test does not test the added fix. Maybe you can test the generated query for the use of CONCAT_WS.
Beakerboy’s picture

1. Adding more comments is certainly a good idea. In fact, if you have any idea why the foreach loop exists I would love to hear it. To me it reads like the foreach loop adds an additional single space character to the array, but only between existing fields. The implode will turn the array into a comma separated string. It seems like it could be simplified by getting rid of the foreach loop and changing the implosion to:

expression = implode(', \’ \’,  ', $separated_fields);

Then this all is used as the argument list for CONCAT_WS, which adds a single space between each argument. Again, we can instead just use a triple-space as the separator...but then again, why are we using three spaces anyways? The whole reason for adding the single-space as an argument seems pointless.

2. The new test was to check if MySQL was fine executing a CONCAT_WS with NO fields, just a seperator. I was thinking it would fail. I have to figure out how everything should work given this fact. Should the whole expression be dropped when the filter contains no fields? The core test FilterCombineTest::testNonFieldsRow() uses only one field, so it will follow the new execution path while all the other tests in the class will use the old one. The new execution path will be covered.

daffie’s picture

Status: Needs work » Needs review
FileSize
1.26 KB
2.31 KB

For #6.1.1: You are right. We can get rid of the foreach loop. I have changed the expression to make it more readable to: $expression = implode(", ' ', ", $separated_fields);.

For #6.1.2: If we do: "The whole reason for adding the single-space as an argument seems pointless." We are changing the way the views filter works. I would do that in another issue. It will make this a far bigger change and would be out of scope for this issue.

For #6.2: Lets keep the change as simple as possible. That way less can go wrong.

daffie’s picture

The last submitted patch, 7: 3131379-7-test-only-should-fail.patch, failed testing. View results

Beakerboy’s picture

Status: Needs review » Reviewed & tested by the community

I’ve reviewed the patch in #8 and successfully tested with sqlsrv.

Beakerboy’s picture

I found an old issue for this that can probably be closed when this is.

Lendude’s picture

#6.1.2 the 3-space logic is so that search strings that contain spaces are still only matched to single field values and not to multi-field values that exist only because we do the concatenation/LIKE trick. Like it or hate it, the 3 spaces are part of the trick :)

Beakerboy’s picture

@lendude, why not just use three spaces as a seperator between fields instead of adding in a single-space string between fields and then concatinating another single space. Is it just to ensure there are six (or four?) spaces between fields when one is NULL? And if so, why is that important?

alexpott’s picture

Can we open an issue to document this 3 space trick here - or at least point to documentation of it. I looked at this code and was thinking what's the point of the spaces.

Lendude’s picture

The three spaces 'why' is discussed on #2405907: Views combined filters add redundant separators in CONCAT_WS(). Feel free to open that back up and continue the discussion or repurpose it for a doc block update.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed and pushed 5c72f9f050 to 9.1.x and 4d80b317ef to 9.0.x and 8920b79e53 to 8.9.x. Thanks!

Okay I've added a comment based on #12 as having something here is definitely better than nothing.

diff --git a/core/modules/views/src/Plugin/views/filter/Combine.php b/core/modules/views/src/Plugin/views/filter/Combine.php
index ad87861513..3492043e92 100644
--- a/core/modules/views/src/Plugin/views/filter/Combine.php
+++ b/core/modules/views/src/Plugin/views/filter/Combine.php
@@ -84,6 +84,10 @@ public function query() {
         $expression = reset($fields);
       }
       else {
+        // Multiple fields are separated by 3 spaces so that so that search
+        // strings that contain spaces are still only matched to single field
+        // values and not to multi-field values that exist only because we do
+        // the concatenation/LIKE trick.
         $expression = implode(", ' ', ", $fields);
         $expression = "CONCAT_WS(' ', $expression)";
       }

I've not backported to 8.8.x due to #3135747: assertStringContainsString() and related BC layer in 8.8.x does not work as expected

  • alexpott committed 5c72f9f on 9.1.x
    Issue #3131379 by daffie, Beakerboy, Lendude, alexpott: CONCAT_WS...

  • alexpott committed 4d80b31 on 9.0.x
    Issue #3131379 by daffie, Beakerboy, Lendude, alexpott: CONCAT_WS...
alexpott’s picture

Version: 8.8.x-dev » 8.9.x-dev

  • alexpott committed 8920b79 on 8.9.x
    Issue #3131379 by daffie, Beakerboy, Lendude, alexpott: CONCAT_WS...
Beakerboy’s picture

@lendude
3 spaces makes sense in that regard, but the method of creating it is odd. Why is the function adding a single-space As a field, and then adding another in the CONCAT_WS. Why not just add three spaces between the actual fields?

Like this:


    if ($fields) {
       $count = count($fields);
      if ($count == 1) {
       $expression = $fields[0];
      }
      else {
        $expression = implode(', ', $fields);
        $expression = "CONCAT_WS('   ', $expression)";
       }
    }
Lendude’s picture

Assigned: Beakerboy » Unassigned

@Beakerboy like I said, feel free to open up the other issue. I really don't care how they are added, as long as they are :)

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.