Skip to content

Commit 0485710

Browse files
committed
Beginings of automatically converting sql functions to their right types
1 parent 34d7259 commit 0485710

File tree

3 files changed

+55
-27
lines changed

3 files changed

+55
-27
lines changed

src/Database/Expression/FunctionExpression.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
namespace Cake\Database\Expression;
1616

1717
use Cake\Database\ExpressionInterface;
18+
use Cake\Database\TypedResultInterface;
19+
use Cake\Database\TypedResultTrait;
1820
use Cake\Database\ValueBinder;
1921

2022
/**
@@ -25,9 +27,11 @@
2527
*
2628
* @internal
2729
*/
28-
class FunctionExpression extends QueryExpression
30+
class FunctionExpression extends QueryExpression implements TypedResultInterface
2931
{
3032

33+
use TypedResultTrait;
34+
3135
/**
3236
* The name of the function to be constructed when generating the SQL string
3337
*
@@ -58,10 +62,12 @@ class FunctionExpression extends QueryExpression
5862
* If associative the key would be used as argument when value is 'literal'
5963
* @param array $types associative array of types to be associated with the
6064
* passed arguments
65+
* @param string $returnType The return type of this expression
6166
*/
62-
public function __construct($name, $params = [], $types = [])
67+
public function __construct($name, $params = [], $types = [], $returnType = 'string')
6368
{
6469
$this->_name = $name;
70+
$this->_returnType = $returnType;
6571
parent::__construct($params, $types, ',');
6672
}
6773

src/Database/FunctionsBuilder.php

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,12 @@ class FunctionsBuilder
3131
* @param string $name the name of the SQL function to constructed
3232
* @param array $params list of params to be passed to the function
3333
* @param array $types list of types for each function param
34+
* @param string $return The return type of the function expression
3435
* @return FunctionExpression
3536
*/
36-
protected function _build($name, $params = [], $types = [])
37+
protected function _build($name, $params = [], $types = [], $return = 'string')
3738
{
38-
return new FunctionExpression($name, $params, $types);
39+
return new FunctionExpression($name, $params, $types, $return);
3940
}
4041

4142
/**
@@ -45,16 +46,17 @@ protected function _build($name, $params = [], $types = [])
4546
* @param string $name name of the function to build
4647
* @param mixed $expression the function argument
4748
* @param array $types list of types to bind to the arguments
49+
* @param string $return The return type for the function
4850
* @return FunctionExpression
4951
*/
50-
protected function _literalArgumentFunction($name, $expression, $types = [])
52+
protected function _literalArgumentFunction($name, $expression, $types = [], $return = 'string')
5153
{
5254
if (!is_string($expression)) {
5355
$expression = [$expression];
5456
} else {
5557
$expression = [$expression => 'literal'];
5658
}
57-
return $this->_build($name, $expression, $types);
59+
return $this->_build($name, $expression, $types, $return);
5860
}
5961

6062
/**
@@ -66,7 +68,7 @@ protected function _literalArgumentFunction($name, $expression, $types = [])
6668
*/
6769
public function sum($expression, $types = [])
6870
{
69-
return $this->_literalArgumentFunction('SUM', $expression, $types);
71+
return $this->_literalArgumentFunction('SUM', $expression, $types, 'float');
7072
}
7173

7274
/**
@@ -78,7 +80,7 @@ public function sum($expression, $types = [])
7880
*/
7981
public function avg($expression, $types = [])
8082
{
81-
return $this->_literalArgumentFunction('AVG', $expression, $types);
83+
return $this->_literalArgumentFunction('AVG', $expression, $types, 'float');
8284
}
8385

8486
/**
@@ -90,7 +92,7 @@ public function avg($expression, $types = [])
9092
*/
9193
public function max($expression, $types = [])
9294
{
93-
return $this->_literalArgumentFunction('MAX', $expression, $types);
95+
return $this->_literalArgumentFunction('MAX', $expression, $types, current($types) ?: 'string');
9496
}
9597

9698
/**
@@ -102,7 +104,7 @@ public function max($expression, $types = [])
102104
*/
103105
public function min($expression, $types = [])
104106
{
105-
return $this->_literalArgumentFunction('MIN', $expression, $types);
107+
return $this->_literalArgumentFunction('MIN', $expression, $types, current($types) ?: 'string');
106108
}
107109

108110
/**
@@ -114,7 +116,7 @@ public function min($expression, $types = [])
114116
*/
115117
public function count($expression, $types = [])
116118
{
117-
return $this->_literalArgumentFunction('COUNT', $expression, $types);
119+
return $this->_literalArgumentFunction('COUNT', $expression, $types, 'integer');
118120
}
119121

120122
/**
@@ -126,7 +128,7 @@ public function count($expression, $types = [])
126128
*/
127129
public function concat($args, $types = [])
128130
{
129-
return $this->_build('CONCAT', $args, $types);
131+
return $this->_build('CONCAT', $args, $types, 'string');
130132
}
131133

132134
/**
@@ -138,7 +140,7 @@ public function concat($args, $types = [])
138140
*/
139141
public function coalesce($args, $types = [])
140142
{
141-
return $this->_build('COALESCE', $args, $types);
143+
return $this->_build('COALESCE', $args, $types, current($types) ?: 'string');
142144
}
143145

144146
/**
@@ -151,7 +153,7 @@ public function coalesce($args, $types = [])
151153
*/
152154
public function dateDiff($args, $types = [])
153155
{
154-
return $this->_build('DATEDIFF', $args, $types);
156+
return $this->_build('DATEDIFF', $args, $types, 'integer');
155157
}
156158

157159
/**
@@ -177,7 +179,7 @@ public function datePart($part, $expression, $types = [])
177179
*/
178180
public function extract($part, $expression, $types = [])
179181
{
180-
$expression = $this->_literalArgumentFunction('EXTRACT', $expression, $types);
182+
$expression = $this->_literalArgumentFunction('EXTRACT', $expression, $types, 'integer');
181183
$expression->tieWith(' FROM')->add([$part => 'literal'], [], true);
182184
return $expression;
183185
}
@@ -197,7 +199,7 @@ public function dateAdd($expression, $value, $unit, $types = [])
197199
$value = 0;
198200
}
199201
$interval = $value . ' ' . $unit;
200-
$expression = $this->_literalArgumentFunction('DATE_ADD', $expression, $types);
202+
$expression = $this->_literalArgumentFunction('DATE_ADD', $expression, $types, 'datetime');
201203
$expression->tieWith(', INTERVAL')->add([$interval => 'literal']);
202204
return $expression;
203205
}
@@ -212,7 +214,7 @@ public function dateAdd($expression, $value, $unit, $types = [])
212214
*/
213215
public function dayOfWeek($expression, $types = [])
214216
{
215-
return $this->_literalArgumentFunction('DAYOFWEEK', $expression, $types);
217+
return $this->_literalArgumentFunction('DAYOFWEEK', $expression, $types, 'integer');
216218
}
217219

