Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Fix for omition of the glue on WHERE after the first call in query builder #956

Closed
wants to merge 13 commits into from

7 participants

@Naouak

As discussed on the mailing list, here is a first fix for the where of the query builder.
It should permit usage of AND and OR in a cleanier way :
$q->where("a = 1")->where("a = 2","OR")->where("b = 1","AND");

will generate :
WHERE a = 1 OR a = 2 AND b = 1

before this patch it would have generated :
WHERE a = 1 AND a = 2 AND b = 1

UPDATE :
Nested is now also available:
$q->where(array("a = 1","a=2"),"OR");
will generate:
WHERE (a = 1 OR a=2)

$q->$q->where(array("a = 1","a=2",array("b=1","b=2")),"OR");
will generate:
WHERE (a=1 OR a=2 OR (b=1 OR b=2))

and finally to do nesting with multiple glue:
$q->$q->where(array("a = 1","a=2",array("b=1",array("b=2","glue"=>" AND ")),"OR");
will generate:
WHERE (a=1 OR a=2 OR (b=1 AND b=2))

See added test to understand how it works.

I also fixed some test on JDatabaseQueryElementTest that were not working on windows because of the difference of end of line characters between systems.

@joomla-jenkins
Collaborator

Unit testing complete. There were 1 failures and 0 errors from 2197 tests and 11594 assertions.
Checkstyle analysis reported 163 warnings and 10 errors.

@chdemko

You have to correct some coding style:

FILE: ...hdemko/Code/php/joomla/platform/git/libraries/joomla/database/query.php
--------------------------------------------------------------------------------
FOUND 10 ERROR(S) AFFECTING 7 LINE(S)
--------------------------------------------------------------------------------
   68 | ERROR | No space found after comma in function call
   80 | ERROR | The comments for parameters $elements (1) and $glue (2) do not
      |       | align
   80 | ERROR | Expected 2 spaces after the longest type
   80 | ERROR | Expected 2 spaces after the longest variable name
   88 | ERROR | Expected "if (...)\n...{...}\n...else\n"; found
      |       | "if(...)\n...{...}\n...else\n"
   97 | ERROR | No space found after comma in function call
  102 | ERROR | Please put a space between the // and the start of comment
      |       | text; found "//Won't add the glue on the first element"
  103 | ERROR | Expected "if (...)\n...{...}\n...else\n"; found
      |       | "if(...)\n...{...}\n...else\n"
  103 | ERROR | The use of function sizeof() is forbidden; use count() instead
 1479 | ERROR | No space found after comma in function call
--------------------------------------------------------------------------------
@AmyStephen

Above, it is mentioned:

$q->where("a = 1")->where("a = 2","OR")->where("b = 1","AND");

generates:

WHERE a = 1 OR a = 2 AND b = 1

My concern is that the results produce an ambiguous where clause. It's not clear if the developer intended:

A. WHERE (a = 1 OR a = 2) AND b = 1 ?
or
B. WHERE a = 1 OR (a = 2 AND b = 1) ?

Each are very different queries.

Right now, there is a way to specify connectors in an intentional manner within the same where clause.

A. $q->where("((a = 1 OR a = 2) AND b = 1)");
or
B. $q->where("(a = 1 OR (a = 2 AND b = 1)");

Unless I am missing something, I think Joomla has a better approach for developers to use right now. Maybe I am not understanding, though.

@chdemko

The error in the tests is:

1) JDatabaseQueryElementTest::test__Construct with data set "array-element" (array('FROM', array('field1', 'field2'), ','), array('FROM', array('field1', 'field2'), ','))
Line 173 elements should be set
Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
 Array (
     0 => 'field1'
-    1 => 'field2'
+    1 => ','
+    2 => 'field2'
 )

tests/suite/joomla/database/JDatabaseQueryElementTest.php:174
@Naouak

Code style and test unit error should be fixed now.

AmyStephen:
With the old version you weren't even able to set a query with multiple AND and OR. The where in your query would have been either with only AND or OR.

I have some code to do nested AND and OR but it's kinda complicated to use. You can have a look at it there :
https://github.com/Naouak/joomla-platform/blob/feature/nested-where/libraries/joomla/database/query.php

I didn't tested it yet but it should give you the possibility to define any nesting of your query.

