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 formatters = string.formatters
83
84local initialize = mysql.mysql
85
86local function connect(session,specification)
87 return session:connect(
88 specification.database or "",
89 specification.username or "",
90 specification.password or "",
91 specification.host or "",
92 specification.port
93 )
94end
95
96local function fetched(specification,query,converter)
97 if not query or query == "" then
98 report_state("no valid query")
99 return false
100 end
101 local id = specification.id
102 local session, connection
103 if id then
104 local c = cache[id]
105 if c then
106 session = c.session
107 connection = c.connection
108 end
109 if not connection then
110 session = initialize()
111 if not session then
112 return formatters["no session for %a"](id)
113 end
114 connection = connect(session,specification)
115 if not connection then
116 return formatters["no connection for %a"](id)
117 end
118 cache[id] = { session = session, connection = connection }
119 end
120 else
121 session = initialize()
122 if not session then
123 return "no session"
124 end
125 connection = connect(session,specification)
126 if not connection then
127 return "no connection"
128 end
129 end
130 if not connection then
131 report_state("error in connection: %s@%s to %s:%s",
132 specification.database or "no database",
133 specification.username or "no username",
134 specification.host or "no host",
135 specification.port or "no port"
136 )
137 return "no connection"
138 end
139 query = lpegmatch(querysplitter,query)
140 local result, okay
141 for i=1,#query do
142 local q = query[i]
143 local r, m = connection:execute(q)
144 if m then
145 report_state("error in query to host %a: %s",specification.host,string.collapsespaces(q or "?"))
146 if m then
147 report_state("message: %s",m)
148 end
149 end
150 local t = type(r)
151 if t == "userdata" then
152 result = r
153 okay = true
154 elseif t == "number" then
155 okay = true
156 end
157 end
158 if not okay then
159 if connection then
160 connection:close()
161 end
162 if session then
163 session:close()
164 end
165 if id then
166 cache[id] = nil
167 end
168 return "execution error"
169 end
170 local data, keys
171 if result then
172 if converter then
173 data = converter.library(result)
174 else
175 keys = result:getcolnames()
176 if keys then
177 data = { }
178 local n = result:numrows() or 0
179 if n > 0 then
180 local k = #keys
181 for i=1,n do
182 local v = { result:fetch() }
183 local d = { }
184 for i=1,k do
185 d[keys[i]] = v[i]
186 end
187 data[#data+1] = d
188 end
189 end
190 end
191 end
192 result:close()
193 end
194 if not id then
195 if connection then
196 connection:close()
197 end
198 if session then
199 session:close()
200 end
201 end
202 return false, data, keys
203end
204
205local function datafetched(specification,query,converter)
206 local callokay, connectionerror, data, keys = pcall(fetched,specification,query,converter)
207 if not callokay then
208 report_state("call error, retrying")
209 callokay, connectionerror, data, keys = pcall(fetched,specification,query,converter)
210 elseif connectionerror then
211 report_state("error: %s, retrying",connectionerror)
212 callokay, connectionerror, data, keys = pcall(fetched,specification,query,converter)
213 end
214 if not callokay then
215 report_state("persistent call error")
216 elseif connectionerror then
217 report_state("persistent error: %s",connectionerror)
218 end
219 return data or { }, keys or { }
220end
221
222local function execute(specification)
223 if trace_sql then
224 report_state("executing library")
225 end
226 if not validspecification(specification) then
227 report_state("error in specification")
228 return
229 end
230 local query = dataprepared(specification)
231 if not query then
232 report_state("error in preparation")
233 return
234 end
235 local data, keys = datafetched(specification,query,specification.converter)
236 if not data then
237 report_state("error in fetching")
238 return
239 end
240 local one = data[1]
241 if one then
242 setmetatable(data,{ __index = one } )
243 end
244 return data, keys
245end
246
247
248
249
250local wraptemplate = [[
251local converters = utilities.sql.converters
252local deserialize = utilities.sql.deserialize
253local fromjson = utilities.sql.fromjson
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 |