218220
/**
@@ -239,23 +241,23 @@ public function weekday($expression, $types = [])
239241
public function now($type = 'datetime')
240242
{
241243
if ($type === 'datetime') {
242-
return $this->_build('NOW');
244+
return $this->_build('NOW')->returnType('datetime');
243245
}
244246
if ($type === 'date') {
245-
return $this->_build('CURRENT_DATE');
247+
return $this->_build('CURRENT_DATE')->returnType('date');
246248
}
247249
if ($type === 'time') {
248-
return $this->_build('CURRENT_TIME');
250+
return $this->_build('CURRENT_TIME')->returnType('time');
249251
}
250252
}
251253

252254
/**
253255
* Magic method dispatcher to create custom SQL function calls
254256
*
255257
* @param string $name the SQL function name to construct
256-
* @param array $args list with up to 2 arguments, first one being an array with
257-
* parameters for the SQL function and second one a list of types to bind to those
258-
* params
258+
* @param array $args list with up to 3 arguments, first one being an array with
259+
* parameters for the SQL function, the second one a list of types to bind to those
260+
* params, and the third one the return type of the function
259261
* @return \Cake\Database\Expression\FunctionExpression
260262
*/
261263
public function __call($name, $args)
@@ -265,8 +267,10 @@ public function __call($name, $args)
265267
return $this->_build($name);
266268
case 1:
267269
return $this->_build($name, $args[0]);
268-
default:
270+
case 2:
269271
return $this->_build($name, $args[0], $args[1]);
272+
default:
273+
return $this->_build($name, $args[0], $args[1], $args[2]);
270274
}
271275
}
272276
}

tests/TestCase/Database/FunctionsBuilderTest.php

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ public function testArbitrary()
4646
$this->assertInstanceOf('Cake\Database\Expression\FunctionExpression', $function);
4747
$this->assertEquals('MyFunc', $function->name());
4848
$this->assertEquals('MyFunc(b)', $function->sql(new ValueBinder));
49+
50+
$function = $this->functions->MyFunc(['b'], ['string'], 'integer');
51+
$this->assertEquals('integer', $function->returnType());
4952
}
5053

5154
/**
@@ -58,6 +61,7 @@ public function testSum()
5861
$function = $this->functions->sum('total');
5962
$this->assertInstanceOf('Cake\Database\Expression\FunctionExpression', $function);
6063
$this->assertEquals('SUM(total)', $function->sql(new ValueBinder));
64+
$this->assertEquals('float', $function->returnType());
6165
}
6266

6367
/**
@@ -70,6 +74,7 @@ public function testAvg()
7074
$function = $this->functions->avg('salary');
7175
$this->assertInstanceOf('Cake\Database\Expression\FunctionExpression', $function);
7276
$this->assertEquals('AVG(salary)', $function->sql(new ValueBinder));
77+
$this->assertEquals('float', $function->returnType());
7378
}
7479

7580
/**
@@ -79,9 +84,10 @@ public function testAvg()
7984
*/
8085
public function testMAX()
8186
{
82-
$function = $this->functions->max('created');
87+
$function = $this->functions->max('created', ['datetime']);
8388
$this->assertInstanceOf('Cake\Database\Expression\FunctionExpression', $function);
8489
$this->assertEquals('MAX(created)', $function->sql(new ValueBinder));
90+
$this->assertEquals('datetime', $function->returnType());
8591
}
8692

