소스 검색

HPCC-10461 Add additional plugins for external databases

Added sqlite plugin as a POC

Signed-off-by: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 11 년 전
부모
커밋
8c3f37da6d
7개의 변경된 파일664개의 추가작업 그리고 5개의 파일을 삭제
  1. 2 1
      cmake_modules/commonSetup.cmake
  2. 6 3
      ecl/hql/hqlgram.y
  3. 2 1
      plugins/CMakeLists.txt
  4. 64 0
      plugins/sqlite3/CMakeLists.txt
  5. 527 0
      plugins/sqlite3/sqlite3.cpp
  6. 25 0
      plugins/sqlite3/sqlite3.ecllib
  7. 38 0
      testing/ecl/sqlite.ecl

+ 2 - 1
cmake_modules/commonSetup.cmake

@@ -73,6 +73,7 @@ IF ("${COMMONSETUP_DONE}" STREQUAL "")
   option(USE_RESOURCE "Use resource download in ECLWatch" OFF)
   option(GENERATE_COVERAGE_INFO "Generate coverage info for gcov" OFF)
 
+  option(USE_SQLITE3 "Enable SqLite3 support" ON)
   option(USE_PYTHON "Enable Python support" ON)
   option(USE_V8 "Enable V8 JavaScript support" ON)
   option(USE_JNI "Enable Java JNI support" ON)
@@ -80,7 +81,7 @@ IF ("${COMMONSETUP_DONE}" STREQUAL "")
 
   option(USE_OPTIONAL "Automatically disable requested features with missing dependencies" ON)
 
-  if ( USE_PYTHON OR USE_V8 OR USE_JNI OR USE_RINSIDE )
+  if ( USE_PYTHON OR USE_V8 OR USE_JNI OR USE_RINSIDE OR USE_SQLITE3)
       set( WITH_PLUGINS ON )
   endif()
 

+ 6 - 3
ecl/hql/hqlgram.y