Naouak added some commits
@Naouak Naouak Fixing test for windows
Test were using "\n" for assertion and function used PHP_EOL for line ending.
On windows every test failed because of that.
6cca79b
@Naouak Naouak Adding support for nesting into where function.
Usage should be :
$q->where(
array(
"a=1",
"b=1",
array(
"c=1",
array("c=2","glue"=>"OR")
),
"AND");

Should generate :
WHERE (a=1 AND b=1 AND (c=1 OR c=2))
e534af4
@Naouak Naouak Fixing forgotten assignation in the constructor.
Modifying where function according to new constructor parameters.
Updating Doc Block.
051ecd7
@Naouak Naouak Adding a test case for nesting of elements in a JDatabaseQueryElement
eba6e69
@Naouak Naouak Merge branch 'master' into feature/nested-where
fa8b92c
@Naouak

UPDATE :
Nested is now also available:
$q->where(array("a = 1","a=2"),"OR");
will generate:
WHERE (a = 1 OR a=2)

$q->$q->where(array("a = 1","a=2",array("b=1","b=2")),"OR");
will generate:
WHERE (a=1 OR a=2 OR (b=1 OR b=2))

and finally to do nesting with multiple glue:
$q->$q->where(array("a = 1","a=2",array("b=1",array("b=2","glue"=>" AND ")),"OR");
will generate:
WHERE (a=1 OR a=2 OR (b=1 AND b=2))

See added test to understand how it works.

I also fixed some test on JDatabaseQueryElementTest that were not working on windows because of the difference of end of line characters between systems.

@AmyStephen

@Naouak said: "AmyStephen:
With the old version you weren't even able to set a query with multiple AND and OR. The where in your query would have been either with only AND or OR."

Here's how I do a complex where statement with JDatabaseQuery:

$db = JFactory::getDbo();
$query = $db->getQuery(true);

$query->select($db->qn('id'));
$query->select($db->qn('title'));
$query->from($db->qn('#__categories'));
$query->where('(id > 6 OR (id < 4 AND title = "Uncategorised"))');

echo $query->__toString();

$db->setQuery($query);
$results = $db->loadObjectList();

That produces this query statement (and you can see both the OR and the AND are present):

SELECT id,title FROM #__categories WHERE (id > 6 OR (id < 4 AND title = "Uncategorised"))

And, it returns the following expected results (plain Joomla 2.5 installation with no sample data):

array(3) {
[0]=>
object(stdClass)#134 (2) {
["id"]=>
string(1) "2"
["title"]=>
string(13) "Uncategorised"
}
[1]=>
object(stdClass)#116 (2) {
["id"]=>
string(1) "3"
["title"]=>
string(13) "Uncategorised"
}
[2]=>
object(stdClass)#156 (2) {
["id"]=>
string(1) "7"
["title"]=>
string(13) "Uncategorised"
}
}

Give it a try and see if that works for you.

@Naouak

@AmyStephen The aim of my pull request is to avoid having to write directly the content of the query. As you may know, SQL engine don't always works the same and the aim of that piece of code is to be able to do the same thing with different SQL engine.

If I extrapolate your answer, it's like saying "you could write : $db->setQuery("SELECT * FROM #__table WHERE a = 1 AND (b =1 AND c = 1)"); ".
If you wan't to be sure to be able to support the more SQL engine possible without having to write queries for each, you have to get an abstract layer like the one I added.

@AmyStephen

No, the other parts of the query do not need to be written together and my example doesn't do that, nor does it require it.

