Browse Source

Merge pull request #8931 from rpastrana/HPCC-15612-couchbaseplugin

Hpcc 15612 Embedded Couchbase Plugin

Reviewed-By: Jamie Noss <james.noss@lexisnexis.com>
Reviewed-By: Dan Camper <dan.camper@lexisnexis.com>
Reviewed-By: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 8 years ago
parent
commit
1aa0b65d3a

+ 3 - 0
.gitmodules

@@ -37,3 +37,6 @@
 [submodule "system/lz4_sm/lz4"]
     path = system/lz4_sm/lz4
     url = https://github.com/hpcc-systems/lz4.git
+[submodule "plugins/couchbase/libcouchbase-cxx"]
+	path = plugins/couchbase/libcouchbase-cxx
+	url = https://github.com/hpcc-systems/libcouchbase-cxx.git

+ 1 - 0
CMakeLists.txt

@@ -152,6 +152,7 @@ if ( PLUGIN )
     HPCC_ADD_SUBDIRECTORY (plugins/sqlite3 "SQLITE3EMBED")
     HPCC_ADD_SUBDIRECTORY (plugins/mysql "MYSQLEMBED")
     HPCC_ADD_SUBDIRECTORY (plugins/exampleplugin "EXAMPLEPLUGIN")
+    HPCC_ADD_SUBDIRECTORY (plugins/couchbase "COUCHBASEEMBED")
 elseif ( NOT MAKE_DOCS_ONLY )
     HPCC_ADD_SUBDIRECTORY (initfiles)
     HPCC_ADD_SUBDIRECTORY (tools)

+ 51 - 0
cmake_modules/FindCOUCHBASE.cmake

@@ -0,0 +1,51 @@
+################################################################################
+#    HPCC SYSTEMS software Copyright (C) 2016 HPCC Systems®.
+#
+#    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.
+################################################################################
+
+
+# - Try to find the Couchbase c library
+# Once done this will define
+#
+#  LIBCOUCHBASE_FOUND, if false, do not try to link with libev
+#  LIBCOUCHBASE_LIBRARIES, Library path and libs
+#  LIBCOUCHBASE_INCLUDE_DIR, where to find the libev headers
+
+
+IF (NOT LIBCOUCHBASE_FOUND)
+
+  #couchbase.h
+  #libcouchbase
+
+  FIND_PATH (
+    LIBCOUCHBASE_INCLUDE_DIR 
+    NAMES couchbase.h
+    PATH_SUFFIXES libcouchbase
+   )
+
+   FIND_LIBRARY (
+     LIBCOUCHBASE_LIBRARIES
+     NAMES couchbase libcouchbase
+     PATH_SUFFIXES libcouchbase
+   )
+
+  include(FindPackageHandleStandardArgs)
+  find_package_handle_standard_args(
+    couchbase DEFAULT_MSG
+    LIBCOUCHBASE_LIBRARIES
+    LIBCOUCHBASE_INCLUDE_DIR
+  )
+
+  MARK_AS_ADVANCED(LIBCOUCHBASE_INCLUDE_DIR LIBCOUCHBASE_LIBRARIES)
+ENDIF()

+ 9 - 2
cmake_modules/commonSetup.cmake

@@ -112,6 +112,7 @@ IF ("${COMMONSETUP_DONE}" STREQUAL "")
   option(KAFKA "Create a package with ONLY the kafkaembed plugin" OFF)
   #"cmake -DEXAMPLEPLUGIN=ON <path-to/HPCC-Platform/>" will configure the plugin makefiles to be built with "make".
   option(EXAMPLEPLUGIN "Create a package with ONLY the exampleplugin plugin" OFF)
+  option(COUCHBASEEMBED "Create a package with ONLY the couchbaseembed plugin" OFF)
 
   if (APPLE OR WIN32)
       option(USE_TBB "Enable Threading Building Block support" OFF)
@@ -124,9 +125,8 @@ IF ("${COMMONSETUP_DONE}" STREQUAL "")
 
   option(USE_OPTIONAL "Automatically disable requested features with missing dependencies" ON)
 
-
     if(REMBED OR V8EMBED OR MEMCACHED OR PYEMBED OR REDIS OR JAVAEMBED OR MYSQLEMBED
-        OR SQLITE3EMBED OR KAFKA OR EXAMPLEPLUGIN)
+        OR SQLITE3EMBED OR KAFKA OR EXAMPLEPLUGIN OR COUCHBASEEMBED)
         set(PLUGIN ON)
         set(CLIENTTOOLS OFF)
         set(PLATFORM OFF)
