Apache Phoenix Core library providing SQL-on-HBase functionality with JDBC connectivity, query compilation, and transaction support
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.
import org.apache.phoenix.expression.*;
import org.apache.phoenix.expression.function.*;
import org.apache.phoenix.expression.aggregator.*;
import org.apache.phoenix.schema.tuple.Tuple;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;Base interface for all Phoenix expressions providing evaluation and metadata methods.
public interface Expression extends Writable {
// Expression evaluation
boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
PDataType getDataType()
Integer getMaxLength()
Integer getScale()
boolean isNullable()
// Expression properties
SortOrder getSortOrder()
boolean isDeterministic()
boolean isStateless()
// Child expressions
List<Expression> getChildren()
void reset()
boolean requiresFinalEvaluation()
// Serialization
void write(DataOutput output) throws IOException
void readFields(DataInput input) throws IOException
}Expression representing constant literal values with type-safe value handling.
public class LiteralExpression implements Expression {
// Factory methods for creating literals
public static LiteralExpression newConstant(Object value)
public static LiteralExpression newConstant(Object value, PDataType dataType)
public static LiteralExpression newConstant(Object value, PDataType dataType,
Integer maxLength, Integer scale)
public static LiteralExpression newConstant(Object value, PDataType dataType,
SortOrder sortOrder)
// Expression implementation
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
public PDataType getDataType()
public Object getValue()
public byte[] getBytes()
// Literal-specific methods
public boolean isNull()
public String toString()
}Usage:
// Create literal expressions
LiteralExpression stringLiteral = LiteralExpression.newConstant("Hello World");
LiteralExpression intLiteral = LiteralExpression.newConstant(42);
LiteralExpression decimalLiteral = LiteralExpression.newConstant(
new BigDecimal("123.45"), PDecimal.INSTANCE, 5, 2
);
// Evaluate literals
ImmutableBytesWritable result = new ImmutableBytesWritable();
boolean success = stringLiteral.evaluate(null, result); // Literals don't need tuple
if (success) {
String value = (String) stringLiteral.getDataType().toObject(result);
System.out.println("String value: " + value);
}
// Create null literal
LiteralExpression nullLiteral = LiteralExpression.newConstant(null, PVarchar.INSTANCE);
boolean isNull = nullLiteral.isNull(); // Returns trueExpression representing column references from tables.
public class ColumnExpression extends BaseTerminalExpression {
public ColumnExpression(PColumn column, byte[] table)
// Column information
public PColumn getColumn()
public byte[] getTableName()
public String getDisplayName()
// Expression implementation
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
public PDataType getDataType()
public boolean isNullable()
}Usage:
// Create column expression
PTable table = connection.getTable(PNameFactory.newName("users"));
PColumn nameColumn = table.getColumn("name");
ColumnExpression nameExpr = new ColumnExpression(nameColumn, table.getName().getBytes());
// Evaluate column expression (requires tuple with row data)
Tuple rowTuple = getCurrentRowTuple(); // From query execution
ImmutableBytesWritable result = new ImmutableBytesWritable();
boolean success = nameExpr.evaluate(rowTuple, result);
if (success) {
String name = (String) nameExpr.getDataType().toObject(result);
System.out.println("Name: " + name);
}
// Column properties
PDataType dataType = nameExpr.getDataType();
String displayName = nameExpr.getDisplayName();
boolean nullable = nameExpr.isNullable();Base class for all Phoenix function expressions providing common function behavior.
public abstract class FunctionExpression implements Expression {
public FunctionExpression(List<Expression> children)
public FunctionExpression(String name, List<Expression> children)
// Function properties
public String getName()
public List<Expression> getChildren()
public abstract PDataType getDataType()
// Function evaluation
public abstract boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
public abstract String toString()
// Helper methods for child evaluation
protected boolean evaluateExpression(Expression expression, Tuple tuple,
ImmutableBytesWritable ptr)
protected Object evaluateExpression(Expression expression, Tuple tuple)
}Base class for scalar functions that return a single value.
public abstract class ScalarFunction extends FunctionExpression {
public ScalarFunction(List<Expression> children)
public ScalarFunction(String name, List<Expression> children)
// Scalar function implementation
public abstract boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
public abstract PDataType getDataType()
// Common scalar function utilities
protected static boolean checkNulls(Tuple tuple, Expression... expressions)
protected static Object getObject(Expression expression, Tuple tuple)
}Base class for aggregate functions that accumulate values across multiple rows.
public abstract class AggregateFunction extends FunctionExpression {
public AggregateFunction(List<Expression> children, String name)
// Aggregate function implementation
public abstract Aggregator newAggregator()
public abstract boolean isAggregator(Expression expression)
// Aggregate properties
public boolean requiresFinalEvaluation() // Returns true
public abstract PDataType getDataType()
}Usage:
// Create scalar function (example: UPPER function)
Expression nameColumn = new ColumnExpression(nameColumn, tableBytes);
List<Expression> upperArgs = Arrays.asList(nameColumn);
ScalarFunction upperFunc = new UpperFunction(upperArgs);
// Evaluate scalar function
Tuple rowTuple = getCurrentRowTuple();
ImmutableBytesWritable result = new ImmutableBytesWritable();
boolean success = upperFunc.evaluate(rowTuple, result);
if (success) {
String upperName = (String) upperFunc.getDataType().toObject(result);
System.out.println("Upper name: " + upperName);
}
// Create aggregate function (example: COUNT function)
List<Expression> countArgs = Arrays.asList(LiteralExpression.newConstant(1));
AggregateFunction countFunc = new CountAggregateFunction(countArgs);
Aggregator aggregator = countFunc.newAggregator();Interface for aggregation operations that accumulate values across multiple rows.
public interface Aggregator {
// Aggregation operations
void aggregate(Tuple tuple, ImmutableBytesWritable ptr)
boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
// Aggregator state
void reset()
Aggregator newAggregator()
// Size estimation
int getSize()
}Container for server-side aggregators used in distributed query execution.
public class ServerAggregators {
public ServerAggregators(List<SingleAggregateFunction> functions,
int minNullableIndex)
// Aggregator access
public SingleAggregateFunction[] getFunctions()
public Aggregator[] getAggregators()
public KeyValueSchema getSchema()
// Aggregation operations
public void aggregate(Aggregator[] aggregators, Tuple result)
public void reset(Aggregator[] aggregators)
public int getEstimatedByteSize()
}Usage:
// Create aggregators for distributed execution
List<SingleAggregateFunction> aggFunctions = Arrays.asList(
new CountAggregateFunction(),
new SumAggregateFunction(salaryColumn),
new MaxAggregateFunction(salaryColumn)
);
ServerAggregators serverAggs = new ServerAggregators(aggFunctions, 0);
Aggregator[] aggregators = serverAggs.getAggregators();
// Process each row in server-side scan
while (scanner.hasNext()) {
Tuple row = scanner.next();
serverAggs.aggregate(aggregators, row);
}
// Get final aggregated results
ImmutableBytesWritable result = new ImmutableBytesWritable();
for (int i = 0; i < aggregators.length; i++) {
boolean success = aggregators[i].evaluate(null, result);
if (success) {
Object value = aggFunctions.get(i).getDataType().toObject(result);
System.out.println("Aggregate " + i + ": " + value);
}
}// String manipulation functions
public class UpperFunction extends ScalarFunction {
public UpperFunction(List<Expression> children)
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
public PDataType getDataType() // Returns PVarchar.INSTANCE
}
public class LowerFunction extends ScalarFunction {
public LowerFunction(List<Expression> children)
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
public PDataType getDataType() // Returns PVarchar.INSTANCE
}
public class SubstrFunction extends ScalarFunction {
public SubstrFunction(List<Expression> children) // string, start, [length]
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
public PDataType getDataType() // Returns PVarchar.INSTANCE
}
public class LengthFunction extends ScalarFunction {
public LengthFunction(List<Expression> children)
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
public PDataType getDataType() // Returns PInteger.INSTANCE
}Usage:
// String function examples
Expression nameColumn = new ColumnExpression(nameColumn, tableBytes);
// UPPER(name)
UpperFunction upper = new UpperFunction(Arrays.asList(nameColumn));
// LENGTH(name)
LengthFunction length = new LengthFunction(Arrays.asList(nameColumn));
// SUBSTR(name, 1, 5)
SubstrFunction substr = new SubstrFunction(Arrays.asList(
nameColumn,
LiteralExpression.newConstant(1),
LiteralExpression.newConstant(5)
));
// Evaluate functions
Tuple row = getCurrentRowTuple();
ImmutableBytesWritable result = new ImmutableBytesWritable();
if (upper.evaluate(row, result)) {
String upperName = (String) upper.getDataType().toObject(result);
System.out.println("Upper: " + upperName);
}
if (length.evaluate(row, result)) {
Integer nameLength = (Integer) length.getDataType().toObject(result);
System.out.println("Length: " + nameLength);
}// Mathematical functions
public class RoundFunction extends ScalarFunction {
public RoundFunction(List<Expression> children) // number, [scale]
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
public PDataType getDataType() // Returns number type
}
public class AbsFunction extends ScalarFunction {
public AbsFunction(List<Expression> children)
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
public PDataType getDataType() // Returns same as input
}
public class PowerFunction extends ScalarFunction {
public PowerFunction(List<Expression> children) // base, exponent
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
public PDataType getDataType() // Returns PDouble.INSTANCE
}// Date/time manipulation functions
public class NowFunction extends ScalarFunction {
public NowFunction() // No arguments
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
public PDataType getDataType() // Returns PTimestamp.INSTANCE
}
public class ToDateFunction extends ScalarFunction {
public ToDateFunction(List<Expression> children) // string, [format]
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
public PDataType getDataType() // Returns PDate.INSTANCE
}
public class YearFunction extends ScalarFunction {
public YearFunction(List<Expression> children) // date
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
public PDataType getDataType() // Returns PInteger.INSTANCE
}// Standard aggregate functions
public class CountAggregateFunction extends SingleAggregateFunction {
public CountAggregateFunction(List<Expression> children)
public Aggregator newAggregator()
public PDataType getDataType() // Returns PLong.INSTANCE
}
public class SumAggregateFunction extends SingleAggregateFunction {
public SumAggregateFunction(List<Expression> children)
public Aggregator newAggregator()
public PDataType getDataType() // Returns numeric type
}
public class AvgAggregateFunction extends SingleAggregateFunction {
public AvgAggregateFunction(List<Expression> children)
public Aggregator newAggregator()
public PDataType getDataType() // Returns PDecimal.INSTANCE
}
public class MinAggregateFunction extends SingleAggregateFunction {
public MinAggregateFunction(List<Expression> children)
public Aggregator newAggregator()
public PDataType getDataType() // Returns same as input
}
public class MaxAggregateFunction extends SingleAggregateFunction {
public MaxAggregateFunction(List<Expression> children)
public Aggregator newAggregator()
public PDataType getDataType() // Returns same as input
}Usage:
// Aggregate function examples
Expression salaryColumn = new ColumnExpression(salaryColumn, tableBytes);
// COUNT(*)
CountAggregateFunction count = new CountAggregateFunction(
Arrays.asList(LiteralExpression.newConstant(1))
);
// SUM(salary)
SumAggregateFunction sum = new SumAggregateFunction(Arrays.asList(salaryColumn));
// AVG(salary)
AvgAggregateFunction avg = new AvgAggregateFunction(Arrays.asList(salaryColumn));
// MIN(salary)
MinAggregateFunction min = new MinAggregateFunction(Arrays.asList(salaryColumn));
// MAX(salary)
MaxAggregateFunction max = new MaxAggregateFunction(Arrays.asList(salaryColumn));
// Create aggregators
Aggregator countAgg = count.newAggregator();
Aggregator sumAgg = sum.newAggregator();
Aggregator avgAgg = avg.newAggregator();
// Process rows
while (hasMoreRows()) {
Tuple row = getNextRow();
// Aggregate each row
countAgg.aggregate(row, null);
ImmutableBytesWritable ptr = new ImmutableBytesWritable();
if (salaryColumn.evaluate(row, ptr)) {
sumAgg.aggregate(row, ptr);
avgAgg.aggregate(row, ptr);
}
}
// Get final results
ImmutableBytesWritable result = new ImmutableBytesWritable();
if (countAgg.evaluate(null, result)) {
Long countValue = (Long) count.getDataType().toObject(result);
System.out.println("Count: " + countValue);
}
if (sumAgg.evaluate(null, result)) {
Object sumValue = sum.getDataType().toObject(result);
System.out.println("Sum: " + sumValue);
}// CASE WHEN expressions
public class CaseExpression extends ScalarFunction {
public CaseExpression(List<Expression> children)
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
public PDataType getDataType()
}
// Usage: CASE WHEN age < 18 THEN 'Minor' ELSE 'Adult' END
Expression ageColumn = new ColumnExpression(ageColumn, tableBytes);
Expression condition = new ComparisonExpression(
CompareOp.LESS,
Arrays.asList(ageColumn, LiteralExpression.newConstant(18))
);
Expression caseExpr = new CaseExpression(Arrays.asList(
condition,
LiteralExpression.newConstant("Minor"),
LiteralExpression.newConstant("Adult")
));// Arithmetic operations
public class ArithmeticExpression extends ScalarFunction {
public ArithmeticExpression(ArithmeticOp op, List<Expression> children)
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
public PDataType getDataType()
}
// Usage: salary * 1.1 (10% raise)
Expression salaryColumn = new ColumnExpression(salaryColumn, tableBytes);
Expression raiseMultiplier = LiteralExpression.newConstant(new BigDecimal("1.1"));
Expression raisedSalary = new ArithmeticExpression(
ArithmeticOp.MULT,
Arrays.asList(salaryColumn, raiseMultiplier)
);// Comparison operations
public class ComparisonExpression extends ScalarFunction {
public ComparisonExpression(CompareOp op, List<Expression> children)
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr)
public PDataType getDataType() // Returns PBoolean.INSTANCE
}
// Usage: age >= 21
Expression ageColumn = new ColumnExpression(ageColumn, tableBytes);
Expression legalAge = LiteralExpression.newConstant(21);
Expression isLegalAge = new ComparisonExpression(
CompareOp.GREATER_OR_EQUAL,
Arrays.asList(ageColumn, legalAge)
);public class ExpressionBuilder {
private final PTable table;
private final byte[] tableBytes;
public ExpressionBuilder(PTable table) {
this.table = table;
this.tableBytes = table.getName().getBytes();
}
public Expression buildFullNameExpression() {
// CONCAT(first_name, ' ', last_name)
Expression firstName = new ColumnExpression(table.getColumn("first_name"), tableBytes);
Expression lastName = new ColumnExpression(table.getColumn("last_name"), tableBytes);
Expression space = LiteralExpression.newConstant(" ");
return new ConcatFunction(Arrays.asList(firstName, space, lastName));
}
public Expression buildAgeGroupExpression() {
// CASE
// WHEN age < 18 THEN 'Minor'
// WHEN age < 65 THEN 'Adult'
// ELSE 'Senior'
// END
Expression ageColumn = new ColumnExpression(table.getColumn("age"), tableBytes);
Expression isMinor = new ComparisonExpression(
CompareOp.LESS,
Arrays.asList(ageColumn, LiteralExpression.newConstant(18))
);
Expression isAdult = new ComparisonExpression(
CompareOp.LESS,
Arrays.asList(ageColumn, LiteralExpression.newConstant(65))
);
return new CaseExpression(Arrays.asList(
isMinor, LiteralExpression.newConstant("Minor"),
isAdult, LiteralExpression.newConstant("Adult"),
LiteralExpression.newConstant("Senior")
));
}
public Expression buildSalaryBonusExpression() {
// salary + (salary * bonus_percentage / 100)
Expression salary = new ColumnExpression(table.getColumn("salary"), tableBytes);
Expression bonusPercent = new ColumnExpression(table.getColumn("bonus_percentage"), tableBytes);
Expression hundred = LiteralExpression.newConstant(100);
Expression bonusAmount = new ArithmeticExpression(
ArithmeticOp.MULT,
Arrays.asList(salary, bonusPercent)
);
Expression bonusDivided = new ArithmeticExpression(
ArithmeticOp.DIV,
Arrays.asList(bonusAmount, hundred)
);
return new ArithmeticExpression(
ArithmeticOp.ADD,
Arrays.asList(salary, bonusDivided)
);
}
}
// Usage
PTable employeeTable = connection.getTable(PNameFactory.newName("employees"));
ExpressionBuilder builder = new ExpressionBuilder(employeeTable);
Expression fullName = builder.buildFullNameExpression();
Expression ageGroup = builder.buildAgeGroupExpression();
Expression totalSalary = builder.buildSalaryBonusExpression();
// Evaluate expressions for a row
Tuple employeeRow = getCurrentEmployeeRow();
ImmutableBytesWritable result = new ImmutableBytesWritable();
if (fullName.evaluate(employeeRow, result)) {
String name = (String) fullName.getDataType().toObject(result);
System.out.println("Full Name: " + name);
}
if (ageGroup.evaluate(employeeRow, result)) {
String group = (String) ageGroup.getDataType().toObject(result);
System.out.println("Age Group: " + group);
}
if (totalSalary.evaluate(employeeRow, result)) {
BigDecimal total = (BigDecimal) totalSalary.getDataType().toObject(result);
System.out.println("Total Salary: $" + total);
}// Custom scalar function example
public class AgeFromBirthdateFunction extends ScalarFunction {
public AgeFromBirthdateFunction(List<Expression> children) {
super("AGE_FROM_BIRTHDATE", children);
if (children.size() != 1) {
throw new IllegalArgumentException("AGE_FROM_BIRTHDATE expects 1 argument");
}
}
@Override
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
Expression birthdateExpr = getChildren().get(0);
if (!birthdateExpr.evaluate(tuple, ptr)) {
return false;
}
if (ptr.getLength() == 0) {
return true; // NULL input -> NULL output
}
// Get birthdate value
Date birthdate = (Date) birthdateExpr.getDataType().toObject(ptr);
// Calculate age
Calendar birth = Calendar.getInstance();
birth.setTime(birthdate);
Calendar now = Calendar.getInstance();
int age = now.get(Calendar.YEAR) - birth.get(Calendar.YEAR);
if (now.get(Calendar.DAY_OF_YEAR) < birth.get(Calendar.DAY_OF_YEAR)) {
age--;
}
// Convert result to bytes
return PInteger.INSTANCE.toBytes(age, ptr.get(), ptr.getOffset());
}
@Override
public PDataType getDataType() {
return PInteger.INSTANCE;
}
}
// Usage
Expression birthdateColumn = new ColumnExpression(table.getColumn("birthdate"), tableBytes);
AgeFromBirthdateFunction ageFunc = new AgeFromBirthdateFunction(Arrays.asList(birthdateColumn));
// Evaluate custom function
Tuple row = getCurrentRow();
ImmutableBytesWritable result = new ImmutableBytesWritable();
if (ageFunc.evaluate(row, result)) {
Integer age = (Integer) ageFunc.getDataType().toObject(result);
System.out.println("Calculated age: " + age);
}Install with Tessl CLI
npx tessl i tessl/maven-org-apache-phoenix--phoenix-core