summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/mkiv/util-sql-imp-ffi.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkiv/util-sql-imp-ffi.lua')
-rw-r--r--Master/texmf-dist/tex/context/base/mkiv/util-sql-imp-ffi.lua590
1 files changed, 269 insertions, 321 deletions
diff --git a/Master/texmf-dist/tex/context/base/mkiv/util-sql-imp-ffi.lua b/Master/texmf-dist/tex/context/base/mkiv/util-sql-imp-ffi.lua
index 3731933f1e1..2a2bc656934 100644
--- a/Master/texmf-dist/tex/context/base/mkiv/util-sql-imp-ffi.lua
+++ b/Master/texmf-dist/tex/context/base/mkiv/util-sql-imp-ffi.lua
@@ -8,7 +8,7 @@ if not modules then modules = { } end modules ['util-sql-imp-ffi'] = {
-- I looked at luajit-mysql to see how the ffi mapping was done but it didn't work
-- out that well (at least not on windows) but I got the picture. As I have somewhat
--- different demands I simplified / redid the ffi bit and just took the swiglib
+-- different demands I simplified / redid the ffi bti and just took the swiglib
-- variant and adapted that.
local tonumber = tonumber
@@ -17,7 +17,6 @@ local format, byte = string.format, string.byte
local lpegmatch = lpeg.match
local setmetatable, type = setmetatable, type
local sleep = os.sleep
-local formatters = string.formatters
local trace_sql = false trackers.register("sql.trace", function(v) trace_sql = v end)
local trace_queries = false trackers.register("sql.queries",function(v) trace_queries = v end)
@@ -34,9 +33,6 @@ ffi.cdef [[
a query. The rest is handled already in the Lua code elsewhere.
*/
- void free(void*ptr);
- void * malloc(size_t size);
-
typedef void MYSQL_instance;
typedef void MYSQL_result;
typedef char **MYSQL_row;
@@ -119,14 +115,6 @@ ffi.cdef [[
MYSQL_result *result
);
- unsigned int mysql_affected_rows (
- MYSQL_instance *mysql
- );
-
- unsigned int mysql_field_count (
- MYSQL_instance *mysql
- );
-
unsigned int mysql_num_fields (
MYSQL_result *res
);
@@ -175,14 +163,12 @@ local dataprepared = helpers.preparetemplate
local serialize = sql.serialize
local deserialize = sql.deserialize
-local mysql_open_session = mysql.mysql_init
+local mysql_initialize = mysql.mysql_init
local mysql_open_connection = mysql.mysql_real_connect
local mysql_execute_query = mysql.mysql_real_query
local mysql_close_connection = mysql.mysql_close
-local mysql_affected_rows = mysql.mysql_affected_rows
-local mysql_field_count = mysql.mysql_field_count
local mysql_field_seek = mysql.mysql_field_seek
local mysql_num_fields = mysql.mysql_num_fields
local mysql_fetch_fields = mysql.mysql_fetch_fields
@@ -194,7 +180,6 @@ local mysql_init = mysql.mysql_init
local mysql_store_result = mysql.mysql_store_result
local mysql_free_result = mysql.mysql_free_result
-local mysql_error_number = mysql.mysql_errno
local mysql_error_message = mysql.mysql_error
local NULL = ffi.cast("MYSQL_result *",0)
@@ -202,361 +187,325 @@ local NULL = ffi.cast("MYSQL_result *",0)
local ffi_tostring = ffi.string
local ffi_gc = ffi.gc
-local instance = mysql.mysql_init(nil)
+----- mysqldata = ffi.cast("MYSQL_instance*",mysql.malloc(1024*1024))
+local instance = mysql.mysql_init(nil) -- (mysqldata)
local mysql_constant_false = false
local mysql_constant_true = true
-local wrapresult do
-
- local function collect(t)
- local result = t._result_
- if result then
- ffi_gc(result,mysql_free_result)
- end
+local function finish(t)
+ local r = t._result_
+ if r then
+ ffi_gc(r,mysql_free_result)
end
+end
- local function finish(t)
- local result = t._result_
- if result then
- t._result_ = nil
- ffi_gc(result,mysql_free_result)
- end
- end
+local function getcolnames(t)
+ return t.names
+end
- local function getcoldata(t)
- local result = t._result_
- local nofrows = t.nofrows
- local noffields = t.noffields
- local names = { }
- local types = { }
- local fields = mysql_fetch_fields(result)
- for i=1,noffields do
- local field = fields[i-1]
- names[i] = ffi_tostring(field.name)
- types[i] = tonumber(field.type) -- todo
- end
- t.names = names
- t.types = types
- end
+local function getcoltypes(t)
+ return t.types
+end
- local function getcolnames(t)
- local names = t.names
- if names then
- return names
- end
- getcoldata(t)
- return t.names
- end
+local function numrows(t)
+ return tonumber(t.nofrows)
+end
- local function getcoltypes(t)
- local types = t.types
- if types then
- return types
+local function list(t)
+ local result = t._result_
+ if result then
+ local row = mysql_fetch_row(result)
+ -- local len = mysql_fetch_lengths(result)
+ local result = { }
+ for i=1,t.noffields do
+ result[i] = ffi_tostring(row[i-1])
end
- getcoldata(t)
- return t.types
- end
-
- local function numrows(t)
- return t.nofrows
+ return result
end
+end
- -- local function fetch(t)
- -- local
- -- local row = mysql_fetch_row(result)
- -- local result = { }
- -- for i=1,t.noffields do
- -- result[i] = ffi_tostring(row[i-1])
- -- end
- -- return unpack(result)
- -- end
-
- local mt = {
- __gc = collect,
- __index = {
- _result_ = nil,
- close = finish,
- numrows = numrows,
- getcolnames = getcolnames,
- getcoltypes = getcoltypes,
- -- fetch = fetch, -- not efficient
- }
- }
-
- wrapresult = function(connection)
- local result = mysql_store_result(connection)
- if result ~= NULL then
- mysql_field_seek(result,0)
- local t = {
- _result_ = result,
- nofrows = tonumber(mysql_num_rows (result) or 0) or 0,
- noffields = tonumber(mysql_num_fields(result) or 0) or 0,
- }
- return setmetatable(t,mt)
- elseif tonumber(mysql_field_count(connection) or 0) or 0 > 0 then
- return tonumber(mysql_affected_rows(connection))
+local function hash(t)
+ local result = t._result_
+ local fields = t.names
+ if result then
+ local row = mysql_fetch_row(result)
+ -- local len = mysql_fetch_lengths(result)
+ local result = { }
+ for i=1,t.noffields do
+ result[fields[i]] = ffi_tostring(row[i-1])
end
+ return result
end
-
end
-local initializesession do
+local function wholelist(t)
+ return fetch_all_rows(t._result_)
+end
- -- timeouts = [ connect_timeout |wait_timeout | interactive_timeout ]
+local mt = { __index = {
+ -- regular
+ finish = finish,
+ list = list,
+ hash = hash,
+ wholelist = wholelist,
+ -- compatibility
+ numrows = numrows,
+ getcolnames = getcolnames,
+ getcoltypes = getcoltypes,
+ -- fallback
+ _result_ = nil,
+ names = { },
+ types = { },
+ noffields = 0,
+ nofrows = 0,
+ }
+}
- local timeout -- = 3600 -- to be tested
+local nt = setmetatable({},mt)
- -- connection
+-- session
- local function close(t)
- -- just a struct ?
- end
+local function close(t)
+ mysql_close_connection(t._connection_)
+end
- local function execute(t,query)
- if query and query ~= "" then
- local connection = t._connection_
- local result = mysql_execute_query(connection,query,#query)
- if result == 0 then
- return wrapresult(connection)
+local function execute(t,query)
+ if query and query ~= "" then
+ local connection = t._connection_
+ local result = mysql_execute_query(connection,query,#query)
+ if result == 0 then
+ local result = mysql_store_result(connection)
+ if result ~= NULL then
+ mysql_field_seek(result,0)
+ local nofrows = tonumber(mysql_num_rows(result) or 0)
+ local noffields = tonumber(mysql_num_fields(result))
+ local names = { }
+ local types = { }
+ local fields = mysql_fetch_fields(result)
+ for i=1,noffields do
+ local field = fields[i-1]
+ names[i] = ffi_tostring(field.name)
+ types[i] = tonumber(field.type) -- todo
+ end
+ local t = {
+ _result_ = result,
+ names = names,
+ types = types,
+ noffields = noffields,
+ nofrows = nofrows,
+ }
+ return setmetatable(t,mt)
else
- -- mysql_error_number(connection)
- return false, ffi_tostring(mysql_error_message(connection))
+ return nt
end
end
- return false
end
+ return false
+end
- local mt = {
- __index = {
- close = close,
- execute = execute,
- }
+local mt = { __index = {
+ close = close,
+ execute = execute,
}
+}
- -- session
-
- local function open(t,database,username,password,host,port)
- local connection = mysql_open_connection(
- t._session_,
- host or "localhost",
- username or "",
- password or "",
- database or "",
- port or 0,
- NULL,
- 0
- )
- if connection ~= NULL then
- if timeout then
- execute(connection,formatters["SET SESSION connect_timeout=%s ;"](timeout))
- end
- local t = {
- _connection_ = connection,
- }
- return setmetatable(t,mt)
- end
+local function open(t,database,username,password,host,port)
+ local connection = mysql_open_connection(
+ t._session_,
+ host or "localhost",
+ username or "",
+ password or "",
+ database or "",
+ port or 0,
+ NULL,
+ 0
+ )
+ if connection ~= NULL then
+ local t = {
+ _connection_ = connection,
+ }
+ return setmetatable(t,mt)
end
+end
- local function message(t)
- return mysql_error_message(t._session_)
- end
+local function message(t)
+ return mysql_error_message(t._session_)
+end
- local function close(t)
- local connection = t._connection_
- if connection and connection ~= NULL then
- ffi_gc(connection, mysql_close)
- t.connection = nil
- end
- end
+local function close(t)
+ -- dummy, as we have a global session
+end
- local mt = {
- __index = {
- connect = open,
- close = close,
- message = message,
- },
+local mt = {
+ __index = {
+ connect = open,
+ close = close,
+ message = message,
}
+}
- initializesession = function()
- local session = {
- _session_ = mysql_open_session(instance) -- maybe share, single thread anyway
- }
- return setmetatable(session,mt)
- end
-
+local function initialize()
+ local session = {
+ _session_ = mysql_initialize(instance) -- maybe share, single thread anyway
+ }
+ return setmetatable(session,mt)
end
-local executequery do
+-- -- -- --
- local function connect(session,specification)
- return session:connect(
- specification.database or "",
- specification.username or "",
- specification.password or "",
- specification.host or "",
- specification.port
+local function connect(session,specification)
+ return session:connect(
+ specification.database or "",
+ specification.username or "",
+ specification.password or "",
+ specification.host or "",
+ specification.port
+ )
+end
+
+local function error_in_connection(specification,action)
+ report_state("error in connection: [%s] user %s into %s at %s:%s",
+ action or "unknown",
+ specification.username or "no username",
+ specification.database or "no database",
+ specification.host or "no host",
+ specification.port or "no port"
)
- end
+end
- local function fetched(specification,query,converter)
- if not query or query == "" then
- report_state("no valid query")
- return false
+local function datafetched(specification,query,converter)
+ if not query or query == "" then
+ report_state("no valid query")
+ return { }, { }
+ end
+ local id = specification.id
+ local session, connection
+ if id then
+ local c = cache[id]
+ if c then
+ session = c.session
+ connection = c.connection
end
- local id = specification.id
- local session, connection
- if id then
- local c = cache[id]
- if c then
- session = c.session
- connection = c.connection
- end
+ if not connection then
+ session = initialize()
+ connection = connect(session,specification)
if not connection then
- session = initializesession()
- if not session then
- return formatters["no session for %a"](id)
- end
- connection = connect(session,specification)
- if not connection then
- return formatters["no connection for %a"](id)
+ for i=1,nofretries do
+ sleep(retrydelay)
+ report_state("retrying to connect: [%s.%s] %s@%s to %s:%s",
+ id,i,
+ specification.database or "no database",
+ specification.username or "no username",
+ specification.host or "no host",
+ specification.port or "no port"
+ )
+ connection = connect(session,specification)
+ if connection then
+ break
+ end
end
- cache[id] = { session = session, connection = connection }
- end
- else
- session = initializesession()
- if not session then
- return "no session"
end
- connection = connect(session,specification)
- if not connection then
- return "no connection"
+ if connection then
+ cache[id] = { session = session, connection = connection }
end
end
+ else
+ session = initialize()
+ connection = connect(session,specification)
if not connection then
- report_state("error in connection: %s@%s to %s:%s",
+ for i=1,nofretries do
+ sleep(retrydelay)
+ report_state("retrying to connect: [%s] %s@%s to %s:%s",
+ i,
+ specification.database or "no database",
+ specification.username or "no username",
+ specification.host or "no host",
+ specification.port or "no port"
+ )
+ connection = connect(session,specification)
+ if connection then
+ break
+ end
+ end
+ end
+ end
+ if not connection then
+ report_state("error in connection: %s@%s to %s:%s",
specification.database or "no database",
specification.username or "no username",
specification.host or "no host",
specification.port or "no port"
)
- return "no connection"
- end
- query = lpegmatch(querysplitter,query)
- local result, okay
- for i=1,#query do
- local q = query[i]
- local r, m = connection:execute(q)
- if m then
- report_state("error in query to host %a: %s",specification.host,string.collapsespaces(q or "?"))
- if m then
- report_state("message: %s",m)
- end
- end
- local t = type(r)
- if t == "table" then
- result = r
- okay = true
- elseif t == "number" then
- okay = true
- end
- end
- if not okay then -- can go
- -- why do we close a session
- if connection then
- connection:close()
- end
- if session then
- session:close()
- end
- if id then
- cache[id] = nil
- end
- return "execution error"
+ return { }, { }
+ end
+ query = lpegmatch(querysplitter,query)
+ local result, message, okay
+ for i=1,#query do
+ local q = query[i]
+ local r, m = connection:execute(q)
+ if m then
+ report_state("error in query, stage: %s",string.collapsespaces(q or "?"))
+ message = message and format("%s\n%s",message,m) or m
end
- local data, keys
- if result then
- if converter then
- data = converter.ffi(result)
- else
- local _result_ = result._result_
- local noffields = result.noffields
- local nofrows = result.nofrows
- keys = result:getcolnames()
- data = { }
- if noffields > 0 and nofrows > 0 then
- for i=1,nofrows do
- local cells = { }
- local row = mysql_fetch_row(_result_)
- for j=1,noffields do
- local s = row[j-1]
- local k = keys[j]
- if s == NULL then
- cells[k] = ""
- else
- cells[k] = ffi_tostring(s)
- end
- end
- data[i] = cells
- end
- end
- end
- result:close()
+ if type(r) == "table" then
+ result = r
+ okay = true
+ elseif not m then
+ okay = true
end
- --
- if not id then
- if connection then
- connection:close()
- end
- if session then
- session:close()
+ end
+ local data, keys
+ if result then
+ if converter then
+ data = converter.ffi(result)
+ else
+ keys = result.names
+ data = { }
+ for i=1,result.nofrows do
+ data[i] = result:hash()
end
end
- return false, data, keys
+ result:finish() -- result:close()
+ elseif message then
+ report_state("message %s",message)
end
-
- local function datafetched(specification,query,converter)
- local callokay, connectionerror, data, keys = pcall(fetched,specification,query,converter)
- if not callokay then
- report_state("call error, retrying")
- callokay, connectionerror, data, keys = pcall(fetched,specification,query,converter)
- elseif connectionerror then
- report_state("error: %s, retrying",connectionerror)
- callokay, connectionerror, data, keys = pcall(fetched,specification,query,converter)
- end
- if not callokay then
- report_state("persistent call error")
- elseif connectionerror then
- report_state("persistent error: %s",connectionerror)
- end
- return data or { }, keys or { }
+ if not keys then
+ keys = { }
end
-
- executequery = function(specification)
- if trace_sql then
- report_state("executing library")
- end
- if not validspecification(specification) then
- report_state("error in specification")
- return
- end
- local query = dataprepared(specification)
- if not query then
- report_state("error in preparation")
- return
- end
- local data, keys = datafetched(specification,query,specification.converter)
- if not data then
- report_state("error in fetching")
- return
- end
- local one = data[1]
- if one then
- setmetatable(data,{ __index = one } )
- end
- return data, keys
+ if not data then
+ data = { }
+ end
+ if not id then
+ connection:close()
+ session:close()
end
+ return data, keys
+end
+local function execute(specification)
+ if trace_sql then
+ report_state("executing library")
+ end
+ if not validspecification(specification) then
+ report_state("error in specification")
+ return
+ end
+ local query = dataprepared(specification)
+ if not query then
+ report_state("error in preparation")
+ return
+ end
+ local data, keys = datafetched(specification,query,specification.converter)
+ if not data then
+ report_state("error in fetching")
+ return
+ end
+ local one = data[1]
+ if one then
+ setmetatable(data,{ __index = one } )
+ end
+ return data, keys
end
local wraptemplate = [[
@@ -581,14 +530,13 @@ return function(result)
if not result then
return { }
end
- local nofrows = result.nofrows
+ local nofrows = result.nofrows or 0
if nofrows == 0 then
return { }
end
- local noffields = result.noffields
- local target = { } -- no %s needed here
+ local noffields = result.noffields or 0
local _result_ = result._result_
- -- we can share cells
+ local target = { } -- no %s needed here
for i=1,nofrows do
local cells = { }
local row = mysql_fetch_row(_result_)
@@ -604,7 +552,7 @@ return function(result)
%s
}
end
- result:close()
+ result:finish() -- result:close()
return target
end
]]
@@ -612,9 +560,9 @@ end
local celltemplate = "cells[%s]"
methods.ffi = {
- runner = function() end, -- never called
- execute = executequery,
- initialize = initializesession, -- returns session
+ runner = function() end, -- never called
+ execute = execute,
+ initialize = initialize, -- returns session
usesfiles = false,
wraptemplate = wraptemplate,
celltemplate = celltemplate,