function augmentSQL(SQLQuery &$query) { // Get the content at a specific date if ($date = Versioned::$reading_archived_date) { foreach ($query->from as $table => $dummy) { if (!isset($baseTable)) { $baseTable = $table; } $query->renameTable($table, $table . '_versions'); $query->replaceText(".ID", ".RecordID"); $query->select[] = "`{$baseTable}_versions`.RecordID AS ID"; if ($table != $baseTable) { $query->from[$table] .= " AND `{$table}_versions`.Version = `{$baseTable}_versions`.Version"; } } // Link to the version archived on that date $this->requireArchiveTempTable($baseTable, $date); $query->from["_Archive{$baseTable}"] = "INNER JOIN `_Archive{$baseTable}`\n\t\t\t\tON `_Archive{$baseTable}`.RecordID = `{$baseTable}_versions`.RecordID \n\t\t\t\tAND `_Archive{$baseTable}`.Version = `{$baseTable}_versions`.Version"; // Get a specific stage } else { if (Versioned::$reading_stage && Versioned::$reading_stage != $this->defaultStage && array_search(Versioned::$reading_stage, $this->stages) !== false) { foreach ($query->from as $table => $dummy) { $query->renameTable($table, $table . '_' . Versioned::$reading_stage); } } } }
/** * Amend the query to select from a future date if necessary. */ function augmentSQL(SQLQuery &$query) { if ($datetime = self::get_future_datetime()) { foreach ($query->from as $table => $dummy) { if (!isset($baseTable)) { $baseTable = $table; } $query->renameTable($table, $table . '_versions'); $query->replaceText("\"{$table}\".\"ID\"", "\"{$table}\".\"RecordID\""); $query->replaceText("\"{$table}_versions\".\"ID\"", "\"{$table}_versions\".\"RecordID\""); if ($table == $baseTable) { // Add all <basetable>_versions columns foreach (Versioned::$db_for_versions_table as $name => $type) { $query->select[] = sprintf('"%s_versions"."%s"', $baseTable, $name); } $query->select[] = sprintf('"%s_versions"."%s" AS "ID"', $baseTable, 'RecordID'); } if ($table != $baseTable) { $query->from[$table] .= " AND \"{$table}_versions\".\"Version\" = \"{$baseTable}_versions\".\"Version\""; } } // Link to the version archived on that date $tempTable = $this->requireFutureStateTempTable($baseTable, $datetime); $query->from[$tempTable] = "INNER JOIN \"{$tempTable}\"\n\t\t\t\tON \"{$tempTable}\".\"ID\" = \"{$baseTable}_versions\".\"RecordID\" \n\t\t\t\tAND \"{$tempTable}\".\"Version\" = \"{$baseTable}_versions\".\"Version\""; } }
protected function adjustForVersioned(SQLQuery $query) { $ownerClass = $this->baseClass; $stage = Versioned::current_stage(); if ($stage && $stage != 'Stage') { foreach ($query->getFrom() as $table => $dummy) { // Only rewrite table names that are actually part of the subclass tree // This helps prevent rewriting of other tables that get joined in, in // particular, many_many tables if (class_exists($table) && ($table == $ownerClass || is_subclass_of($table, $ownerClass) || is_subclass_of($ownerClass, $table))) { $query->renameTable($table, $table . '_' . $stage); } } } }
function augmentSQL(SQLQuery &$query) { // Get the content at a specific date if ($date = Versioned::current_archived_date()) { foreach ($query->from as $table => $dummy) { if (!isset($baseTable)) { $baseTable = $table; } $query->renameTable($table, $table . '_versions'); $query->replaceText("\"{$table}\".\"ID\"", "\"{$table}\".\"RecordID\""); // Add all <basetable>_versions columns foreach (self::$db_for_versions_table as $name => $type) { $query->select[] = sprintf('"%s_versions"."%s"', $baseTable, $name); } $query->select[] = sprintf('"%s_versions"."%s" AS "ID"', $baseTable, 'RecordID'); if ($table != $baseTable) { $query->from[$table] .= " AND \"{$table}_versions\".\"Version\" = \"{$baseTable}_versions\".\"Version\""; } } // Link to the version archived on that date $archiveTable = $this->requireArchiveTempTable($baseTable, $date); $query->from[$archiveTable] = "INNER JOIN \"{$archiveTable}\"\n\t\t\t\tON \"{$archiveTable}\".\"ID\" = \"{$baseTable}_versions\".\"RecordID\" \n\t\t\t\tAND \"{$archiveTable}\".\"Version\" = \"{$baseTable}_versions\".\"Version\""; // Get a specific stage } else { if (Versioned::current_stage() && Versioned::current_stage() != $this->defaultStage && array_search(Versioned::current_stage(), $this->stages) !== false) { foreach ($query->from as $table => $dummy) { $query->renameTable($table, $table . '_' . Versioned::current_stage()); } } } }
function augmentSQL(SQLQuery &$query) { if (!$this->stat('enabled')) { return false; } if (($lang = self::current_lang()) && !self::is_default_lang() || self::$bypass) { foreach ($query->from as $table => $dummy) { if (!isset($baseTable)) { $baseTable = $table; } if (self::table_exists("{$table}_lang")) { $query->renameTable($table, $table . '_lang'); if (stripos($query->sql(), '.ID')) { // Every reference to ID is now OriginalLangID $query->replaceText(".ID", ".OriginalLangID"); $query->where = str_replace("`ID`", "`OriginalLangID`", $query->where); $query->select[] = "`{$baseTable}_lang`.OriginalLangID AS ID"; } if ($query->where) { foreach ($query->where as $i => $wherecl) { if (substr($wherecl, 0, 4) == 'ID =') { // Another reference to ID to be changed $query->where[$i] = str_replace('ID =', 'OriginalLangID =', $wherecl); } else { $parts = explode(' AND ', $wherecl); foreach ($parts as $j => $part) { // Divide this clause between the left ($innerparts[1]) and right($innerparts[2]) part of the condition ereg('(`?[[:alnum:]_-]*`?\\.?`?[[:alnum:]_-]*`?)(.*)', $part, $innerparts); if (strpos($innerparts[1], '.') === false) { //it may be ambiguous, so sometimes we will need to add the table $parts[$j] = ($this->isInAugmentedTable($innerparts[1], $table) ? "`{$table}_lang`." : "") . "{$part}"; } else { /* if the table has been specified we have to determine if the original (without _lang) name has to be used * because we don't have the queried field in the augmented table (which usually means * that is not a translatable field) */ $clauseparts = explode('.', $innerparts[1]); $originalTable = str_replace('`', '', str_replace('_lang', '', $clauseparts[0])); $parts[$j] = ($this->isInAugmentedTable($clauseparts[1], $originalTable) ? "`{$originalTable}_lang`" : "`{$originalTable}`") . ".{$clauseparts[1]}{$innerparts[2]}"; } } $query->where[$i] = implode(' AND ', $parts); } } } if ($table != $baseTable) { $query->from["{$table}_lang"] = $query->from[$table]; } else { // _lang is now the base table (the first one) $query->from = array("{$table}_lang" => $query->from[$table]) + $query->from; } // unless we are bypassing this query, add the language filter if (!self::$bypass) { $query->where[] = "`{$table}_lang`.Lang = '{$lang}'"; } // unless this is a deletion, the query is applied to the joined table if (!$query->delete) { $query->from[$table] = "INNER JOIN `{$table}`" . " ON `{$table}_lang`.OriginalLangID = `{$table}`.ID"; /* if we are selecting fields (not doing counts for example) we need to select everything from * the original table (was renamed to _lang) since some fields that we require may be there */ if ($query->select[0][0] == '`') { $query->select = array_merge(array("`{$table}`.*"), $query->select); } } else { unset($query->from[$table]); } } else { $query->from[$table] = str_replace("`{$table}`.OriginalLangID", "`{$table}`.ID", $query->from[$table]); } } } }
/** * Augment the the SQLQuery that is created by the DataQuery * @todo Should this all go into VersionedDataQuery? */ function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery) { $baseTable = ClassInfo::baseDataClass($dataQuery->dataClass()); switch ($dataQuery->getQueryParam('Versioned.mode')) { // Noop case '': break; // Reading a specific data from the archive // Reading a specific data from the archive case 'archive': $date = $dataQuery->getQueryParam('Versioned.date'); foreach ($query->from as $table => $dummy) { $query->renameTable($table, $table . '_versions'); $query->replaceText("\"{$table}\".\"ID\"", "\"{$table}\".\"RecordID\""); // Add all <basetable>_versions columns foreach (self::$db_for_versions_table as $name => $type) { $query->select[] = sprintf('"%s_versions"."%s"', $baseTable, $name); } $query->select[] = sprintf('"%s_versions"."%s" AS "ID"', $baseTable, 'RecordID'); if ($table != $baseTable) { $query->from[$table] .= " AND \"{$table}_versions\".\"Version\" = \"{$baseTable}_versions\".\"Version\""; } } // Link to the version archived on that date $archiveTable = $this->requireArchiveTempTable($baseTable, $date); $query->from[$archiveTable] = "INNER JOIN \"{$archiveTable}\"\n\t\t\t\tON \"{$archiveTable}\".\"ID\" = \"{$baseTable}_versions\".\"RecordID\" \n\t\t\t\tAND \"{$archiveTable}\".\"Version\" = \"{$baseTable}_versions\".\"Version\""; break; // Reading a specific stage (Stage or Live) // Reading a specific stage (Stage or Live) case 'stage': $stage = $dataQuery->getQueryParam('Versioned.stage'); if ($stage && $stage != $this->defaultStage) { foreach ($query->from as $table => $dummy) { // Only rewrite table names that are actually part of the subclass tree // This helps prevent rewriting of other tables that get joined in, in // particular, many_many tables if (class_exists($table) && ($table == $this->owner->class || is_subclass_of($table, $this->owner->class) || is_subclass_of($this->owner->class, $table))) { $query->renameTable($table, $table . '_' . $stage); } } } break; // Return all version instances // Return all version instances case 'all_versions': case 'latest_versions': foreach ($query->from as $alias => $join) { if ($alias != $baseTable) { $query->setJoinFilter($alias, "\"{$alias}\".\"RecordID\" = \"{$baseTable}_versions\".\"RecordID\" AND \"{$alias}\".\"Version\" = \"{$baseTable}_versions\".\"Version\""); } $query->renameTable($alias, $alias . '_versions'); } // Add all <basetable>_versions columns foreach (self::$db_for_versions_table as $name => $type) { $query->selectMore(sprintf('"%s_versions"."%s"', $baseTable, $name)); } $query->selectMore(sprintf('"%s_versions"."%s" AS "ID"', $baseTable, 'RecordID')); // latest_version has one more step // Return latest version instances, regardless of whether they are on a particular stage // This provides "show all, including deleted" functonality if ($dataQuery->getQueryParam('Versioned.mode') == 'latest_versions') { $archiveTable = self::requireArchiveTempTable($baseTable); $query->innerJoin($archiveTable, "\"{$archiveTable}\".\"ID\" = \"{$baseTable}_versions\".\"RecordID\" AND \"{$archiveTable}\".\"Version\" = \"{$baseTable}_versions\".\"Version\""); } break; default: throw new InvalidArgumentException("Bad value for query parameter Versioned.mode: " . $dataQuery->getQueryParam('Versioned.mode')); } }
/** * Augment the the SQLQuery that is created by the DataQuery * @todo Should this all go into VersionedDataQuery? */ public function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null) { $baseTable = ClassInfo::baseDataClass($dataQuery->dataClass()); switch ($dataQuery->getQueryParam('Versioned.mode')) { // Noop case '': break; // Reading a specific data from the archive // Reading a specific data from the archive case 'archive': $date = $dataQuery->getQueryParam('Versioned.date'); foreach ($query->getFrom() as $table => $dummy) { $query->renameTable($table, $table . '_versions'); $query->replaceText("\"{$table}_versions\".\"ID\"", "\"{$table}_versions\".\"RecordID\""); $query->replaceText("`{$table}_versions`.`ID`", "`{$table}_versions`.`RecordID`"); // Add all <basetable>_versions columns foreach (self::$db_for_versions_table as $name => $type) { $query->selectField(sprintf('"%s_versions"."%s"', $baseTable, $name), $name); } $query->selectField(sprintf('"%s_versions"."%s"', $baseTable, 'RecordID'), "ID"); if ($table != $baseTable) { $query->addWhere("\"{$table}_versions\".\"Version\" = \"{$baseTable}_versions\".\"Version\""); } } // Link to the version archived on that date $safeDate = Convert::raw2sql($date); $query->addWhere("\"{$baseTable}_versions\".\"Version\" IN \n\t\t\t\t\t(SELECT LatestVersion FROM \n\t\t\t\t\t\t(SELECT \n\t\t\t\t\t\t\t\"{$baseTable}_versions\".\"RecordID\", \n\t\t\t\t\t\t\tMAX(\"{$baseTable}_versions\".\"Version\") AS LatestVersion\n\t\t\t\t\t\t\tFROM \"{$baseTable}_versions\"\n\t\t\t\t\t\t\tWHERE \"{$baseTable}_versions\".\"LastEdited\" <= '{$safeDate}'\n\t\t\t\t\t\t\tGROUP BY \"{$baseTable}_versions\".\"RecordID\"\n\t\t\t\t\t\t) AS \"{$baseTable}_versions_latest\"\n\t\t\t\t\t\tWHERE \"{$baseTable}_versions_latest\".\"RecordID\" = \"{$baseTable}_versions\".\"RecordID\"\n\t\t\t\t\t)"); break; // Reading a specific stage (Stage or Live) // Reading a specific stage (Stage or Live) case 'stage': $stage = $dataQuery->getQueryParam('Versioned.stage'); if ($stage && $stage != $this->defaultStage) { foreach ($query->getFrom() as $table => $dummy) { // Only rewrite table names that are actually part of the subclass tree // This helps prevent rewriting of other tables that get joined in, in // particular, many_many tables if (class_exists($table) && ($table == $this->owner->class || is_subclass_of($table, $this->owner->class) || is_subclass_of($this->owner->class, $table))) { $query->renameTable($table, $table . '_' . $stage); } } } break; // Reading a specific stage, but only return items that aren't in any other stage // Reading a specific stage, but only return items that aren't in any other stage case 'stage_unique': $stage = $dataQuery->getQueryParam('Versioned.stage'); // Recurse to do the default stage behavior (must be first, we rely on stage renaming happening before // below) $dataQuery->setQueryParam('Versioned.mode', 'stage'); $this->augmentSQL($query, $dataQuery); // Now exclude any ID from any other stage. Note that we double rename to avoid the regular stage rename // renaming all subquery references to be Versioned.stage foreach ($this->stages as $excluding) { if ($excluding == $stage) { continue; } $tempName = 'ExclusionarySource_' . $excluding; $excludingTable = $baseTable . ($excluding && $excluding != $this->defaultStage ? "_{$excluding}" : ''); $query->addWhere('"' . $baseTable . '"."ID" NOT IN (SELECT "ID" FROM "' . $tempName . '")'); $query->renameTable($tempName, $excludingTable); } break; // Return all version instances // Return all version instances case 'all_versions': case 'latest_versions': foreach ($query->getFrom() as $alias => $join) { if ($alias != $baseTable) { $query->setJoinFilter($alias, "\"{$alias}\".\"RecordID\" = \"{$baseTable}_versions\".\"RecordID\"" . " AND \"{$alias}\".\"Version\" = \"{$baseTable}_versions\".\"Version\""); } $query->renameTable($alias, $alias . '_versions'); } // Add all <basetable>_versions columns foreach (self::$db_for_versions_table as $name => $type) { $query->selectField(sprintf('"%s_versions"."%s"', $baseTable, $name), $name); } $query->selectField(sprintf('"%s_versions"."%s"', $baseTable, 'RecordID'), "ID"); $query->addOrderBy(sprintf('"%s_versions"."%s"', $baseTable, 'Version')); // latest_version has one more step // Return latest version instances, regardless of whether they are on a particular stage // This provides "show all, including deleted" functonality if ($dataQuery->getQueryParam('Versioned.mode') == 'latest_versions') { $query->addWhere("\"{$alias}_versions\".\"Version\" IN \n\t\t\t\t\t(SELECT LatestVersion FROM \n\t\t\t\t\t\t(SELECT \n\t\t\t\t\t\t\t\"{$alias}_versions\".\"RecordID\", \n\t\t\t\t\t\t\tMAX(\"{$alias}_versions\".\"Version\") AS LatestVersion\n\t\t\t\t\t\t\tFROM \"{$alias}_versions\"\n\t\t\t\t\t\t\tGROUP BY \"{$alias}_versions\".\"RecordID\"\n\t\t\t\t\t\t) AS \"{$alias}_versions_latest\"\n\t\t\t\t\t\tWHERE \"{$alias}_versions_latest\".\"RecordID\" = \"{$alias}_versions\".\"RecordID\"\n\t\t\t\t\t)"); } break; default: throw new InvalidArgumentException("Bad value for query parameter Versioned.mode: " . $dataQuery->getQueryParam('Versioned.mode')); } }