setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); echo "Validating existing table layout\n"; // The only cross-db way to do this, is to just fetch a single record. $row = $pdo->query("SELECT * FROM calendarobjects LIMIT 1")->fetch(); if (!$row) { echo "Error: This database did not have any records in the calendarobjects table, you should just recreate the table.\n"; exit(-1); } $requiredFields = array( 'id', 'calendardata', 'uri', 'calendarid', 'lastmodified', ); foreach($requiredFields as $requiredField) { if (!array_key_exists($requiredField,$row)) { echo "Error: The current 'calendarobjects' table was missing a field we expected to exist.\n"; echo "For safety reasons, this process is stopped.\n"; exit(-1); } } $fields17 = array( 'etag', 'size', 'componenttype', 'firstoccurence', 'lastoccurence', ); $found = 0; foreach($fields17 as $field) { if (array_key_exists($field, $row)) { $found++; } } if ($found === 0) { echo "The database had the 1.6 schema. Table will now be altered.\n"; echo "This may take some time for large tables\n"; switch($pdo->getAttribute(PDO::ATTR_DRIVER_NAME)) { case 'mysql' : $pdo->exec(<<exec('ALTER TABLE calendarobjects ADD etag text'); $pdo->exec('ALTER TABLE calendarobjects ADD size integer'); $pdo->exec('ALTER TABLE calendarobjects ADD componenttype TEXT'); $pdo->exec('ALTER TABLE calendarobjects ADD firstoccurence integer'); $pdo->exec('ALTER TABLE calendarobjects ADD lastoccurence integer'); break; default : die('This upgrade script does not support this driver (' . $pdo->getAttribute(PDO::ATTR_DRIVER_NAME) . ")\n"); } echo "Database schema upgraded.\n"; } elseif ($found === 5) { echo "Database already had the 1.7 schema\n"; } else { echo "The database had $found out of 5 from the changes for 1.7. This is scary and unusual, so we have to abort.\n"; echo "You can manually try to upgrade the schema, and then run this script again.\n"; exit(-1); } echo "Now, we need to parse every record and pull out some information.\n"; $result = $pdo->query('SELECT id, calendardata FROM calendarobjects'); $stmt = $pdo->prepare('UPDATE calendarobjects SET etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ? WHERE id = ?'); echo "Total records found: " . $result->rowCount() . "\n"; $done = 0; $total = $result->rowCount(); while($row = $result->fetch()) { try { $newData = getDenormalizedData($row['calendardata']); } catch (Exception $e) { echo "===\nException caught will trying to parser calendarobject.\n"; echo "Error message: " . $e->getMessage() . "\n"; echo "Record id: " . $row['id'] . "\n"; echo "This record is ignored, you should inspect it to see if there's anything wrong.\n===\n"; continue; } $stmt->execute(array( $newData['etag'], $newData['size'], $newData['componentType'], $newData['firstOccurence'], $newData['lastOccurence'], $row['id'], )); $done++; if ($done % 500 === 0) { echo "Completed: $done / $total\n"; } } echo "Process completed!\n"; /** * Parses some information from calendar objects, used for optimized * calendar-queries. * * Blantently copied from Sabre_CalDAV_Backend_PDO * * Returns an array with the following keys: * * etag * * size * * componentType * * firstOccurence * * lastOccurence * * @param string $calendarData * @return array */ function getDenormalizedData($calendarData) { $vObject = Sabre_VObject_Reader::read($calendarData); $componentType = null; $component = null; $firstOccurence = null; $lastOccurence = null; foreach($vObject->getComponents() as $component) { if ($component->name!=='VTIMEZONE') { $componentType = $component->name; break; } } if (!$componentType) { throw new Sabre_DAV_Exception_BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component'); } if ($componentType === 'VEVENT') { $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp(); // Finding the last occurence is a bit harder if (!isset($component->RRULE)) { if (isset($component->DTEND)) { $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp(); } elseif (isset($component->DURATION)) { $endDate = clone $component->DTSTART->getDateTime(); $endDate->add(Sabre_VObject_DateTimeParser::parse($component->DURATION->value)); $lastOccurence = $endDate->getTimeStamp(); } elseif ($component->DTSTART->getDateType()===Sabre_VObject_Property_DateTime::DATE) { $endDate = clone $component->DTSTART->getDateTime(); $endDate->modify('+1 day'); $lastOccurence = $endDate->getTimeStamp(); } else { $lastOccurence = $firstOccurence; } } else { $it = new Sabre_VObject_RecurrenceIterator($vObject, (string)$component->UID); $maxDate = new DateTime(Sabre_CalDAV_Backend_PDO::MAX_DATE); if ($it->isInfinite()) { $lastOccurence = $maxDate->getTimeStamp(); } else { $end = $it->getDtEnd(); while($it->valid() && $end < $maxDate) { $end = $it->getDtEnd(); $it->next(); } $lastOccurence = $end->getTimeStamp(); } } } return array( 'etag' => md5($calendarData), 'size' => strlen($calendarData), 'componentType' => $componentType, 'firstOccurence' => $firstOccurence, 'lastOccurence' => $lastOccurence, ); }