rev |
line source |
jbe/bsw@0
|
1 #!/usr/bin/env lua
|
jbe/bsw@0
|
2
|
jbe/bsw@0
|
3 local _G = _G
|
jbe/bsw@0
|
4 local _VERSION = _VERSION
|
jbe/bsw@0
|
5 local assert = assert
|
jbe/bsw@0
|
6 local error = error
|
jbe/bsw@0
|
7 local getmetatable = getmetatable
|
jbe/bsw@0
|
8 local ipairs = ipairs
|
jbe/bsw@0
|
9 local next = next
|
jbe/bsw@0
|
10 local pairs = pairs
|
jbe/bsw@0
|
11 local print = print
|
jbe/bsw@0
|
12 local rawequal = rawequal
|
jbe/bsw@0
|
13 local rawget = rawget
|
jbe@64
|
14 local rawlen = rawlen
|
jbe/bsw@0
|
15 local rawset = rawset
|
jbe/bsw@0
|
16 local select = select
|
jbe/bsw@0
|
17 local setmetatable = setmetatable
|
jbe/bsw@0
|
18 local tonumber = tonumber
|
jbe/bsw@0
|
19 local tostring = tostring
|
jbe/bsw@0
|
20 local type = type
|
jbe/bsw@0
|
21
|
jbe/bsw@0
|
22 local math = math
|
jbe/bsw@0
|
23 local string = string
|
jbe@64
|
24 local table = table
|
jbe/bsw@0
|
25
|
jbe/bsw@0
|
26 local mondelefant = require("mondelefant")
|
jbe/bsw@0
|
27 local atom = require("atom")
|
jbe@177
|
28 local json = require("json")
|
jbe/bsw@0
|
29
|
jbe@64
|
30 local _M = {}
|
jbe@64
|
31 if _ENV then
|
jbe@64
|
32 _ENV = _M
|
jbe@64
|
33 else
|
jbe@64
|
34 _G[...] = _M
|
jbe@64
|
35 setfenv(1, _M)
|
jbe@64
|
36 end
|
jbe/bsw@0
|
37
|
jbe/bsw@0
|
38
|
jbe/bsw@0
|
39 input_converters = setmetatable({}, { __mode = "k" })
|
jbe/bsw@0
|
40
|
jbe@549
|
41 input_converters["boolean"] = function(conn, value, rawtext_mode)
|
jbe@549
|
42 if rawtext_mode then
|
jbe@549
|
43 if value then return "t" else return "f" end
|
jbe@549
|
44 else
|
jbe@549
|
45 if value then return "TRUE" else return "FALSE" end
|
jbe@549
|
46 end
|
jbe/bsw@0
|
47 end
|
jbe/bsw@0
|
48
|
jbe@549
|
49 input_converters["number"] = function(conn, value, rawtext_mode)
|
jbe@490
|
50 if _VERSION == "Lua 5.2" then
|
jbe@490
|
51 -- TODO: remove following compatibility hack to allow large integers (e.g. 1e14) in Lua 5.2
|
jbe@490
|
52 local integer_string = string.format("%i", value)
|
jbe@490
|
53 if tonumber(integer_string) == value then
|
jbe@490
|
54 return integer_string
|
jbe@419
|
55 else
|
jbe@490
|
56 local number_string = tostring(value)
|
jbe@490
|
57 if string.find(number_string, "^[0-9.e+-]+$") then
|
jbe@490
|
58 return number_string
|
jbe@490
|
59 else
|
jbe@549
|
60 if rawtext_mode then return "NaN" else return "'NaN'" end
|
jbe@490
|
61 end
|
jbe@419
|
62 end
|
jbe/bsw@0
|
63 end
|
jbe@490
|
64 local integer = math.tointeger(value)
|
jbe@490
|
65 if integer then
|
jbe@490
|
66 return tostring(integer)
|
jbe@490
|
67 end
|
jbe@490
|
68 local str = tostring(value)
|
jbe@490
|
69 if string.find(str, "^[0-9.e+-]+$") then
|
jbe@490
|
70 return str
|
jbe@490
|
71 end
|
jbe@549
|
72 if rawtext_mode then return "NaN" else return "'NaN'" end
|
jbe/bsw@0
|
73 end
|
jbe/bsw@0
|
74
|
jbe@549
|
75 input_converters[atom.fraction] = function(conn, value, rawtext_mode)
|
jbe/bsw@0
|
76 if value.invalid then
|
jbe@549
|
77 if rawtext_mode then return "NaN" else return "'NaN'" end
|
jbe/bsw@0
|
78 else
|
jbe/bsw@0
|
79 local n, d = tostring(value.numerator), tostring(value.denominator)
|
jbe/bsw@0
|
80 if string.find(n, "^%-?[0-9]+$") and string.find(d, "^%-?[0-9]+$") then
|
jbe@549
|
81 if rawtext_mode then
|
jbe@549
|
82 return n .. "/" .. d
|
jbe@549
|
83 else
|
jbe@549
|
84 return "(" .. n .. "::numeric / " .. d .. "::numeric)"
|
jbe@549
|
85 end
|
jbe/bsw@0
|
86 else
|
jbe@549
|
87 if rawtext_mode then return "NaN" else return "'NaN'" end
|
jbe/bsw@0
|
88 end
|
jbe/bsw@0
|
89 end
|
jbe/bsw@0
|
90 end
|
jbe/bsw@0
|
91
|
jbe@549
|
92 input_converters[atom.date] = function(conn, value, rawtext_mode)
|
jbe@549
|
93 if rawtext_mode then
|
jbe@549
|
94 return tostring(value)
|
jbe@549
|
95 else
|
jbe@549
|
96 return conn:quote_string(tostring(value)) .. "::date"
|
jbe@549
|
97 end
|
jbe/bsw@0
|
98 end
|
jbe/bsw@0
|
99
|
jbe@549
|
100 input_converters[atom.timestamp] = function(conn, value, rawtext_mode)
|
jbe@549
|
101 if rawtext_mode then
|
jbe@549
|
102 return tostring(value)
|
jbe@549
|
103 else
|
jbe@549
|
104 return conn:quote_string(tostring(value)) -- don't define type
|
jbe@549
|
105 end
|
jbe/bsw@0
|
106 end
|
jbe/bsw@0
|
107
|
jbe@549
|
108 input_converters[atom.time] = function(conn, value, rawtext_mode)
|
jbe@549
|
109 if rawtext_mode then
|
jbe@549
|
110 return tostring(value)
|
jbe@549
|
111 else
|
jbe@549
|
112 return conn:quote_string(tostring(value)) .. "::time"
|
jbe@549
|
113 end
|
jbe/bsw@0
|
114 end
|
jbe/bsw@0
|
115
|
jbe@556
|
116 input_converters["rawtable"] = function(conn, value, rawtext_mode)
|
jbe@549
|
117 -- treat tables as arrays
|
jbe@548
|
118 local parts = { "{" }
|
jbe@548
|
119 for i, v in ipairs(value) do
|
jbe@548
|
120 if i > 1 then parts[#parts+1] = "," end
|
jbe@549
|
121 local converter =
|
jbe@549
|
122 input_converters[getmetatable(v)] or
|
jbe@549
|
123 input_converters[type(v)]
|
jbe@549
|
124 if converter then
|
jbe@549
|
125 v = converter(conn, v, true)
|
jbe@549
|
126 else
|
jbe@549
|
127 v = tostring(v)
|
jbe@549
|
128 end
|
jbe@548
|
129 parts[#parts+1] = '"'
|
jbe@549
|
130 parts[#parts+1] = string.gsub(v, '[\\"]', '\\%0')
|
jbe@548
|
131 parts[#parts+1] = '"'
|
jbe@548
|
132 end
|
jbe@548
|
133 parts[#parts+1] = "}"
|
jbe@548
|
134 return conn:quote_string(table.concat(parts))
|
jbe@548
|
135 end
|
jbe@548
|
136
|
jbe/bsw@0
|
137
|
jbe/bsw@0
|
138 output_converters = setmetatable({}, { __mode = "k" })
|
jbe/bsw@0
|
139
|
jbe/bsw@0
|
140 output_converters.int8 = function(str) return atom.integer:load(str) end
|
jbe/bsw@0
|
141 output_converters.int4 = function(str) return atom.integer:load(str) end
|
jbe/bsw@0
|
142 output_converters.int2 = function(str) return atom.integer:load(str) end
|
jbe/bsw@0
|
143
|
jbe/bsw@0
|
144 output_converters.numeric = function(str) return atom.number:load(str) end
|
jbe/bsw@0
|
145 output_converters.float4 = function(str) return atom.number:load(str) end
|
jbe/bsw@0
|
146 output_converters.float8 = function(str) return atom.number:load(str) end
|
jbe/bsw@0
|
147
|
jbe/bsw@0
|
148 output_converters.bool = function(str) return atom.boolean:load(str) end
|
jbe/bsw@0
|
149
|
jbe/bsw@0
|
150 output_converters.date = function(str) return atom.date:load(str) end
|
jbe/bsw@0
|
151
|
jbe@548
|
152 local function timestamp_loader_func(str)
|
jbe@1
|
153 local year, month, day, hour, minute, second = string.match(
|
jbe/bsw@0
|
154 str,
|
jbe@1
|
155 "^([0-9][0-9][0-9][0-9])%-([0-9][0-9])%-([0-9][0-9]) ([0-9]?[0-9]):([0-9][0-9]):([0-9][0-9])"
|
jbe/bsw@0
|
156 )
|
jbe@1
|
157 if year then
|
jbe/bsw@0
|
158 return atom.timestamp{
|
jbe@1
|
159 year = tonumber(year),
|
jbe@1
|
160 month = tonumber(month),
|
jbe@1
|
161 day = tonumber(day),
|
jbe/bsw@0
|
162 hour = tonumber(hour),
|
jbe/bsw@0
|
163 minute = tonumber(minute),
|
jbe/bsw@0
|
164 second = tonumber(second)
|
jbe/bsw@0
|
165 }
|
jbe/bsw@0
|
166 else
|
jbe/bsw@0
|
167 return atom.timestamp.invalid
|
jbe/bsw@0
|
168 end
|
jbe/bsw@0
|
169 end
|
jbe/bsw@0
|
170 output_converters.timestamp = timestamp_loader_func
|
jbe/bsw@0
|
171 output_converters.timestamptz = timestamp_loader_func
|
jbe/bsw@0
|
172
|
jbe@548
|
173 local function time_loader_func(str)
|
jbe@1
|
174 local hour, minute, second = string.match(
|
jbe/bsw@0
|
175 str,
|
jbe@1
|
176 "^([0-9]?[0-9]):([0-9][0-9]):([0-9][0-9])"
|
jbe/bsw@0
|
177 )
|
jbe@1
|
178 if hour then
|
jbe/bsw@0
|
179 return atom.time{
|
jbe/bsw@0
|
180 hour = tonumber(hour),
|
jbe/bsw@0
|
181 minute = tonumber(minute),
|
jbe/bsw@0
|
182 second = tonumber(second)
|
jbe/bsw@0
|
183 }
|
jbe/bsw@0
|
184 else
|
jbe/bsw@0
|
185 return atom.time.invalid
|
jbe/bsw@0
|
186 end
|
jbe/bsw@0
|
187 end
|
jbe/bsw@0
|
188 output_converters.time = time_loader_func
|
jbe/bsw@0
|
189 output_converters.timetz = time_loader_func
|
jbe/bsw@0
|
190
|
jbe@548
|
191 local function json_loader_func(str)
|
jbe@178
|
192 return assert(json.import(str))
|
jbe@178
|
193 end
|
jbe@178
|
194 output_converters.json = json_loader_func
|
jbe@178
|
195 output_converters.jsonb = json_loader_func
|
jbe@176
|
196
|
jbe/bsw@0
|
197 mondelefant.postgresql_connection_prototype.type_mappings = {
|
jbe/bsw@0
|
198 int8 = atom.integer,
|
jbe/bsw@0
|
199 int4 = atom.integer,
|
jbe/bsw@0
|
200 int2 = atom.integer,
|
jbe/bsw@0
|
201 bool = atom.boolean,
|
jbe/bsw@0
|
202 date = atom.date,
|
jbe/bsw@0
|
203 timestamp = atom.timestamp,
|
jbe/bsw@0
|
204 time = atom.time,
|
jbe/bsw@0
|
205 text = atom.string,
|
jbe/bsw@0
|
206 varchar = atom.string,
|
jbe@176
|
207 json = json,
|
jbe@176
|
208 jsonb = json,
|
jbe/bsw@0
|
209 }
|
jbe/bsw@0
|
210
|
jbe/bsw@0
|
211
|
jbe/bsw@0
|
212 function mondelefant.postgresql_connection_prototype.input_converter(conn, value, info)
|
jbe/bsw@0
|
213 if value == nil then
|
jbe/bsw@0
|
214 return "NULL"
|
jbe/bsw@0
|
215 else
|
jbe@556
|
216 local mt = getmetatable(value)
|
jbe@556
|
217 local converter = input_converters.mt
|
jbe@556
|
218 if not converter then
|
jbe@556
|
219 local t = type(value)
|
jbe@556
|
220 if t == "table" and mt == nil then
|
jbe@556
|
221 converter = input_converters.rawtable
|
jbe@556
|
222 else
|
jbe@557
|
223 converter = input_converters.t
|
jbe@556
|
224 end
|
jbe@556
|
225 end
|
jbe/bsw@0
|
226 local converter =
|
jbe/bsw@0
|
227 input_converters[getmetatable(value)] or
|
jbe/bsw@0
|
228 input_converters[type(value)]
|
jbe/bsw@0
|
229 if converter then
|
jbe/bsw@0
|
230 return converter(conn, value)
|
jbe/bsw@0
|
231 else
|
jbe/bsw@0
|
232 return conn:quote_string(tostring(value))
|
jbe/bsw@0
|
233 end
|
jbe/bsw@0
|
234 end
|
jbe/bsw@0
|
235 end
|
jbe/bsw@0
|
236
|
jbe/bsw@0
|
237 function mondelefant.postgresql_connection_prototype.output_converter(conn, value, info)
|
jbe/bsw@0
|
238 if value == nil then
|
jbe/bsw@0
|
239 return nil
|
jbe/bsw@0
|
240 else
|
jbe@552
|
241 local array_type = nil
|
jbe@552
|
242 if info.type then
|
jbe@552
|
243 array_type = string.match(info.type, "^(.*)_array$")
|
jbe@552
|
244 end
|
jbe@548
|
245 if array_type then
|
jbe@548
|
246 local result = {}
|
jbe@548
|
247 local count = 0
|
jbe@548
|
248 local inner_converter = output_converters[array_type]
|
jbe@548
|
249 if not inner_converter then
|
jbe@548
|
250 inner_converter = function(x) return x end
|
jbe@548
|
251 end
|
jbe@548
|
252 value = string.match(value, "^{(.*)}$")
|
jbe@548
|
253 if not value then
|
jbe@548
|
254 error("Could not parse database array")
|
jbe@548
|
255 end
|
jbe@548
|
256 local pos = 1
|
jbe@548
|
257 while pos <= #value do
|
jbe@548
|
258 count = count + 1
|
jbe@548
|
259 if string.find(value, '^""$', pos) then
|
jbe@548
|
260 result[count] = inner_converter("")
|
jbe@548
|
261 pos = pos + 2
|
jbe@548
|
262 elseif string.find(value, '^"",', pos) then
|
jbe@548
|
263 result[count] = inner_converter("")
|
jbe@548
|
264 pos = pos + 3
|
jbe@548
|
265 elseif string.find(value, '^"', pos) then
|
jbe@548
|
266 local p1, p2, entry = string.find(value, '^"(.-[^\\])",', pos)
|
jbe@548
|
267 if not p1 then
|
jbe@548
|
268 p1, p2, entry = string.find(value, '^"(.*[^\\])"$', pos)
|
jbe@548
|
269 end
|
jbe@548
|
270 if not entry then error("Could not parse database array") end
|
jbe@548
|
271 entry = string.gsub(entry, "\\(.)", "%1")
|
jbe@548
|
272 result[count] = inner_converter(entry)
|
jbe@548
|
273 pos = p2 + 1
|
jbe@548
|
274 else
|
jbe@548
|
275 local p1, p2, entry = string.find(value, '^(.-),', pos)
|
jbe@548
|
276 if not p1 then p1, p2, entry = string.find(value, '^(.*)$', pos) end
|
jbe@548
|
277 result[count] = inner_converter(entry)
|
jbe@548
|
278 pos = p2 + 1
|
jbe@548
|
279 end
|
jbe@548
|
280 end
|
jbe@548
|
281 return result
|
jbe/bsw@0
|
282 else
|
jbe@548
|
283 local converter = output_converters[info.type]
|
jbe@548
|
284 if converter then
|
jbe@548
|
285 return converter(value)
|
jbe@548
|
286 else
|
jbe@548
|
287 return value
|
jbe@548
|
288 end
|
jbe/bsw@0
|
289 end
|
jbe/bsw@0
|
290 end
|
jbe/bsw@0
|
291 end
|
jbe/bsw@0
|
292
|
jbe@374
|
293
|
jbe@374
|
294 function mondelefant.save_mutability_state(value)
|
jbe@374
|
295 local jsontype = json.type(value)
|
jbe@374
|
296 if jsontype == "object" or jsontype == "array" then
|
jbe@374
|
297 return tostring(value)
|
jbe@374
|
298 end
|
jbe@374
|
299 end
|
jbe@374
|
300
|
jbe@374
|
301 function mondelefant.verify_mutability_state(value, state)
|
jbe@375
|
302 return tostring(value) ~= state
|
jbe@374
|
303 end
|
jbe@374
|
304
|
jbe@375
|
305
|
jbe@64
|
306 return _M
|
jbe@64
|
307
|
jbe/bsw@0
|
308
|
jbe/bsw@0
|
309 --[[
|
jbe/bsw@0
|
310
|
jbe/bsw@0
|
311 db = assert(mondelefant.connect{engine='postgresql', dbname='test'})
|
jbe/bsw@0
|
312 result = db:query{'SELECT ? + 1', atom.date{ year=1999, month=12, day=31}}
|
jbe/bsw@0
|
313 print(result[1][1].year)
|
jbe/bsw@0
|
314
|
jbe/bsw@0
|
315 --]]
|