1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2013 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Ard Biesheuvel <a.k.biesheuvel@ewi.tudelft.nl> |
16 +----------------------------------------------------------------------+
17 */
18
19 /* $Id$ */
20
21 /**
22 * This UDF library adds the ability to call PHP functions from SQL
23 * statements. Because of SQL's strong typing, you will have to declare
24 * an external function for every combination { output type, #args } that
25 * your application requires.
26 *
27 * Declare the functions like this:
28 *
29 * DECLARE EXTERNAL FUNCTION CALL_PHP1
30 * CSTRING(xx),
31 * <return type> BY DESCRIPTOR,
32 * INTEGER BY DESCRIPTOR
33 * RETURNS PARAMETER 2
34 * ENTRY_POINT 'udf_call_php1' MODULE_NAME 'php_ibase_udf'
35 *
36 * DECLARE EXTERNAL FUNCTION CALL_PHP2
37 * CSTRING(xx),
38 * <return type> BY DESCRIPTOR,
39 * INTEGER BY DESCRIPTOR,
40 * INTEGER BY DESCRIPTOR
41 * RETURNS PARAMETER 2
42 * ENTRY_POINT 'udf_call_php2' MODULE_NAME 'php_ibase_udf'
43 *
44 * ... and so on. [for up to 8 input arguments]
45 *
46 * The first input parameter contains the name of the PHP function you want
47 * to call. The second argument is the result. (omit this argument when calling
48 * the function) The return type of the function is the declared type of the
49 * result. The value returned from the PHP function being called will
50 * automatically be converted if necessary.
51 * The arguments should have their types declared as well, but you're free
52 * to pass arguments of other types instead. They will be converted to the
53 * best matching PHP type before being passed to the PHP function.
54 *
55 * The declared functions can be called from SQL like:
56 *
57 * SELECT * FROM <table> WHERE CALL_PHP1('soundex',<field>) NOT LIKE ?
58 * or
59 * UPDATE <table> SET <field> = CALL_PHP1('ucwords',<field>)
60 *
61 * Additionally, there's a function 'exec_php' which allows the contents
62 * of text BLOB fields to be parsed and executed by PHP. This is most useful
63 * for declaring functions that can then be called with CALL_PHPx.
64 *
65 * DECLARE EXTERNAL FUNCTION EXEC_PHP
66 * BLOB,
67 * INTEGER BY DESCRIPTOR,
68 * SMALLINT
69 * RETURNS PARAMETER 2
70 * ENTRY_POINT 'exec_php' MODULE_NAME 'php_ibase_udf'
71 *
72 * The function will return 1 if execution succeeded and 0 if an error
73 * occurred. The result that is returned from the executed PHP code is
74 * ignored. You can pass a non-zero value as second argument to force
75 * the embedded PHP engine to re-initialise.
76 *
77 * There are several ways to build this library, depending on which way the
78 * database is accessed. If you're using the classic server on a local
79 * connection, you should compile the library like this:
80 *
81 * gcc -shared `php-config --includes` `php-config --ldflags` \
82 * `php-config --libs` -o php_ibase_udf.so php_ibase_udf.c
83 *
84 * If you connect to the classic server by TCP/IP, you should build the
85 * PHP embedded static library and link against that.
86 *
87 * gcc -shared `php-config --includes` `php-config --ldflags` \
88 * `php-config --libs` -o php_ibase_udf.so php_ibase_udf.c \
89 * /usr/lib/libphp5.a
90 *
91 * If you use the super server, you should also link against the embedded
92 * library, but be sure to enable thread safety, as the super server is
93 * multi-threaded. After building, copy the resulting file to the folder
94 * where your database expects to find its UDFs.
95 */
96
97 #include "zend.h"
98 #include "zend_API.h"
99
100 #include "php.h"
101 #include "php_ini.h"
102
103 #include "ibase.h"
104
105 #define min(a,b) ((a)<(b)?(a):(b))
106
107 #ifdef PHP_WIN32
108 #define LL_LIT(lit) lit ## I64
109 #else
110 #define LL_LIT(lit) lit ## ll
111 #endif
112
113 #ifdef ZTS
114 # include <pthread.h>
115
116 static void ***tsrm_ls;
117 pthread_mutex_t mtx_res = PTHREAD_MUTEX_INITIALIZER;
118
119 #define LOCK() do { pthread_mutex_lock(&mtx_res); } while (0)
120 #define UNLOCK() do { pthread_mutex_unlock(&mtx_res); } while (0)
121 #else
122 #define LOCK()
123 #define UNLOCK()
124 #endif
125
126 #ifdef PHP_EMBED
127 # include "php_main.h"
128 # include "sapi/embed/php_embed.h"
129
init()130 static void __attribute__((constructor)) init()
131 {
132 php_embed_init(0, NULL PTSRMLS_CC);
133 }
134
fini()135 static void __attribute__((destructor)) fini()
136 {
137 php_embed_shutdown(TSRMLS_C);
138 }
139
140 #endif
141
142 /**
143 * Gets the contents of the BLOB b and offers it to Zend for parsing/execution
144 */
exec_php(BLOBCALLBACK b,PARAMDSC * res,ISC_SHORT * init)145 void exec_php(BLOBCALLBACK b, PARAMDSC *res, ISC_SHORT *init)
146 {
147 int result, remaining = b->blob_total_length, i = 0;
148 char *code = pemalloc(remaining+1, 1);
149 ISC_USHORT read;
150
151 for (code[remaining] = '\0'; remaining > 0; remaining -= read)
152 b->blob_get_segment(b->blob_handle, &code[i++<<16],min(0x10000,remaining), &read);
153
154 LOCK();
155
156 switch (init && *init) {
157
158 default:
159 #ifdef PHP_EMBED
160 php_request_shutdown(NULL);
161 if (FAILURE == (result = php_request_startup(TSRMLS_C))) {
162 break;
163 }
164 case 0:
165 #endif
166 /* feed it to the parser */
167 zend_first_try {
168 result = zend_eval_stringl(code, b->blob_total_length, NULL, "Firebird Embedded PHP engine" TSRMLS_CC);
169 } zend_end_try();
170 }
171
172 UNLOCK();
173
174 free(code);
175
176 res->dsc_dtype = dtype_long;
177 *(ISC_LONG*)res->dsc_address = (result == SUCCESS);
178 }
179
180 static ISC_INT64 const scales[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 100000000, 1000000000,
181 1000000000, LL_LIT(10000000000),LL_LIT(100000000000),LL_LIT(10000000000000),LL_LIT(100000000000000),
182 LL_LIT(1000000000000000),LL_LIT(1000000000000000),LL_LIT(1000000000000000000) };
183
184
call_php(char * name,PARAMDSC * r,int argc,PARAMDSC ** argv)185 static void call_php(char *name, PARAMDSC *r, int argc, PARAMDSC **argv)
186 {
187 do {
188 zval callback, args[4], *argp[4], return_value;
189 PARAMVARY *res = (PARAMVARY*)r->dsc_address;
190 int i;
191
192 INIT_ZVAL(callback);
193 ZVAL_STRING(&callback,name,0);
194
195 LOCK();
196
197 /* check if the requested function exists */
198 if (!zend_is_callable(&callback, 0, NULL TSRMLS_CC)) {
199 break;
200 }
201
202 UNLOCK();
203
204 /* create the argument array */
205 for (i = 0; i < argc; ++i) {
206
207 INIT_ZVAL(args[i]);
208 argp[i] = &args[i];
209
210 /* test arg for null */
211 if (argv[i]->dsc_flags & DSC_null) {
212 ZVAL_NULL(argp[i]);
213 continue;
214 }
215
216 switch (argv[i]->dsc_dtype) {
217 ISC_INT64 l;
218 struct tm t;
219 char const *fmt;
220 char d[64];
221
222 case dtype_cstring:
223 ZVAL_STRING(argp[i], (char*)argv[i]->dsc_address,0);
224 break;
225
226 case dtype_text:
227 ZVAL_STRINGL(argp[i], (char*)argv[i]->dsc_address, argv[i]->dsc_length,0);
228 break;
229
230 case dtype_varying:
231 ZVAL_STRINGL(argp[i], ((PARAMVARY*)argv[i]->dsc_address)->vary_string,
232 ((PARAMVARY*)argv[i]->dsc_address)->vary_length,0);
233 break;
234
235 case dtype_short:
236 if (argv[i]->dsc_scale == 0) {
237 ZVAL_LONG(argp[i], *(short*)argv[i]->dsc_address);
238 } else {
239 ZVAL_DOUBLE(argp[i],
240 ((double)*(short*)argv[i]->dsc_address)/scales[-argv[i]->dsc_scale]);
241 }
242 break;
243
244 case dtype_long:
245 if (argv[i]->dsc_scale == 0) {
246 ZVAL_LONG(argp[i], *(ISC_LONG*)argv[i]->dsc_address);
247 } else {
248 ZVAL_DOUBLE(argp[i],
249 ((double)*(ISC_LONG*)argv[i]->dsc_address)/scales[-argv[i]->dsc_scale]);
250 }
251 break;
252
253 case dtype_int64:
254 l = *(ISC_INT64*)argv[i]->dsc_address;
255
256 if (argv[i]->dsc_scale == 0 && l <= LONG_MAX && l >= LONG_MIN) {
257 ZVAL_LONG(argp[i], (long)l);
258 } else {
259 ZVAL_DOUBLE(argp[i], ((double)l)/scales[-argv[i]->dsc_scale]);
260 }
261 break;
262
263 case dtype_real:
264 ZVAL_DOUBLE(argp[i], *(float*)argv[i]->dsc_address);
265 break;
266
267 case dtype_double:
268 ZVAL_DOUBLE(argp[i], *(double*)argv[i]->dsc_address);
269 break;
270
271 case dtype_sql_date:
272 isc_decode_sql_date((ISC_DATE*)argv[i]->dsc_address, &t);
273 ZVAL_STRINGL(argp[i], d, strftime(d, sizeof(d), INI_STR("ibase.dateformat"), &t),1);
274 break;
275
276 case dtype_sql_time:
277 isc_decode_sql_time((ISC_TIME*)argv[i]->dsc_address, &t);
278 ZVAL_STRINGL(argp[i], d, strftime(d, sizeof(d), INI_STR("ibase.timeformat"), &t),1);
279 break;
280
281 case dtype_timestamp:
282 isc_decode_timestamp((ISC_TIMESTAMP*)argv[i]->dsc_address, &t);
283 ZVAL_STRINGL(argp[i], d, strftime(d, sizeof(d), INI_STR("ibase.timestampformat"), &t),1);
284 break;
285 }
286 }
287
288 LOCK();
289
290 /* now call the function */
291 if (FAILURE == call_user_function(EG(function_table), NULL,
292 &callback, &return_value, argc, argp TSRMLS_CC)) {
293 UNLOCK();
294 break;
295 }
296
297 UNLOCK();
298
299 for (i = 0; i < argc; ++i) {
300 switch (argv[i]->dsc_dtype) {
301 case dtype_sql_date:
302 case dtype_sql_time:
303 case dtype_timestamp:
304 zval_dtor(argp[i]);
305
306 }
307 }
308
309 /* return whatever type we got back from the callback: let DB handle conversion */
310 switch (Z_TYPE(return_value)) {
311
312 case IS_LONG:
313 r->dsc_dtype = dtype_long;
314 *(long*)r->dsc_address = Z_LVAL(return_value);
315 r->dsc_length = sizeof(long);
316 break;
317
318 case IS_DOUBLE:
319 r->dsc_dtype = dtype_double;
320 *(double*)r->dsc_address = Z_DVAL(return_value);
321 r->dsc_length = sizeof(double);
322 break;
323
324 case IS_NULL:
325 r->dsc_flags |= DSC_null;
326 break;
327
328 default:
329 convert_to_string(&return_value);
330
331 case IS_STRING:
332 r->dsc_dtype = dtype_varying;
333 memcpy(res->vary_string, Z_STRVAL(return_value),
334 (res->vary_length = min(r->dsc_length-2,Z_STRLEN(return_value))));
335 r->dsc_length = res->vary_length+2;
336 break;
337 }
338
339 zval_dtor(&return_value);
340
341 return;
342
343 } while (0);
344
345 /**
346 * If we end up here, we should report an error back to the DB engine, but
347 * that's not possible. We can however report it back to PHP.
348 */
349 LOCK();
350 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error calling function '%s' from database", name);
351 UNLOCK();
352 }
353
354
355 /* Entry points for the DB engine */
356
udf_call_php1(char * name,PARAMDSC * r,PARAMDSC * arg1)357 void udf_call_php1(char *name, PARAMDSC *r, PARAMDSC *arg1)
358 {
359 PARAMDSC *args[1] = { arg1 };
360 call_php(name, r, 1, args);
361 }
362
udf_call_php2(char * name,PARAMDSC * r,PARAMDSC * arg1,PARAMDSC * arg2)363 void udf_call_php2(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2)
364 {
365 PARAMDSC *args[2] = { arg1, arg2 };
366 call_php(name, r, 2, args);
367 }
368
udf_call_php3(char * name,PARAMDSC * r,PARAMDSC * arg1,PARAMDSC * arg2,PARAMDSC * arg3)369 void udf_call_php3(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2, PARAMDSC *arg3)
370 {
371 PARAMDSC *args[3] = { arg1, arg2, arg3 };
372 call_php(name, r, 3, args);
373 }
374
udf_call_php4(char * name,PARAMDSC * r,PARAMDSC * arg1,PARAMDSC * arg2,PARAMDSC * arg3,PARAMDSC * arg4)375 void udf_call_php4(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2, PARAMDSC *arg3,
376 PARAMDSC *arg4)
377 {
378 PARAMDSC *args[4] = { arg1, arg2, arg3, arg4 };
379 call_php(name, r, 4, args);
380 }
381
udf_call_php5(char * name,PARAMDSC * r,PARAMDSC * arg1,PARAMDSC * arg2,PARAMDSC * arg3,PARAMDSC * arg4,PARAMDSC * arg5)382 void udf_call_php5(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2, PARAMDSC *arg3,
383 PARAMDSC *arg4, PARAMDSC *arg5)
384 {
385 PARAMDSC *args[5] = { arg1, arg2, arg3, arg4, arg5 };
386 call_php(name, r, 5, args);
387 }
388
udf_call_php6(char * name,PARAMDSC * r,PARAMDSC * arg1,PARAMDSC * arg2,PARAMDSC * arg3,PARAMDSC * arg4,PARAMDSC * arg5,PARAMDSC * arg6)389 void udf_call_php6(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2, PARAMDSC *arg3,
390 PARAMDSC *arg4, PARAMDSC *arg5, PARAMDSC *arg6)
391 {
392 PARAMDSC *args[6] = { arg1, arg2, arg3, arg4, arg5, arg6 };
393 call_php(name, r, 6, args);
394 }
395
udf_call_php7(char * name,PARAMDSC * r,PARAMDSC * arg1,PARAMDSC * arg2,PARAMDSC * arg3,PARAMDSC * arg4,PARAMDSC * arg5,PARAMDSC * arg6,PARAMDSC * arg7)396 void udf_call_php7(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2, PARAMDSC *arg3,
397 PARAMDSC *arg4, PARAMDSC *arg5, PARAMDSC *arg6, PARAMDSC *arg7)
398 {
399 PARAMDSC *args[7] = { arg1, arg2, arg3, arg4, arg5, arg6, arg7 };
400 call_php(name, r, 7, args);
401 }
402
udf_call_php8(char * name,PARAMDSC * r,PARAMDSC * arg1,PARAMDSC * arg2,PARAMDSC * arg3,PARAMDSC * arg4,PARAMDSC * arg5,PARAMDSC * arg6,PARAMDSC * arg7,PARAMDSC * arg8)403 void udf_call_php8(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2, PARAMDSC *arg3,
404 PARAMDSC *arg4, PARAMDSC *arg5, PARAMDSC *arg6, PARAMDSC *arg7, PARAMDSC *arg8)
405 {
406 PARAMDSC *args[8] = { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 };
407 call_php(name, r, 8, args);
408 }
409
410