[ https://issues.apache.org/jira/browse/PHOENIX-476?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15558377#comment-15558377 ]
James Taylor commented on PHOENIX-476: -------------------------------------- Thanks for the patch, [~kliew]. Here's some feedback: - Let's revert this change to ExpressionUtil as it's creating too many diffs in other places. Plus, you'll only use the logic to check that an expression is a constant in one place: {code} - public static boolean isConstant(Expression expression) { + public static boolean isConstant(Expression expression) { + return (expression.isStateless() && expression.getDeterminism() == Determinism.ALWAYS); + } + + public static boolean isPureExpression(Expression expression) { {code} - This change in ExpressionCompiler can be much simpler. No need to recreate a ColumnDef. Also, PColumn needs to stay immutable, so no setters there. Instead, just parse the expressionStr using {{expressionNode = new SQLParser(expressionStr).parseExpression()}} and call expressionNode.accept(this) to get the Expression here (and minor nit, but just check that {{column.getExpressionStr() != null}}. Shouldn't be any need to check if empty too): {code} + if (!SchemaUtil.isPKColumn(column)) { + if (column.getExpressionStr() != null && !column.getExpressionStr().isEmpty() && column.getDefaultExpression() == null) { + // We have the default expression but it has not been compiled yet + ExpressionCompiler compiler = new ExpressionCompiler(context); + ColumnDef columnDef = new ParseNodeFactory().columnDef( + ColumnName.caseSensitiveColumnName(column.getFamilyName().getString(), column.getName().getString()), + column.getDataType().getSqlTypeName(), + column.isNullable(), + column.getMaxLength(), + column.getScale(), + false, + column.getSortOrder(), + column.getExpressionStr(), + column.isRowTimestamp()); + Expression defaultExpression = columnDef.getDefaultExpressionNode().accept(compiler); + if (!columnDef.getDefaultExpressionNode().isStateless() || !ExpressionUtil.isConstant(defaultExpression)) { + throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_CREATE_STATEFUL_DEFAULT) + .setColumnName(columnDef.getColumnDefName().getColumnName()).build().buildException(); + } + column.setDefaultExpression(defaultExpression); + } + if (column.getDefaultExpression() != null) { + expression = new DefaultValueExpression(Arrays.asList(expression, column.getDefaultExpression())); + } + } {code} - Also, ColumnDef is immutable and needs to stay that way. Don't bother caching the Expression on it here: {code} + if (columnDef.getDefaultExpressionNode() != null) { + ExpressionCompiler compiler = new ExpressionCompiler(context); + Expression defaultExpression = columnDef.getDefaultExpressionNode().accept(compiler); + if (!columnDef.getDefaultExpressionNode().isStateless() || !ExpressionUtil.isConstant(defaultExpression)) { + throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_CREATE_STATEFUL_DEFAULT) + .setColumnName(columnDef.getColumnDefName().getColumnName()).build().buildException(); + } + columnDef.setDefaultExpression(defaultExpression); + } {code} - Should be able to revert changes to PColumn and PColumnImpl (and sorry to have led you down that path). - There's a bit of code missing in PTableImpl.newKey() here (if this isn't the case, let me know what you mean by the "trailing null issue"): {code} public int newKey(ImmutableBytesWritable key, byte[][] values) { ... // If some non null pk values aren't set, then throw if (i < nColumns) { PColumn column = columns.get(i); if (column.getDataType().isFixedWidth() || !column.isNullable()) { throw new ConstraintViolationException(name.getString() + "." + column.getName().getString() + " may not be null"); } } {code} You'll need to loop until {{i == nColumns}} to check if the column is a PK column with a default value and then evaluate and set the value. You'll need to insert null values for PK columns which don't have values before that. > Support declaration of DEFAULT in CREATE statement > -------------------------------------------------- > > Key: PHOENIX-476 > URL: https://issues.apache.org/jira/browse/PHOENIX-476 > Project: Phoenix > Issue Type: Task > Affects Versions: 3.0-Release > Reporter: James Taylor > Assignee: Kevin Liew > Labels: enhancement > Attachments: PHOENIX-476.2.patch, PHOENIX-476.3.patch, > PHOENIX-476.4.patch, PHOENIX-476.patch > > > Support the declaration of a default value in the CREATE TABLE/VIEW statement > like this: > CREATE TABLE Persons ( > Pid int NOT NULL PRIMARY KEY, > LastName varchar(255) NOT NULL, > FirstName varchar(255), > Address varchar(255), > City varchar(255) DEFAULT 'Sandnes' > ) > To implement this, we'd need to: > 1. add a new DEFAULT_VALUE key value column in SYSTEM.TABLE and pass through > the value when the table is created (in MetaDataClient). > 2. always set NULLABLE to ResultSetMetaData.columnNoNulls if a default value > is present, since the column will never be null. > 3. add a getDefaultValue() accessor in PColumn > 4. for a row key column, during UPSERT use the default value if no value was > specified for that column. This could be done in the PTableImpl.newKey method. > 5. for a key value column with a default value, we can get away without > incurring any storage cost. Although a little bit of extra effort than if we > persisted the default value on an UPSERT for key value columns, this approach > has the benefit of not incurring any storage cost for a default value. > * serialize any default value into KeyValueColumnExpression > * in the evaluate method of KeyValueColumnExpression, conditionally use > the default value if the column value is not present. If doing partial > evaluation, you should not yet return the default value, as we may not have > encountered the the KeyValue for the column yet (since a filter evaluates > each time it sees each KeyValue, and there may be more than one KeyValue > referenced in the expression). Partial evaluation is determined by calling > Tuple.isImmutable(), where false means it is NOT doing partial evaluation, > while true means it is. > * modify EvaluateOnCompletionVisitor by adding a visitor method for > RowKeyColumnExpression and KeyValueColumnExpression to set > evaluateOnCompletion to true if they have a default value specified. This > will cause filter evaluation to execute one final time after all KeyValues > for a row have been seen, since it's at this time we know we should use the > default value. -- This message was sent by Atlassian JIRA (v6.3.4#6332)