//--------------------------------------------------------------------------------------------------------------------------------- // File: OdbcOperation.cpp // Contents: Functions called by thread queue for background ODBC operations // // Copyright Microsoft Corporation and contributors // // 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 "stdafx.h" #include #include #include #include #include namespace mssql { OdbcOperation::OdbcOperation(const shared_ptr &connection, const size_t query_id, Local cb) : Nan::AsyncWorker(new Nan::Callback(cb.As())), _connection(connection), _statement(nullptr), _callback(Isolate::GetCurrent(), cb.As()), _cb(cb), _can_lock(true), _failed(false), _failures(nullptr) { _statementId = static_cast(query_id); const nodeTypeFactory fact; _output_param = fact.null(); } OdbcOperation::OdbcOperation(const size_t query_id, const Local cb) : OdbcOperation(nullptr, -1, cb) { } OdbcOperation::OdbcOperation(const shared_ptr & connection, Local cb) : OdbcOperation(connection, -1, cb) { } void OdbcOperation::Execute () { if (_statement && _can_lock) { const std::lock_guard lock(_statement->_statement_mutex); // std::cout << " invoke_background .... " << timer.get_counter() << endl; _failed = !TryInvokeOdbc(); } else { _failed = !TryInvokeOdbc(); } // std::cout << " .... invoke_background " << timer.get_counter() << endl; if (_failed) { getFailure(); } } void OdbcOperation::HandleOKCallback () { if (_callback.IsEmpty()) return; Local args[4]; const auto argc = _failed ? error(args) : success(args); // std::cout << " complete_foreground " << timer.get_counter() << endl; //args[argc] = fact.new_number(timer.get_counter()); Nan::Call(Nan::New(_callback), Nan::GetCurrentContext()->Global(), argc, args); } OdbcOperation::~OdbcOperation() { _callback.Reset(); // int count = _statement.use_count(); // cerr << "~OdbcOperation statementId " << _statementId << " count " << count << endl; } bool OdbcOperation::fetch_statement() { _statement = _connection->getStatamentCache()->checkout(_statementId); // int count = _statement.use_count(); // cerr << "fetch_statement statementId " << _statementId << " count " << count << endl; const bool res = _statement ? true : false; return res; } void OdbcOperation::getFailure() { if (_connection) { _failures = _connection->errors(); } if (!_failures || (_failures->empty() && _statement)) { _failures = _statement->errors(); } if (!_failures || _failures->empty()) { _failures = make_shared>>(); if (!_statement) { _failures->push_back(make_shared("0000", "cannot call dead statement", -1, 0, "", "", 0)); } else { _failures->push_back(make_shared("unknown", "internal error", -1, 0, "", "", 0)); } } } int OdbcOperation::error(Local args[]) { const nodeTypeFactory fact; const unsigned int error_count = _failures ? static_cast(_failures->size()) : 0; const auto errors = fact.new_array(error_count); for (unsigned int i = 0; i < error_count; ++i) { const auto failure = (*_failures)[i]; const auto err = fact.error(failure->Message()); Nan::Set(err, Nan::New("sqlstate").ToLocalChecked(), Nan::New(failure->SqlState()).ToLocalChecked()); Nan::Set(err, Nan::New("code").ToLocalChecked(), Nan::New(failure->Code())); Nan::Set(err, Nan::New("severity").ToLocalChecked(), Nan::New(failure->Severity())); Nan::Set(err, Nan::New("serverName").ToLocalChecked(), Nan::New(failure->ServerName()).ToLocalChecked()); Nan::Set(err, Nan::New("procName").ToLocalChecked(), Nan::New(failure->ProcName()).ToLocalChecked()); Nan::Set(err, Nan::New("lineNumber").ToLocalChecked(), Nan::New(failure->LineNumber())); Nan::Set(errors, i, err); } auto more = false; if (_statement) { const auto rs = _statement->get_result_set(); if (rs) more = !rs->EndOfRows(); } args[0] = errors; if (more) { const auto arg = CreateCompletionArg(); args[1] = Nan::New(arg); } else { args[1] = fact.new_array(); } args[2] = Nan::New(more); constexpr auto argc = 3; return argc; } int OdbcOperation::success(Local args[]) { args[0] = Nan::New(false); const auto arg = CreateCompletionArg(); args[1] = arg; const auto c = _output_param->IsNull() ? 0 : _output_param.As()->Length(); if (c > 0) args[2] = _output_param; const auto argc = c == 0 ? 2 : 3; return argc; } }