8793
/**
@@ -91,9 +97,10 @@ public function testMAX()
9197
*/
9298
public function testMin()
9399
{
94-
$function = $this->functions->min('created');
100+
$function = $this->functions->min('created', ['date']);
95101
$this->assertInstanceOf('Cake\Database\Expression\FunctionExpression', $function);
96102
$this->assertEquals('MIN(created)', $function->sql(new ValueBinder));
103+
$this->assertEquals('date', $function->returnType());
97104
}
98105

99106
/**
@@ -106,6 +113,7 @@ public function testCount()
106113
$function = $this->functions->count('*');
107114
$this->assertInstanceOf('Cake\Database\Expression\FunctionExpression', $function);
108115
$this->assertEquals('COUNT(*)', $function->sql(new ValueBinder));
116+
$this->assertEquals('integer', $function->returnType());
109117
}
110118

111119
/**
@@ -118,6 +126,7 @@ public function testConcat()
118126
$function = $this->functions->concat(['title' => 'literal', ' is a string']);
119127
$this->assertInstanceOf('Cake\Database\Expression\FunctionExpression', $function);
120128
$this->assertEquals("CONCAT(title, :c0)", $function->sql(new ValueBinder));
129+
$this->assertEquals('string', $function->returnType());
121130
}
122131

123132
/**
@@ -127,9 +136,10 @@ public function testConcat()
127136
*/
128137
public function testCoalesce()
129138
{
130-
$function = $this->functions->coalesce(['NULL' => 'literal', '1', '2']);
139+
$function = $this->functions->coalesce(['NULL' => 'literal', '1', 'a'], ['a' => 'date']);
131140
$this->assertInstanceOf('Cake\Database\Expression\FunctionExpression', $function);
132141
$this->assertEquals("COALESCE(NULL, :c0, :c1)", $function->sql(new ValueBinder));
142+
$this->assertEquals('date', $function->returnType());
133143
}
134144

135145
/**
@@ -142,14 +152,17 @@ public function testNow()
142152
$function = $this->functions->now();
143153
$this->assertInstanceOf('Cake\Database\Expression\FunctionExpression', $function);
144154
$this->assertEquals("NOW()", $function->sql(new ValueBinder));
155+
$this->assertEquals('datetime', $function->returnType());
145156

146157
$function = $this->functions->now('date');
147158
$this->assertInstanceOf('Cake\Database\Expression\FunctionExpression', $function);
148159
$this->assertEquals("CURRENT_DATE()", $function->sql(new ValueBinder));
160+
$this->assertEquals('date', $function->returnType());
149161

150162
$function = $this->functions->now('time');
151163
$this->assertInstanceOf('Cake\Database\Expression\FunctionExpression', $function);
152164
$this->assertEquals("CURRENT_TIME()", $function->sql(new ValueBinder));
165+
$this->assertEquals('time', $function->returnType());
153166
}
154167

155168
/**
@@ -162,10 +175,12 @@ public function testExtract()
162175
$function = $this->functions->extract('day', 'created');
163176
$this->assertInstanceOf('Cake\Database\Expression\FunctionExpression', $function);
164177
$this->assertEquals("EXTRACT(day FROM created)", $function->sql(new ValueBinder));
178+
$this->assertEquals('integer', $function->returnType());
165179

166180
$function = $this->functions->datePart('year', 'modified');
167181
$this->assertInstanceOf('Cake\Database\Expression\FunctionExpression', $function);
168182
$this->assertEquals("EXTRACT(year FROM modified)", $function->sql(new ValueBinder));
183+
$this->assertEquals('integer', $function->returnType());
169184
}
170185

171186
/**
@@ -178,6 +193,7 @@ public function testDateAdd()
178193
$function = $this->functions->dateAdd('created', -3, 'day');
179194
$this->assertInstanceOf('Cake\Database\Expression\FunctionExpression', $function);
180195
$this->assertEquals("DATE_ADD(created, INTERVAL -3 day)", $function->sql(new ValueBinder));
196+
$this->assertEquals('datetime', $function->returnType());
181197
}
182198

183199
/**
@@ -190,9 +206,11 @@ public function testDayOfWeek()
190206
$function = $this->functions->dayOfWeek('created');
191207
$this->assertInstanceOf('Cake\Database\Expression\FunctionExpression', $function);
192208
$this->assertEquals("DAYOFWEEK(created)", $function->sql(new ValueBinder));
209+
$this->assertEquals('integer', $function->returnType());
193210

194211
$function = $this->functions->weekday('created');
195212
$this->assertInstanceOf('Cake\Database\Expression\FunctionExpression', $function);
196213
$this->assertEquals("DAYOFWEEK(created)", $function->sql(new ValueBinder));
214+
$this->assertEquals('integer', $function->returnType());
197215
}
198216
}

0 commit comments

Comments
 (0)