Импортируйте CSV в Vertica с помощью Rfc4180CsvParser и исключите строку заголовка

Есть ли способ исключить строку заголовка при импорте данных через Rfc4180CsvParser? COPY команда имеет SKIP опция, но эта опция не работает при использовании анализаторов CSV, предоставленных в Vertica SDK.

Фон

В качестве фона COPY Команда не читает файлы CSV сама по себе. Для простых файлов CSV можно сказать COPY schema.table FROM '/data/myfile.csv' DELIMITER ',' ENCLOSED BY '"'; но это не удастся с файлами данных, которые имеют строковые значения со встроенными кавычками.

Добавление ESCAPE AS '"' сгенерирует ошибку ERROR 3169: ENCLOSED BY and ESCAPE AS can not be the same value , Это проблема, поскольку значения CSV заключены и экранированы ",

Vertica SDK CsvParser расширения для спасения

Vertica предоставляет SDK под /opt/vertica/sdk/examples с программами C ++, которые могут быть скомпилированы в расширения. Одним из них является /opt/vertica/sdk/examples/ParserFunctions/Rfc4180CsvParser.cpp,

Это прекрасно работает следующим образом:

 cd /opt/vertica/sdk/examples
make clean
vsql
==> CREATE LIBRARY Rfc4180CsvParserLib AS '/opt/vertica/sdk/examples/build/Rfc4180CsvParser.so';
==> COPY myschema.mytable FROM '/data/myfile.csv' WITH PARSER Rfc4180CsvParser();

проблема

Выше работает отлично Кроме что он импортирует первую строку файла данных как строку. COPY команда имеет SKIP 1 вариант, но это не работает с парсером.

Вопрос

Можно ли редактировать Rfc4180CsvParser.cpp чтобы пропустить первую строку, или, еще лучше, взять какой-либо параметр, чтобы указать количество пропускаемых строк?

В программе всего 135 строк, но я не вижу, где / как сделать этот надрез. Советы?

Копирование всей программы ниже, так как я не вижу публичного репозитория с ссылками на …

Rfc4180CsvParser.cpp

/* Copyright (c) 2005 - 2012 Vertica, an HP company -*- C++ -*- */

#include "Vertica.h"#include "StringParsers.h"#include "csv.h"
using namespace Vertica;

// Note, the class template is mostly for demonstration purposes,
// so that the same class can use each of two string-parsers.
// Custom parsers can also just pick a string-parser to use.

/**
* A parser that parses something approximating the "official" CSV format
* as defined in IETF RFC-4180:  <http://tools.ietf.org/html/rfc4180>
* Oddly enough, many "CSV" files don't actually conform to this standard
* for one reason or another.  But for sources that do, this parser should
* be able to handle the data.
* Note that the CSV format does not specify how to handle different
* data types; it is entirely a string-based format.
* So we just use standard parsers based on the corresponding column type.
*/
template <class StringParsersImpl>
class LibCSVParser : public UDParser {
public:
LibCSVParser() : colNum(0) {}

// Keep a copy of the information about each column.
// Note that Vertica doesn't let us safely keep a reference to
// the internal copy of this data structure that it shows us.
// But keeping a copy is fine.
SizedColumnTypes colInfo;

// An instance of the class containing the methods that we're
// using to parse strings to the various relevant data types
StringParsersImpl sp;

/// Current column index
size_t colNum;

/// Parsing state for libcsv
struct csv_parser parser;

// Format strings
std::vector<std::string> formatStrings;

/**
* Given a field in string form (a pointer to the first character and
* a length), submit that field to Vertica.
* `colNum` is the column number from the input file; how many fields
* it is into the current record.
*/
bool handleField(size_t colNum, char* start, size_t len) {
if (colNum >= colInfo.getColumnCount()) {
// Ignore column overflow
return false;
}
// Empty colums are null.
if (len==0) {
writer->setNull(colNum);
return true;
} else {
return parseStringToType(start, len, colNum, colInfo.getColumnType(c
olNum), writer, sp);
}
}

static void handle_record(void *data, size_t len, void *p) {
static_cast<LibCSVParser*>(p)->handleField(static_cast<LibCSVParser*>(p)
->colNum++, (char*)data, len);
}

static void handle_end_of_row(int c, void *p) {
// Ignore 'c' (the terminating character); trust that it's correct
static_cast<LibCSVParser*>(p)->colNum = 0;
static_cast<LibCSVParser*>(p)->writer->next();
}

virtual StreamState process(ServerInterface &srvInterface, DataBuffer &input
, InputState input_state) {
size_t processed;
while ((processed = csv_parse(&parser, input.buf + input.offset, input.s
ize - input.offset,
handle_record, handle_end_of_row, this)) > 0) {
input.offset += processed;
}

if (input_state == END_OF_FILE && input.size == input.offset) {
csv_fini(&parser, handle_record, handle_end_of_row, this);
return DONE;
}

return INPUT_NEEDED;
}

virtual void setup(ServerInterface &srvInterface, SizedColumnTypes &returnTy
pe);
virtual void destroy(ServerInterface &srvInterface, SizedColumnTypes &return
Type) {
csv_free(&parser);
}
};

