From c68776ae92edb74bda3be506b5e2316c8d3b64c5 Mon Sep 17 00:00:00 2001 From: Geoff Appleby Date: Sun, 14 Aug 2016 18:28:39 -0700 Subject: [PATCH 1/4] Split db version columns to major + minor values --- src/DrupalReleaseDate/Installation.php | 73 +++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 6 deletions(-) diff --git a/src/DrupalReleaseDate/Installation.php b/src/DrupalReleaseDate/Installation.php index 926bfb1..fa55093 100644 --- a/src/DrupalReleaseDate/Installation.php +++ b/src/DrupalReleaseDate/Installation.php @@ -154,27 +154,30 @@ protected function installSchema() $schema = new Schema(); $samples = $schema->createTable('samples'); - $samples->addColumn('version', 'string', array('length' => 32)); + $samples->addColumn('version_major', 'smallint'); + $samples->addColumn('version_minor', 'smallint'); $samples->addColumn('when', 'datetime'); $samples->addColumn('notes', 'string', array('default' => '')); - $samples->setPrimaryKey(array('version', 'when')); + $samples->setPrimaryKey(array('version_major', 'version_minor', 'when')); $sample_values = $schema->createTable('sample_values'); - $sample_values->addColumn('version', 'string', array('length' => 32)); + $sample_values->addColumn('version_major', 'smallint'); + $sample_values->addColumn('version_minor', 'smallint'); $sample_values->addColumn('when', 'datetime'); $sample_values->addColumn('key', 'string', array('length' => 64)); $sample_values->addColumn('value', 'smallint', array('notnull' => false)); - $sample_values->setPrimaryKey(array('version', 'when', 'key')); + $sample_values->setPrimaryKey(array('version_major', 'version_minor', 'when', 'key')); $estimates = $schema->createTable('estimates'); - $estimates->addColumn('version', 'string', array('length' => 32)); + $estimates->addColumn('version_major', 'smallint'); + $estimates->addColumn('version_minor', 'smallint'); $estimates->addColumn('when', 'datetime'); $estimates->addColumn('started', 'datetime'); $estimates->addColumn('completed', 'datetime', array('notnull' => false)); $estimates->addColumn('estimate', 'datetime', array('notnull' => false)); $estimates->addColumn('note', 'string', array('default' => '')); $estimates->addColumn('data', 'blob'); - $estimates->setPrimaryKey(array('version', 'when')); + $estimates->setPrimaryKey(array('version_major', 'version_minor', 'when')); $state = $schema->createTable('state'); $state->addColumn('key', 'string', array('length' => 255)); @@ -437,4 +440,62 @@ protected function update_7() unlink($this->configDir . '/InstallationVersion'); } + + /** + * Split string version fields to major + minor integers. + */ + protected function update_8() + { + $synchronizer = new SingleDatabaseSynchronizer($this->db); + + $schema = $this->db->getSchemaManager()->createSchema(); + $schema = clone $schema; + + // Add fields. + $samplesTable = $schema->getTable('samples'); + $samplesTable->addColumn('version_major', 'smallint'); + $samplesTable->addColumn('version_minor', 'smallint'); + + $sampleValuesTable = $schema->getTable('sample_values'); + $sampleValuesTable->addColumn('version_major', 'smallint'); + $sampleValuesTable->addColumn('version_minor', 'smallint'); + + $estimatesTable = $schema->getTable('estimates'); + $estimatesTable->addColumn('version_major', 'smallint'); + $estimatesTable->addColumn('version_minor', 'smallint'); + + $synchronizer->updateSchema($schema); + + // Copy values to new fields. + $this->db->createQueryBuilder() + ->update('samples') + ->set('version_major', 'SUBSTRING(version FROM 1 FOR 1)') + ->set('version_minor', 'SUBSTRING(version FROM 3 FOR 1)') + ->execute(); + $this->db->createQueryBuilder() + ->update('sample_values') + ->set('version_major', 'SUBSTRING(version FROM 1 FOR 1)') + ->set('version_minor', 'SUBSTRING(version FROM 3 FOR 1)') + ->execute(); + $this->db->createQueryBuilder() + ->update('estimates') + ->set('version_major', 'SUBSTRING(version FROM 1 FOR 1)') + ->set('version_minor', 'SUBSTRING(version FROM 3 FOR 1)') + ->execute(); + + // Update primary keys. + $samplesTable->dropPrimaryKey(); + $samplesTable->setPrimaryKey(array('version_major', 'when', 'version_minor')); + $sampleValuesTable->dropPrimaryKey(); + $sampleValuesTable->setPrimaryKey(array('version_major', 'when', 'key', 'version_minor')); + $estimatesTable->dropPrimaryKey(); + $estimatesTable->setPrimaryKey(array('version_major', 'when', 'version_minor')); + $synchronizer->updateSchema($schema); + + // Drop old version column. + $samplesTable->dropColumn('version'); + $sampleValuesTable->dropColumn('version'); + $estimatesTable->dropColumn('version'); + $synchronizer->updateSchema($schema); + } } From 0c999177d7e7fb92446d1f2bddb8deebd96f544a Mon Sep 17 00:00:00 2001 From: Geoff Appleby Date: Mon, 15 Aug 2016 14:37:02 -0700 Subject: [PATCH 2/4] Code style fixes in Controllers/Data --- src/DrupalReleaseDate/Controllers/Data.php | 29 +++++++++++----------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/DrupalReleaseDate/Controllers/Data.php b/src/DrupalReleaseDate/Controllers/Data.php index c867ecd..ea18f93 100644 --- a/src/DrupalReleaseDate/Controllers/Data.php +++ b/src/DrupalReleaseDate/Controllers/Data.php @@ -4,6 +4,7 @@ use DateTime; use DateInterval; +use Doctrine\DBAL\Connection; use Silex\Application; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -44,7 +45,8 @@ public static function parseVersionFromRequest(Request $request) * @param string $key * @return \DateTime */ - public static function parseDateFromRequest(Request $request, $key) { + public static function parseDateFromRequest(Request $request, $key) + { $value = null; if ($request->query->has($key)) { $value = new DateTime($request->query->get($key)); @@ -58,27 +60,24 @@ public function samples(Application $app, Request $request) $responseData = array(); try { $versionString = self::parseVersionFromRequest($request); - } - catch (\Exception $e) { - $app->abort(400, 'Invalid "version" parameter'); + } catch (\Exception $e) { + $app->abort(400, 'Invalid version parameters'); } $responseData['version'] = $versionString; try { - if ($from = self::parseDateFromRequest($request, 'from')) { + if (($from = self::parseDateFromRequest($request, 'from'))) { $responseData['from'] = $from->format(DateTime::ATOM); } - } - catch (\Exception $e) { + } catch (\Exception $e) { $app->abort(400, 'Invalid "from" parameter'); } try { - if($to = self::parseDateFromRequest($request, 'to')) { + if (($to = self::parseDateFromRequest($request, 'to'))) { $responseData['to'] = $to->format(DateTime::ATOM); } - } - catch (\Exception $e) { + } catch (\Exception $e) { $app->abort(400, 'Invalid "to" parameter'); } @@ -108,16 +107,16 @@ public function samples(Application $app, Request $request) $lastResults = $lastQuery->execute(); $lastDate = null; $cacheMaxAge = 3600; - if ($lastResultRow = $lastResults->fetch(\PDO::FETCH_ASSOC)) { + if (($lastResultRow = $lastResults->fetch(\PDO::FETCH_ASSOC))) { $lastDate = new DateTime($lastResultRow['when']); $responseData['modified'] = $lastDate->format(DateTime::ATOM); $nowDate = new DateTime(); - if ($to && $to < $nowDate){ - // If the request limits to data in the past, we can set a very long expiry since the results will never change. + if ($to && $to < $nowDate) { + // If the request limits to data in the past, we can set a very + // long expiry since the results will never change. $cacheMaxAge = 31536000; - } - else { + } else { // Calculate cache max age based on the time to the next sample. $nextSampleDate = clone $lastDate; $nextSampleDate->add(new DateInterval('PT' . $app['config']['sample.interval'] . 'S')); From dfee0beab75053d0645c9bfe822984eedd8d0eab Mon Sep 17 00:00:00 2001 From: Geoff Appleby Date: Mon, 15 Aug 2016 14:39:16 -0700 Subject: [PATCH 3/4] Update Contollers/Data::parseVersionFromRequest to accept separate major and minor values --- src/DrupalReleaseDate/Controllers/Data.php | 45 ++++++++++++++++------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/DrupalReleaseDate/Controllers/Data.php b/src/DrupalReleaseDate/Controllers/Data.php index ea18f93..db7f4c2 100644 --- a/src/DrupalReleaseDate/Controllers/Data.php +++ b/src/DrupalReleaseDate/Controllers/Data.php @@ -15,27 +15,48 @@ class Data /** * Parse a version string from the request's GET parameters. * - * Accepts either a string containing only a major version number, or major - * and minor version numbers separated by a period. If only a major version - * is provided, the return value is normalized to include '0' as the minor - * version. + * 'version' accepts either a string containing only a major version number, + * or major and minor version numbers separated by a period. If only a + * major version is provided, the return value is normalized to include '0' + * as the minor version. * * @param Request $request - * @return string + * @return array * * @throws \Exception */ public static function parseVersionFromRequest(Request $request) { - $versionString = $request->query->get('version', '8.0'); - if (!preg_match('/^([0-9]+)(\\.[0-9]+)?$/', $versionString)) { - throw new \Exception("Invalid version parameter"); + if (($versionString = $request->query->get('version', false)) !== false) { + if (!preg_match('/^([0-9]+)(\\.[0-9]+)?$/', $versionString)) { + throw new \Exception("Invalid version parameter"); + } + $segments = explode('.', $versionString); + $major = $segments[0]; + $minor = empty($segments[1]) ? '0' : $segments[1]; + + return array( + 'major' => $major, + 'minor' => $minor, + ); + } elseif (($majorVersion = $request->query->get('version_major', false)) !== false) { + $version = array( + 'major' => (int) $majorVersion, + ); + + if (($minorVersion = $request->query->get('version_minor', false)) !== false) { + $version['minor'] = $minorVersion; + } + + return $version; + } elseif (($minorVersion = $request->query->get('version_minor', false)) !== false) { + throw new \Exception("Major version must be specified when minor version is provided."); } - $segments = explode('.', $versionString); - $major = $segments[0]; - $minor = empty($segments[1])? '0' : $segments[1]; - return $major . '.' . $minor; + return array( + 'major' => 8, + 'minor' => 0, + ); } /** From d3132c8f0562194a78fb585a70a35e2d0d2da9a9 Mon Sep 17 00:00:00 2001 From: Geoff Appleby Date: Mon, 15 Aug 2016 14:42:37 -0700 Subject: [PATCH 4/4] Update Controllers/Data::samples to handle new version number format - Handle array response from parseVersionFromRequest - Handle queries with only major version specified --- src/DrupalReleaseDate/Controllers/Data.php | 84 +++++++++++++++------- 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/src/DrupalReleaseDate/Controllers/Data.php b/src/DrupalReleaseDate/Controllers/Data.php index db7f4c2..fb3c4eb 100644 --- a/src/DrupalReleaseDate/Controllers/Data.php +++ b/src/DrupalReleaseDate/Controllers/Data.php @@ -78,13 +78,19 @@ public static function parseDateFromRequest(Request $request, $key) public function samples(Application $app, Request $request) { + /** @var Connection $db */ + $db = $app['db']; + $responseData = array(); try { - $versionString = self::parseVersionFromRequest($request); + $version = self::parseVersionFromRequest($request); } catch (\Exception $e) { $app->abort(400, 'Invalid version parameters'); } - $responseData['version'] = $versionString; + $responseData['version_major'] = $version['major']; + if (isset($version['minor'])) { + $responseData['version_minor'] = $version['minor']; + } try { @@ -107,22 +113,28 @@ public function samples(Application $app, Request $request) } // Check against Last-Modified header. - $lastQuery = $app['db']->createQueryBuilder() + $lastQuery = $db->createQueryBuilder() ->select('s.when') ->from('samples', 's') - ->where('s.version = :version') - ->setParameter('version', $app['db']->convertToDatabaseValue($versionString, 'string'), \PDO::PARAM_STR) - ->orderBy($app['db']->quoteIdentifier('when'), 'DESC') + ->where('s.version_major = :version_major') + ->setParameter('version_major', $version['major'], \PDO::PARAM_INT) + ->orderBy($db->quoteIdentifier('when'), 'DESC') ->setMaxResults(1); + + if (isset($version['minor'])) { + $lastQuery + ->andWhere('s.version_minor = :version_minor') + ->setParameter('version_minor', $version['minor'], \PDO::PARAM_INT); + } if ($from) { $lastQuery ->andWhere('s.when >= :from') - ->setParameter('from', $app['db']->convertToDatabaseValue($from, 'datetime'), \PDO::PARAM_STR); + ->setParameter('from', $db->convertToDatabaseValue($from, 'datetime'), \PDO::PARAM_STR); } if ($to) { $lastQuery ->andWhere('s.when <= :to') - ->setParameter('to', $app['db']->convertToDatabaseValue($to, 'datetime'), \PDO::PARAM_STR); + ->setParameter('to', $db->convertToDatabaseValue($to, 'datetime'), \PDO::PARAM_STR); } $lastResults = $lastQuery->execute(); @@ -156,25 +168,33 @@ public function samples(Application $app, Request $request) } } - $sampleValuesQuery = $app['db']->createQueryBuilder() + $sampleValuesQuery = $db->createQueryBuilder() ->select( + 'sv.version_major', + 'sv.version_minor', 'sv.when', 'sv.key', 'sv.value' ) ->from('sample_values', 'sv') - ->where('sv.version = :version') - ->setParameter('version', $app['db']->convertToDatabaseValue($versionString, 'string'), \PDO::PARAM_STR) - ->orderBy($app['db']->quoteIdentifier('when'), 'ASC'); + ->where('sv.version_major = :version_major') + ->setParameter('version_major', $version['major'], \PDO::PARAM_INT) + ->orderBy($db->quoteIdentifier('when'), 'ASC'); + + if (isset($version['minor'])) { + $sampleValuesQuery + ->andWhere('sv.version_minor = :version_minor') + ->setParameter('version_minor', $version['minor'], \PDO::PARAM_INT); + } if ($from) { $sampleValuesQuery ->andWhere('sv.when >= :from') - ->setParameter('from', $app['db']->convertToDatabaseValue($from, 'datetime'), \PDO::PARAM_STR); + ->setParameter('from', $db->convertToDatabaseValue($from, 'datetime'), \PDO::PARAM_STR); } if ($to) { $sampleValuesQuery ->andWhere('sv.when <= :to') - ->setParameter('to', $app['db']->convertToDatabaseValue($to, 'datetime'), \PDO::PARAM_STR); + ->setParameter('to', $db->convertToDatabaseValue($to, 'datetime'), \PDO::PARAM_STR); } if ($request->query->has('limit')) { @@ -184,22 +204,28 @@ public function samples(Application $app, Request $request) } $responseData['limit'] = $limit; - $dateLimitQuery = $app['db']->createQueryBuilder() + $dateLimitQuery = $db->createQueryBuilder() ->select('s.when') ->from('samples', 's') - ->where('s.version = :version') - ->setParameter('version', $app['db']->convertToDatabaseValue($versionString, 'string'), \PDO::PARAM_STR) - ->orderBy($app['db']->quoteIdentifier('when'), 'DESC') + ->where('s.version_major = :version_major') + ->setParameter('version_major', $version['major'], \PDO::PARAM_INT) + ->orderBy($db->quoteIdentifier('when'), 'DESC') ->setMaxResults($limit); + + if (isset($version['minor'])) { + $dateLimitQuery + ->andWhere('s.version_minor = :version_minor') + ->setParameter('version_minor', $version['minor'], \PDO::PARAM_INT); + } if ($from) { $dateLimitQuery ->andWhere('s.when >= :from') - ->setParameter('from', $app['db']->convertToDatabaseValue($from, 'datetime'), \PDO::PARAM_STR); + ->setParameter('from', $db->convertToDatabaseValue($from, 'datetime'), \PDO::PARAM_STR); } if ($to) { $dateLimitQuery ->andWhere('s.when <= :to') - ->setParameter('to', $app['db']->convertToDatabaseValue($to, 'datetime'), \PDO::PARAM_STR); + ->setParameter('to', $db->convertToDatabaseValue($to, 'datetime'), \PDO::PARAM_STR); } $dateLimitResults = $dateLimitQuery @@ -227,15 +253,21 @@ public function samples(Application $app, Request $request) $sampleValuesResult = $sampleValuesQuery->execute(); $data = array(); - while ($sampleValueRow = $sampleValuesResult->fetch(\PDO::FETCH_ASSOC)) { - $valueWhen = $app['db']->convertToPhpValue($sampleValueRow['when'], 'datetime'); - $valueWhenTimestamp = $valueWhen->getTimestamp(); - if (!isset($data[$valueWhenTimestamp])) { - $data[$valueWhenTimestamp] = array( + while (($sampleValueRow = $sampleValuesResult->fetch(\PDO::FETCH_ASSOC))) { + $valueWhen = $db->convertToPhpValue($sampleValueRow['when'], 'datetime'); + $sampleKey = implode(array( + $sampleValueRow['version_major'], + $sampleValueRow['version_minor'], + $valueWhen->getTimestamp(), + )); + if (!isset($data[$sampleKey])) { + $data[$sampleKey] = array( + 'version_major' => $sampleValueRow['version_major'], + 'version_minor' => $sampleValueRow['version_minor'], 'when' => $valueWhen->format(DateTime::ATOM), ); } - $data[$valueWhenTimestamp][$sampleValueRow['key']] = $app['db']->convertToPhpValue($sampleValueRow['value'], 'smallint'); + $data[$sampleKey][$sampleValueRow['key']] = $db->convertToPhpValue($sampleValueRow['value'], 'smallint'); } $responseData['data'] = array_values($data);