/* * mod_v8 for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2013-2014, Peter Olsson * * Version: MPL 1.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * Ported from the Original Code in FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * * The Initial Developer of the Original Code is * Anthony Minessale II * Portions created by the Initial Developer are Copyright (C) * the Initial Developer. All Rights Reserved. * * Contributor(s): * Peter Olsson * Anthony Minessale II * * fsodbc.cpp -- JavaScript ODBC class * */ #include "fsodbc.hpp" using namespace std; using namespace v8; #ifdef HAVE_ODBC static const char js_class_name[] = "ODBC"; FSODBC::~FSODBC(void) { if (_stmt) { SQLFreeHandle(SQL_HANDLE_STMT, _stmt); } if (_handle) { switch_odbc_handle_destroy(&_handle); } switch_safe_free(_colbuf); } string FSODBC::GetJSClassName() { return js_class_name; } void FSODBC::Init() { _handle = NULL; _stmt = NULL; _colbuf = NULL; _cblen = 0; } FSODBC *FSODBC::New(char *dsn, char *username, char *password, const v8::FunctionCallbackInfo& info) { FSODBC *new_obj = NULL; if (!(new_obj = new FSODBC(info))) { goto err; } if (!(new_obj->_handle = switch_odbc_handle_new(dsn, username, password))) { goto err; } new_obj->_dsn = dsn; return new_obj; err: if (new_obj) { if (new_obj->_handle) { switch_odbc_handle_destroy(&new_obj->_handle); } delete new_obj; } return NULL; } switch_odbc_status_t FSODBC::Connect() { return switch_odbc_handle_connect(_handle); } void *FSODBC::Construct(const v8::FunctionCallbackInfo& info) { FSODBC *odbc_obj = NULL; char *dsn, *username, *password; int32_t blen = 1024; if (info.Length() < 3) { info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Invalid parameters")); return NULL; } String::Utf8Value str1(info[0]); String::Utf8Value str2(info[1]); String::Utf8Value str3(info[2]); dsn = *str1; username = *str2; password = *str3; if (info.Length() > 3) { int32_t len = info[3]->Int32Value(); if (len > 0) { blen = len; } } if (zstr(username)) { username = NULL; } if (zstr(password)) { password = NULL; } if (dsn) { odbc_obj = New(dsn, username, password, info); } if (!odbc_obj) { info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Failed to create new ODBC instance")); return NULL; } if (!(odbc_obj->_colbuf = (SQLCHAR *) malloc(blen))) { delete odbc_obj; info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Memory error")); return NULL; } odbc_obj->_cblen = blen; return odbc_obj; } JS_ODBC_FUNCTION_IMPL(Connect) { HandleScope handle_scope(info.GetIsolate()); bool tf = true; if (Connect() == SWITCH_ODBC_SUCCESS) { tf = true; } else { tf = false; } info.GetReturnValue().Set(tf); } JS_ODBC_FUNCTION_IMPL(Execute) { HandleScope handle_scope(info.GetIsolate()); const char *sql; bool tf = false; SQLHSTMT local_stmt; String::Utf8Value str(info[0]); if (info.Length() < 1) { goto done; } if (switch_odbc_handle_get_state(_handle) != SWITCH_ODBC_STATE_CONNECTED) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Database is not connected!\n"); goto done; } sql = js_safe_str(*str); if (switch_odbc_handle_exec(_handle, sql, &local_stmt, NULL) != SWITCH_ODBC_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "[ODBC] Execute failed for: %s\n", sql); goto done; } SQLFreeHandle(SQL_HANDLE_STMT, local_stmt); tf = true; done: info.GetReturnValue().Set(tf); } JS_ODBC_FUNCTION_IMPL(Exec) { HandleScope handle_scope(info.GetIsolate()); const char *sql; bool tf = false; String::Utf8Value str(info[0]); if (info.Length() < 1) { goto done; } if (switch_odbc_handle_get_state(_handle) != SWITCH_ODBC_STATE_CONNECTED) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Database is not connected!\n"); goto done; } if (_stmt) { SQLFreeHandle(SQL_HANDLE_STMT, _stmt); _stmt = NULL; } sql = js_safe_str(*str); if (switch_odbc_handle_exec(_handle, sql, &_stmt, NULL) != SWITCH_ODBC_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "[ODBC] query failed: %s\n", sql); goto done; } tf = true; done: info.GetReturnValue().Set(tf); } JS_ODBC_FUNCTION_IMPL(NumRows) { HandleScope handle_scope(info.GetIsolate()); SQLLEN row_count = 0; if (switch_odbc_handle_get_state(_handle) != SWITCH_ODBC_STATE_CONNECTED) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Database is not connected!\n"); goto done; } if (_stmt) { SQLRowCount(_stmt, &row_count); } done: info.GetReturnValue().Set(Integer::New(info.GetIsolate(), (int32_t)row_count)); } JS_ODBC_FUNCTION_IMPL(NumCols) { HandleScope handle_scope(info.GetIsolate()); SQLSMALLINT cols = 0; if (switch_odbc_handle_get_state(_handle) != SWITCH_ODBC_STATE_CONNECTED) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Database is not connected!\n"); goto done; } if (_stmt) { SQLNumResultCols(_stmt, &cols); } done: info.GetReturnValue().Set(cols); } JS_ODBC_FUNCTION_IMPL(NextRow) { HandleScope handle_scope(info.GetIsolate()); int result = 0; bool tf = false; if (switch_odbc_handle_get_state(_handle) != SWITCH_ODBC_STATE_CONNECTED) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Database is not connected!\n"); goto done; } if (_stmt) { if ((result = SQLFetch(_stmt) == SQL_SUCCESS)) { tf = true; } } done: info.GetReturnValue().Set(tf); } JS_ODBC_FUNCTION_IMPL(GetData) { HandleScope handle_scope(info.GetIsolate()); if (switch_odbc_handle_get_state(_handle) != SWITCH_ODBC_STATE_CONNECTED) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Database is not connected!\n"); goto done; } if (_stmt) { SQLSMALLINT nColumns = 0, x = 0; if (SQLNumResultCols(_stmt, &nColumns) != SQL_SUCCESS) { goto done; } Handle arg = Array::New(GetIsolate(), nColumns); for (x = 1; x <= nColumns; x++) { SQLSMALLINT NameLength, DataType, DecimalDigits, Nullable; SQLULEN ColumnSize; SQLCHAR name[1024] = ""; SQLCHAR *data = _colbuf; SQLLEN pcbValue; SQLDescribeCol(_stmt, x, name, sizeof(name), &NameLength, &DataType, &ColumnSize, &DecimalDigits, &Nullable); SQLGetData(_stmt, x, SQL_C_CHAR, _colbuf, _cblen, &pcbValue); if (name) { if (SQL_NULL_DATA == pcbValue) { arg->Set(String::NewFromUtf8(GetIsolate(), (const char *)name), Null(info.GetIsolate())); } else { arg->Set(String::NewFromUtf8(GetIsolate(), (const char *)name), String::NewFromUtf8(GetIsolate(), data ? (const char *)data : "")); } } } info.GetReturnValue().Set(arg); return; } done: info.GetReturnValue().Set(false); } JS_ODBC_FUNCTION_IMPL_STATIC(Close) { JS_CHECK_SCRIPT_STATE(); HandleScope handle_scope(info.GetIsolate()); FSODBC *obj = JSBase::GetInstance(info); if (obj) { delete obj; } } JS_ODBC_FUNCTION_IMPL(Disconnect) { HandleScope handle_scope(info.GetIsolate()); if (switch_odbc_handle_get_state(_handle) != SWITCH_ODBC_STATE_CONNECTED) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Database is not connected!\n"); return; } if (_stmt) { SQLFreeHandle(SQL_HANDLE_STMT, _stmt); _stmt = NULL; } switch_odbc_handle_disconnect(_handle); } JS_ODBC_GET_PROPERTY_IMPL(GetProperty) { HandleScope handle_scope(info.GetIsolate()); String::Utf8Value str(property); if (!strcmp(js_safe_str(*str), "name")) { info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), _dsn.length() > 0 ? _dsn.c_str() : "")); } else { info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Bad property")); } } static const js_function_t odbc_methods[] = { {"connect", FSODBC::Connect}, {"disconnect", FSODBC::Disconnect}, {"exec", FSODBC::Exec}, // Deprecated {"query", FSODBC::Exec}, {"execute", FSODBC::Execute}, {"numRows", FSODBC::NumRows}, {"numCols", FSODBC::NumCols}, {"nextRow", FSODBC::NextRow}, {"getData", FSODBC::GetData}, {"close", FSODBC::Close}, {0} }; static const js_property_t odbc_props[] = { {"name", FSODBC::GetProperty, JSBase::DefaultSetProperty}, {0} }; static const js_class_definition_t odbc_desc = { js_class_name, FSODBC::Construct, odbc_methods, odbc_props }; static switch_status_t odbc_load(const v8::FunctionCallbackInfo& info) { JSBase::Register(info.GetIsolate(), &odbc_desc); return SWITCH_STATUS_SUCCESS; } static const v8_mod_interface_t odbc_module_interface = { /*.name = */ js_class_name, /*.js_mod_load */ odbc_load }; const v8_mod_interface_t *FSODBC::GetModuleInterface() { return &odbc_module_interface; } #endif /* HAVE_ODBC */ /* For Emacs: * Local Variables: * mode:c * indent-tabs-mode:t * tab-width:4 * c-basic-offset:4 * End: * For VIM: * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: */