template <class StringParsersImpl>
void LibCSVParser<StringParsersImpl>::setup(ServerInterface &srvInterface, Sized
ColumnTypes &returnType) {
csv_init(&parser, CSV_APPEND_NULL);
colInfo = returnType;
}

template <>
void LibCSVParser<FormattedStringParsers>::setup(ServerInterface &srvInterface,
SizedColumnTypes &returnType) {
csv_init(&parser, CSV_APPEND_NULL);
colInfo = returnType;
if (formatStrings.size() != returnType.getColumnCount()) {
formatStrings.resize(returnType.getColumnCount(), "");
}
sp.setFormats(formatStrings);
}

template <class StringParsersImpl>
class LibCSVParserFactoryTmpl : public ParserFactory {
public:
virtual void plan(ServerInterface &srvInterface,
PerColumnParamReader &perColumnParamReader,
PlanContext &planCtxt) {}

virtual UDParser* prepare(ServerInterface &srvInterface,
PerColumnParamReader &perColumnParamReader,
PlanContext &planCtxt,
const SizedColumnTypes &returnType)
{
return vt_createFuncObj(srvInterface.allocator,
LibCSVParser<StringParsersImpl>);
}
};

typedef LibCSVParserFactoryTmpl<StringParsers> LibCSVParserFactory;
RegisterFactory(LibCSVParserFactory);

typedef LibCSVParserFactoryTmpl<FormattedStringParsers> FormattedLibCSVParserFac
tory;
RegisterFactory(FormattedLibCSVParserFactory);

2

Решение

Быстрый и грязный способ — просто жестко закодировать его. Он использует обратный вызов для handle_end_of_row, Отслеживайте номер строки и просто не обрабатывайте первый ряд. Что-то вроде:

static void handle_end_of_row(int c, void *ptr) {
// Ignore 'c' (the terminating character); trust that it's correct
LibCSVParser *p = static_cast<LibCSVParser*>(ptr);
p->colNum = 0;

if (rowcnt <= 0) {
p->bad_field = "";
rowcnt++;
} else if (p->bad_field.empty()) {
p->writer->next();
} else {
// libcsv doesn't give us the whole row to reject.
// So just write to the log.
// TODO: Come up with something more clever.
if (p->currSrvInterface) {
p->currSrvInterface->log("Invalid CSV field value: '%s'  Row skipped.",
p->bad_field.c_str());
}
p->bad_field = "";
}
}

Кроме того, лучше всего инициализировать rownum = 0 в process так как я думаю, что это будет вызывать это для каждого файла в вашем COPY заявление. Там могут быть более умные способы сделать это. По сути, это просто обработает запись, а затем отбросит ее.

Что касается поддержки SKIP в общем … посмотрите на TraditionalCSVParser для того, как обрабатывать передачу параметров. Вы должны были бы добавить это к фактору парсера prepare и отправить значение в LibCSVParser класс и переопределение getParameterType, Затем в LibCSVParser вам нужно принять параметр в конструкторе и изменить process пропустить первый skip строк. Затем используйте это значение вместо жестко закодированного 0 выше.

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]