/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed 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.
 */

#include "src/trace_processor/sqlite3_str_split.h"

#include "src/trace_processor/sqlite_utils.h"

namespace perfetto {
namespace trace_processor {

namespace {
constexpr char kDelimiterError[] =
    "str_split: delimiter must be a non-empty string";
constexpr char kSplitFieldIndexError[] =
    "str_split: field number must be a non-negative integer";

void sqlite_str_split(sqlite3_context* context,
                      int argc,
                      sqlite3_value** argv) {
  PERFETTO_DCHECK(argc == 3);
  if (sqlite3_value_type(argv[1]) != SQLITE_TEXT) {
    sqlite3_result_error(context, kDelimiterError, -1);
    return;
  }
  const char* delimiter =
      reinterpret_cast<const char*>(sqlite3_value_text(argv[1]));
  const size_t delimiter_len = strlen(delimiter);
  if (delimiter_len == 0) {
    sqlite3_result_error(context, kDelimiterError, -1);
    return;
  }
  if (sqlite3_value_type(argv[2]) != SQLITE_INTEGER) {
    sqlite3_result_error(context, kSplitFieldIndexError, -1);
    return;
  }
  int fld = sqlite3_value_int(argv[2]);
  if (fld < 0) {
    sqlite3_result_error(context, kSplitFieldIndexError, -1);
    return;
  }
  if (sqlite3_value_type(argv[0]) != SQLITE_TEXT) {
    sqlite3_result_null(context);
    return;
  }
  const char* in = reinterpret_cast<const char*>(sqlite3_value_text(argv[0]));
  const char* next;
  do {
    next = strstr(in, delimiter);
    if (fld == 0) {
      int size = next != nullptr ? static_cast<int>(next - in)
                                 : static_cast<int>(strlen(in));
      sqlite3_result_text(context, in, size, sqlite_utils::kSqliteTransient);
      return;
    } else if (next == nullptr) {
      break;
    }
    in = next + delimiter_len;
    --fld;
  } while (fld >= 0);
  sqlite3_result_null(context);
}
}  // namespace

void sqlite3_str_split_init(sqlite3* db) {
  PERFETTO_CHECK(sqlite3_create_function(db, "str_split", 3,
                                         SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
                                         &sqlite_str_split, 0, 0) == SQLITE_OK);
}

}  // namespace trace_processor
}  // namespace perfetto