Merge pull request #1090 in FS/freeswitch from ~ANDYWOLK/freeswitch:feature/FS-9798-implement-native-postgresql-in-javascript to master

* commit 'fff5b939b58d47aed98b5f26b21282e82e25ee14':
  FS-9798 [mod_v8] Implement native PostgreSQL in JavaScript by adding a Database Handler (DBH) class
This commit is contained in:
Brian West 2016-12-04 06:53:07 -06:00
commit 8853873d68
6 changed files with 507 additions and 0 deletions

View File

@ -80,6 +80,7 @@ mod_v8_la_SOURCES = \
src/jsmain.cpp \
src/jsbase.cpp \
src/fscoredb.cpp \
src/fsdbh.cpp \
src/fscurl.cpp \
src/fsdtmf.cpp \
src/fsevent.cpp \

View File

@ -0,0 +1,96 @@
/*
* mod_v8 for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2013-2014, Peter Olsson <peter@olssononline.se>
*
* 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.
*
* The Original Code is mod_v8 for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Andrey Volk <andywolk@gmail.com>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Peter Olsson <peter@olssononline.se>
* Andrey Volk <andywolk@gmail.com>
*
* fsdbh.hpp -- JavaScript DBH class header
*
*/
#ifndef FS_DBH_H
#define FS_DBH_H
#include "mod_v8.h"
/* Macros for easier V8 callback definitions */
#define JS_DBH_GET_PROPERTY_DEF(method_name) JS_GET_PROPERTY_DEF(method_name, FSDBH)
#define JS_DBH_SET_PROPERTY_DEF(method_name) JS_SET_PROPERTY_DEF(method_name, FSDBH)
#define JS_DBH_FUNCTION_DEF(method_name) JS_FUNCTION_DEF(method_name, FSDBH)
#define JS_DBH_GET_PROPERTY_IMPL(method_name) JS_GET_PROPERTY_IMPL(method_name, FSDBH)
#define JS_DBH_SET_PROPERTY_IMPL(method_name) JS_SET_PROPERTY_IMPL(method_name, FSDBH)
#define JS_DBH_FUNCTION_IMPL(method_name) JS_FUNCTION_IMPL(method_name, FSDBH)
#define JS_DBH_GET_PROPERTY_IMPL_STATIC(method_name) JS_GET_PROPERTY_IMPL_STATIC(method_name, FSDBH)
#define JS_DBH_SET_PROPERTY_IMPL_STATIC(method_name) JS_SET_PROPERTY_IMPL_STATIC(method_name, FSDBH)
#define JS_DBH_FUNCTION_IMPL_STATIC(method_name) JS_FUNCTION_IMPL_STATIC(method_name, FSDBH)
class FSDBH : public JSBase
{
private:
v8::Persistent<v8::Function> _callback;
std::string _dsn;
switch_cache_db_handle_t *dbh;
char *err;
void Init();
bool _release();
void clear_error();
static int Callback(void *pArg, int argc, char **argv, char **columnNames);
public:
FSDBH(JSMain *owner) : JSBase(owner) { Init(); }
FSDBH(const v8::FunctionCallbackInfo<v8::Value>& info) : JSBase(info) { Init(); }
virtual ~FSDBH(void);
virtual std::string GetJSClassName();
static const v8_mod_interface_t *GetModuleInterface();
static FSDBH *New(char *dsn, char *username, char *password, const v8::FunctionCallbackInfo<v8::Value>& info);
/* Methods available from JavaScript */
static void *Construct(const v8::FunctionCallbackInfo<v8::Value>& info);
JS_DBH_FUNCTION_DEF(affected_rows);
JS_DBH_FUNCTION_DEF(connected);
JS_DBH_FUNCTION_DEF(last_error);
JS_DBH_FUNCTION_DEF(query);
JS_DBH_FUNCTION_DEF(release);
JS_DBH_FUNCTION_DEF(test_reactive);
JS_DBH_GET_PROPERTY_DEF(GetProperty);
};
#endif /* FS_DB_H */
/* 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:
*/

View File

@ -11,6 +11,9 @@
<ClCompile Include=".\src\fscoredb.cpp">
<Filter>FSClasses</Filter>
</ClCompile>
<ClCompile Include=".\src\fsdbh.cpp">
<Filter>FSClasses</Filter>
</ClCompile>
<ClCompile Include=".\src\fscurl.cpp">
<Filter>FSClasses</Filter>
</ClCompile>
@ -62,6 +65,9 @@
<ClInclude Include=".\include\fscoredb.hpp">
<Filter>FSClasses\include</Filter>
</ClInclude>
<ClInclude Include=".\include\fsdbh.hpp">
<Filter>FSClasses\include</Filter>
</ClInclude>
<ClInclude Include=".\include\fscurl.hpp">
<Filter>FSClasses\include</Filter>
</ClInclude>

View File

