Index: trunk/FACT++/src/InterpreterV8.cc
===================================================================
--- trunk/FACT++/src/InterpreterV8.cc	(revision 14135)
+++ trunk/FACT++/src/InterpreterV8.cc	(revision 14177)
@@ -1,3 +1,7 @@
 #include "InterpreterV8.h"
+
+#ifdef HAVE_SQL
+#include "Database.h"
+#endif
 
 #include <boost/tokenizer.hpp>
@@ -294,5 +298,200 @@
 }
 
-Handle<Value> Convert(char type, const char* &ptr)
+Handle<Value> InterpreterV8::FuncDbClose(const Arguments &args)
+{
+    HandleScope handle_scope;
+
+    void *ptr = Local<External>::Cast(args.This()->GetInternalField(0))->Value();
+    if (!ptr)
+        return Boolean::New(false);
+
+#ifdef HAVE_SQL
+    Database *db = reinterpret_cast<Database*>(ptr);
+    auto it = find(fDatabases.begin(), fDatabases.end(), db);
+    fDatabases.erase(it);
+    delete db;
+#endif
+
+    args.This()->SetInternalField(0, External::New(0));
+
+    return Boolean::New(true);
+}
+Handle<Value> InterpreterV8::FuncDbQuery(const Arguments &args)
+{
+    if (args.Length()!=1)
+        return ThrowException(String::New("Number of arguments must be exactly 1."));
+
+    if (!args[0]->IsString())
+        return ThrowException(String::New("Both arguments must be a string."));
+
+    HandleScope handle_scope;
+
+    void *ptr = Local<External>::Cast(args.This()->GetInternalField(0))->Value();
+    if (!ptr)
+        return Undefined();
+
+    const String::Utf8Value query(args[0]);
+
+#ifdef HAVE_SQL
+    try
+    {
+        Database *db = reinterpret_cast<Database*>(ptr);
+
+        const mysqlpp::StoreQueryResult res = db->query(*query).store();
+
+        Handle<Array> ret = Array::New();
+        ret->Set(String::New("table"), String::New(res.table()));
+
+        int irow=0;
+        for (vector<mysqlpp::Row>::const_iterator it=res.begin(); it<res.end(); it++)
+        {
+            Handle<Array> row = Array::New();
+
+            const mysqlpp::FieldNames *list = it->field_list().list;
+
+            for (size_t i=0; i<it->size(); i++)
+            {
+                const Handle<Value> name = String::New((*list)[i].c_str());
+
+                if ((*it)[i].is_null())
+                {
+                    row->Set(name, Undefined());
+                    continue;
+                }
+
+                const string sql_type = (*it)[i].type().sql_name();
+
+                const bool uns = sql_type.find("UNSIGNED")==string::npos;
+
+                if (sql_type.find("BIGINT")!=string::npos)
+                {
+                    if (uns)
+                    {
+                        const uint64_t val = (uint64_t)(*it)[i];
+                        if (val>UINT32_MAX)
+                            row->Set(name, Number::New(val));
+                        else
+                            row->Set(name, Integer::NewFromUnsigned(val));
+                    }
+                    else
+                    {
+                        const int64_t val = (int64_t)(*it)[i];
+                        if (val<INT32_MIN || val>INT32_MAX)
+                            row->Set(name, Number::New(val));
+                        else
+                            row->Set(name, Integer::NewFromUnsigned(val));
+                    }
+                    continue;
+                }
+
+                // 32 bit
+                if (sql_type.find("INT")!=string::npos)
+                {
+                    if (uns)
+                        row->Set(name, Integer::NewFromUnsigned((uint32_t)(*it)[i]));
+                    else
+                        row->Set(name, Integer::New((int32_t)(*it)[i]));
+                }
+
+                if (sql_type.find("BOOL")!=string::npos )
+                {
+                    row->Set(name, Boolean::New((bool)(*it)[i]));
+                    continue;
+                }
+
+                if (sql_type.find("FLOAT")!=string::npos)
+                {
+                    ostringstream val;
+                    val << setprecision(7) << (float)(*it)[i];
+                    row->Set(name, Number::New(stod(val.str())));
+                    continue;
+
+                }
+                if (sql_type.find("DOUBLE")!=string::npos)
+                {
+                    row->Set(name, Number::New((double)(*it)[i]));
+                    continue;
+                }
+
+                if (sql_type.find("CHAR")!=string::npos ||
+                    sql_type.find("TEXT")!=string::npos)
+                {
+                    row->Set(name, String::New((const char*)(*it)[i]));
+                    continue;
+                }
+
+                if (sql_type.find("TIMESTAMP")!=string::npos)
+                {
+                    row->Set(name, Date::New(time_t(mysqlpp::Time((*it)[i]))*1000));
+                    continue;
+                }
+
+                if (sql_type.find("DATETIME")!=string::npos)
+                {
+                    row->Set(name, Date::New(time_t(mysqlpp::DateTime((*it)[i]))*1000));
+                    continue;
+                }
+
+                if (sql_type.find(" DATE ")!=string::npos)
+                {
+                    row->Set(name, Date::New(time_t((mysqlpp::Date)(*it)[i])*1000));
+                    continue;
+                }
+
+            }
+
+            ret->Set(Integer::New(irow++), row);
+        }
+
+        return handle_scope.Close(ret);
+    }
+    catch (const exception &e)
+    {
+        return ThrowException(String::New(e.what()));
+    }
+#endif
+}
+
+Handle<Value> InterpreterV8::FuncDatabase(const Arguments &args)
+{
+    if (args.Length()!=1)
+        return ThrowException(String::New("Number of arguments must be exactly 1."));
+
+    if (!args[0]->IsString())
+        return ThrowException(String::New("Argument 1 must be a string."));
+
+    HandleScope handle_scope;
+
+    const String::Utf8Value database(args[0]);
+    const String::Utf8Value query   (args[1]);
+
+#ifdef HAVE_SQL
+    try
+    {
+        Database *db = new Database(*database);
+        fDatabases.push_back(db);
+
+        Handle<ObjectTemplate> tem = ObjectTemplate::New();
+        tem->Set(String::New("user"),     String::New(db->user.c_str()), ReadOnly);
+        tem->Set(String::New("server"),   String::New(db->server.c_str()), ReadOnly);
+        tem->Set(String::New("database"), String::New(db->db.c_str()), ReadOnly);
+        tem->Set(String::New("port"),     db->port==0?Undefined():Integer::NewFromUnsigned(db->port), ReadOnly);
+        tem->Set(String::New("query"),    FunctionTemplate::New(WrapDbQuery), ReadOnly);
+        tem->Set(String::New("close"),    FunctionTemplate::New(WrapDbClose),   ReadOnly);
+        tem->SetInternalFieldCount(1);
+
+        Handle<Object> obj = tem->NewInstance();
+        obj->SetInternalField(0, External::New(db));
+
+        return handle_scope.Close(obj);
+    }
+    catch (const exception &e)
+    {
+        return ThrowException(String::New(e.what()));
+    }
+#endif
+}
+
+Handle<Value> InterpreterV8::Convert(char type, const char* &ptr)
 {
     // Dim values are always unsigned per (FACT++) definition
@@ -327,5 +526,4 @@
 }
 