@@ -204,6 +204,13 @@ IF ("${COMMONSETUP_DONE}" STREQUAL "")
            set(pluginname "exampleplugin")
         endif()
     endif()
+    if(COUCHBASEEMBED)
+        if(DEFINED pluginname)
+            message(FATAL_ERROR "Cannot enable couchbaseembed, already declared ${pluginname}")
+        else()
+            set(pluginname "couchbaseembed")
+        endif()
+    endif()
 
   if ( USE_XALAN AND USE_LIBXSLT )
       set(USE_LIBXSLT OFF)

+ 2 - 0
plugins/CMakeLists.txt

@@ -35,3 +35,5 @@ add_subdirectory (memcached)
 add_subdirectory (redis)
 add_subdirectory (kafka)
 add_subdirectory (exampleplugin)
+add_subdirectory (couchbase)
+

+ 66 - 0
plugins/couchbase/CMakeLists.txt

@@ -0,0 +1,66 @@
+################################################################################
+#    HPCC SYSTEMS software Copyright (C) 2016 HPCC Systems®.
+#
+#    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.
+################################################################################
+
+# Component: couchbaseembed
+
+#####################################################
+# Description:
+# ------------
+#    Cmake Input File for couchbaseembed
+#####################################################
+
+project(couchbaseembed)
+
+IF ( COUCHBASEEMBED )
+    ADD_PLUGIN(COUCHBASEEMBED PACKAGES COUCHBASE)
+    IF ( MAKE_COUCHBASEEMBED )
+        set(
+            SRCS
+            couchbaseembed.cpp
+        )
+
+        INCLUDE_DIRECTORIES (
+            ${PROJECT_SOURCE_DIR}/libcouchbase-cxx/include
+            ./../../esp/platform #for token serialize/deserialize
+            ./../../system/include
+            ./../../rtl/eclrtl
+            ./../../rtl/include
+            ./../../rtl/nbcd
+            ./../../common/deftype
+            ./../../system/jlib
+            ./../../roxie/roxiemem
+        )
+
+        ADD_DEFINITIONS(-D_USRDLL -DCOUCHBASEEMBED_EXPORTS)
+        HPCC_ADD_LIBRARY(couchbaseembed SHARED ${SRCS})
+        install(
+            TARGETS couchbaseembed
+            DESTINATION plugins)
+
+        target_link_libraries(
+            couchbaseembed
+            ${LIBCOUCHBASE_LIBRARIES}
+            eclrtl
+            jlib)
+    endif()
+endif()
+
+if(PLATFORM OR CLIENTTOOLS_ONLY)
+    install(
+        FILES ${CMAKE_CURRENT_SOURCE_DIR}/couchbase.ecllib
+        DESTINATION plugins
+        COMPONENT Runtime)
+endif()

+ 157 - 0
plugins/couchbase/README.md