@@ -1042,7 +1042,10 @@ embedBody
                         {
                             OwnedHqlExpr language = $1.getExpr();
                             OwnedHqlExpr embedText = $2.getExpr();
-                            $$.setExpr(parser->processEmbedBody($2, embedText, language, NULL), $1);
+                            if (language->getOperator()==no_comma)
+                                $$.setExpr(parser->processEmbedBody($2, embedText, language->queryChild(0), language->queryChild(1)), $1);
+                            else
+                                $$.setExpr(parser->processEmbedBody($2, embedText, language, NULL), $1);
                         }
     | EMBED '(' abstractModule ',' expression ')'
                         {
@@ -1063,10 +1066,10 @@ embedBody
     ;
 
 embedPrefix
-    : EMBED '(' abstractModule ')'
+    : EMBED '(' abstractModule attribs ')'
                         {
                             parser->getLexer()->enterEmbeddedMode();
-                            $$.inherit($3);
+                            $$.setExpr(createComma($3.getExpr(), $4.getExpr()));
                         }
     ;
 

+ 2 - 1
plugins/CMakeLists.txt

@@ -22,7 +22,8 @@ add_subdirectory (stringlib)
 add_subdirectory (unicodelib)
 add_subdirectory (workunitservices)
 add_subdirectory (proxies)
+add_subdirectory (sqlite3)
 add_subdirectory (v8embed)
 add_subdirectory (pyembed)
 add_subdirectory (javaembed)
-add_subdirectory (Rembed)
+add_subdirectory (Rembed)

+ 64 - 0
plugins/sqlite3/CMakeLists.txt

@@ -0,0 +1,64 @@
+################################################################################
+#    HPCC SYSTEMS software Copyright (C) 2013 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: sqlite3embed
+
+#####################################################
+# Description:
+# ------------
+#    Cmake Input File for sqlite3embed
+#####################################################
+
+project( sqlite3embed )
+
+if (USE_SQLITE3)
+  ADD_PLUGIN(sqlite3embed PACKAGES sqlite3 OPTION MAKE_SQLITE3)
+  if ( 1 ) # MAKE_SQLITE3 )
+    set ( SRCS
+          sqlite3.cpp
+        )
+
+    include_directories (
+         ./../../system/include
+         ./../../rtl/eclrtl
+         ./../../rtl/include
+         ./../../rtl/nbcd
+         ./../../common/deftype
+         ./../../system/jlib
+       )
+
+    ADD_DEFINITIONS( -D_USRDLL -DSQLITE3_EXPORTS )
+
+    HPCC_ADD_LIBRARY( sqlite3embed SHARED ${SRCS} )
+    if (${CMAKE_VERSION} VERSION_LESS "2.8.9")
+      message("WARNING: Cannot set NO_SONAME. shlibdeps will give warnings when package is installed")
+    else()
+      set_target_properties( sqlite3embed PROPERTIES NO_SONAME 1 )
+    endif()
+
+    install ( TARGETS sqlite3embed DESTINATION plugins )
+    target_link_libraries ( sqlite3embed sqlite3 )
+
+    target_link_libraries ( sqlite3embed
+        eclrtl
+        jlib
+        )
+  endif()
+endif()
+
+# Even if not making the SqLite3 plugin, we want to install the header
+install ( FILES ${CMAKE_CURRENT_SOURCE_DIR}/sqlite3.ecllib DESTINATION plugins COMPONENT Runtime)

+ 527 - 0
plugins/sqlite3/sqlite3.cpp

@@ -0,0 +1,527 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2013 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.
+############################################################################## */
+
+#include "platform.h"
+#include "sqlite3.h"
+#include "jexcept.hpp"
+#include "jthread.hpp"
+#include "hqlplugins.hpp"
+#include "deftype.hpp"
+#include "eclhelper.hpp"
+#include "eclrtl.hpp"
+#include "eclrtl_imp.hpp"
+#include "rtlds_imp.hpp"
+#include "rtlfield_imp.hpp"
+#include "nbcd.hpp"
+
+#ifdef _WIN32
+#define EXPORT __declspec(dllexport)
+#else
+#define EXPORT
+#endif
+
+
+#define UNSUPPORTED throw MakeStringException(-1, "UNSUPPORTED feature at %s(%d)", __FILE__, __LINE__)
+
+static const char * compatibleVersions[] = {
+    "SqLite3 Embed Helper 1.0.0",
+    NULL };
+
+static const char *version = "SqLite3 Embed Helper 1.0.0";
+
+extern "C" EXPORT bool getECLPluginDefinition(ECLPluginDefinitionBlock *pb)
+{
+    if (pb->size == sizeof(ECLPluginDefinitionBlockEx))
+    {
+        ECLPluginDefinitionBlockEx * pbx = (ECLPluginDefinitionBlockEx *) pb;
+        pbx->compatibleVersions = compatibleVersions;
+    }
+    else if (pb->size != sizeof(ECLPluginDefinitionBlock))
+        return false;
+    pb->magicVersion = PLUGIN_VERSION;
+    pb->version = version;
+    pb->moduleName = "sqlite3";
+    pb->ECL = NULL;
+    pb->flags = PLUGIN_MULTIPLE_VERSIONS;
+    pb->description = "SqLite3 Embed Helper";
+    return true;
+}
+
+namespace sqlite3embed {
+
+// Use class OwnedPyObject for any objects that are not 'borrowed references'
+// so that the appropriate Py_DECREF call is made when the OwnedPyObject goes
+// out of scope, even if the function returns prematurely (such as via an exception).
+// In particular, checkPythonError is a lot easier to call safely if this is used.
+
+class OwnedStatement
+{
+    sqlite3_stmt *ptr;
+public:
+    inline OwnedStatement() : ptr(NULL)     {}
+    inline OwnedStatement(sqlite3_stmt *_ptr) : ptr(_ptr) {}
+    inline ~OwnedStatement()                { clear(); }
+    inline sqlite3_stmt * get() const           { return ptr; }
+    inline sqlite3_stmt * getClear()            { sqlite3_stmt *ret = ptr; ptr = NULL; return ret; }
+    inline sqlite3_stmt * operator -> () const { return ptr; }
+    inline operator sqlite3_stmt *() const    { return ptr; }
+    inline void clear()                         { sqlite3_finalize(ptr); ptr = NULL; }
+    inline void setown(sqlite3_stmt *_ptr)      { clear(); ptr = _ptr; }
+    inline sqlite3_stmt **ref()                  { return &ptr; }
+};
+
+// Conversions from SqLite3 values to ECL data
+
+static void checkSqliteError(int rc)
+{
+    if (rc != SQLITE_OK)
+        rtlFail(rc, "SqLite3 error");
+}
+
+static void typeError(const char *expected, const RtlFieldInfo *field) __attribute__((noreturn));
+
+static void typeError(const char *expected, const RtlFieldInfo *field)
+{
+    VStringBuffer msg("sqlite3: type mismatch - %s expected", expected);
+    if (field)
+        msg.appendf(" for field %s", field->name->str());
+    rtlFail(0, msg.str());
+}
+
+static bool getBooleanResult(const RtlFieldInfo *field, sqlite3_value *val)
+{
+    assertex(val);
+    if (sqlite3_value_type(val) != SQLITE_INTEGER)
+        typeError("boolean", field);
+    return sqlite3_value_int64(val) != 0;
+}
+
+static void getDataResult(const RtlFieldInfo *field, sqlite3_value *val, size32_t &chars, void * &result)
+{
+    assertex(val);
+    if (sqlite3_value_type(val) != SQLITE_BLOB)
+        typeError("blob", field);
+    const void *blob = sqlite3_value_blob(val);
+    int bytes = sqlite3_value_bytes(val);
+    rtlStrToDataX(chars, result, bytes, blob);
+}
+
+static double getRealResult(const RtlFieldInfo *field, sqlite3_value *val)
+{
+    assertex(val);
+    if (sqlite3_value_type(val) != SQLITE_FLOAT)
+        typeError("real", field);
+    return sqlite3_value_double(val);
+}
+
+static __int64 getSignedResult(const RtlFieldInfo *field, sqlite3_value *val)
+{
+    assertex(val);
+    if (sqlite3_value_type(val) != SQLITE_INTEGER)
+        typeError("integer", field);
+    return sqlite3_value_int64(val);
+}
+
+static unsigned __int64 getUnsignedResult(const RtlFieldInfo *field, sqlite3_value *val)
+{
+    assertex(val);
+    if (sqlite3_value_type(val) != SQLITE_INTEGER)
+        typeError("integer", field);
+    return (unsigned __int64) sqlite3_value_int64(val);
+}
+
+static void getStringResult(const RtlFieldInfo *field, sqlite3_value *val, size32_t &chars, char * &result)
+{
+    assertex(val);
+    if (sqlite3_value_type(val) != SQLITE_TEXT)
+        typeError("string", field);
+    const char *text = (const char *) sqlite3_value_text(val);
+    int bytes = sqlite3_value_bytes(val);
+    unsigned numchars = rtlUtf8Length(bytes, text);
+    rtlUtf8ToStrX(chars, result, numchars, text);
+}
+
+static void getUTF8Result(const RtlFieldInfo *field, sqlite3_value *val, size32_t &chars, char * &result)
+{
+    assertex(val);
+    if (sqlite3_value_type(val) != SQLITE_TEXT)
+        typeError("string", field);
+    const char *text = (const char *) sqlite3_value_text(val);
+    int bytes = sqlite3_value_bytes(val);
+    unsigned numchars = rtlUtf8Length(bytes, text);
+    rtlUtf8ToUtf8X(chars, result, numchars, text);
+}
+
+static void getUnicodeResult(const RtlFieldInfo *field, sqlite3_value *val, size32_t &chars, UChar * &result)
+{
+    assertex(val);
+    if (sqlite3_value_type(val) != SQLITE_TEXT)
+        typeError("string", field);
+    const UChar *text = (const UChar *) sqlite3_value_text16(val);
+    int bytes = sqlite3_value_bytes(val);
+    unsigned numchars = bytes / sizeof(UChar);
+    rtlUnicodeToUnicodeX(chars, result, numchars, text);
+}
+
+// A SqLite3RowBuilder object is used to construct an ECL row from a sqlite result
+
+class SqLite3RowBuilder : public CInterfaceOf<IFieldSource>
+{
+public:
+    SqLite3RowBuilder(sqlite3_stmt *_stmt)
+    : stmt(_stmt), val(NULL), colIdx(0)
+    {
+    }
+    virtual bool getBooleanResult(const RtlFieldInfo *field)
+    {
+        nextField(field);
+        return sqlite3embed::getBooleanResult(field, val);
+    }
+    virtual void getDataResult(const RtlFieldInfo *field, size32_t &len, void * &result)
+    {
+        nextField(field);
+        sqlite3embed::getDataResult(field, val, len, result);
+    }
+    virtual double getRealResult(const RtlFieldInfo *field)
+    {
+        nextField(field);
+        return sqlite3embed::getRealResult(field, val);
+    }
+    virtual __int64 getSignedResult(const RtlFieldInfo *field)
+    {
+        nextField(field);
+        return sqlite3embed::getSignedResult(field, val);
+    }
+    virtual unsigned __int64 getUnsignedResult(const RtlFieldInfo *field)
+    {
+        nextField(field);
+        return sqlite3embed::getUnsignedResult(field, val);
+    }
+    virtual void getStringResult(const RtlFieldInfo *field, size32_t &chars, char * &result)
+    {
+        nextField(field);
+        sqlite3embed::getStringResult(field, val, chars, result);
+    }
+    virtual void getUTF8Result(const RtlFieldInfo *field, size32_t &chars, char * &result)
+    {
+        nextField(field);
+        sqlite3embed::getUTF8Result(field, val, chars, result);
+    }
+    virtual void getUnicodeResult(const RtlFieldInfo *field, size32_t &chars, UChar * &result)
+    {
+        nextField(field);
+        sqlite3embed::getUnicodeResult(field, val, chars, result);
+    }
+    virtual void getDecimalResult(const RtlFieldInfo *field, Decimal &value)
+    {
+        nextField(field);
+        double ret = sqlite3embed::getRealResult(field, val);
+        value.setReal(ret);
+    }
+
+    virtual void processBeginSet(const RtlFieldInfo * field, bool &isAll)
+    {
+        UNSUPPORTED;
+    }
+    virtual bool processNextSet(const RtlFieldInfo * field)
+    {
+        throwUnexpected();
+    }
+    virtual void processBeginDataset(const RtlFieldInfo * field)
+    {
+        UNSUPPORTED;
+    }
+    virtual void processBeginRow(const RtlFieldInfo * field)
+    {
+    }
+    virtual bool processNextRow(const RtlFieldInfo * field)
+    {
+        UNSUPPORTED;
+    }
+    virtual void processEndSet(const RtlFieldInfo * field)
+    {
+        throwUnexpected();
+    }
+    virtual void processEndDataset(const RtlFieldInfo * field)
+    {
+        throwUnexpected();
+    }
+    virtual void processEndRow(const RtlFieldInfo * field)
+    {
+    }
+protected:
+    void nextField(const RtlFieldInfo * field)
+    {
+        val = sqlite3_column_value(stmt, colIdx++);
+    }
+    sqlite3_value *val;
+    sqlite3_stmt *stmt;
+    unsigned colIdx;
+};
+
+// A SqLite3 function that returns a dataset will return a SqLite3RowStream object that can be
+// interrogated to return each row of the result in turn
+
+class SqLite3RowStream : public CInterfaceOf<IRowStream>
+{
+public:
+    SqLite3RowStream(sqlite3_stmt *_stmt, IEngineRowAllocator *_resultAllocator)
+    : stmt(_stmt), resultAllocator(_resultAllocator)
+    {
+        assertex(stmt);
+    }
+    virtual const void *nextRow()
+    {
+        if (!stmt)
+            return NULL;
+        int rc = sqlite3_step(stmt);
+        if (rc != SQLITE_ROW)
+        {
+            stmt.clear();
+            return NULL;
+        }
+        RtlDynamicRowBuilder rowBuilder(resultAllocator);
+        SqLite3RowBuilder sqliteRowBuilder(stmt);
+        const RtlTypeInfo *typeInfo = resultAllocator->queryOutputMeta()->queryTypeInfo();
+        assertex(typeInfo);
+        RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
+        size32_t len = typeInfo->build(rowBuilder, 0, &dummyField, sqliteRowBuilder);
+        return rowBuilder.finalizeRowClear(len);
+    }
+    virtual void stop()
+    {
+        resultAllocator.clear();
+        stmt.clear();
+    }
+
+protected:
+    Linked<IEngineRowAllocator> resultAllocator;
+    OwnedStatement stmt;
+};
+
+// Each call to a SqLite3 function will use a new SqLite3EmbedFunctionContext object
+
+class SqLite3EmbedFunctionContext : public CInterfaceOf<IEmbedFunctionContext>
+{
+public:
+    SqLite3EmbedFunctionContext(const char *options) : db(NULL)
+    {
+        const char *dbname = NULL;
+        StringArray opts;
+        opts.appendList(options, ",");
+        ForEachItemIn(idx, opts)
+        {
+            const char *opt = opts.item(idx);
+            const char *val = strchr(opt, '=');
+            if (val)
+            {
+                StringBuffer optName(val-opt, opt);
+                val++;
+                if (stricmp(optName, "file")==0)
+                    dbname = val;   // Note that lifetime of val is adequate for this to be safe
+            }
+        }
+        if (!dbname)
+            rtlFail(0, "sqlite3: no filename specified");
+        int rc = sqlite3_open(dbname, &db);
+        checkSqliteError(rc);
+    }
+    ~SqLite3EmbedFunctionContext()
+    {
+        if (db)
+            sqlite3_close(db);
+    }
+
+    virtual bool getBooleanResult()
+    {
+        return sqlite3embed::getBooleanResult(NULL, getScalarResult());
+    }
+    virtual void getDataResult(size32_t &len, void * &result)
+    {
+        sqlite3embed::getDataResult(NULL, getScalarResult(), len, result);
+    }
+    virtual double getRealResult()
+    {
+        return sqlite3embed::getRealResult(NULL, getScalarResult());
+    }
+    virtual __int64 getSignedResult()
+    {
+        return sqlite3embed::getSignedResult(NULL, getScalarResult());
+    }
+    virtual unsigned __int64 getUnsignedResult()
+    {
+        return sqlite3embed::getUnsignedResult(NULL, getScalarResult());
+    }
+    virtual void getStringResult(size32_t &chars, char * &result)
+    {
+        sqlite3embed::getStringResult(NULL, getScalarResult(), chars, result);
+    }
+    virtual void getUTF8Result(size32_t &chars, char * &result)
+    {
+        sqlite3embed::getUTF8Result(NULL, getScalarResult(), chars, result);
+    }
+    virtual void getUnicodeResult(size32_t &chars, UChar * &result)
+    {
+        sqlite3embed::getUnicodeResult(NULL, getScalarResult(), chars, result);
+    }
+    virtual void getDecimalResult(Decimal &value)
+    {
+        double ret = sqlite3embed::getRealResult(NULL, getScalarResult());
+        value.setReal(ret);
+    }
+    virtual void getSetResult(bool & __isAllResult, size32_t & __resultBytes, void * & __result, int elemType, size32_t elemSize)
+    {
+        UNSUPPORTED;
+    }
+    virtual IRowStream *getDatasetResult(IEngineRowAllocator * _resultAllocator)
+    {
+        return new SqLite3RowStream(stmt.getClear(), _resultAllocator);
+    }
+    virtual byte * getRowResult(IEngineRowAllocator * _resultAllocator)
+    {
+        RtlDynamicRowBuilder rowBuilder(_resultAllocator);
+        SqLite3RowBuilder sqliteRowBuilder(stmt);
+        const RtlTypeInfo *typeInfo = _resultAllocator->queryOutputMeta()->queryTypeInfo();
+        assertex(typeInfo);
+        RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
+        size32_t len = typeInfo->build(rowBuilder, 0, &dummyField, sqliteRowBuilder);
+        return (byte *) rowBuilder.finalizeRowClear(len);
+    }
+    virtual size32_t getTransformResult(ARowBuilder & rowBuilder)
+    {
+        SqLite3RowBuilder sqliteRowBuilder(stmt);
+        const RtlTypeInfo *typeInfo = rowBuilder.queryAllocator()->queryOutputMeta()->queryTypeInfo();
+        assertex(typeInfo);
+        RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
+        return typeInfo->build(rowBuilder, 0, &dummyField, sqliteRowBuilder);
+    }
+    virtual void bindRowParam(const char *name, IOutputMetaData & metaVal, byte *val)
+    {
+        UNSUPPORTED;  // Not sure it ever makes sense
+    }
+    virtual void bindDatasetParam(const char *name, IOutputMetaData & metaVal, IRowStream * val)
+    {
+        UNSUPPORTED;   // Would need some thought, if it ever made sense
+    }
+
+    virtual void bindBooleanParam(const char *name, bool val)
+    {
+        checkSqliteError(sqlite3_bind_int64(stmt, findParameter(name), val ? 1 : 0));
+    }
+    virtual void bindDataParam(const char *name, size32_t len, const void *val)
+    {
+        checkSqliteError(sqlite3_bind_blob(stmt, findParameter(name), val, len, SQLITE_TRANSIENT));
+    }
+    virtual void bindRealParam(const char *name, double val)
+    {
+        checkSqliteError(sqlite3_bind_double(stmt, findParameter(name), val));
+    }
+    virtual void bindSignedParam(const char *name, __int64 val)
+    {
+        checkSqliteError(sqlite3_bind_int64(stmt, findParameter(name), val));
+    }
+    virtual void bindUnsignedParam(const char *name, unsigned __int64 val)
+    {
+        checkSqliteError(sqlite3_bind_int64(stmt, findParameter(name), val));
+    }
+    virtual void bindStringParam(const char *name, size32_t len, const char *val)
+    {
+        size32_t utf8chars;
+        char *utf8;
+        rtlStrToUtf8X(utf8chars, utf8, len, val);
+        checkSqliteError(sqlite3_bind_text(stmt, findParameter(name), utf8, rtlUtf8Size(len, utf8), rtlFree));
+    }
+    virtual void bindVStringParam(const char *name, const char *val)
+    {
+        bindStringParam(name, strlen(val), val);
+    }
+    virtual void bindUTF8Param(const char *name, size32_t chars, const char *val)
+    {
+        size32_t sizeBytes = rtlUtf8Size(chars, val);
+        checkSqliteError(sqlite3_bind_text(stmt, findParameter(name), val, sizeBytes, SQLITE_TRANSIENT)); // NOTE - requires size in bytes not chars
+    }
+    virtual void bindUnicodeParam(const char *name, size32_t chars, const UChar *val)
+    {
+        size32_t sizeBytes = chars * sizeof(UChar);
+        checkSqliteError(sqlite3_bind_text16(stmt, findParameter(name), val, sizeBytes, SQLITE_TRANSIENT)); // NOTE - requires size in bytes not chars
+    }
+    virtual void bindSetParam(const char *name, int elemType, size32_t elemSize, bool isAll, size32_t totalBytes, void *setData)
+    {
+        UNSUPPORTED;
+    }
+
+    virtual void importFunction(size32_t lenChars, const char *text)
+    {
+        UNSUPPORTED;
+    }
+    virtual void compileEmbeddedScript(size32_t len, const char *script)
+    {
+        int rc = sqlite3_prepare_v2(db, script, len, stmt.ref(), NULL);
+        checkSqliteError(rc);
+    }
+    virtual void callFunction()
+    {
+        assertex(stmt);
+        int rc = sqlite3_reset(stmt);
+        checkSqliteError(rc);
+    }
+protected:
+    sqlite3_value *getScalarResult()
+    {
+        int rc = sqlite3_step(stmt);
+        if (rc != SQLITE_ROW || sqlite3_column_count(stmt) != 1)
+            typeError("scalar", NULL);
+        return sqlite3_column_value(stmt, 0);
+    }
+    inline int findParameter(const char *name)
+    {
+        VStringBuffer pname(":%s", name);
+        int idx = sqlite3_bind_parameter_index(stmt, pname);
+        if (!idx)
+        {
+            VStringBuffer msg("sqlite3: bound parameter %s not found", name);
+            rtlFail(0, msg.str());
+        }
+        return idx;
+    }
+    OwnedStatement stmt;
+    sqlite3 *db;
+};
+
+class SqLite3EmbedContext : public CInterfaceOf<IEmbedContext>
+{
+public:
+    virtual IEmbedFunctionContext *createFunctionContext(bool isImport, const char *options)
+    {
+        if (isImport)
+            UNSUPPORTED;
+        else
+            return new SqLite3EmbedFunctionContext(options);
+    }
+};
+
+extern IEmbedContext* getEmbedContext()
+{
+    return new SqLite3EmbedContext();
+}
+
+extern bool syntaxCheck(const char *script)
+{
+    return true; // MORE
+}
+
+} // namespace

+ 25 - 0
plugins/sqlite3/sqlite3.ecllib

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

+ 38 - 0
testing/ecl/sqlite.ecl

@@ -0,0 +1,38 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2013 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.
+############################################################################## */
+
+IMPORT SqLite3;
+
+childrec := RECORD
+   string name => unsigned value;
+END;
+
+dataset(childrec) testSqLite(unsigned lim) := EMBED(SqLite3 : file('test.db'))
+  SELECT * from tbl1 where two = :lim;
+ENDEMBED;
+
+dataset(childrec) testSqLite2(string v) := EMBED(SqLite3 : file('test.db'))
+  SELECT * from tbl1 where one = :v;
+ENDEMBED;
+
+unsigned testSqLite3(string v) := EMBED(SqLite3 : file('test.db'))
+  SELECT count(*) from tbl1 where one = :v;
+ENDEMBED;
+
+output(testSqLite(20));
+output(testSqLite2('hello!'));
+output(testSqLite3('hello!'));