When it comes to the WHERE clause and combining OR's and AND's (and NOT's), then it is logic, not JDatabaseQuery, that requires the statement be expressed as a whole in order to produce expected results.

Your first patch produces ambiguous results, so I would be disappointed to see that introduced to core.

Your second patch attempts to address that problem but, in my thinking, presents a far more complex approach to developing the query that many developers will find difficult:

$q->where(array("a = 1","a=2",array("b=1",array("b=2","glue"=>" AND ")),"OR");

Whereas the existing method is pretty straightforward:

$q->where('(a=1 OR a=2 OR (b=1 AND b=2))');

Anyway, that's just my 2 cents on this.

Maybe what is missing is the ability to define parenthesis for grouping statements? That could be handy.

Thanks for working on this. Please don't be discouraged by my comments. It's just how I see it. No big deal. :)

@AmyStephen

@gpongelll - if I am understanding correctly, my response is #956 (comment)

@Naouak

@AmyStephen
The function before my patch is : where($elements,$glue)
where elements is a string or an array of string and glue the piece that will be before the string and between elements of the array. The glue parameter was only taken into account on the first call of the where function.
If you for example put OR as a glue on your first call, then the where parameters would be only composed of "OR" and you couldn't change that.
My pull request was to correct that behavior which seems to be a bug to me.

Then you came and said it was ambiguous because of missing parameters. So I proposed an update of my patch that permit to define nested parameters so parenthesis would be addable.

Old way of writing the code is still available as my code is fully backward compatible (that was my aim) but in my opinion, it's not a clean way to do it as it's like using an ORM for nothing.

@AmyStephen

@Naouak said "If you for example put OR as a glue on your first call, then the where parameters would be only composed of "OR" and you couldn't change that."

That makes sense to me since predictable results can be produced by always using an AND or always using an OR in a set of WHERE clauses where there is no parenthesis.

Do this IF a or b or c or d.

Do this IF a and b and c and d.

Both are clear. It's when you vary the AND and the OR that you must provide a grouping mechanism for the where clauses.

This is just logic.

@gpongelli

@AmyStephen
You're right about ambiguous result, but look at my test file to know how I've handled these case.

To have
WHERE (a = 1 OR a = 2) AND b = 1
the code is
$q->where('(a = 1 OR a = 2)')->where('b = 1');

The other case is handled with
$q->where('a = 1')->where('(a = 2 AND b = 1)', 'OR');

Easy to understand, simple to do, without use of array that give harder call and harder handling.

Eng. Gabriele Pongelli

@joomla-jenkins
Collaborator

Build triggered by changes to the head.

Unit testing complete. There were 2 failures and 1 errors from 2201 tests and 11594 assertions.
Checkstyle analysis reported 165 warnings and 12 errors.

@joomla-jenkins
Collaborator

Build triggered by changes to the head.

Unit testing complete. There were 2 failures and 1 errors from 2201 tests and 11594 assertions.
Checkstyle analysis reported 165 warnings and 12 errors.

@joomla-jenkins
Collaborator

Build triggered by changes to the head.

Unit testing complete. There were 0 failures and 0 errors from 2201 tests and 11599 assertions.
Checkstyle analysis reported 165 warnings and 12 errors.

@eddieajau eddieajau commented on the diff
...s/suite/joomla/database/JDatabaseQueryElementTest.php
@@ -127,25 +127,25 @@ public function dataTestToString()
'FROM',
'table1',
',',
- "\nFROM table1"
+ PHP_EOL . "FROM table1"

Could you put the all the PHP_EOL changes into a separate pull? I think that will fix up some problems we are having with windows testing in general and I'd like to get those in while your pull is being reviewed. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@eddieajau

I like where this conversation is heading. Here's my "thinking out loud".

I think we should consider the "glue" to apply to how "this piece" glues to the "last piece", not the glue for all pieces. There is a risk that could introduce a B/C issue if someone had accidentally mixed glues (but it never worked because only the first one was honored). So if that was the case the following query:

$q->where('a=1')
    ->where('b=2')
    ->where('c=3', 'OR')
    ->where('d=4')

could render as:
WHERE a=1 AND b=2 OR c=3 AND d=4

The next step could be that we add a third argument to enclose an array in ( )'s, for example:

$q->where('a=1')
    ->where('b=2')
    ->where(array('c=3', 'd=4'), 'OR', true)

might render as:
WHERE a=1 AND b=2 OR (c=3 ?? d=4)

The obvious problem their is what should ?? be - AND or OR. Ok, we could add a fourth argument (sigh):

$q->where('a=1')
    ->where('b=2')
    ->where(array('c=3', 'd=4'), 'OR', true, 'AND')

might render as:
WHERE a=1 AND b=2 OR (c=3 AND d=4)

Getting messy so let's start over. So, what if we had some new gluer methods called and and or that supported multiple arguments, and did something funky like this:

$q->where(
    $q->and(
        'a=1',
        'b=2',
        $q->or(array('c=3', 'd=4'), 'e=5')
    )
);

rendering to:
WHERE a=1 AND b=2 AND (c=3 OR d=4) OR e=5

the special rule being that an array is always enclosed in ( ). The above should actually be equivalent to:

$q->where('a=1')
    ->where('b=2')
    ->where( 
        $q->or(array('c=3', 'd=4'), 'e=5')
    );

or, if we changed the "glue rule" as I mentioned initially (which is probably a good thing to do anyway), we could have:

$q->where('a=1')
    ->where('b=2')
    ->where( 
        $q->or(array('c=3', 'd=4'))
    );
    ->where('e=5', 'OR');

Just throwing it out there.

@elinw elinw commented on the diff
libraries/joomla/database/query.php
((73 lines not shown))
}
else
{
- $this->elements = array_merge($this->elements, array($elements));
+ //Won't add the glue on the first element of the list or of a nesting
@elinw
elinw added a note

Please put a space after the //.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@elinw elinw commented on the diff
libraries/joomla/database/query.php
((50 lines not shown))
- $this->append($elements);
+ $this->append($elements,$glue,$nested);
@elinw
elinw added a note

You need spaces after commas in a bunch of places.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@AmyStephen

rendering to:
WHERE a=1 AND b=2 AND (c=3 OR d=4) OR e=5

Andrew - after staring at it awhile, it dawned on me that the result you shared in your example is also an ambiguous where statement.

If bringing this improvement is a goal, though, maybe we should look at what others are doing? I have not looked very closely at the class, probably should do that.

In all honesty, I'm not unsatisfied with the current approach. JDatabaseQuery is very easy to work with and part of it's beauty is that is should be easier for even frontend folks to pick up. Hate to see it get overly complex, either.

Thanks for looking into this (and for building it.)

@eddieajau

Do you mean to say the "result" is ambiguous to MySQL, or the result it's different from what you'd expect given the code example?

Whatever the case, you can construct your entire string by hand if you want - it's all opt in for dev's that see a need for it. But I've come across cases where building nested OR's with implodes and explodes of arrays is just painful to do, not to mention unreadable.

@AmyStephen

No, just simply that in the example shared it's not possible to know for certain what the developer intended:

WHERE a=1 AND b=2 AND (c=3 OR d=4) OR e=5

I suppose MySQL would interpret the results as this (I think, didn't test):

WHERE (a=1 AND b=2 AND (c=3 OR d=4)) OR e=5

Maybe that's good enough? I suppose the tool doesn't have to prevent ambiguous SQL? I might be overthinking this. They can build bad joins - ask for columns that don't exist, and so on.

Anyway, I use JDatabaseQuery all the time. Rarely have I had to break out into native SQL. It's useful because it's simple and powerful. I would just be careful not to lose that.

@eddieajau

Operator precedence is well defined otherwise the whole shebang would fall apart ;)

http://dev.mysql.com/doc/refman/5.0/en/operator-precedence.html

@AmyStephen

Of course. Good night.

@elinw

Andrew about the parentheses comment ...I was working with WHERE and I was wondering why it doesn't support where() the way some other elements to .. i.e.
in some elementdng () to the end of it will automatically wrap the clause in parentheses.
So
$q->where()(array('a=1',b=2'),'OR')
would be
(a=1 OR b=2)

@AmyStephen

At the very least, what Elin is proposing should produce predictable and accurate results.

Personally, I think $q->where('(a = 1 and b = 2)'); is far more intuitive, but maybe there is a use case for the array approach in very complex queries.

@elinw

The arrays are basically if you are using OR as the glue or if you have multiple clauses they save code. If you use AND as the glue you can leave out array since it defaults to AND. i.e if you are using AND normally you would just write
$q->where('a=2','b=3');

at least if you are trying to take advantage of jdatabasequery and not just use it as a wrapper for strings. Of course the cool think is you can indeed stay old school and have the string input.

Anyway I think the element() picking up that is supposed to wrap is a really great thing, it made it possible to do multiple unions with ) UNION ( as the glue but for whatever reason it does not seem to work for where.

@AmyStephen

@elinw -

Your comment makes me wonder if maybe you have identified a new issue.

This issue is intended to allow a developer to change the glue between WHERE clauses, thus combining OR's and AND's.

The challenge we were discussing was how to do so in a predictable way.

After reading your second post, it now sounds like maybe you are discussing another issue related to the WHERE clause - but not related to changing the glue. If so, it's probably a different issue.

@elinw

I was commenting on Andrew's suggestion (note the "Andrew about the parentheses comment .." beginning of my comment specifically addresses his comment and him no one else) about extending the proposed solution.

@elinw

@Naouak This has been sitting unmergable for a while. Do you want to rebase?

@eddieajau

I'm going to close this pull request a) because it's not mergable, and b) because I don't think there is a clear consensus on how this problem should be solved. I suggest, to move the idea behind the pull request forward, a thread is opened on the platform mailing list to see if we can reach an agreement on how to do AND and OR nesting/grouping and then write the code from there.

@eddieajau eddieajau closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 3, 2012
  1. @Naouak

    Adding a way to be able to change the glue when appending elements. W…

    Naouak authored
    …ill be usefull for WHERE statement in which the glue can either be "AND" or "OR"
  2. @Naouak
  3. @Naouak
  4. @Naouak

    Adding nesting

    Naouak authored
  5. @Naouak
  6. @Naouak

    Fixing code style

    Naouak authored
  7. @Naouak
  8. @Naouak

    Fixing test for windows

    Naouak authored
    Test were using "\n" for assertion and function used PHP_EOL for line ending.
    On windows every test failed because of that.
  9. @Naouak

    Adding support for nesting into where function.

    Naouak authored
    Usage should be :
    $q->where(
    array(
    "a=1",
    "b=1",
    array(
    "c=1",
    array("c=2","glue"=>"OR")
    ),
    "AND");
    
    Should generate :
    WHERE (a=1 AND b=1 AND (c=1 OR c=2))
  10. @Naouak

    Fixing forgotten assignation in the constructor.

    Naouak authored
    Modifying where function according to new constructor parameters.
    Updating Doc Block.
  11. @Naouak
  12. @Naouak
Commits on Mar 4, 2012
  1. @Naouak
This page is out of date. Refresh to see the latest.
View
98 libraries/joomla/database/query.php
@@ -31,27 +31,49 @@ class JDatabaseQueryElement
protected $elements = null;
/**
+ * @var array An array of elements with glues.
+ * @since 11.1
+ */
+ protected $elements_with_glues = null;
+
+ /**
* @var string Glue piece.
* @since 11.1
*/
protected $glue = null;
/**
+ * @var string Start of a nesting of element
+ * @since 12.1
+ */
+ protected $nesting_start = null;
+
+ /**
+ * @var string End of a nesting of element
+ * @since 12.1
+ */
+ protected $nesting_end = null;
+
+ /**
* Constructor.
*
- * @param string $name The name of the element.
- * @param mixed $elements String or array.
- * @param string $glue The glue for elements.
- *
+ * @param string $name The name of the element.
+ * @param mixed $elements String or array.
+ * @param string $glue The default glue for elements.
+ * @param string $nesting_start Starting character for nesting
+ * @param string $nesting_end Ending character for nesting
+ * @param bool $nested If there is nesting for first added elements
* @since 11.1
*/
- public function __construct($name, $elements, $glue = ',')
+ public function __construct($name, $elements, $glue = ',',$nesting_start = '',$nesting_end = '',$nested = false)
{
$this->elements = array();
$this->name = $name;
$this->glue = $glue;
+ $this->nesting_start = $nesting_start;
+ $this->nesting_end = $nesting_end;
- $this->append($elements);
+ $this->append($elements,$glue,$nested);
@elinw
elinw added a note

You need spaces after commas in a bunch of places.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
}
/**
@@ -65,32 +87,80 @@ public function __toString()
{
if (substr($this->name, -2) == '()')
{
- return PHP_EOL . substr($this->name, 0, -2) . '(' . implode($this->glue, $this->elements) . ')';
+ return PHP_EOL . substr($this->name, 0, -2) . '(' . implode('', $this->elements_with_glues) . ')';
}
else
{
- return PHP_EOL . $this->name . ' ' . implode($this->glue, $this->elements);
+ return PHP_EOL . $this->name . ' ' . implode('', $this->elements_with_glues);
}
}
/**
* Appends element parts to the internal list.
*
- * @param mixed $elements String or array.
+ * @param mixed $elements String or array.
+ * @param string $glue A glue that will replace the default one if different from null.
+ * @param boolean $nesting In case of an array, do we need to nest it ?
+ * @param boolean $nesting_start To notify the function that the last action done was the starting of a nesting
*
* @return void
*
* @since 11.1
*/
- public function append($elements)
+ public function append($elements, $glue = null, $nesting = false, $nesting_start = false)
{
+ if($glue === null)
+ {
+ $glue = $this->glue;
+ }
+
if (is_array($elements))
{
- $this->elements = array_merge($this->elements, $elements);
+ //Won't add the glue on the first element of the list or of a nesting
+ if (count($this->elements) > 0 && $nesting_start == false)
+ {
+ $this->elements_with_glues[] = $glue;
+ }
+
+ if ($nesting)
+ $this->elements_with_glues[] = $this->nesting_start;
+
+ $firstelement = true;
+ foreach ($elements as $element)
+ {
+ if (is_array($element) && isset($element["glue"]))
+ {
+ $this->append(
+ $element[0],
+ $element['glue'],
+ $nesting,
+ $firstelement?true:false
+ );
+ }
+ else
+ {
+ $this->append(
+ $element,
+ $glue,
+ $nesting,
+ $firstelement?true:false
+ );
+ }
+ $firstelement = false;
+ }
+
+ if ($nesting)
+ $this->elements_with_glues[] = $this->nesting_end;
}
else
{
- $this->elements = array_merge($this->elements, array($elements));
+ //Won't add the glue on the first element of the list or of a nesting
@elinw
elinw added a note

Please put a space after the //.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ if (count($this->elements) > 0 && $nesting_start == false)
+ {
+ $this->elements_with_glues[] = $glue;
+ }
+ $this->elements[] = $elements;
+ $this->elements_with_glues[] = $elements;
}
}
@@ -1458,11 +1528,11 @@ public function where($conditions, $glue = 'AND')
if (is_null($this->where))
{
$glue = strtoupper($glue);
- $this->where = new JDatabaseQueryElement('WHERE', $conditions, " $glue ");
+ $this->where = new JDatabaseQueryElement('WHERE', $conditions, " $glue ", "(", ")", true);
}
else
{
- $this->where->append($conditions);
+ $this->where->append($conditions," $glue ", true);
}
return $this;
View
109 tests/suite/joomla/database/JDatabaseQueryElementTest.php
@@ -127,25 +127,25 @@ public function dataTestToString()
'FROM',
'table1',
',',
- "\nFROM table1"
+ PHP_EOL . "FROM table1"

Could you put the all the PHP_EOL changes into a separate pull? I think that will fix up some problems we are having with windows testing in general and I'd like to get those in while your pull is being reviewed. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
),
array(
'SELECT',
array('column1', 'column2'),
',',
- "\nSELECT column1,column2"
+ PHP_EOL . "SELECT column1,column2"
),
array(
'()',
array('column1', 'column2'),
',',
- "\n(column1,column2)"
+ PHP_EOL . "(column1,column2)"
),
array(
'CONCAT()',
array('column1', 'column2'),
',',
- "\nCONCAT(column1,column2)"
+ PHP_EOL . "CONCAT(column1,column2)"
),
);
}
@@ -265,4 +265,105 @@ public function test__clone_object()
$this->assertFalse($baseElement === $cloneElement);
$this->assertFalse($baseElement->testObject === $cloneElement->testObject);
}
+
+ /**
+ * Tests case for nesting of elements.
+ *
+ * @return array
+ *
+ * @since 12.1
+ */
+ public function dataTestNested()
+ {
+ return array(
+ array(
+ 'WHERE',
+ array(
+ "a=1",
+ "b=1"
+ ),
+ 'AND',
+ PHP_EOL . "WHERE (a=1 AND b=1)"
+ ),
+ array(
+ 'WHERE',
+ array(
+ "a=1",
+ "b=1",
+ array(
+ "c=1",
+ array(
+ "c=2",
+ "glue" => " OR "
+ )
+ )
+ ),
+ 'AND',
+ PHP_EOL . "WHERE (a=1 AND b=1 AND (c=1 OR c=2))"
+ ),
+ array(
+ 'WHERE',
+ array(
+ "a=1",
+ "b=1",
+ array(
+ "c=1",
+ array(
+ array(
+ "c=2",
+ "d=1"
+ ),
+ "glue" => " OR "
+ )
+ )
+ ),
+ 'AND',
+ PHP_EOL . "WHERE (a=1 AND b=1 AND (c=1 OR (c=2 OR d=1)))"
+ ),
+ array(
+ 'WHERE',
+ array(
+ "a=1",
+ "b=1",
+ array(
+ "c=1",
+ array(
+ array(
+ "c=2",
+ array(
+ "d=1",
+ "glue" => " AND "
+ )
+ ),
+ "glue" => " OR "
+ )
+ )
+ ),
+ 'AND',
+ PHP_EOL . "WHERE (a=1 AND b=1 AND (c=1 OR (c=2 AND d=1)))"
+ )
+ );
+ }
+
+ /**
+ * Tests JDatabaseQueryElement::__constructor for nesting of elements.
+ *
+ * @param string $name name of the element
+ * @param mixed $elements append elements value
+ * @param string $glue default glue
+ * @param string $expected expected string generated by __toString
+ *
+ * @dataProvider dataTestNested
+ *
+ * @since 12.1
+ */
+ public function testNested($name, $elements, $glue, $expected)
+ {
+ $e = new JDatabaseQueryElement($name, $elements, " $glue ","(",")", true);
+
+ $this->assertThat(
+ (string) $e,
+ $this->equalTo($expected)
+ );
+ }
}
View
66 tests/suite/joomla/database/JDatabaseQueryTest.php
@@ -140,7 +140,7 @@ public function test__toStringFrom_subquery()
$this->assertThat(
(string) $q,
- $this->equalTo("\nSELECT col\nFROM ( \nSELECT col2\nFROM table\nWHERE a=1 ) AS `alias`")
+ $this->equalTo("" . PHP_EOL . "SELECT col" . PHP_EOL . "FROM ( " . PHP_EOL . "SELECT col2" . PHP_EOL . "FROM table" . PHP_EOL . "WHERE a=1 ) AS `alias`")
);
}
@@ -161,14 +161,14 @@ public function test__toStringInsert_subquery()
$this->assertThat(
(string) $q,
- $this->equalTo("\nINSERT INTO table\n(col)\n(\nSELECT col2\nWHERE a=1)")
+ $this->equalTo("" . PHP_EOL . "INSERT INTO table" . PHP_EOL . "(col)" . PHP_EOL . "(" . PHP_EOL . "SELECT col2" . PHP_EOL . "WHERE a=1)")
);
$q->clear();
$q->insert('table')->columns('col')->values('3');
$this->assertThat(
(string) $q,
- $this->equalTo("\nINSERT INTO table\n(col) VALUES \n(3)")
+ $this->equalTo("" . PHP_EOL . "INSERT INTO table" . PHP_EOL . "(col) VALUES " . PHP_EOL . "(3)")
);
}
@@ -187,7 +187,7 @@ public function test__toStringYear()
$this->assertThat(
(string) $q,
- $this->equalTo("\nSELECT YEAR(`col`)\nFROM table")
+ $this->equalTo("" . PHP_EOL . "SELECT YEAR(`col`)" . PHP_EOL . "FROM table")
);
}
@@ -206,7 +206,7 @@ public function test__toStringMonth()
$this->assertThat(
(string) $q,
- $this->equalTo("\nSELECT MONTH(`col`)\nFROM table")
+ $this->equalTo("" . PHP_EOL . "SELECT MONTH(`col`)" . PHP_EOL . "FROM table")
);
}
@@ -225,7 +225,7 @@ public function test__toStringDay()
$this->assertThat(
(string) $q,
- $this->equalTo("\nSELECT DAY(`col`)\nFROM table")
+ $this->equalTo("" . PHP_EOL . "SELECT DAY(`col`)" . PHP_EOL . "FROM table")
);
}
@@ -244,7 +244,7 @@ public function test__toStringHour()
$this->assertThat(
(string) $q,
- $this->equalTo("\nSELECT HOUR(`col`)\nFROM table")
+ $this->equalTo("" . PHP_EOL . "SELECT HOUR(`col`)" . PHP_EOL . "FROM table")
);
}
@@ -263,7 +263,7 @@ public function test__toStringMinute()
$this->assertThat(
(string) $q,
- $this->equalTo("\nSELECT MINUTE(`col`)\nFROM table")
+ $this->equalTo("" . PHP_EOL . "SELECT MINUTE(`col`)" . PHP_EOL . "FROM table")
);
}
@@ -282,7 +282,7 @@ public function test__toStringSecond()
$this->assertThat(
(string) $q,
- $this->equalTo("\nSELECT SECOND(`col`)\nFROM table")
+ $this->equalTo("" . PHP_EOL . "SELECT SECOND(`col`)" . PHP_EOL . "FROM table")
);
}
@@ -308,13 +308,13 @@ public function test__toStringSelect()
$this->assertThat(
(string) $q,
$this->equalTo(
- "\nSELECT a.id" .
- "\nFROM a" .
- "\nINNER JOIN b ON b.id = a.id" .
- "\nWHERE b.id = 1" .
- "\nGROUP BY a.id" .
- "\nHAVING COUNT(a.id) > 3" .
- "\nORDER BY a.id"
+ "" . PHP_EOL . "SELECT a.id" .
+ "" . PHP_EOL . "FROM a" .
+ "" . PHP_EOL . "INNER JOIN b ON b.id = a.id" .
+ "" . PHP_EOL . "WHERE b.id = 1" .
+ "" . PHP_EOL . "GROUP BY a.id" .
+ "" . PHP_EOL . "HAVING COUNT(a.id) > 3" .
+ "" . PHP_EOL . "ORDER BY a.id"
),
'Tests for correct rendering.'
);
@@ -340,10 +340,10 @@ public function test__toStringUpdate()
$this->assertThat(
(string) $q,
$this->equalTo(
- "\nUPDATE #__foo AS a" .
- "\nINNER JOIN b ON b.id = a.id" .
- "\nSET a.id = 2" .
- "\nWHERE b.id = 1"
+ "" . PHP_EOL . "UPDATE #__foo AS a" .
+ "" . PHP_EOL . "INNER JOIN b ON b.id = a.id" .
+ "" . PHP_EOL . "SET a.id = 2" .
+ "" . PHP_EOL . "WHERE b.id = 1"
),
'Tests for correct rendering.'
);
@@ -358,7 +358,7 @@ public function test__toStringUnion()
$this->assertThat(
(string) $q->union,
$this->equalTo(
- "\nUNION (SELECT id FROM a)"
+ "" . PHP_EOL . "UNION (SELECT id FROM a)"
),
'Tests union for correct rendering.'
);
@@ -761,7 +761,7 @@ public function testDump()
$q->dump(),
$this->equalTo(
'<pre class="jdatabasequery">' .
- "\nSELECT *\nFROM foo" .
+ "" . PHP_EOL . "SELECT *" . PHP_EOL . "FROM foo" .
'</pre>'
),
'Tests that the dump method replaces the prefix correctly.'
@@ -1351,7 +1351,7 @@ public function testSet()
$this->assertThat(
trim($q->set),
- $this->identicalTo("SET foo = 1\n\t, bar = 2"),
+ $this->identicalTo("SET foo = 1" . PHP_EOL . "\t, bar = 2"),
'Tests set with an array.'
);
@@ -1367,7 +1367,7 @@ public function testSet()
$this->assertThat(
trim($q->set),
- $this->identicalTo("SET foo = 1\n\t; bar = 2"),
+ $this->identicalTo("SET foo = 1" . PHP_EOL . "\t; bar = 2"),
'Tests set with an array and glue.'
);
}
@@ -1473,7 +1473,7 @@ public function testWhere()
$this->assertThat(
trim($q->where),
- $this->equalTo('WHERE foo = 1 AND bar = 2 AND goo = 3'),
+ $this->equalTo('WHERE foo = 1 AND (bar = 2 AND goo = 3)'),
'Tests rendered value after second use and array input.'
);
@@ -1489,7 +1489,7 @@ public function testWhere()
$this->assertThat(
trim($q->where),
- $this->equalTo('WHERE bar = 2 OR goo = 3'),
+ $this->equalTo('WHERE (bar = 2 OR goo = 3)'),
'Tests rendered value with glue.'
);
}
@@ -1590,7 +1590,7 @@ public function testUnionUnion()
$teststring = (string) $q->union;
$this->assertThat(
$teststring,
- $this->equalTo("\nUNION (SELECT name FROM foo)"),
+ $this->equalTo("" . PHP_EOL . "UNION (SELECT name FROM foo)"),
'Tests rendered query with union.'
);
}
@@ -1611,7 +1611,7 @@ public function testUnionDistinctString()
$teststring = (string) $q->union;
$this->assertThat(
$teststring,
- $this->equalTo("\nUNION DISTINCT (SELECT name FROM foo)"),
+ $this->equalTo("" . PHP_EOL . "UNION DISTINCT (SELECT name FROM foo)"),
'Tests rendered query with union distinct as a string.'
);
}
@@ -1632,7 +1632,7 @@ public function testUnionDistinctTrue()
$teststring = (string) $q->union;
$this->assertThat(
$teststring,
- $this->equalTo("\nUNION DISTINCT (SELECT name FROM foo)"),
+ $this->equalTo("" . PHP_EOL . "UNION DISTINCT (SELECT name FROM foo)"),
'Tests rendered query with union distinct true.'
);
}
@@ -1653,7 +1653,7 @@ public function testUnionDistinctFalse()
$teststring = (string) $q->union;
$this->assertThat(
$teststring,
- $this->equalTo("\nUNION (SELECT name FROM foo)"),
+ $this->equalTo("" . PHP_EOL . "UNION (SELECT name FROM foo)"),
'Tests rendered query with union distinct false.'
);
}
@@ -1674,7 +1674,7 @@ public function testUnionArray()
$teststring = (string) $q->union;
$this->assertThat(
$teststring,
- $this->equalTo("\nUNION (SELECT name FROM foo)\nUNION (SELECT name FROM bar)"),
+ $this->equalTo("" . PHP_EOL . "UNION (SELECT name FROM foo)" . PHP_EOL . "UNION (SELECT name FROM bar)"),
'Tests rendered query with two unions as an array.'
);
}
@@ -1696,7 +1696,7 @@ public function testUnionTwo()
$teststring = (string) $q->union;
$this->assertThat(
$teststring,
- $this->equalTo("\nUNION (SELECT name FROM foo)\nUNION (SELECT name FROM bar)"),
+ $this->equalTo("" . PHP_EOL . "UNION (SELECT name FROM foo)" . PHP_EOL . "UNION (SELECT name FROM bar)"),
'Tests rendered query with two unions sequentially.'
);
}
@@ -1739,7 +1739,7 @@ public function testUnionDistinctArray()
$teststring = (string) $q->union;
$this->assertThat(
$teststring,
- $this->equalTo("\nUNION DISTINCT (SELECT name FROM foo)\nUNION DISTINCT (SELECT name FROM bar)"),
+ $this->equalTo("" . PHP_EOL . "UNION DISTINCT (SELECT name FROM foo)" . PHP_EOL . "UNION DISTINCT (SELECT name FROM bar)"),
'Tests rendered query with two unions distinct.'
);
}
Something went wrong with that request. Please try again.