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