@ -170,6 +170,7 @@
<ClCompile Include=".\src\jsmain.cpp" />
<ClCompile Include=".\src\jsbase.cpp" />
<ClCompile Include=".\src\fscoredb.cpp" />
<ClCompile Include=".\src\fsdbh.cpp" />
<ClCompile Include=".\src\fscurl.cpp" />
<ClCompile Include=".\src\fsdtmf.cpp" />
<ClCompile Include=".\src\fsevent.cpp" />
@ -189,6 +190,7 @@
<ItemGroup>
<ClInclude Include=".\include\javascript.hpp" />
<ClInclude Include=".\include\fscoredb.hpp" />
<ClInclude Include=".\include\fsdbh.hpp" />
<ClInclude Include=".\include\fscurl.hpp" />
<ClInclude Include=".\include\fsdtmf.hpp" />
<ClInclude Include=".\include\fsevent.hpp" />

View File

@ -83,6 +83,7 @@
/* Optional JavaScript classes (loaded on demand) */
#include "fscoredb.hpp"
#include "fsdbh.hpp"
#include "fscurl.hpp"
#include "fsteletone.hpp"
#include "fssocket.hpp"
@ -840,6 +841,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_v8_load)
/* Make all "built in" modules available to load on demand */
v8_mod_init_built_in(FSCoreDB::GetModuleInterface());
v8_mod_init_built_in(FSDBH::GetModuleInterface());
v8_mod_init_built_in(FSCURL::GetModuleInterface());
#ifdef HAVE_ODBC
/* Only add ODBC class if ODBC is available in the system */

View File