@@ -0,0 +1,157 @@
+#ECL Embedded Couchbase plugin
+
+This is the ECL plugin to access couchbase systems
+
+Client access is based on libcouchbase (c interface provided by couchbase install) and
+libcouchbase-cxx (from https://github.com/couchbaselabs/libcouchbase-cxx)
+
+##Installation and Dependencies
+
+[libcouchbase] is installed via standard package managers
+
+
+(https://github.com/couchbaselabs/libcouchbase-cxx) is included as a git
+submodule in HPCC-Platform.  It will be built and integrated automatically when
+you build the HPCC-Platform project with the couchbase plugin flag turned on.
+
+The recommended method for obtaining libcouchbase is via
+On Ubuntu: sudo apt-get install libcouchbase-dev libcouchbase2-bin build-essential
+
+##ECL Plugin Use
+This is a WIP plugin, currently only exposing a general 'executen1ql' interface
+
+Examples:
+IMPORT * FROM plugins.couchbase;
+
+server := '127.0.0.1';
+
+ECLGPS := RECORD
+    string latitude,
+    string longitude;
+END;
+
+flatiotrec := RECORD
+    string latitude,
+    real4 longitude,
+    boolean isStaleData,
+    INTEGER sequence;
+END;
+
+rawdatarec := RECORD
+    real4 ambientTemp;
+    real4 barometer;
+    real4 batteryLevelPercentage;
+    real4 bodyTemp;
+    real4 coLevel;
+    real4 forceSensitiveResistance;
+    INTEGER heartRate;
+END;
+
+fulliotrec := RECORD
+   real4 accelx;
+	 real4 accely;
+	 real4 accelz;
+	 //ECLGPS gps;
+	 //dataset(contextualDatarec) contextualData;
+	 string4 eventId;
+	 boolean eventStatus;
+	 string guid;
+	 boolean isStaleData;
+	 integer sequence;
+	 integer sourceoffset;
+	 unsigned sourcepartition;
+	 string sourcetopic;
+	 string timestamp;
+	 //dataset(locationDatarec) locationData;
+	 dataset(rawdatarec) rawData;
+END;
+
+
+createprimeindex() := EMBED(couchbase : server(server), bucket('iot'), user('rpastrana'), detailed_errcodes(1), operation_timeout(5.5), config_total_timeout(15))
+  create primary index on iot;
+ENDEMBED;
+
+string scalarsimpleselect() := EMBED(couchbase : server(server), bucket('iot'), user('rpastrana'), detailed_errcodes(1), operation_timeout(5.5), config_total_timeout(15))
+  SELECT timestamp from iot where timestamp = '2016-01-07 11:36:05.657314-04:00' ;
+ENDEMBED;
+
+string invalidquery() := EMBED(couchbase : server(server), bucket('iot'), user('rpastrana'), detailed_errcodes(1), operation_timeout(5.5), config_total_timeout(15))
+  SELECT x from iot;
+ENDEMBED;
+
+string simplefilteredselect() := EMBED(couchbase : server(server), bucket('iot'), user('rpastrana'), detailed_errcodes(1), operation_timeout(5.5), config_total_timeout(15))
+  SELECT contextualData.gps from iot where contextualData.gps.latitude = 44.968046;
+ENDEMBED;
+
+unsigned parameterizedselectbool(boolean m) := EMBED(couchbase : server(server), bucket('iot'), user('rpastrana'), detailed_errcodes(1), operation_timeout(5.5), config_total_timeout(15))
+  SELECT count(accelx) from iots where accelx = $m ;
+ENDEMBED;
+
+string parameterizedselect(REAL m) := EMBED(couchbase : server(server), bucket('iot'), user('rpastrana'), detailed_errcodes(1), operation_timeout(5.5), config_total_timeout(15))
+  SELECT contextualData.gps.longitude from iot where contextualData.gps.latitude = $m;
+ENDEMBED;
+
+ INTEGER selectnegativeint (REAL m) := EMBED(couchbase : server(server), bucket('iot'), user('rpastrana'), detailed_errcodes(1), operation_timeout(5.5), config_total_timeout(15))
+   select sourceoffset  from  iot where sourceoffset < $m;
+ENDEMBED;
+
+INTEGER preparedselectint(REAL m) := EMBED(couchbase : server(server), bucket('iot'), user('rpastrana'), detailed_errcodes(1), operation_timeout(5.5), config_total_timeout(15))
+     SELECT sequence from iot where contextualData.gps.latitude = $m;
+ENDEMBED;
+
+REAL preparedselectreal(REAL m) := EMBED(couchbase : server(server), bucket('iot'), user('rpastrana'), detailed_errcodes(1), operation_timeout(5.5), config_total_timeout(15))
+  SELECT contextualData.gps.longitude from iot where contextualData.gps.latitude = $m;
+ENDEMBED;
+
+boolean preparedselectboolean(REAL m) := EMBED(couchbase : server(server), bucket('iot'), user('rpastrana'), detailed_errcodes(1), operation_timeout(5.5), config_total_timeout(15))
+  SELECT isStaleData from iot where contextualData.gps.latitude = $m;
+ENDEMBED;
+
+dataset(flatiotrec) flatdatasetpreparedselect(REAL m) := EMBED(couchbase : server(server), bucket('iot'), user('rpastrana'), detailed_errcodes(1), operation_timeout(5.5), config_total_timeout(15))
+  SELECT contextualData.gps.latitude,contextualData.gps.longitude, isStaleData, sequence  from iot where contextualData.gps.latitude = $m;
+ENDEMBED;
+
+dataset(iotrec) datasetpreparedselect(REAL m) := EMBED(couchbase : server(server), bucket('iot'), user('rpastrana'), detailed_errcodes(1), operation_timeout(5.5), config_total_timeout(15))
+  SELECT contextualData.gps  from iot where contextualData.gps.latitude = $m;
+ENDEMBED;
+
+//embeded couchbase call results in odd ECL error indicating a missing plugin function, but the function exists...
+//Error:  no matching function for call to ‘IEmbedFunctionContext::getRowResult()’ (35, 32 - W20160701-104659_1.cpp)
+//Error:  Compile/Link failed for W20160701-104659 (see '//10.0.2.15/var/lib/HPCCSystems/myeclccserver/eclcc.log' for details) (0, 0 - W20160701-104659)
+row rowpreparedselect(REAL m) := EMBED(couchbase : server(server), bucket('iot'), user('rpastrana'), detailed_errcodes(1), operation_timeout(5.5), config_total_timeout(15))
+  SELECT contextualData.gps from iot where contextualData.gps.latitude = $m;
+ENDEMBED;
+
+string stringselecteventid(REAL m) := EMBED(couchbase : server(server), bucket('iot'), user('rpastrana'), detailed_errcodes(1), operation_timeout(5.5), config_total_timeout(15))
+  SELECT eventId from iot where contextualData.gps.latitude = $m;
+ENDEMBED;
+
+string  dataselecteventid(REAL m) := EMBED(couchbase : server(server), bucket('iot'), user('rpastrana'), detailed_errcodes(1), operation_timeout(5.5), config_total_timeout(15))
+  SELECT timestamp from iot where contextualData.gps.latitude = $m;
+ENDEMBED;
+
+boolean  boolselecteventstatus(REAL m) := EMBED(couchbase : server(server), bucket('iot'), user('rpastrana'), detailed_errcodes(1), operation_timeout(5.5), config_total_timeout(15))
+  SELECT eventStatus from iot where contextualData.gps.latitude = $m;
+ENDEMBED;
+
+dataset(fulliotrec) fullselect() := EMBED(couchbase : server(server), bucket('iot'), user('rpastrana'), detailed_errcodes(1), operation_timeout(5.5), config_total_timeout(15))
+  SELECT iot.* from iot;
+ENDEMBED;
+
+sequential (
+  createprimeindex(),
+  OUTPUT(parameterizedselectbool(1)),
+  OUTPUT(scalarsimpleselect()),
+  OUTPUT(parameterizedselect(44.968046)),
+  OUTPUT(selectnegativeint(0)),
+  output(preparedselect(44.968046)),
+  OUTPUT(stringselecteventid(44.968046)),
+  OUTPUT(preparedselectint(44.968046)),
+  OUTPUT(rowpreparedselect(44.968046)),
+  OUTPUT(flatdatasetpreparedselect(44.968046)),
+  OUTPUT(datasetpreparedselect(44.968046)),
+  Count(datasetpreparedselect(44.968046)),
+  OUTPUT(parameterizedselect(44.968046)),
+  OUTPUT(fullselect())
+  OUTPUT('Done');
+);

+ 26 - 0
plugins/couchbase/couchbase.ecllib

@@ -0,0 +1,26 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2016 HPCC Systems®.
+
+    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.
+############################################################################## */
+
+EXPORT Language := SERVICE : plugin('couchbaseembed')
+  boolean getEmbedContext():cpp,pure,namespace='couchbaseembed',entrypoint='getEmbedContext',prototype='IEmbedContext* getEmbedContext()';
+  boolean syntaxCheck(const varstring src):cpp,pure,namespace='couchbaseembed',entrypoint='syntaxCheck';
+END;
+
+EXPORT getEmbedContext := Language.getEmbedContext;
+EXPORT syntaxCheck := Language.syntaxCheck;
+EXPORT boolean supportsImport := false;
+EXPORT boolean supportsScript := true;

File diff suppressed because it is too large
+ 999 - 0
plugins/couchbase/couchbaseembed.cpp


+ 449 - 0
plugins/couchbase/couchbaseembed.hpp

@@ -0,0 +1,449 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2016 HPCC Systems®.
+
+    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.
+############################################################################## */
+
+#ifndef _COUCHBASEEMBED_INCL
+#define _COUCHBASEEMBED_INCL
+
+#ifdef _WIN32
+#define COUCHBASEEMBED_PLUGIN_CALL _cdecl
+#ifdef COUCHBASEEMBED_PLUGIN_EXPORTS
+#define COUCHBASEEMBED_PLUGIN_API __declspec(dllexport)
+#else
+#define COUCHBASEEMBED_PLUGIN_API __declspec(dllimport)
+#endif
+#else
+#define COUCHBASEEMBED_PLUGIN_CALL
+#define COUCHBASEEMBED_PLUGIN_API
+#endif
+
+//Using cpp wrapper from https://github.com/couchbaselabs/libcouchbase-cxx
+#include <libcouchbase/couchbase++.h>
+#include <libcouchbase/couchbase++/views.h>
+#include <libcouchbase/couchbase++/query.h>
+#include <libcouchbase/couchbase++/endure.h>
+#include <libcouchbase/couchbase++/logging.h>
+
+#include "platform.h"
+#include "jthread.hpp"
+#include "hqlplugins.hpp"
+#include "eclrtl_imp.hpp"
+#include "eclhelper.hpp"
+#include "rtlembed.hpp"
+#include "jptree.hpp"
+#include "tokenserialization.hpp"
+#include "rtlds_imp.hpp"
+#include "rtlfield_imp.hpp"
+#include "roxiemem.hpp"
+
+
+namespace couchbaseembed
+{
+    extern void UNSUPPORTED(const char *feature) __attribute__((noreturn));
+    extern void failx(const char *msg, ...) __attribute__((noreturn))  __attribute__((format(printf, 1, 2)));
+    extern void fail(const char *msg) __attribute__((noreturn));
+
+    static void typeError(const char *expected, const char * fieldname)
+    {
+        VStringBuffer msg("Couchbase: type mismatch - %s expected", expected);
+        if (fieldname && *fieldname)
+            msg.appendf(" for field %s", fieldname);
+        rtlFail(0, msg.str());
+    }
+
+    static void typeError(const char *expected, const RtlFieldInfo *field)
+    {
+        typeError(expected, field ? field->name->queryStr() : nullptr);
+    }
+
+    static int getNumFields(const RtlTypeInfo *record)
+    {
+        int count = 0;
+        const RtlFieldInfo * const *fields = record->queryFields();
+        assertex(fields);
+        while (*fields++)
+            count++;
+        return count;
+    }
+
+    static void handleDeserializeOutcome(DeserializationResult resultcode, const char * targetype, const char * culpritvalue)
+    {
+        switch (resultcode)
+        {
+            case Deserialization_SUCCESS:
+                break;
+            case Deserialization_BAD_TYPE:
+                failx("Deserialization error (%s): value cannot be const", targetype);
+                break;
+            case Deserialization_UNSUPPORTED:
+                failx("Deserialization error (%s): encountered value type not supported", targetype);
+                break;
+            case Deserialization_INVALID_TOKEN:
+                failx("Deserialization error (%s): token cannot be NULL, empty, or all whitespace", targetype);
+                break;
+            case Deserialization_NOT_A_NUMBER:
+                failx("Deserialization error (%s): non-numeric characters found in numeric conversion: '%s'", targetype, culpritvalue);
+                break;
+            case Deserialization_OVERFLOW:
+                failx("Deserialization error (%s): number too large to be represented by receiving value", targetype);
+                break;
+            case Deserialization_UNDERFLOW:
+                failx("Deserialization error (%s): number too small to be represented by receiving value", targetype);
+                break;
+            default:
+                typeError(targetype, culpritvalue);
+                break;
+        }
+    }
+
+    static const char * findUnquotedChar(const char *query, char searchFor)
+    {
+        // Note - returns pointer to char AFTER the first occurrence of searchFor outside of quotes
+        char inStr = '\0';
+        char ch;
+        while ((ch = *query++) != 0)
+        {
+            if (ch == inStr)
+                inStr = false;
+            else switch (ch)
+            {
+            case '\'':
+            case '"':
+                inStr = ch;
+                break;
+            case '\\':
+                if (inStr && *query)
+                    query++;
+                break;
+            case '/':
+                if (!inStr)
+                {
+                    if (*query=='/')
+                    {
+                        while (*query && *query != '\n')
+                            query++;
+                    }
+                    else if (*query=='*')
+                    {
+                        query++;
+                        loop
+                        {
+                            if (!*query)
+                                fail("Unterminated comment in query string");
+                            if (*query=='*' && query[1]=='/')
+                            {
+                                query+= 2;
+                                break;
+                            }
+                            query++;
+                        }
+                    }
+                }
+                break;
+            default:
+                if (!inStr && ch==searchFor)
+                    return query;
+                break;
+            }
+        }
+        return NULL;
+    }
+
+    static unsigned countParameterPlaceholders(const char *query)
+    {
+        unsigned queryCount = 0;
+        while ((query = findUnquotedChar(query, '$')) != NULL)
+            queryCount++;
+        return queryCount;
+    }
+
+    class CouchbaseRowStream : public RtlCInterface, implements IRowStream
+    {
+    public:
+        CouchbaseRowStream(IEngineRowAllocator* _resultAllocator, Couchbase::Query * cbaseQuery);
+        virtual ~CouchbaseRowStream();
+
+        RTLIMPLEMENT_IINTERFACE
+        virtual const void* nextRow();
+        virtual void stop();
+    private:
+        Couchbase::Query *              m_CouchBaseQuery;   //!< pointer to couchbase query (holds results and metadata)
+
+        Linked<IEngineRowAllocator>     m_resultAllocator;  //!< Pointer to allocator used when building result rows
+        bool                            m_shouldRead;       //!< If true, we should continue trying to read more messages
+        StringArray                     m_Rows;             //!< Local copy of result rows
+        __int64                         m_currentRow;       //!< Current result row
+
+    };
+
+    class CouchbaseConnection : public CInterface
+    {
+    public:
+        inline CouchbaseConnection(bool useSSL, const char * host, unsigned port, const char * bucketname, const char * user, const char * password, const char * connOptions)
+        {
+            m_connectionString.setf("couchbase%s://%s/%s%s", useSSL ? "s" : "", host, bucketname, connOptions);
+            m_pCouchbaseClient = new Couchbase::Client(m_connectionString.str());//USER/PASS still needed
+            m_pQuery = nullptr;
+        }
+
+        inline ~CouchbaseConnection()
+        {
+        }
+
+        inline void connect()
+        {
+            m_connectionStatus = m_pCouchbaseClient->connect();
+            if (!m_connectionStatus.success())
+                failx("Failed to connect to couchbase instance: %s Reason: %s", m_connectionString.str(), m_connectionStatus.description());
+        }
+
+        Couchbase::Query * query(Couchbase::QueryCommand * qcommand);
+
+    private:
+        StringBuffer m_connectionString;
+        Couchbase::Client * m_pCouchbaseClient;
+        Couchbase::Status  m_connectionStatus;
+        Couchbase::Query  * m_pQuery;
+
+        CouchbaseConnection(const CouchbaseConnection &);
+    };
+
+    class CouchbaseRowBuilder : public CInterfaceOf<IFieldSource>
+    {
+    public:
+        CouchbaseRowBuilder(IPropertyTree * resultrow) :  m_fieldsProcessedCount(0), m_rowFieldCount(0)
+        {
+            m_oResultRow.set(resultrow);
+        }
+
+        virtual bool getBooleanResult(const RtlFieldInfo *field);
+        virtual void getDataResult(const RtlFieldInfo *field, size32_t &len, void * &result);
+        virtual double getRealResult(const RtlFieldInfo *field);
+        virtual __int64 getSignedResult(const RtlFieldInfo *field);
+        virtual unsigned __int64 getUnsignedResult(const RtlFieldInfo *field);
+        virtual void getStringResult(const RtlFieldInfo *field, size32_t &chars, char * &result);
+        virtual void getUTF8Result(const RtlFieldInfo *field, size32_t &chars, char * &result);
+        virtual void getUnicodeResult(const RtlFieldInfo *field, size32_t &chars, UChar * &result);
+        virtual void getDecimalResult(const RtlFieldInfo *field, Decimal &value);
+        virtual void processBeginSet(const RtlFieldInfo * field, bool &isAll)
+        {
+            UNSUPPORTED("Embedded Couchbase support error: processBeginSet() not supported");
+        }
+        virtual bool processNextSet(const RtlFieldInfo * field)
+        {
+            UNSUPPORTED("Embedded Couchbase support error: processNextSet() not supported");
+            return false;
+        }
+        virtual void processBeginDataset(const RtlFieldInfo * field);
+        virtual void processBeginRow(const RtlFieldInfo * field);
+        virtual bool processNextRow(const RtlFieldInfo * field);
+        virtual void processEndSet(const RtlFieldInfo * field)
+        {
+            UNSUPPORTED("Embedded Couchbase support error: processEndSet() not supported");
+        }
+        virtual void processEndDataset(const RtlFieldInfo * field);
+        virtual void processEndRow(const RtlFieldInfo * field);
+
+    protected:
+        const char * nextField(const RtlFieldInfo * field);
+    private:
+        TokenDeserializer m_tokenDeserializer;
+        Owned<IPropertyTree> m_oResultRow;
+        Owned<IPropertyTree> m_oNestedField;
+        int m_fieldsProcessedCount;
+        int m_rowFieldCount;
+    };
+
+    // Bind Couchbase columns from an ECL record
+    class CouchbaseRecordBinder : public CInterfaceOf<IFieldProcessor>
+    {
+    public:
+        CouchbaseRecordBinder(const IContextLogger &_logctx, const RtlTypeInfo *_typeInfo, Couchbase::QueryCommand * _pQcmd, int _firstParam)
+         : logctx(_logctx), typeInfo(_typeInfo), m_pQcmd(_pQcmd), firstParam(_firstParam), dummyField("<row>", NULL, typeInfo), thisParam(_firstParam) {}
+
+        int numFields();
+        void processRow(const byte *row);
+        virtual void processString(unsigned len, const char *value, const RtlFieldInfo * field);
+        virtual void processBool(bool value, const RtlFieldInfo * field);
+        virtual void processData(unsigned len, const void *value, const RtlFieldInfo * field);
+        virtual void processInt(__int64 value, const RtlFieldInfo * field);
+        virtual void processUInt(unsigned __int64 value, const RtlFieldInfo * field);
+        virtual void processReal(double value, const RtlFieldInfo * field);
+        virtual void processDecimal(const void *value, unsigned digits, unsigned precision, const RtlFieldInfo * field);
+        virtual void processUDecimal(const void *value, unsigned digits, unsigned precision, const RtlFieldInfo * field)
+        {
+            UNSUPPORTED("UNSIGNED decimals");
+        }
+
+        virtual void processUnicode(unsigned chars, const UChar *value, const RtlFieldInfo * field);
+        virtual void processQString(unsigned len, const char *value, const RtlFieldInfo * field);
+        virtual void processUtf8(unsigned chars, const char *value, const RtlFieldInfo * field);
+        virtual bool processBeginSet(const RtlFieldInfo * field, unsigned numElements, bool isAll, const byte *data)
+        {
+            UNSUPPORTED("SET");
+            return false;
+        }
+        virtual bool processBeginDataset(const RtlFieldInfo * field, unsigned numRows)
+        {
+            return false;
+        }
+        virtual bool processBeginRow(const RtlFieldInfo * field)
+        {
+            return true;
+        }
+        virtual void processEndSet(const RtlFieldInfo * field)
+        {
+            UNSUPPORTED("SET");
+        }
+        virtual void processEndDataset(const RtlFieldInfo * field)
+        {
+            UNSUPPORTED("DATASET");
+        }
+        virtual void processEndRow(const RtlFieldInfo * field)
+        {
+        }
+
+    protected:
+        inline unsigned checkNextParam(const RtlFieldInfo * field);
+
+        const RtlTypeInfo *typeInfo;
+        Couchbase::QueryCommand * m_pQcmd;
+        const IContextLogger &logctx;
+        int firstParam;
+        RtlFieldStrInfo dummyField;
+        int thisParam;
+        TokenSerializer m_tokenSerializer;
+    };
+
+    class CouchbaseDatasetBinder : public CouchbaseRecordBinder
+    {
+    public:
+        CouchbaseDatasetBinder(const IContextLogger &_logctx, IRowStream * _input, const RtlTypeInfo *_typeInfo, Couchbase::QueryCommand * _pQcmd, int _firstParam)
+          : input(_input), CouchbaseRecordBinder(_logctx, _typeInfo, _pQcmd, _firstParam)
+        {
+        }
+
+        bool bindNext()
+        {
+            roxiemem::OwnedConstRoxieRow nextRow = (const byte *) input->ungroupedNextRow();
+            if (!nextRow)
+                return false;
+            processRow((const byte *) nextRow.get());   // Bind the variables for the current row
+            return true;
+        }
+
+        void executeAll(CouchbaseConnection * conn)
+        {
+            while (bindNext())
+            {
+                auto m_pQuery = conn->query(m_pQcmd);
+
+                if (m_pQuery->meta().status().errcode() != LCB_SUCCESS )//rows.length() == 0)
+                    failx("Query execution error: %s", m_pQuery->meta().body().to_string().c_str());
+
+                //consider parsing json result
+                if (strstr(m_pQuery->meta().body().data(), "\"status\": \"errors\""))
+                    failx("Err: %s", m_pQuery->meta().body().data());
+            }
+        }
+
+    protected:
+        Owned<IRowStream> input;
+    };
+
+    class CouchbaseEmbedFunctionContext : public CInterfaceOf<IEmbedFunctionContext>
+    {
+       public:
+           CouchbaseEmbedFunctionContext(const IContextLogger &_logctx, const char *options, unsigned _flags);
+           IPropertyTree * nextResultRowTree();
+           IPropertyTreeIterator * nextResultRowIterator();
+           const char * nextResultScalar();
+           virtual bool getBooleanResult();
+           virtual void getDataResult(size32_t &len, void * &result);
+           virtual double getRealResult();
+           virtual __int64 getSignedResult();
+           virtual unsigned __int64 getUnsignedResult();
+           virtual void getStringResult(size32_t &chars, char * &result);
+           virtual void getUTF8Result(size32_t &chars, char * &result);
+           virtual void getUnicodeResult(size32_t &chars, UChar * &result);
+           virtual void getDecimalResult(Decimal &value);
+           virtual void getSetResult(bool & __isAllResult, size32_t & __resultBytes, void * & __result, int elemType, size32_t elemSize)
+           {
+               UNSUPPORTED("SET results");
+           }
+           virtual IRowStream * getDatasetResult(IEngineRowAllocator * _resultAllocator);
+           virtual byte * getRowResult(IEngineRowAllocator * _resultAllocator);
+           virtual size32_t getTransformResult(ARowBuilder & rowBuilder);
+           virtual void bindRowParam(const char *name, IOutputMetaData & metaVal, byte *val);
+           virtual void bindDatasetParam(const char *name, IOutputMetaData & metaVal, IRowStream * val);
+           virtual void bindBooleanParam(const char *name, bool val);
+           virtual void bindDataParam(const char *name, size32_t len, const void *val);
+           virtual void bindFloatParam(const char *name, float val);
+           virtual void bindRealParam(const char *name, double val);
+           virtual void bindSignedSizeParam(const char *name, int size, __int64 val);
+           virtual void bindSignedParam(const char *name, __int64 val);
+           virtual void bindUnsignedSizeParam(const char *name, int size, unsigned __int64 val);
+           virtual void bindUnsignedParam(const char *name, unsigned __int64 val);
+           virtual void bindStringParam(const char *name, size32_t len, const char *val);
+           virtual void bindVStringParam(const char *name, const char *val);
+           virtual void bindUTF8Param(const char *name, size32_t chars, const char *val);
+           virtual void bindUnicodeParam(const char *name, size32_t chars, const UChar *val);
+           virtual void bindSetParam(const char *name, int elemType, size32_t elemSize, bool isAll, size32_t totalBytes, const void *setData)
+           {
+               UNSUPPORTED("SET parameters");
+           }
+           virtual IInterface *bindParamWriter(IInterface *esdl, const char *esdlservice, const char *esdltype, const char *name)
+           {
+               return NULL;
+           }
+           virtual void paramWriterCommit(IInterface *writer)
+           {
+               UNSUPPORTED("paramWriterCommit");
+           }
+           virtual void writeResult(IInterface *esdl, const char *esdlservice, const char *esdltype, IInterface *writer)
+           {
+               UNSUPPORTED("writeResult");
+           }
+           virtual void importFunction(size32_t lenChars, const char *text)
+           {
+               UNSUPPORTED("importFunction");
+           }
+           virtual void compileEmbeddedScript(size32_t chars, const char *script);
+           virtual void callFunction();
+       protected:
+           void execute();
+           unsigned countBindings(const char *query);
+           const char * findUnquoted(const char *query, char searchFor);
+           unsigned checkNextParam(const char *name);
+
+           const IContextLogger &logctx;
+           Owned<CouchbaseConnection>    m_oCBConnection;
+           Couchbase::Client           * m_pCouchbaseClient;
+           Couchbase::Query            * m_pQuery;
+           Couchbase::QueryCommand     * m_pQcmd;
+
+           StringArray m_Rows;
+           int m_NextRow;
+           Owned<CouchbaseDatasetBinder> m_oInputStream;
+           Couchbase::Internal::RowIterator<Couchbase::QueryRow> * cbQueryIterator;
+           TokenDeserializer m_tokenDeserializer;
+           TokenSerializer m_tokenSerializer;
+           unsigned m_nextParam;
+           unsigned m_numParams;
+           unsigned m_scriptFlags;
+       };
+} // couchbaseembed namespace
+#endif

+ 1 - 0
plugins/couchbase/libcouchbase-cxx

@@ -0,0 +1 @@
+Subproject commit b390d9c0c2e1b0880c00a9836eac54dcec07bb7a

+ 1 - 1
system/jlib/jptree.cpp

@@ -1057,7 +1057,7 @@ void PTree::setName(const char *_name)
         name = NULL;
     else
     {
-        if (!validateXMLTag(_name)) 
+        if (!validateXMLTag(_name))
             throw MakeIPTException(PTreeExcpt_InvalidTagName, ": %s", _name);
         name = kT->queryCreate(_name);
     }