/**
  * test the creation of a complex table with the TableManager
  * @see TableManager
  */
 public function testCreateComplexTable()
 {
     $myLevelTblMgr = new TableManager('_15ClassLevel');
     $this->assertFalse($myLevelTblMgr->exists());
     $this->assertTrue($myLevelTblMgr->create());
     $this->assertTrue($myLevelTblMgr->exists());
     $myRoleTblMgr = new TableManager('_15ClassRole');
     $this->assertFalse($myRoleTblMgr->exists());
     $this->assertTrue($myRoleTblMgr->create(array(array('name' => '15Description'), array('name' => '15Level', 'foreign' => '15ClassLevel'))));
     $this->assertTrue($myRoleTblMgr->exists());
     $this->assertTrue($myLevelTblMgr->remove());
     $this->assertFalse($myLevelTblMgr->exists());
     $this->assertTrue($myRoleTblMgr->remove());
     $this->assertFalse($myRoleTblMgr->exists());
 }
    /**
     * Short description of method unReferenceClass
     *
     * @access public
     * @author Joel Bout, <*****@*****.**>
     * @param  Class class
     * @return boolean
     */
    public function unReferenceClass(\core_kernel_classes_Class $class)
    {
        $returnValue = (bool) false;
        if ($this->isClassReferenced($class)) {
            $tableName = '_' . Utils::getShortName($class);
            //need to instanciate table manager before unreferencing otherwise, the "remove table" will fail
            $tm = new TableManager($tableName);
            // Delete reference of the class in classs_to_table, resource_has_class, resource_to_table
            $dbWrapper = \core_kernel_classes_DbWrapper::singleton();
            // Remove references of the resources in the resource has class table
            $queries = array();
            $queries[] = 'DELETE 
				FROM "resource_has_class" 
				WHERE "resource_has_class"."resource_id" 
					IN (SELECT "resource_to_table"."id" FROM "resource_to_table" WHERE "resource_to_table"."table" = \'' . $tableName . '\' );';
            // Remove reference of the class in the additional properties tables
            $queries[] = 'DELETE 
				FROM "class_additional_properties"
				WHERE "class_id" 
					IN (SELECT "class_to_table"."id" FROM "class_to_table" WHERE "class_to_table"."table" = \'' . $tableName . '\' );';
            // Remove resferences of the resources int resource to table table
            $queries[] = 'DELETE FROM "resource_to_table" WHERE "resource_to_table"."table" = \'' . $tableName . '\';';
            // Remove reference of the class in the class to table table
            $queries[] = 'DELETE FROM "class_to_table" WHERE "class_to_table"."table" = \'' . $tableName . '\';';
            $returnValue = true;
            try {
                foreach ($queries as $query) {
                    $result = $dbWrapper->exec($query);
                    if ($result === false) {
                        $returnValue = false;
                    }
                }
                if ($returnValue !== false) {
                    // delete table associated to the class
                    $tm->remove();
                    // remove class from the cache
                    if ($this->cacheModes['class'] == self::CACHE_MEMORY && is_array(self::$_classes)) {
                        foreach (self::$_classes as $index => $aClass) {
                            if ($aClass['uri'] == $class->getUri()) {
                                unset(self::$_classes[$index]);
                            }
                        }
                    }
                }
                ClassProxy::$ressourcesDelegatedTo = array();
                ResourceProxy::$ressourcesDelegatedTo = array();
                PropertyProxy::$ressourcesDelegatedTo = array();
            } catch (\PDOException $e) {
                throw new Exception("Unable to unreference class {$class->getUri()} : " . $e->getMessage());
            }
        }
        return (bool) $returnValue;
    }
 /**
  * Calling this method will transfer all instances of $class from the statements table
  * to specific optimized relational tables.
  * 
  * During optimization, the current user has all privileges on the persistent memory. At
  * the end of the process, the old privileges will be set back.
  * 
  * The $options array can contain the following key => values (all booleans):
  * 
  * - recursive: compile the target class and its subclasses (default: false).
  * - append: append data to the existing optimized table if it already exists (default: false).
  * - rmSources: remove the triples in the statement table after transfer (default: true).
  *
  * @access public
  * @author Bertrand Chevrier, <*****@*****.**>
  * @param  \core_kernel_classes_Class class
  * @param  array options
  * @return boolean Will return true if it succeeds, false otherwise.
  */
 public function hardify(\core_kernel_classes_Class $class, $options = array())
 {
     $returnValue = (bool) false;
     $oldUpdatableModels = core_kernel_persistence_smoothsql_SmoothModel::getUpdatableModelIds();
     try {
         // Give access to all models during hardification.
         core_kernel_persistence_smoothsql_SmoothModel::forceUpdatableModelIds(self::getAllModelIds());
         $classLabel = $class->getLabel();
         \common_Logger::i("Hardifying class {$classLabel}", array("GENERIS"));
         if (defined("DEBUG_PERSISTENCE") && DEBUG_PERSISTENCE) {
             if (in_array($class->getUri(), self::$debug_tables)) {
                 return;
             }
             \common_Logger::d('hardify ' . $class->getUri());
             self::$debug_tables[] = $class->getUri();
             $countStatement = $this->countStatements();
         }
         if (in_array($class->getUri(), self::$blackList)) {
             return $returnValue;
         }
         // ENTER IN SMOOTH SQL MODE
         PersistenceProxy::forceMode(PERSISTENCE_SMOOTH);
         //recursive will hardify the class and it's subclasses in the same table!
         isset($options['recursive']) ? $recursive = $options['recursive'] : ($recursive = false);
         //createForeigns will hardify the class that are range of the properties
         isset($options['createForeigns']) ? $createForeigns = $options['createForeigns'] : ($createForeigns = false);
         //check if we append the data in case the hard table exists or truncate the table and add the new rows
         isset($options['append']) ? $append = $options['append'] : ($append = false);
         //if true, the instances of the class will  be removed from the statements table!
         isset($options['rmSources']) ? $rmSources = (bool) $options['rmSources'] : ($rmSources = false);
         //if defined, we took all the properties of the class and it's parents till the topclass
         isset($options['topclass']) ? $topclass = $options['topclass'] : ($topclass = new \core_kernel_classes_Class(RDFS_RESOURCE));
         //if defined, compile the additional properties
         isset($options['additionalProperties']) ? $additionalProperties = $options['additionalProperties'] : ($additionalProperties = array());
         //if defined, reference the additional class to the table
         isset($options['referencesAllTypes']) ? $referencesAllTypes = $options['referencesAllTypes'] : ($referencesAllTypes = false);
         $tableName = '_' . Utils::getShortName($class);
         $myTableMgr = new TableManager($tableName);
         $referencer = ResourceReferencer::singleton();
         //get the table columns from the class properties
         $columns = array();
         $ps = new PropertySwitcher($class);
         $properties = $ps->getProperties($additionalProperties);
         $columns = $ps->getTableColumns($additionalProperties, self::$blackList);
         //init the count value in hardened classes:
         if (isset($this->hardenedClasses[$class->getUri()])) {
             PersistenceProxy::restoreImplementation();
             return true;
             //already being compiled
         } else {
             $this->hardenedClasses[$class->getUri()] = 0;
         }
         if (!$append || $append && !$myTableMgr->exists()) {
             //create the table
             if ($myTableMgr->exists()) {
                 $myTableMgr->remove();
             }
             $myTableMgr->create($columns);
             //reference the class
             $referencer->referenceClass($class, array("topclass" => $topclass, "additionalProperties" => $additionalProperties));
             if ($referencesAllTypes) {
                 $referencer->referenceInstanceTypes($class);
             }
         }
         //insert the resources
         $startIndex = 0;
         $instancePackSize = 100;
         $instances = $class->getInstances(false, array('offset' => $startIndex, 'limit' => $instancePackSize));
         $count = count($instances);
         $notDeletedInstances = array();
         do {
             //reset timeout:
             //set_time_limit(30);
             \helpers_TimeOutHelper::setTimeOutLimit(\helpers_TimeOutHelper::MEDIUM);
             $rows = array();
             foreach ($instances as $index => $resource) {
                 if ($referencer->isResourceReferenced($resource)) {
                     PersistenceProxy::forceMode(PERSISTENCE_HARD);
                     $resource->delete();
                     PersistenceProxy::restoreImplementation();
                 }
                 $row = array('uri' => $resource->getUri());
                 foreach ($properties as $property) {
                     $propValue = $resource->getOnePropertyValue($property);
                     $row[Utils::getShortName($property)] = $propValue;
                 }
                 $rows[] = $row;
             }
             $rowMgr = new RowManager($tableName, $columns);
             $rowMgr->insertRows($rows);
             foreach ($instances as $resource) {
                 $referencer->referenceResource($resource, $tableName, null, true);
                 if ($rmSources) {
                     //remove exported resources in smooth sql, if required:
                     // Be carefull, the resource can still exist even if
                     // delete returns true. Indeed, modelIds can be mixed between
                     // multiple models and only a part of the triples that consitute
                     // the resource might have been deleted.
                     if (!$resource->delete() || $resource->exists()) {
                         //@TODO : modified resource::delete() because resource not in local modelId cannot be deleted
                         $notDeletedInstances[] = $resource->getUri();
                         $startIndex++;
                     }
                 }
             }
             if (!$rmSources) {
                 //increment start index only if not removed
                 $startIndex += $instancePackSize;
             }
             //record hardened instances number
             if (isset($this->hardenedClasses[$class->getUri()])) {
                 $this->hardenedClasses[$class->getUri()] += $count;
             } else {
                 $this->hardenedClasses[$class->getUri()] = $count;
             }
             //update instance array and count value
             $instances = $class->getInstances(false, array('offset' => $startIndex, 'limit' => $instancePackSize));
             foreach ($notDeletedInstances as $uri) {
                 unset($instances[$uri]);
             }
             $count = count($instances);
             \helpers_TimeOutHelper::reset();
         } while ($count > 0);
         $returnValue = true;
         // Treat subclasses of the current class
         if ($recursive) {
             foreach ($class->getSubClasses(true) as $subClass) {
                 $returnValue = $this->hardify($subClass, array_merge($options, array('recursive' => false, 'append' => true)));
             }
         }
         //reset cache:
         $referencer->clearCaches();
         // EXIT SMOOTH SQL MODE
         PersistenceProxy::restoreImplementation();
         if (defined("DEBUG_PERSISTENCE") && DEBUG_PERSISTENCE) {
             $this->unhardify($class, array_merge($options, array('recursive' => false, 'removeForeigns' => false)));
             \common_Logger::d('unhardened result statements ' . $this->countStatements() . ' / ' . $countStatement);
         }
         // Give the normal rights on models to the session.
         core_kernel_persistence_smoothsql_SmoothModel::forceUpdatableModelIds($oldUpdatableModels);
     } catch (Exception $e) {
         \common_Logger::e('An error occured during hardification: ' . $e->getMessage());
         core_kernel_persistence_smoothsql_SmoothModel::forceUpdatableModelIds($oldUpdatableModels);
     }
     return (bool) $returnValue;
 }