if not cjson then cjson = require "cjson" end
if not msgpack then msgpack = require "msgpack" end
if not LuaDate then LuaDate = require "LuaDate" end
if not Transform then Transform = require "transform" end
if not JSONBMAPNEW then JSONBMAPNEW = {} end
if not JSONPath then JSONPath = require 'JSONPath' end
if not ASTCache then ASTCache = setmetatable({},{__mode='v'}) end
if not ASTSETCache then ASTSETCache = setmetatable({},{__mode='v'}) end

local pairs = pairs
local tonumber = tonumber
local rawget, rawset = rawget, rawset
local mpdecode = msgpack.decode
local concat = table.concat
local lower = utf8.lower
local ncasecmp = utf8.ncasecmp
local istable,isnumber,isstring,isfunction = extend.istable,extend.isnumber,extend.isstring,extend.isfunction
local _BBREAD = blob.read
local jsonencode = cjson.encode

function DATETOEPOCH(date)
  if not istable(date) then return nil end
  return LuaDate.diff(LuaDate(date.year,date.month,date.day):toutc(),LuaDate.epoch()):spanseconds()*1000
end

function TIMETOEPOCH(time)
  if not istable(time) then return nil end
  return LuaDate.diff(LuaDate(1,1,1,time.hour,time.min,time.sec,time.msec*1000):toutc(),LuaDate.epoch()):spanseconds()*1000
end

function TIMESTAMPTOEPOCH(ts)
  if not istable(ts) then return nil end
  return LuaDate.diff(LuaDate(ts.year,ts.month,ts.day,ts.hour,ts.min,ts.sec,ts.msec*1000):toutc(),LuaDate.epoch()):spanseconds()*1000
end

local _newindex,_index
if DM_IDCAP == 1 then
  local function SearchKey(t,k)
    for _k in pairs(t) do
      if ncasecmp(k, _k) == 0 then       -- found
        return _k
      end
    end
    return k                             --  not found, default
  end
  _newindex = function (t,k,v)           --  fallback to newindex
    rawset(t,SearchKey(t,k),v)           --   set value via key mapping
  end
  _index = function(t,k)                 --  fallback to index
    if not k then return nil end
    return rawget(t,SearchKey(t,k))      -- get value via key mapping
  end
else
  _newindex = rawset
  _index = function(t,k) return end
end

-- make a shallow copy of a table for restore origin key
local function obj_restore(t)
  if not t.__index then return t end  -- v2
  local res = {}                      -- v1
  local keymap = t.__index
  for k,v in pairs(t) do              -- copy table
    if keymap[k] then
      res[keymap[k]] = v
    end
  end
  return res
end

local function deepmerge(t1,t2)
  for k,v in pairs(t2) do
    if (istable(v) and istable(t1[k])) then
      deepmerge(t1[k],t2[k])
    else
      t1[k] = v
    end
  end
  return t1
end

local function deepcopy(t)
  if not istable(t) then return t end
  local res = {}
  for k,v in pairs(t) do
    if istable(v) then
      v = deepcopy(v)
    end
    res[k] = v
  end
  return res
end

local function obj_cache_upd(tab)
  local bbid = JSONBMAPNEW[tab]
  if not bbid then
    return deepcopy(tab) -- tab from OLDMAP
  end
  return tab
end

local function JC_CREATE(...)
  local arg = {...}
  if arg[1] == nil then return nil end
  local tab = {}
  for i = 1,#arg,2 do
    tab[arg[i]] = arg[i+1] 
  end
  return tab 
end

local function JC_CREATE_U8(...)
  local arg = {...}
  for i = 1,#arg,2 do
    arg[i] = iconv:ToU8(arg[i])
    if isstring(arg[i+1]) then arg[i+1] = iconv:ToU8(arg[i+1]) end
  end
  return JC_CREATE(table.unpack(arg))
end

