Changeset 11899

Show
Ignore:
Timestamp:
08/30/07 18:33:18 (1 year ago)
Author:
rambo
Message:

cleanup, code re-use, enable re-execution, refs #102

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/midcom/midcom.core/midcom/core/querybuilder.php

    r11894 r11899  
    106106 * 
    107107 * @package midcom 
    108  * @todo Optimize the limit/offset implementation. 
    109  * @todo Refactor the class to promote code reuse in the execution handlers. 
    110108 */ 
    111109class midcom_core_querybuilder extends midcom_baseclasses_core_object 
     
    180178     * impact. 
    181179     * 
    182      * While on-site, this is enabled by default, in AIS it is disabled by default. 
    183180     */ 
    184181    var $hide_invisible = true; 
     
    271268    } 
    272269 
     270    /** 
     271     * Executes the internal QB and filters objects based on ACLs and metadata 
     272     * 
     273     * @param bool $false_on_empty_mgd_resultset used in the moving window loop to get false in stead of empty array back from this method in case the **core** QB returns empty resultset 
     274     * @return array of objects filtered by ACL and metadata visibility (or false in case of failure) 
     275     */ 
    273276    function _execute_and_check_privileges($false_on_empty_mgd_resultset = false) 
    274277    { 
    275278        debug_push_class(__CLASS__, __FUNCTION__); 
     279        // TODO: Remove this silence after all MgdSchemas are fixed 
    276280        $result = @$this->_qb->execute(); 
    277281        if (!is_array($result)) 
     
    361365 
    362366    /** 
     367     * Resets some internal variables for re-execute 
     368     */ 
     369    function _reset() 
     370    { 
     371        $this->_seen_guids = array();  
     372        $this->_qb_error_result = 'UNDEFINED'; 
     373        $this->count = -1; 
     374        $this->denied = 0; 
     375    } 
     376 
     377    /** 
    363378     * This function will execute the Querybuilder and call the appropriate callbacks from the 
    364379     * class it is associated to. This way, class authors have full control over what is actually 
     
    373388     *    may remove any unwanted entries from the resultset at this point. 
    374389     * 
    375      * If the execution of the query fails for some reason all available error information is logged 
    376      * and a MIDCOM_ERRCRIT level error is triggered, halting execution. 
    377      * 
    378      * @param midgard_query_builder $qb An instance of the Query builder obtained by the new_query_builder 
    379      *     function of this class. 
    380390     * @return Array The result of the query builder or null on any error. Note, that empty resultsets 
    381391     *     will return an empty array. 
    382      * @todo Implement proper count / Limit support. 
    383392     */ 
    384393    function execute_windowed() 
    385394    { 
    386         // Reset these two in case someone tries to re-execute this 
    387         $this->_seen_guids = array();  
    388         $this->_qb_error_result = 'UNDEFINED'; 
     395        $this->_reset(); 
    389396         
    390397        if (! call_user_func_array(array($this->_real_class, '_on_prepare_exec_query_builder'), array(&$this))) 
     
    426433            while (($resultset = $this->_execute_and_check_privileges(true)) !== false) 
    427434            { 
    428                 //debug_add("Iteration loop #{$i}"); 
     435                debug_add("Iteration loop #{$i}"); 
    429436                if ($this->_qb_error_result !== 'UNDEFINED') 
    430437                { 
     
    468475        call_user_func_array(array($this->_real_class, '_on_process_query_result'), array(&$newresult)); 
    469476 
    470         /* 
    471         // correct record count by the number of limit-skipped objects. 
    472         $this->count = count($newresult) + $skipped_objects; 
    473         */ 
    474477        $this->count = count($newresult); 
    475478 
     
    539542    { 
    540543        return $this->execute_windowed(); 
     544        //return $this->execute_notwindowed(); 
    541545    } 
    542546 
     
    554558     *    may remove any unwanted entries from the resultset at this point. 
    555559     * 
    556      * If the execution of the query fails for some reason all available error information is logged 
    557      * and a MIDCOM_ERRCRIT level error is triggered, halting execution. 
    558      * 
    559      * @param midgard_query_builder $qb An instance of the Query builder obtained by the new_query_builder 
    560      *     function of this class. 
    561560     * @return Array The result of the query builder or null on any error. Note, that empty resultsets 
    562561     *     will return an empty array. 
    563      * @todo Implement proper count / Limit support. 
    564562     */ 
    565563    function execute_notwindowed() 
    566564    { 
    567         debug_push_class(__CLASS__, __FUNCTION__); 
    568  
     565        $this->_reset(); 
    569566        if (! call_user_func_array(array($this->_real_class, '_on_prepare_exec_query_builder'), array(&$this))) 
    570567        { 
     568            debug_push_class(__CLASS__, __FUNCTION__); 
    571569            debug_add('The _on_prepare_exec_query_builder callback returned false, so we abort now.'); 
    572570            debug_pop(); 
     
    576574        if ($this->_constraint_count == 0) 
    577575        { 
    578             debug_add('This Query Builder instance has no constraints.', MIDCOM_LOG_WARN); 
     576            debug_push_class(__CLASS__, __FUNCTION__); 
     577            debug_add('This Query Builder instance has no constraints, see debug log for stacktrace', MIDCOM_LOG_WARN); 
    579578            debug_print_function_stack('We were called from here:'); 
    580         } 
    581  
    582         $result = $this->_qb->execute(); 
     579            debug_pop(); 
     580        } 
     581 
     582        $result = $this->_execute_and_check_privileges(); 
    583583        if (!is_array($result)) 
    584584        { 
    585             debug_print_r('Result was:', $result); 
    586             debug_add('The querybuilder failed to execute, aborting.', MIDCOM_LOG_ERROR); 
    587             debug_add('Last Midgard error was: ' . mgd_errstr(), MIDCOM_LOG_ERROR); 
    588             if (isset($php_errormsg)) 
    589             { 
    590                 debug_add("Error message was: {$php_errormsg}", MIDCOM_LOG_ERROR); 
    591             } 
    592  
    593             /* 
    594             $_MIDCOM->generate_error(MIDCOM_ERRCRIT, 
    595                 'The query builder failed to execute, see the log file for more information.'); 
    596             // This will exit. 
    597             */ 
    598             return false; 
     585            return $result; 
    599586        } 
    600587 
     
    606593        $skipped_objects = 0; 
    607594        $this->denied = 0; 
    608         // Workaround to ML bug where we get multiple results in non-strict mode 
    609         $seen_guids = array(); 
    610         foreach ($result as $key => $value) 
     595 
     596        foreach ($result as $key => $object) 
    611597        { 
    612598            if (   $this->_limit > 0 
    613599                && $limit == 0) 
    614600            { 
    615                 $skipped_objects++; 
    616                 continue; 
    617             } 
    618  
    619             // Create a new object instance (checks read privilege implicitly) using the copy-constuctor. 
    620             $object = new $classname($value); 
    621  
    622             if (mgd_errno() == MGD_ERR_ACCESS_DENIED) 
    623             { 
    624                 // This is logged by the callers 
    625                 $this->denied++; 
    626                 $skipped_objects++; 
    627                 continue; 
    628             } 
    629  
    630             if (   ! $object 
    631                 || ! is_object($object)) 
    632             { 
    633                 debug_add("Could not create a MidCOM DBA instance of the {$classname} ID {$value->id}. See debug level log for details.", 
    634                     MIDCOM_LOG_INFO); 
    635601                $skipped_objects++; 
    636602                continue; 
     
    645611            } 
    646612 
    647             // Check visibility 
    648             if ($this->hide_invisible) 
    649             { 
    650                 $metadata =& midcom_helper_metadata::retrieve($object); 
    651                 if (! $metadata) 
    652                 { 
    653                     debug_add("Could not create a MidCOM metadata instance for {$classname} ID {$value->id}, assuming an invisible object.", 
    654                         MIDCOM_LOG_INFO); 
    655                     $skipped_objects++; 
    656                     continue; 
    657                 } 
    658  
    659                 if (! $metadata->is_object_visible_onsite()) 
    660                 { 
    661                     debug_add("The {$classname} ID {$value->id} is hidden by metadata.", MIDCOM_LOG_INFO); 
    662                     $skipped_objects++; 
    663                     continue; 
    664                 } 
    665             } 
    666              
    667             if (isset($seen_guids[$object->guid])) 
    668             { 
    669                 debug_push_class(__CLASS__, __FUNCTION__); 
    670                 debug_add("The {$classname} object {$object->guid} has already been seen, probably MultiLang bug", MIDCOM_LOG_WARN); 
    671                 debug_pop(); 
    672                 continue; 
    673             } 
    674  
    675613            $newresult[] = $object; 
    676              
    677             $seen_guids[$object->guid] = true; 
    678614 
    679615            if ($this->_limit > 0) 
     
    685621        call_user_func_array(array($this->_real_class, '_on_process_query_result'), array(&$newresult)); 
    686622 
    687         // correct record count by the number of limit-skipped objects. 
    688         $this->count = count($newresult) + $skipped_objects; 
    689  
    690         debug_pop(); 
     623        $this->count = count($newresult); 
     624 
    691625        return $newresult; 
    692626    } 
     
    707641    function execute_unchecked() 
    708642    { 
    709         debug_push_class(__CLASS__, __FUNCTION__); 
     643        $this->_reset(); 
    710644 
    711645        if (! call_user_func_array(array($this->_real_class, '_on_prepare_exec_query_builder'), array(&$this))) 
    712646        { 
     647            debug_push_class(__CLASS__, __FUNCTION__); 
    713648            debug_add('The _on_prepare_exec_query_builder callback returned false, so we abort now.'); 
    714649            debug_pop(); 
     
    718653        if ($this->_constraint_count == 0) 
    719654        { 
    720             debug_add('This Query Builder instance has no constraints.', MIDCOM_LOG_WARN); 
     655            debug_push_class(__CLASS__, __FUNCTION__); 
     656            debug_add('This Query Builder instance has no constraints, see debug level log for stacktrace', MIDCOM_LOG_WARN); 
    721657            debug_print_function_stack('We were called from here:'); 
     658            debug_pop(); 
    722659        } 
    723660 
     
    732669        } 
    733670 
    734         $result = $this->_qb->execute(); 
    735         if (!is_array($result)) 
    736         { 
    737             debug_print_r('Result was:', $result); 
    738             debug_add('The querybuilder failed to execute, aborting.', MIDCOM_LOG_ERROR); 
    739             debug_add('Last Midgard error was: ' . mgd_errstr(), MIDCOM_LOG_ERROR); 
    740             if (isset($php_errormsg)) 
    741             { 
    742                 debug_add("Error message was: {$php_errormsg}", MIDCOM_LOG_ERROR); 
    743             } 
    744  
    745             /* 
    746             $_MIDCOM->generate_error(MIDCOM_ERRCRIT, 
    747                 'The query builder failed to execute, see the log file for more information.'); 
    748             // This will exit. 
    749             */ 
    750             return false; 
    751         } 
    752  
    753         // Workaround until the QB returns the correct type, refetch everything 
    754         $newresult = Array(); 
    755         $classname = $this->_real_class; 
    756         $this->denied = 0; 
    757         foreach ($result as $key => $value) 
    758         { 
    759             // Create a new object instance (checks read privilege implicitly) using the copy-constuctor. 
    760             $object = new $classname($value); 
    761  
    762             if (mgd_errno() == MGD_ERR_ACCESS_DENIED) 
    763             { 
    764                 // This is logged by the callers 
    765                 $this->denied++; 
    766                 continue; 
    767             } 
    768  
    769             if (   ! $object 
    770                 || ! is_object($object)) 
    771             { 
    772                 debug_add("Could not create a MidCOM DBA instance of the {$classname} ID {$value->id}. See debug level log for details.", 
    773                     MIDCOM_LOG_INFO); 
    774                 continue; 
    775             } 
    776  
    777             // Check visibility 
    778             if ($this->hide_invisible) 
    779             { 
    780                 $metadata =& midcom_helper_metadata::retrieve($object); 
    781                 if (! $metadata) 
    782                 { 
    783                     debug_add("Could not create a MidCOM metadata instance for {$classname} ID {$value->id}, assuming an invisible object.", 
    784                         MIDCOM_LOG_INFO); 
    785                     continue; 
    786                 } 
    787  
    788                 if (! $metadata->is_object_visible_onsite()) 
    789                 { 
    790                     debug_add("The {$classname} ID {$value->id} is hidden by metadata.", MIDCOM_LOG_INFO); 
    791                     continue; 
    792                 } 
    793             } 
    794  
    795             $newresult[$key] = $object; 
     671        $newresult = $this->_execute_and_check_privileges(); 
     672        if (!is_array($newresult)) 
     673        { 
     674            return $newresult; 
    796675        } 
    797676 
     
    800679        $this->count = count($newresult); 
    801680 
    802         debug_pop(); 
    803681        return $newresult; 
    804682    } 
     
    817695    function add_constraint($field, $operator, $value) 
    818696    { 
     697        $this->_reset(); 
    819698        // Add check against null values, Core QB is too stupid to get this right. 
    820699        if ($value === null) 
     
    928807    function set_limit($limit) 
    929808    { 
     809        $this->_reset(); 
    930810        $this->_limit = $limit; 
    931811    } 
     
    943823    function set_offset($offset) 
    944824    { 
     825        $this->_reset(); 
    945826        $this->_offset = $offset; 
    946827    } 
     
    955836    function set_lang($language) 
    956837    { 
     838        $this->_reset(); 
    957839        $this->_qb->set_lang($language); 
    958840    } 
     
    960842    /** 
    961843     * Include deleted objects (metadata.deleted is TRUE) in query results. 
     844     * 
     845     * Note: this may cause all kinds of weird behaviour with the DBA helpers 
    962846     */ 
    963847    function include_deleted() 
    964848    { 
     849        $this->_reset(); 
    965850        $this->_qb->include_deleted(); 
    966851    } 
     
    969854     * Returns the number of elements matching the current query. 
    970855     * 
    971      * <i>Developer's note:</i> According to the Midgard core documentation, the count method 
    972      * does <b>not</b> execute the query using some COUNT() SQL statement. It merely returns 
    973      * the number of records found and is, thus, mostly useless as you can just count($result) 
    974      * on the PHP level anyway. 
    975      * 
    976      * To match the original inteded behavoir, the class will automatically execute the given 
    977      * query if it has not yet been executed, thus breaking full API compatibility to the Midgard 
    978      * core deliberatly on this point. 
    979      * 
    980      * Therefore, it is currently <i>strongly discouraged</i> to assume midgard_query_builder::count 
    981      * to be useful as-is. See http://midgard.tigris.org/issues/show_bug.cgi?id=56 for details. 
     856     * Due to ACL checking we must first execute the full query to get 
    982857     * 
    983858     * @return int The number of records found by the last query. 
     
    1001876     * mind might nevertheless be able to take advantage of it. 
    1002877     * 
    1003      * @return int The number of records matching the last query without taking access control into account. 
     878     * @return int The number of records matching the constraints without taking access control or visibility into account. 
    1004879     */ 
    1005880    function count_unchecked() 
    1006881    { 
    1007         // TODO: Handle limit and offset 
     882        if ($this->_limit) 
     883        { 
     884            $this->_qb->set_limit($this->_limit); 
     885        } 
     886        if ($this->_offset) 
     887        { 
     888            $this->_qb->set_offset($this->_offset); 
     889        } 
    1008890        return $this->_qb->count(); 
    1009891    } 
     
    1012894 
    1013895?> 
     896