1if not modules then modules = { } end modules ['util-sql-imp-library'] = {
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
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65local format = string.format
66local lpegmatch = lpeg.match
67local setmetatable, type = setmetatable, type
68
69local trace_sql = false trackers.register("sql.trace", function(v) trace_sql = v end)
70local trace_queries = false trackers.register("sql.queries",function(v) trace_queries = v end)
71local report_state = logs.reporter("sql","library")
72
73local sql = utilities.sql
74local mysql = require("luasql.mysql")
75local cache = { }
76local helpers = sql.helpers
77local methods = sql.methods
78local validspecification = helpers.validspecification
79local querysplitter = helpers.querysplitter
80local dataprepared = helpers.preparetemplate
81local serialize = sql.serialize
82local deserialize = sql.deserialize
83local formatters = string.formatters
84
85local initialize = mysql.mysql
86
87local function connect(session,specification)
88 return session:connect(
89 specification.database or "",
90 specification.username or "",
91 specification.password or "",
92 specification.host or "",
93 specification.port
94 )
95end
96
97local function fetched(specification,query,converter)
98 if not query or query == "" then
99 report_state("no valid query")
100 return false
101 end
102 local id = specification.id
103 local session, connection
104 if id then
105 local c = cache[id]
106 if c then
107 session = c.session
108 connection = c.connection
109 end
110 if not connection then
111 session = initialize()
112 if not session then
113 return formatters["no session for %a"](id)
114 end
115 connection = connect(session,specification)
116 if not connection then
117 return formatters["no connection for %a"](id)
118 end
119 cache[id] = { session = session, connection = connection }
120 end
121 else
122 session = initialize()
123 if not session then
124 return "no session"
125 end
126 connection = connect(session,specification)
127 if not connection then
128 return "no connection"
129 end
130 end
131 if not connection then
132 report_state("error in connection: %s@%s to %s:%s",
133 specification.database or "no database",
134 specification.username or "no username",
135 specification.host or "no host",
136 specification.port or "no port"
137 )
138 return "no connection"
139 end
140 query = lpegmatch(querysplitter,query)
141 local result, okay
142 for i=1,#query do
143 local q = query[i]
144 local r, m = connection:execute(q)
145 if m then
146 report_state("error in query to host %a: %s",specification.host,string.collapsespaces(q or "?"))
147 if m then
148 report_state("message: %s",m)
149 end
150 end
151 local t = type(r)
152 if t == "userdata" then
153 result = r
154 okay = true
155 elseif t == "number" then
156 okay = true
157 end
158 end
159 if not okay then
160 if connection then
161 connection:close()
162 end
163 if session then
164 session:close()
165 end
166 if id then
167 cache[id] = nil
168 end
169 return "execution error"
170 end
171 local data, keys
172 if result then
173 if converter then
174 data = converter.library(result)
175 else
176 keys = result:getcolnames()
177 if keys then
178 data = { }
179 local n = result:numrows() or 0
180 if n > 0 then
181 local k = #keys
182 for i=1,n do
183 local v = { result:fetch() }
184 local d = { }
185 for i=1,k do
186 d[keys[i]] = v[i]
187 end
188 data[#data+1] = d
189 end
190 end
191 end
192 end
193 result:close()
194 end
195 if not id then
196 if connection then
197 connection:close()
198 end
199 if session then
200 session:close()
201 end
202 end
203 return false, data, keys
204end
205
206local function datafetched(specification,query,converter)
207 local callokay, connectionerror, data, keys = pcall(fetched,specification,query,converter)
208 if not callokay then
209 report_state("call error, retrying")
210 callokay, connectionerror, data, keys = pcall(fetched,specification,query,converter)
211 elseif connectionerror then
212 report_state("error: %s, retrying",connectionerror)
213 callokay, connectionerror, data, keys = pcall(fetched,specification,query,converter)
214 end
215 if not callokay then
216 report_state("persistent call error")
217 elseif connectionerror then
218 report_state("persistent error: %s",connectionerror)
219 end
220 return data or { }, keys or { }
221end
222
223local function execute(specification)
224 if trace_sql then
225 report_state("executing library")
226 end
227 if not validspecification(specification) then
228 report_state("error in specification")
229 return
230 end
231 local query = dataprepared(specification)
232 if not query then
233 report_state("error in preparation")
234 return
235 end
236 local data, keys = datafetched(specification,query,specification.converter)
237 if not data then
238 report_state("error in fetching")
239 return
240 end
241 local one = data[1]
242 if one then
243 setmetatable(data,{ __index = one } )
244 end
245 return data, keys
246end
247
248
249
250
251local wraptemplate = [[
252local converters = utilities.sql.converters
253local deserialize = utilities.sql.deserialize
254
255local tostring = tostring
256local tonumber = tonumber
257local booleanstring = string.booleanstring
258
259%s
260
261return function(result)
262 if not result then
263 return { }
264 end
265 local nofrows = result:numrows() or 0
266 if nofrows == 0 then
267 return { }
268 end
269 local target = { } -- no %s needed here
270 for i=1,nofrows do
271 local cells = { result:fetch() }
272 target[%s] = {
273 %s
274 }
275 end
276 return target
277end
278]]
279
280local celltemplate = "cells[%s]"
281
282methods.library = {
283 runner = function() end,
284 execute = execute,
285 initialize = initialize,
286 usesfiles = false,
287 wraptemplate = wraptemplate,
288 celltemplate = celltemplate,
289}
290 |