/**
  * Test fixture for validateMatrixRow() method, 'trie' mode, 'data' trie
  *
  * @version 1.0
  * @since 1.0
  * 
  * =======================================================================================
  */
 public function test_validateMatrixRow_trie_data()
 {
     $struct = array("table" => "FOX_dataStore_validators", "engine" => "InnoDB", "cache_namespace" => "FOX_dataStore_validators", "cache_strategy" => "paged", "cache_engine" => array("memcached", "redis", "apc", "thread"), "columns" => array("X5" => array("php" => "int", "sql" => "int", "format" => "%d", "width" => null, "flags" => "NOT NULL", "auto_inc" => false, "default" => null, "index" => true), "X4" => array("php" => "string", "sql" => "varchar", "format" => "%s", "width" => 32, "flags" => "NOT NULL", "auto_inc" => false, "default" => null, "index" => true), "X3" => array("php" => "int", "sql" => "int", "format" => "%d", "width" => null, "flags" => "NOT NULL", "auto_inc" => false, "default" => null, "index" => true), "X2" => array("php" => "string", "sql" => "varchar", "format" => "%s", "width" => 32, "flags" => "NOT NULL", "auto_inc" => false, "default" => null, "index" => true), "X1" => array("php" => "int", "sql" => "int", "format" => "%d", "width" => null, "flags" => "NOT NULL", "auto_inc" => false, "default" => null, "index" => true), "X0" => array("php" => "serialize", "sql" => "longtext", "format" => "%s", "width" => null, "flags" => "", "auto_inc" => false, "default" => null, "index" => false)));
     $cls = new FOX_dataStore_validator($struct);
     // PASS - Valid data trie, branch terminating at clip order, TRUE node
     // ####################################################################
     $ctrl = array('end_node_format' => 'trie', 'trie_ctrl' => array('order' => 3, 'mode' => 'data', 'allow_wildcard' => false, 'clip_order' => 2));
     $row = array('X5' => 1, 'X4' => 'Y', 'X3' => array(1 => array('X' => array(1 => true, 5 => true), 'Y' => true)));
     try {
         $result = $cls->validateMatrixRow($row, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertEquals(true, $result);
     // PASS - Valid data trie, branch terminating at clip order, ARRAY node
     // ####################################################################
     $ctrl = array('end_node_format' => 'trie', 'trie_ctrl' => array('order' => 3, 'mode' => 'data', 'allow_wildcard' => false, 'clip_order' => 2));
     $row = array('X5' => 1, 'X4' => 'Y', 'X3' => array(1 => array('X' => array(1 => true, 5 => true), 'Y' => array())));
     try {
         $result = $cls->validateMatrixRow($row, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertEquals(true, $result);
     // PASS - Valid data trie, L1 node values ignored
     // ####################################################################
     $ctrl = array('end_node_format' => 'trie', 'trie_ctrl' => array('order' => 3, 'mode' => 'data', 'allow_wildcard' => false, 'clip_order' => 2));
     $row = array('X5' => 1, 'X4' => 'Y', 'X3' => array(1 => array('X' => array(1 => true, 5 => "foo"))));
     try {
         $result = $cls->validateMatrixRow($row, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertEquals(true, $result);
     // PASS - Valid data trie, implied trie order
     // ####################################################################
     $ctrl = array('end_node_format' => 'trie', 'trie_ctrl' => array('order' => null, 'mode' => 'data', 'allow_wildcard' => false, 'clip_order' => 2));
     $row = array('X5' => 1, 'X4' => 'Y', 'X3' => array(1 => array('X' => array(1 => true, 5 => "foo"))));
     try {
         $result = $cls->validateMatrixRow($row, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertEquals(true, $result);
     // FAIL - Invalid L1 node data type
     // ####################################################################
     $ctrl = array('end_node_format' => 'trie', 'trie_ctrl' => array('order' => 3, 'mode' => 'data', 'allow_wildcard' => false, 'clip_order' => 2));
     $row = array('X5' => 1, 'X4' => 'Y', 'X3' => array(1 => array('X' => array(1 => true, "F" => true), 'Y' => array())));
     try {
         $result = $cls->validateMatrixRow($row, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertNotEquals(true, $result);
     // FAIL - Invalid L2 node value
     // ####################################################################
     $ctrl = array('end_node_format' => 'trie', 'trie_ctrl' => array('order' => 3, 'mode' => 'data', 'allow_wildcard' => false, 'clip_order' => 2));
     $row = array('X5' => 1, 'X4' => 'Y', 'X3' => array(1 => array('X' => array(1 => true, 2 => true), 'Y' => "Fail")));
     try {
         $result = $cls->validateMatrixRow($row, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertNotEquals(true, $result);
     // FAIL - Invalid L3 data type
     // ####################################################################
     $ctrl = array('end_node_format' => 'trie', 'trie_ctrl' => array('order' => 3, 'mode' => 'data', 'allow_wildcard' => false, 'clip_order' => 2));
     $row = array('X5' => 1, 'X4' => 'Y', 'X3' => array(1 => array('X' => array(1 => true, 2 => true), 'Y' => true), "F" => array('X' => array(1 => true, 2 => true), 'Y' => true)));
     try {
         $result = $cls->validateMatrixRow($row, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertNotEquals(true, $result);
     // FAIL - Invalid L3 value
     // ####################################################################
     $ctrl = array('end_node_format' => 'trie', 'trie_ctrl' => array('order' => 3, 'mode' => 'data', 'allow_wildcard' => false, 'clip_order' => 2));
     $row = array('X5' => 1, 'X4' => 'Y', 'X3' => array(1 => array('X' => array(1 => true, 2 => true), 'Y' => true), 2 => "FAIL"));
     try {
         $result = $cls->validateMatrixRow($row, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertNotEquals(true, $result);
     // FAIL - Branch terminates above clip plane
     // ####################################################################
     $ctrl = array('end_node_format' => 'trie', 'trie_ctrl' => array('order' => 3, 'mode' => 'data', 'allow_wildcard' => false, 'clip_order' => 2));
     $row = array('X5' => 1, 'X4' => 'Y', 'X3' => array(1 => array('X' => array(1 => true, 2 => true), 'Y' => true), 2 => true));
     try {
         $result = $cls->validateMatrixRow($row, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertNotEquals(true, $result);
     // FAIL - Branch terminates below clip plane, but not at L1
     // ####################################################################
     $ctrl = array('end_node_format' => 'trie', 'trie_ctrl' => array('order' => 3, 'mode' => 'data', 'allow_wildcard' => false, 'clip_order' => 3));
     $row = array('X5' => 1, 'X4' => 'Y', 'X3' => array(1 => array('X' => array(1 => true, 2 => true), 'Y' => true)));
     try {
         $result = $cls->validateMatrixRow($row, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertNotEquals(true, $result);
     // FAIL - L3 isn't a trie
     // ####################################################################
     $ctrl = array('end_node_format' => 'trie', 'trie_ctrl' => array('order' => 3, 'mode' => 'data', 'allow_wildcard' => false, 'clip_order' => 2));
     $row = array('X5' => 1, 'X4' => 'Y', 'X3' => "FAIL");
     try {
         $result = $cls->validateMatrixRow($row, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertNotEquals(true, $result);
     // FAIL - Invalid L4 value
     // ####################################################################
     $ctrl = array('end_node_format' => 'trie', 'trie_ctrl' => array('order' => 3, 'mode' => 'data', 'allow_wildcard' => false, 'clip_order' => 2));
     $row = array('X5' => 1, 'X4' => '2', 'X3' => array(1 => array('X' => array(1 => true, 2 => true), 'Y' => true)));
     try {
         $result = $cls->validateMatrixRow($row, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertNotEquals(true, $result);
     // FAIL - Invalid L5 value
     // ####################################################################
     $ctrl = array('end_node_format' => 'trie', 'trie_ctrl' => array('order' => 3, 'mode' => 'data', 'allow_wildcard' => false, 'clip_order' => 2));
     $row = array('X5' => 2.4, 'X4' => 'Y', 'X3' => array(1 => array('X' => array(1 => true, 2 => true), 'Y' => true), 2 => "FAIL"));
     try {
         $result = $cls->validateMatrixRow($row, $ctrl);
     } catch (FOX_exception $child) {
         $this->fail($child->dumpString(1));
     }
     $this->assertNotEquals(true, $result);
     // EXCEPTION - Invalid ['trie_ctrl']['order']
     // ####################################################################
     $ctrl = array('end_node_format' => 'trie', 'trie_ctrl' => array('order' => 99, 'mode' => 'data', 'allow_wildcard' => false, 'clip_order' => 2));
     $row = array('X5' => '1', 'X4' => 'Y', 'X3' => array(1 => array('X' => array(1 => true, 2 => true), 'Y' => true), 'F' => true));
     try {
         $result = $cls->validateMatrixRow($row, $ctrl);
         // Execution will halt on the previous line if validateMatrixRow() throws an exception
         $this->fail("Method validateMatrixRow() failed to throw an exception on invalid trie ['trie_ctrl']['order']");
     } catch (FOX_exception $child) {
     }
     // EXCEPTION - Missing ['trie_ctrl']['clip_order']
     // ####################################################################
     $ctrl = array('end_node_format' => 'trie', 'trie_ctrl' => array('order' => 2, 'mode' => 'data', 'allow_wildcard' => false, 'clip_order' => null));
     $row = array('X5' => '1', 'X4' => 'Y', 'X3' => array(1 => array('X' => array(1 => true, 2 => true), 'Y' => true), 'F' => true));
     try {
         $result = $cls->validateMatrixRow($row, $ctrl);
         // Execution will halt on the previous line if validateMatrixRow() throws an exception
         $this->fail("Method validateMatrixRow() failed to throw an exception on missing trie ['trie_ctrl']['clip_order']");
     } catch (FOX_exception $child) {
     }
     // EXCEPTION - Wildcards on data trie
     // ####################################################################
     $ctrl = array('end_node_format' => 'trie', 'trie_ctrl' => array('order' => 3, 'mode' => 'data', 'allow_wildcard' => true, 'clip_order' => 2));
     $row = array('X5' => '1', 'X4' => 'Y', 'X3' => array(1 => array('X' => array(1 => true, 2 => true), 'Y' => true), 'F' => true));
     try {
         $result = $cls->validateMatrixRow($row, $ctrl);
         // Execution will halt on the previous line if validateMatrixRow() throws an exception
         $this->fail("Method validateMatrixRow() failed to throw an exception on wildcards on data trie");
     } catch (FOX_exception $child) {
     }
 }
 /**
  * Drops multiple L5->L1 walks from the datastore
  *
  * @version 1.0
  * @since 1.0
  *
  * [MATRIX MODE] 
  * @param array $data | Array of row arrays 
  *	=> ARR @param int '' | Individual row array
  *	    => VAL @param int/string $L5 | Single L5 id as int/string
  *	    => VAL @param int/string $L4 | Single L4 id as int/string
  *	    => VAL @param int/string $L3 | Single L3 id as int/string
  *	    => VAL @param int/string $L2 | Single L2 id as int/string
  *	    => VAL @param int/string $L1 | Single L1 id as int/string
  * 
  * [TRIE MODE]
  * @param array $data | array of L5's in the form "L5_id"=>"L4s"	
  *	=> ARR @param array $L4s | array of L4's in the form "L4_id"=>"L3s"	 
  *	    => ARR @param array $L3s | array of L3's in the form "L3_id"=>"L2s"
  *		=> ARR @param array $L2s | array of L2's in the form "L2_id"=>"L1s"
  *		    => ARR @param array $L1s | array of L1's in the form "L1_id"=>"L1_value"
  *			=> KEY @param int/string | L1 id
  *			    => VAL @param NULL	 
  * 
  * @param array $ctrl | Control parameters
  *	=> VAL @param bool $validate | Validate keys
  *	=> VAL @param string $mode | Operation mode 'matrix' | 'trie'
  * 
  * @return int | Exception on failure. Int number of rows changed on success.
  */
 public function dropMulti($data, $ctrl = null)
 {
     if (!$this->init) {
         throw new FOX_exception(array('numeric' => 0, 'text' => "Descendent class must call init() before using class methods", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
     }
     if ($this->debug_on) {
         extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "method_start", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
     }
     // Add default control params
     // ==========================
     $ctrl_default = array('validate' => true, 'mode' => 'trie', 'trap_*' => true);
     $ctrl = FOX_sUtil::parseArgs($ctrl, $ctrl_default);
     if (!is_array($data) || count($data) < 1) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "Invalid data array", 'data' => $data, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
     }
     // Validate data array
     // ===========================================================
     $struct = $this->_struct();
     $del_data = array();
     if ($ctrl['mode'] == 'matrix') {
         if ($ctrl['validate'] != false) {
             // Performance optimization (saves 1 op per key)
             if ($this->debug_on) {
                 extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "matrix_validate_start", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
             }
             $row_valid = false;
             try {
                 $validator = new FOX_dataStore_validator($struct);
                 $row_ctrl = array('end_node_format' => 'scalar');
                 foreach ($data as $row) {
                     $row_valid = $validator->validateMatrixRow($row, $row_ctrl);
                     if ($row_valid !== true) {
                         break;
                     }
                 }
                 unset($row);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 2, 'text' => "Error in validator", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
             if ($row_valid !== true) {
                 throw new FOX_exception(array('numeric' => 3, 'text' => "Invalid row in data array", 'data' => $row_valid, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
             }
             if ($this->debug_on) {
                 extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "matrix_validate_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
             }
         }
         // Loft the individual rows into a trie, to merge overlapping entries, then clip
         // the tree to get the highest order cache LUT's affected by the delete
         $columns = array($this->L5_col, $this->L4_col, $this->L3_col, $this->L2_col, $this->L1_col);
         if ($this->debug_on) {
             extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "matrix_transform_start", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
         }
         $trie = FOX_trie::loftMatrix($data, $columns, null);
         $del_data = FOX_trie::clipAssocTrie($trie, $columns, null);
         if ($this->debug_on) {
             extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "matrix_transform_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
         }
     } elseif ($ctrl['mode'] == 'trie') {
         if ($ctrl['validate'] != false) {
             // Validate the $data array
             if ($this->debug_on) {
                 extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "trie_validate_start", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
             }
             try {
                 $validator = new FOX_dataStore_validator($struct);
                 $val_ctrl = array('order' => 5, 'mode' => 'control', 'allow_wildcard' => false);
                 $tree_valid = $validator->validateTrie($data, $val_ctrl);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 4, 'text' => "Error in validator", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
             if ($tree_valid !== true) {
                 throw new FOX_exception(array('numeric' => 5, 'text' => "Invalid key in data array", 'data' => $tree_valid, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
             }
             if ($this->debug_on) {
                 extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "trie_validate_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
             }
         }
         $del_data = $data;
     } else {
         throw new FOX_exception(array('numeric' => 6, 'text' => "Invalid ctrl['mode'] parameter", 'data' => $ctrl, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
     }
     // Trap "DELETE * WHERE TRUE"
     // ===========================================================
     if ($ctrl['trap_*'] == true) {
         if (!array_keys($del_data)) {
             // @see http://en.wikipedia.org/wiki/Universal_set
             $error_msg = "INTERLOCK TRIP: One or more of the conditions set in the \$data array reduces to the universal set, ";
             $error_msg .= "which is equivalent to 'WHERE 1 = 1'. Running this command would have cleared the entire datastore. ";
             $error_msg .= "If this is actually your design intent, set \$ctrl['trap_*'] = false to disable this interlock.";
             throw new FOX_exception(array('numeric' => 7, 'text' => "{$error_msg}", 'data' => $data, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
         }
     }
     // Lock affected cache pages
     // ===========================================================
     if ($this->debug_on) {
         extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "persistent_cache_lock_start", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
     }
     try {
         $cache_pages = self::lockCachePage(array_keys($del_data));
         $update_cache = $cache_pages;
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 8, 'text' => "Error locking cache", 'data' => $del_data, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
     if ($this->debug_on) {
         extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "persistent_cache_lock_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
     }
     // Build db insert array and updated cache pages array
     // ===========================================================
     $dead_pages = array();
     if ($this->debug_on) {
         extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "build_data_array_start", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
     }
     foreach ($del_data as $L5 => $L4s) {
         // Handle "true", "null" etc end nodes. The algorithm is implemented this
         // way to avoid excessive if-else nesting indentation. We know that any
         // non-array keys are valid end nodes because the trie passed validation
         // at the beginning of the class method
         if (!is_array($L4s)) {
             $L4s = array();
         }
         if (count($L4s) == 0) {
             // If the trie has no L4 structures, delete the
             // entire cache page from the class cache, and flag
             $dead_pages[] = $L5;
             // the page to be flushed from the persistent cache
             unset($update_cache[$L5]);
             continue;
         }
         foreach ($L4s as $L4 => $L3s) {
             if (!is_array($L3s)) {
                 $L3s = array();
             }
             if (count($L3s) == 0) {
                 // If the L4 structure has no L3 structures,
                 // delete its descendents' cache entries
                 unset($update_cache[$L5][$this->L4_col][$L4]);
                 unset($update_cache[$L5][$this->L3_col][$L4]);
                 unset($update_cache[$L5][$this->L2_col][$L4]);
                 unset($update_cache[$L5]["keys"][$L4]);
             }
             foreach ($L3s as $L3 => $L2s) {
                 if (!is_array($L2s)) {
                     $L2s = array();
                 }
                 if (count($L2s) == 0) {
                     // If the L3 structure has no L2 structures,
                     // delete its descendents' cache entries
                     unset($update_cache[$L5][$this->L3_col][$L4][$L3]);
                     unset($update_cache[$L5][$this->L2_col][$L4][$L3]);
                     unset($update_cache[$L5]["keys"][$L4][$L3]);
                 }
                 foreach ($L2s as $L2 => $L1s) {
                     if (!is_array($L1s)) {
                         $L1s = array();
                     }
                     if (count($L1s) == 0) {
                         // If the L2 structure has no L1 structures,
                         // delete its descendents' cache entries
                         unset($update_cache[$L5][$this->L2_col][$L4][$L3][$L2]);
                         unset($update_cache[$L5]["keys"][$L4][$L3][$L2]);
                     }
                     foreach ($L1s as $L1 => $val) {
                         unset($update_cache[$L5]["keys"][$L4][$L3][$L2][$L1]);
                     }
                     unset($L1, $val);
                 }
                 unset($L2, $L1s);
             }
             unset($L3, $L2s);
         }
         unset($L4, $L3s);
         // Clear empty walks from the keystore and LUT's
         // ==========================================================================
         $update_cache[$L5]['keys'] = FOX_sUtil::arrayPrune($update_cache[$L5]['keys'], 4);
         $update_cache[$L5][$this->L2_col] = FOX_sUtil::arrayPrune($update_cache[$L5][$this->L2_col], 3);
         $update_cache[$L5][$this->L3_col] = FOX_sUtil::arrayPrune($update_cache[$L5][$this->L3_col], 2);
         $update_cache[$L5][$this->L4_col] = FOX_sUtil::arrayPrune($update_cache[$L5][$this->L4_col], 1);
         if (count($update_cache[$L5]['keys']) == 0) {
             // If a cache page is empty after being pruned, delete
             // the entire cache page from the class cache, and flag
             $dead_pages[] = $L5;
             // the page to be flushed from the persistent cache
             unset($update_cache[$L5]);
         }
         if ($this->debug_on) {
             extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "build_data_array_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
         }
     }
     unset($L5, $L4s);
     // Clear the specified structures from the DB
     // ===========================================================
     if ($this->debug_on) {
         extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "db_delete_start", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
     }
     $args = array('key_col' => array($this->L5_col, $this->L4_col, $this->L3_col, $this->L2_col, $this->L1_col), 'args' => $data);
     $del_ctrl = array('args_format' => $ctrl['mode'], 'hash_key_vals' => false);
     try {
         $rows_changed = $this->db->runDeleteQuery($struct, $args, $del_ctrl);
     } catch (FOX_exception $child) {
         // Try to unlock the cache pages we locked
         try {
             self::writeCachePage($cache_pages);
         } catch (FOX_exception $child_2) {
             throw new FOX_exception(array('numeric' => 9, 'text' => "Error while writing to the database. Error unlocking cache pages.", 'data' => array('cache_exception' => $child_2, 'cache_pages' => $cache_pages, 'del_args' => $args, 'del_ctrl' => $del_ctrl), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
         throw new FOX_exception(array('numeric' => 10, 'text' => "Error while writing to the database. Successfully unlocked cache pages.", 'data' => array('del_args' => $args, 'del_ctrl' => $del_ctrl), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
     if ($this->debug_on) {
         extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "db_delete_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
     }
     // NOTE: we *must* update the class cache before the persistent cache so that if
     // the persistent cache write fails, the class cache will still in the correct
     // state. If we failed to do this, the class cache could end up with 'ghost' pages
     // that no longer exist in the db. If the persistent cache throws an error during
     // the write operation, any pages that fail to update will remain locked, causing
     // them to be purged on the next read operation. This guarantees cache coherency.
     // Write updated cache page images to class cache
     // ===========================================================
     foreach ($update_cache as $L5 => $page_image) {
         $this->cache[$L5] = $page_image;
     }
     unset($L5, $page_image);
     // Flush dead pages from the class cache
     // ===========================================================
     foreach ($dead_pages as $L5) {
         unset($this->cache[$L5]);
     }
     unset($L5);
     // Write updated cache page images to persistent cache
     // ===========================================================
     if ($update_cache) {
         // Trap deleting nothing but L5's
         if ($this->debug_on) {
             extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "persistent_cache_write_start", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
         }
         try {
             self::writeCachePage($update_cache);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 11, 'text' => "Error writing to cache", 'data' => $update_cache, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
         if ($this->debug_on) {
             extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "persistent_cache_write_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
         }
     }
     // Flush any dead pages from the cache
     // ===========================================================
     if ($dead_pages) {
         if ($this->debug_on) {
             extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "persistent_cache_flush_start", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
         }
         try {
             self::flushCachePage($dead_pages);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 12, 'text' => "Error flushing pages from cache", 'data' => $dead_pages, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
         if ($this->debug_on) {
             extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "persistent_cache_flush_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
         }
     }
     if ($this->debug_on) {
         extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "method_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
     }
     return (int) $rows_changed;
 }