local function JC_SET(tab, ...)
  if tab == nil then return JC_CREATE(...) end
  local arg = {...}
  if not arg[1] then return tab end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once
  tab = obj_cache_upd(tab)
  if tab.__index then     -- v1 upgrade to v2
    tab = obj_restore(tab)
  end
  for i = 1,#arg,2 do
    _newindex(tab, arg[i], arg[i+1])
  end
  return tab
end

local function JC_SET_U8(tab, ...)
  local arg = {...}
  for i = 1,#arg,2 do
    arg[i] = iconv:ToU8(arg[i])
    if isstring(arg[i+1]) then arg[i+1] = iconv:ToU8(arg[i+1]) end
  end
  return JC_SET(tab, table.unpack(arg))
end

local function JC_DELETE(tab, key)
  if tab == nil then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once
  if key ~= nil then
    tab = obj_cache_upd(tab)
    _newindex(tab, key, nil)
  end
  return tab
end

local function JC_DELETE_U8(tab, key)
  return JC_DELETE(tab,iconv:ToU8(key))
end

local function JC_LIST(tab)
  if tab == nil then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once
  local res = {}
  for k in pairs(tab) do
    res[#res+1] = "'" .. k .. "'"
  end
  return '{' .. concat(res,',') .. '}'
end

local function JC_LIST_U8(tab)
  return iconv:ToLoc(JC_LIST(tab))
end

local function JC_GETINT(tab, key)
  if not tab then return nil end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once
  return tonumber(tab[key] or _index(tab,key))
end

local function JC_GETINT_U8(tab, key)
  return JC_GETINT(tab,iconv:ToU8(key))
end

local function JC_GETSTRING(tab, key)
  if not tab then return nil end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once
  local res = tab[key] or _index(tab,key)
  return istable(res) and jsonencode(res) or res
end

local function JC_GETSTRING_U8(tab, key)
  return iconv:ToLoc(JC_GETSTRING(tab, iconv:ToU8(key)))
end

local function JC_GETDATE(tab, key)
  if not tab then return nil end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once
  local dt = tab[key] or _index(tab, key)
  if isnumber(dt) then --new epoch
    local dt_table={}
    dt = LuaDate(dt/1000):tolocal()
    dt_table.year,dt_table.month,dt_table.day = dt:getdate()
    dt_table.hour,dt_table.min,dt_table.sec,dt_table.msec = dt:gettime()
    dt_table.msec = dt_table.msec/1000
    return dt_table
  else  -- old version return table 
    return dt
  end
end

local function JC_GETDATE_U8(tab, key)
  return JC_GETDATE(tab, iconv:ToU8(key))
end

local function JC_GETJSON(tab)
  if tab == nil then return nil end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once
  if DM_IDCAP == 1 then
    return jsonencode(obj_restore(tab))
  else
    return jsonencode(tab)
  end
end

local function JC_GETJSON_U8(tab)
  return iconv:ToLoc(JC_GETJSON(tab))
end

local function JC_MERGE(tab, tab2)
  if tab == nil or tab2 == nil then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end  
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once
  if not istable(tab2) then 
    tab2 = mpdecode(_BBREAD(tab2))
    if not istable(tab2) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once
  local tab = obj_cache_upd(tab)
--  local tab2 = obj_cache_upd(tab2)
  return deepmerge(tab,tab2)
end

local function JC_ARRAY(...)
  local arg = {...}
  if arg[1] == nil then return nil end
  local tab = {}
  for i = 1,#arg,1 do
    tab[i] = arg[i]
  end
  return tab 
end

local function JC_ARRAY_U8(...)
  local arg = {...}
  for i = 1,#arg,1 do
    if isstring(arg[i]) then arg[i] = iconv:ToU8(arg[i]) end
  end
  return JC_ARRAY(table.unpack(arg))
end

local function JC_JSONSTRMERGE(tab, u8str)
  if tab == nil then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end  
  if u8str == nil then return tab end
  local mergetab,msg = cjson.decode(u8str)
  if not istable(mergetab) then error(ErrTab['ERR_INV_JSON_FORMAT'],0) end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once
  local tab = obj_cache_upd(tab)
  for k, v in pairs(mergetab) do _newindex(tab, k, v) end
  return tab
end

local function JC_JSONMERGE(tab, CBID)
  return JC_JSONSTRMERGE(tab, _BBREAD(CBID))
end

local function JC_JSONMERGE_U8(tab, CBID)
  return JC_JSONSTRMERGE(tab, iconv:ToU8(_BBREAD(CBID)))
end

local function JC_JSONSTRCREATE(u8str)
  if u8str == nil then return nil end
  local tab,msg = cjson.decode(u8str)
  if not istable(tab) then error(ErrTab['ERR_INV_JSON_FORMAT'],0) end
  return tab
end

local function JC_JSONCREATE(CBID)
  return JC_JSONSTRCREATE(_BBREAD(CBID))
end

local function JC_JSONCREATE_U8(CBID)
  return JC_JSONSTRCREATE(iconv:ToU8(_BBREAD(CBID)))
end

local function JC_EXISTS(tab, key)
  if tab == nil then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once
  local val = tab[key] or _index(tab, key)
  return val and true or false
end

local function JC_EXISTS_U8(tab, key)
  return JC_EXISTS(tab, iconv:ToU8(key))
end

local function JC_GETOBJECT(tab, key)
  if tab == nil or key == nil then return nil end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once
  local subtab = tab[key] or _index(tab, key)
  if istable(subtab) then
    return subtab
  end
  return nil    
end

local function JC_GETOBJECT_U8(tab, key)
  return JC_GETOBJECT(tab, iconv:ToU8(key))
end

local function JC_RENAME(tab, src, dst)
  if tab == nil then return nil end 
  if not src or not dst then return tab end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once 
  tab = obj_cache_upd(tab)
  _newindex(tab, dst, tab[src] or _index(tab, src))  --src value need IDCAP
  _newindex(tab, src, nil)     --make null src value need IDCAP
  return tab
end

local function JC_RENAME_U8(tab, src, dst)
  return JC_RENAME(tab, iconv:ToU8(src), iconv:ToU8(dst))
end

--CREATE BUILTIN FUNCTION jsoncol.JSONCOLS_TRANSFORM(BLOB,STRING) RETURNS BLOB LANGUAGE LUA
local function JC_TRANSFORM_U8(tab,templete)
  return JC_TRANSFORM(tab,iconv:ToU8(templete))
end

local function JC_TRANSFORM(tab,templete)
  if not tab then return JC_JSONSTRCREATE(templete) end
  if not templete then return tab end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end
  tab = obj_cache_upd(tab)
  tab = Transform(tab,JC_JSONSTRCREATE(templete))
  return tab    
end

--
-- exported UDF function
--
if DM_LCODE == 10 then
JSONCOLS_CREATE = JC_CREATE
JSONCOLS_SET = JC_SET
JSONCOLS_DELETE = JC_DELETE
JSONCOLS_LIST = JC_LIST
JSONCOLS_GETSMALLINT = JC_GETINT
JSONCOLS_GETINT = JC_GETINT
JSONCOLS_GETBIGINT = JC_GETINT
JSONCOLS_GETFLOAT = JC_GETINT
JSONCOLS_GETDOUBLE = JC_GETINT
JSONCOLS_GETSTRING = JC_GETSTRING
JSONCOLS_GETNSTRING = JC_GETSTRING
JSONCOLS_GETDATE = JC_GETDATE
JSONCOLS_GETTIME = JC_GETDATE
JSONCOLS_GETTIMESTAMP = JC_GETDATE
JSONCOLS_GETJSON = JC_GETJSON
JSONCOLS_MERGE = JC_MERGE
JSONCOLS_ARRAY = JC_ARRAY
JSONCOLS_OBJECT = JC_CREATE
JSONCOLS_JSONMERGE = JC_JSONMERGE
JSONCOLS_JSONCREATE = JC_JSONCREATE
JSONCOLS_EXISTS = JC_EXISTS
JSONCOLS_GETOBJECT = JC_GETOBJECT
JSONCOLS_RENAME = JC_RENAME
JSONCOLS_TRANSFORM = JC_TRANSFORM
else
JSONCOLS_CREATE = JC_CREATE_U8
JSONCOLS_SET = JC_SET_U8
JSONCOLS_DELETE = JC_DELETE_U8
JSONCOLS_LIST = JC_LIST_U8
JSONCOLS_GETSMALLINT = JC_GETINT_U8
JSONCOLS_GETINT = JC_GETINT_U8
JSONCOLS_GETBIGINT = JC_GETINT_U8
JSONCOLS_GETFLOAT = JC_GETINT_U8
JSONCOLS_GETDOUBLE = JC_GETINT_U8
JSONCOLS_GETSTRING = JC_GETSTRING_U8
JSONCOLS_GETNSTRING = JC_GETSTRING_U8
JSONCOLS_GETDATE = JC_GETDATE_U8
JSONCOLS_GETTIME = JC_GETDATE_U8
JSONCOLS_GETTIMESTAMP = JC_GETDATE_U8
JSONCOLS_GETJSON = JC_GETJSON_U8
JSONCOLS_MERGE = JC_MERGE
JSONCOLS_ARRAY = JC_ARRAY_U8
JSONCOLS_OBJECT = JC_CREATE_U8
JSONCOLS_JSONMERGE = JC_JSONMERGE_U8
JSONCOLS_JSONCREATE = JC_JSONCREATE_U8
JSONCOLS_EXISTS = JC_EXISTS_U8
JSONCOLS_GETOBJECT = JC_GETOBJECT_U8
JSONCOLS_RENAME = JC_RENAME_U8
JSONCOLS_TRANSFORM = JC_TRANSFORM_U8
end

--JSONPath
local function pathscan(tab, path)
  local ast = ASTCache[path]
  if not ast then    --no cache
--  dac  return load string
--  expr reutrn ast 
    ast = jpext.dac(JSONPath.parse(path))
    if isstring(ast) then ast = load(ast)() end
    ASTCache[path] = ast
  end

  if isfunction(ast) then
    local rc,ret = pcall(ast, tab)
    return rc and ret or nil
  else --if type(ast) == 'table' then expression mode
    return JSONPath.expression(tab, ast) --return 2 args need check
  end
end

local function pathset(dst_tb, path, value)
  local ast = ASTSETCache[path]
  if not ast then    --no cache
--  dac  return load string
--  expr reutrn ast 
    ast = jpext.dacset(JSONPath.parse(path))
    if isstring(ast) then ast = load(ast)() end
    ASTSETCache[path] = ast
  end
  
  if isfunction(ast) then
    local rc = pcall(ast,dst_tb,value)
    if not rc then
      local i, key = 2
      local pathtab, tab = JSONPath.parse(path), dst_tb
      while i <= #pathtab do
        if istable(pathtab[i]) then
          key = tonumber(pathtab[i][2]) + 1
        else
          key = tonumber(pathtab[i]) and tonumber(pathtab[i])+1 or pathtab[i]
        end
        if not istable(tab[key]) then 
          tab[key] = {}
        end
        tab = tab[key]
        i = i + 1
      end
      ast(dst_tb,value)
    end
  else --if type(ast) == 'table' then expression mode
    return JSONPath.expression_set(dst_tb, ast, value)
  end
end

local function JP_CREATE(...)
  local arg = {...}
  if arg[1] == nil then return nil end
  local dst_tb = {}
  for i = 1,#arg,2 do
    pathset(dst_tb, arg[i], arg[i+1])
  end
  return dst_tb
end

local function JP_CREATE_U8(...)
  local arg = {...}
  for i = 1,#arg,2 do
    arg[i] = iconv:ToU8(arg[i])
    if isstring(arg[i+1]) then arg[i+1] = iconv:ToU8(arg[i+1]) end
  end
  return JP_CREATE(table.unpack(arg))
end

local function JP_SET(tab, ...)
  if tab == nil then return JP_CREATE(...) end
  local arg = {...}
  if not arg[1] then return tab end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once 
  tab = obj_cache_upd(tab)
  for i = 1,#arg,2 do
    pathset(tab, arg[i], arg[i+1])
  end
  return tab
end

local function JP_SET_U8(tab, ...)
  local arg = {...}
  for i = 1,#arg,2 do
    arg[i] = iconv:ToU8(arg[i])
    if isstring(arg[i+1]) then arg[i+1] = iconv:ToU8(arg[i+1]) end
  end
  return JP_SET(tab, table.unpack(arg))
end

local function JP_DELETE(tab, path)
  if tab == nil then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once
  if path ~=nil then
    tab = obj_cache_upd(tab)
    pathset(tab,path,nil)
  end
  return tab
end

local function JP_DELETE_U8(tab, path)
  return	 JP_DELETE(tab,iconv:ToU8(path))
end

local function JP_EXISTS(tab, path)
  if tab == nil then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  if path == nil then return false end
  tab = pathscan(tab,path)
  return tab and true or false  
end

local function JP_EXISTS_U8(tab, path)
  return JP_EXISTS(tab, iconv:ToU8(path))
end

local function JP_GETOBJECT(tab, path)
  if tab == nil or path == nil then return nil end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once
  local subtab = pathscan(tab,path)
  if istable(subtab)  then
    return subtab
  end
  return nil
end

local function JP_GETOBJECT_U8(tab, path)
  return JP_GETOBJECT(tab, iconv:ToU8(path))
end

local function JP_GETINT(tab, path)
  if tab == nil or path == nil then return nil end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once
  return tonumber(pathscan(tab,path))
end

local function JP_GETINT_U8(tab, path)
  return JP_GETINT(tab, iconv:ToU8(path))
end

local function JP_GETSTRING(tab, path)
  if tab == nil or path == nil then return nil end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once
  local res = pathscan(tab,path)
  if istable(res) then
    return next(res) and jsonencode(res)
  else
    return res
  end
end

local function JP_GETSTRING_U8(tab, path)
  return iconv:ToLoc(JP_GETSTRING(tab, iconv:ToU8(path)))
end

local function JP_GETDATE(tab, path)
  if tab == nil or path == nil then return nil end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once
  local dt = pathscan(tab,path) 
  if type(dt) == 'number' then --new epoch
    local dt_table={}
    dt = LuaDate(dt/1000):tolocal()
    dt_table.year,dt_table.month,dt_table.day = dt:getdate()
    dt_table.hour,dt_table.min,dt_table.sec,dt_table.msec = dt:gettime()
    dt_table.msec = dt_table.msec/1000
    return dt_table
  else  -- old version return table 
    return dt
  end
end

local function JP_GETDATE_U8(tab, path)
  return JP_GETDATE(tab, iconv:ToU8(path))
end

local function JP_JSONSTRMERGE(tab, path, u8str)
  if tab == nil then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  if u8str == nil then return tab end
  local mergetab,msg = cjson.decode(u8str)
  if not istable(mergetab) then error(ErrTab['ERR_INV_JSON_FORMAT'],0) end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once  
  local tab = obj_cache_upd(tab)
  pathset(tab, path, function(parent, path) deepmerge(path and parent[path] or parant, mergetab) end )
  return tab
end

local function JP_JSONMERGE(tab, path, CBID)
  return JP_JSONSTRMERGE(tab, path, _BBREAD(CBID))
end

local function JP_JSONMERGE_U8(tab, path, CBID)
  return JP_JSONSTRMERGE(tab, iconv:ToU8(path), iconv:ToU8(_BBREAD(CBID)))
end

local function JP_LENGTH(tab, path)
  if tab == nil then return nil end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once
  local res = path and pathscan(tab,path) or tab -- path nil -> dynamic $ len
  if not istable(res) then return 1 end  
  if #res ~= 0 then  -- array table
    return #res
  else   -- map table
    local num = 0
    for _ in pairs(res) do num = num + 1 end
    return num   
  end
end

local function JP_LENGTH_U8(tab, path)
  return JP_LENGTH(tab, iconv:ToU8(path))
end

local function JP_TYPE(tab, path)
  if tab == nil then return nil end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once
  local res = path and pathscan(tab,path) or tab -- path nil -> dynamic $ len
  if not istable(res) then 
    if isstring(res) then 
      return 'STRING' 
    else 
      return string.upper(math.type(res))
    end
  else
    if #res ~= 0 then  -- array table
      return 'ARRAY'
    else   -- map table  
      return 'OBJECT'
    end
  end
end

local function JP_TYPE_U8(tab, path)
  return JP_TYPE(tab, iconv:ToU8(path))
end

local function JP_RENAME(tab, src, dst)
  if tab == nil then return nil end 
  if not src then return tab end
  if not istable(tab) then 
    tab = mpdecode(_BBREAD(tab))
    if not istable(tab) then error(ErrTab['ERR_INV_DYNAMIC_BLOB'],0) end
  end -- for UDF call once
  tab = obj_cache_upd(tab)
  JSONPath.rename(tab, JSONPath.parse(src), nil, dst)  --discard the reture value tab is updated
  return tab
end

local function JP_RENAME_U8(tab, src, dst)
  return JP_RENAME(tab, iconv:ToU8(src), iconv:ToU8(dst))
end

if DM_LCODE == 10 then
JSONPATH_CREATE = JP_CREATE
JSONPATH_SET = JP_SET
JSONPATH_DELETE = JP_DELETE
JSONPATH_GETSMALLINT = JP_GETINT
JSONPATH_GETINT = JP_GETINT
JSONPATH_GETBIGINT = JP_GETINT
JSONPATH_GETFLOAT = JP_GETINT
JSONPATH_GETDOUBLE = JP_GETINT
JSONPATH_GETSTRING = JP_GETSTRING
JSONPATH_GETNSTRING = JP_GETSTRING
JSONPATH_GETDATE = JP_GETDATE
JSONPATH_GETTIME = JP_GETDATE
JSONPATH_GETTIMESTAMP = JP_GETDATE
JSONPATH_GETJSON = JC_GETJSON
JSONPATH_JSONMERGE = JP_JSONMERGE
JSONPATH_LENGTH = JP_LENGTH
JSONPATH_TYPE = JP_TYPE
--JSONPATH_JSONCREATE = JP_JSONCREATE
JSONPATH_EXISTS = JP_EXISTS
JSONPATH_GETOBJECT = JP_GETOBJECT
JSONPATH_RENAME = JP_RENAME
JSONPATH_TRANSFORM = JC_TRANSFORM
else
JSONPATH_CREATE = JP_CREATE_U8
JSONPATH_SET = JP_SET_U8
JSONPATH_DELETE = JP_DELETE_U8
JSONPATH_GETSMALLINT = JP_GETINT_U8
JSONPATH_GETINT = JP_GETINT_U8
JSONPATH_GETBIGINT = JP_GETINT_U8
JSONPATH_GETFLOAT = JP_GETINT_U8
JSONPATH_GETDOUBLE = JP_GETINT_U8
JSONPATH_GETSTRING = JP_GETSTRING_U8
JSONPATH_GETNSTRING = JP_GETSTRING_U8
JSONPATH_GETDATE = JP_GETDATE_U8
JSONPATH_GETTIME = JP_GETDATE_U8
JSONPATH_GETTIMESTAMP = JP_GETDATE_U8
JSONPATH_GETJSON = JC_GETJSON
JSONPATH_JSONMERGE = JP_JSONMERGE_U8
JSONPATH_LENGTH = JP_LENGTH_U8
JSONPATH_TYPE = JP_TYPE_U8
--JSONPATH_JSONCREATE = JP_JSONCREATE_U8
JSONPATH_EXISTS = JP_EXISTS_U8
JSONPATH_GETOBJECT = JP_GETOBJECT_U8
JSONPATH_RENAME = JP_RENAME_U8
JSONPATH_TRANSFORM = JC_TRANSFORM_U8
end