// 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.

#include "mysql_scanner.h"


#include "common/logging.h"

namespace doris {

MysqlScanner::MysqlScanner(const MysqlScannerParam& param)
    : _my_param(param),
      _my_conn(NULL),
      _my_result(NULL),
      _is_open(false),
      _field_num(0) {
}

MysqlScanner::~MysqlScanner() {
    if (_my_result) {
        mysql_free_result(_my_result);
        _my_result = NULL;
    }

    if (_my_conn) {
        mysql_close(_my_conn);
        _my_conn = NULL;
    }
}

Status MysqlScanner::open() {
    if (_is_open) {
        LOG(INFO) << "this scanner already opened";
        return Status::OK;
    }

    _my_conn = mysql_init(NULL);

    if (NULL == _my_conn) {
        return Status("mysql init failed.");
    }

    VLOG(1) << "MysqlScanner::Connect";

    if (NULL == mysql_real_connect(_my_conn, _my_param.host.c_str(), _my_param.user.c_str(),
                                   _my_param.passwd.c_str(), _my_param.db.c_str(),
                                   atoi(_my_param.port.c_str()), NULL, _my_param.client_flag)) {
        LOG(WARNING) << "connect Mysql: " << "Host: " << _my_param.host
                     << " user: " << _my_param.user << " passwd: " << _my_param.passwd
                     << " db: " << _my_param.db << " port: " << _my_param.port;

        return _error_status("mysql real connect failed.");
    }

    if (mysql_set_character_set(_my_conn, "utf8")) {
        return Status("mysql set character set failed.");
    }

    _is_open = true;

    return Status::OK;
}

Status MysqlScanner::query(const std::string& query) {
    if (!_is_open) {
        return Status("Query before open.");
    }

    int sql_result = mysql_query(_my_conn, query.c_str());

    if (0 != sql_result) {
        LOG(WARNING) << "mysql query failed. query =" << query;
        return _error_status("mysql query failed.");
    } else {
        LOG(INFO) << "mysql query success. query =" << query;
    }

    // clean the last query result
    if (_my_result) {
        mysql_free_result(_my_result);
    }

    // use store result because mysql table is small, can load in memory avoid of many RPC
    _my_result = mysql_store_result(_my_conn);

    if (NULL == _my_result) {
        return _error_status("mysql store result failed.");
    }

    _field_num = mysql_num_fields(_my_result);

    return Status::OK;
}

Status MysqlScanner::query(const std::string& table, const std::vector<std::string>& fields,
                           const std::vector<std::string>& filters) {
    if (!_is_open) {
        return Status("Query before open.");
    }

    _sql_str = "SELECT";

    for (int i = 0; i < fields.size(); ++i) {
        if (0 != i) {
            _sql_str += ",";
        }

        _sql_str += " " + fields[i];
    }

    _sql_str += " FROM " + table;

    if (!filters.empty()) {
        _sql_str += " WHERE ";

        for (int i = 0; i < filters.size(); ++i) {
            if (0 != i) {
                _sql_str += " AND";
            }

            _sql_str += " (" + filters[i] + ") ";
        }
    }

    return query(_sql_str);
}

Status MysqlScanner::get_next_row(char** *buf, unsigned long** lengths, bool* eos) {
    if (!_is_open) {
        return Status("GetNextRow before open.");
    }

    if (NULL == buf || NULL == lengths || NULL == eos) {
        return Status("input parameter invalid.");
    }

    if (NULL == _my_result) {
        return Status("get next row before query.");
    }

    *buf = mysql_fetch_row(_my_result);

    if (NULL == *buf) {
        *eos = true;
        return Status::OK;
    }

    *lengths = mysql_fetch_lengths(_my_result);

    if (NULL == *lengths) {
        return _error_status("mysql fetch row failed.");
    }

    *eos = false;

    return Status::OK;
}

}

/* vim: set ts=4 sw=4 sts=4 tw=100 noet: */
