/***************************** {{{ LICENSE ********************************\ * dbobj, the relational object persistence module for PHP * * Copyright (C) 2006 Adam Banko * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * \***************************** LICENSE }}} ********************************/ #include "dbobj.h" #include "zend_exceptions.h" #include "zend.h" #include "SQL.h" #include "intermediate.h" #include "dbobjIterator.h" #include //{{{DBOBJITERATOR_DEBUG #define DBOBJITERATOR_DEBUG DBOBJ_DEBUG #if DBOBJITERATOR_DEBUG #else //! DBOBJITERATOR_DEBUG #endif //DBOBJITERATOR_DEBUG //}}} // {{{ZDTHIS, ZDGETTHIS #define ZDTHIS (((dbobjIterator_it)zIter)->object) #define ZDGETTHIS ((dbobjIterator)zend_object_store_get_object(this_ptr TSRMLS_CC)) //}}} //{{{php_dbobjIterator_class_functions static zend_function_entry php_dbobjIterator_class_functions[] = { PHP_ME(dbobjIterator, rewind, NULL, ZEND_ACC_PUBLIC) PHP_ME(dbobjIterator, current, NULL, ZEND_ACC_PUBLIC) PHP_ME(dbobjIterator, next, NULL, ZEND_ACC_PUBLIC) PHP_ME(dbobjIterator, valid, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} };//}}} DBOBJ_API zend_class_entry *dbobjIterator_class_entry_ptr; static zend_object_iterator *dbobjIterator_get_iterator(zend_class_entry *ce, zval *zobject, int by_ref TSRMLS_DC); /* {{{php_dbobj_MINIT_dbobjIterator */ DBOBJ_API void php_dbobj_MINIT_dbobjIterator(int module_number TSRMLS_DC) { //dbobj::MINIT's multiple load protection also protects us zend_class_entry dbobjIterator_class_entry; INIT_CLASS_ENTRY(dbobjIterator_class_entry, "dbobjIterator", php_dbobjIterator_class_functions); dbobjIterator_class_entry.create_object = dbobjIterator_create_object; dbobjIterator_class_entry.get_iterator = dbobjIterator_get_iterator; dbobjIterator_class_entry_ptr = zend_register_internal_class(&dbobjIterator_class_entry TSRMLS_CC); dbobjIterator_class_entry_ptr->create_object = dbobjIterator_create_object; }//}}} /* {{{php_dbobj_RINIT_dbobjIterator */ DBOBJ_API void php_dbobj_RINIT_dbobjIterator(TSRMLS_D) { }//}}} /* {{{dbobjIterator_get_properties */ static HashTable *dbobjIterator_get_properties(zval *zIter TSRMLS_DC) { dbobjIterator it = ZDTHIS; return &it->properties; }//}}} /* {{{dbobjIterator_get_method */ static union _zend_function* dbobjIterator_get_method(zval** object, char *methodName, int methodNameL TSRMLS_DC) { return dbobj_general_get_method(dbobjIterator_class_entry_ptr, methodName, methodNameL TSRMLS_CC); }//}}} //{{{dbobjIterator_object_handlers zend_object_handlers dbobjIterator_object_handlers = { dbobj_z_add_ref, /* add_ref */ dbobj_z_del_ref, /* del_ref */ NULL, /* clone_obj */ NULL, /* read_property */ NULL, /* write_property */ /*FIXME todo direct access*/ NULL, /* read_dimension */ NULL, /* write_dimension */ NULL, /* get_property_ptr_ptr */ NULL, /* get */ NULL, /* set */ NULL, /* has_property */ NULL, /* unset_property */ NULL, /* has_dimension */ NULL, /* unset_dimension */ dbobjIterator_get_properties, /* get_properties */ dbobjIterator_get_method, /* get_method */ NULL, /* call_method */ NULL, /* get_constructor */ dbobjIterator_get_class, /* get_class_entry */ dbobjIterator_get_className, /* get_class_name */ dbobj_compare, /* compare_objects */ NULL, /* cast_object */ NULL, /* count_elements */ };//}}} ////////////////////////////////// //////dbobjIterator_iterator////// ////////////////////////////////// /* {{{ forward declarations to the iterator handlers */ static void dbobjIterator_it_dtor(zend_object_iterator *zIter TSRMLS_DC); static int dbobjIterator_it_valid(zend_object_iterator *zIter TSRMLS_DC); static void dbobjIterator_it_current_data(zend_object_iterator *zIter, zval ***data TSRMLS_DC); static int dbobjIterator_it_current_key(zend_object_iterator *zIter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC); static void dbobjIterator_it_move_forward(zend_object_iterator *zIter TSRMLS_DC); static void dbobjIterator_it_rewind(zend_object_iterator *zIter TSRMLS_DC); //}}} /* {{{ iterator handler table */ zend_object_iterator_funcs dbobjIterator_it_funcs = { dbobjIterator_it_dtor, dbobjIterator_it_valid, dbobjIterator_it_current_data, dbobjIterator_it_current_key, dbobjIterator_it_move_forward, dbobjIterator_it_rewind };//}}} /* {{{dbobjIterator_get_iterator */ static zend_object_iterator *dbobjIterator_get_iterator(zend_class_entry *ce, zval *zobject, int by_ref TSRMLS_DC) { dbobjIterator_it iterator = emalloc(sizeof(*iterator)); dbobjIterator it = (dbobjIterator)zend_object_store_get_object(zobject TSRMLS_CC); iterator->object = it; iterator->funcs = &dbobjIterator_it_funcs; iterator->zobject = zobject; return (zend_object_iterator*)iterator; }//}}} /* {{{dbobjIterator_it_dtor */ static void dbobjIterator_it_dtor(zend_object_iterator *zIter TSRMLS_DC) { // dbobjIterator it = ZDTHIS; efree(zIter); }//}}} /* {{{dbobjIterator_it_valid */ static int dbobjIterator_it_valid(zend_object_iterator *zIter TSRMLS_DC) { dbobjIterator it; it = ZDTHIS; switch(it->position) { case ITERATOR_FIRST: case ITERATOR_MIDDLE: return SUCCESS; case ITERATOR_AFTER_LAST: return FAILURE; } assert(0);return 0; }//}}} /* {{{dbobjIterator_it_current_data */ static void dbobjIterator_it_current_data(zend_object_iterator *zIter, zval ***dataPP TSRMLS_DC) { dbobjIterator it; it = ZDTHIS; assert(it); *dataPP = &it->zCurrent; }//}}} /* {{{dbobjIterator_it_current_key */ static int dbobjIterator_it_current_key(zend_object_iterator *zIter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) { // dbobjIterator it = ZDTHIS; *int_key = 0; DBOBJ_WARNING("dbobjIterator::key UNIMPLEMENTED!"); return HASH_KEY_IS_LONG; }//}}} /* {{{dbobjIterator_it_move_forward */ static void dbobjIterator_it_move_forward(zend_object_iterator *zIter TSRMLS_DC) { dbobjIterator it; it = ZDTHIS; dbobjIterator_fetch(it TSRMLS_CC); }//}}} /* {{{dbobjIterator_it_rewind */ static void dbobjIterator_it_rewind(zend_object_iterator *zIter TSRMLS_DC) { dbobjIterator it; it = ZDTHIS; switch(it->position) { case ITERATOR_FIRST: break; case ITERATOR_MIDDLE: case ITERATOR_AFTER_LAST: dbobjIterator_rewind(it TSRMLS_CC); break; } }//}}} /* {{{dbobjIterator_set_current */ static void dbobjIterator_set_current(dbobjIterator it, dbobj o TSRMLS_DC) { /* cleanup old */ if(it->current) { dbobj_del_ref(it->current TSRMLS_CC); } //FIXME todo reuse zval if no one else is using it zval_ptr_dtor(&it->zCurrent); ALLOC_INIT_ZVAL(it->zCurrent); Z_OBJ_HT_P(it->zCurrent) = &dbobj_object_handlers; /* register new */ it->current = o; if(o) { dbobj_add_ref(o TSRMLS_CC); Z_TYPE_P(it->zCurrent) = IS_OBJECT; Z_OBJ_HANDLE_P(it->zCurrent) = it->current->handle; } else { Z_TYPE_P(it->zCurrent) = IS_NULL; } }//}}} /* {{{fetch */ DBOBJ_API void dbobjIterator_fetch(dbobjIterator it TSRMLS_DC) { dbobj o; LocalClassConfig lConf = it->lConf; classConfig cConf = lConf->cConf; uidT uid; dbVal* loadedValues; if(it->position == ITERATOR_AFTER_LAST) return; //nothing to load loadedValues = CALM(it->dbIterator, getRow, it->loaded, it->loadedL TSRMLS_CC); if(!loadedValues) { it->position = ITERATOR_AFTER_LAST; return; } if(it->position == ITERATOR_FIRST) { it->position = ITERATOR_MIDDLE; } //init dbobj o = dbobj_allocate_and_init(lConf TSRMLS_CC); o->dbStatus = DBSTATUS_IN_DATABASE; o->desired_dbStatus = DBSTATUS_IN_DATABASE; //make dbProps from dbValues { basicPropConfig* loaded = it->loaded; uint loadedL = it->loadedL; HashTable* dbPropsH = &o->dbProps; //load PKs at the same time o->PKsL = cConf->PKsL; {uint i; for(i = 0; i < loadedL; i++) { char* Key = loaded[i]->name; uint KeyL = loaded[i]->nameL; dbProp p; p = emalloc(sizeof(*p)); p->config = loaded[i]; p->changed = 0; p->value = loadedValues[i]; zend_hash_add(dbPropsH, Key,KeyL,&p,sizeof(p),NULL); //if PK, put zIter in the PKs array also if(loaded[i]->PKIndex != PKINDEX_NOTPK) { uint index = loaded[i]->PKIndex; o->PKs[index] = p; } }} } efree(loadedValues); //create UID uid = dbobj_get_UID(o TSRMLS_CC); assert(uid); { //see if we can find the object in the cache (to ensure there is only one object for a record) dbobj obj_in_cache = dbobj_lookup_in_cache(uid TSRMLS_CC); if(obj_in_cache) { efree(uid); dbobj_deallocate(o); dbobjIterator_set_current(it, obj_in_cache TSRMLS_CC); return; } } //create dbobj o->handle = dbobj_register(o, uid TSRMLS_CC); efree(uid); if(it->extraLoad) { dbobj_dbPropsLoad(o, it->extraLoad, it->extraLoadL TSRMLS_CC); } //assign dbobj as current element dbobjIterator_set_current(it, o TSRMLS_CC); }//}}} /* {{{dbobjIterator_rewind */ DBOBJ_API void dbobjIterator_rewind(dbobjIterator it TSRMLS_DC) { #if DBOBJ_DEBUG if(it->position == ITERATOR_FIRST) DBOBJ_ERROR_CRIT("dbobjIterator_rewind must not be called when iterator is at first position"); #endif //DBOBJ_DEBUG CALMT(it->dbIterator, rewind); it->position = ITERATOR_FIRST; /* * FIXME nem hivjuk meg a fetch-t??? */ }//}}} ///////////////////////// ///////PHP methods/////// ///////////////////////// /* {{{rewind */ PHP_METHOD(dbobjIterator, rewind) { dbobjIterator it = ZDGETTHIS; dbobjIterator_rewind(it TSRMLS_CC); }//}}} /* {{{current */ PHP_METHOD(dbobjIterator, current) { dbobjIterator it = ZDGETTHIS; assert(it); switch(it->position) { case ITERATOR_AFTER_LAST: RETURN_NULL(); break; case ITERATOR_FIRST: case ITERATOR_MIDDLE: Z_TYPE_P(return_value) = IS_OBJECT; Z_OBJ_HANDLE_P(return_value) = it->current->handle; Z_OBJ_HT_P(return_value) = &dbobj_object_handlers; break; } }//}}} /* {{{fetch */ PHP_METHOD(dbobjIterator, fetch) { dbobjIterator it = ZDGETTHIS; assert(it); switch(it->position) { case ITERATOR_AFTER_LAST: RETURN_NULL(); break; case ITERATOR_FIRST: case ITERATOR_MIDDLE: Z_TYPE_P(return_value) = IS_OBJECT; Z_OBJ_HANDLE_P(return_value) = it->current->handle; Z_OBJ_HT_P(return_value) = &dbobj_object_handlers; dbobjIterator_fetch(it TSRMLS_CC); break; } }//}}} /* {{{next */ PHP_METHOD(dbobjIterator, next) { dbobjIterator it = ZDGETTHIS; dbobjIterator_fetch(it TSRMLS_CC); }//}}} /* {{{valid */ PHP_METHOD(dbobjIterator, valid) { dbobjIterator it = ZDGETTHIS; switch(it->position) { case ITERATOR_FIRST: case ITERATOR_MIDDLE: RETURN_BOOL(1); case ITERATOR_AFTER_LAST: RETURN_BOOL(0); } }//}}} ////////////////////////////////// /////dbobjIterator_php_object///// ////////////////////////////////// /* {{{dbobjIterator_create_object * this function is not used in normal operation */ DBOBJ_API zend_object_value dbobjIterator_create_object(zend_class_entry *class_type TSRMLS_DC){ zend_object_value ret = {0, 0}; DBOBJ_ERROR("dbobjIterator can't be constructed with 'new'!"); return ret; }//}}} /* {{{dbobjIterator_destroy_object */ DBOBJ_API void dbobjIterator_destroy_object(dbobjIterator this, zend_object_handle handle TSRMLS_DC) { if(this->loaded != this->lConf->cConf->defaultLoadWithPKs) { efree(this->loaded); } efree(this->knownProps); zend_hash_destroy(&this->properties); zval_ptr_dtor(&this->zCurrent); CALMT(this->dbIterator, destroy); //free the object itself efree(this); }//}}} /* {{{dbobjIterator_get_class */ DBOBJ_API zend_class_entry* dbobjIterator_get_class(zval *object TSRMLS_DC) { return dbobjIterator_class_entry_ptr; }//}}} /* {{{dbobjIterator_get_className */ DBOBJ_API int dbobjIterator_get_className(zval *object, char** className, zend_uint* classNameL, int parent TSRMLS_DC) { static char* SclassName = "dbobjIterator"; static zend_uint SclassNameL = sizeof("dbobjIterator") - 1; *classNameL = SclassNameL; *className = emalloc(SclassNameL); strncpy(*className, SclassName, SclassNameL + 1 ); return SUCCESS; }//}}} /* {{{dbobjIterator_get_constructor */ DBOBJ_API union _zend_function* dbobjIterator_get_constructor(zval* object TSRMLS_DC) { return dbobjIterator_class_entry_ptr->constructor; }//}}} /* {{{dbobjIterator_cast */ DBOBJ_API int dbobjIterator_cast(zval* readObj, zval* writeObj, int type, int shouldFree TSRMLS_DC) { dbobjIterator it = (dbobjIterator)zend_object_store_get_object(readObj TSRMLS_CC); switch(type) { case IS_OBJECT: Z_OBJ_HANDLE_P(writeObj) = Z_OBJ_HANDLE_P(readObj); Z_OBJ_HT_P(writeObj) = &dbobjIterator_object_handlers; break; case IS_BOOL: Z_LVAL_P(writeObj) = (it->position == ITERATOR_AFTER_LAST); break; case IS_NULL: break; default: return FAILURE; } return SUCCESS; }//}}} /* {{{dbobjIterator_properties_freeer */ DBOBJ_API void dbobjIterator_properties_freeer(void *ptr) { }//}}} /* {{{dbobjIterator_create */ DBOBJ_API dbobjIterator dbobjIterator_create(LocalClassConfig lConf, dbCondition *conditions, uint conditionsL, basicPropConfig load[], uint loadL TSRMLS_DC) { classConfig cConf = lConf->cConf; dbobjIterator iterator; knownPropsT *knownProps; uint i; ALLOC_INIT_DBOBJITERATOR(iterator); iterator->lConf = lConf; knownProps = ecalloc(sizeof(*knownProps), conditionsL); iterator->knownProps = knownProps; /*TODO "(load) -= (conditions)" ne toltsunk be feleslegesen olyan mezoket, amiket ugyis tudunk!*/ for(i = 0; i < conditionsL; i++) { knownProps[i].config = conditions[i].config; knownProps[i].value = conditions[i].value; } /*FIXME biztositsuk, hogy az osszes PK-t lekerdezzuk, kulonben nem tudjuk azonositani, hogy mit talaltunk!*/ if(load) { DBOBJ_NOTICE("FIXME biztositsuk, hogy az osszes PK-t lekerdezzuk, kulonben nem tudjuk azonositani, hogy mit talaltunk!"); iterator->loaded = load; iterator->loadedL = loadL; } else { load = iterator->loaded = cConf->defaultLoadWithPKs; loadL = iterator->loadedL = cConf->defaultLoadWithPKsL; } //TODO make extraLoad user-configuratable iterator->extraLoad = NULL; iterator->extraLoadL = 0; iterator->dbIterator = CALM(lConf->conn, selectMany, cConf, conditions, conditionsL, load, loadL TSRMLS_CC); if(!iterator->dbIterator) { DBOBJ_ERROR("iterator creation failed"); return NULL; } //fetch first record to the iterator dbobjIterator_fetch(iterator TSRMLS_CC); if(iterator->position == ITERATOR_AFTER_LAST) { /* * iterator is empty */ dbobjIterator_destroy_object(iterator, 0 TSRMLS_CC); return NULL; } else { iterator->position = ITERATOR_FIRST; } //register object iterator->handle = ZEND_OBJECTS_STORE_PUT_DBOBJITERATOR(iterator); return iterator; }//}}} /* * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */