blob: 72905ca7dc08be6bb7ade898f359243f29d83034 [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hive.metastore;
import com.google.common.base.Joiner;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.hadoop.hive.metastore.api.FieldSchema;
import org.apache.hadoop.hive.metastore.api.MetaException;
import org.apache.hadoop.hive.metastore.api.Order;
import org.apache.hadoop.hive.metastore.api.Partition;
import org.apache.hadoop.hive.metastore.api.SerDeInfo;
import org.apache.hadoop.hive.metastore.api.SkewedInfo;
import org.apache.hadoop.hive.metastore.api.StorageDescriptor;
import org.apache.hadoop.hive.metastore.utils.MetaStoreServerUtils;
import org.apache.hadoop.hive.metastore.utils.MetaStoreUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import java.math.BigDecimal;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
/**
* Helper utilities used by DirectSQL code in HiveMetastore.
*/
class MetastoreDirectSqlUtils {
private static final Logger LOG = LoggerFactory.getLogger(MetastoreDirectSqlUtils.class);
private MetastoreDirectSqlUtils() {
}
@SuppressWarnings("unchecked")
static <T> T executeWithArray(Query query, Object[] params, String sql) throws MetaException {
return (T)executeWithArray(query, params, sql, -1);
}
@SuppressWarnings("unchecked")
static <T> T executeWithArray(Query query, Object[] params, String sql, int limit) throws MetaException {
try {
if (limit >= 0)
query.setRange(0, limit);
return (T)((params == null) ? query.execute() : query.executeWithArray(params));
} catch (Exception ex) {
StringBuilder errorBuilder = new StringBuilder("Failed to execute [" + sql + "] with parameters [");
if (params != null) {
boolean isFirst = true;
for (Object param : params) {
errorBuilder.append((isFirst ? "" : ", ") + param);
isFirst = false;
}
}
LOG.warn(errorBuilder.toString() + "]", ex);
// We just logged an exception with (in case of JDO) a humongous callstack. Make a new one.
throw new MetaException("See previous errors; " + ex.getMessage() + errorBuilder.toString() + "]");
}
}
@SuppressWarnings("unchecked")
static List<Object[]> ensureList(Object result) throws MetaException {
if (!(result instanceof List<?>)) {
throw new MetaException("Wrong result type " + result.getClass());
}
return (List<Object[]>)result;
}
static Long extractSqlLong(Object obj) throws MetaException {
if (obj == null) return null;
if (!(obj instanceof Number)) {
throw new MetaException("Expected numeric type but got " + obj.getClass().getName());
}
return ((Number)obj).longValue();
}
static void timingTrace(boolean doTrace, String queryText, long start, long queryTime) {
if (!doTrace) return;
LOG.debug("Direct SQL query in " + (queryTime - start) / 1000000.0 + "ms + " +
(System.nanoTime() - queryTime) / 1000000.0 + "ms, the query is [" + queryText + "]");
}
static <T> int loopJoinOrderedResult(PersistenceManager pm, TreeMap<Long, T> tree,
String queryText, int keyIndex, ApplyFunc<T> func) throws MetaException {
return loopJoinOrderedResult(pm, tree, queryText, null, keyIndex, func);
}
/**
* Merges applies the result of a PM SQL query into a tree of object.
* Essentially it's an object join. DN could do this for us, but it issues queries
* separately for every object, which is suboptimal.
* @param pm
* @param tree The object tree, by ID.
* @param queryText The query text.
* @param keyIndex Index of the Long column corresponding to the map ID in query result rows.
* @param func The function that is called on each (object,row) pair with the same id.
* @return the count of results returned from the query.
*/
static <T> int loopJoinOrderedResult(PersistenceManager pm, TreeMap<Long, T> tree,
String queryText, Object[] parameters, int keyIndex, ApplyFunc<T> func) throws MetaException {
boolean doTrace = LOG.isDebugEnabled();
long start = doTrace ? System.nanoTime() : 0;
int rv = 0;
long queryTime = 0;
try (Query query = pm.newQuery("javax.jdo.query.SQL", queryText)) {
Object result = null;
if (parameters == null || parameters.length == 0) {
result = query.execute();
} else {
result = query.executeWithArray(parameters);
}
queryTime = doTrace ? System.nanoTime() : 0;
if (result == null) {
query.closeAll();
return 0;
}
List<Object[]> list = ensureList(result);
Iterator<Object[]> iter = list.iterator();
Object[] fields = null;
for (Map.Entry<Long, T> entry : tree.entrySet()) {
if (fields == null && !iter.hasNext())
break;
long id = entry.getKey();
while (fields != null || iter.hasNext()) {
if (fields == null) {
fields = iter.next();
}
long nestedId = extractSqlLong(fields[keyIndex]);
if (nestedId < id) {
throw new MetaException("Found entries for unknown ID " + nestedId);
}
if (nestedId > id)
break; // fields belong to one of the next entries
func.apply(entry.getValue(), fields);
fields = null;
}
Deadline.checkTimeout();
}
rv = list.size();
} catch (Exception e) {
throwMetaOrRuntimeException(e);
}
timingTrace(doTrace, queryText, start, queryTime);
return rv;
}
static void setPartitionParameters(String PARTITION_PARAMS, boolean convertMapNullsToEmptyStrings,
PersistenceManager pm, String partIds, TreeMap<Long, Partition> partitions)
throws MetaException {
String queryText;
queryText = "select \"PART_ID\", \"PARAM_KEY\", \"PARAM_VALUE\" from " + PARTITION_PARAMS + ""
+ " where \"PART_ID\" in (" + partIds + ") and \"PARAM_KEY\" is not null"
+ " order by \"PART_ID\" asc";
loopJoinOrderedResult(pm, partitions, queryText, 0, new ApplyFunc<Partition>() {
@Override
public void apply(Partition t, Object[] fields) {
t.putToParameters(extractSqlClob(fields[1]), extractSqlClob(fields[2]));
}});
// Perform conversion of null map values
for (Partition t : partitions.values()) {
t.setParameters(MetaStoreServerUtils.trimMapNulls(t.getParameters(), convertMapNullsToEmptyStrings));
}
}
static void setPartitionParametersWithFilter(String PARTITION_PARAMS,
boolean convertMapNullsToEmptyStrings, PersistenceManager pm, String partIds,
TreeMap<Long, Partition> partitions, String includeParamKeyPattern, String excludeParamKeyPattern)
throws MetaException {
StringBuilder queryTextBuilder = new StringBuilder("select \"PART_ID\", \"PARAM_KEY\", \"PARAM_VALUE\" from ")
.append(PARTITION_PARAMS)
.append(" where \"PART_ID\" in (")
.append(partIds)
.append(") and \"PARAM_KEY\" is not null");
List<Object> queryParams = new ArrayList<>(2);;
if (includeParamKeyPattern != null && !includeParamKeyPattern.isEmpty()) {
queryTextBuilder.append(" and \"PARAM_KEY\" LIKE (?)");
queryParams.add(includeParamKeyPattern);
}
if (excludeParamKeyPattern != null && !excludeParamKeyPattern.isEmpty()) {
queryTextBuilder.append(" and \"PARAM_KEY\" NOT LIKE (?)");
queryParams.add(excludeParamKeyPattern);
}
queryTextBuilder.append(" order by \"PART_ID\" asc");
String queryText = queryTextBuilder.toString();
loopJoinOrderedResult(pm, partitions, queryText, queryParams.toArray(), 0, new ApplyFunc<Partition>() {
@Override
public void apply(Partition t, Object[] fields) {
t.putToParameters((String) fields[1], extractSqlClob(fields[2]));
}
});
// Perform conversion of null map values
for (Partition t : partitions.values()) {
t.setParameters(MetaStoreServerUtils.trimMapNulls(t.getParameters(), convertMapNullsToEmptyStrings));
}
}
static void setPartitionValues(String PARTITION_KEY_VALS, PersistenceManager pm, String partIds,
TreeMap<Long, Partition> partitions)
throws MetaException {
String queryText;
queryText = "select \"PART_ID\", \"PART_KEY_VAL\" from " + PARTITION_KEY_VALS + ""
+ " where \"PART_ID\" in (" + partIds + ")"
+ " order by \"PART_ID\" asc, \"INTEGER_IDX\" asc";
loopJoinOrderedResult(pm, partitions, queryText, 0, new ApplyFunc<Partition>() {
@Override
public void apply(Partition t, Object[] fields) {
t.addToValues((String)fields[1]);
}});
}
static String extractSqlClob(Object value) {
if (value == null) return null;
try {
if (value instanceof Clob) {
// we trim the Clob value to a max length an int can hold
int maxLength = (((Clob)value).length() < Integer.MAX_VALUE - 2) ? (int)((Clob)value).length() : Integer.MAX_VALUE - 2;
return ((Clob)value).getSubString(1L, maxLength);
} else {
return value.toString();
}
} catch (SQLException sqle) {
return null;
}
}
static void setSDParameters(String SD_PARAMS, boolean convertMapNullsToEmptyStrings,
PersistenceManager pm, TreeMap<Long, StorageDescriptor> sds, String sdIds)
throws MetaException {
String queryText;
queryText = "select \"SD_ID\", \"PARAM_KEY\", \"PARAM_VALUE\" from " + SD_PARAMS + ""
+ " where \"SD_ID\" in (" + sdIds + ") and \"PARAM_KEY\" is not null"
+ " order by \"SD_ID\" asc";
loopJoinOrderedResult(pm, sds, queryText, 0, new ApplyFunc<StorageDescriptor>() {
@Override
public void apply(StorageDescriptor t, Object[] fields) {
t.putToParameters((String)fields[1], extractSqlClob(fields[2]));
}});
// Perform conversion of null map values
for (StorageDescriptor t : sds.values()) {
t.setParameters(MetaStoreServerUtils.trimMapNulls(t.getParameters(), convertMapNullsToEmptyStrings));
}
}
static int extractSqlInt(Object field) {
return ((Number)field).intValue();
}
static void setSDSortCols(String SORT_COLS, List<String> columnNames, PersistenceManager pm,
TreeMap<Long, StorageDescriptor> sds, String sdIds)
throws MetaException {
StringBuilder queryTextBuilder = new StringBuilder("select \"SD_ID\"");
int counter = 0;
if (columnNames.contains("col")) {
counter++;
queryTextBuilder.append(", \"COLUMN_NAME\"");
}
if (columnNames.contains("order")) {
counter++;
queryTextBuilder.append(", \"ORDER\"");
}
queryTextBuilder
.append(" from ")
.append(SORT_COLS)
.append(" where \"SD_ID\" in (")
.append(sdIds)
.append(") order by \"SD_ID\" asc, \"INTEGER_IDX\" asc");
String queryText = queryTextBuilder.toString();
final int finalCounter = counter;
loopJoinOrderedResult(pm, sds, queryText, 0, new ApplyFunc<StorageDescriptor>() {
@Override
public void apply(StorageDescriptor t, Object[] fields) {
if (finalCounter > 1 && fields[2] == null) {
return;
}
Order order = new Order();
if (finalCounter > 0) {
order.setCol((String) fields[1]);
}
if (finalCounter > 1) {
order.setOrder(extractSqlInt(fields[2]));
}
t.addToSortCols(order);
}});
}
static void setSDSortCols(String SORT_COLS, PersistenceManager pm,
TreeMap<Long, StorageDescriptor> sds, String sdIds)
throws MetaException {
String queryText;
queryText = "select \"SD_ID\", \"COLUMN_NAME\", " + SORT_COLS + ".\"ORDER\""
+ " from " + SORT_COLS + ""
+ " where \"SD_ID\" in (" + sdIds + ")"
+ " order by \"SD_ID\" asc, \"INTEGER_IDX\" asc";
loopJoinOrderedResult(pm, sds, queryText, 0, new ApplyFunc<StorageDescriptor>() {
@Override
public void apply(StorageDescriptor t, Object[] fields) {
if (fields[2] == null) return;
t.addToSortCols(new Order((String)fields[1], extractSqlInt(fields[2])));
}});
}
static void setSDBucketCols(String BUCKETING_COLS, PersistenceManager pm,
TreeMap<Long, StorageDescriptor> sds, String sdIds)
throws MetaException {
String queryText;
queryText = "select \"SD_ID\", \"BUCKET_COL_NAME\" from " + BUCKETING_COLS + ""
+ " where \"SD_ID\" in (" + sdIds + ")"
+ " order by \"SD_ID\" asc, \"INTEGER_IDX\" asc";
loopJoinOrderedResult(pm, sds, queryText, 0, new ApplyFunc<StorageDescriptor>() {
@Override
public void apply(StorageDescriptor t, Object[] fields) {
t.addToBucketCols((String)fields[1]);
}});
}
static boolean setSkewedColNames(String SKEWED_COL_NAMES, PersistenceManager pm,
TreeMap<Long, StorageDescriptor> sds, String sdIds)
throws MetaException {
String queryText;
queryText = "select \"SD_ID\", \"SKEWED_COL_NAME\" from " + SKEWED_COL_NAMES + ""
+ " where \"SD_ID\" in (" + sdIds + ")"
+ " order by \"SD_ID\" asc, \"INTEGER_IDX\" asc";
return loopJoinOrderedResult(pm, sds, queryText, 0, new ApplyFunc<StorageDescriptor>() {
@Override
public void apply(StorageDescriptor t, Object[] fields) {
if (!t.isSetSkewedInfo()) t.setSkewedInfo(new SkewedInfo());
t.getSkewedInfo().addToSkewedColNames((String)fields[1]);
}}) > 0;
}
static void setSkewedColValues(String SKEWED_STRING_LIST_VALUES, String SKEWED_VALUES,
PersistenceManager pm, TreeMap<Long, StorageDescriptor> sds, String sdIds)
throws MetaException {
String queryText;
queryText =
"select " + SKEWED_VALUES + ".\"SD_ID_OID\","
+ " " + SKEWED_STRING_LIST_VALUES + ".\"STRING_LIST_ID\","
+ " " + SKEWED_STRING_LIST_VALUES + ".\"STRING_LIST_VALUE\" "
+ "from " + SKEWED_VALUES + " "
+ " left outer join " + SKEWED_STRING_LIST_VALUES + " on " + SKEWED_VALUES + "."
+ "\"STRING_LIST_ID_EID\" = " + SKEWED_STRING_LIST_VALUES + ".\"STRING_LIST_ID\" "
+ "where " + SKEWED_VALUES + ".\"SD_ID_OID\" in (" + sdIds + ") "
+ " and " + SKEWED_VALUES + ".\"STRING_LIST_ID_EID\" is not null "
+ " and " + SKEWED_VALUES + ".\"INTEGER_IDX\" >= 0 "
+ "order by " + SKEWED_VALUES + ".\"SD_ID_OID\" asc, " + SKEWED_VALUES + ".\"INTEGER_IDX\" asc,"
+ " " + SKEWED_STRING_LIST_VALUES + ".\"INTEGER_IDX\" asc";
loopJoinOrderedResult(pm, sds, queryText, 0, new ApplyFunc<StorageDescriptor>() {
private Long currentListId;
private List<String> currentList;
@Override
public void apply(StorageDescriptor t, Object[] fields) throws MetaException {
if (!t.isSetSkewedInfo()) t.setSkewedInfo(new SkewedInfo());
// Note that this is not a typical list accumulator - there's no call to finalize
// the last list. Instead we add list to SD first, as well as locally to add elements.
if (fields[1] == null) {
currentList = null; // left outer join produced a list with no values
currentListId = null;
t.getSkewedInfo().addToSkewedColValues(Collections.<String>emptyList());
} else {
long fieldsListId = extractSqlLong(fields[1]);
if (currentListId == null || fieldsListId != currentListId) {
currentList = new ArrayList<String>();
currentListId = fieldsListId;
t.getSkewedInfo().addToSkewedColValues(currentList);
}
currentList.add((String)fields[2]);
}
}});
}
static void setSkewedColLocationMaps(String SKEWED_COL_VALUE_LOC_MAP,
String SKEWED_STRING_LIST_VALUES, PersistenceManager pm, TreeMap<Long, StorageDescriptor> sds,
String sdIds)
throws MetaException {
String queryText;
queryText =
"select " + SKEWED_COL_VALUE_LOC_MAP + ".\"SD_ID\","
+ " " + SKEWED_STRING_LIST_VALUES + ".STRING_LIST_ID,"
+ " " + SKEWED_COL_VALUE_LOC_MAP + ".\"LOCATION\","
+ " " + SKEWED_STRING_LIST_VALUES + ".\"STRING_LIST_VALUE\" "
+ "from " + SKEWED_COL_VALUE_LOC_MAP + ""
+ " left outer join " + SKEWED_STRING_LIST_VALUES + " on " + SKEWED_COL_VALUE_LOC_MAP + "."
+ "\"STRING_LIST_ID_KID\" = " + SKEWED_STRING_LIST_VALUES + ".\"STRING_LIST_ID\" "
+ "where " + SKEWED_COL_VALUE_LOC_MAP + ".\"SD_ID\" in (" + sdIds + ")"
+ " and " + SKEWED_COL_VALUE_LOC_MAP + ".\"STRING_LIST_ID_KID\" is not null "
+ "order by " + SKEWED_COL_VALUE_LOC_MAP + ".\"SD_ID\" asc,"
+ " " + SKEWED_STRING_LIST_VALUES + ".\"STRING_LIST_ID\" asc,"
+ " " + SKEWED_STRING_LIST_VALUES + ".\"INTEGER_IDX\" asc";
loopJoinOrderedResult(pm, sds, queryText, 0, new ApplyFunc<StorageDescriptor>() {
private Long currentListId;
private List<String> currentList;
@Override
public void apply(StorageDescriptor t, Object[] fields) throws MetaException {
if (!t.isSetSkewedInfo()) {
SkewedInfo skewedInfo = new SkewedInfo();
skewedInfo.setSkewedColValueLocationMaps(new HashMap<List<String>, String>());
t.setSkewedInfo(skewedInfo);
}
Map<List<String>, String> skewMap = t.getSkewedInfo().getSkewedColValueLocationMaps();
// Note that this is not a typical list accumulator - there's no call to finalize
// the last list. Instead we add list to SD first, as well as locally to add elements.
if (fields[1] == null) {
currentList = new ArrayList<String>(); // left outer join produced a list with no values
currentListId = null;
} else {
long fieldsListId = extractSqlLong(fields[1]);
if (currentListId == null || fieldsListId != currentListId) {
currentList = new ArrayList<String>();
currentListId = fieldsListId;
} else {
skewMap.remove(currentList); // value based compare.. remove first
}
currentList.add((String)fields[3]);
}
skewMap.put(currentList, (String)fields[2]);
}});
}
static void setSDCols(String COLUMNS_V2, List<String> columnNames, PersistenceManager pm,
TreeMap<Long, List<FieldSchema>> colss, String colIds)
throws MetaException {
StringBuilder queryTextBuilder = new StringBuilder("select \"CD_ID\"");
int counter = 0;
if (columnNames.contains("name")) {
counter++;
queryTextBuilder.append(", \"COLUMN_NAME\"");
}
if (columnNames.contains("type")) {
counter++;
queryTextBuilder.append(", \"TYPE_NAME\"");
}
if (columnNames.contains("comment")) {
counter++;
queryTextBuilder.append(", \"COMMENT\"");
}
queryTextBuilder
.append(" from ")
.append(COLUMNS_V2)
.append(" where \"CD_ID\" in (")
.append(colIds)
.append(") order by \"CD_ID\" asc, \"INTEGER_IDX\" asc");
String queryText = queryTextBuilder.toString();
int finalCounter = counter;
loopJoinOrderedResult(pm, colss, queryText, 0, new ApplyFunc<List<FieldSchema>>() {
@Override
public void apply(List<FieldSchema> t, Object[] fields) {
FieldSchema fieldSchema = new FieldSchema();
if (finalCounter > 0) {
fieldSchema.setName((String) fields[1]);
}
if (finalCounter > 1) {
fieldSchema.setType(extractSqlClob(fields[2]));
}
if (finalCounter > 2) {
fieldSchema.setComment((String) fields[3]);
}
t.add(fieldSchema);
}});
}
static void setSDCols(String COLUMNS_V2, PersistenceManager pm,
TreeMap<Long, List<FieldSchema>> colss, String colIds)
throws MetaException {
String queryText;
queryText = "select \"CD_ID\", \"COMMENT\", \"COLUMN_NAME\", \"TYPE_NAME\""
+ " from " + COLUMNS_V2 + " where \"CD_ID\" in (" + colIds + ")"
+ " order by \"CD_ID\" asc, \"INTEGER_IDX\" asc";
loopJoinOrderedResult(pm, colss, queryText, 0, new ApplyFunc<List<FieldSchema>>() {
@Override
public void apply(List<FieldSchema> t, Object[] fields) {
t.add(new FieldSchema((String)fields[2], extractSqlClob(fields[3]), (String)fields[1]));
}});
}
static void setSerdeParams(String SERDE_PARAMS, boolean convertMapNullsToEmptyStrings,
PersistenceManager pm, TreeMap<Long, SerDeInfo> serdes, String serdeIds) throws MetaException {
String queryText;
queryText = "select \"SERDE_ID\", \"PARAM_KEY\", \"PARAM_VALUE\" from " + SERDE_PARAMS + ""
+ " where \"SERDE_ID\" in (" + serdeIds + ") and \"PARAM_KEY\" is not null"
+ " order by \"SERDE_ID\" asc";
loopJoinOrderedResult(pm, serdes, queryText, 0, new ApplyFunc<SerDeInfo>() {
@Override
public void apply(SerDeInfo t, Object[] fields) {
t.putToParameters((String)fields[1], extractSqlClob(fields[2]));
}});
// Perform conversion of null map values
for (SerDeInfo t : serdes.values()) {
t.setParameters(MetaStoreServerUtils.trimMapNulls(t.getParameters(), convertMapNullsToEmptyStrings));
}
}
/**
* Convert a boolean value returned from the RDBMS to a Java Boolean object.
* MySQL has booleans, but e.g. Derby uses 'Y'/'N' mapping and Oracle DB
* doesn't supports boolean, so we have used Number to store the value,
* which maps to BigDecimal.
*
* @param value
* column value from the database
* @return The Boolean value of the database column value, null if the column
* value is null
* @throws MetaException
* if the column value cannot be converted into a Boolean object
*/
static Boolean extractSqlBoolean(Object value) throws MetaException {
if (value == null) {
return null;
}
if (value instanceof Boolean) {
return (Boolean)value;
}
// check if oracle db returned 0 or 1 for boolean value
if (value instanceof Number) {
try {
return BooleanUtils.toBooleanObject(Integer.valueOf(((Number) value).intValue()), 1, 0, null);
} catch (IllegalArgumentException iae) {
// NOOP
}
}
if (value instanceof String) {
try {
return BooleanUtils.toBooleanObject((String) value, "Y", "N", null);
} catch (IllegalArgumentException iae) {
// NOOP
}
}
if (value instanceof BigDecimal) {
BigDecimal bigDecimal = (BigDecimal) value;
if (bigDecimal.intValue() == 0) {
return false;
} else {
return true;
}
}
LOG.debug("Value is of type {}", value.getClass());
throw new MetaException("Cannot extract boolean from column value " + value);
}
static String extractSqlString(Object value) {
if (value == null) return null;
return value.toString();
}
static Double extractSqlDouble(Object obj) throws MetaException {
if (obj == null)
return null;
if (!(obj instanceof Number)) {
throw new MetaException("Expected numeric type but got " + obj.getClass().getName());
}
return ((Number) obj).doubleValue();
}
static byte[] extractSqlBlob(Object value) throws MetaException {
if (value == null)
return null;
if (value instanceof Blob) {
//derby, oracle
try {
// getBytes function says: pos the ordinal position of the first byte in
// the BLOB value to be extracted; the first byte is at position 1
return ((Blob) value).getBytes(1, (int) ((Blob) value).length());
} catch (SQLException e) {
throw new MetaException("Encounter error while processing blob.");
}
}
else if (value instanceof byte[]) {
// mysql, postgres, sql server
return (byte[]) value;
}
else {
// this may happen when enablebitvector is false
LOG.debug("Expected blob type but got " + value.getClass().getName());
return null;
}
}
@FunctionalInterface
static interface ApplyFunc<Target> {
void apply(Target t, Object[] fields) throws MetaException;
}
public static void throwMetaOrRuntimeException(Exception e) throws MetaException {
if (e instanceof MetaException) {
throw (MetaException) e;
} else if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else {
throw new RuntimeException(e);
}
}
}