@ -0,0 +1,400 @@
/*
* mod_v8 for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2013-2014, Peter Olsson <peter@olssononline.se>
*
* 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
* Andrey Volk <andywolk@gmail.com>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Peter Olsson <peter@olssononline.se>
* Andrey Volk <andywolk@gmail.com>
*
* fsdbh.cpp -- JavaScript DBH class
*
*/
#include "fsdbh.hpp"
using namespace std;
using namespace v8;
static const char js_class_name[] = "DBH";
FSDBH::~FSDBH()
{
if (dbh) _release();
clear_error();
}
bool FSDBH::_release()
{
if (dbh) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "DBH handle %p released.\n", (void *)dbh);
switch_cache_db_release_db_handle(&dbh);
return true;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "DBH NOT Connected.\n");
return false;
}
string FSDBH::GetJSClassName()
{
return js_class_name;
}
void FSDBH::Init()
{
dbh = NULL;
}
FSDBH *FSDBH::New(char *dsn, char *user, char *pass, const v8::FunctionCallbackInfo<Value>& info)
{
FSDBH *new_obj = NULL;
char *tmp = NULL;
if (!(new_obj = new FSDBH(info))) {
goto err;
}
new_obj->dbh = NULL;
new_obj->err = NULL;
if (!zstr(user) || !zstr(pass)) {
tmp = switch_mprintf("%s%s%s%s%s", dsn,
zstr(user) ? "" : ":",
zstr(user) ? "" : user,
zstr(pass) ? "" : ":",
zstr(pass) ? "" : pass
);
dsn = tmp;
}
if (!zstr(dsn) && switch_cache_db_get_db_handle_dsn(&(new_obj->dbh), dsn) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "DBH handle %p Connected.\n", (void *)(new_obj->dbh));
}
else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Connection failed. DBH NOT Connected.\n");
goto err;
}
new_obj->_dsn = dsn;
return new_obj;
err:
if (new_obj) {
delete new_obj;
}
return NULL;
}
void *FSDBH::Construct(const v8::FunctionCallbackInfo<Value>& info)
{
FSDBH *dbh_obj = NULL;
char *dsn, *username = NULL, *password = NULL;
if (info.Length() < 1 || info.Length() > 3) {
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Invalid parameters"));
return NULL;
}
String::Utf8Value str1(info[0]);
dsn = *str1;
if (info.Length() > 1)
{
String::Utf8Value str2(info[1]);
username = *str2;
}
if (info.Length() > 2)
{
String::Utf8Value str3(info[2]);
password = *str3;
}
if (zstr(username)) {
username = NULL;
}
if (zstr(password)) {
password = NULL;
}
if (dsn) {
dbh_obj = New(dsn, username, password, info);
}
if (!dbh_obj) {
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Failed to create new DBH instance"));
return NULL;
}
return dbh_obj;
}
int FSDBH::Callback(void *pArg, int argc, char **argv, char **columnNames)
{
FSDBH *dbo = static_cast<FSDBH *>(pArg);
int x = 0;
if (!dbo) {
return 0;
}
HandleScope handle_scope(dbo->GetIsolate());
if (dbo->_callback.IsEmpty()) {
dbo->GetIsolate()->ThrowException(String::NewFromUtf8(dbo->GetIsolate(), "No callback specified"));
return 0;
}
Handle<Array> arg = Array::New(dbo->GetIsolate(), argc);
for (x = 0; x < argc; x++) {
if (columnNames[x] && argv[x]) {
arg->Set(String::NewFromUtf8(dbo->GetIsolate(), columnNames[x]), String::NewFromUtf8(dbo->GetIsolate(), argv[x]));
}
}
HandleScope scope(dbo->GetIsolate());
Handle<Function> func = Local<Function>::New(dbo->GetIsolate(), dbo->_callback);
Handle<Value> jsargv[1] = { arg };
func->Call(dbo->GetIsolate()->GetCurrentContext()->Global(), 1, jsargv);
return 0;
}
void FSDBH::clear_error()
{
switch_safe_free(err);
}
JS_DBH_FUNCTION_IMPL(last_error)
{
if (err)
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), err));
else
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
return;
}
JS_DBH_FUNCTION_IMPL(query)
{
HandleScope handle_scope(info.GetIsolate());
info.GetReturnValue().Set(true);
clear_error();
if (info.Length() < 1 || info.Length() > 2) {
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Invalid parameters"));
return info.GetReturnValue().Set(false);
}
if (!dbh) {
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Database is not connected"));
return info.GetReturnValue().Set(false);
}
String::Utf8Value str(info[0]);
const char *sql = js_safe_str(*str);
if (zstr(sql)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing SQL query.\n");
return info.GetReturnValue().Set(false);
}
void *arg = NULL;
switch_core_db_callback_func_t cb_func = NULL;
Handle<Function> func = Handle<Function>();
if (info.Length() > 1)
func = JSBase::GetFunctionFromArg(info.GetIsolate(), info[1]);
if (!func.IsEmpty()) {
_callback.Reset(info.GetIsolate(), func);
cb_func = FSDBH::Callback;
arg = this;
}
if (dbh) {
if (!func.IsEmpty()) {
if (switch_cache_db_execute_sql_callback(dbh, sql, cb_func, arg, &err) == SWITCH_STATUS_SUCCESS) {
return;
}
}
else { /* if no callback func arg is passed from javascript, an empty initialized struct will be sent - see freeswitch.i */
if (switch_cache_db_execute_sql(dbh, (char *)sql, &err) == SWITCH_STATUS_SUCCESS) {
return;
}
}
if (err) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error %s\n", err);
switch_core_db_free(err);
info.GetReturnValue().Set(false);
}
}
else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "DBH NOT Connected.\n");
info.GetReturnValue().Set(false);
}
/* Make sure to release the callback handle again */
_callback.Reset();
return;
}
JS_DBH_FUNCTION_IMPL(release)
{
return info.GetReturnValue().Set(_release());
}
JS_DBH_FUNCTION_IMPL(test_reactive)
{
HandleScope handle_scope(info.GetIsolate());
info.GetReturnValue().Set(true);
clear_error();
if (info.Length() < 3) {
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Invalid parameters"));
return info.GetReturnValue().Set(false);
}
if (!dbh) {
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Database is not connected"));
return info.GetReturnValue().Set(false);
}
String::Utf8Value str0(info[0]);
String::Utf8Value str1(info[1]);
String::Utf8Value str2(info[2]);
const char *test_sql = js_safe_str(*str0);
const char *drop_sql = js_safe_str(*str1);
const char *reactive_sql = js_safe_str(*str2);
if (dbh) {
if (!zstr(test_sql) && !zstr(reactive_sql)) {
if (switch_cache_db_test_reactive(dbh, test_sql, drop_sql, reactive_sql) == SWITCH_TRUE) {
return;
}
}
else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing parameters.\n");
return info.GetReturnValue().Set(false);
}
}
else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "DBH NOT Connected.\n");
return info.GetReturnValue().Set(false);
}
return;
}
JS_DBH_FUNCTION_IMPL(connected)
{
return info.GetReturnValue().Set(dbh ? true : false);
}
JS_DBH_FUNCTION_IMPL(affected_rows)
{
if (dbh) {
return info.GetReturnValue().Set(switch_cache_db_affected_rows(dbh));
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "DBH is NOT Connected.\n");
return info.GetReturnValue().Set(0);
}
JS_DBH_GET_PROPERTY_IMPL(GetProperty)
{
HandleScope handle_scope(info.GetIsolate());
if (!this) {
info.GetReturnValue().Set(false);
return;
}
String::Utf8Value str(property);
if (!strcmp(js_safe_str(*str), "dsn")) {
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), _dsn.c_str()));
}
else {
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Bad property"));
}
}
static const js_function_t dbh_methods[] = {
{ "affected_rows", FSDBH::affected_rows },
{ "connected", FSDBH::connected },
{ "last_error", FSDBH::last_error },
{ "query", FSDBH::query },
{ "release", FSDBH::release },
{ "test_reactive", FSDBH::test_reactive },
{0}
};
static const js_property_t dbh_props[] = {
{"dsn", FSDBH::GetProperty, JSBase::DefaultSetProperty},
{0}
};
static const js_class_definition_t dbh_desc = {
js_class_name,
FSDBH::Construct,
dbh_methods,
dbh_props
};
static switch_status_t dbh_load(const v8::FunctionCallbackInfo<Value>& info)
{
JSBase::Register(info.GetIsolate(), &dbh_desc);
return SWITCH_STATUS_SUCCESS;
}
static const v8_mod_interface_t dbh_module_interface = {
/*.name = */ js_class_name,
/*.js_mod_load */ dbh_load
};
const v8_mod_interface_t *FSDBH::GetModuleInterface()
{
return &dbh_module_interface;
}
/* 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:
*/