323 lines
10 KiB
C++
323 lines
10 KiB
C++
/**
|
|
* Copyright 2008 The Apache Software Foundation
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* Instructions:
|
|
* 1. Run Thrift to generate the cpp module HBase
|
|
* thrift --gen cpp ../../../src/main/resources/org/apache/hadoop/hbase/thrift/Hbase.thrift
|
|
* 2. Execute {make}.
|
|
* 3. Execute {./DemoClient}.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <sys/time.h>
|
|
#include <poll.h>
|
|
|
|
#include <iostream>
|
|
|
|
#include <boost/lexical_cast.hpp>
|
|
#include <protocol/TBinaryProtocol.h>
|
|
#include <transport/TSocket.h>
|
|
#include <transport/TTransportUtils.h>
|
|
|
|
#include "Hbase.h"
|
|
|
|
using namespace apache::thrift;
|
|
using namespace apache::thrift::protocol;
|
|
using namespace apache::thrift::transport;
|
|
|
|
using namespace apache::hadoop::hbase::thrift;
|
|
|
|
namespace {
|
|
|
|
typedef std::vector<std::string> StrVec;
|
|
typedef std::map<std::string,std::string> StrMap;
|
|
typedef std::vector<ColumnDescriptor> ColVec;
|
|
typedef std::map<std::string,ColumnDescriptor> ColMap;
|
|
typedef std::vector<TCell> CellVec;
|
|
typedef std::map<std::string,TCell> CellMap;
|
|
|
|
|
|
static void
|
|
printRow(const std::vector<TRowResult> &rowResult)
|
|
{
|
|
for (size_t i = 0; i < rowResult.size(); i++) {
|
|
std::cout << "row: " << rowResult[i].row << ", cols: ";
|
|
for (CellMap::const_iterator it = rowResult[i].columns.begin();
|
|
it != rowResult[i].columns.end(); ++it) {
|
|
std::cout << it->first << " => " << it->second.value << "; ";
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
}
|
|
|
|
static void
|
|
printVersions(const std::string &row, const CellVec &versions)
|
|
{
|
|
std::cout << "row: " << row << ", values: ";
|
|
for (CellVec::const_iterator it = versions.begin(); it != versions.end(); ++it) {
|
|
std::cout << (*it).value << "; ";
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
}
|
|
|
|
int
|
|
main(int argc, char** argv)
|
|
{
|
|
if (argc < 3) {
|
|
std::cerr << "Invalid arguments!\n" << "Usage: DemoClient host port" << std::endl;
|
|
return -1;
|
|
}
|
|
bool isFramed = false;
|
|
boost::shared_ptr<TTransport> socket(new TSocket(argv[1], boost::lexical_cast<int>(argv[2])));
|
|
boost::shared_ptr<TTransport> transport;
|
|
|
|
if (isFramed) {
|
|
transport.reset(new TFramedTransport(socket));
|
|
} else {
|
|
transport.reset(new TBufferedTransport(socket));
|
|
}
|
|
boost::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));
|
|
|
|
const std::map<Text, Text> dummyAttributes; // see HBASE-6806 HBASE-4658
|
|
HbaseClient client(protocol);
|
|
try {
|
|
transport->open();
|
|
|
|
std::string t("demo_table");
|
|
|
|
//
|
|
// Scan all tables, look for the demo table and delete it.
|
|
//
|
|
std::cout << "scanning tables..." << std::endl;
|
|
StrVec tables;
|
|
client.getTableNames(tables);
|
|
for (StrVec::const_iterator it = tables.begin(); it != tables.end(); ++it) {
|
|
std::cout << " found: " << *it << std::endl;
|
|
if (t == *it) {
|
|
if (client.isTableEnabled(*it)) {
|
|
std::cout << " disabling table: " << *it << std::endl;
|
|
client.disableTable(*it);
|
|
}
|
|
std::cout << " deleting table: " << *it << std::endl;
|
|
client.deleteTable(*it);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create the demo table with two column families, entry: and unused:
|
|
//
|
|
ColVec columns;
|
|
columns.push_back(ColumnDescriptor());
|
|
columns.back().name = "entry:";
|
|
columns.back().maxVersions = 10;
|
|
columns.push_back(ColumnDescriptor());
|
|
columns.back().name = "unused:";
|
|
|
|
std::cout << "creating table: " << t << std::endl;
|
|
try {
|
|
client.createTable(t, columns);
|
|
} catch (const AlreadyExists &ae) {
|
|
std::cerr << "WARN: " << ae.message << std::endl;
|
|
}
|
|
|
|
ColMap columnMap;
|
|
client.getColumnDescriptors(columnMap, t);
|
|
std::cout << "column families in " << t << ": " << std::endl;
|
|
for (ColMap::const_iterator it = columnMap.begin(); it != columnMap.end(); ++it) {
|
|
std::cout << " column: " << it->second.name << ", maxVer: " << it->second.maxVersions << std::endl;
|
|
}
|
|
|
|
//
|
|
// Test UTF-8 handling
|
|
//
|
|
std::string invalid("foo-\xfc\xa1\xa1\xa1\xa1\xa1");
|
|
std::string valid("foo-\xE7\x94\x9F\xE3\x83\x93\xE3\x83\xBC\xE3\x83\xAB");
|
|
|
|
// non-utf8 is fine for data
|
|
std::vector<Mutation> mutations;
|
|
mutations.push_back(Mutation());
|
|
mutations.back().column = "entry:foo";
|
|
mutations.back().value = invalid;
|
|
client.mutateRow(t, "foo", mutations, dummyAttributes);
|
|
|
|
// try empty strings
|
|
mutations.clear();
|
|
mutations.push_back(Mutation());
|
|
mutations.back().column = "entry:";
|
|
mutations.back().value = "";
|
|
client.mutateRow(t, "", mutations, dummyAttributes);
|
|
|
|
// this row name is valid utf8
|
|
mutations.clear();
|
|
mutations.push_back(Mutation());
|
|
mutations.back().column = "entry:foo";
|
|
mutations.back().value = valid;
|
|
client.mutateRow(t, valid, mutations, dummyAttributes);
|
|
|
|
// non-utf8 is now allowed in row names because HBase stores values as binary
|
|
mutations.clear();
|
|
mutations.push_back(Mutation());
|
|
mutations.back().column = "entry:foo";
|
|
mutations.back().value = invalid;
|
|
client.mutateRow(t, invalid, mutations, dummyAttributes);
|
|
|
|
// Run a scanner on the rows we just created
|
|
StrVec columnNames;
|
|
columnNames.push_back("entry:");
|
|
|
|
std::cout << "Starting scanner..." << std::endl;
|
|
int scanner = client.scannerOpen(t, "", columnNames, dummyAttributes);
|
|
try {
|
|
while (true) {
|
|
std::vector<TRowResult> value;
|
|
client.scannerGet(value, scanner);
|
|
if (value.size() == 0)
|
|
break;
|
|
printRow(value);
|
|
}
|
|
} catch (const IOError &ioe) {
|
|
std::cerr << "FATAL: Scanner raised IOError" << std::endl;
|
|
}
|
|
|
|
client.scannerClose(scanner);
|
|
std::cout << "Scanner finished" << std::endl;
|
|
|
|
//
|
|
// Run some operations on a bunch of rows.
|
|
//
|
|
for (int i = 100; i >= 0; --i) {
|
|
// format row keys as "00000" to "00100"
|
|
char buf[32];
|
|
sprintf(buf, "%05d", i);
|
|
std::string row(buf);
|
|
std::vector<TRowResult> rowResult;
|
|
|
|
mutations.clear();
|
|
mutations.push_back(Mutation());
|
|
mutations.back().column = "unused:";
|
|
mutations.back().value = "DELETE_ME";
|
|
client.mutateRow(t, row, mutations, dummyAttributes);
|
|
client.getRow(rowResult, t, row, dummyAttributes);
|
|
printRow(rowResult);
|
|
client.deleteAllRow(t, row, dummyAttributes);
|
|
|
|
mutations.clear();
|
|
mutations.push_back(Mutation());
|
|
mutations.back().column = "entry:num";
|
|
mutations.back().value = "0";
|
|
mutations.push_back(Mutation());
|
|
mutations.back().column = "entry:foo";
|
|
mutations.back().value = "FOO";
|
|
client.mutateRow(t, row, mutations, dummyAttributes);
|
|
client.getRow(rowResult, t, row, dummyAttributes);
|
|
printRow(rowResult);
|
|
|
|
// sleep to force later timestamp
|
|
poll(0, 0, 50);
|
|
|
|
mutations.clear();
|
|
mutations.push_back(Mutation());
|
|
mutations.back().column = "entry:foo";
|
|
mutations.back().isDelete = true;
|
|
mutations.push_back(Mutation());
|
|
mutations.back().column = "entry:num";
|
|
mutations.back().value = "-1";
|
|
client.mutateRow(t, row, mutations, dummyAttributes);
|
|
client.getRow(rowResult, t, row, dummyAttributes);
|
|
printRow(rowResult);
|
|
|
|
mutations.clear();
|
|
mutations.push_back(Mutation());
|
|
mutations.back().column = "entry:num";
|
|
mutations.back().value = boost::lexical_cast<std::string>(i);
|
|
mutations.push_back(Mutation());
|
|
mutations.back().column = "entry:sqr";
|
|
mutations.back().value = boost::lexical_cast<std::string>(i*i);
|
|
client.mutateRow(t, row, mutations, dummyAttributes);
|
|
client.getRow(rowResult, t, row, dummyAttributes);
|
|
printRow(rowResult);
|
|
|
|
mutations.clear();
|
|
mutations.push_back(Mutation());
|
|
mutations.back().column = "entry:num";
|
|
mutations.back().value = "-999";
|
|
mutations.push_back(Mutation());
|
|
mutations.back().column = "entry:sqr";
|
|
mutations.back().isDelete = true;
|
|
client.mutateRowTs(t, row, mutations, 1, dummyAttributes); // shouldn't override latest
|
|
client.getRow(rowResult, t, row, dummyAttributes);
|
|
printRow(rowResult);
|
|
|
|
CellVec versions;
|
|
client.getVer(versions, t, row, "entry:num", 10, dummyAttributes);
|
|
printVersions(row, versions);
|
|
assert(versions.size());
|
|
std::cout << std::endl;
|
|
|
|
try {
|
|
std::vector<TCell> value;
|
|
client.get(value, t, row, "entry:foo", dummyAttributes);
|
|
if (value.size()) {
|
|
std::cerr << "FATAL: shouldn't get here!" << std::endl;
|
|
return -1;
|
|
}
|
|
} catch (const IOError &ioe) {
|
|
// blank
|
|
}
|
|
}
|
|
|
|
// scan all rows/columns
|
|
|
|
columnNames.clear();
|
|
client.getColumnDescriptors(columnMap, t);
|
|
std::cout << "The number of columns: " << columnMap.size() << std::endl;
|
|
for (ColMap::const_iterator it = columnMap.begin(); it != columnMap.end(); ++it) {
|
|
std::cout << " column with name: " + it->second.name << std::endl;
|
|
columnNames.push_back(it->second.name);
|
|
}
|
|
std::cout << std::endl;
|
|
|
|
std::cout << "Starting scanner..." << std::endl;
|
|
scanner = client.scannerOpenWithStop(t, "00020", "00040", columnNames, dummyAttributes);
|
|
try {
|
|
while (true) {
|
|
std::vector<TRowResult> value;
|
|
client.scannerGet(value, scanner);
|
|
if (value.size() == 0)
|
|
break;
|
|
printRow(value);
|
|
}
|
|
} catch (const IOError &ioe) {
|
|
std::cerr << "FATAL: Scanner raised IOError" << std::endl;
|
|
}
|
|
|
|
client.scannerClose(scanner);
|
|
std::cout << "Scanner finished" << std::endl;
|
|
|
|
transport->close();
|
|
} catch (const TException &tx) {
|
|
std::cerr << "ERROR: " << tx.what() << std::endl;
|
|
}
|
|
}
|