/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2004 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_0.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Author:Alan Knowles | +----------------------------------------------------------------------+ */ /* $Id: pdo_mysql.c,v 1.3 2005/02/09 05:02:03 wez Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" #include "php_dbdo.h" enum dbdo_type { DBDO_TYPE_INVALID, DBDO_TYPE_INT, DBDO_TYPE_MONEY, DBDO_TYPE_DOUBLE, DBDO_TYPE_STRING, DBDO_TYPE_BINARY, DBDO_TYPE_DATE, DBDO_TYPE_TIME, DBDO_TYPE_DATETIME, DBDO_TYPE_MYSQLTIMESTAMP }; enum dbdo_flags { DBDO_FLAGS_NOTNULL = 1, DBDO_FLAGS_PRIMARYKEY = 2, DBDO_FLAGS_UNIQUEINDEX = 4, DBDO_FLAGS_AUTOINCREMENT = 8 }; ZEND_DECLARE_MODULE_GLOBALS(dbdo) static zend_object_handlers dbdo_object_handlers, dbdo_object_exception_handlers; /** {{{ dbdo_object_free_storage * * Free the Object Storage * * @param object (a dbdo_object) */ static void dbdo_object_free_storage(void *object TSRMLS_DC) { dbdo_object *intern = (dbdo_object *)object; if (!intern) { /* we must have free'd it already */ return; } zend_hash_destroy(intern->zo.properties); FREE_HASHTABLE(intern->zo.properties); if (intern->row_cache) { zend_hash_destroy(intern->row_cache); FREE_HASHTABLE(intern->row_cache); intern->row_cache = NULL; } if (intern->visable_hash) { zend_hash_destroy(intern->visable_hash); FREE_HASHTABLE(intern->visable_hash); intern->visable_hash = NULL; } intern->row_cached = 0; if (intern->assigned_hash) { zend_hash_destroy(intern->assigned_hash); FREE_HASHTABLE(intern->assigned_hash); intern->assigned_hash = NULL; } /* if (G_IS_OBJECT(intern->result)) { g_object_unref (intern->result); intern->result = NULL; } */ if (intern->condition) { intern->condition->refcount--; if (intern->condition->refcount < 1) { if (intern->condition->where) { efree(intern->condition->where); } if (intern->condition->groupBy) { efree(intern->condition->groupBy); } if (intern->condition->orderBy) { efree(intern->condition->orderBy); } if (intern->condition->having) { efree(intern->condition->having); } if (intern->condition->data_select) { efree(intern->condition->data_select); } efree(intern->condition); } intern->condition = NULL; } /* we cant free intern, cause it contains pointers to values */ intern->table = NULL; efree(intern); } /** {{{ dbdo_objects_new * * Initialize a new object. * * @param zend_class_entry (not sure what it for..) */ PHP_DBDO_API zend_object_value dbdo_objects_new(zend_class_entry *class_type TSRMLS_DC) { zval *tmp; zend_object_value retval; dbdo_object *intern; intern = emalloc(sizeof(dbdo_object)); intern->zo.ce = class_type; intern->zo.in_get = 0; intern->zo.in_set = 0; intern->zo.properties = NULL; intern->row_cache = NULL; intern->visable_hash = NULL; intern->assigned_hash = NULL; ALLOC_HASHTABLE(intern->assigned_hash); zend_hash_init(intern->assigned_hash, 0, NULL, ZVAL_PTR_DTOR, 0); intern->row_cached = 0; intern->con = NULL; intern->result = NULL; intern->row_id = -1; intern->table = NULL; intern->condition = emalloc(sizeof(dbdo_condition)); intern->condition->refcount = 1; intern->condition->where = NULL; intern->condition->groupBy = NULL; intern->condition->orderBy = NULL; intern->condition->having = NULL; intern->condition->limit_from = 0; intern->condition->limit_qty = 0; intern->condition->limit_use = FALSE; intern->condition->data_select = NULL; ALLOC_HASHTABLE(intern->zo.properties); zend_hash_init(intern->zo.properties, 0, NULL, ZVAL_PTR_DTOR, 0); zend_hash_copy(intern->zo.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); retval.handle = zend_objects_store_put(intern, NULL, (zend_objects_free_object_storage_t) dbdo_object_free_storage, NULL TSRMLS_CC); retval.handlers = (zend_object_handlers *) & dbdo_object_handlers; return retval; } /** {{{ dbdo_object_exception_new * * Initialize a new exception. * * @param zend_class_entry (not sure what it for..) */ PHP_DBDO_API zend_object_value dbdo_object_exception_new(zend_class_entry *class_type TSRMLS_DC) { zend_object_value retval; dbdo_object *intern; zval *tmp; /* we use the dbdo_object for exceptions, even thought we dont really use it. */ intern = emalloc(sizeof(dbdo_object)); intern->zo.ce = class_type; intern->zo.in_get = 0; intern->zo.in_set = 0; intern->zo.properties = NULL; ALLOC_HASHTABLE(intern->zo.properties); zend_hash_init(intern->zo.properties, 0, NULL, ZVAL_PTR_DTOR, 0); zend_hash_copy(intern->zo.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); retval.handle = zend_objects_store_put(intern, NULL, (zend_objects_free_object_storage_t) dbdo_object_free_storage, NULL TSRMLS_CC); retval.handlers = (zend_object_handlers *) & dbdo_object_exception_handlers; return retval; } /* {{{ pdo_mysql_functions[] */ /* ??? is this used anywhere??? */ function_entry dbdo_functions[] = { {NULL, NULL, NULL} }; /* }}} */ /* {{{ dbdo_funcs_exception[] */ function_entry dbdo_funcs_exception[] = { {NULL, NULL, NULL} }; ZEND_BEGIN_ARG_INFO(__call_args, 0) ZEND_ARG_PASS_INFO(0) ZEND_ARG_PASS_INFO(0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(__dbdo_call_args, 0) ZEND_ARG_PASS_INFO(0) ZEND_ARG_PASS_INFO(0) ZEND_ARG_PASS_INFO(0) ZEND_ARG_PASS_INFO(0) ZEND_ARG_PASS_INFO(1) ZEND_END_ARG_INFO() /* and now our object.. */ function_entry dbdo_class_functions[] = { PHP_ME(dbdo, __construct, NULL,0) PHP_ME(dbdo, factory, NULL, ZEND_ACC_STATIC|ZEND_ACC_PUBLIC) PHP_ME(dbdo, debugLevel, NULL, ZEND_ACC_STATIC|ZEND_ACC_PUBLIC) PHP_ME(dbdo, config, NULL, ZEND_ACC_STATIC|ZEND_ACC_PUBLIC) PHP_ME(dbdo, reproduce, NULL,0) PHP_ME(dbdo, get, NULL,0) /* PHP_ME(dbdo, fetch, NULL,0) PHP_ME(dbdo, fetchAll, NULL,0) PHP_ME(dbdo, count, NULL,0) PHP_ME(dbdo, insert, NULL,0) PHP_ME(dbdo, update, NULL,0) PHP_ME(dbdo, delete, NULL,0) PHP_ME(dbdo, query, NULL,0) PHP_ME(dbdo, toArray, NULL,0) PHP_ME(dbdo, assignFrom, NULL,0) PHP_ME(dbdo, valueSet, NULL,0) PHP_ME(dbdo, valueGet, NULL,0) */ PHP_ME(dbdo, schema, NULL,0) PHP_ME(dbdo, info, NULL, 0) /* PHP_ME(dbdo, sequenceKey, NULL,0) PHP_ME(dbdo, keys, NULL,0) PHP_ME(dbdo, selectAdd, NULL,0) PHP_ME(dbdo, whereAdd, NULL,0) PHP_ME(dbdo, limit, NULL,0) PHP_ME(dbdo, orderBy, NULL,0) PHP_ME(dbdo, groupBy, NULL,0) PHP_ME(dbdo, having, NULL,0) PHP_ME(dbdo, __sleep, NULL, 0) PHP_ME(dbdo, __call, __call_args, 0) ZEND_FENTRY(__dbdoCall, ZEND_FN(dbdo___call), __dbdo_call_args, 0) */ {NULL, NULL, NULL} }; /* {{{ pdo_mysql_module_entry */ zend_module_entry dbdo_module_entry = { STANDARD_MODULE_HEADER, "DBDO", NULL, PHP_MINIT(dbdo), PHP_MSHUTDOWN(dbdo), NULL, NULL, PHP_MINFO(dbdo), "0.2", STANDARD_MODULE_PROPERTIES }; /* }}} */ #ifdef COMPILE_DL_DBDO ZEND_GET_MODULE(dbdo) #endif /* true global environment */ static void php_dbdo_init_globals(zend_dbdo_globals *dbdo_globals) { /* QUESTION? what does the last parementer : persistant actually mean */ /* QUESTION? need a good example of a destructor callback.. */ dbdo_globals->debugLevel = 0; /* should this be on by default?? */ dbdo_globals->config = NULL; dbdo_globals->config_len = 0; dbdo_globals->tables = NULL; dbdo_globals->tables_len = 0; dbdo_globals->constants = NULL; //dbdo_globals->transaction = NULL; } /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(dbdo) { ZEND_INIT_MODULE_GLOBALS(dbdo, php_dbdo_init_globals, NULL); int i; zend_class_entry ce, ce_ex; memcpy(&dbdo_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); dbdo_object_handlers.clone_obj = dbdo_clone; INIT_CLASS_ENTRY(ce, "DBDO", dbdo_class_functions); ce.create_object = dbdo_objects_new; dbdo_class_entry_ce = zend_register_internal_class(&ce TSRMLS_CC); /* now for constants */ DBDO_G(constants) = emalloc(sizeof(zval*) * 6); for (i=0;i<6;i++) { DBDO_G(constants)[i] = NULL; } MAKE_STD_ZVAL(DBDO_G(constants)[0]); ZVAL_LONG(DBDO_G(constants)[0], 0); zend_hash_add(&dbdo_class_entry_ce->constants_table, "OBJECTS", strlen("OBJECTS")+1, &DBDO_G(constants)[0], sizeof(zval *), NULL); MAKE_STD_ZVAL(DBDO_G(constants)[1]); ZVAL_LONG(DBDO_G(constants)[1], 1); zend_hash_add(&dbdo_class_entry_ce->constants_table, "KEY_VALUE", strlen("KEY_VALUE")+1, &DBDO_G(constants)[1], sizeof(zval *), NULL); zend_hash_add(&dbdo_class_entry_ce->constants_table, "BUILD", strlen("BUILD")+1, &DBDO_G(constants)[1], sizeof(zval *), NULL); MAKE_STD_ZVAL(DBDO_G(constants)[2]); ZVAL_LONG(DBDO_G(constants)[2], 2); zend_hash_add(&dbdo_class_entry_ce->constants_table, "VALUE", strlen("VALUE")+1, &DBDO_G(constants)[2], sizeof(zval *), NULL); MAKE_STD_ZVAL(DBDO_G(constants)[3]); ZVAL_LONG(DBDO_G(constants)[3], 3); zend_hash_add(&dbdo_class_entry_ce->constants_table, "SINGLE", strlen("SINGLE")+1, &DBDO_G(constants)[3], sizeof(zval *), NULL); /* Debug reporting constants */ MAKE_STD_ZVAL(DBDO_G(constants)[4]); ZVAL_LONG(DBDO_G(constants)[4], 3); zend_hash_add(&dbdo_class_entry_ce->constants_table, "E_ALL", strlen("E_ALL")+1, &DBDO_G(constants)[4], sizeof(zval *), NULL); MAKE_STD_ZVAL(DBDO_G(constants)[5]); ZVAL_LONG(DBDO_G(constants)[5], 63); zend_hash_add(&dbdo_class_entry_ce->constants_table, "E_FULL", strlen("E_FULL")+1, &DBDO_G(constants)[5], sizeof(zval *), NULL); /* exception */ memcpy(&dbdo_object_exception_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); ce_ex.create_object = dbdo_object_exception_new; INIT_CLASS_ENTRY(ce_ex, "DBDO_Exception", dbdo_funcs_exception); dbdo_class_entry_exception_ce = zend_register_internal_class_ex(&ce_ex, zend_exception_get_default(), NULL TSRMLS_CC); dbdo_object_exception_handlers.clone_obj = NULL; return SUCCESS; /*php_pdo_register_driver(&pdo_mysql_driver);*/ } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ PHP_MSHUTDOWN_FUNCTION(dbdo) { /* php_pdo_unregisterq_driver(&pdo_mysql_driver); */ /* uncomment this line if you have INI entries UNREGISTER_INI_ENTRIES(); * / /* TODO: free sequences, tables .. */ int i; /* none of this works correctly at present!! */ return SUCCESS; /* free client */ /* free config */ if (DBDO_G(config_len)) { for (i=0;idsn) { efree(con->dsn); con->dsn = NULL; } if (con->require_path) { efree(con->require_path); con->require_path = NULL; } if (con->class_name) { efree(con->class_name); con->class_name = NULL; } if (con->links) { efree(con->links); con->links = NULL; } efree(con); DBDO_G(config)[i] = NULL; } efree(DBDO_G(config)); } DBDO_G(config) = NULL; DBDO_G(config_len) = 0; /* lets clear up the tables.. */ DBDO_DEBUG0(16,"CLEARING TABLES"); if (DBDO_G(tables)) { for (i=0;idsn,table->name); if (table->dsn) { efree(table->dsn); } if (table->name) { efree(table->name); } if (table->schema) { } if (table->sequenceColumn) { efree(table->sequenceColumn); } if (table->sequenceName) { efree(table->sequenceName); } for (j = 0; j < table->keys_len; j++) { efree(table->keys[j]); } if (table->keys_len) { efree(table->keys); } table->keys_len = 0; table->keys = NULL; table->dsn = NULL; table->name = NULL; table->schema = NULL; table->sequenceColumn = NULL; table->sequenceName = NULL; efree(table); DBDO_G(tables)[i] = NULL; } efree(DBDO_G(tables)); } DBDO_G(tables) = NULL; DBDO_G(tables_len) = 0; return SUCCESS; if (DBDO_G(constants)) { for (i=0;i<6;i++) { zval_dtor(DBDO_G(constants)[i]); FREE_ZVAL(DBDO_G(constants)[i]); } efree(DBDO_G(constants)); DBDO_G(constants) = NULL; } return SUCCESS; } /* }}} */ /* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(dbdo) { php_info_print_table_start(); php_info_print_table_header(2, "DBDO support", "enabled"); php_info_print_table_row(2, "CVS id", "$Id: dbdo.c,v 1.62 2005/02/03 14:43:38 alan_k Exp $"); php_info_print_table_end(); } /* }}} */ static zend_object_value dbdo_clone(zval *zobject TSRMLS_DC) { zend_object_value new_obj_val; zend_object *old_object; zend_object *new_object; zend_object_handle handle = Z_OBJ_HANDLE_P(zobject); dbdo_object *intern; dbdo_object *old_obj; old_object = zend_objects_get_address(zobject TSRMLS_CC); old_obj = zend_object_store_get_object(zobject TSRMLS_CC); /*new_obj_val = dbdo_object_new_ex(old_object->ce, &intern TSRMLS_CC); */ { zend_class_entry *class_type = old_object->ce; zval *tmp; intern = emalloc(sizeof(dbdo_object)); intern->zo.ce = class_type; intern->zo.in_get = 0; intern->zo.in_set = 0; intern->zo.properties = NULL; intern->row_cache = NULL; intern->visable_hash = NULL; intern->assigned_hash = NULL; intern->row_cached = 0; intern->con = old_obj->con; intern->result = old_obj->result; if (G_IS_OBJECT(intern->result)) { g_object_ref(intern->result); } intern->row_id = old_obj->row_id; /* ALL THESE need refcounting */ intern->table = old_obj->table; intern->condition = old_obj->condition; if (intern->condition) { old_obj->condition->refcount++; } ALLOC_HASHTABLE(intern->zo.properties); zend_hash_init(intern->zo.properties, 0, NULL, ZVAL_PTR_DTOR, 0); zend_hash_copy(intern->zo.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); ALLOC_HASHTABLE(intern->assigned_hash); zend_hash_init(intern->assigned_hash, 0, NULL, ZVAL_PTR_DTOR, 0); zend_hash_copy(intern->assigned_hash, old_obj->assigned_hash, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); new_obj_val.handle = zend_objects_store_put(intern, NULL, (zend_objects_free_object_storage_t) dbdo_object_free_storage, NULL TSRMLS_CC); new_obj_val.handlers = (zend_object_handlers *) & dbdo_object_handlers; } new_object = &intern->zo; zend_objects_clone_members(new_object, new_obj_val, old_object, handle TSRMLS_CC); return new_obj_val; } /* dbdo_info stuff */ #define DBDO_INFO_LEN 24 static char *dbdo_info_values[] = { /* 0 */ "table", "dsn", /* 2 */ "row", "rows", /* 4 */ "assigned", "fetched", /* 6 */ "config", "sequence_column", /* 8 */ "sequence_is_native", "sequence_assigned", /* 10*/ "sequence_name", "keys", /* 12*/ "where", "group_by", /* 14*/ "order_by", "having", /* 16*/ "limit_from", "limit_qty", /* 18*/ "select" }; /* get the id for a specified column */ int dbdo_info_get_id(char * col) { int i; for (i=0;i< DBDO_INFO_LEN;i++) { if (!strcasecmp(dbdo_info_values[i],col)) { return i; } } return -1; } dbdo_config * dbdo_config_get(char *dsn TSRMLS_DC) { dbdo_config *con,*cons; int i; /* look for it on the stack.. */ for (i=0;idsn,dsn)==0) { return cons; } } DBDO_G(config_len)++; if (DBDO_G(config) != NULL) { DBDO_G(config) = (dbdo_config **) erealloc(DBDO_G(config),DBDO_G(config_len) * sizeof(dbdo_config *)); } else { DBDO_G(config) = (dbdo_config **) emalloc(sizeof(dbdo_config *)); } con = emalloc(sizeof(dbdo_config)); con->connect_dsn = NULL; con->username = NULL; con->password = NULL; con->driver = NULL; con->description = NULL; con->require_path = NULL; con->class_name = NULL; con->links = NULL; con->lowercase_columns = FALSE; con->quote_identifiers = FALSE; con->dsn = estrdup(dsn); DBDO_G(config)[DBDO_G(config_len)-1] = con; return con; } #define DBDO_INFO_RETURN(col, val) DBDO_ARRAY_ADD(return_value, col, val) #define DBDO_ARRAY_ADD(array, col, val) { \ long h;\ h = zend_hash_func(col, strlen(col)+1);\ zend_hash_quick_update(HASH_OF(array), \ col, strlen(col)+1, h, &val, sizeof(zval *), NULL);\ } #define DBDO_ARRAY_STRADD(array, col, string) { \ zval *zvalue; \ MAKE_STD_ZVAL(zvalue); \ ZVAL_STRING(zvalue, string, 1); \ DBDO_ARRAY_ADD(array, col,zvalue) \ } #define DBDO_ARRAY_BOOLADD(array, col, bvalue) { \ zval *zvalue; \ MAKE_STD_ZVAL(zvalue); \ ZVAL_BOOL(zvalue, bvalue); \ DBDO_ARRAY_ADD(array, col,zvalue) \ } #define DBDO_ARRAY_LONGADD(array, col, lvalue) { \ zval *zvalue; \ MAKE_STD_ZVAL(zvalue); \ ZVAL_LONG(zvalue, lvalue); \ DBDO_ARRAY_ADD(array, col,zvalue) \ } /* get the data for a specified id */ zval * dbdo_info_get(dbdo_object *obj, zval *object, int id TSRMLS_DC) { zval *ret; MAKE_STD_ZVAL(ret); switch (id) { case 0: /* tablename; */ ZVAL_STRING(ret,obj->table->name, 1); return ret; case 1: /* dsn (alias) */ ZVAL_STRING(ret,obj->table->dsn, 1); return ret; case 2: /* row */ ZVAL_LONG(ret, obj->row_id); return ret; case 3: /* rows in result. */ /* ZVAL_LONG(ret, obj->result ? gda_data_model_get_n_rows (obj->result) : 0 ); */ ZVAL_NULL(ret); return ret; case 4: /* assigned */ array_init(ret); if (obj->result) { /* TODO: check if we have results.. */ /* int column_id; GdaRow *row; // TODO!!! - use schema here - if we are assigning on a new object! !!!! row = (GdaRow *) gda_data_model_get_row (obj->result, obj->row_id); for (column_id = 0; column_id < gda_data_model_get_n_columns (obj->result); column_id++) { zval *value, **element; char *title; ulong h; title = (gchar *) gda_data_model_get_column_title (obj->result, column_id); if (zend_hash_find(obj->assigned_hash, title, strlen(title)+1, (void **) &element) != SUCCESS) { continue; } value = *element; // g_free(title);is this needed?? h = zend_hash_func(title, strlen(title)+1); zend_hash_quick_update((HashTable *) Z_ARRVAL_P(ret), title, strlen(title)+1, h, &value, sizeof(zval *), NULL); } */ return ret; } /* else - no results = use schema */ { /* int row_id; long h; dbdo_schema_load(obj TSRMLS_CC); for (row_id = 0; row_id < (int) gda_data_model_get_n_rows (obj->table->schema); row_id++) { char *title; zval *value, **element; title = (char *) gda_value_get_string ( (GdaValue *) gda_data_model_get_value_at (obj->table->schema, 0, row_id)); if (zend_hash_find(obj->assigned_hash, title, strlen(title)+1, (void **) &element) != SUCCESS) { continue; } value = *element; // g_free(title);is this needed?? h = zend_hash_func(title, strlen(title)+1); zend_hash_quick_update((HashTable *) Z_ARRVAL_P(ret), title, strlen(title)+1, h, &value, sizeof(zval *), NULL); } */ return ret; } case 5: /* fetched */ { int column_id; array_init(ret); /* if (!obj->result) { return ret; } row = (GdaRow *) gda_data_model_get_row (obj->result, obj->row_id); for (column_id = 0; column_id < gda_data_model_get_n_columns (obj->result); column_id++) { zval *value, *ztitle; GdaValue *gdavalue; char *title; ulong h; title = (gchar *) gda_data_model_get_column_title (obj->result, column_id); gdavalue = (GdaValue *) gda_row_get_value (row, column_id); value = dbdo_value_to_zval(gdavalue); // g_free(title);is this needed?? h = zend_hash_func(title, strlen(title)+1); zend_hash_quick_update((HashTable *) Z_ARRVAL_P(ret), title, strlen(title)+1, h, &value, sizeof(zval *), NULL); } */ return ret; } case 6: /* config */ { dbdo_config *config; zval *zconfig[9]; array_init(ret); config = dbdo_config_get(obj->table->dsn TSRMLS_CC); MAKE_STD_ZVAL(zconfig[0]); ZVAL_STRING(zconfig[0], config->require_path ? config->require_path : "", 1); DBDO_ARRAY_ADD(ret,"return_path",zconfig[0]); MAKE_STD_ZVAL(zconfig[1]); ZVAL_STRING(zconfig[1], config->class_name ? config->class_name : "", 1); DBDO_ARRAY_ADD(ret,"class_name",zconfig[1]); MAKE_STD_ZVAL(zconfig[2]); ZVAL_STRING(zconfig[2], config->links ? config->links : "", 1); DBDO_ARRAY_ADD(ret,"links",zconfig[2]); MAKE_STD_ZVAL(zconfig[3]); ZVAL_BOOL(zconfig[3], (int) config->lowercase_columns); DBDO_ARRAY_ADD(ret,"lowercase_columns",zconfig[3]); MAKE_STD_ZVAL(zconfig[4]); ZVAL_BOOL(zconfig[4], (int) config->quote_identifiers); DBDO_ARRAY_ADD(ret,"quote_identifiers",zconfig[4]); MAKE_STD_ZVAL(zconfig[5]); ZVAL_STRING(zconfig[5], config->driver ? config->driver : "", 1); DBDO_ARRAY_ADD(ret,"driver",zconfig[5]); MAKE_STD_ZVAL(zconfig[6]); ZVAL_STRING(zconfig[6], config->connect_dsn ? config->connect_dsn : "", 1); DBDO_ARRAY_ADD(ret,"dsn",zconfig[6]); MAKE_STD_ZVAL(zconfig[7]); ZVAL_STRING(zconfig[7], config->username ? config->username : "", 1); DBDO_ARRAY_ADD(ret,"username",zconfig[7]); MAKE_STD_ZVAL(zconfig[8]); ZVAL_STRING(zconfig[8], config->password ? config->password : "", 1); DBDO_ARRAY_ADD(ret,"password",zconfig[8]); MAKE_STD_ZVAL(zconfig[9]); ZVAL_STRING(zconfig[9], config->description ? config->description : "", 1); DBDO_ARRAY_ADD(ret,"description",zconfig[9]); return ret; } case 7: /* 12 = sequence Column */ if (!obj->table->sequenceColumn) { ZVAL_BOOL(ret, FALSE); } else { ZVAL_STRING(ret, obj->table->sequenceColumn, 1); } return ret; case 8: /* 13 = sequence is native? */ ZVAL_BOOL(ret, obj->table->sequenceIsNative); return ret; case 9: /* 14 = assigned or guessed? */ ZVAL_BOOL(ret, obj->table->sequenceAssigned); return ret; case 10: /* 15 = sequenceName */ if (!obj->table->sequenceName) { ZVAL_BOOL(ret, FALSE); } else { ZVAL_STRING(ret, obj->table->sequenceName, 1); } return ret; case 11: /* keys */ { zval **zconfig; int i; array_init(ret); if (!obj->table->keys_len) { return ret; } zconfig = emalloc(sizeof(zval *) * obj->table->keys_len); for (i = 0; i < obj->table->keys_len; i++) { MAKE_STD_ZVAL(zconfig[i]); ZVAL_STRING(zconfig[i], obj->table->keys[i] , 1); zend_hash_next_index_insert( HASH_OF(ret), (void *) &zconfig[i], sizeof(zval *), NULL); } efree(zconfig); return ret; } case 12: /*"where", */ if (!obj->condition->where) { ZVAL_BOOL(ret, FALSE); } else { ZVAL_STRING(ret, obj->condition->where, 1); } return ret; case 13: /* "group_by", */ if (!obj->condition->groupBy) { ZVAL_BOOL(ret, FALSE); } else { ZVAL_STRING(ret, obj->condition->groupBy, 1); } return ret; case 14: /* "order_by",*/ if (!obj->condition->orderBy) { ZVAL_BOOL(ret, FALSE); } else { ZVAL_STRING(ret, obj->condition->orderBy, 1); } return ret; case 15: /* "having",*/ if (!obj->condition->having) { ZVAL_BOOL(ret, FALSE); } else { ZVAL_STRING(ret, obj->condition->having, 1); } return ret; case 16: /* "limit_from",*/ if (!obj->condition->limit_use) { ZVAL_BOOL(ret, FALSE); } else { ZVAL_LONG(ret, obj->condition->limit_from); } return ret; case 17: /* "limit_qty",*/ if (!obj->condition->limit_use) { ZVAL_BOOL(ret, FALSE); } else { ZVAL_LONG(ret, obj->condition->limit_qty); } return ret; case 18: /* "select" */ if (!obj->condition->data_select) { ZVAL_BOOL(ret, FALSE); } else { ZVAL_STRING(ret, obj->condition->data_select, 1); } return ret; } FREE_ZVAL(ret); return NULL; } dbdo_table * dbdo_table_new(char *dsn, char *name TSRMLS_DC) { dbdo_table *tbls; int i; for (i=0;idsn, dsn)) && (0 == strcmp(tbls->name, name))) { /* got match */ DBDO_DEBUG3(16,"RETURNING EXISTING TABLE OBJECT: %s , %s : %x",dsn, name, tbls); return tbls; } } DBDO_DEBUG2(16,"CREATING NEW TABLE OBJECT: %s , %s",dsn, name); tbls = emalloc(sizeof(dbdo_table)); tbls->dsn = estrdup(dsn); tbls->name = estrdup(name); tbls->schema = NULL; tbls->schema_len = 0; tbls->sequenceColumn = NULL; tbls->sequenceName = NULL; tbls->sequenceIsNative = FALSE; tbls->sequenceAssigned = FALSE; tbls->keys_len = 0; tbls->keys = NULL; DBDO_G(tables_len)++; if (DBDO_G(tables)) { DBDO_G(tables) = (dbdo_table **) erealloc(DBDO_G(tables),DBDO_G(tables_len) * sizeof (dbdo_table *)); } else { DBDO_G(tables) = (dbdo_table **) emalloc(sizeof(dbdo_table *)); } DBDO_G(tables)[DBDO_G(tables_len)-1] = tbls; DBDO_DEBUG3(16,"RETURNING NEW TABLE OBJECT: %s , %s : %x",tbls->dsn, tbls->name, tbls); return tbls; } int dbdo_connect(dbdo_object *obj TSRMLS_DC) { /* get the configuration for this alias set obj->con to dbh */ dbdo_config *config; zval *driver_options = NULL; pdo_driver_t *driver = NULL; int is_persistent = 1; // have we already got the connection! if (obj->con) { return; } config = dbdo_config_get(obj->table->dsn TSRMLS_CC); if (!config->driver) { php_error(E_ERROR, "configuration error, Driver not specified for alias '%s'", obj->table->dsn); return; } driver = pdo_find_driver(config->driver, strlen(config->driver)); if (!driver) { /* Question: should you be speculatively loading drivers!??? */ php_error(E_ERROR, "could not find driver %s", config->driver); return; } /* for the time being lets assume always persistant... */ { zval **v; int plen; char *hashkey = NULL; list_entry *le; pdo_dbh_t *pdbh = NULL; plen = spprintf(&hashkey, 0, "PDO:DBH:DSN=%s:%s:%s:%s", config->connect_dsn, config->username ? config->username : "", config->password ? config->password : "", obj->table->dsn); /* let's see if we have one cached.... */ if (is_persistent && SUCCESS == zend_hash_find(&EG(persistent_list), hashkey, plen+1, (void*)&le)) { if (Z_TYPE_P(le) == php_pdo_list_entry()) { pdbh = (pdo_dbh_t*)le->ptr; /* is the connection still alive ? */ if (pdbh->methods->check_liveness && FAILURE == (pdbh->methods->check_liveness)(pdbh TSRMLS_CC)) { /* nope... need to kill it */ pdbh = NULL; } } } if (!pdbh) { /* need a brand new pdbh */ pdbh = pecalloc(1, sizeof(*pdbh), 1); if (!pdbh) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "out of memory while allocating PDO handle"); /* NOTREACHED */ } pdbh->is_persistent = 1; pdbh->persistent_id = pemalloc(plen + 1, 1); memcpy((char *)pdbh->persistent_id, hashkey, plen+1); pdbh->persistent_id_len = plen+1; pdbh->refcount = 1; } if (pdbh) { /* let's copy the emalloc bits over from the other handle */ if (obj->con) { pdbh->ce = obj->con->ce; pdbh->properties = obj->con->properties; /* kill the non-persistent thingamy */ efree(obj->con); } /* switch over to the persistent one */ obj->con = pdbh; obj->con->refcount++; } if (hashkey) { efree(hashkey); } } obj->con->data_source_len = strlen(config->connect_dsn )+ 1; obj->con->data_source = (const char*)pestrdup(config->connect_dsn, is_persistent); obj->con->username = config->username ? pestrdup(config->username, is_persistent) : NULL; obj->con->password = config->password ? pestrdup(config->password, is_persistent) : NULL; obj->con->auto_commit = 1; //TODO add me to config.. if (!obj->con->data_source || (config->username && !obj->con->username) || (config->password && !obj->con->password)) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "out of memory"); } if (driver->db_handle_factory(obj->con, driver_options TSRMLS_CC)) { /* all set */ if (is_persistent) { list_entry le; /* register in the persistent list etc. */ /* we should also need to replace the object store entry, since it was created with emalloc */ le.type = php_pdo_list_entry(); le.ptr = obj->con; if (FAILURE == zend_hash_update(&EG(persistent_list), (char*)obj->con->persistent_id, obj->con->persistent_id_len, (void*)&le, sizeof(le), NULL)) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Failed to register persistent entry"); } } return; } return 1; } /* {{{ proto mixed DBDO::factory ( $alias, $table ) instantate a class based on the config. */ PHP_METHOD(dbdo,factory) { /* only supports dsn : key : valueat present */ zval *eretval, *c_ret, constructor; int v, dsn_len, table_len; char *cmd, *fn, *compiled_string_description, *dsn, *table, *classname, *lc_name, *dsn_cp, *table_cp; dbdo_config *con; zend_class_entry **ce; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &dsn, &dsn_len, &table, &table_len) == FAILURE) { RETURN_FALSE; } con = dbdo_config_get(dsn TSRMLS_CC); if (!con->class_name || !strlen(con->class_name)) { /* these are preventable errors: so php_error it.. */ php_error_docref(NULL TSRMLS_CC, E_ERROR, "DBDO::factory - configuration class_name is not set, \n" "class_name: %s", con->class_name ? con->class_name : "EMPTY" ); return; } /* a does the class exist */ spprintf(&classname, 0, con->class_name, table); /* now does the class exist!! (lowercase for searching?) */ lc_name = emalloc(strlen(classname) + 1); zend_str_tolower_copy(lc_name, classname, strlen(classname)); /* we need to dupe these values as they seem to get lost after doing any type of call stuff. */ if (SUCCESS != zend_hash_find(EG(class_table), lc_name, strlen(classname)+1, (void **) &ce)) { /* require_once '%s';\0 */ if (!con->require_path || !strlen(con->require_path)) { efree(classname); efree(lc_name); php_error_docref(NULL TSRMLS_CC, E_ERROR, "DBDO::factory - configuration problem: require_path is not set, \n" "require_path: %s", con->require_path ? con->require_path : "EMPTY" ); return; } /* I cant think of any better way to do this.. - this is cheesy but it works.. */ spprintf(&fn, 0, con->require_path, table); spprintf(&cmd, 0, "require_once '%s';",fn); efree(fn); eretval = emalloc(sizeof(zval)); compiled_string_description = zend_make_compiled_string_description("DBDO::factory()" TSRMLS_CC); zend_eval_string(cmd, eretval, compiled_string_description TSRMLS_DC); efree(compiled_string_description ); efree(cmd); zval_dtor(eretval); FREE_ZVAL(eretval); } /* create the instance */ if (SUCCESS != zend_hash_find(EG(class_table), lc_name, strlen(classname)+1, (void **) &ce)) { efree(lc_name); zend_throw_exception_ex( dbdo_class_entry_exception_ce , 0 TSRMLS_CC, "DBDO::factory - class %s does not exist", classname); efree(classname); return; } efree(lc_name); if ((*ce)->ce_flags & ZEND_ACC_INTERFACE) { zend_throw_exception_ex( dbdo_class_entry_exception_ce , 0 TSRMLS_CC, "DBDO::factory - class %s is an interface", classname); efree(classname); return; } efree(classname); /* now instantate it!! */ object_init_ex(return_value, *ce); /* call the constructor with dsn & tablename */ if (zend_hash_exists(&Z_OBJCE_P(return_value)->function_table, ZEND_CONSTRUCTOR_FUNC_NAME, sizeof(ZEND_CONSTRUCTOR_FUNC_NAME))) { zval *cb_data[2]; zval **cb_args[2]; /* MAKE_STD_ZVAL(c_ret); */ c_ret = NULL; /*ALLOC_ZVAL(constructor); */ DBDO_DEBUG2(16,"calling constructor on object with args %s , %s",dsn, table); MAKE_STD_ZVAL(cb_data[0]); ZVAL_STRING(cb_data[0], dsn, 1); cb_args[0] = &cb_data[0]; MAKE_STD_ZVAL(cb_data[1]); ZVAL_STRING(cb_data[1], table, 1); cb_args[1] = &cb_data[1]; ZVAL_STRING(&constructor, ZEND_CONSTRUCTOR_FUNC_NAME, 1); if (call_user_function_ex(EG(function_table), &return_value, &constructor, &c_ret, 2, cb_args, 0, NULL TSRMLS_CC) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Error calling constructor"); } zval_dtor(&constructor); zval_dtor(cb_data[0]); FREE_ZVAL(cb_data[0]); zval_dtor(cb_data[1]); FREE_ZVAL(cb_data[1]); if (c_ret) { zval_dtor(c_ret); FREE_ZVAL(c_ret); } } } /* }}} */ /* {{{ proto mixed DBDO::_construct ( $alias, $table ) instantate the object */ PHP_METHOD(dbdo,__construct) { zval *object = getThis(); char *alias_name; char *table_name; int alias_name_length; int table_name_length; dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &alias_name, &alias_name_length, &table_name,&table_name_length) == FAILURE) { RETURN_FALSE; } if (!object) { DBDO_DEBUG0(16,"NO OBJECT!"); RETURN_FALSE; } obj->table = dbdo_table_new(alias_name, table_name); if (!dbdo_connect(obj TSRMLS_CC)) { DBDO_DEBUG0(16,"CONNECT FAILED!"); RETURN_FALSE; } /* should we allow anonymous (eg. no table ??) */ DBDO_DEBUG0(16,"CONSTUCTOR DONE!!"); } /* }}} */ /* {{{ proto array $dbdo->info ( [string $what] ) Retrieve information about an object. */ PHP_METHOD(dbdo,info) { /* info retreieved: - tablename - dsn - database name - connection string - provider - username - ???password??? - current row - numrows - assigned data - retrieved data - config ? - sequenceColumn - sequenceAssigned - sequenceIsNative - sequenceName - where ... all condition variables... */ zval *object = getThis(); zval *rv[DBDO_INFO_LEN]; int i,key_len; char *key = NULL; dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &key, &key_len) == FAILURE) { RETURN_FALSE; } if (key) { zval *ret; i = dbdo_info_get_id(key); if (i == -1) { php_error(E_ERROR, "DBDO::info() no information for unknown key %s", key); RETURN_FALSE; } ret = dbdo_info_get(obj, object, i TSRMLS_CC); RETURN_ZVAL(ret ,0,1); } array_init(return_value); for (i=0;iconnect_dsn = estrdup( value); } if (strcmp(key,"username") ==0) { con->username = estrdup( value); } if (strcmp(key,"password") ==0) { con->password = estrdup(value); } if (strcmp(key,"description") ==0) { con->description = estrdup(value); } if (strcmp(key,"driver") ==0) { con->driver = estrdup(value); } if (strcmp(key,"require_path") ==0) { con->require_path =estrdup( value); } if (strcmp(key,"class_name") ==0) { con->class_name = estrdup( value); } if (strcmp(key,"links") ==0) { con->links = estrdup(value); } if (strcmp(key,"lowercase_columns") ==0) { con->lowercase_columns = bvalue; } if (strcmp(key,"quote_identifiers") ==0) { con->quote_identifiers = bvalue; } RETURN_TRUE; } /* }}} */ /* {{{ proto DBDO::debugLevel (mixed int) set the debug behavior. (debugging messages are issued as E_NOTICE, preceded by DBDO:) you can capture them by using set_error_handler.. */ PHP_METHOD(dbdo,debugLevel) { int newlevel; if (!ZEND_NUM_ARGS()) { RETURN_LONG(DBDO_G(debugLevel)); } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &newlevel) == FAILURE) { RETURN_FALSE; } DBDO_G(debugLevel) = newlevel; RETURN_LONG(DBDO_G(debugLevel)); } /* }}} */ /* {{{ proto $dbdo->reproduce() creates a clean new instance of the object with object properties and resultset removed. */ PHP_METHOD(dbdo,reproduce) { zval *tmp; zval *object = getThis(); zval *zclone; dbdo_object *dclone; zend_class_entry *ce = zend_objects_get_address(object TSRMLS_CC)->ce; object_init_ex(return_value, ce); return_value->value.obj = dbdo_clone(object TSRMLS_CC); dclone = zend_object_store_get_object(return_value TSRMLS_CC); dclone->row_id = -1; /* reduce refcount on result (before blanking) if (G_IS_OBJECT(dclone->result)) { g_object_unref(dclone->result); } */ dclone->result = NULL; if (dclone->condition) { dclone->condition->refcount--; } dclone->condition = NULL; zend_hash_destroy(dclone->zo.properties); zend_hash_init(dclone->zo.properties, 0, NULL, ZVAL_PTR_DTOR, 0); zend_hash_copy(dclone->zo.properties, &ce->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); } /* }}} */ int dbdo_schema_load_mysql(dbdo_object *obj TSRMLS_DC) { char *statement; int statement_len, i, row; pdo_stmt_t *stmt; struct pdo_column_data *cols = NULL; struct { const char *name; int type; } types[] = { { "blob", DBDO_TYPE_BINARY }, { "bigint", DBDO_TYPE_INT }, { "char", DBDO_TYPE_STRING }, { "date", DBDO_TYPE_DATE }, { "datetime", DBDO_TYPE_DATETIME }, { "decimal", DBDO_TYPE_MONEY }, { "double", DBDO_TYPE_DOUBLE }, { "enum", DBDO_TYPE_STRING }, { "float", DBDO_TYPE_DOUBLE }, { "int", DBDO_TYPE_INT }, { "integer", DBDO_TYPE_INT }, { "long", DBDO_TYPE_INT }, { "longblob", DBDO_TYPE_BINARY }, { "longtext", DBDO_TYPE_STRING }, { "mediumint", DBDO_TYPE_INT }, { "mediumblob", DBDO_TYPE_BINARY }, { "mediumtext", DBDO_TYPE_STRING }, { "set", DBDO_TYPE_STRING }, { "smallint", DBDO_TYPE_INT }, { "text", DBDO_TYPE_STRING }, { "tinyint", DBDO_TYPE_INT }, /* really a boolean */ { "tinyblob", DBDO_TYPE_BINARY }, { "tinytext", DBDO_TYPE_STRING }, { "time", DBDO_TYPE_TIME }, { "timestamp", DBDO_TYPE_MYSQLTIMESTAMP }, { "varchar", DBDO_TYPE_STRING }, { "year", DBDO_TYPE_INT } }; statement_len = spprintf(&statement, 0, "DESCRIBE %s", obj->table->name); DBDO_DEBUG(16, "mysql_schema execute %s", statement); stmt = ecalloc(1, sizeof(*stmt)); stmt->active_query_string = stmt->query_string = estrndup(statement, statement_len); stmt->active_query_stringlen = stmt->query_stringlen = statement_len; stmt->default_fetch_type = PDO_FETCH_BOTH; stmt->dbh = obj->con; if (!obj->con->methods->preparer( obj->con, statement, statement_len, stmt, NULL TSRMLS_CC)) // driver_options { efree(stmt); PDO_HANDLE_DBH_ERR(); return 0; } stmt->methods->executer(stmt TSRMLS_CC); row = 0; while(stmt->methods->fetcher(stmt, PDO_FETCH_ORI_NEXT, 0 TSRMLS_CC)) { char *col_type, *tmp_str, *col_lenstr, *col_extra; int tmp_int, col_len, i, caller_frees = 0; if (!stmt->columns && !pdo_stmt_describe_columns(stmt TSRMLS_CC)) { printf("help - unable to describe columns"); return 0; } DBDO_DEBUG0(16, "mysql_schema got row"); if (cols) { cols = erealloc(cols,sizeof(struct pdo_column_data) * (row+1)); } else { cols = emalloc(sizeof(struct pdo_column_data)); } cols[row].dbdo_stuff = emalloc(sizeof(dbdo_column_data *)); // since we always get this in the same order!!! //Field,Type,Null,Key,Default,Extra stmt->methods->get_col(stmt, 0, &(tmp_str), &(cols[row].namelen), &caller_frees TSRMLS_CC); cols[row].name = estrdup(tmp_str); cols[row].precision = 0; stmt->methods->get_col(stmt, 1, &col_type, &tmp_int, &caller_frees TSRMLS_CC); cols[row].maxlen = 0; col_lenstr = php_memnstr(col_type, "(", 1, col_type+ tmp_int); *col_lenstr = '\0'; ((dbdo_column_data *) cols[row].dbdo_stuff)->native_type_name = estrdup(col_type); col_len = 0; if (col_lenstr != NULL) { //col_lenstr--; *col_lenstr = '\0'; col_lenstr++; col_extra = php_memnstr(col_lenstr, ")", 1, col_lenstr + strlen(col_lenstr)); *col_extra = '\0'; /* todo decimal (10,2) */ cols[row].maxlen= atoi(col_lenstr); col_extra++; } /* abstract_type */ for (i =0; i < sizeof (types) ; i++ ) { if (!strcmp(((dbdo_column_data *) cols[row].dbdo_stuff)->native_type_name,types[i].name)) { ((dbdo_column_data *) cols[row].dbdo_stuff)->abstract_type = types[i].type; break; } } ((dbdo_column_data *) cols[row].dbdo_stuff)->abstract_flags = 0; /* null */ stmt->methods->get_col(stmt, 2, &col_type, &tmp_int, &caller_frees TSRMLS_CC); if (strcmp(col_type, "YES")) { ((dbdo_column_data *) cols[row].dbdo_stuff)->abstract_flags += DBDO_FLAGS_NOTNULL; } /* primary / unique */ stmt->methods->get_col(stmt, 3, &col_type, &tmp_int, &caller_frees TSRMLS_CC); if (0==strcmp(col_type, "PRI")) { ((dbdo_column_data *) cols[row].dbdo_stuff)->abstract_flags += DBDO_FLAGS_PRIMARYKEY; } if (0==strcmp(col_type, "UNI")) { ((dbdo_column_data *) cols[row].dbdo_stuff)->abstract_flags += DBDO_FLAGS_UNIQUEINDEX; } /* autoincrement (crap test) */ stmt->methods->get_col(stmt, 5, &col_type, &tmp_int, &caller_frees TSRMLS_CC); if (!strcmp(col_type, "auto_increment")) { ((dbdo_column_data *) cols[row].dbdo_stuff)->abstract_flags += DBDO_FLAGS_AUTOINCREMENT; } /*printf("structure: %s : (%d) : %s : %d : %d\n", cols[row].name, cols[row].maxlen, cols[row].native_type_name , cols[row].abstract_type, cols[row].abstract_flags ); */ row++; } obj->table->schema = cols; obj->table->schema_len = row; return 1; } /* load the schema into the object. */ int dbdo_schema_load(dbdo_object *obj TSRMLS_DC) { /* loop through the schema and find it.. */ //GdaParameterList * parm_list; if (obj->table->schema) { return 1; /* already got it... */ } if (!dbdo_connect(obj)) { return 0; } /* work out a sensible way to do this!! */ return dbdo_schema_load_mysql(obj); } int dbdo_schema_get_type(dbdo_object *obj, char *colname) { int i; for(i=0;itable->schema_len;i++) { if (strcmp(obj->table->schema[i].name,colname) != 0) { continue; } return ((dbdo_column_data *) obj->table->schema[i].dbdo_stuff)->abstract_type; } return DBDO_TYPE_INVALID; } enum pdo_param_type dbdo_type_to_pdo(int dbdo_type) { switch (dbdo_type) { case DBDO_TYPE_INVALID: return 0; case DBDO_TYPE_INT: return PDO_PARAM_INT; case DBDO_TYPE_MONEY: // this is a little dumb... case DBDO_TYPE_DOUBLE: case DBDO_TYPE_STRING: return PDO_PARAM_STR; case DBDO_TYPE_BINARY: return PDO_PARAM_LOB; case DBDO_TYPE_DATE: case DBDO_TYPE_TIME: case DBDO_TYPE_DATETIME: return PDO_PARAM_STR; case DBDO_TYPE_MYSQLTIMESTAMP: return PDO_PARAM_STR; } } /* {{{ proto mixed $dbdo->schema ( [string column] ) get schema info about a table (no args returns a list of columns), */ PHP_METHOD(dbdo,schema) { zval *object = getThis(), *zvalue; dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); int rows, column_id, row_id, col, column_name_length, match_id = -1; char *string, *title, *ntitle, *column_name; ulong h; dbdo_schema_load(obj TSRMLS_CC); if (ZEND_NUM_ARGS() == 0) { /* return a list of columns */ array_init(return_value); for (row_id = 0; row_id < obj->table->schema_len;row_id++) { //printf("ADD: %d : %s\n", row_id, obj->table->schema[row_id].name); MAKE_STD_ZVAL(zvalue); ZVAL_STRING(zvalue, obj->table->schema[row_id].name, 1); zend_hash_next_index_insert(HASH_OF(return_value), &zvalue, sizeof(zval *), NULL); } return; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &column_name, &column_name_length) == FAILURE) { RETURN_FALSE; } for (row_id = 0; row_id < obj->table->schema_len;row_id++) { DBDO_DEBUG2(16,"compare %s ?= %s", obj->table->schema[row_id].name, column_name); if (!strcmp(obj->table->schema[row_id].name, column_name)) { match_id = row_id; break; } } if (match_id < 0) { DBDO_DEBUG0(16,"lost it"); RETURN_FALSE; } array_init(return_value); add_assoc_string( return_value, "name", obj->table->schema[match_id].name, 1); add_assoc_string( return_value, "type", ((dbdo_column_data *) obj->table->schema[match_id].dbdo_stuff)->native_type_name, 1); add_assoc_long( return_value, "size", obj->table->schema[match_id].maxlen); add_assoc_long( return_value, "precision", obj->table->schema[match_id].precision); add_assoc_long( return_value, "abstract type", ((dbdo_column_data *) obj->table->schema[match_id].dbdo_stuff)->abstract_type); add_assoc_bool( return_value, "not null", ((dbdo_column_data *) obj->table->schema[match_id].dbdo_stuff)->abstract_flags & DBDO_FLAGS_NOTNULL); add_assoc_bool( return_value, "primary key", ((dbdo_column_data *) obj->table->schema[match_id].dbdo_stuff)->abstract_flags & DBDO_FLAGS_PRIMARYKEY); add_assoc_bool( return_value, "unique index", ((dbdo_column_data *) obj->table->schema[match_id].dbdo_stuff)->abstract_flags & DBDO_FLAGS_UNIQUEINDEX); add_assoc_bool( return_value, "auto increment", ((dbdo_column_data *) obj->table->schema[match_id].dbdo_stuff)->abstract_flags & DBDO_FLAGS_AUTOINCREMENT); add_assoc_string( return_value, "default", "", 1); } /* {{{ proto mixed $dbdo->get ( [value|key] [value] ) perform the select on either the sequence key column = value, or a column=>value combo - fetching the result into the object. - returns 0 | 1 - throws exception if more than 1 row returned.. TODO: this should really understand things like double primary keys (perhaps by using an assoc. array as an argument) */ PHP_METHOD(dbdo,get) { zval *object = getThis(), *search_value; char *search_key = NULL, *sql_str, *skey_cpy; int key_len, value_len, rows, col_type, nparams = 0; pdo_stmt_t *stmt; struct pdo_bound_param_data param; struct pdo_bound_param_data *params; dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); smart_str cmd = {0}; if (obj->result) { php_error( E_WARNING, "DBDO::%s(): was run on a object with results", get_active_function_name(TSRMLS_C)); RETURN_FALSE; } /* test ? - if I send it an it, are we going to be ok? */ if (ZEND_NUM_ARGS() == 1) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &search_value ) == FAILURE) { RETURN_FALSE; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &search_key, &key_len, &search_value ) == FAILURE) { RETURN_FALSE; } } smart_str_appends(&cmd, "SELECT "); if (obj->condition->data_select != NULL) { smart_str_appends(&cmd, obj->condition->data_select); } else { smart_str_appends(&cmd, "*"); } smart_str_appends(&cmd, " FROM "); smart_str_appends(&cmd, obj->table->name); smart_str_appends(&cmd, " WHERE "); /* create an empty statement */ stmt = ecalloc(1, sizeof(*stmt)); stmt->default_fetch_type = PDO_FETCH_BOTH; stmt->dbh = obj->con; if (!obj->con->methods->preparer( obj->con, "", 0, stmt, NULL TSRMLS_CC)) // driver_options { efree(stmt); PDO_HANDLE_DBH_ERR(); return 0; } /* now at this point, - if the driver supports prepared statements = use it otherwise dont bother... (as we end up going thru the parse stage twice!! */ if (0) { // THIS WILL NNOT WORK HERE AS WE HAVE NOT PREPARED THE STATEMENT YET!!! enum pdo_param_event event_type; stmt->methods->param_hook(stmt, ¶m, event_type TSRMLS_CC); /* event_type = PDO_PARAM_EVT_EXEC_PRE (before) ; */ /* event_type = PDO_PARAM_EVT_EXEC_POST (after) ; */ } /* 1 arg = use sequence keys.. */ if (!search_key) { dbdo_sequence_guess(obj); if (obj->table->sequenceColumn == NULL) { smart_str_free(&cmd); zend_throw_exception(dbdo_class_entry_exception_ce , "unable to determine which column to use for get ", 0 TSRMLS_CC); return; } search_key = obj->table->sequenceColumn; } col_type = dbdo_schema_get_type(obj, search_key); if (col_type < 0) { smart_str_free(&cmd); zend_throw_exception_ex( dbdo_class_entry_exception_ce , 0 TSRMLS_CC, "unable to determine type of sequence column '%s'", search_key); return; } smart_str_appends(&cmd, search_key); smart_str_appends(&cmd, " = "); if (PDO_PLACEHOLDER_NONE == stmt->supports_placeholders) { // write the value (with optional quotes... sql_str = dbdo_zval_to_sql_string(obj, search_value, col_type); smart_str_appends(&cmd, sql_str); efree(sql_str); } else { //if (params) { // params = erealloc(params,sizeof(struct pdo_bound_param_data) * (row+1)); //} else { params = emalloc(sizeof(struct pdo_bound_param_data)); //} spprintf(¶ms[0].name, 0, ":%d", 0); // long params[0].namelen = strlen(param.name);//strlen params[0].parameter = search_value; params[0].param_type = dbdo_type_to_pdo(col_type); // ¶m.max_value_len = // ¶m.driver_params smart_str_appends(&cmd, " "); smart_str_appends(&cmd, params[0].name); smart_str_appends(&cmd, " "); params[0] = param; nparams = 1; } smart_str_0(&cmd); /* run the pre params stuff */ stmt = ecalloc(1, sizeof(*stmt)); stmt->query_string = estrdup(cmd.c); stmt->query_stringlen = strlen(cmd.c); stmt->default_fetch_type = PDO_FETCH_BOTH; if (PDO_PLACEHOLDER_NONE == stmt->supports_placeholders) { stmt->active_query_string = stmt->query_string ; stmt->active_query_stringlen = stmt->query_stringlen ; } stmt->dbh = obj->con; DBDO_DEBUG(1,"PREPARE:\n%s\n",cmd.c); if (!obj->con->methods->preparer( obj->con, stmt->query_string, stmt->query_stringlen, stmt, NULL TSRMLS_CC)) // driver_options { efree(stmt); PDO_HANDLE_DBH_ERR(); RETURN_FALSE; } if (nparams > 0) { int i; for (i = 0; i < nparams ; i++) { stmt->methods->param_hook(stmt, ¶ms[i], PDO_PARAM_EVT_EXEC_PRE TSRMLS_CC); } } /* run the query */ stmt->methods->executer(stmt TSRMLS_CC); /* run the post params stuff */ if (nparams > 0) { int i; for (i = 0; i < nparams ; i++) { stmt->methods->param_hook(stmt, ¶ms[i], PDO_PARAM_EVT_EXEC_POST TSRMLS_CC); } } /* TODO: set our result object */ smart_str_free(&cmd); /* check our result object is not null !? - throw exception if necessary.. */ /* return the correct number of rows */ rows = 1; /* Throw exception if more than one row is returned. */ RETURN_LONG(rows); } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */