/**
  * Update one property table by inserting or deleting rows, and compute
  * the changes that this entails for the property usage counts. The
  * given rows are inserted into the table if $insert is true; otherwise
  * they are deleted. The property usage counts are recorded in the
  * call-by-ref parameter $propertyUseIncrements.
  *
  * The method assumes that all of the given rows are about the same
  * subject. This is ensured by callers.
  *
  * @since 1.8
  * @param array $propertyUseIncrements
  * @param SMWSQLStore3Table $propertyTable
  * @param array $rows array of rows to insert/delete
  * @param boolean $insert
  * @param DatabaseBase $dbw used for writing
  */
 protected function writePropertyTableRowUpdates(array &$propertyUseIncrements, SMWSQLStore3Table $propertyTable, array $rows, $insert, DatabaseBase $dbw)
 {
     if (empty($rows)) {
         //print "Nothing to " . ( $insert ? 'insert' : 'delete' ) . " for table {$propertyTable->getName()}.\n"; //DEBUG
         return;
     }
     //print ( $insert ? 'Inserting ' : 'Deleting ' ) . count( $rows ) . " row(s) in table {$propertyTable->getName()}.\n"; //DEBUG
     //print var_export( $rows, true ) . "\n"; //DEBUG
     if (!$propertyTable->usesIdSubject()) {
         // does not occur, but let's be strict
         throw new InvalidArgumentException('Operation not supported for tables without subject IDs.');
     }
     if ($insert) {
         $dbw->insert($propertyTable->getName(), array_values($rows), "SMW::writePropertyTableRowUpdates-insert-{$propertyTable->getName()}");
     } else {
         $condition = '';
         // We build a condition that mentions s_id only once,
         // since it must be the same for all rows. This should
         // help the DBMS in selecting the rows (it would not be
         // easy for to detect that all tuples share one s_id).
         $sid = false;
         foreach ($rows as $row) {
             if ($sid === false) {
                 $sid = $row['s_id'];
                 // 's_id' exists for all tables with $propertyTable->usesIdSubject()
             }
             unset($row['s_id']);
             if ($condition != '') {
                 $condition .= ' OR ';
             }
             $condition .= '(' . $dbw->makeList($row, LIST_AND) . ')';
         }
         $condition = "s_id=" . $dbw->addQuotes($sid) . " AND ({$condition})";
         $dbw->delete($propertyTable->getName(), array($condition), "SMW::writePropertyTableRowUpdates-delete-{$propertyTable->getName()}");
     }
     if ($propertyTable->isFixedPropertyTable()) {
         $property = new SMWDIProperty($propertyTable->getFixedProperty());
         $pid = $this->store->smwIds->makeSMWPropertyID($property);
     }
     foreach ($rows as $row) {
         if (!$propertyTable->isFixedPropertyTable()) {
             $pid = $row['p_id'];
         }
         if (!array_key_exists($pid, $propertyUseIncrements)) {
             $propertyUseIncrements[$pid] = 0;
         }
         $propertyUseIncrements[$pid] += $insert ? 1 : -1;
     }
 }
 /**
  * Helper function for reading all data for from a given property table
  * (specified by an SMWSQLStore3Table object), based on certain
  * restrictions. The function can filter data based on the subject (1)
  * or on the property it belongs to (2) -- but one of those must be
  * done. The Boolean $issubject is true for (1) and false for (2).
  *
  * In case (1), the first two parameters are taken to refer to a
  * subject; in case (2) they are taken to refer to a property. In any
  * case, the retrieval is limited to the specified $proptable. The
  * parameters are an internal $id (of a subject or property), and an
  * $object (being an SMWDIWikiPage or SMWDIProperty). Moreover, when
  * filtering by property, it is assumed that the given $proptable
  * belongs to the property: if it is a table with fixed property, it
  * will not be checked that this is the same property as the one that
  * was given in $object.
  *
  * In case (1), the result in general is an array of pairs (arrays of
  * size 2) consisting of a property key (string), and DB keys (array if
  * many, string if one) from which a datvalue object for this value can
  * be built. It is possible that some of the DB keys are based on
  * internal objects; these will be represented by similar result arrays
  * of (recursive calls of) fetchSemanticData().
  *
  * In case (2), the result is simply an array of DB keys (array)
  * without the property keys. Container objects will be encoded with
  * nested arrays like in case (1).
  *
  * @todo Maybe share DB handler; asking for it seems to take quite some
  * time and we do not want to change it in one call.
  *
  * @since 1.8
  * @param integer $id
  * @param SMWDataItem $object
  * @param SMWSQLStore3Table $proptable
  * @param boolean $issubject
  * @param SMWRequestOptions $requestoptions
  *
  * @return array
  */
 protected function fetchSemanticData($id, SMWDataItem $object = null, SMWSQLStore3Table $proptable, $issubject = true, SMWRequestOptions $requestoptions = null)
 {
     // stop if there is not enough data:
     // properties always need to be given as object,
     // subjects at least if !$proptable->idsubject
     if ($id == 0 || is_null($object) && (!$issubject || !$proptable->usesIdSubject())) {
         return array();
     }
     wfProfileIn("SMWSQLStore3::fetchSemanticData-" . $proptable->getName() . " (SMW)");
     $result = array();
     $db = wfGetDB(DB_SLAVE, 'smw');
     $diHandler = $this->store->getDataItemHandlerForDIType($proptable->getDiType());
     // ***  First build $from, $select, and $where for the DB query  ***//
     $from = $db->tableName($proptable->getName());
     // always use actual table
     $select = '';
     $where = '';
     if ($issubject) {
         // restrict subject, select property
         $where .= $proptable->usesIdSubject() ? 's_id=' . $db->addQuotes($id) : 's_title=' . $db->addQuotes($object->getDBkey()) . ' AND s_namespace=' . $db->addQuotes($object->getNamespace());
         if (!$proptable->isFixedPropertyTable()) {
             // select property name
             $from .= ' INNER JOIN ' . $db->tableName(SMWSql3SmwIds::tableName) . ' AS p ON p_id=p.smw_id';
             $select .= 'p.smw_title as prop';
         }
         // else: fixed property, no select needed
     } elseif (!$proptable->isFixedPropertyTable()) {
         // restrict property only
         $where .= 'p_id=' . $db->addQuotes($id);
     }
     $valuecount = 0;
     // Don't use DISTINCT for value of one subject:
     $usedistinct = !$issubject;
     $valueField = $diHandler->getIndexField();
     $labelField = $diHandler->getLabelField();
     $fields = $diHandler->getFetchFields();
     foreach ($fields as $fieldname => $typeid) {
         // select object column(s)
         if ($typeid == 'p') {
             // get data from ID table
             $from .= ' INNER JOIN ' . $db->tableName(SMWSql3SmwIds::tableName) . " AS o{$valuecount} ON {$fieldname}=o{$valuecount}.smw_id";
             $select .= ($select !== '' ? ',' : '') . "{$fieldname} AS id{$valuecount}" . ",o{$valuecount}.smw_title AS v{$valuecount}" . ",o{$valuecount}.smw_namespace AS v" . ($valuecount + 1) . ",o{$valuecount}.smw_iw AS v" . ($valuecount + 2) . ",o{$valuecount}.smw_sortkey AS v" . ($valuecount + 3) . ",o{$valuecount}.smw_subobject AS v" . ($valuecount + 4);
             if ($valueField == $fieldname) {
                 $valueField = "o{$valuecount}.smw_sortkey";
             }
             if ($labelField == $fieldname) {
                 $labelField = "o{$valuecount}.smw_sortkey";
             }
             $valuecount += 4;
         } else {
             $select .= ($select !== '' ? ',' : '') . "{$fieldname} AS v{$valuecount}";
         }
         $valuecount += 1;
     }
     if (!$issubject) {
         // Apply sorting/string matching; only with given property
         $where .= $this->store->getSQLConditions($requestoptions, $valueField, $labelField, $where !== '');
     } else {
         $valueField = '';
     }
     // ***  Now execute the query and read the results  ***//
     $res = $db->select($from, $select, $where, 'SMW::getSemanticData', $usedistinct ? $this->store->getSQLOptions($requestoptions, $valueField) + array('DISTINCT') : $this->store->getSQLOptions($requestoptions, $valueField));
     foreach ($res as $row) {
         if ($issubject) {
             // use joined or predefined property name
             $propertykey = $proptable->isFixedPropertyTable() ? $proptable->getFixedProperty() : $row->prop;
         }
         // Use enclosing array only for results with many values:
         if ($valuecount > 1) {
             $valuekeys = array();
             for ($i = 0; $i < $valuecount; $i += 1) {
                 // read the value fields from the current row
                 $fieldname = "v{$i}";
                 $valuekeys[] = $row->{$fieldname};
             }
         } else {
             $valuekeys = $row->v0;
         }
         // Filter out any accidentally retrieved internal things (interwiki starts with ":"):
         if ($valuecount < 3 || implode('', $fields) != 'p' || $valuekeys[2] === '' || $valuekeys[2][0] != ':') {
             $result[] = $issubject ? array($propertykey, $valuekeys) : $valuekeys;
         }
     }
     $db->freeResult($res);
     wfProfileOut("SMWSQLStore3::fetchSemanticData-" . $proptable->getName() . " (SMW)");
     return $result;
 }