Skip to content

Commit dce7eda

Browse files
arontsangjakubmisek
authored andcommitted
Add support for Aggregate
1 parent 08024ba commit dce7eda

File tree

2 files changed

+128
-23
lines changed

2 files changed

+128
-23
lines changed

src/PDO/Peachpie.Library.PDO.Sqlite/PDOSqliteDriver.cs

+86-23
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,26 @@ public override ExtensionMethodDelegate TryGetExtensionMethod(string name)
6565

6666
private static PhpValue sqliteCreateAggregate(Context ctx, PDO pdo, PhpArray arguments)
6767
{
68-
return PhpValue.False;
68+
if (pdo.GetCurrentConnection<SqliteConnection>() is not {} connection)
69+
return PhpValue.False;
70+
71+
var name = arguments[0].String;
72+
var step = arguments[1].AsCallable();
73+
var finalize = arguments[2].AsCallable();
74+
var numberOfArguments = -1;
75+
if (arguments.TryGetValue(3, out var args) && args.IsInteger())
76+
numberOfArguments = args.ToInt();
77+
78+
var handle = connection.Handle;
79+
80+
raw.sqlite3_create_function(
81+
handle,
82+
name,
83+
numberOfArguments,
84+
null,
85+
CreateAggregateStep(ctx, step),
86+
CreateAggregateFinalize(ctx, finalize));
87+
return PhpValue.True;
6988
}
7089
private static PhpValue sqliteCreateCollation(Context ctx, PDO pdo, PhpArray arguments)
7190
{
@@ -126,39 +145,83 @@ public override string GetLastInsertId(PDO pdo, string name)
126145
}
127146
}
128147

129-
private static delegate_function_scalar CreateScalarFunction(Context ctx, IPhpCallable callback)
148+
private static delegate_function_aggregate_step CreateAggregateStep(Context ctx, IPhpCallable callback)
130149
{
131150
return (sqliteContext, data, args) =>
132151
{
152+
if (sqliteContext.state is not StepFunctionState state)
153+
{
154+
sqliteContext.state = state = new StepFunctionState();
155+
}
156+
var rowIndex = state.RowIndex++;
157+
133158
var phpArgs = args
134159
.Select(AsPhp)
160+
.Prepend(PhpValue.FromClr(rowIndex))
161+
.Prepend(state.Value)
135162
.ToArray();
136-
var ret = callback.Invoke(ctx, phpArgs);
137-
138163

139-
if (ret.IsLong(out var longValue))
140-
{
141-
raw.sqlite3_result_int64(sqliteContext, longValue);
142-
}
143-
else if (ret.IsInteger())
144-
{
145-
raw.sqlite3_result_int(sqliteContext, ret.ToInt());
146-
}
147-
else if (ret.IsDouble(out var doubleValue))
148-
{
149-
raw.sqlite3_result_double(sqliteContext, doubleValue);
150-
}
151-
else if (ret.IsString(out var stringValue))
152-
{
153-
var foo = stringValue;
154-
raw.sqlite3_result_text(sqliteContext, foo);
155-
}
156-
else
164+
var ret = callback.Invoke(ctx, phpArgs);
165+
state.Value = ret;
166+
};
167+
}
168+
169+
private static delegate_function_aggregate_final CreateAggregateFinalize(Context ctx, IPhpCallable callback)
170+
{
171+
return (sqliteContext, data) =>
172+
{
173+
if (sqliteContext.state is not StepFunctionState state)
157174
{
158-
raw.sqlite3_result_null(sqliteContext);
175+
sqliteContext.state = state = new StepFunctionState();
159176
}
177+
178+
var ret = callback.Invoke(ctx, state.Value, state.RowIndex);
179+
SetSqliteReturnValue(ret, sqliteContext);
180+
};
181+
}
182+
183+
private static delegate_function_scalar CreateScalarFunction(Context ctx, IPhpCallable callback)
184+
{
185+
return (sqliteContext, data, args) =>
186+
{
187+
var phpArgs = args
188+
.Select(AsPhp)
189+
.ToArray();
190+
var ret = callback.Invoke(ctx, phpArgs);
191+
SetSqliteReturnValue(ret, sqliteContext);
160192
};
161193
}
194+
195+
private static void SetSqliteReturnValue(PhpValue ret, sqlite3_context sqliteContext)
196+
{
197+
if (ret.IsLong(out var longValue))
198+
{
199+
raw.sqlite3_result_int64(sqliteContext, longValue);
200+
}
201+
else if (ret.IsInteger())
202+
{
203+
raw.sqlite3_result_int(sqliteContext, ret.ToInt());
204+
}
205+
else if (ret.IsDouble(out var doubleValue))
206+
{
207+
raw.sqlite3_result_double(sqliteContext, doubleValue);
208+
}
209+
else if (ret.IsString(out var stringValue))
210+
{
211+
var foo = stringValue;
212+
raw.sqlite3_result_text(sqliteContext, foo);
213+
}
214+
else
215+
{
216+
raw.sqlite3_result_null(sqliteContext);
217+
}
218+
}
219+
220+
private class StepFunctionState
221+
{
222+
public int RowIndex { get; set; }
223+
public PhpValue Value { get; set; } = PhpValue.Null;
224+
}
162225

163226
private static PhpValue AsPhp(sqlite3_value value)
164227
{

tests/pdo/sqlite_udf_aggregate.php

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
$data = array(
4+
'one',
5+
'two',
6+
'three',
7+
'four',
8+
'five',
9+
'six',
10+
'seven',
11+
'eight',
12+
'nine',
13+
'ten',
14+
);
15+
$pdo = new \PDO("sqlite::memory:");
16+
$pdo->exec("CREATE TABLE strings(a)");
17+
$insert = $pdo->prepare('INSERT INTO strings VALUES (?)');
18+
foreach ($data as $str) {
19+
$insert->execute(array($str));
20+
}
21+
$insert = null;
22+
23+
function max_len_step($context, $rownumber, $string)
24+
{
25+
if (strlen($string) > $context) {
26+
$context = strlen($string);
27+
}
28+
return $context;
29+
}
30+
31+
function max_len_finalize($context, $rownumber)
32+
{
33+
return $context === null ? 0 : $context;
34+
}
35+
36+
$pdo->sqliteCreateAggregate('max_len', max_len_step, max_len_finalize);
37+
$stmt = $pdo->prepare('SELECT max_len(a) from strings');
38+
$stmt->execute();
39+
$result = $stmt->fetch(\PDO::FETCH_NUM)[0];
40+
if ($result != 5) {
41+
throw new ErrorException("Expecting '5'");
42+
}

0 commit comments

Comments
 (0)