lua-decision: initial sources
authorJanos Kovacs <jankovac503@gmail.com>
Fri, 26 Oct 2012 04:11:41 +0000 (07:11 +0300)
committerKrisztian Litkey <krisztian.litkey@intel.com>
Tue, 6 Nov 2012 09:19:12 +0000 (11:19 +0200)
src/core/lua-decision/mdb.c [new file with mode: 0644]
src/core/lua-decision/mdb.h [new file with mode: 0644]
src/core/lua-decision/tests/decision-test.c [new file with mode: 0644]
src/core/lua-decision/tests/decision-test.lua [new file with mode: 0644]

diff --git a/src/core/lua-decision/mdb.c b/src/core/lua-decision/mdb.c
new file mode 100644 (file)
index 0000000..c9dc9d9
--- /dev/null
@@ -0,0 +1,873 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Intel Corporation nor the names of its contributors
+ *     may be used to endorse or promote products derived from this software
+ *     without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <murphy/common.h>
+#include <murphy-db/mql.h>
+
+#include <murphy/core/lua-decision/mdb.h>
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-utils/strarray.h>
+
+
+#define TABLE_CLASS   MRP_LUA_CLASS(mdb, table)
+#define SELECT_CLASS  MRP_LUA_CLASS(mdb, select)
+
+typedef enum   field_e        field_t;
+typedef struct select_s       select_t;
+typedef struct const_def_s    const_def_t;
+
+enum field_e {
+    NAME = 1,
+    INDEX,
+    COLUMNS,
+    TABLE,
+    CONDITION,
+    STATEMENT
+};
+
+struct mrp_lua_mdb_table_s {
+    bool                builtin;
+    mqi_handle_t        handle;
+    const char         *name;
+    mrp_lua_strarray_t *index;
+    size_t              ncolumn;
+    mqi_column_def_t   *columns;
+    size_t              nrow;
+};
+
+
+struct select_s {
+    const char                *name;
+    struct {
+        mqi_handle_t  handle;
+        const char   *name;
+    }                          table;
+    mrp_lua_strarray_t        *columns;
+    const char                *condition;
+    struct {
+        const char      *string;
+        mql_statement_t *precomp;
+    }                          statement;
+};
+
+struct const_def_s {
+    const char *name;
+    mqi_data_type_t value;
+};
+
+static int  table_create_from_lua(lua_State *);
+static int  table_getfield(lua_State *);
+static int  table_setfield(lua_State *);
+static int  table_tostring(lua_State *);
+static void table_destroy_from_lua(void *);
+static mrp_lua_mdb_table_t *table_check(lua_State *, int);
+
+static void table_row_class_create(lua_State *);
+static int  table_row_create(lua_State *, int, int);
+static int  table_row_getfield(lua_State *);
+static int  table_row_setfield(lua_State *);
+static mrp_lua_mdb_table_t *table_row_check(lua_State *, int, int *);
+
+static int  select_create_from_lua(lua_State *);
+static int  select_getfield(lua_State *);
+static int  select_setfield(lua_State *);
+static void select_destroy_from_lua(void *);
+static select_t *select_check(lua_State *, int);
+static int  select_update_from_lua(lua_State *);
+
+static void select_row_class_create(lua_State *);
+static int  select_row_create(lua_State *, int, int);
+static int  select_row_getfield(lua_State *);
+static int  select_row_setfield(lua_State *);
+static select_t *select_row_check(lua_State *, int, int *);
+
+static bool define_constants(lua_State *);
+
+static field_t field_check(lua_State *, int, const char **);
+static field_t field_name_to_type(const char *, size_t);
+
+static mqi_column_def_t *check_coldefs(lua_State *, int, size_t *);
+static int push_coldefs(lua_State *, mqi_column_def_t *, size_t);
+static void free_coldefs(mqi_column_def_t *);
+
+static int   row_create(lua_State *, int, int, const char *);
+static void *row_check(lua_State *, int, mrp_lua_classdef_t *, int *);
+
+static void adjust_table_size(lua_State *, int, size_t, size_t,
+                              mrp_lua_classdef_t *);
+
+
+MRP_LUA_METHOD_LIST_TABLE (
+    table_methods,           /* methodlist name */
+    MRP_LUA_METHOD_CONSTRUCTOR  (table_create_from_lua)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+    table_row_methods,       /* methodlist name */
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+    select_methods,          /* methodlist name */
+    MRP_LUA_METHOD_CONSTRUCTOR  (select_create_from_lua)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+    table_overrides,         /* methodlist name */
+    MRP_LUA_OVERRIDE_CALL       (table_create_from_lua)
+    MRP_LUA_OVERRIDE_GETFIELD   (table_getfield)
+    MRP_LUA_OVERRIDE_SETFIELD   (table_setfield)
+    MRP_LUA_OVERRIDE_STRINGIFY  (table_tostring)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+    table_row_overrides,     /* methodlist name */
+    MRP_LUA_OVERRIDE_GETFIELD   (table_row_getfield)
+    MRP_LUA_OVERRIDE_SETFIELD   (table_row_setfield)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+    select_overrides,        /* methodlist name */
+    MRP_LUA_OVERRIDE_CALL       (select_create_from_lua)
+    MRP_LUA_OVERRIDE_GETFIELD   (select_getfield)
+    MRP_LUA_OVERRIDE_SETFIELD   (select_setfield)
+    MRP_LUA_METHOD     (update,  select_update_from_lua)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+    select_row_overrides,    /* methodlist name */
+    MRP_LUA_OVERRIDE_GETFIELD   (select_row_getfield)
+    MRP_LUA_OVERRIDE_SETFIELD   (select_row_setfield)
+);
+
+MRP_LUA_CLASS_DEF (
+   mdb,                      /* class name */
+   table,                    /* constructor name */
+   mrp_lua_mdb_table_t,      /* userdata type */
+   table_destroy_from_lua,   /* userdata destructor */
+   table_methods,            /* class methods */
+   table_overrides           /* override methods */
+);
+
+MRP_LUA_CLASS_DEF (
+   mdb,                      /* class name */
+   select,                   /* constructor name */
+   select_t,                 /* userdata type */
+   select_destroy_from_lua,  /* userdata destructor */
+   select_methods,           /* class methods */
+   select_overrides          /* override methods */
+);
+
+
+void mrp_lua_create_mdb_class(lua_State *L)
+{
+    mrp_lua_create_object_class(L, TABLE_CLASS);
+    mrp_lua_create_object_class(L, SELECT_CLASS);
+
+    table_row_class_create(L);
+    select_row_class_create(L);
+
+    define_constants(L);
+
+    luaL_findtable(L, LUA_GLOBALSINDEX, "builtin.table", 20);
+}
+
+mrp_lua_mdb_table_t *mrp_lua_create_builtin_table(lua_State    *L,
+                                                  mqi_handle_t  handle)
+{
+    mrp_lua_mdb_table_t *tbl = NULL;
+
+
+    return NULL;
+}
+
+static int table_create_from_lua(lua_State *L)
+{
+    mrp_lua_mdb_table_t *tbl;
+    size_t fldnamlen;
+    const char *fldnam;
+
+    tbl = (mrp_lua_mdb_table_t *)mrp_lua_create_object(L, TABLE_CLASS, NULL);
+
+    tbl->builtin = false;
+
+    MRP_LUA_FOREACH_FIELD(L, 2, fldnam, fldnamlen) {
+
+        switch (field_name_to_type(fldnam, fldnamlen)) {
+
+        case NAME:
+            tbl->name = mrp_strdup(luaL_checkstring(L, -1));
+            break;
+
+        case INDEX:
+            tbl->index = mrp_lua_check_strarray(L, -1);
+            break;
+
+        case COLUMNS:
+            tbl->columns = check_coldefs(L, -1, &tbl->ncolumn);
+            break;
+
+        default:
+            luaL_error(L, "unexpected field '%s'", fldnam);
+            break;
+        }
+
+    } /* MRP_LUA_FOREACH_FIELD */
+
+    if (!tbl->name)
+        luaL_error(L, "mandatory 'name' field is unspecified");
+    if (!tbl->columns || !tbl->ncolumn)
+        luaL_error(L, "mandatory 'column' field is unspecified or invalid");
+
+    mrp_lua_set_object_name(L, TABLE_CLASS, tbl->name);
+
+    return 1;
+}
+
+static int table_getfield(lua_State *L)
+{
+    mrp_lua_mdb_table_t *tbl = table_check(L, 1);
+    field_t fld;
+
+    if (lua_type(L, 2) == LUA_TNUMBER) {
+        lua_rawget(L, 1);
+
+        printf("*** reading row in table '%s'\n", tbl->name);
+    }
+    else {
+        fld = field_check(L, 2, NULL);
+        lua_pop(L, 1);
+
+        if (!tbl)
+            lua_pushnil(L);
+        else {
+            switch (fld) {
+            case NAME:     lua_pushstring(L, tbl->name);                 break;
+            case INDEX:    mrp_lua_push_strarray(L, tbl->index);         break;
+            case COLUMNS:  push_coldefs(L, tbl->columns, tbl->ncolumn);  break;
+            default:       lua_pushnil(L);                               break;
+            }
+        }
+    }
+
+    return 1;
+}
+
+static int table_setfield(lua_State *L)
+{
+    mrp_lua_mdb_table_t *tbl = table_check(L, 1);
+    size_t rowidx;
+
+    if (lua_type(L, 2) != LUA_TNUMBER)
+        luaL_error(L, "'%s' is read-only", tbl->name);
+    else {
+        rowidx = lua_tointeger(L, 2);
+
+        if (rowidx-- < 1)
+            luaL_error(L, "invalid row index %u", rowidx);
+        if (rowidx > tbl->nrow)
+            luaL_error(L, "row index '%u' is out of sequence", rowidx);
+
+        if (rowidx == tbl->nrow) {
+            adjust_table_size(L, 1, tbl->nrow, tbl->nrow+1, TABLE_CLASS);
+            tbl->nrow++;
+        }
+        else {
+            lua_pushvalue(L, 2);
+            lua_rawget(L, 1);
+            luaL_checktype(L, -1, LUA_TTABLE);
+        }
+
+        printf("*** setting row %d in table '%s'\n", rowidx+1, tbl->name);
+
+    }
+
+    return 0;
+}
+
+static int table_tostring(lua_State *L)
+{
+    mrp_lua_mdb_table_t *tbl = table_check(L, 1);
+
+    if (tbl && tbl->name)
+        lua_pushstring(L, tbl->name);
+    else
+        lua_pushstring(L, "<error>");
+
+    return 1;
+}
+
+static void table_destroy_from_lua(void *data)
+{
+    mrp_lua_mdb_table_t *tbl = (mrp_lua_mdb_table_t *)data;
+
+    if (tbl) {
+        mrp_free((void *)tbl->name);
+        mrp_lua_free_strarray(tbl->index);
+        free_coldefs(tbl->columns);
+    }
+}
+
+static mrp_lua_mdb_table_t *table_check(lua_State *L, int idx)
+{
+    return (mrp_lua_mdb_table_t *)mrp_lua_check_object(L, TABLE_CLASS, idx);
+}
+
+static void table_row_class_create(lua_State *L)
+{
+    /* create a metatable for row's */
+    luaL_newmetatable(L, MRP_LUA_CLASSID_ROOT "table_row");
+    lua_pushliteral(L, "__index");
+    lua_pushvalue(L, -2);
+    lua_settable(L, -3);        /* metatable.__index = metatable */
+    luaL_openlib(L, NULL, table_row_overrides, 0);
+}
+
+static int table_row_create(lua_State *L, int tbl, int rowidx)
+{
+    return row_create(L, tbl, rowidx, MRP_LUA_CLASSID_ROOT "table_row");
+}
+
+static int table_row_getfield(lua_State *L)
+{
+    mrp_lua_mdb_table_t *tbl;
+    int rowidx;
+
+    tbl = table_row_check(L, 1, &rowidx);
+
+    printf("*** reading field in row %d of '%s' table\n", rowidx+1, tbl->name);
+
+    lua_pushnil(L);
+
+    return 1;
+}
+
+static int table_row_setfield(lua_State *L)
+{
+    mrp_lua_mdb_table_t *tbl;
+    int rowidx;
+
+    tbl = table_row_check(L, 1, &rowidx);
+
+    printf("*** writing field in row %d of '%s' table\n", rowidx+1, tbl->name);
+
+    return 0;
+}
+
+static mrp_lua_mdb_table_t *table_row_check(lua_State *L,
+                                            int  idx,
+                                            int *ret_rowidx)
+{
+    return (mrp_lua_mdb_table_t *)row_check(L, idx, TABLE_CLASS, ret_rowidx);
+}
+
+
+static int select_create_from_lua(lua_State *L)
+{
+    select_t *sel;
+    size_t fldnamlen;
+    const char *fldnam;
+    const char *condition;
+    char  cols[1024];
+    char  qry[2048];
+
+    sel = (select_t *)mrp_lua_create_object(L, SELECT_CLASS, NULL);
+
+    MRP_LUA_FOREACH_FIELD(L, 2, fldnam, fldnamlen) {
+
+        switch (field_name_to_type(fldnam, fldnamlen)) {
+
+        case NAME:
+            sel->name = mrp_strdup(luaL_checkstring(L, -1));
+            break;
+
+        case TABLE:
+            sel->table.name = mrp_strdup(luaL_checkstring(L, -1));
+            break;
+
+        case COLUMNS:
+            sel->columns = mrp_lua_check_strarray(L, -1);
+            break;
+
+        case CONDITION:
+            condition = luaL_checkstring(L, -1);
+
+            if (strchr(condition, '%'))
+                luaL_error(L, "non-static condition '%s'", condition);
+            else
+                sel->condition = mrp_strdup(condition);
+            break;
+
+        default:
+            luaL_error(L, "unexpected field '%s'", fldnam);
+            break;
+        }
+    } /* MRP_LUA_FOREACH_FIELD */
+
+    if (!sel->name)
+        luaL_error(L, "mandatory 'name' field is missing");
+    if (!sel->table.name)
+        luaL_error(L, "mandatory 'table' field is missing");
+    if (!sel->columns || !sel->columns->nstring)
+        luaL_error(L, "mandatory 'column' field is missing or invalid");
+
+    mrp_lua_print_strarray(sel->columns, cols, sizeof(cols));
+
+    if (!sel->condition) {
+        snprintf(qry, sizeof(qry), "SELECT %s FROM %s",
+                 cols, sel->table.name);
+    }
+    else {
+        snprintf(qry, sizeof(qry), "SELECT %s FROM %s WHERE %s",
+                 cols, sel->table.name, sel->condition);
+    }
+
+    sel->statement.string = mrp_strdup(qry);
+
+    mrp_lua_set_object_name(L, SELECT_CLASS, sel->name);
+
+    return 1;
+}
+
+static int select_getfield(lua_State *L)
+{
+    select_t *sel = select_check(L, 1);
+    field_t fld;
+    const char *fldnam;
+
+    if (!sel)
+        lua_pushnil(L);
+    else {
+        if (lua_type(L, 2) == LUA_TNUMBER) {
+            lua_rawget(L, 1);
+
+            printf("*** reading row in selection '%s'\n", sel->name);
+        }
+        else {
+            fld = field_check(L, 2, &fldnam);
+            lua_pop(L, 1);
+
+            if (fld) {
+                switch (fld) {
+                case NAME:      lua_pushstring(L, sel->name);            break;
+                case TABLE:     lua_pushstring(L, sel->table.name);      break;
+                case COLUMNS:   mrp_lua_push_strarray(L, sel->columns);  break;
+                case CONDITION: lua_pushstring(L, sel->condition);       break;
+                case STATEMENT: lua_pushstring(L,sel->statement.string); break;
+                default:        lua_pushnil(L);                          break;
+                }
+            }
+            else {
+                if (!fldnam || !luaL_getmetafield(L, 1, fldnam))
+                    lua_pushnil(L);
+            }
+        }
+    }
+
+    return 1;
+}
+
+static int select_setfield(lua_State *L)
+{
+    select_t *sel = select_check(L, 1);
+
+    luaL_error(L, "'%s' is read-only", sel->name);
+
+    return 0;
+}
+
+static void select_destroy_from_lua(void *data)
+{
+    select_t *sel = (select_t *)data;
+
+    if (sel) {
+        mrp_lua_free_strarray(sel->columns);
+        mrp_free((void *)sel->name);
+        mrp_free((void *)sel->table.name);
+        mrp_free((void *)sel->condition);
+        mrp_free((void *)sel->statement.string);
+    }
+}
+
+static select_t *select_check(lua_State *L, int idx)
+{
+    return (select_t *)mrp_lua_check_object(L, SELECT_CLASS, idx);
+}
+
+static int select_update_from_lua(lua_State *L)
+{
+    select_t *sel = select_check(L, 1);
+
+    printf("*** update request for select '%s'\n", sel->name);
+
+    lua_pushinteger(L, 0);
+
+    return 1;
+}
+
+
+static void select_row_class_create(lua_State *L)
+{
+    /* create a metatable for row's */
+    luaL_newmetatable(L, MRP_LUA_CLASSID_ROOT "select_row");
+    lua_pushliteral(L, "__index");
+    lua_pushvalue(L, -2);
+    lua_settable(L, -3);        /* metatable.__index = metatable */
+    luaL_openlib(L, NULL, select_row_overrides, 0);
+}
+
+static int select_row_create(lua_State *L, int tbl, int rowidx)
+{
+    return row_create(L, tbl, rowidx, MRP_LUA_CLASSID_ROOT "select_row");
+}
+
+static int select_row_getfield(lua_State *L)
+{
+    select_t *sel;
+    int rowidx;
+
+    sel = select_row_check(L, 1, &rowidx);
+
+    printf("*** reading field in row %d of '%s' selection\n",
+           rowidx+1, sel->name);
+
+    lua_pushnil(L);
+
+    return 1;
+}
+
+static int select_row_setfield(lua_State *L)
+{
+    select_t *sel;
+    int rowidx;
+
+    sel = select_row_check(L, 1, &rowidx);
+
+    luaL_error(L, "attempt to write row %u of read-only selection '%s'",
+               rowidx+1, sel->name);
+
+    return 0;
+}
+
+static select_t *select_row_check(lua_State *L, int  idx, int *ret_rowidx)
+{
+    return (select_t *)row_check(L, idx, SELECT_CLASS, ret_rowidx);
+}
+
+static bool define_constants(lua_State *L)
+{
+    static const_def_t const_defs[] = {
+        { "string"  , mqi_string  },
+        { "integer" , mqi_integer },
+        { "floating", mqi_floating},
+        {    NULL   , mqi_unknown }
+    };
+
+    const_def_t *cd;
+    bool success = false;
+
+    lua_getglobal(L, "mdb");
+
+    if (lua_istable(L, -1)) {
+        for (cd = const_defs;   cd->name;   cd++) {
+            lua_pushinteger(L, cd->value);
+            lua_setfield(L, -2, cd->name);
+        }
+
+        lua_pop(L, 1);
+
+        success = true;
+    }
+
+    return success;
+}
+
+static field_t field_check(lua_State *L, int idx, const char **ret_fldnam)
+{
+    const char *fldnam;
+    size_t fldnamlen;
+    field_t fldtyp;
+
+    if (!(fldnam = lua_tolstring(L, idx, &fldnamlen)))
+        fldtyp = 0;
+    else
+        fldtyp = field_name_to_type(fldnam, fldnamlen);
+
+    if (ret_fldnam)
+        *ret_fldnam = fldnam;
+
+    return fldtyp;
+}
+
+static field_t field_name_to_type(const char *name, size_t len)
+{
+    switch (len) {
+
+    case 4:
+        if (!strcmp(name, "name"))
+            return NAME;
+        break;
+
+    case 5:
+        if (!strcmp(name, "index"))
+            return INDEX;
+        if (!strcmp(name, "table"))
+            return TABLE;
+        break;
+
+    case 7:
+        if (!strcmp(name, "columns"))
+            return COLUMNS;
+        break;
+
+    case 9:
+        if (!strcmp(name, "statement"))
+            return STATEMENT;
+        if (!strcmp(name, "condition"))
+            return CONDITION;
+        break;
+
+    default:
+        break;
+    }
+
+    return 0;
+}
+
+
+static mqi_column_def_t *check_coldefs(lua_State *L, int t, size_t *ret_len)
+{
+    size_t tlen, dlen;
+    size_t size;
+    mqi_column_def_t *coldefs, *cd;
+    size_t i,j;
+
+    t = (t < 0) ? lua_gettop(L) + t + 1 : t;
+
+    luaL_checktype(L, t, LUA_TTABLE);
+    tlen  = luaL_getn(L, t);
+    size = sizeof(mqi_column_def_t) * (tlen + 1);
+
+    if (!(coldefs = mrp_alloc(size)))
+        luaL_error(L, "can't allocate %d byte long memory", size);
+    else {
+        memset(coldefs, 0, size);
+
+        for (i = 0;  i < tlen;  i++) {
+            cd = coldefs + i;
+
+            lua_pushinteger(L, (int)(i+1));
+            lua_gettable(L, t);
+
+            if (!lua_istable(L, -1))
+                goto error;
+
+            luaL_checktype(L, -1, LUA_TTABLE);
+
+            dlen = luaL_getn(L, -1);
+
+            for (j = 0;  j < dlen;  j++) {
+                lua_pushnumber(L, (int)(j+1));
+                lua_gettable(L, -2);
+
+                switch (j) {
+                case 0:    cd->name   = mrp_strdup(lua_tostring(L, -1)); break;
+                case 1:    cd->type   = lua_tointeger(L, -1);            break;
+                case 2:    cd->length = lua_tointeger(L, -1);            break;
+                default:   cd->type   = mqi_error;                       break;
+                }
+
+                lua_pop(L, 1);
+            }
+
+            lua_pop(L, 1);
+
+            if ( cd->name == NULL        ||
+                (cd->type != mqi_string  &&
+                 cd->type != mqi_integer &&
+                 cd->type != mqi_floating ))
+                 goto error;
+        }
+
+        if (ret_len)
+            *ret_len = tlen;
+    }
+
+    return coldefs;
+
+ error:
+    free_coldefs(coldefs);
+    luaL_argerror(L, i+1, "malformed column definition");
+    if (ret_len)
+        *ret_len = 0;
+    return NULL;
+}
+
+static int push_coldefs(lua_State *L, mqi_column_def_t *coldefs, size_t hint)
+{
+    mqi_column_def_t *cd;
+    int i;
+
+    if (!coldefs)
+        lua_pushnil(L);
+    else {
+        lua_createtable(L, hint, 0);
+
+        for (cd = coldefs, i = 1;  cd->name;   cd++, i++) {
+            lua_pushinteger(L, i);
+
+            lua_createtable(L, cd->length ? 3 : 2, 0);
+
+            lua_pushinteger(L, 1);
+            lua_pushstring(L, cd->name);
+            lua_settable(L, -3);
+
+            lua_pushinteger(L, 2);
+            lua_pushinteger(L, cd->type);
+            lua_settable(L, -3);
+
+            if (cd->length) {
+                lua_pushinteger(L, 3);
+                lua_pushinteger(L, cd->length);
+                lua_settable(L, -3);
+            }
+
+            lua_settable(L, -3);
+        }
+    }
+
+    return 1;
+}
+
+static void free_coldefs(mqi_column_def_t *coldefs)
+{
+    mqi_column_def_t *cd;
+
+    if (coldefs) {
+        for (cd = coldefs;  cd->name;  cd++)
+            mrp_free((void *)cd->name);
+        mrp_free(coldefs);
+    }
+}
+
+static int row_create(lua_State *L, int tbl, int rowidx, const char *class_id)
+{
+    tbl = (tbl < 0) ? lua_gettop(L) + tbl + 1 : tbl;
+
+    luaL_checktype(L, tbl, LUA_TTABLE);
+
+    lua_createtable(L, 2, 0);
+
+    luaL_getmetatable(L, class_id);
+    lua_setmetatable(L, -2);
+
+    lua_pushinteger(L, 1);
+    lua_pushinteger(L, rowidx);
+    lua_rawset(L, -3);
+
+    lua_pushinteger(L, 2);
+    lua_pushvalue(L, tbl);
+    lua_rawset(L, -3);
+
+    return 1;
+}
+
+static void *row_check(lua_State *L,
+                       int  idx,
+                       mrp_lua_classdef_t *class_def,
+                       int *ret_rowidx)
+{
+    void *tbl;
+    int rowidx;
+
+    luaL_checktype(L, idx, LUA_TTABLE);
+
+    lua_pushinteger(L, 1);
+    lua_rawget(L, -2);
+    rowidx = luaL_checknumber(L, -1);
+    lua_pop(L, 1);
+
+    lua_pushinteger(L, 2);
+    lua_rawget(L, -2);
+    tbl = mrp_lua_check_object(L, class_def, -1);
+    lua_pop(L, 1);
+
+    if (ret_rowidx)
+        *ret_rowidx = rowidx;
+
+    return tbl;
+}
+
+static void adjust_table_size(lua_State *L,
+                              int tbl,
+                              size_t old_size,
+                              size_t new_size,
+                              mrp_lua_classdef_t *def)
+{
+    size_t rowidx;
+
+    tbl = (tbl < 0) ? lua_gettop(L) + tbl + 1 : tbl;
+
+    luaL_checktype(L, tbl, LUA_TTABLE);
+
+    if (old_size < new_size) {
+        for (rowidx = old_size;   rowidx < new_size;   rowidx++) {
+            lua_pushinteger(L, (int)(rowidx+1));
+            row_create(L, tbl, rowidx, def->class_id);
+            lua_rawset(L, tbl);
+        }
+        return;
+    }
+
+    if (old_size > new_size) {
+        for (rowidx = old_size - 1;  rowidx >= new_size;   rowidx--) {
+            lua_pushinteger(L, (int)(rowidx+1));
+            lua_pushnil(L);
+            lua_rawset(L, tbl);
+        }
+        return;
+    }
+}
+
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
diff --git a/src/core/lua-decision/mdb.h b/src/core/lua-decision/mdb.h
new file mode 100644 (file)
index 0000000..f3aa9e4
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Intel Corporation nor the names of its contributors
+ *     may be used to endorse or promote products derived from this software
+ *     without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_LUA_MDB_H__
+#define __MURPHY_LUA_MDB_H__
+
+#include <murphy-db/mqi.h>
+
+typedef struct mrp_lua_mdb_table_s  mrp_lua_mdb_table_t;
+
+
+void mrp_lua_create_mdb_class(lua_State *L);
+mrp_lua_mdb_table_t *mrp_lua_create_builtin_table(lua_State *L,
+                                                  mqi_handle_t handle);
+
+
+
+#endif  /* __MURPHY_LUA_MDB_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
diff --git a/src/core/lua-decision/tests/decision-test.c b/src/core/lua-decision/tests/decision-test.c
new file mode 100644 (file)
index 0000000..8d83722
--- /dev/null
@@ -0,0 +1,456 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Intel Corporation nor the names of its contributors
+ *     may be used to endorse or promote products derived from this software
+ *     without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <libgen.h>
+#include <errno.h>
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <murphy/common.h>
+
+#include <murphy/core/lua-utils/funcbridge.h>
+#include <murphy/core/lua-utils/strarray.h>
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-decision/mdb.h>
+
+
+#define VOLUME_CLASS   MRP_LUA_CLASS(volume, limit)
+
+
+typedef enum {
+    DEVICE = 1,
+    STREAM
+} volume_type_t;
+
+typedef enum {
+    TYPE = 1,
+    NAME,
+    DEVICES,
+    STREAMS,
+    LIMIT,
+    UPDATE,
+} volume_field_t;
+
+typedef struct volume_s {
+    volume_type_t       type;
+    const char         *name;
+    mrp_lua_strarray_t *nodes;
+    double              limit;
+    mrp_funcbridge_t   *update;
+    void               *user_data;
+} volume_t;
+
+static int  volume_create(lua_State *);
+static int  volume_getfield(lua_State *);
+static int  volume_setfield(lua_State *);
+static void volume_destroy(void *);
+
+MRP_LUA_METHOD_LIST_TABLE (
+   volume_methods,
+   MRP_LUA_METHOD_CONSTRUCTOR  (volume_create)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+   volume_overrides,
+   MRP_LUA_OVERRIDE_CALL       (volume_create)
+   MRP_LUA_OVERRIDE_GETFIELD   (volume_getfield)
+   MRP_LUA_OVERRIDE_SETFIELD   (volume_setfield)
+);
+
+
+MRP_LUA_CLASS_DEF (
+   volume,                      /* class name */
+   limit,                       /* constructor name */
+   volume_t,                    /* userdata type */
+   volume_destroy,              /* userdata destructor */
+   volume_methods,
+   volume_overrides
+);
+
+
+static size_t    nvol;
+static volume_t *vols[5];
+
+static int volume_create(lua_State *L)
+{
+    int         table;
+    size_t      fldnamlen;
+    const char *fldnam;
+    volume_t   *vol;
+
+    vol = (volume_t *)mrp_lua_create_object(L, VOLUME_CLASS, NULL);
+
+    table = lua_gettop(L);
+
+    MRP_LUA_FOREACH_FIELD(L, 2, fldnam, fldnamlen) {
+
+        switch (fldnamlen) {
+        case 7:
+            if (!strcmp(fldnam, "devices")) {
+                if (vol->nodes) {
+                    return luaL_error(L, "streams and devices are "
+                                      "mutually exclusive");
+                }
+                vol->type  = DEVICE;
+                vol->nodes = mrp_lua_check_strarray(L, -1);
+                break;
+            }
+            if (!strcmp(fldnam, "streams")) {
+                if (vol->nodes) {
+                    return luaL_error(L, "streams and devices are "
+                                      "mutually exclusive");
+                }
+                vol->type  = STREAM;
+                vol->nodes = mrp_lua_check_strarray(L, -1);
+                break;
+            }
+            goto not_userdata;
+
+        case 6:
+            if (!strcmp(fldnam, "update")) {
+                vol->update = mrp_funcbridge_create_luafunc(L, -1);
+                break;
+            }
+            goto not_userdata;
+
+        case 5:
+            if (!strcmp(fldnam, "limit")) {
+                vol->limit = luaL_checknumber(L, -1);
+                break;
+            }
+            goto not_userdata;
+
+        case 4:
+            if (!strcmp(fldnam, "type")) {
+                return luaL_error(L, "type field is readonly");
+            }
+            if (!strcmp(fldnam, "name")) {
+                vol->name = luaL_checklstring(L, -1, NULL);
+                break;
+            }
+            goto not_userdata;
+
+        default:
+        not_userdata:
+            lua_pushvalue(L, -2);
+            lua_pushvalue(L, -2);
+            lua_rawset(L, table);
+            break;
+        }
+    } /* MRP_LUA_FOREACH_FIELD */
+
+    if (!vol->type || !vol->nodes)
+        return luaL_error(L, "Either streams or devices must be present");
+    if (!vol->name)
+        return luaL_error(L, "name is not present");
+
+    mrp_lua_set_object_name(L, VOLUME_CLASS, vol->name);
+
+    vols[nvol++] = vol;
+
+    printf("volume %p\n", vol);
+
+    return 1;
+}
+
+static volume_t *checkvolume(lua_State *L)
+{
+    return (volume_t *)mrp_lua_check_object(L, VOLUME_CLASS, 1);
+}
+
+static volume_field_t checkfield(lua_State *L)
+{
+    size_t      len;
+    const char *name;
+
+    name = luaL_checklstring(L, 2, &len);
+
+    switch (len) {
+    case 4:
+        if (!strcmp(name, "type"))
+            return TYPE;
+        if (!strcmp(name, "name"))
+            return NAME;
+        break;
+    case 5:
+        if (!strcmp(name, "limit"))
+            return LIMIT;
+        break;
+    case 6:
+        if (!strcmp(name, "update"))
+            return UPDATE;
+        break;
+    case 7:
+        if (!strcmp(name, "streams"))
+            return STREAMS;
+        if (!strcmp(name, "devices"))
+            return DEVICES;
+        break;
+    default:
+        break;
+    }
+
+    return 0;
+}
+
+static void volume_destroy(void *data)
+{
+    volume_t *vol = (volume_t *)data;
+    size_t i;
+
+    printf("*** volume destroyed\n");
+
+    mrp_lua_free_strarray(vol->nodes);
+
+    for (i = 0; i < nvol; i++) {
+        if (vols[i] == vol) {
+            while ((i+1) < sizeof(vols)/sizeof(vols[0]) && vols[i+1]) {
+                vols[i] = vols[i+1];
+                i++;
+            }
+            vols[i] = NULL;
+            break;
+        }
+    }
+}
+
+static const char *volumetype2str(volume_type_t type)
+{
+    switch (type) {
+    case DEVICE:     return "device";
+    case STREAM:     return "stream";
+    default:         return "<unknown>";
+    }
+}
+
+
+static int volume_getfield(lua_State *L)
+{
+    volume_t         *vol = checkvolume(L);
+    volume_field_t fld = checkfield(L);
+    char buf[256];
+
+    printf("index %d for %s volume (node %s) \n",
+           fld, volumetype2str(vol->type),
+           mrp_lua_print_strarray(vol->nodes, buf, sizeof(buf)));
+
+    switch (fld) {
+    case TYPE:
+        lua_pushstring(L, volumetype2str(vol->type));
+        break;
+    case STREAMS:
+        if (vol->type == STREAM)
+            mrp_lua_push_strarray(L, vol->nodes);
+        else
+            lua_pushnil(L);
+        break;
+    case DEVICES:
+        if (vol->type == DEVICE)
+            mrp_lua_push_strarray(L, vol->nodes);
+        else
+            lua_pushnil(L);
+        break;
+    case LIMIT:
+        lua_pushnumber(L, vol->limit);
+        break;
+    case UPDATE:
+        mrp_funcbridge_push(L, vol->update);
+        break;
+    default:
+        lua_pushvalue(L, 2);
+        lua_rawget(L, 1);
+        break;
+    }
+
+    return 1;
+}
+
+static int volume_setfield(lua_State *L)
+{
+    volume_t       *vol = checkvolume(L);
+    volume_field_t  fld = checkfield(L);
+    char            buf[256];
+
+    printf("new index %d for %s volume (node %s) \n",
+           fld, volumetype2str(vol->type),
+           mrp_lua_print_strarray(vol->nodes, buf, sizeof(buf)));
+
+    switch (fld) {
+    case STREAMS:
+        if (vol->type != STREAM) {
+            return luaL_error(L, "attempt to set sterams for device "
+                              "volume limit");
+        }
+        goto set_nodes;
+    case DEVICES:
+        if (vol->type != STREAM) {
+            return luaL_error(L, "attempt to set sterams for device "
+                              "volume limit");
+        }
+        goto set_nodes;
+    set_nodes:
+        mrp_lua_free_strarray(vol->nodes);
+        vol->nodes = mrp_lua_check_strarray(L, 3);
+        break;
+    case LIMIT:
+        vol->limit = luaL_checknumber(L, 3);
+        break;
+    case UPDATE:
+        vol->update = mrp_funcbridge_create_luafunc(L, 3);
+    default:
+        lua_rawset(L, 1);
+        break;
+    }
+
+    return 0;
+}
+
+
+static void volume_openlib(lua_State *L)
+{
+
+    mrp_lua_create_object_class(L, VOLUME_CLASS);
+}
+
+
+bool my_update_func(lua_State *L, void *data,
+                    const char *signature, mrp_funcbridge_value_t *args,
+                    char  *ret_type, mrp_funcbridge_value_t *ret_val)
+{
+    MRP_UNUSED(L);
+
+    printf("**** %s(%p) signature='%s' arg1=%p arg2='%s'\n",
+           __FUNCTION__, data, signature,
+           signature[0] == 'o' ? args[0].pointer : NULL,
+           signature[1] == 's' ? args[1].string  : "<undefined>");
+
+    *ret_type = MRP_FUNCBRIDGE_FLOATING;
+    ret_val->floating = 3.1415;
+
+    return true;
+}
+
+int main(int argc, char **argv)
+{
+    mrp_funcbridge_value_t args[] = {
+        {.pointer = NULL },
+        {.string  = "Hello world, here I am"}
+    };
+
+    const char *pnam = basename(argv[0]);
+    lua_State *L;
+    char buf[512];
+    int error;
+    volume_t *v;
+    mrp_funcbridge_t *fb;
+    mrp_funcbridge_value_t ret;
+    char t;
+
+    if (argc > 2) {
+        printf("Usage: %s [file]\n", pnam);
+        exit(1);
+    }
+
+    if (!(L = luaL_newstate())) {
+        printf("failed to initialize Lua\n");
+        exit(1);
+    }
+
+    printf("Lua initialized\n");
+
+    luaL_openlibs(L);
+    mrp_create_funcbridge_class(L);
+    mrp_lua_create_mdb_class(L);
+    volume_openlib(L);
+
+    mrp_funcbridge_create_cfunc(L, "my_update_func", "os",
+                                my_update_func, (void *)0x1234);
+
+    if (argc == 2) {
+        error = luaL_loadfile(L, argv[1]) ||
+                lua_pcall(L, 0, 0, 0);
+        if (error) {
+            printf("%s\n", lua_tostring(L, -1));
+            lua_pop(L, 1);
+        }
+
+        if ((v = args[0].pointer = vols[0]) && (fb = v->update)) {
+            char value[32];
+            if (!mrp_funcbridge_call_from_c(L, fb, "os", args, &t, &ret))
+                printf("*** call failed\n");
+            else {
+                switch (t) {
+                case MRP_FUNCBRIDGE_NO_DATA:
+                    snprintf(value, sizeof(value), "<no data>");
+                    break;
+                case MRP_FUNCBRIDGE_STRING:
+                    snprintf(value, sizeof(value), "%s", ret.string);
+                    break;
+                case MRP_FUNCBRIDGE_INTEGER:
+                    snprintf(value, sizeof(value), "%d", ret.integer);
+                    break;
+                case MRP_FUNCBRIDGE_FLOATING:
+                    snprintf(value, sizeof(value), "%lf", ret.floating);
+                    break;
+                default:
+                    snprintf(value, sizeof(value), "<unsupported>");
+                    break;
+                }
+                printf("*** return value %s\n", value);
+            }
+        }
+    }
+    else {
+        printf("%s> ", pnam);
+        fflush(stdout);
+
+        while (fgets(buf, sizeof(buf), stdin)) {
+            error = luaL_loadbuffer(L, buf, strlen(buf), "line") ||
+                    lua_pcall(L, 0, 0, 0);
+            if (error) {
+                printf("%s\n", lua_tostring(L, -1));
+                lua_pop(L, 1);
+            }
+
+            printf("%s> ", pnam);
+            fflush(stdout);
+        }
+    }
+
+    lua_close(L);
+
+    return 0;
+}
diff --git a/src/core/lua-decision/tests/decision-test.lua b/src/core/lua-decision/tests/decision-test.lua
new file mode 100644 (file)
index 0000000..8992af0
--- /dev/null
@@ -0,0 +1,114 @@
+--[[
+for n in pairs(_G) do
+    print(n)
+end
+--]]
+
+rows = {{key="a",value="A"},{key="b",value="B"},{key="c",value="C"}}
+
+print("rows[2].value="..rows[2].value)
+
+print("mdb.string="..mdb.string)
+
+mdb.table {
+    name    = "amb_values",
+    index   = {"key"},
+    columns = {{"key", mdb.string, 16}, {"value", mdb.floating}}
+}
+
+print("mdb.amb_values="..tostring(mdb.amb_values))
+
+index = "{"
+
+for _, k in ipairs(mdb.amb_values.index) do
+    index = index.." "..k
+end
+
+index = index.." }"
+
+
+coldefs = "{"
+
+for _, cd in ipairs(mdb.amb_values.columns) do
+    local name, type, length
+
+    for i, v in ipairs(cd) do
+        if i == 1 then  name = v   end
+        if i == 2 then  type = v   end
+        if i == 3 then  length = v end
+    end
+
+    coldefs = coldefs.." {"..name..","..type
+
+    if length then
+        coldefs = coldefs..","..length
+    end
+
+    coldefs = coldefs.."}"
+end
+
+coldefs = coldefs.." }"
+
+print("mdb.amb_values.name="..mdb.amb_values.name)
+print("mdb.amb_values.index="..index)
+print("mdb.amb_values.columns="..coldefs)
+
+mdb.amb_values[1] = { key = "foo", value = 3.1415 }
+
+mdb.select {
+           name = "speed",
+           table = "amb_values",
+           columns = {"value"},
+           condition = "key = 'speed'"
+}
+
+print("mdb.speed.statement="..mdb.speed.statement)
+
+q = mdb.speed[0]
+
+mdb.speed:update()
+
+
+volume.limit {
+    name = "speed_adjust",
+    devices = {"speaker", "headphone", "headset"},
+    limit = -0.5,
+    extra = 2468,
+    update = builtin.method.my_update_func
+
+    --[[
+    update = function(self, arg)
+                 print("*** lua update function arg="..arg.." extra="..self.extra)
+                 return 987.0
+             end
+    --]]
+}
+
+
+foo = volume.speed_adjust
+
+for k,v in pairs(foo) do
+    print(k..": "..type(v))
+end
+
+a = pairs(foo)
+print("a "..type(a))
+
+print("limit "..foo.limit)
+
+foo.limit = -31.2
+foo.yoyo = "a"
+
+print("limit "..foo.limit)
+print("type "..foo.type)
+print("yoyo "..foo.yoyo)
+print("extra "..foo.extra)
+print("volume.speed_adjust "..type(volume.speed_adjust))
+print("builtin.method.my_update_func "..type(builtin.method.my_update_func))
+
+-- builtin.my_update_func()
+print("update returned "..foo:update("Hello world"))
+-- volume.speed_adjust=nil
+-- foo=nil
+collectgarbage()
+print("done");