/* +----------------------------------------------------------------------+ | 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: Alan Knowles | +----------------------------------------------------------------------+ */ /* $Id: dbdo.c,v 1.32 2004/11/22 14:09:02 alan_k 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" /* If you declare any globals in php_dbdo.h uncomment this: */ ZEND_DECLARE_MODULE_GLOBALS(dbdo) /* True global resources - no need for thread safety here */ static zend_object_handlers dbdo_object_handlers; static zend_object_handlers dbdo_object_exception_handlers; /* static void dbdo_objects_dtor(void *object, zend_object_handle handle TSRMLS_DC) { dbdo_object *intern = ( dbdo_object *) object; if (!intern) { return; } if (intern->zo.properties) { zend_hash_destroy(intern->zo.properties); FREE_HASHTABLE(intern->zo.properties); } efree(intern); } */ 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 (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 */ 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->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; } /* }}} */ 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; } /* {{{ dbdo_functions[] * * Every user visible function must have an entry in dbdo_functions[]. */ function_entry dbdo_functions[] = { PHP_FE(dbdo_new, NULL) PHP_FE(dbdo_factory, NULL) PHP_FE(dbdo_get, NULL) PHP_FE(dbdo_fetch, NULL) PHP_FE(dbdo_fetchAll, NULL) PHP_FE(dbdo_count, NULL) PHP_FE(dbdo_numRows, NULL) PHP_FE(dbdo_insert, NULL) PHP_FE(dbdo_update, NULL) PHP_FE(dbdo_delete, NULL) PHP_FE(dbdo_query, NULL) PHP_FE(dbdo_toArray, NULL) PHP_FE(dbdo_assignFrom, NULL) PHP_FE(dbdo_valueSet, NULL) PHP_FE(dbdo_valueGet, NULL) PHP_FE(dbdo_schema, NULL) PHP_FE(dbdo_saveDataSource, NULL) PHP_FE(dbdo_selectAdd, NULL) PHP_FE(dbdo_whereAdd, NULL) PHP_FE(dbdo_limit, NULL) PHP_FE(dbdo_orderBy, NULL) PHP_FE(dbdo_groupBy, NULL) PHP_FE(dbdo_having, NULL) PHP_FE(dbdo_tableName, NULL) PHP_FE(dbdo_sequenceKey, NULL) PHP_FE(dbdo_debugLevel, NULL) PHP_FE(dbdo_config, NULL) {NULL, NULL, NULL} /* Must be the last line in dbdo_functions[] */ }; /* }}} */ function_entry dbdo_funcs_exception[] = { {NULL, NULL, NULL} }; #define DBDO_ME_MAPPING(name, func_name, arg_types,flags) \ ZEND_FENTRY(name, ZEND_FN(func_name), arg_types,flags) /* and now our object.. */ function_entry dbdo_class_functions[] = { DBDO_ME_MAPPING(__construct, dbdo_new, NULL,0) DBDO_ME_MAPPING(factory, dbdo_factory, NULL, ZEND_ACC_STATIC|ZEND_ACC_PUBLIC) DBDO_ME_MAPPING(get, dbdo_get, NULL,0) DBDO_ME_MAPPING(fetch, dbdo_fetch, NULL,0) DBDO_ME_MAPPING(fetchAll, dbdo_fetchAll, NULL,0) DBDO_ME_MAPPING(count, dbdo_count, NULL,0) DBDO_ME_MAPPING(numRows, dbdo_numRows, NULL,0) DBDO_ME_MAPPING(insert, dbdo_insert, NULL,0) DBDO_ME_MAPPING(update, dbdo_update, NULL,0) DBDO_ME_MAPPING(delete, dbdo_delete, NULL,0) DBDO_ME_MAPPING(query, dbdo_query, NULL,0) DBDO_ME_MAPPING(toArray, dbdo_toArray, NULL,0) DBDO_ME_MAPPING(assignFrom, dbdo_assignFrom, NULL,0) DBDO_ME_MAPPING(valueSet, dbdo_valueSet, NULL,0) DBDO_ME_MAPPING(valueGet, dbdo_valueGet, NULL,0) DBDO_ME_MAPPING(schema, dbdo_schema, NULL,0) DBDO_ME_MAPPING(saveDataSource, dbdo_saveDataSource, NULL, ZEND_ACC_STATIC|ZEND_ACC_PUBLIC) DBDO_ME_MAPPING(debugLevel, dbdo_debugLevel, NULL, ZEND_ACC_STATIC|ZEND_ACC_PUBLIC) DBDO_ME_MAPPING(config, dbdo_config, NULL, ZEND_ACC_STATIC|ZEND_ACC_PUBLIC) DBDO_ME_MAPPING(selectAdd, dbdo_selectAdd, NULL,0) DBDO_ME_MAPPING(whereAdd, dbdo_whereAdd, NULL,0) DBDO_ME_MAPPING(limit, dbdo_limit, NULL,0) DBDO_ME_MAPPING(orderBy, dbdo_orderBy, NULL,0) DBDO_ME_MAPPING(groupBy, dbdo_groupBy, NULL,0) DBDO_ME_MAPPING(having, dbdo_having, NULL,0) DBDO_ME_MAPPING(tableName, dbdo_tableName, NULL,0) DBDO_ME_MAPPING(sequenceKey, dbdo_sequenceKey, NULL,0) {NULL, NULL, NULL} }; /* {{{ dbdo_module_entry */ zend_module_entry dbdo_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif "dbdo", NULL, PHP_MINIT(dbdo), PHP_MSHUTDOWN(dbdo), PHP_RINIT(dbdo), /* Replace with NULL if there's nothing to do at request start */ PHP_RSHUTDOWN(dbdo), /* Replace with NULL if there's nothing to do at request end */ PHP_MINFO(dbdo), #if ZEND_MODULE_API_NO >= 20010901 "0.1", /* Replace with version number for your extension */ #endif STANDARD_MODULE_PROPERTIES }; /* }}} */ #ifdef COMPILE_DL_DBDO ZEND_GET_MODULE(dbdo) #endif /* {{{ PHP_INI */ /* Remove comments and fill if you need to have entries in php.ini PHP_INI_BEGIN() STD_PHP_INI_ENTRY("dbdo.global_value", "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_dbdo_globals, dbdo_globals) STD_PHP_INI_ENTRY("dbdo.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_dbdo_globals, dbdo_globals) PHP_INI_END() */ /* }}} */ /* {{{ php_dbdo_init_globals */ /* Uncomment this function if you have INI entries */ static void php_dbdo_init_globals(zend_dbdo_globals *dbdo_globals) { int argc = 1,i; char **argv = NULL; argv = (char **)g_new(char *, argc); argv[0] = g_strdup("-"); gda_init ("TestGDA", "0.1", argc, argv); dbdo_globals->client = gda_client_new (); /* 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) { /* If you have INI entries, uncomment these lines */ ZEND_INIT_MODULE_GLOBALS(dbdo, php_dbdo_init_globals, NULL); /* REGISTER_INI_ENTRIES(); */ { int i; zend_class_entry ce; memcpy(&dbdo_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); dbdo_object_handlers.read_property = dbdo_property_read; dbdo_object_handlers.has_property = dbdo_property_has; dbdo_object_handlers.get_properties = dbdo_properties_get; dbdo_object_handlers.clone_obj = dbdo_clone; INIT_CLASS_ENTRY(ce, "DBDO", dbdo_class_functions); ce.create_object = dbdo_objects_new; /* zend_hash_init(&ce.constants_table, 0, NULL, ZVAL_PTR_DTOR, 0); */ dbdo_class_entry_ce = zend_register_internal_class(&ce TSRMLS_CC); 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); } { zval *con; 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 */ { zval *con; 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); } } { zend_class_entry ce; memcpy(&dbdo_object_exception_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); ce.create_object = dbdo_object_exception_new; INIT_CLASS_ENTRY(ce, "DBDO_Exception", dbdo_funcs_exception); dbdo_class_entry_exception_ce = zend_register_internal_class_ex(&ce, zend_exception_get_default(), NULL TSRMLS_CC); dbdo_object_exception_handlers.clone_obj = NULL; } return SUCCESS; } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ PHP_MSHUTDOWN_FUNCTION(dbdo) { /* uncomment this line if you have INI entries UNREGISTER_INI_ENTRIES(); */ return SUCCESS; } /* }}} */ /* Remove if there's nothing to do at request start */ /* {{{ PHP_RINIT_FUNCTION */ PHP_RINIT_FUNCTION(dbdo) { return SUCCESS; } /* }}} */ /* {{{ PHP_RSHUTDOWN_FUNCTION */ PHP_RSHUTDOWN_FUNCTION(dbdo) { /* TODO: free sequences, tables .. */ int i; /* free client */ if (DBDO_G(client)) { gda_client_close_all_connections ( DBDO_G(client)); g_object_unref(G_OBJECT( DBDO_G(client))); DBDO_G(client) = NULL; } /* 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; /*DBDO_G(config) = 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) { g_object_unref(table->schema); } if (table->sequenceColumn) { efree(table->sequenceColumn); } if (table->sequenceName) { efree(table->sequenceName); } 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; 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_end(); /* Remove comments if you have entries in php.ini DISPLAY_INI_ENTRIES(); */ } /* }}} */ /* }}} */ /* constructor! */ PHP_FUNCTION(dbdo_saveDataSource) { char *name, *provider, *cnc_string, *description, *username, *password; int name_l, provider_l, cnc_string_l, description_l, username_l, password_l; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssssss", &name, &name_l, &provider, &provider_l, &cnc_string, &cnc_string_l, &description, &description_l, &username, &username_l, &password, &password_l ) == FAILURE) { RETURN FALSE; } gda_config_save_data_source (name, provider, cnc_string, description, username,password); } PHP_FUNCTION(dbdo_new) { zval *object = getThis(); char *database_name; char *table_name; int database_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", &database_name, &database_name_length, &table_name,&table_name_length) == FAILURE) { RETURN_FALSE; } if (!object) { DBDO_DEBUG0(16,"NO OBJECT!"); RETURN_FALSE; } obj->table = dbdo_table_new(database_name, table_name); if (!dbdo_connect(obj TSRMLS_CC)) { DBDO_DEBUG0(16,"CONNECT FAILED!"); RETURN_FALSE; } /* should we allow anonymous (eg. no table ??) */ /* schema loading should be done as required, so cached calls work. if (!dbdo_schema_load(obj TSRMLS_CC)) { RETURN_FALSE; } */ DBDO_DEBUG0(16,"CONSTUCTOR DONE!!"); } /* {{{ proto mixed DBDO::factory ( $dsn, $table ) instantate a class based on the config. */ PHP_FUNCTION(dbdo_factory) { /* only supports dsn : key : valueat present */ int dsn_len,table_len; char *dsn, *table, *classname, *lc_name, *dsn_cp, *table_cp; zval *c_ret, constructor; dbdo_config *con; zend_class_entry **ce; zval *eretval; int v; char *cmd, *fn; char *compiled_string_description; 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 || !con->require_path) { zend_throw_exception_ex( dbdo_class_entry_exception_ce , 0 TSRMLS_CC, "DBDO::factory - class_name and require_path configuration has not been set - use DBDO::config('%s','class_name', '.....'); to set", dsn); return; } /* a does the class exist */ spprintf(&classname, 0, con->class_name, table); /* now does the class exist!! */ 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 */ 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_cp,table_cp); 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->schema ( [string column] ) get schema info about a table (no args returns a list of columns), */ PHP_FUNCTION(dbdo_schema) { zval *object = getThis(); gint rows,column_id, row_id; GdaRow *row; gint col; GdaValue *value; gchar *string, *title, *ntitle; zval *zvalue; dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); char *column_name; int column_name_length; ulong h; dbdo_schema_load(obj TSRMLS_CC); if (ZEND_NUM_ARGS() == 0) { column_id = gda_data_model_get_column_position(obj->table->schema,"Field name"); if (column_id == -1) { RETURN_FALSE; } array_init(return_value); rows = gda_data_model_get_n_rows (obj->table->schema); for (row_id = 0; row_id < rows;row_id++) { row = (GdaRow *) gda_data_model_get_row (obj->table->schema, row_id); value = (GdaValue *) gda_row_get_value (row, column_id); string = gda_value_stringify (value); MAKE_STD_ZVAL(zvalue); ZVAL_STRING(zvalue, string, 1); g_free(string); 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; } row_id = dbdo_schema_get_col_row(obj,column_name); if (row_id < 0) { DBDO_DEBUG0(16,"lost it"); RETURN_FALSE; } array_init(return_value); row = (GdaRow *) gda_data_model_get_row (obj->table->schema, row_id); for (column_id = 0; column_id < (int) gda_data_model_get_n_columns (obj->table->schema); column_id++) { title = (gchar *)gda_data_model_get_column_title (obj->table->schema, column_id); ntitle = estrdup(title); value = (GdaValue *) gda_row_get_value (row, column_id); MAKE_STD_ZVAL(zvalue); string = gda_value_stringify (value); if (!string) { ZVAL_NULL(zvalue); } else { ZVAL_STRING(zvalue, string, 1); g_free(string); } h = zend_hash_func(title, strlen(title)+1); zend_hash_quick_update(HASH_OF(return_value), ntitle, strlen(title)+1, h, &zvalue, sizeof(zval *), NULL); efree(ntitle); } } /* {{{ 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.. */ PHP_FUNCTION(dbdo_get) { zval *object = getThis(); char *search_key = NULL, *search_value = NULL; int key_len, value_len, rows; GdaCommand *gdacmd; dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); smart_str cmd = {0}; if (G_IS_OBJECT(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_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &search_key, &key_len, &search_value, &value_len ) == 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 "); /* 1 arg = use sequence keys.. */ if (!search_value) { GdaValue *gdav_tmp; gchar * sql_str; gchar *skey_cpy; /* get the sequence key - and try to guess the key */ dbdo_sequence_guess(obj); if (obj->table->sequenceColumn == NULL) { smart_str_free(&cmd); zend_throw_exception(dbdo_class_entry_exception_ce , "DBDO::get sequence unknown ", 0 TSRMLS_CC); return; } smart_str_appends(&cmd, obj->table->sequenceColumn); smart_str_appends(&cmd, " = "); gdav_tmp = g_new0 (GdaValue, 1); gda_value_set_from_string(gdav_tmp, search_key, dbdo_value_type_from_column(obj, obj->table->sequenceColumn)); sql_str = dbdo_value_to_sql(gdav_tmp); smart_str_appends(&cmd, sql_str); efree(sql_str); } else { GdaValue *gdav_tmp; GdaValueType vtype; char *tmpstr; smart_str_appends(&cmd, search_key); smart_str_appends(&cmd, " = "); DBDO_DEBUG(16,"Look up type %s", search_key); vtype = dbdo_value_type_from_column(obj, search_key); DBDO_DEBUG(16,"Got Type %d", (int) vtype); if (vtype == GDA_VALUE_TYPE_UNKNOWN) { smart_str_free(&cmd); zend_throw_exception_ex( dbdo_class_entry_exception_ce , 0 TSRMLS_CC, "DBDO::get unknown column type for %s", search_key); return; } gdav_tmp = g_new0 (GdaValue, 1); tmpstr = estrdup(search_value); gda_value_set_from_string(gdav_tmp, (const gchar*) tmpstr, vtype); efree(tmpstr); tmpstr = dbdo_value_to_sql(gdav_tmp); smart_str_appends(&cmd, tmpstr); efree(tmpstr); } smart_str_0(&cmd); DBDO_DEBUG(1,"QUERY:\n%s\n",cmd.c); gdacmd = gda_command_new ((gchar*)cmd.c, GDA_COMMAND_TYPE_SQL, GDA_COMMAND_OPTION_STOP_ON_ERRORS); obj->result = gda_connection_execute_single_command (obj->con, gdacmd, NULL); smart_str_free(&cmd); gda_command_free (gdacmd); /*? do we really want to do this? */ if (obj->result == NULL) { dbdo_throw_errors (obj->con TSRMLS_CC); return; } g_object_ref(obj->result); rows = gda_data_model_get_n_rows (obj->result); /* flag it as got! - this is soooo clean! */ obj->row_id = -1; if (rows > 0) { obj->row_id = 0; } /* Throw exception if more than one row is returned. */ if (rows != 1) { zend_throw_exception_ex( dbdo_class_entry_exception_ce , 0 TSRMLS_CC, "DBDO::%s returned %d rows, only one is expected", get_active_function_name(TSRMLS_C), rows); } /* g_print("returning %d",rows); */ RETURN_LONG(rows); } /* {{{ proto mixed $dbdo->query ( [$query] ) Without arguments, it builds a query based on the object var values. with $query - sends a raw query to the database. */ PHP_FUNCTION(dbdo_query) { zval *object = getThis(); dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); char *cmd = NULL; int cmd_len; GdaCommand *gdacmd; GdaDataModel *dm; gint rows; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &cmd, &cmd_len) == FAILURE) { RETURN_FALSE; } /* it's pretty clear that we should not run a query on the same object using $obj->factory()->query('xxx') seems a reasonable trade off given the wierd situation that may occur trying to run a raw query on a existing object.. */ if (cmd) { /* if (cmd_len == 5 && strncasecmp(cmd, "BEGIN",5)) { dbdo_connect(obj); if (DBDO_G(transaction) !== NULL) { zend_throw_exception(dbdo_class_entry_exception_ce , "DBDO::Query BEGIN was run when Transaction already started", 0 TSRMLS_CC); return; } DBDO_G(transaction) = gda_transaction_new("dbdo"); gda_transaction_set_isolation_levelDBDO_G(transaction), GDA_TRANSACTION_ISOLATION_SERIALIZABLE); gda_connection_begin_transaction(obj->con,DBDO_G(transaction)); RETURN_TRUE; } */ if (obj->result) { /* this should be an error!! = but we are trying out exeptions.. */ zend_throw_exception(dbdo_class_entry_exception_ce , "DBDO::Query was run on a object with results ", 0 TSRMLS_CC); return; } dbdo_connect(obj); /* TODO: catch transaction queries : BEGIN,COMMIT,ROLLBACK Logging detecting SELECT etc. for returned rows or affected rows.. */ DBDO_DEBUG(1,"DBDO::query(SQL): \n%s\n",cmd); gdacmd = gda_command_new ((gchar*)cmd, GDA_COMMAND_TYPE_SQL, GDA_COMMAND_OPTION_STOP_ON_ERRORS); obj->result = gda_connection_execute_single_command (obj->con, gdacmd, NULL); /* g_object_ref(obj->result); */ gda_command_free(gdacmd); /* if the first 6 characters of command = update|insert|delete then return the affected rows..? otherwise return number of rows returned.. */ } else { if (G_IS_OBJECT(obj->result)) { zend_throw_exception(dbdo_class_entry_exception_ce , "DBDO::find was run on a object with results ", 0 TSRMLS_CC); return; } /* if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &autofetch) == FAILURE) { RETURN_FALSE; } */ obj->row_id = -1; cmd = dbdo_find_query_build(object, obj, DBDO_QUERY_BUILD_SELECT TSRMLS_CC); if (cmd == NULL) { /* on throw. */ return; } DBDO_DEBUG(1,"DBDO::query(SQL): \n%s\n",cmd); gdacmd = gda_command_new ((gchar*)cmd, GDA_COMMAND_TYPE_SQL, GDA_COMMAND_OPTION_STOP_ON_ERRORS); obj->result = gda_connection_execute_single_command (obj->con, gdacmd, NULL); /*g_object_ref(obj->result); */ gda_command_free (gdacmd); /* ? do we really want to do this? */ efree(cmd); if (obj->result == NULL) { dbdo_throw_errors (obj->con TSRMLS_CC); return; } } /* g_object_ref(obj->result); */ rows = gda_data_model_get_n_rows (obj->result); /* zend_bool autofetch = FALSE; if (autofetch) { if (!rows) { zend_throw_exception_ex( dbdo_class_entry_exception_ce , 0 TSRMLS_CC, "DBDO::find(autofetch) failed, as there where no results" ); return; } else { obj->row_id = 0; } } */ RETURN_LONG(rows); } /* {{{ proto mixed $dbdo->fetchAll ( DBDO::OBJECTS | DBDO::VALUE | DBDO::KEY_VALUE | DBDO::SINGLE ) perform the fetch, and returl All the results, in various formats throws errors if return set is unexpected, php_errors, if you specified the wrong number of columns. */ PHP_FUNCTION(dbdo_fetchAll) { zval *object = getThis(); dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); gint rows,r; GdaCommand *gdacmd; GdaRow *row; GdaValue *value; char *cmd; int return_type = 0; /* DEFAULT = array of objects.. */ DBDO_DEBUG0(16,"run Fetchall SQL"); if (!obj->result || !G_IS_OBJECT(obj->result)) { /* maybe this should be a php_error */ zend_throw_exception_ex( dbdo_class_entry_exception_ce , 0 TSRMLS_CC, "DBDO::%s was run on a object with results", get_active_function_name(TSRMLS_C) ); return; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &return_type) == FAILURE) { RETURN_FALSE; } if (obj->row_id != -1) { /* maybe this should be a php_error */ zend_throw_exception_ex( dbdo_class_entry_exception_ce , 0 TSRMLS_CC, "DBDO::%s() was run on a object with results already fetched", get_active_function_name(TSRMLS_C) ); return; } if (obj->result == NULL) { dbdo_throw_errors (obj->con TSRMLS_CC); return; } switch (return_type) { case 0: /* DBDO::OBJECTS - array of objects. */ array_init(return_value); /* use cloning */ for (r = 0; r < gda_data_model_get_n_rows (obj->result); r++) { zval *zclone; dbdo_object *dclone; MAKE_STD_ZVAL(zclone); zclone->type = IS_OBJECT; zclone->value.obj = dbdo_clone(object TSRMLS_CC); dclone = zend_object_store_get_object(zclone TSRMLS_CC); dclone->row_id = r; zend_hash_next_index_insert(HASH_OF(return_value), &zclone, sizeof(zval *), NULL); } g_object_unref(obj->result); obj->result = NULL; return; case 1: /* DBDO::KEY_VALUE */ { zval *value_r; char *string; ulong h; if (gda_data_model_get_n_columns(obj->result) != 2) { php_error(E_WARNING, "DBDO::%s(DBDO::KEY_VALUE) you must select exactly 2 columns, %d returned", get_active_function_name(TSRMLS_C), gda_data_model_get_n_columns(obj->result)); obj->result = NULL; return; } array_init(return_value); for (r = 0; r < gda_data_model_get_n_rows (obj->result); r++) { zval *value_r; GdaValue *rvalue; gchar *gstr; value = (GdaValue *) gda_data_model_get_value_at (obj->result, 0, r); gstr = gda_value_stringify (value); string = estrdup(gstr); g_free(gstr); h = zend_hash_func(string, strlen(string)+1); rvalue = (GdaValue *) gda_data_model_get_value_at (obj->result, 1, r); value_r = dbdo_value_to_zval(rvalue); gstr = gda_value_stringify(rvalue); DBDO_DEBUG2(16,"got row: %s,%s",string, gstr); g_free(gstr); zend_hash_quick_update(HASH_OF(return_value), string, strlen(string)+1, h, &value_r, sizeof(zval *), NULL); efree(string); } g_object_unref(obj->result); obj->result = NULL; return; } case 2: /* VALUE */ if (gda_data_model_get_n_columns(obj->result) != 1) { php_error(E_WARNING, "DBDO::%s(DBDO::VALUE) you must select exactly 1 column, %d returned", get_active_function_name(TSRMLS_C), gda_data_model_get_n_columns(obj->result)); g_object_unref(obj->result); obj->result = NULL; return; } array_init(return_value); for (r = 0; r < gda_data_model_get_n_rows (obj->result); r++) { gchar *gstr; zval *value_r; /*MAKE_STD_ZVAL(value_r); ZVAL_LONG(value_r,123); zend_hash_next_index_insert(HASH_OF(return_value), (void *) &value_r, sizeof(zval *), NULL); */ GdaValue *rvalue; rvalue = (GdaValue *) gda_data_model_get_value_at (obj->result, 0, r); value_r = dbdo_value_to_zval(rvalue); //zval_add_ref(&value_r); gstr = gda_value_stringify(rvalue); DBDO_DEBUG(16,"got row: %s",gstr); g_free(gstr); zend_hash_next_index_insert(HASH_OF(return_value), (void *) &value_r, sizeof(zval *), NULL); } g_object_unref(obj->result); obj->result = NULL; return; case 3: /* SINGLE */ { zval *ret; rows = (gint) gda_data_model_get_n_rows (obj->result); if (!rows || rows > 1) { zend_throw_exception_ex( dbdo_class_entry_exception_ce , 0 TSRMLS_CC, "DBDO::%s(DBDO::SINGLE) must return exactly 1 row. %d returned", get_active_function_name(TSRMLS_C), rows ); obj->result = NULL; return; } rows = (gint) gda_data_model_get_n_columns (obj->result); if (!rows || rows > 1) { zend_throw_exception_ex( dbdo_class_entry_exception_ce , 0 TSRMLS_CC, "DBDO::selectAll(DBDO::SINGLE) must return exactly 1 column. %d returned", rows ); obj->result = NULL; return; } value = (GdaValue *) gda_data_model_get_value_at(obj->result, 0, 0); ret = dbdo_value_to_zval(value); zval_add_ref(&ret); *return_value = *ret; zval_dtor(ret); FREE_ZVAL(ret); g_object_unref(obj->result); obj->result = NULL; return; } } } /* {{{ proto mixed $dbdo->fetch ( ) fetch the results into the object's properties */ PHP_FUNCTION(dbdo_fetch) { zval *object = getThis(); dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); zval *ztitle; GList *node; gint column_id; GdaValue *value; GdaRow *row; gchar *string; gchar *title; if (obj->result == NULL) { /* throw errror!! */ RETURN_FALSE; } /* destroy the row data hash */ if (obj->row_cache) { zend_hash_destroy(obj->row_cache); FREE_HASHTABLE(obj->row_cache); obj->row_cache = NULL; } if (obj->visable_hash) { zend_hash_destroy(obj->visable_hash); FREE_HASHTABLE(obj->visable_hash); obj->visable_hash = NULL; } obj->row_cached = 0; obj->row_id++; if (obj->row_id >= gda_data_model_get_n_rows (obj->result)) { /*g_print("fetched all? %d >= %d unreffing dm?\n",obj->row_id , gda_data_model_get_n_rows (obj->dm)); */ /* we cant free it here as it may be used later! */ /* g_object_unref (obj->dm); */ if (obj->result && G_IS_OBJECT(obj->result)) { g_object_unref(obj->result); obj->result = NULL; } /* obj->dm = NULL; */ RETURN_FALSE; } /* char *ntitle = NULL; */ 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++) { title = (gchar *) gda_data_model_get_column_title (obj->result, column_id); value = (GdaValue *) gda_row_get_value (row, column_id); string = gda_value_stringify (value); MAKE_STD_ZVAL(ztitle); ZVAL_STRING(ztitle, title, 1); if (std_object_handlers.has_property(object, ztitle, 0 TSRMLS_CC)) { std_object_handlers.unset_property(object, ztitle TSRMLS_CC); } zval_dtor(ztitle); FREE_ZVAL(ztitle); g_free(string); } RETURN_TRUE; } /* {{{ proto mixed $dbdo->count ( what ) fetch the number of results that would be returned old args where: whereAddOnly ... is this necessary?????? Probably not..! should we add a clear method??!! (to removed assigned args) countWhat .. used to do distinct(xxxx) */ PHP_FUNCTION(dbdo_count) { zval *object = getThis(); dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); char *old_data_select; char *str = NULL; int str_len =0; gint rows; GdaCommand *gdacmd; GdaDataModel *dm; GdaRow *row; GdaValue *value; char *cmd; zval *ret; /* $quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']); */ /* the old design didnt allow this to happen after a fetch we keep the query args after a find, so we could use them however - it has a horrible snag that we may get upredictable results based on the fact that you dont know if it would/should use the result values. so we will stick with the old behavior.. */ if (G_IS_OBJECT(obj->result)) { /* programming error!! - not exception! */ zend_throw_exception(dbdo_class_entry_exception_ce , "DBDO::count was run on a object with results", 0 TSRMLS_CC); return; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &str, &str_len ) == FAILURE) { RETURN_FALSE; } /* magic trickery - replace out the obj->condition. */ old_data_select = obj->condition->data_select; obj->condition->data_select = NULL; if (str != NULL) { spprintf(&(obj->condition->data_select), 0, "count(%s) as DATAOBJECT_NUM",str); } else { /* %s.%s as DATAOBJECT_NUM */ int size; /* load sequence */ dbdo_sequence_guess(obj); if (obj->table->sequenceColumn == NULL) { obj->condition->data_select = old_data_select; zend_throw_exception(dbdo_class_entry_exception_ce , "DBDO::count sequence unknown ", 0 TSRMLS_CC); return; } spprintf(&(obj->condition->data_select), 0, "count(%s.%s) as DATAOBJECT_NUM", obj->table->name, obj->table->sequenceColumn); } cmd = dbdo_find_query_build(object, obj, DBDO_QUERY_BUILD_SELECT TSRMLS_CC); /* restore */ efree(obj->condition->data_select); obj->condition->data_select = old_data_select; if (cmd == NULL) { /* exception situation. */ return; } old_data_select = obj->condition->data_select; obj->condition->data_select = NULL; DBDO_DEBUG(16,"find SQL: %s",cmd); gdacmd = gda_command_new ((gchar*)cmd, GDA_COMMAND_TYPE_SQL, GDA_COMMAND_OPTION_STOP_ON_ERRORS); dm = gda_connection_execute_single_command (obj->con, gdacmd, NULL); gda_command_free (gdacmd); /* ? do we really want to do this? */ efree(cmd); value = (GdaValue *) gda_data_model_get_value_at(dm, 0, 0); ret = dbdo_value_to_zval(value); zval_add_ref(&ret); *return_value = *ret; zval_dtor(ret); FREE_ZVAL(ret); } static int dbdo_property_has(zval *object, zval *member, int check_empty TSRMLS_DC) { zval *return_value; char *name; GdaRow *row; gint column_id; GdaValue *value; char *string; char *title; dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); /* TODO: honour check_empty */ /* if we have it stored return that! */ if (std_object_handlers.has_property(object, member, 0 TSRMLS_CC)) { return 1; } if ((obj->result == NULL) || !G_IS_OBJECT(obj->result)) { return 0; } /* fetch past end ! - do we really want to do this!!! ??? */ if (obj->row_id >= gda_data_model_get_n_rows (obj->result)) { return 0; } /* ?? should we be using the schema to determine this? = probably not! */ 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++) { title = (gchar *) gda_data_model_get_column_title (obj->result, column_id); if (strcmp(name, title) != 0) { continue; } return 1; } return 0; } static zval * dbdo_property_read(zval *object, zval *member, int type TSRMLS_DC) { zval *return_value; char *name; GdaRow *row; gint column_id; GdaValue *value; char *string; char *title; dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); /* if we have it stored return that! */ if (std_object_handlers.has_property(object, member, 0 TSRMLS_CC) || ((obj->result == NULL) || !G_IS_OBJECT(obj->result)) || (obj->row_id >= gda_data_model_get_n_rows (obj->result)) ) { DBDO_DEBUG(16,"property_read(%s) from properties.",Z_STRVAL_P(member)); return std_object_handlers.read_property(object, member, type TSRMLS_CC); } if (!obj->row_cache) { ALLOC_HASHTABLE(obj->row_cache); zend_hash_init(obj->row_cache, 0, NULL, ZVAL_PTR_DTOR, 0); } else { zval **element, *use; if (zend_hash_find(obj->row_cache, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, (void **) &element) == SUCCESS) { DBDO_DEBUG(16,"property_read(%s) from result cache.",Z_STRVAL_P(member)); use = *element; return use; } } //ZVAL_NULL(return_value); name = Z_STRVAL_P(member); 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 *ret; ulong h; title = (gchar *) gda_data_model_get_column_title (obj->result, column_id); if (strcmp(name, title) != 0) { continue; } value = (GdaValue *) gda_row_get_value (row, column_id); ret = dbdo_value_to_zval(value); h = zend_hash_func(title, strlen(title)+1); zend_hash_quick_update(obj->row_cache, title, strlen(title)+1, h, &ret, sizeof(zval *), NULL); DBDO_DEBUG(16,"property_read(%s) Directly from result set.",Z_STRVAL_P(member)); return ret; } DBDO_DEBUG(16,"property_read(%s) fell back to properties.",Z_STRVAL_P(member)); return std_object_handlers.read_property(object, member, type TSRMLS_CC); } /* get all the properties.. (eg. used by print_r etc..) */ static HashTable * dbdo_properties_get(zval *object TSRMLS_DC) { zval *tmp; zval **data_ptr; zval *value,*ztitle; zval *newptr; zval *rvalue; GdaValue *gdavalue; gint column_id; char *title; char *string; GdaRow *row; ulong h; dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); char *name; /* BOTH THESE APPEAR TO LEAK */ if (obj->result == NULL) { /* throw errror!! - what about user set values...???? */ /* empty! */ /* zend_hash_init(rv, 0, NULL, ZVAL_PTR_DTOR, 0); */ return std_object_handlers.get_properties(object TSRMLS_CC); } if (obj->visable_hash) { /* if we only have a partial view (eg. from a property_read */ zend_hash_destroy(obj->visable_hash); FREE_HASHTABLE(obj->visable_hash); obj->visable_hash = NULL; } if (obj->row_cached == 1 && obj->row_cache) { DBDO_DEBUG0(16,"Returning properties from cache"); ALLOC_HASHTABLE(obj->visable_hash); zend_hash_init(obj->visable_hash, 0, NULL, ZVAL_PTR_DTOR, 0); zend_hash_copy(obj->visable_hash,obj->row_cache, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); /* overwrite with assigned values. */ zend_hash_merge(obj->visable_hash,std_object_handlers.get_properties(object TSRMLS_CC), (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *), 1); return obj->visable_hash; } if (obj->row_cache) { /* if we only have a partial view (eg. from a property_read */ zend_hash_destroy(obj->row_cache); FREE_HASHTABLE(obj->row_cache); obj->row_cache = NULL; } ALLOC_HASHTABLE(obj->row_cache); zend_hash_init(obj->row_cache, 0, NULL, ZVAL_PTR_DTOR, 0); /* g_print("copying existing hash?\n"); */ /* FIXME : visable hash should only be a represenation of the object now */ row = (GdaRow *) gda_data_model_get_row (obj->result, obj->row_id); obj->row_cached = 1; if (!row) { return obj->row_cache; /* an empty array! */ } for (column_id = 0; column_id < gda_data_model_get_n_columns (obj->result); column_id++) { title = (gchar *) gda_data_model_get_column_title (obj->result, column_id); gdavalue = (GdaValue *) gda_row_get_value (row, column_id); rvalue = dbdo_value_to_zval(gdavalue); DBDO_DEBUG2(16,"fetch row: %s = '%s'",title, gda_value_stringify(gdavalue)); h = zend_hash_func(title, strlen(title)+1); zend_hash_quick_update(obj->row_cache, title, strlen(title)+1, h, &rvalue, sizeof(zval *), NULL); } ALLOC_HASHTABLE(obj->visable_hash); zend_hash_init(obj->visable_hash, 0, NULL, ZVAL_PTR_DTOR, 0); zend_hash_copy(obj->visable_hash,obj->row_cache, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); /* overwrite with assigned values. */ zend_hash_merge(obj->visable_hash,std_object_handlers.get_properties(object TSRMLS_CC), (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *), 1); return obj->visable_hash; } /* {{{ proto array $do->toArray ( [ string $format ] ) get the result as an array from the object, and optionally format it. */ PHP_FUNCTION(dbdo_toArray) { zval *object = getThis(); dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); char *format = NULL; int format_len = 0, column_id; GdaRow *row; gchar *title,*ftitle; /* TODO: barf it not a column ?*/ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &format, &format_len) == FAILURE) { RETURN_FALSE; } array_init(return_value); 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; ulong h; title = (gchar *) gda_data_model_get_column_title (obj->result, column_id); MAKE_STD_ZVAL(ztitle); ZVAL_STRING(ztitle, title, 1); if (std_object_handlers.has_property(object, ztitle, 0 TSRMLS_CC)) { /* if we have manually set it - use it */ value = std_object_handlers.read_property(object, ztitle, 0 TSRMLS_CC); } else { gdavalue = (GdaValue *) gda_row_get_value (row, column_id); value = dbdo_value_to_zval(gdavalue); //zval_add_ref(&value); } zval_dtor(ztitle); FREE_ZVAL(ztitle); if (!format_len) { ftitle = estrdup(title); } else { spprintf(&ftitle, 0, format, title); } /* g_free(title);is this needed?? */ h = zend_hash_func(ftitle, strlen(ftitle)+1); zend_hash_quick_update((HashTable *) Z_ARRVAL_P(return_value), ftitle, strlen(ftitle)+1, h, &value, sizeof(zval *), NULL); efree(ftitle); if (format_len) { /* efree(title); */ } } } /* {{{ proto array $do->assignFrom ( [ array|object $from ], $format ) assign object variable from a array or object. */ PHP_FUNCTION(dbdo_assignFrom) { zval *object = getThis(); dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); char *format = NULL; int format_len = 0; zval *from; gint rows,row_id; zval *ztitle; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|s", &from, &format, &format_len) == FAILURE) { RETURN_FALSE; } if ((Z_TYPE_P(from) != IS_OBJECT) && (Z_TYPE_P(from) != IS_ARRAY)) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "DBDO::assignFrom invalid type provided '%s'\n",zend_zval_type_name(from)); RETURN FALSE; } dbdo_schema_load(obj TSRMLS_CC); /* loop through schema - skip sequenceKey. - if object: - if formated value exists use = .... - else continue; - if formated value in array use = .... - if (!use) continue - if setter exists on self - use it. - if setter throws exception.. - return..) continue; - if use = array|object throw exception. - TODO:: run it through assignValue('name',$value) - assign value ontinue; */ rows = gda_data_model_get_n_rows (obj->table->schema); for (row_id = 0; row_id < rows;row_id++) { char *colname; char *setter; zval *use = NULL; zval *zotitle; GdaValue *gdav_tmp; colname = (gchar *) gda_value_get_string ( (GdaValue *) gda_data_model_get_value_at (obj->table->schema, 0, row_id)); MAKE_STD_ZVAL(ztitle); if (!format_len) { ZVAL_STRING(ztitle, colname, 1); } else { char *tmp_col = NULL; spprintf(&tmp_col, 0, format, colname); ZVAL_STRING(ztitle, tmp_col, 0); } if (Z_TYPE_P(from) == IS_OBJECT) { if (!Z_OBJ_HT_P(object)->has_property(from, ztitle, 0 TSRMLS_CC)) { zval_dtor(ztitle); FREE_ZVAL(ztitle); continue; } use = Z_OBJ_HT_P(object)->read_property(from, ztitle, 0 TSRMLS_CC); } if (Z_TYPE_P(from) == IS_ARRAY) { zval **element; if (zend_hash_find(HASH_OF(from), Z_STRVAL_P(ztitle), Z_STRLEN_P(ztitle)+1, (void **) &element) == FAILURE) { zval_dtor(ztitle); FREE_ZVAL(ztitle); continue; } use = *element; } /* should never happen really.. */ if (use == NULL) { zval_dtor(ztitle); FREE_ZVAL(ztitle); continue; } spprintf(&setter, 0, "set%s",colname); /* call setter ! */ if (zend_hash_exists(&Z_OBJCE_P(object)->function_table, setter, strlen(setter)+1)) { zval *c_ret = NULL; zval **cb_args[1]; zval zsetter; /* MAKE_STD_ZVAL(c_ret); */ c_ret = NULL; /*ALLOC_ZVAL(constructor); */ cb_args[0] = &use; INIT_ZVAL(*c_ret); INIT_ZVAL(zsetter); ZVAL_STRING(&zsetter, setter, 1); efree(setter); setter = NULL; if (call_user_function_ex(EG(function_table), &object, &zsetter, &c_ret, 1, cb_args, 0, NULL TSRMLS_CC) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "DBDO::assignFrom Error calling setter"); /* TODO : - what happens here!!! */ } /* return if it threw an exception */ zval_dtor(&zsetter); FREE_ZVAL(&zsetter); if (c_ret) { zval_dtor(c_ret); FREE_ZVAL(c_ret); } zval_dtor(ztitle); FREE_ZVAL(ztitle); continue; } else { efree(setter); setter = NULL; zval_dtor(ztitle); FREE_ZVAL(ztitle); } /* TODO: implement toValue() */ MAKE_STD_ZVAL(ztitle); ZVAL_STRING(ztitle, colname, 1); /* set it manually.. */ std_object_handlers.write_property(object, ztitle, use TSRMLS_CC); zval_dtor(ztitle); FREE_ZVAL(ztitle); } } /* {{{ */ /* Local zend_object_value creation (on stack) Load the 'other' object Create a new empty object (See spl_ce_dir_object_new_ex) Open the directory Clone other members (properties) */ 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->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 *)); 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; } /* {{{ proto mixed $dbdo->sequenceKey ($name,$isNative=false,$sequence_name=NULL) ) set the tables sequence column and details. */ PHP_FUNCTION(dbdo_sequenceKey) { zval *object = getThis(); dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); char *column,*sequenceName; gboolean isNative; int column_length, sequenceName_length; /* should this be (void **) &sequence_struct */ if (!ZEND_NUM_ARGS()) { /* lookup in the sequence hash for the details!!! */ /* key = dsn::tablename */ dbdo_sequence_guess(obj); if (obj->table->sequenceColumn == NULL) { RETURN_FALSE; } array_init(return_value); { zval *zvalue; MAKE_STD_ZVAL(zvalue); ZVAL_STRING(zvalue, obj->table->sequenceColumn, 1); zend_hash_next_index_insert(HASH_OF(return_value), &zvalue, sizeof(zval *), NULL); } { zval *zvalue; MAKE_STD_ZVAL(zvalue); ZVAL_BOOL(zvalue, obj->table->sequenceIsNative); zend_hash_next_index_insert(HASH_OF(return_value), &zvalue, sizeof(zval *), NULL); } if (obj->table->sequenceName) { zval *zvalue; MAKE_STD_ZVAL(zvalue); ZVAL_STRING(zvalue, obj->table->sequenceName, 1); zend_hash_next_index_insert(HASH_OF(return_value), &zvalue, sizeof(zval *), NULL); } return; } /* now we are setting */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bs", &column, &column_length, &isNative, &sequenceName,&sequenceName_length ) == FAILURE) { RETURN_FALSE; } obj->table->sequenceAssigned = TRUE; /* free up the old values */ if (obj->table->sequenceColumn) { efree(obj->table->sequenceColumn); obj->table->sequenceColumn = NULL; } if (obj->table->sequenceName) { efree(obj->table->sequenceName); obj->table->sequenceName = NULL; } obj->table->sequenceColumn = estrdup(column); obj->table->sequenceIsNative = (ZEND_NUM_ARGS()>1) ? isNative : FALSE; if (ZEND_NUM_ARGS()>2) { obj->table->sequenceName = estrdup( sequenceName); } RETURN_TRUE; } /* {{{ proto mixed $dbdo->insert ( ) insert the contents of the object properties into the database */ PHP_FUNCTION(dbdo_insert) { zval *object = getThis(); zval *zvalue,*ztitle, value_copy; int ret_int = 0; char **result_cols; GdaValue **result_values; GList *node; gboolean errors = FALSE; dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); gint column_id,rows,row_id; GdaValue *value; GdaDataModel *dm; GdaRow *row; gchar *string = NULL, *seqname = NULL, *ret_str = NULL; smart_str cmd = {0}; int started = 0, skippedseq; int nextId = 0; GdaCommand *gda_cmd; dbdo_schema_load(obj TSRMLS_CC); dbdo_sequence_guess(obj); if (obj->table->sequenceColumn == NULL) { zend_throw_exception(dbdo_class_entry_exception_ce , "DBDO::insert sequence unknown ", 0 TSRMLS_CC); return; } /* TODO: sequenceName usage */ if (obj->table->sequenceName) { zend_throw_exception(dbdo_class_entry_exception_ce , "DBDO::insert sequenceName not implemented yet.", 0 TSRMLS_CC); return; } if (obj->result) { php_error(E_ERROR,"DBDO::insert(), you can not insert from an existing result, \n" "use: $new->assignFrom($do); $new->insert() "); return; } smart_str_appends(&cmd, "INSERT INTO "); smart_str_appends(&cmd, obj->table->name); smart_str_appends(&cmd, " ( "); rows = gda_data_model_get_n_rows (obj->table->schema); started = 0; skippedseq = 0; result_cols = emalloc(sizeof(char *) * rows); result_values = emalloc(sizeof(GdaValue *) * rows); for (row_id = 0; row_id < rows;row_id++) { gchar *colname; zval *ztitle; /* reset our result store */ result_cols[row_id] = NULL; result_values[row_id] = NULL; /* g_print("get from schema row:%d, col:%d\n",row_id,0); */ colname = (gchar *) gda_value_get_string ( (GdaValue *) gda_data_model_get_value_at (obj->table->schema, 0, row_id)); /* dont assign the sequence column */ if (!skippedseq && !strcmp(colname,obj->table->sequenceColumn)) { skippedseq = 1; /* g_print("Skipping matches column: %s\n",colname); */ continue; } MAKE_STD_ZVAL(ztitle); ZVAL_STRING(ztitle, colname, 1); /* insert from selected row ? */ if (!std_object_handlers.has_property(object, ztitle, 0 TSRMLS_CC)) { zval_dtor(ztitle); FREE_ZVAL(ztitle); continue; } zval_dtor(ztitle); FREE_ZVAL(ztitle); if (started) { smart_str_appends(&cmd, " , "); } DBDO_DEBUG(16,"::insert() ADDING %s",colname); result_cols[row_id] = colname; smart_str_appends(&cmd, colname); started = 1; } if (!started) { zend_throw_exception(dbdo_class_entry_exception_ce , "DBDO::insert No data found to assign to columns", 0 TSRMLS_CC); smart_str_free(&cmd); return; } DBDO_DEBUG0(16,"DONE LEFT:"); smart_str_appends(&cmd, " ) VALUES ( "); started = 0; skippedseq = 0; for (row_id = 0; row_id < rows;row_id++) { char *tmp_str, *tmp_str2; char *colname; GdaValueType gv; colname = (gchar *) gda_value_get_string ( (GdaValue *) gda_data_model_get_value_at (obj->table->schema, 0, row_id)); DBDO_DEBUG(16,"col: %s", colname); MAKE_STD_ZVAL(ztitle); ZVAL_STRING(ztitle, colname, 1); if (!skippedseq && !strcmp(colname,obj->table->sequenceColumn)) { skippedseq = 1; zval_dtor(ztitle); FREE_ZVAL(ztitle); continue; } if (!std_object_handlers.has_property(object, ztitle, 0 TSRMLS_CC)) { zval_dtor(ztitle); FREE_ZVAL(ztitle); continue; } zvalue = std_object_handlers.read_property(object, ztitle, 0 TSRMLS_CC); value = dbdo_object_value_from_zval(obj, colname, zvalue TSRMLS_CC); if (!value) { zval_dtor(ztitle); FREE_ZVAL(ztitle); zval_dtor(zvalue); smart_str_free(&cmd); return; } /* perhaps we should copy here.. */ std_object_handlers.unset_property(object, ztitle TSRMLS_CC); zval_dtor(ztitle); FREE_ZVAL(ztitle); /* zval_dtor(zvalue); */ DBDO_DEBUG0(16,"Succeeded!"); if (started) { smart_str_appends(&cmd, " , "); } /* TODO: -- use prepare /execute !!! */ tmp_str2 = dbdo_value_to_sql(value); smart_str_appends(&cmd, tmp_str2); efree(tmp_str2); result_values[row_id] = value; started = 1; } smart_str_appends(&cmd, " )"); smart_str_0(&cmd); DBDO_DEBUG(1,"QUERY:\n%s\n", cmd.c); /* now get the autoincrement back!!! :) */ gda_cmd = gda_command_new ((gchar*)cmd.c, GDA_COMMAND_TYPE_SQL, GDA_COMMAND_OPTION_STOP_ON_ERRORS); smart_str_free(&cmd); dm = gda_connection_execute_single_command (obj->con, gda_cmd, NULL); gda_command_free (gda_cmd); DBDO_DEBUG(16,"sent query", cmd.c); /* TODO: implement a wrapper around get_last_insert_id() */ ret_str = gda_connection_get_last_insert_id(obj->con, dm); DBDO_DEBUG(2,"returning %s", ret_str); ret_int = atoi(ret_str); /* g_free(ret_str); */ /* TODO:: set the value column to match.... */ /* make the dataset for the object from the args.. */ /* TODO: add the autoinc col. */ if (0) { GdaDataModel *model; int i,no_cols=0; GList *value_list = NULL; for (i=0;iresult = gda_data_model_array_new (no_cols+1); gda_data_model_set_column_title (obj->result, 0,obj->table->sequenceColumn); value_list = g_list_append (value_list, gda_value_new_from_string (ret_str, GDA_VALUE_TYPE_INTEGER) ); no_cols = 1; for (i=0;iresult, no_cols,result_cols[i]); /* printf("adding value %s\n", gda_value_stringify(result_values[i])); */ value_list = g_list_append (value_list,result_values[i]); no_cols++; } if (!gda_data_model_append_row (obj->result, value_list)) { DBDO_DEBUG0(1,"error inserting rows."); return; } g_list_foreach (value_list, (GFunc) gda_value_free, NULL); g_list_free (value_list); g_object_ref(obj->result); obj->row_id = 0; } efree(result_cols); efree(result_values); RETURN_LONG(ret_int); } /* {{{ proto mixed $dbdo->update ( [DBDO::BUILD] ) update the database with contents of the object properties, TODO: utilize whereAdd() / limit() values (done when DBDO::BUILD used) */ PHP_FUNCTION(dbdo_update) { zval *object = getThis(); zval *zvalue,*ztitle; GdaCommand *gdacmd; GList *node; gboolean errors = FALSE; dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); int build = 0; gint column_id,rows,row_id; GdaValue *value, *gdav; GdaRow *row; gchar *string, *colname, *sql_str; smart_str cmd = {0}; int started = 0; gint update_id; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &build) == FAILURE) { RETURN_FALSE; } dbdo_schema_load(obj TSRMLS_CC); smart_str_appends(&cmd, "UPDATE "); smart_str_appends(&cmd, obj->table->name); smart_str_appends(&cmd, " SET "); if (!build) { /* not built = set : changes in everything except for sequenceKey (or keys if set) where = sequenceKeys or keys() */ dbdo_sequence_guess(obj); if (obj->table->sequenceColumn == NULL) { zend_throw_exception(dbdo_class_entry_exception_ce , "DBDO::update sequence unknown ", 0 TSRMLS_CC); return; } rows = gda_data_model_get_n_rows (obj->table->schema); started = 0; for (row_id = 0; row_id < rows;row_id++) { /* g_print ("rows: %d\n", title,string); */ row = (GdaRow *) gda_data_model_get_row (obj->table->schema, row_id); value = (GdaValue *) gda_row_get_value (row, 0); colname = gda_value_stringify (value); /* g_print("Col: %s\n",string); */ MAKE_STD_ZVAL(ztitle); ZVAL_STRING(ztitle, colname, 1); /* skip colnames or empty values.. */ if ( !strcmp(colname,obj->table->sequenceColumn) || !std_object_handlers.has_property(object, ztitle, 0 TSRMLS_CC) ) { g_free(colname); zval_dtor(ztitle); FREE_ZVAL(ztitle); continue; } if (started) { smart_str_appends(&cmd, " , "); } smart_str_appends(&cmd, colname); smart_str_appends(&cmd, "="); zvalue = std_object_handlers.read_property(object, ztitle, 0 TSRMLS_CC); /* create a GdaValue with type matching the column do a dbdo_gda_to_sql on it. */ if (zvalue->type != IS_STRING) { convert_to_string(zvalue); } convert_to_string(zvalue); gdav = g_new0 (GdaValue, 1); gda_value_set_from_string(gdav, Z_STRVAL_P(zvalue), dbdo_value_type_from_column(obj, colname)); g_free(colname); sql_str = dbdo_value_to_sql(gdav); smart_str_appends(&cmd, sql_str); efree(sql_str); /* TODO: quoting colname. */ zval_dtor(ztitle); FREE_ZVAL(ztitle); started = 1; } if (!started) { zend_throw_exception(dbdo_class_entry_exception_ce , "DBDO::update no values changed. ", 0 TSRMLS_CC); smart_str_free(&cmd); return; } smart_str_appends(&cmd, " WHERE "); smart_str_appends(&cmd, obj->table->sequenceColumn); smart_str_appends(&cmd, " = "); /* now find it's value. ? */ MAKE_STD_ZVAL(ztitle); ZVAL_STRING(ztitle, obj->table->sequenceColumn, 1); /* favour existing database value of assigned one! */ if ( obj->result && G_IS_OBJECT(obj->result) && (gda_data_model_get_column_position(obj->result, obj->table->sequenceColumn) > -1) ) { GdaValue *r_gvalue; r_gvalue = (GdaValue *) gda_data_model_get_value_at ( obj->result, gda_data_model_get_column_position(obj->result, obj->table->sequenceColumn), obj->row_id); sql_str = dbdo_value_to_sql(r_gvalue); smart_str_appends(&cmd, sql_str); efree(sql_str); /* otherwise - is the php value set.. */ } else if (std_object_handlers.has_property(object, ztitle, 0 TSRMLS_CC)) { zvalue = std_object_handlers.read_property(object, ztitle, 0 TSRMLS_CC); if (zvalue->type != IS_STRING) { convert_to_string(zvalue); } convert_to_string(zvalue); gdav = g_new0 (GdaValue, 1); gda_value_set_from_string(gdav, Z_STRVAL_P(zvalue), dbdo_value_type_from_column(obj, obj->table->sequenceColumn)); sql_str = dbdo_value_to_sql(gdav); smart_str_appends(&cmd, sql_str); efree(sql_str); } else { /* use the result.. ??? what if no dm..... */ if (obj->table->sequenceColumn == NULL) { zend_throw_exception(dbdo_class_entry_exception_ce , "DBDO::update sequence value not set (perhaps use DBDO::BUILD?) ", 0 TSRMLS_CC); zval_dtor(ztitle); FREE_ZVAL(ztitle); smart_str_free(&cmd); return; } } zval_dtor(ztitle); FREE_ZVAL(ztitle); } else { /* use all the object values to update set. */ char *condition; /* build run on an existing object? */ if (obj->result) { php_error(E_ERROR, "DBDO::update(BUILD) run on an object with a result set."); smart_str_free(&cmd); return; } rows = gda_data_model_get_n_rows (obj->table->schema); started = 0; for (row_id = 0; row_id < rows;row_id++) { /* g_print ("rows: %d\n", title,string); */ row = (GdaRow *) gda_data_model_get_row (obj->table->schema, row_id); value = (GdaValue *) gda_row_get_value (row, 0); colname = gda_value_stringify (value); /* g_print("Col: %s\n",string); */ MAKE_STD_ZVAL(ztitle); ZVAL_STRING(ztitle, colname, 1); /* skip colnames or empty values.. */ if (!std_object_handlers.has_property(object, ztitle, 0 TSRMLS_CC)) { g_free(colname); zval_dtor(ztitle); FREE_ZVAL(ztitle); continue; } if (started) { smart_str_appends(&cmd, " , "); } smart_str_appends(&cmd, colname); smart_str_appends(&cmd, "="); zvalue = std_object_handlers.read_property(object, ztitle, 0 TSRMLS_CC); /* create a GdaValue with type matching the column do a dbdo_gda_to_sql on it. */ if (zvalue->type != IS_STRING) { convert_to_string(zvalue); } convert_to_string(zvalue); gdav = g_new0 (GdaValue, 1); gda_value_set_from_string(gdav, Z_STRVAL_P(zvalue), dbdo_value_type_from_column(obj, colname)); g_free(colname); sql_str = dbdo_value_to_sql(gdav); smart_str_appends(&cmd, sql_str); efree(sql_str); /* TODO: quoting colname. */ zval_dtor(ztitle); FREE_ZVAL(ztitle); started = 1; } if (!started) { zend_throw_exception(dbdo_class_entry_exception_ce , "DBDO::update no values changed. ", 0 TSRMLS_CC); smart_str_free(&cmd); return; } condition = dbdo_find_query_build(object, obj, DBDO_QUERY_BUILD_UPDATE TSRMLS_CC); if (!condition) { zend_throw_exception(dbdo_class_entry_exception_ce , "DBDO::update no conditions available to create condition.", 0 TSRMLS_CC); smart_str_free(&cmd); return; } smart_str_appends(&cmd, condition); efree(condition); } smart_str_0(&cmd); DBDO_DEBUG(1,"QUERY:\n%s\n", cmd.c); gdacmd = gda_command_new ((gchar*)cmd.c, GDA_COMMAND_TYPE_SQL, GDA_COMMAND_OPTION_STOP_ON_ERRORS); smart_str_free(&cmd); gda_connection_execute_single_command (obj->con, gdacmd, NULL); gda_command_free (gdacmd); /* TODO : rows Affected */ RETURN_TRUE; } /* {{{ proto mixed $dbdo->selectAdd ( ) modfy the select condition part. */ PHP_FUNCTION(dbdo_selectAdd) { char *select; zval *object = getThis(); int select_length; dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); if (ZEND_NUM_ARGS() ==0) { if (!obj->condition->data_select) { RETURN_NULL(); } /* g_print("selectAdd:%s\n",obj->condition->data_select); */ RETVAL_STRING(obj->condition->data_select, 1); efree(obj->condition->data_select); obj->condition->data_select = NULL; return; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &select, &select_length) == FAILURE) { RETURN_FALSE; } if (!obj->condition->data_select) { obj->condition->data_select = estrdup(select); /* g_print("selectAdd:set to %s\n",obj->condition->data_select); */ RETURN_TRUE; } obj->condition->data_select = erealloc(obj->condition->data_select,select_length + strlen(obj->condition->data_select) + 4); strcat(obj->condition->data_select," , "); strcat(obj->condition->data_select,select); RETURN_TRUE; } /* {{{ proto mixed $dbdo->whereAdd ( ) modify the where part.. */ PHP_FUNCTION(dbdo_whereAdd) { char *where, *whereCond = NULL, *old; int where_len, whereCond_len; zval *object = getThis(); dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); if(ZEND_NUM_ARGS() == 0) { if (!obj->condition->where) { RETURN_NULL(); } /* g_print("selectAdd:%s\n",obj->condition->data_select); */ RETVAL_STRING(obj->condition->where, 1); efree(obj->condition->where); obj->condition->where = NULL; return; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &where, &where_len, &whereCond, &whereCond_len) == FAILURE) { RETURN_FALSE; } if (!whereCond) { whereCond_len = 3; } /* start! */ if (!obj->condition->where) { obj->condition->where = estrdup(where); /* g_print("selectAdd:set to %s\n",obj->condition->data_select); */ RETURN_TRUE; } /* append.. */ old = estrdup(obj->condition->where); efree(obj->condition->where ); obj->condition->where = emalloc(strlen(old) + where_len + 3 + whereCond_len); strcpy(obj->condition->where,old); efree(old); strcat(obj->condition->where," "); if (ZEND_NUM_ARGS()>1) { strcat(obj->condition->where,whereCond); } else { strcat(obj->condition->where,"AND"); } strcat(obj->condition->where," "); strcat(obj->condition->where,where); RETURN_TRUE; } /* {{{ proto mixed $dbdo->limit ( [$num, [$num]] ) modify the limit part.. */ PHP_FUNCTION(dbdo_limit) { zval *object = getThis(); long arg1, arg2; dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ll", &arg1, &arg2) == FAILURE) { RETURN_FALSE; } switch (ZEND_NUM_ARGS()) { case 0: obj->condition->limit_from = 0; obj->condition->limit_qty = 0; obj->condition->limit_use = FALSE; break; case 1: obj->condition->limit_from = 0; obj->condition->limit_qty = arg1; obj->condition->limit_use = TRUE; break; case 2: obj->condition->limit_from = arg1; obj->condition->limit_qty = arg2; obj->condition->limit_use = TRUE; break; } RETURN_TRUE; } /* {{{ proto mixed $dbdo->orderBy ( ) modify the orderBy */ PHP_FUNCTION(dbdo_orderBy) { char *orderBy; zval *object = getThis(); int orderBy_length; dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); if (ZEND_NUM_ARGS() ==0) { if (!obj->condition->orderBy) { RETURN_NULL(); } /* g_print("selectAdd:%s\n",obj->condition->data_select); */ RETVAL_STRING(obj->condition->orderBy, 1); efree(obj->condition->orderBy); obj->condition->orderBy = NULL; return; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &orderBy, &orderBy_length) == FAILURE) { RETURN_FALSE; } if (!obj->condition->orderBy) { obj->condition->orderBy = estrdup(orderBy); /* g_print("selectAdd:set to %s\n",obj->condition->data_select);*/ RETURN_TRUE; } obj->condition->orderBy = erealloc(obj->condition->orderBy,orderBy_length + strlen(obj->condition->orderBy) + 4); strcat(obj->condition->orderBy," , "); strcat(obj->condition->orderBy,orderBy); RETURN_TRUE; } /* {{{ proto mixed $dbdo->groupBy ( ) modify the groupBy */ PHP_FUNCTION(dbdo_groupBy) { char *groupBy; zval *object = getThis(); int groupBy_length; dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); if (ZEND_NUM_ARGS() ==0) { if (!obj->condition->groupBy) { RETURN_NULL(); } /* g_print("selectAdd:%s\n",obj->condition->data_select);*/ RETVAL_STRING(obj->condition->groupBy, 1); efree(obj->condition->groupBy); obj->condition->groupBy = NULL; return; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &groupBy, &groupBy_length) == FAILURE) { RETURN_FALSE; } if (!obj->condition->groupBy) { obj->condition->groupBy = estrdup(groupBy); /* g_print("selectAdd:set to %s\n",obj->condition->data_select); */ RETURN_TRUE; } obj->condition->groupBy = erealloc(obj->condition->groupBy,groupBy_length + strlen(obj->condition->groupBy) + 4); strcat(obj->condition->groupBy," , "); strcat(obj->condition->groupBy,groupBy); RETURN_TRUE; } /* {{{ proto mixed $dbdo->groupBy ( ) modify the having clause */ PHP_FUNCTION(dbdo_having) { zval *object = getThis(); char *having, *havingCond = NULL; int having_len, havingCond_len; dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); if (ZEND_NUM_ARGS() ==0) { if (!obj->condition->having) { RETURN_NULL(); } /* g_print("selectAdd:%s\n",obj->condition->data_select); */ RETVAL_STRING(obj->condition->having, 1); efree(obj->condition->having); obj->condition->having = NULL; return; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &having, &having_len, &havingCond, &havingCond_len) == FAILURE) { RETURN_FALSE; } if (!obj->condition->having) { obj->condition->having = estrdup(having); /* g_print("selectAdd:set to %s\n",obj->condition->data_select); */ RETURN_TRUE; } obj->condition->having = erealloc(obj->condition->having,having_len + strlen(obj->condition->having) + 6); if (!havingCond) { strcat(obj->condition->having," AND "); } else if (0 == strcasecmp("AND", havingCond)) { strcat(obj->condition->having, " AND "); } else if (0 == strcasecmp("OR", havingCond)) { strcat(obj->condition->having," OR "); } else { obj->condition->having = erealloc(obj->condition->having, strlen(obj->condition->having)); php_error(E_WARNING, "%s(): Second argument should be (AND or OR)", get_active_function_name(TSRMLS_C)); RETURN_FALSE; } strcat(obj->condition->having,having); /* reduce the size of having to fit.. - probaly not necessary... */ obj->condition->having = erealloc(obj->condition->having, strlen(obj->condition->having)); RETURN_TRUE; } /* {{{ proto string $dbdo->tableName ( ) change the table name !! = should this be allowed on an existing object?? - NO! - could cause chaos! */ PHP_FUNCTION(dbdo_tableName) { zval *object = getThis(); dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); if ( ZEND_NUM_ARGS() > 0) { php_error(E_NOTICE, "Too many arguments to function"); RETURN_FALSE; } RETVAL_STRING(obj->table->name, 1); } /* {{{ proto string $dbdo->numRows( ) get the number of rows returned by a query (also the number returned by fetch()) */ PHP_FUNCTION(dbdo_numRows) { zval *object = getThis(); dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); if ( ZEND_NUM_ARGS() > 0) { php_error(E_NOTICE, "Too many arguments to function"); RETURN_FALSE; } if (!obj->result) { php_error(E_ERROR, "DBDO::numRows run on an object without results."); RETURN_FALSE; } RETVAL_LONG(gda_data_model_get_n_rows (obj->result)); } dbdo_config * dbdo_config_get(char *name TSRMLS_DC) { dbdo_config *con,*cons; int i; /* look for it on the stack.. */ for (i=0;idsn,name)==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->require_path = NULL; con->class_name = NULL; con->links = NULL; con->lowercase_columns = FALSE; con->quote_identifiers = FALSE; con->dsn = estrdup(name); DBDO_G(config)[DBDO_G(config_len)-1] = con; return con; } /* {{{ proto DBDO::config ( $dsn, $key, $value) set any of the config variables.. */ PHP_FUNCTION(dbdo_config) { /* only supports dsn : key : valueat present */ int dsn_len,key_len,value_len; char *dsn, *key,*value; gboolean bvalue; dbdo_config *con; if (zend_parse_parameters(2 TSRMLS_CC, "ss", &dsn, &dsn_len, &key, &key_len) == FAILURE) { RETURN_FALSE; } /* not amazingly efficient code.. but it should work.. */ if ((strcmp(key,"require_path") ==0) || (strcmp(key,"class_name") ==0) || (strcmp(key,"links") ==0)) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &dsn, &dsn_len, &key, &key_len,&value, &value_len) == FAILURE) { RETURN_FALSE; } } if ((strcmp(key,"lowercase_columns") ==0) || (strcmp(key,"quote_identifiers") ==0)) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &dsn, &dsn_len, &key, &key_len,&bvalue) == FAILURE) { RETURN_FALSE; } } con = dbdo_config_get(dsn TSRMLS_CC); 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_FUNCTION(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::delete([DBDO::BUILD]) without paramenters only deletes items with matching keys (eg. single), with DBDO::BUILD, uses whereAdd and object variables to determine what to delete. */ PHP_FUNCTION(dbdo_delete) { int build =0; zval *object = getThis(); zval *ztitle; dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); smart_str cmd = {0}; char *condition; int started = 0; gint update_id; GdaCommand *gdacmd; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &build) == FAILURE) { RETURN_FALSE; } dbdo_schema_load(obj TSRMLS_CC); smart_str_appends(&cmd, "DELETE FROM "); smart_str_appends(&cmd, obj->table->name); /* either built (using conditions) or defaults to use the native sequence */ if (!build) { smart_str_appends(&cmd, " WHERE "); dbdo_sequence_guess(obj); if (!build && obj->table->sequenceColumn == NULL) { zend_throw_exception(dbdo_class_entry_exception_ce , "DBDO::delete sequence unknown ", 0 TSRMLS_CC); smart_str_free(&cmd); return; } smart_str_appends(&cmd, obj->table->sequenceColumn); smart_str_appends(&cmd, " = "); /* if we have set the value - use that */ MAKE_STD_ZVAL(ztitle); ZVAL_STRING(ztitle, obj->table->sequenceColumn, 1); if (std_object_handlers.has_property(object, ztitle, 0 TSRMLS_CC)) { zval *zvalue; GdaValue *gdav; gchar *sql_str; zvalue = std_object_handlers.read_property(object, ztitle, 0 TSRMLS_CC); if (zvalue->type != IS_STRING) { convert_to_string(zvalue); } zval_dtor(ztitle); FREE_ZVAL(ztitle); convert_to_string(zvalue); gdav = g_new0 (GdaValue, 1); gda_value_set_from_string(gdav, Z_STRVAL_P(zvalue), dbdo_value_type_from_column(obj, obj->table->sequenceColumn)); sql_str = dbdo_value_to_sql(gdav); smart_str_appends(&cmd, sql_str); efree(sql_str); /* TODO: free the gvalue */ } else { GdaValue *r_gvalue; int col = -1; gchar *sql_str; zval_dtor(ztitle); FREE_ZVAL(ztitle); if (obj->result) { col = gda_data_model_get_column_position(obj->result, obj->table->sequenceColumn); } if (!obj->result || (obj->row_id < 0) || (col < 0)) { zend_throw_exception(dbdo_class_entry_exception_ce , "DBDO::delete sequence value not set", 0 TSRMLS_CC); smart_str_free(&cmd); return; } r_gvalue = (GdaValue *) gda_data_model_get_value_at ( obj->result, col, obj->row_id); sql_str = dbdo_value_to_sql(r_gvalue); smart_str_appends(&cmd, sql_str); efree(sql_str); /* TODO: free the gvalue */ } } else { condition = dbdo_find_query_build(object, obj, DBDO_QUERY_BUILD_DELETE TSRMLS_CC); if (!condition) { zend_throw_exception(dbdo_class_entry_exception_ce , "DBDO::delete no values available to create condition.", 0 TSRMLS_CC); smart_str_free(&cmd); return; } smart_str_appends(&cmd, condition); efree(condition); } smart_str_0(&cmd); DBDO_DEBUG(1,"QUERY:\n%s\n", cmd.c); gdacmd = gda_command_new ((gchar*)cmd.c, GDA_COMMAND_TYPE_SQL, GDA_COMMAND_OPTION_STOP_ON_ERRORS); smart_str_free(&cmd); gda_connection_execute_single_command (obj->con, gdacmd, NULL); gda_command_free (gdacmd); /* TODO : check return value */ RETURN_TRUE; } /* {{{ proto DBDO::valueSet(string column, mixed value) sets the value of a column to a value, doing checks to see if it is valid. */ PHP_FUNCTION(dbdo_valueSet) { zval *value, *ztitle; zval *object = getThis(); char *col; int col_len, row_id; GdaValueType target_type; dbdo_object *obj = zend_object_store_get_object(object TSRMLS_CC); GdaValue *test; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &col, &col_len, &value) == FAILURE) { RETURN_FALSE; } dbdo_schema_load(obj TSRMLS_CC); row_id = dbdo_schema_get_col_row(obj,col); if (row_id == -1) { zend_throw_exception_ex( dbdo_class_entry_exception_ce , 0 TSRMLS_CC, "DBDO::valueSet() unknown column: %s", col); RETURN_FALSE; } test = dbdo_object_value_from_zval(obj, col, value TSRMLS_CC); if (NULL == test) { RETURN_FALSE; } g_free(test); MAKE_STD_ZVAL(ztitle); ZVAL_STRING(ztitle, col, 1); /* set it manually.. */ std_object_handlers.write_property(object, ztitle, value TSRMLS_CC); zval_dtor(ztitle); FREE_ZVAL(ztitle); RETURN_TRUE; } /* {{{ proto DBDO::valueGet(string column) fetches the value of a column, intended to be used internally when making everything private.. */ PHP_FUNCTION(dbdo_valueGet) { zval *member; zval *object = getThis(); zval *ret; char *col; int col_len, row_id; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &col, &col_len) == FAILURE) { RETURN_FALSE; } /* we get it again (as it's easier to sent to getproperty.. */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &member) == FAILURE) { RETURN_FALSE; } /* we have no idea what type (3rd arg means but we are sticking 1 in there */ ret = dbdo_property_read(object, member, 1 TSRMLS_CC); RETURN_ZVAL(ret, 1, 0); }