package com.klarna.hiverunner.data;
import java.io.File;
import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hive.hcatalog.api.HCatClient;
import org.apache.hive.hcatalog.api.HCatTable;
import org.apache.hive.hcatalog.common.HCatException;
import com.klarna.hiverunner.HiveShell;
/**
* A class for fluently creating a list of rows and inserting them into a table.
*/
public final class InsertIntoTable {
private final TableDataBuilder builder;
private final TableDataInserter inserter;
/**
* Factory method for creating an {@link InsertIntoTable}.
* <p/>
* This method is intended to be called via {@link HiveShell#insertInto(String, String)}.
*
* @param databaseName The database name.
* @param tableName The table name.
* @param conf The {@link HiveConf}.
* @return InsertIntoTable
*/
public static InsertIntoTable newInstance(String databaseName, String tableName, HiveConf conf) {
TableDataBuilder builder = new TableDataBuilder(getHCatTable(databaseName, tableName, conf));
TableDataInserter inserter = new TableDataInserter(databaseName, tableName, conf);
return new InsertIntoTable(builder, inserter);
}
private static HCatTable getHCatTable(String databaseName, String tableName, HiveConf conf) {
HCatClient client = null;
try {
client = HCatClient.create(conf);
return client.getTable(databaseName, tableName);
} catch (HCatException e) {
throw new RuntimeException("Unable to get table from the metastore.", e);
} finally {
if (client != null) {
try {
client.close();
} catch (HCatException e) {
throw new RuntimeException("Unable close client.", e);
}
}
}
}
InsertIntoTable(TableDataBuilder builder, TableDataInserter inserter) {
this.builder = builder;
this.inserter = inserter;
}
/**
* Defines a subset of columns (a column name mask) so that only pertinent columns can be set.
* <p/>
* e.g.
*
* <pre>
* {@code
* tableDataBuilder
* .withColumns("col1", "col3")
* .addRow("value1", "value3")
* }
* </pre>
*
* @param names The column names.
* @return {@code this}
* @throws IllegalArgumentException if a column name does not exist in the table.
*/
public InsertIntoTable withColumns(String... names) {
builder.withColumns(names);
return this;
}
/**
* Resets the column name mask to all the columns in the table.
*
* @return {@code this}
*/
public InsertIntoTable withAllColumns() {
builder.withAllColumns();
return this;
}
/**
* Flushes the current row and creates a new row with {@code null} values for all columns.
*
* @return {@code this}
*/
public InsertIntoTable newRow() {
builder.newRow();
return this;
}
/**
* Flushes the current row and creates a new row with the values specified.
*
* @param values The values to set.
* @return {@code this}
*/
public InsertIntoTable addRow(Object... values) {
builder.addRow(values);
return this;
}
/**
* Sets the current row with the values specified.
*
* @param values The values to set.
* @return {@code this}
*/
public InsertIntoTable setRow(Object... values) {
builder.setRow(values);
return this;
}
/**
* Adds all rows from the TSV file specified. The default delimiter is tab and the default null value is an empty
* string.
*
* @param file The file to read the data from.
* @return {@code this}
*/
public InsertIntoTable addRowsFromTsv(File file) {
builder.addRowsFromTsv(file);
return this;
}
/**
* Adds all rows from the TSV file specified, using the provided delimiter and null value.
*
* @param file The file to read the data from.
* @param delimiter A column delimiter.
* @param nullValue Value to be treated as null in the source data.
* @return {@code this}
*/
public InsertIntoTable addRowsFromDelimited(File file, String delimiter, Object nullValue) {
builder.addRowsFromDelimited(file, delimiter, nullValue);
return this;
}
/**
* Adds all rows from the file specified, using the provided parser.
*
* @param file File to read the data from.
* @param fileParser Parser to be used to parse the file.
* @return {@code this}
*/
public InsertIntoTable addRowsFrom(File file, FileParser fileParser) {
builder.addRowsFrom(file, fileParser);
return this;
}
/**
* Flushes the current row and creates a new row with the same values.
*
* @return {@code this}
*/
public InsertIntoTable copyRow() {
builder.copyRow();
return this;
}
/**
* Set the given column name to the given value.
*
* @param name The column name to set.
* @param value the value to set.
* @return {@code this}
* @throws IllegalArgumentException if a column name does not exist in the table.
*/
public InsertIntoTable set(String name, Object value) {
builder.set(name, value);
return this;
}
/**
* Inserts the data into the table. This does not replace any existing data, but appends new part files to the
* table/partition location(s).
*/
public void commit() {
inserter.insert(builder.build());
}
}