-
 Handle<Value> InterpreterV8::FuncClose(const Arguments &args)
 {
@@ -477,11 +675,12 @@
     // Create a template for the global object.
     Handle<ObjectTemplate> dim = ObjectTemplate::New();
-    dim->Set(String::New("print"),   FunctionTemplate::New(WrapPrint), ReadOnly);
-    dim->Set(String::New("out"),     FunctionTemplate::New(WrapOut),   ReadOnly);
-    dim->Set(String::New("wait"),    FunctionTemplate::New(WrapWait),  ReadOnly);
-    dim->Set(String::New("send"),    FunctionTemplate::New(WrapSend),  ReadOnly);
-    dim->Set(String::New("state"),   FunctionTemplate::New(WrapState), ReadOnly);
-    dim->Set(String::New("sleep"),   FunctionTemplate::New(WrapSleep), ReadOnly);
-    dim->Set(String::New("open"),    FunctionTemplate::New(WrapOpen),  ReadOnly);
+    dim->Set(String::New("print"),    FunctionTemplate::New(WrapPrint),    ReadOnly);
+    dim->Set(String::New("out"),      FunctionTemplate::New(WrapOut),      ReadOnly);
+    dim->Set(String::New("wait"),     FunctionTemplate::New(WrapWait),     ReadOnly);
+    dim->Set(String::New("send"),     FunctionTemplate::New(WrapSend),     ReadOnly);
+    dim->Set(String::New("state"),    FunctionTemplate::New(WrapState),    ReadOnly);
+    dim->Set(String::New("sleep"),    FunctionTemplate::New(WrapSleep),    ReadOnly);
+    dim->Set(String::New("open"),     FunctionTemplate::New(WrapOpen),     ReadOnly);
+    dim->Set(String::New("database"), FunctionTemplate::New(WrapDatabase), ReadOnly);
 
     Handle<ObjectTemplate> global = ObjectTemplate::New();
@@ -492,5 +691,5 @@
 
     // Persistent
-    Handle<Context> context = Context::New(NULL, global);
+    Persistent<Context> context = Context::New(NULL, global);
     if (context.IsEmpty())
     {
@@ -515,5 +714,12 @@
     //context->Exit();
 
-    //context.Dispose();
+    context.Dispose();
+
+#ifdef HAVE_SQL
+    for (auto it=fDatabases.begin(); it!=fDatabases.end(); it++)
+        delete *it;
+    fDatabases.clear();
+#endif
+
 
     JsEnd(filename);
