0
# Expression Framework
1
2
Phoenix's expression framework provides a powerful system for evaluating SQL expressions, functions, and operators. The framework supports literal values, column references, function calls, and complex expressions with proper type handling and optimization.
3
4
## Core Imports
5
6
```java
7
import org.apache.phoenix.expression.*;
8
import org.apache.phoenix.expression.function.*;
9
import org.apache.phoenix.expression.aggregator.*;
10
import org.apache.phoenix.schema.tuple.Tuple;
11
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
12
```
13
14
## Core Expression Types
15
16
### Expression
17
18
Base interface for all Phoenix expressions providing evaluation and metadata methods.
19
20
```java{ .api }
21
public interface Expression extends Writable {
22
// Expression evaluation
23
boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
24
PDataType getDataType()
25
Integer getMaxLength()
26
Integer getScale()
27
boolean isNullable()
28
29
// Expression properties
30
SortOrder getSortOrder()
31
boolean isDeterministic()
32
boolean isStateless()
33
34
// Child expressions
35
List<Expression> getChildren()
36
void reset()
37
boolean requiresFinalEvaluation()
38
39
// Serialization
40
void write(DataOutput output) throws IOException
41
void readFields(DataInput input) throws IOException
42
}
43
```
44
45
### LiteralExpression
46
47
Expression representing constant literal values with type-safe value handling.
48
49
```java{ .api }
50
public class LiteralExpression implements Expression {
51
// Factory methods for creating literals
52
public static LiteralExpression newConstant(Object value)
53
public static LiteralExpression newConstant(Object value, PDataType dataType)
54
public static LiteralExpression newConstant(Object value, PDataType dataType,
55
Integer maxLength, Integer scale)
56
public static LiteralExpression newConstant(Object value, PDataType dataType,
57
SortOrder sortOrder)
58
59
// Expression implementation
60
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
61
public PDataType getDataType()
62
public Object getValue()
63
public byte[] getBytes()
64
65
// Literal-specific methods
66
public boolean isNull()
67
public String toString()
68
}
69
```
70
71
**Usage:**
72
```java
73
// Create literal expressions
74
LiteralExpression stringLiteral = LiteralExpression.newConstant("Hello World");
75
LiteralExpression intLiteral = LiteralExpression.newConstant(42);
76
LiteralExpression decimalLiteral = LiteralExpression.newConstant(
77
new BigDecimal("123.45"), PDecimal.INSTANCE, 5, 2
78
);
79
80
// Evaluate literals
81
ImmutableBytesWritable result = new ImmutableBytesWritable();
82
boolean success = stringLiteral.evaluate(null, result); // Literals don't need tuple
83
if (success) {
84
String value = (String) stringLiteral.getDataType().toObject(result);
85
System.out.println("String value: " + value);
86
}
87
88
// Create null literal
89
LiteralExpression nullLiteral = LiteralExpression.newConstant(null, PVarchar.INSTANCE);
90
boolean isNull = nullLiteral.isNull(); // Returns true
91
```
92
93
### ColumnExpression
94
95
Expression representing column references from tables.
96
97
```java{ .api }
98
public class ColumnExpression extends BaseTerminalExpression {
99
public ColumnExpression(PColumn column, byte[] table)
100
101
// Column information
102
public PColumn getColumn()
103
public byte[] getTableName()
104
public String getDisplayName()
105
106
// Expression implementation
107
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
108
public PDataType getDataType()
109
public boolean isNullable()
110
}
111
```
112
113
**Usage:**
114
```java
115
// Create column expression
116
PTable table = connection.getTable(PNameFactory.newName("users"));
117
PColumn nameColumn = table.getColumn("name");
118
ColumnExpression nameExpr = new ColumnExpression(nameColumn, table.getName().getBytes());
119
120
// Evaluate column expression (requires tuple with row data)
121
Tuple rowTuple = getCurrentRowTuple(); // From query execution
122
ImmutableBytesWritable result = new ImmutableBytesWritable();
123
boolean success = nameExpr.evaluate(rowTuple, result);
124
if (success) {
125
String name = (String) nameExpr.getDataType().toObject(result);
126
System.out.println("Name: " + name);
127
}
128
129
// Column properties
130
PDataType dataType = nameExpr.getDataType();
131
String displayName = nameExpr.getDisplayName();
132
boolean nullable = nameExpr.isNullable();
133
```
134
135
## Function Framework
136
137
### FunctionExpression
138
139
Base class for all Phoenix function expressions providing common function behavior.
140
141
```java{ .api }
142
public abstract class FunctionExpression implements Expression {
143
public FunctionExpression(List<Expression> children)
144
public FunctionExpression(String name, List<Expression> children)
145
146
// Function properties
147
public String getName()
148
public List<Expression> getChildren()
149
public abstract PDataType getDataType()
150
151
// Function evaluation
152
public abstract boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
153
public abstract String toString()
154
155
// Helper methods for child evaluation
156
protected boolean evaluateExpression(Expression expression, Tuple tuple,
157
ImmutableBytesWritable ptr)
158
protected Object evaluateExpression(Expression expression, Tuple tuple)
159
}
160
```
161
162
### ScalarFunction
163
164
Base class for scalar functions that return a single value.
165
166
```java{ .api }
167
public abstract class ScalarFunction extends FunctionExpression {
168
public ScalarFunction(List<Expression> children)
169
public ScalarFunction(String name, List<Expression> children)
170
171
// Scalar function implementation
172
public abstract boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
173
public abstract PDataType getDataType()
174
175
// Common scalar function utilities
176
protected static boolean checkNulls(Tuple tuple, Expression... expressions)
177
protected static Object getObject(Expression expression, Tuple tuple)
178
}
179
```
180
181
### AggregateFunction
182
183
Base class for aggregate functions that accumulate values across multiple rows.
184
185
```java{ .api }
186
public abstract class AggregateFunction extends FunctionExpression {
187
public AggregateFunction(List<Expression> children, String name)
188
189
// Aggregate function implementation
190
public abstract Aggregator newAggregator()
191
public abstract boolean isAggregator(Expression expression)
192
193
// Aggregate properties
194
public boolean requiresFinalEvaluation() // Returns true
195
public abstract PDataType getDataType()
196
}
197
```
198
199
**Usage:**
200
```java
201
// Create scalar function (example: UPPER function)
202
Expression nameColumn = new ColumnExpression(nameColumn, tableBytes);
203
List<Expression> upperArgs = Arrays.asList(nameColumn);
204
ScalarFunction upperFunc = new UpperFunction(upperArgs);
205
206
// Evaluate scalar function
207
Tuple rowTuple = getCurrentRowTuple();
208
ImmutableBytesWritable result = new ImmutableBytesWritable();
209
boolean success = upperFunc.evaluate(rowTuple, result);
210
if (success) {
211
String upperName = (String) upperFunc.getDataType().toObject(result);
212
System.out.println("Upper name: " + upperName);
213
}
214
215
// Create aggregate function (example: COUNT function)
216
List<Expression> countArgs = Arrays.asList(LiteralExpression.newConstant(1));
217
AggregateFunction countFunc = new CountAggregateFunction(countArgs);
218
Aggregator aggregator = countFunc.newAggregator();
219
```
220
221
## Aggregation Framework
222
223
### Aggregator
224
225
Interface for aggregation operations that accumulate values across multiple rows.
226
227
```java{ .api }
228
public interface Aggregator {
229
// Aggregation operations
230
void aggregate(Tuple tuple, ImmutableBytesWritable ptr)
231
boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
232
233
// Aggregator state
234
void reset()
235
Aggregator newAggregator()
236
237
// Size estimation
238
int getSize()
239
}
240
```
241
242
### ServerAggregators
243
244
Container for server-side aggregators used in distributed query execution.
245
246
```java{ .api }
247
public class ServerAggregators {
248
public ServerAggregators(List<SingleAggregateFunction> functions,
249
int minNullableIndex)
250
251
// Aggregator access
252
public SingleAggregateFunction[] getFunctions()
253
public Aggregator[] getAggregators()
254
public KeyValueSchema getSchema()
255
256
// Aggregation operations
257
public void aggregate(Aggregator[] aggregators, Tuple result)
258
public void reset(Aggregator[] aggregators)
259
public int getEstimatedByteSize()
260
}
261
```
262
263
**Usage:**
264
```java
265
// Create aggregators for distributed execution
266
List<SingleAggregateFunction> aggFunctions = Arrays.asList(
267
new CountAggregateFunction(),
268
new SumAggregateFunction(salaryColumn),
269
new MaxAggregateFunction(salaryColumn)
270
);
271
272
ServerAggregators serverAggs = new ServerAggregators(aggFunctions, 0);
273
Aggregator[] aggregators = serverAggs.getAggregators();
274
275
// Process each row in server-side scan
276
while (scanner.hasNext()) {
277
Tuple row = scanner.next();
278
serverAggs.aggregate(aggregators, row);
279
}
280
281
// Get final aggregated results
282
ImmutableBytesWritable result = new ImmutableBytesWritable();
283
for (int i = 0; i < aggregators.length; i++) {
284
boolean success = aggregators[i].evaluate(null, result);
285
if (success) {
286
Object value = aggFunctions.get(i).getDataType().toObject(result);
287
System.out.println("Aggregate " + i + ": " + value);
288
}
289
}
290
```
291
292
## Common Function Types
293
294
### String Functions
295
296
```java
297
// String manipulation functions
298
public class UpperFunction extends ScalarFunction {
299
public UpperFunction(List<Expression> children)
300
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
301
public PDataType getDataType() // Returns PVarchar.INSTANCE
302
}
303
304
public class LowerFunction extends ScalarFunction {
305
public LowerFunction(List<Expression> children)
306
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
307
public PDataType getDataType() // Returns PVarchar.INSTANCE
308
}
309
310
public class SubstrFunction extends ScalarFunction {
311
public SubstrFunction(List<Expression> children) // string, start, [length]
312
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
313
public PDataType getDataType() // Returns PVarchar.INSTANCE
314
}
315
316
public class LengthFunction extends ScalarFunction {
317
public LengthFunction(List<Expression> children)
318
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
319
public PDataType getDataType() // Returns PInteger.INSTANCE
320
}
321
```
322
323
**Usage:**
324
```java
325
// String function examples
326
Expression nameColumn = new ColumnExpression(nameColumn, tableBytes);
327
328
// UPPER(name)
329
UpperFunction upper = new UpperFunction(Arrays.asList(nameColumn));
330
331
// LENGTH(name)
332
LengthFunction length = new LengthFunction(Arrays.asList(nameColumn));
333
334
// SUBSTR(name, 1, 5)
335
SubstrFunction substr = new SubstrFunction(Arrays.asList(
336
nameColumn,
337
LiteralExpression.newConstant(1),
338
LiteralExpression.newConstant(5)
339
));
340
341
// Evaluate functions
342
Tuple row = getCurrentRowTuple();
343
ImmutableBytesWritable result = new ImmutableBytesWritable();
344
345
if (upper.evaluate(row, result)) {
346
String upperName = (String) upper.getDataType().toObject(result);
347
System.out.println("Upper: " + upperName);
348
}
349
350
if (length.evaluate(row, result)) {
351
Integer nameLength = (Integer) length.getDataType().toObject(result);
352
System.out.println("Length: " + nameLength);
353
}
354
```
355
356
### Numeric Functions
357
358
```java
359
// Mathematical functions
360
public class RoundFunction extends ScalarFunction {
361
public RoundFunction(List<Expression> children) // number, [scale]
362
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
363
public PDataType getDataType() // Returns number type
364
}
365
366
public class AbsFunction extends ScalarFunction {
367
public AbsFunction(List<Expression> children)
368
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
369
public PDataType getDataType() // Returns same as input
370
}
371
372
public class PowerFunction extends ScalarFunction {
373
public PowerFunction(List<Expression> children) // base, exponent
374
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
375
public PDataType getDataType() // Returns PDouble.INSTANCE
376
}
377
```
378
379
### Date/Time Functions
380
381
```java
382
// Date/time manipulation functions
383
public class NowFunction extends ScalarFunction {
384
public NowFunction() // No arguments
385
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
386
public PDataType getDataType() // Returns PTimestamp.INSTANCE
387
}
388
389
public class ToDateFunction extends ScalarFunction {
390
public ToDateFunction(List<Expression> children) // string, [format]
391
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
392
public PDataType getDataType() // Returns PDate.INSTANCE
393
}
394
395
public class YearFunction extends ScalarFunction {
396
public YearFunction(List<Expression> children) // date
397
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
398
public PDataType getDataType() // Returns PInteger.INSTANCE
399
}
400
```
401
402
### Aggregate Functions
403
404
```java
405
// Standard aggregate functions
406
public class CountAggregateFunction extends SingleAggregateFunction {
407
public CountAggregateFunction(List<Expression> children)
408
public Aggregator newAggregator()
409
public PDataType getDataType() // Returns PLong.INSTANCE
410
}
411
412
public class SumAggregateFunction extends SingleAggregateFunction {
413
public SumAggregateFunction(List<Expression> children)
414
public Aggregator newAggregator()
415
public PDataType getDataType() // Returns numeric type
416
}
417
418
public class AvgAggregateFunction extends SingleAggregateFunction {
419
public AvgAggregateFunction(List<Expression> children)
420
public Aggregator newAggregator()
421
public PDataType getDataType() // Returns PDecimal.INSTANCE
422
}
423
424
public class MinAggregateFunction extends SingleAggregateFunction {
425
public MinAggregateFunction(List<Expression> children)
426
public Aggregator newAggregator()
427
public PDataType getDataType() // Returns same as input
428
}
429
430
public class MaxAggregateFunction extends SingleAggregateFunction {
431
public MaxAggregateFunction(List<Expression> children)
432
public Aggregator newAggregator()
433
public PDataType getDataType() // Returns same as input
434
}
435
```
436
437
**Usage:**
438
```java
439
// Aggregate function examples
440
Expression salaryColumn = new ColumnExpression(salaryColumn, tableBytes);
441
442
// COUNT(*)
443
CountAggregateFunction count = new CountAggregateFunction(
444
Arrays.asList(LiteralExpression.newConstant(1))
445
);
446
447
// SUM(salary)
448
SumAggregateFunction sum = new SumAggregateFunction(Arrays.asList(salaryColumn));
449
450
// AVG(salary)
451
AvgAggregateFunction avg = new AvgAggregateFunction(Arrays.asList(salaryColumn));
452
453
// MIN(salary)
454
MinAggregateFunction min = new MinAggregateFunction(Arrays.asList(salaryColumn));
455
456
// MAX(salary)
457
MaxAggregateFunction max = new MaxAggregateFunction(Arrays.asList(salaryColumn));
458
459
// Create aggregators
460
Aggregator countAgg = count.newAggregator();
461
Aggregator sumAgg = sum.newAggregator();
462
Aggregator avgAgg = avg.newAggregator();
463
464
// Process rows
465
while (hasMoreRows()) {
466
Tuple row = getNextRow();
467
468
// Aggregate each row
469
countAgg.aggregate(row, null);
470
ImmutableBytesWritable ptr = new ImmutableBytesWritable();
471
if (salaryColumn.evaluate(row, ptr)) {
472
sumAgg.aggregate(row, ptr);
473
avgAgg.aggregate(row, ptr);
474
}
475
}
476
477
// Get final results
478
ImmutableBytesWritable result = new ImmutableBytesWritable();
479
480
if (countAgg.evaluate(null, result)) {
481
Long countValue = (Long) count.getDataType().toObject(result);
482
System.out.println("Count: " + countValue);
483
}
484
485
if (sumAgg.evaluate(null, result)) {
486
Object sumValue = sum.getDataType().toObject(result);
487
System.out.println("Sum: " + sumValue);
488
}
489
```
490
491
## Complex Expression Examples
492
493
### Conditional Expressions
494
495
```java
496
// CASE WHEN expressions
497
public class CaseExpression extends ScalarFunction {
498
public CaseExpression(List<Expression> children)
499
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
500
public PDataType getDataType()
501
}
502
503
// Usage: CASE WHEN age < 18 THEN 'Minor' ELSE 'Adult' END
504
Expression ageColumn = new ColumnExpression(ageColumn, tableBytes);
505
Expression condition = new ComparisonExpression(
506
CompareOp.LESS,
507
Arrays.asList(ageColumn, LiteralExpression.newConstant(18))
508
);
509
Expression caseExpr = new CaseExpression(Arrays.asList(
510
condition,
511
LiteralExpression.newConstant("Minor"),
512
LiteralExpression.newConstant("Adult")
513
));
514
```
515
516
### Arithmetic Expressions
517
518
```java
519
// Arithmetic operations
520
public class ArithmeticExpression extends ScalarFunction {
521
public ArithmeticExpression(ArithmeticOp op, List<Expression> children)
522
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
523
public PDataType getDataType()
524
}
525
526
// Usage: salary * 1.1 (10% raise)
527
Expression salaryColumn = new ColumnExpression(salaryColumn, tableBytes);
528
Expression raiseMultiplier = LiteralExpression.newConstant(new BigDecimal("1.1"));
529
Expression raisedSalary = new ArithmeticExpression(
530
ArithmeticOp.MULT,
531
Arrays.asList(salaryColumn, raiseMultiplier)
532
);
533
```
534
535
### Comparison Expressions
536
537
```java
538
// Comparison operations
539
public class ComparisonExpression extends ScalarFunction {
540
public ComparisonExpression(CompareOp op, List<Expression> children)
541
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
542
public PDataType getDataType() // Returns PBoolean.INSTANCE
543
}
544
545
// Usage: age >= 21
546
Expression ageColumn = new ColumnExpression(ageColumn, tableBytes);
547
Expression legalAge = LiteralExpression.newConstant(21);
548
Expression isLegalAge = new ComparisonExpression(
549
CompareOp.GREATER_OR_EQUAL,
550
Arrays.asList(ageColumn, legalAge)
551
);
552
```
553
554
## Practical Examples
555
556
### Building Complex Expressions
557
558
```java
559
public class ExpressionBuilder {
560
private final PTable table;
561
private final byte[] tableBytes;
562
563
public ExpressionBuilder(PTable table) {
564
this.table = table;
565
this.tableBytes = table.getName().getBytes();
566
}
567
568
public Expression buildFullNameExpression() {
569
// CONCAT(first_name, ' ', last_name)
570
Expression firstName = new ColumnExpression(table.getColumn("first_name"), tableBytes);
571
Expression lastName = new ColumnExpression(table.getColumn("last_name"), tableBytes);
572
Expression space = LiteralExpression.newConstant(" ");
573
574
return new ConcatFunction(Arrays.asList(firstName, space, lastName));
575
}
576
577
public Expression buildAgeGroupExpression() {
578
// CASE
579
// WHEN age < 18 THEN 'Minor'
580
// WHEN age < 65 THEN 'Adult'
581
// ELSE 'Senior'
582
// END
583
Expression ageColumn = new ColumnExpression(table.getColumn("age"), tableBytes);
584
585
Expression isMinor = new ComparisonExpression(
586
CompareOp.LESS,
587
Arrays.asList(ageColumn, LiteralExpression.newConstant(18))
588
);
589
590
Expression isAdult = new ComparisonExpression(
591
CompareOp.LESS,
592
Arrays.asList(ageColumn, LiteralExpression.newConstant(65))
593
);
594
595
return new CaseExpression(Arrays.asList(
596
isMinor, LiteralExpression.newConstant("Minor"),
597
isAdult, LiteralExpression.newConstant("Adult"),
598
LiteralExpression.newConstant("Senior")
599
));
600
}
601
602
public Expression buildSalaryBonusExpression() {
603
// salary + (salary * bonus_percentage / 100)
604
Expression salary = new ColumnExpression(table.getColumn("salary"), tableBytes);
605
Expression bonusPercent = new ColumnExpression(table.getColumn("bonus_percentage"), tableBytes);
606
Expression hundred = LiteralExpression.newConstant(100);
607
608
Expression bonusAmount = new ArithmeticExpression(
609
ArithmeticOp.MULT,
610
Arrays.asList(salary, bonusPercent)
611
);
612
613
Expression bonusDivided = new ArithmeticExpression(
614
ArithmeticOp.DIV,
615
Arrays.asList(bonusAmount, hundred)
616
);
617
618
return new ArithmeticExpression(
619
ArithmeticOp.ADD,
620
Arrays.asList(salary, bonusDivided)
621
);
622
}
623
}
624
625
// Usage
626
PTable employeeTable = connection.getTable(PNameFactory.newName("employees"));
627
ExpressionBuilder builder = new ExpressionBuilder(employeeTable);
628
629
Expression fullName = builder.buildFullNameExpression();
630
Expression ageGroup = builder.buildAgeGroupExpression();
631
Expression totalSalary = builder.buildSalaryBonusExpression();
632
633
// Evaluate expressions for a row
634
Tuple employeeRow = getCurrentEmployeeRow();
635
ImmutableBytesWritable result = new ImmutableBytesWritable();
636
637
if (fullName.evaluate(employeeRow, result)) {
638
String name = (String) fullName.getDataType().toObject(result);
639
System.out.println("Full Name: " + name);
640
}
641
642
if (ageGroup.evaluate(employeeRow, result)) {
643
String group = (String) ageGroup.getDataType().toObject(result);
644
System.out.println("Age Group: " + group);
645
}
646
647
if (totalSalary.evaluate(employeeRow, result)) {
648
BigDecimal total = (BigDecimal) totalSalary.getDataType().toObject(result);
649
System.out.println("Total Salary: $" + total);
650
}
651
```
652
653
### Custom Function Implementation
654
655
```java
656
// Custom scalar function example
657
public class AgeFromBirthdateFunction extends ScalarFunction {
658
public AgeFromBirthdateFunction(List<Expression> children) {
659
super("AGE_FROM_BIRTHDATE", children);
660
if (children.size() != 1) {
661
throw new IllegalArgumentException("AGE_FROM_BIRTHDATE expects 1 argument");
662
}
663
}
664
665
@Override
666
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
667
Expression birthdateExpr = getChildren().get(0);
668
669
if (!birthdateExpr.evaluate(tuple, ptr)) {
670
return false;
671
}
672
673
if (ptr.getLength() == 0) {
674
return true; // NULL input -> NULL output
675
}
676
677
// Get birthdate value
678
Date birthdate = (Date) birthdateExpr.getDataType().toObject(ptr);
679
680
// Calculate age
681
Calendar birth = Calendar.getInstance();
682
birth.setTime(birthdate);
683
684
Calendar now = Calendar.getInstance();
685
686
int age = now.get(Calendar.YEAR) - birth.get(Calendar.YEAR);
687
if (now.get(Calendar.DAY_OF_YEAR) < birth.get(Calendar.DAY_OF_YEAR)) {
688
age--;
689
}
690
691
// Convert result to bytes
692
return PInteger.INSTANCE.toBytes(age, ptr.get(), ptr.getOffset());
693
}
694
695
@Override
696
public PDataType getDataType() {
697
return PInteger.INSTANCE;
698
}
699
}
700
701
// Usage
702
Expression birthdateColumn = new ColumnExpression(table.getColumn("birthdate"), tableBytes);
703
AgeFromBirthdateFunction ageFunc = new AgeFromBirthdateFunction(Arrays.asList(birthdateColumn));
704
705
// Evaluate custom function
706
Tuple row = getCurrentRow();
707
ImmutableBytesWritable result = new ImmutableBytesWritable();
708
if (ageFunc.evaluate(row, result)) {
709
Integer age = (Integer) ageFunc.getDataType().toObject(result);
710
System.out.println("Calculated age: " + age);
711
}
712
```