From 81095dd1bf20eb5f7e126adbdc8047f940504180 Mon Sep 17 00:00:00 2001 From: Hans Hagen Date: Mon, 20 Jul 2020 11:09:33 +0200 Subject: 2020-07-20 10:42:00 --- tex/context/base/mkiv/libs-imp-mysql.lmt | 220 +++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 tex/context/base/mkiv/libs-imp-mysql.lmt (limited to 'tex/context/base/mkiv/libs-imp-mysql.lmt') diff --git a/tex/context/base/mkiv/libs-imp-mysql.lmt b/tex/context/base/mkiv/libs-imp-mysql.lmt new file mode 100644 index 000000000..3e938a6de --- /dev/null +++ b/tex/context/base/mkiv/libs-imp-mysql.lmt @@ -0,0 +1,220 @@ +if not modules then modules = { } end modules ['libs-imp-mysql'] = { + version = 1.001, + comment = "companion to util-sql.lua", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- c:/data/develop/tex-context/tex/texmf-win64/bin/lib/luametatex/lua/copies/mysql/libmysql.dll + +local libname = "mysql" +local libfile = "libmysql" + +local mysqllib = resolvers.libraries.validoptional(libname) + +if not mysqllib then return end + +local function okay() + if resolvers.libraries.optionalloaded(libname,libfile) then + okay = function() return true end + else + okay = function() return false end + end + return okay() +end + +local lpegmatch = lpeg.match +local setmetatable = setmetatable + +local sql = utilities.sql or require("util-sql") +local report = logs.reporter(libname) + +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) + +local mysql_open = mysqllib.open +local mysql_close = mysqllib.close +local mysql_execute = mysqllib.execute +local mysql_getmessage = mysqllib.getmessage + +local helpers = sql.helpers +local methods = sql.methods +local validspecification = helpers.validspecification +local preparetemplate = helpers.preparetemplate +local querysplitter = helpers.querysplitter +local cache = { } +local timeout -- = 3600 -- to be tested + +local function connect(specification) + local db = mysql_open( + specification.database or "", + specification.username or "", + specification.password or "", + specification.host or "", + specification.port + ) + if db and timeout then + mysql_execute(db,formatters["SET SESSION connect_timeout=%s ;"](timeout)) + end + return db +end + +local function execute_once(specification,retry) + if okay() then + if trace_sql then + report("executing mysql") + end + if not validspecification(specification) then + report("error in specification") + end + local query = preparetemplate(specification) + if not query then + report("error in preparation") + return + else + query = lpegmatch(querysplitter,query) + end + local base = specification.database -- or specification.presets and specification.presets.database + if not base then + report("no database specified") + return + end + local result = { } + local keys = { } + local id = specification.id + local db = nil + if id then + local session = cache[id] + if session then + db = session.db + else + db = connect(specification) + if not db then + report("no session database specified") + else + cache[id] = { + specification = specification, + db = db, + } + end + end + else + db = connect(specification) + end + if not db then + report("no database opened") + else + local converter = specification.converter + local nofrows = 0 + local callback = nil + if converter then + local convert = converter.mysql + callback = function(nofcolumns,values,fields) + nofrows = nofrows + 1 + result[nofrows] = convert(values) + end + else + callback = function(nofcolumns,values,fields) + local column = { } + for i=1,nofcolumns do + local field + if fields then + field = fields[i] + keys[i] = field + else + field = keys[i] + end + if field then + column[field] = values[i] + end + end + nofrows = nofrows + 1 + result[nofrows] = column + end + end + for i=1,#query do + local okay = mysql_execute(db,query[i],callback) + if not okay then + if id and retry and i == 1 then + report("error: %s, retrying to connect",mysql_getmessage(db)) + mysql_close(db) + cache[id] = nil + return execute_once(specification,false) + else + report("error: %s",mysql_getmessage(db)) + end + end + end + end + if db and not id then + mysql_close(db) + end + -- bonus + local one = result[1] + if one then + setmetatable(result,{ __index = one } ) + end + -- + return result, keys + else + report("error: ","no library loaded") + end +end + +local function execute(specification) + return execute_once(specification,true) +end + +-- Here we build the dataset stepwise so we don't use the data hack that +-- is used in the client variant. + +local wraptemplate = [[ +local converters = utilities.sql.converters +local deserialize = utilities.sql.deserialize + +local tostring = tostring +local tonumber = tonumber +local booleanstring = string.booleanstring + +%s + +return function(cells) + -- %s (not needed) + -- %s (not needed) + return { + %s + } +end +]] + +-- return function(result) +-- if not result then +-- return { } +-- end +-- local nofrows = #result +-- if nofrows == 0 then +-- return { } +-- end +-- local target = { } -- no %s needed here +-- for i=1,nofrows do +-- target[%s] = { +-- %s +-- } +-- end +-- return result +-- end + +local celltemplate = "cells[%s]" + +methods.mysql = { + execute = execute, + usesfiles = false, + wraptemplate = wraptemplate, + celltemplate = celltemplate, +} + +package.loaded["util-sql-imp-ffi"] = methods.mysql +package.loaded["util-sql-imp-mysql"] = methods.mysql +package.loaded["util-sql-imp-library"] = methods.mysql +package.loaded[libname] = methods.mysql -- cgit v1.2.3