/** * Encode the primary key values from the table as a byte array. The values must be in the same * order as the primary key constraint. If the connection and table are both tenant-specific, the * tenant ID column must not be present in the values. * * @param conn an open connection * @param fullTableName the full table name * @param values the values of the primary key columns ordered in the same order as the primary * key constraint * @return the encoded byte array * @throws SQLException if the table cannot be found or the incorrect number of of values are * provided * @see #decodePK(Connection, String, byte[]) to decode the byte[] back to the values */ public static byte[] encodePK(Connection conn, String fullTableName, Object[] values) throws SQLException { PTable table = getTable(conn, fullTableName); PhoenixConnection pconn = conn.unwrap(PhoenixConnection.class); int offset = (table.getBucketNum() == null ? 0 : 1) + (table.isMultiTenant() && pconn.getTenantId() != null ? 1 : 0); List<PColumn> pkColumns = table.getPKColumns(); if (pkColumns.size() - offset != values.length) { throw new SQLException( "Expected " + (pkColumns.size() - offset) + " but got " + values.length); } PDataType type = null; TrustedByteArrayOutputStream output = new TrustedByteArrayOutputStream(table.getRowKeySchema().getEstimatedValueLength()); try { for (int i = offset; i < pkColumns.size(); i++) { if (type != null && !type.isFixedWidth()) { output.write(QueryConstants.SEPARATOR_BYTE); } type = pkColumns.get(i).getDataType(); byte[] value = type.toBytes(values[i - offset]); output.write(value); } return output.toByteArray(); } finally { try { output.close(); } catch (IOException e) { throw new RuntimeException(e); // Impossible } } }
private void evaluateAndAssertResult( Expression expression, Object expectedResult, String context) { context = context == null ? "" : context; ImmutableBytesWritable ptr = new ImmutableBytesWritable(); assertTrue(expression.evaluate(null, ptr)); PDataType dataType = expression.getDataType(); SortOrder sortOrder = expression.getSortOrder(); Object result = dataType.toObject(ptr.get(), ptr.getOffset(), ptr.getLength(), dataType, sortOrder); assertEquals(context, expectedResult, result); }
@Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (isNullable() ? 1231 : 1237); Integer maxLength = this.getMaxLength(); result = prime * result + ((maxLength == null) ? 0 : maxLength.hashCode()); PDataType type = this.getDataType(); result = prime * result + ((type == null) ? 0 : type.hashCode()); return result; }
@Override public final Object getValue(Tuple tuple, PDataType type, ImmutableBytesWritable ptr) throws SQLException { try { Expression expression = getExpression(); if (!expression.evaluate(tuple, ptr)) { return null; } if (ptr.getLength() == 0) { return null; } return type.toObject( ptr, expression.getDataType(), expression.getSortOrder(), expression.getMaxLength(), expression.getScale()); } catch (RuntimeException e) { // FIXME: Expression.evaluate does not throw SQLException // so this will unwrap throws from that. if (e.getCause() instanceof SQLException) { throw (SQLException) e.getCause(); } throw e; } }
public static Expression convertToRoundExpressionIfNeeded( PDataType fromDataType, PDataType targetDataType, List<Expression> expressions) throws SQLException { Expression firstChildExpr = expressions.get(0); if (fromDataType == targetDataType) { return firstChildExpr; } else if (fromDataType == PDataType.DECIMAL && targetDataType.isCoercibleTo(PDataType.LONG)) { return new RoundDecimalExpression(expressions); } else if ((fromDataType == PDataType.TIMESTAMP || fromDataType == PDataType.UNSIGNED_TIMESTAMP) && targetDataType.isCoercibleTo(PDataType.DATE)) { return RoundTimestampExpression.create(expressions); } else if (!fromDataType.isCoercibleTo(targetDataType)) { throw TypeMismatchException.newException( fromDataType, targetDataType, firstChildExpr.toString()); } return firstChildExpr; }
private void appendPKColumnValue(StringBuilder buf, byte[] range, Boolean isNull, int slotIndex) { if (Boolean.TRUE.equals(isNull)) { buf.append("null"); return; } if (Boolean.FALSE.equals(isNull)) { buf.append("not null"); return; } if (range.length == 0) { buf.append('*'); return; } ScanRanges scanRanges = context.getScanRanges(); PDataType type = scanRanges.getSchema().getField(slotIndex).getDataType(); ColumnModifier modifier = tableRef.getTable().getPKColumns().get(slotIndex).getColumnModifier(); if (modifier != null) { buf.append('~'); range = modifier.apply(range, 0, new byte[range.length], 0, range.length); } Format formatter = context.getConnection().getFormatter(type); buf.append(type.toStringLiteral(range, formatter)); }
@Override public void readFields(DataInput input) throws IOException { // read/write type ordinal, maxLength presence, scale presence and isNullable bit together to // save space int typeAndFlag = WritableUtils.readVInt(input); isNullable = (typeAndFlag & 0x01) != 0; if ((typeAndFlag & 0x02) != 0) { scale = WritableUtils.readVInt(input); } if ((typeAndFlag & 0x04) != 0) { maxLength = WritableUtils.readVInt(input); } type = PDataType.values()[typeAndFlag >>> 3]; sortOrder = SortOrder.fromSystemValue(WritableUtils.readVInt(input)); }
@Override public void write(DataOutput output) throws IOException { // read/write type ordinal, maxLength presence, scale presence and isNullable bit together to // save space int typeAndFlag = (isNullable ? 1 : 0) | ((scale != null ? 1 : 0) << 1) | ((maxLength != null ? 1 : 0) << 2) | (type.ordinal() << 3); WritableUtils.writeVInt(output, typeAndFlag); if (scale != null) { WritableUtils.writeVInt(output, scale); } if (maxLength != null) { WritableUtils.writeVInt(output, maxLength); } WritableUtils.writeVInt(output, sortOrder.getSystemValue()); }
@Override public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { tuple.getKey(ptr); int offset = accessor.getOffset(ptr.get(), ptr.getOffset()); // Null is represented in the last expression of a multi-part key // by the bytes not being present. int maxOffset = ptr.getOffset() + ptr.getLength(); if (offset < maxOffset) { byte[] buffer = ptr.get(); int fixedByteSize = -1; // FIXME: fixedByteSize <= maxByteSize ? fixedByteSize : 0 required because HBase passes bogus // keys to filter to position scan (HBASE-6562) if (fromType.isFixedWidth()) { fixedByteSize = getByteSize(); fixedByteSize = fixedByteSize <= maxOffset ? fixedByteSize : 0; } int length = fixedByteSize >= 0 ? fixedByteSize : accessor.getLength(buffer, offset, maxOffset); // In the middle of the key, an empty variable length byte array represents null if (length > 0) { /* if (type == fromType) { ptr.set(buffer,offset,length); } else { ptr.set(type.toBytes(type.toObject(buffer, offset, length, fromType))); } */ ptr.set(buffer, offset, length); type.coerceBytes(ptr, fromType, getSortOrder(), getSortOrder()); } else { ptr.set(ByteUtil.EMPTY_BYTE_ARRAY); } return true; } return false; }
CastParseNode(ParseNode expr, String dataType) { super(expr); dt = PDataType.fromSqlTypeName(dataType); }
/** * Get list of ColumnInfos that contain Column Name and its associated PDataType for an import. * The supplied list of columns can be null -- if it is non-null, it represents a user-supplied * list of columns to be imported. * * @param conn Phoenix connection from which metadata will be read * @param tableName Phoenix table name whose columns are to be checked. Can include a schema name * @param columns user-supplied list of import columns, can be null * @param strict if true, an exception will be thrown if unknown columns are supplied */ public static List<ColumnInfo> generateColumnInfo( Connection conn, String tableName, List<String> columns, boolean strict) throws SQLException { Map<String, Integer> columnNameToTypeMap = Maps.newLinkedHashMap(); DatabaseMetaData dbmd = conn.getMetaData(); int unfoundColumnCount = 0; // TODO: escape wildcard characters here because we don't want that // behavior here String escapedTableName = StringUtil.escapeLike(tableName); String[] schemaAndTable = escapedTableName.split("\\."); ResultSet rs = null; try { rs = dbmd.getColumns( null, (schemaAndTable.length == 1 ? "" : schemaAndTable[0]), (schemaAndTable.length == 1 ? escapedTableName : schemaAndTable[1]), null); while (rs.next()) { String sqlTypeName = rs.getString(QueryUtil.DATA_TYPE_NAME_POSITION); columnNameToTypeMap.put( rs.getString(QueryUtil.COLUMN_NAME_POSITION), PDataType.fromSqlTypeName(sqlTypeName).getSqlType()); } if (columnNameToTypeMap.isEmpty()) { throw new IllegalArgumentException("Table " + tableName + " not found"); } } finally { if (rs != null) { rs.close(); } } List<ColumnInfo> columnInfoList = Lists.newArrayList(); if (columns == null) { for (Map.Entry<String, Integer> entry : columnNameToTypeMap.entrySet()) { columnInfoList.add(new ColumnInfo(entry.getKey(), entry.getValue())); } } else { // Leave "null" as indication to skip b/c it doesn't exist for (int i = 0; i < columns.size(); i++) { String columnName = columns.get(i).trim(); Integer sqlType = columnNameToTypeMap.get(columnName); if (sqlType == null) { if (strict) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.COLUMN_NOT_FOUND) .setColumnName(columnName) .setTableName(tableName) .build() .buildException(); } unfoundColumnCount++; } else { columnInfoList.add(new ColumnInfo(columnName, sqlType)); } } if (unfoundColumnCount == columns.size()) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.COLUMN_NOT_FOUND) .setColumnName(Arrays.toString(columns.toArray(new String[0]))) .setTableName(tableName) .build() .buildException(); } } return columnInfoList; }
private static String getTypeDisplayString(PDataType type, Integer precision, Integer scale) { return type.toString() + "(" + precision + "," + scale + ")"; }