add timestamp features, fix sqlite positional parameters
This commit is contained in:
parent
bf8ce59b73
commit
cf38b947e1
@ -55,6 +55,9 @@ var renderLuaCode string
|
||||
//go:embed lua/session.lua
|
||||
var sessionLuaCode string
|
||||
|
||||
//go:embed lua/timestamp.lua
|
||||
var timestampLuaCode string
|
||||
|
||||
// Module represents a Lua module to load
|
||||
type Module struct {
|
||||
name string
|
||||
@ -78,6 +81,7 @@ var modules = []Module{
|
||||
{"time", timeLuaCode, false},
|
||||
{"math", mathLuaCode, false},
|
||||
{"env", envLuaCode, true},
|
||||
{"timestamp", timestampLuaCode, false},
|
||||
}
|
||||
|
||||
// loadModule loads a single module into the Lua state
|
||||
|
@ -18,7 +18,7 @@ function cookie_set(name, value, options)
|
||||
end
|
||||
|
||||
function cookie_get(name)
|
||||
return __ctx._request_cookies and __ctx._request_cookies[name]
|
||||
return __ctx.cookies and __ctx.cookies[name]
|
||||
end
|
||||
|
||||
function cookie_delete(name, path, domain)
|
||||
|
93
runner/lua/timestamp.lua
Normal file
93
runner/lua/timestamp.lua
Normal file
@ -0,0 +1,93 @@
|
||||
-- timestamp.lua
|
||||
local timestamp = {}
|
||||
|
||||
-- Standard format presets using Lua format codes
|
||||
local FORMATS = {
|
||||
iso = "%Y-%m-%dT%H:%M:%SZ",
|
||||
datetime = "%Y-%m-%d %H:%M:%S",
|
||||
us_date = "%m/%d/%Y",
|
||||
us_datetime = "%m/%d/%Y %I:%M:%S %p",
|
||||
date = "%Y-%m-%d",
|
||||
time = "%H:%M:%S",
|
||||
time12 = "%I:%M:%S %p",
|
||||
readable = "%B %d, %Y %I:%M:%S %p",
|
||||
compact = "%Y%m%d_%H%M%S"
|
||||
}
|
||||
|
||||
-- Parse input to unix timestamp and microseconds
|
||||
local function parse_input(input)
|
||||
local unix_time, micros = 0, 0
|
||||
|
||||
if type(input) == "string" then
|
||||
local frac, secs = input:match("^(0%.%d+)%s+(%d+)$")
|
||||
if frac and secs then
|
||||
unix_time = tonumber(secs)
|
||||
micros = math.floor((tonumber(frac) * 1000000) + 0.5)
|
||||
else
|
||||
unix_time = tonumber(input) or 0
|
||||
end
|
||||
elseif type(input) == "number" then
|
||||
unix_time = math.floor(input)
|
||||
micros = math.floor(((input - unix_time) * 1000000) + 0.5)
|
||||
end
|
||||
|
||||
return unix_time, micros
|
||||
end
|
||||
|
||||
-- Remove leading zeros from number string
|
||||
local function no_leading_zero(s)
|
||||
return s:gsub("^0+", "") or "0"
|
||||
end
|
||||
|
||||
-- Main format function
|
||||
function timestamp.format(input, fmt)
|
||||
fmt = fmt or "datetime"
|
||||
local format_str = FORMATS[fmt] or fmt
|
||||
local unix_time, micros = parse_input(input)
|
||||
local result = os.date(format_str, unix_time)
|
||||
|
||||
-- Handle microseconds if format contains dot
|
||||
if format_str:find("%.") then
|
||||
result = result .. string.format(".%06d", micros)
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
-- US date/time with no leading zeros
|
||||
function timestamp.us_datetime_no_zero(input)
|
||||
local unix_time, micros = parse_input(input)
|
||||
local month = no_leading_zero(os.date("%m", unix_time))
|
||||
local day = no_leading_zero(os.date("%d", unix_time))
|
||||
local year = os.date("%Y", unix_time)
|
||||
local hour = no_leading_zero(os.date("%I", unix_time))
|
||||
local min = os.date("%M", unix_time)
|
||||
local sec = os.date("%S", unix_time)
|
||||
local ampm = os.date("%p", unix_time)
|
||||
|
||||
return string.format("%s/%s/%s %s:%s:%s %s", month, day, year, hour, min, sec, ampm)
|
||||
end
|
||||
|
||||
-- Quick preset functions
|
||||
function timestamp.iso(input) return timestamp.format(input, "iso") end
|
||||
function timestamp.datetime(input) return timestamp.format(input, "datetime") end
|
||||
function timestamp.us_date(input) return timestamp.format(input, "us_date") end
|
||||
function timestamp.us_datetime(input) return timestamp.us_datetime_no_zero(input) end
|
||||
function timestamp.date(input) return timestamp.format(input, "date") end
|
||||
function timestamp.time(input) return timestamp.format(input, "time") end
|
||||
function timestamp.time12(input) return timestamp.format(input, "time12") end
|
||||
function timestamp.readable(input) return timestamp.format(input, "readable") end
|
||||
|
||||
-- Microsecond precision variants
|
||||
function timestamp.datetime_micro(input)
|
||||
return timestamp.format(input, "%Y-%m-%d %H:%M:%S.") .. string.format("%06d", select(2, parse_input(input)))
|
||||
end
|
||||
|
||||
function timestamp.iso_micro(input)
|
||||
return timestamp.format(input, "%Y-%m-%dT%H:%M:%S.") .. string.format("%06dZ", select(2, parse_input(input)))
|
||||
end
|
||||
|
||||
-- Register global convenience function
|
||||
_G.format_time = timestamp.format
|
||||
|
||||
return timestamp
|
@ -244,6 +244,28 @@ func setupParams(state *luajit.State, paramIndex int, execOpts *sqlitex.ExecOpti
|
||||
return fmt.Errorf("invalid parameters: %w", err)
|
||||
}
|
||||
|
||||
// Handle direct array types
|
||||
if arrParams, ok := paramsAny.([]any); ok {
|
||||
execOpts.Args = arrParams
|
||||
return nil
|
||||
}
|
||||
if strArr, ok := paramsAny.([]string); ok {
|
||||
args := make([]any, len(strArr))
|
||||
for i, v := range strArr {
|
||||
args[i] = v
|
||||
}
|
||||
execOpts.Args = args
|
||||
return nil
|
||||
}
|
||||
if floatArr, ok := paramsAny.([]float64); ok {
|
||||
args := make([]any, len(floatArr))
|
||||
for i, v := range floatArr {
|
||||
args[i] = v
|
||||
}
|
||||
execOpts.Args = args
|
||||
return nil
|
||||
}
|
||||
|
||||
params, ok := paramsAny.(map[string]any)
|
||||
if !ok {
|
||||
return fmt.Errorf("unsupported parameter type: %T", paramsAny)
|
||||
|
@ -140,6 +140,13 @@ func (r *Runner) buildHTTPContext(ctx *fasthttp.RequestCtx, params *router.Param
|
||||
})
|
||||
luaCtx.Set("headers", headers)
|
||||
|
||||
// Cookies
|
||||
cookies := r.ctxPool.Get().(map[string]any)
|
||||
ctx.Request.Header.VisitAllCookie(func(key, value []byte) {
|
||||
cookies[string(key)] = string(value)
|
||||
})
|
||||
luaCtx.Set("cookies", cookies)
|
||||
|
||||
// Route parameters
|
||||
if params != nil && len(params.Keys) > 0 {
|
||||
paramMap := r.paramsPool.Get().(map[string]any)
|
||||
@ -187,8 +194,8 @@ func (r *Runner) buildHTTPContext(ctx *fasthttp.RequestCtx, params *router.Param
|
||||
return luaCtx
|
||||
}
|
||||
|
||||
// Releases the HTTP context's maps back to their pool
|
||||
func (r *Runner) releaseHTTPContext(luaCtx *Context) {
|
||||
// Return pooled maps
|
||||
if headers, ok := luaCtx.Get("headers").(map[string]any); ok {
|
||||
for k := range headers {
|
||||
delete(headers, k)
|
||||
@ -196,6 +203,13 @@ func (r *Runner) releaseHTTPContext(luaCtx *Context) {
|
||||
r.ctxPool.Put(headers)
|
||||
}
|
||||
|
||||
if cookies, ok := luaCtx.Get("cookies").(map[string]any); ok {
|
||||
for k := range cookies {
|
||||
delete(cookies, k)
|
||||
}
|
||||
r.ctxPool.Put(cookies)
|
||||
}
|
||||
|
||||
if params, ok := luaCtx.Get("params").(map[string]any); ok && len(params) > 0 {
|
||||
for k := range params {
|
||||
delete(params, k)
|
||||
|
Loading…
x
Reference in New Issue
Block a user