Return to php_ibase_udf.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / interbase |
1.1 ! misho 1: /* ! 2: +----------------------------------------------------------------------+ ! 3: | PHP Version 5 | ! 4: +----------------------------------------------------------------------+ ! 5: | Copyright (c) 1997-2012 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: php_ibase_udf.c 321634 2012-01-01 13:15:04Z felipe $ */ ! 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: ! 130: static void __attribute__((constructor)) init() ! 131: { ! 132: php_embed_init(0, NULL PTSRMLS_CC); ! 133: } ! 134: ! 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: */ ! 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: ! 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: ! 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: ! 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: ! 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: ! 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: ! 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: ! 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: ! 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: ! 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: