44
44
sqlreturn (context, val:: Bool ) = sqlreturn (context, Int (val))
45
45
sqlreturn (context, val) = sqlreturn (context, sqlserialize (val))
46
46
47
- # Internal method for generating an SQLite scalar function from
48
- # a Julia function name
49
- function scalarfunc (func, fsym = Symbol (string (func)))
50
- # check if name defined in Base so we don't clobber Base methods
51
- nm = isdefined (Base, fsym) ? :(Base.$ fsym) : fsym
52
- return quote
53
- # nm needs to be a symbol or expr, i.e. :sin or :(Base.sin)
54
- function $ (nm)(
55
- context:: Ptr{Cvoid} ,
56
- nargs:: Cint ,
57
- values:: Ptr{Ptr{Cvoid}} ,
58
- )
59
- args = [sqlvalue (values, i) for i in 1 : nargs]
60
- ret = $ (func)(args... )
61
- sqlreturn (context, ret)
62
- nothing
63
- end
64
- return $ (nm)
65
- end
66
- end
67
- function scalarfunc (expr:: Expr )
68
- f = eval (expr)
69
- return scalarfunc (f)
47
+ function wrap_scalarfunc (
48
+ func,
49
+ context:: Ptr{Cvoid} ,
50
+ nargs:: Cint ,
51
+ values:: Ptr{Ptr{Cvoid}} ,
52
+ )
53
+ args = [sqlvalue (values, i) for i in 1 : nargs]
54
+ ret = func (args... )
55
+ sqlreturn (context, ret)
56
+ nothing
70
57
end
71
58
72
59
# convert a byteptr to an int, assumes little-endian
@@ -82,135 +69,116 @@ function bytestoint(ptr::Ptr{UInt8}, start::Int, len::Int)
82
69
return htol (s)
83
70
end
84
71
85
- function stepfunc (init, func, fsym = Symbol (string (func) * " _step" ))
86
- nm = isdefined (Base, fsym) ? :(Base.$ fsym) : fsym
87
- return quote
88
- function $ (nm)(
89
- context:: Ptr{Cvoid} ,
90
- nargs:: Cint ,
91
- values:: Ptr{Ptr{Cvoid}} ,
92
- )
93
- args = [sqlvalue (values, i) for i in 1 : nargs]
94
-
95
- intsize = sizeof (Int)
96
- ptrsize = sizeof (Ptr)
97
- acsize = intsize + ptrsize
98
- acptr = convert (
99
- Ptr{UInt8},
100
- C. sqlite3_aggregate_context (context, acsize),
101
- )
102
-
103
- # acptr will be zeroed-out if this is the first iteration
104
- ret = ccall (
105
- :memcmp ,
106
- Cint,
107
- (Ptr{UInt8}, Ptr{UInt8}, Cuint),
108
- zeros (UInt8, acsize),
109
- acptr,
110
- acsize,
111
- )
112
- if ret == 0
113
- acval = $ (init)
114
- valsize = 256
115
- # avoid the garbage collector using malloc
116
- valptr = convert (Ptr{UInt8}, Libc. malloc (valsize))
117
- valptr == C_NULL && throw (SQLiteException (" memory error" ))
118
- else
119
- # size of serialized value is first sizeof(Int) bytes
120
- valsize = bytestoint (acptr, 1 , intsize)
121
- # ptr to serialized value is last sizeof(Ptr) bytes
122
- valptr = reinterpret (
123
- Ptr{UInt8},
124
- bytestoint (acptr, intsize + 1 , ptrsize),
125
- )
126
- # deserialize the value pointed to by valptr
127
- acvalbuf = zeros (UInt8, valsize)
128
- unsafe_copyto! (pointer (acvalbuf), valptr, valsize)
129
- acval = sqldeserialize (acvalbuf)
130
- end
131
-
132
- local funcret
133
- try
134
- funcret = sqlserialize ($ (func)(acval, args... ))
135
- catch
136
- Libc. free (valptr)
137
- rethrow ()
138
- end
139
-
140
- newsize = sizeof (funcret)
141
- if newsize > valsize
142
- # TODO : increase this in a cleverer way?
143
- tmp = convert (Ptr{UInt8}, Libc. realloc (valptr, newsize))
144
- if tmp == C_NULL
145
- Libc. free (valptr)
146
- throw (SQLiteException (" memory error" ))
147
- else
148
- valptr = tmp
149
- end
150
- end
151
- # copy serialized return value
152
- unsafe_copyto! (valptr, pointer (funcret), newsize)
153
-
154
- # copy the size of the serialized value
155
- unsafe_copyto! (
156
- acptr,
157
- pointer (reinterpret (UInt8, [newsize])),
158
- intsize,
159
- )
160
- # copy the address of the pointer to the serialized value
161
- valarr = reinterpret (UInt8, [valptr])
162
- for i in 1 : length (valarr)
163
- unsafe_store! (acptr, valarr[i], intsize + i)
164
- end
165
- nothing
72
+ function wrap_stepfunc (
73
+ init,
74
+ func,
75
+ context:: Ptr{Cvoid} ,
76
+ nargs:: Cint ,
77
+ values:: Ptr{Ptr{Cvoid}} ,
78
+ )
79
+ args = [sqlvalue (values, i) for i in 1 : nargs]
80
+
81
+ intsize = sizeof (Int)
82
+ ptrsize = sizeof (Ptr)
83
+ acsize = intsize + ptrsize
84
+ acptr = convert (Ptr{UInt8}, C. sqlite3_aggregate_context (context, acsize))
85
+
86
+ # acptr will be zeroed-out if this is the first iteration
87
+ ret = ccall (
88
+ :memcmp ,
89
+ Cint,
90
+ (Ptr{UInt8}, Ptr{UInt8}, Cuint),
91
+ zeros (UInt8, acsize),
92
+ acptr,
93
+ acsize,
94
+ )
95
+ if ret == 0
96
+ acval = init
97
+ valsize = 256
98
+ # avoid the garbage collector using malloc
99
+ valptr = convert (Ptr{UInt8}, Libc. malloc (valsize))
100
+ valptr == C_NULL && throw (SQLiteException (" memory error" ))
101
+ else
102
+ # size of serialized value is first sizeof(Int) bytes
103
+ valsize = bytestoint (acptr, 1 , intsize)
104
+ # ptr to serialized value is last sizeof(Ptr) bytes
105
+ valptr =
106
+ reinterpret (Ptr{UInt8}, bytestoint (acptr, intsize + 1 , ptrsize))
107
+ # deserialize the value pointed to by valptr
108
+ acvalbuf = zeros (UInt8, valsize)
109
+ unsafe_copyto! (pointer (acvalbuf), valptr, valsize)
110
+ acval = sqldeserialize (acvalbuf)
111
+ end
112
+
113
+ local funcret
114
+ try
115
+ funcret = sqlserialize (func (acval, args... ))
116
+ catch
117
+ Libc. free (valptr)
118
+ rethrow ()
119
+ end
120
+
121
+ newsize = sizeof (funcret)
122
+ if newsize > valsize
123
+ # TODO : increase this in a cleverer way?
124
+ tmp = convert (Ptr{UInt8}, Libc. realloc (valptr, newsize))
125
+ if tmp == C_NULL
126
+ Libc. free (valptr)
127
+ throw (SQLiteException (" memory error" ))
128
+ else
129
+ valptr = tmp
166
130
end
167
- return $ (nm)
168
131
end
132
+ # copy serialized return value
133
+ unsafe_copyto! (valptr, pointer (funcret), newsize)
134
+
135
+ # copy the size of the serialized value
136
+ unsafe_copyto! (acptr, pointer (reinterpret (UInt8, [newsize])), intsize)
137
+ # copy the address of the pointer to the serialized value
138
+ valarr = reinterpret (UInt8, [valptr])
139
+ for i in 1 : length (valarr)
140
+ unsafe_store! (acptr, valarr[i], intsize + i)
141
+ end
142
+ nothing
169
143
end
170
144
171
- function finalfunc (init, func, fsym = Symbol (string (func) * " _final" ))
172
- nm = isdefined (Base, fsym) ? :(Base.$ fsym) : fsym
173
- return quote
174
- function $ (nm)(
175
- context:: Ptr{Cvoid} ,
176
- nargs:: Cint ,
177
- values:: Ptr{Ptr{Cvoid}} ,
178
- )
179
- acptr = convert (Ptr{UInt8}, C. sqlite3_aggregate_context (context, 0 ))
180
-
181
- # step function wasn't run
182
- if acptr == C_NULL
183
- sqlreturn (context, $ (init))
184
- else
185
- intsize = sizeof (Int)
186
- ptrsize = sizeof (Ptr)
187
- acsize = intsize + ptrsize
188
-
189
- # load size
190
- valsize = bytestoint (acptr, 1 , intsize)
191
- # load ptr
192
- valptr = reinterpret (
193
- Ptr{UInt8},
194
- bytestoint (acptr, intsize + 1 , ptrsize),
195
- )
196
-
197
- # load value
198
- acvalbuf = zeros (UInt8, valsize)
199
- unsafe_copyto! (pointer (acvalbuf), valptr, valsize)
200
- acval = sqldeserialize (acvalbuf)
201
-
202
- local ret
203
- try
204
- ret = $ (func)(acval)
205
- finally
206
- Libc. free (valptr)
207
- end
208
- sqlreturn (context, ret)
209
- end
210
- nothing
145
+ function wrap_finalfunc (
146
+ init,
147
+ func,
148
+ context:: Ptr{Cvoid} ,
149
+ nargs:: Cint ,
150
+ values:: Ptr{Ptr{Cvoid}} ,
151
+ )
152
+ acptr = convert (Ptr{UInt8}, C. sqlite3_aggregate_context (context, 0 ))
153
+
154
+ # step function wasn't run
155
+ if acptr == C_NULL
156
+ sqlreturn (context, init)
157
+ else
158
+ intsize = sizeof (Int)
159
+ ptrsize = sizeof (Ptr)
160
+ acsize = intsize + ptrsize
161
+
162
+ # load size
163
+ valsize = bytestoint (acptr, 1 , intsize)
164
+ # load ptr
165
+ valptr =
166
+ reinterpret (Ptr{UInt8}, bytestoint (acptr, intsize + 1 , ptrsize))
167
+
168
+ # load value
169
+ acvalbuf = zeros (UInt8, valsize)
170
+ unsafe_copyto! (pointer (acvalbuf), valptr, valsize)
171
+ acval = sqldeserialize (acvalbuf)
172
+
173
+ local ret
174
+ try
175
+ ret = func (acval)
176
+ finally
177
+ Libc. free (valptr)
211
178
end
212
- return $ (nm )
179
+ sqlreturn (context, ret )
213
180
end
181
+ nothing
214
182
end
215
183
216
184
"""
@@ -223,6 +191,8 @@ macro register(db, func)
223
191
:(register ($ (esc (db)), $ (esc (func))))
224
192
end
225
193
194
+ UDF_keep_alive_list = []
195
+
226
196
"""
227
197
SQLite.register(db, func)
228
198
SQLite.register(db, init, step_func, final_func; nargs=-1, name=string(step), isdeterm=true)
@@ -242,9 +212,12 @@ function register(
242
212
nargs < - 1 && (nargs = - 1 )
243
213
@assert sizeof (name) <= 255 " size of function name must be <= 255"
244
214
245
- f = eval (scalarfunc (func, Symbol (name)))
246
-
215
+ f =
216
+ (context, nargs, values) ->
217
+ wrap_scalarfunc (func, context, nargs, values)
247
218
cfunc = @cfunction ($ f, Cvoid, (Ptr{Cvoid}, Cint, Ptr{Ptr{Cvoid}}))
219
+ push! (db. registered_UDFs, cfunc)
220
+
248
221
# TODO : allow the other encodings
249
222
enc = C. SQLITE_UTF8
250
223
enc = isdeterm ? enc | C. SQLITE_DETERMINISTIC : enc
@@ -263,12 +236,11 @@ function register(
263
236
end
264
237
265
238
# as above but for aggregate functions
266
- newidentity () = @eval x -> x
267
239
function register (
268
240
db,
269
241
init,
270
242
step:: Function ,
271
- final:: Function = newidentity () ;
243
+ final:: Function = identity ;
272
244
nargs:: Int = - 1 ,
273
245
name:: AbstractString = string (step),
274
246
isdeterm:: Bool = true ,
@@ -277,10 +249,16 @@ function register(
277
249
nargs < - 1 && (nargs = - 1 )
278
250
@assert sizeof (name) <= 255 " size of function name must be <= 255 chars"
279
251
280
- s = eval (stepfunc (init, step, Base. nameof (step)))
252
+ s =
253
+ (context, nargs, values) ->
254
+ wrap_stepfunc (init, step, context, nargs, values)
281
255
cs = @cfunction ($ s, Cvoid, (Ptr{Cvoid}, Cint, Ptr{Ptr{Cvoid}}))
282
- f = eval (finalfunc (init, final, Base. nameof (final)))
256
+ f =
257
+ (context, nargs, values) ->
258
+ wrap_finalfunc (init, final, context, nargs, values)
283
259
cf = @cfunction ($ f, Cvoid, (Ptr{Cvoid}, Cint, Ptr{Ptr{Cvoid}}))
260
+ push! (db. registered_UDFs, cs)
261
+ push! (db. registered_UDFs, cf)
284
262
285
263
enc = C. SQLITE_UTF8
286
264
enc = isdeterm ? enc | C. SQLITE_DETERMINISTIC : enc
0 commit comments