libs-imp-mysql.lmt /size: 6705 b    last modification: 2024-01-16 09:02
1if not modules then modules = { } end modules ['libs-imp-mysql'] = {
2    version   = 1.001,
3    comment   = "companion to util-sql.lua",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9-- c:/data/develop/tex-context/tex/texmf-win64/bin/lib/luametatex/lua/copies/mysql/libmysql.dll
10
11local libname = "mysql"
12local libfile = "libmysql"
13
14local mysqllib = resolvers.libraries.validoptional(libname)
15
16if not mysqllib then return end
17
18local function okay()
19    if resolvers.libraries.optionalloaded(libname,libfile) then
20        okay = function() return true end
21    else
22        okay = function() return false end
23    end
24    return okay()
25end
26
27local lpegmatch = lpeg.match
28local setmetatable = setmetatable
29
30local sql                = utilities.sql or require("util-sql")
31local report             = logs.reporter(libname)
32
33local trace_sql          = false  trackers.register("sql.trace",  function(v) trace_sql     = v end)
34local trace_queries      = false  trackers.register("sql.queries",function(v) trace_queries = v end)
35
36local mysql_open         = mysqllib.open
37local mysql_close        = mysqllib.close
38local mysql_execute      = mysqllib.execute
39local mysql_getmessage   = mysqllib.getmessage
40
41local helpers            = sql.helpers
42local methods            = sql.methods
43local validspecification = helpers.validspecification
44local preparetemplate    = helpers.preparetemplate
45local querysplitter      = helpers.querysplitter
46local cache              = { }
47local timeout         -- = 3600 -- to be tested
48
49local function connect(specification)
50    local db = mysql_open(
51        specification.database or "",
52        specification.username or "",
53        specification.password or "",
54        specification.host     or "",
55        specification.port
56    )
57    if db and timeout then
58        mysql_execute(db,formatters["SET SESSION connect_timeout=%s ;"](timeout))
59    end
60    return db
61end
62
63local function execute_once(specification,retry)
64    if okay() then
65        if trace_sql then
66            report("executing mysql")
67        end
68        if not validspecification(specification) then
69            report("error in specification")
70        end
71        local query = preparetemplate(specification)
72        if not query then
73            report("error in preparation")
74            return
75        else
76            query = lpegmatch(querysplitter,query)
77        end
78        local base = specification.database -- or specification.presets and specification.presets.database
79        if not base then
80            report("no database specified")
81            return
82        end
83        local result = { }
84        local keys   = { }
85        local id     = specification.id
86        local db     = nil
87        if id then
88            local session = cache[id]
89            if session then
90                db = session.db
91            else
92                db = connect(specification)
93                if not db then
94                    report("no session database specified")
95                else
96                    cache[id] = {
97                        specification = specification,
98                        db            = db,
99                    }
100                end
101            end
102        else
103            db = connect(specification)
104        end
105        if not db then
106            report("no database opened")
107        else
108            local converter = specification.converter
109            local nofrows   = 0
110            local callback  = nil
111            if converter then
112                local convert = converter.mysql
113                callback = function(nofcolumns,values,fields)
114                    nofrows = nofrows + 1
115                    result[nofrows] = convert(values)
116                end
117            else
118                callback = function(nofcolumns,values,fields)
119                    local column = { }
120                    for i=1,nofcolumns do
121                        local field
122                        if fields then
123                            field = fields[i]
124                            keys[i] = field
125                        else
126                            field = keys[i]
127                        end
128                        if field then
129                            column[field] = values[i]
130                        end
131                    end
132                    nofrows  = nofrows + 1
133                    result[nofrows] = column
134                end
135            end
136            for i=1,#query do
137                local okay = mysql_execute(db,query[i],callback)
138                if not okay then
139                    if id and retry and i == 1 then
140                        report("error: %s, retrying to connect",mysql_getmessage(db))
141                        mysql_close(db)
142                        cache[id] = nil
143                        return execute_once(specification,false)
144                    else
145                        report("error: %s",mysql_getmessage(db))
146                    end
147                end
148            end
149        end
150        if db and not id then
151            mysql_close(db)
152        end
153        -- bonus
154        local one = result[1]
155        if one then
156            setmetatable(result,{ __index = one } )
157        end
158        --
159        return result, keys
160    else
161        report("error: ","no library loaded")
162    end
163end
164
165local function execute(specification)
166    return execute_once(specification,true)
167end
168
169-- Here we build the dataset stepwise so we don't use the data hack that
170-- is used in the client variant.
171
172local wraptemplate = [[
173local converters    = utilities.sql.converters
174local deserialize   = utilities.sql.deserialize
175local fromjson      = utilities.sql.fromjson
176
177local tostring      = tostring
178local tonumber      = tonumber
179local booleanstring = string.booleanstring
180
181%s
182
183return function(cells)
184    -- %s (not needed)
185    -- %s (not needed)
186    return {
187        %s
188    }
189end
190]]
191
192-- return function(result)
193--     if not result then
194--         return { }
195--     end
196--     local nofrows = #result
197--     if nofrows == 0 then
198--         return { }
199--     end
200--     local target = { } -- no %s needed here
201--     for i=1,nofrows do
202--         target[%s] = {
203--             %s
204--         }
205--     end
206--     return result
207-- end
208
209local celltemplate = "cells[%s]"
210
211methods.mysql = {
212    execute      = execute,
213    usesfiles    = false,
214    wraptemplate = wraptemplate,
215    celltemplate = celltemplate,
216}
217
218package.loaded["util-sql-imp-ffi"]     = methods.mysql
219package.loaded["util-sql-imp-mysql"]   = methods.mysql
220package.loaded["util-sql-imp-library"] = methods.mysql
221package.loaded[libname]                = methods.mysql
222