'The base table for project_project nodes.', 'fields' => array( 'nid' => array( 'description' => 'Primary Key: The {node}.nid of the project_release node.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'pid' => array( 'description' => 'The {project_projects}.nid of the project_project node with which the project_release node is associated.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'version' => array( 'description' => 'A string containing the full version of a release. The format of this string for a given project is dictated by {project_release_projects}.version_format.', 'type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '', ), 'tag' => array( 'description' => 'The name of a CVS branch or tag on which a release is based.', 'type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '', ), 'rebuild' => array( 'description' => 'A flag indicating whether or not the file associated with a release should be rebuilt periodically. For official releases this should be 0, for development snapshots it should be 1.', 'type' => 'int', 'size' => 'tiny', 'unsigned' => FALSE, 'not null' => FALSE, 'default' => 0, ), 'version_major' => array( 'description' => 'The major version number of a release.', 'type' => 'int', 'unsigned' => FALSE, 'not null' => FALSE, 'default' => NULL, ), 'version_minor' => array( 'description' => 'The minor version number of a release.', 'type' => 'int', 'unsigned' => FALSE, 'not null' => FALSE, 'default' => NULL, ), 'version_patch' => array( 'description' => 'The patch level version number of a release.', 'type' => 'int', 'unsigned' => FALSE, 'not null' => FALSE, 'default' => NULL, ), 'version_extra' => array( 'description' => 'A text string that can be used to provide additional information about a release. Ex: BETA', 'type' => 'varchar', 'length' => 255, 'not null' => FALSE, 'default' => NULL, ), 'version_extra_weight' => array( 'description' => 'Numeric code for ordering releases that define "version_extra".', 'type' => 'int', 'not null' => TRUE, 'default' => 0, ), 'version_extra_delta' => array( 'description' => 'The first span of digits found in version_extra. This is needed because we cannot natural sort natively without a stored procedure.', 'type' => 'int', 'not null' => TRUE, 'default' => 0, ), 'version_api_tid' => array( 'description' => 'The denormalized {term_node}.tid of the API compatibility term for this release, or 0 if the release has no such term.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => NULL, ), 'security_update' => array( 'description' => 'Denormalized flag to record if this release has the "project_release_security_update_tid" taxonomy term set or not', 'type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'update_status' => array( 'description' => 'Denormalized flag to record the update status for this release. Allowed values: PROJECT_RELEASE_UPDATE_STATUS_CURRENT (0), PROJECT_RELEASE_UPDATE_STATUS_NOT_CURRENT (1), PROJECT_RELEASE_UPDATE_STATUS_NOT_SECURE (2)', 'type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0, ), ), 'primary key' => array('nid'), 'indexes' => array( 'project_releases_pid' => array('pid') ), ); $schema['project_release_file'] = array( 'description' => 'Stores information about files attached to release nodes.', 'fields' => array( 'fid' => array( 'description' => 'Foreign Key: {files}.fid.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'nid' => array( 'description' => 'Foreign Key: {project_release_nodes}.nid.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'filehash' => array( 'description' => 'An MD5 hash of the file.', 'type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => '', ), ), 'primary key' => array('fid'), 'indexes' => array('nid' => array('nid')), ); $schema['project_release_projects'] = array( 'description' => 'Table used to store release specific information about projects.', 'fields' => array( 'nid' => array( 'description' => 'Primary Key: The {project_projects}.nid of the project_project node.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'releases' => array( 'description' => 'A flag indicating whether or not releases are enabled for a project.', 'type' => 'int', 'size' => 'tiny', 'unsigned' => FALSE, 'not null' => TRUE, 'default' => 1, ), 'version_format' => array( 'description' => 'A string used to designate the format of the {project_release_nodes}.version field for releases of a project.', 'type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '', ), ), 'primary key' => array('nid'), 'indexes' => array( 'project_release_projects_releases' => array('releases') ), ); $schema['project_release_supported_versions'] = array( 'description' => 'Table used to store information about which major versions of a project are supported and/or recommended.', 'fields' => array( 'nid' => array( 'description' => 'Primary Key: The {project_projects}.nid of the project_project node.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'tid' => array( 'description' => 'Primary Key: The {term_data}.tid of the API compatability version associated with a major version of a project.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'major' => array( 'description' => 'Primary Key: The {project_release_nodes}.version_major of a release node.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'supported' => array( 'description' => 'A flag to indicate whether or not a given major version of a project is supported.', 'type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 1, ), 'recommended' => array( 'description' => 'A flag to indicate whether or not a given major version of a project is recommended.', 'type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'snapshot' => array( 'description' => 'A flag to indicate whether or not snapshot releases of a major version of a project should be shown in the release download table.', 'type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'recommended_release' => array( 'description' => 'The {project_release_nodes}.nid of the recommended release node for this API tid and major version (the latest release without any "extra" version info such as "alpha1").', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'latest_release' => array( 'description' => 'The {project_release_nodes}.nid of the latest release node for this API tid and major version (even if it has "extra" version info such as "alpha1").', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'latest_security_release' => array( 'description' => 'The {project_release_nodes}.nid of the latest release node marked as a "security update" for this API tid and major version.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), ), 'primary key' => array('nid', 'tid', 'major'), ); $schema['project_release_package_errors'] = array( 'description' => 'Table used to store error messages generated by the scripts that package project_release nodes into tarballs.', 'fields' => array( 'nid' => array( 'description' => 'Primary Key: The {node}.nid of the project_release node.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'messages' => array( 'description' => 'The text of any error messages created by the packaging scripts.', 'type' => 'text', 'not null' => FALSE, ) ), 'primary key' => array('nid'), ); $schema['cache_project_release'] = array( 'description' => 'Cache table used to store the project release download tables.', 'fields' => array( 'cid' => array( 'description' => 'Primary Key: Unique cache ID.', 'type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '', ), 'data' => array( 'description' => 'A collection of data to cache.', 'type' => 'blob', 'not null' => FALSE, 'size' => 'big', ), 'expire' => array( 'description' => 'A Unix timestamp indicating when the cache entry should expire, or 0 for never.', 'type' => 'int', 'not null' => TRUE, 'default' => 0, ), 'created' => array( 'description' => 'A Unix timestamp indicating when the cache entry was created.', 'type' => 'int', 'not null' => TRUE, 'default' => 0, ), 'headers' => array( 'description' => 'Any custom HTTP headers to be added to cached data.', 'type' => 'text', 'not null' => FALSE, ), 'serialized' => array( 'description' => 'A flag to indicate whether content is serialized (1) or not (0).', 'type' => 'int', 'size' => 'small', 'not null' => TRUE, 'default' => 0 ), ), 'primary key' => array('cid'), 'indexes' => array( 'expire' => array('expire') ), ); return $schema; } /** * Populate the {project_release_nodes}.security_update field. * * @param $ret * Reference to an array for hook_update_N() return values. */ function _project_release_check_security_updates(&$ret) { $security_update_tid = variable_get('project_release_security_update_tid', 0); if (!empty($security_update_tid)) { $ret[] = update_sql("UPDATE {project_release_nodes} SET security_update = (SELECT tn.tid IS NOT NULL FROM node n LEFT JOIN term_node tn ON n.vid = tn.vid AND tn.tid = $security_update_tid WHERE n.nid = {project_release_nodes}.nid)"); } } /** * Populate the {project_release_nodes}.version_extra_weight field. * * @param $ret * Reference to an array for hook_update_N() return values. */ function _project_release_update_version_extra_weights(&$ret) { $weights = project_release_get_version_extra_weight_map(); foreach ($weights as $prefix => $weight) { if ($prefix == 'NULL') { $ret[] = update_sql("UPDATE {project_release_nodes} SET version_extra_weight = $weight WHERE version_extra IS NULL"); } else { $ret[] = update_sql("UPDATE {project_release_nodes} SET version_extra_weight = $weight WHERE LOWER(version_extra) LIKE '" . $prefix . "%'"); } } } /** * Add the 'serialized' field to the {cache_project_release} table. */ function project_release_update_6000() { $ret = array(); $spec = array( 'type' => 'int', 'size' => 'small', 'default' => 0, 'not null' => TRUE, ); db_add_field($ret, 'cache_project_release', 'serialized', $spec); return $ret; } /** * Add {project_release_file}. */ function project_release_update_6001() { $ret = array(); $schema = project_release_schema(); db_create_table($ret, 'project_release_file', $schema['project_release_file']); return $ret; } /** * Convert release file attachments to use core's file API. */ function project_release_update_6002() { // This determines how many issue nodes will be processed in each // batch run. A reasonable default has been chosen, but you may // want to tweak depending on your setup. $limit = 100; // Multi-part update if (!isset($_SESSION['project_release_update_6002'])) { $_SESSION['project_release_update_6002'] = 0; $_SESSION['project_release_update_6002_max'] = db_result(db_query("SELECT COUNT(prn.nid) FROM {project_release_nodes} prn INNER JOIN {node} n ON prn.nid = n.nid WHERE prn.file_path <> ''")); } // Pull the next batch of files. $files = db_query_range("SELECT prn.*, n.uid FROM {project_release_nodes} prn INNER JOIN {node} n ON prn.nid = n.nid WHERE prn.file_path <> '' ORDER BY prn.nid", $_SESSION['project_release_update_6002'], $limit); // Loop through each file. while ($file = db_fetch_object($files)) { // Make sure file is still there. if (file_exists($file->file_path)) { $filename = basename($file->file_path); $filesize = filesize(file_create_path($file->file_path)); $filemime = file_get_mimetype($filename); db_query("INSERT INTO {files} (uid, filename, filepath, filemime, filesize, status, timestamp) VALUES (%d, '%s', '%s', '%s', '%s', %d, %d)", $file->uid, $filename, $file->file_path, $filemime, $filesize, FILE_STATUS_PERMANENT, $file->file_date); $fid = db_last_insert_id('files', 'fid'); db_query("INSERT INTO {project_release_file} (fid, nid, filehash) VALUES (%d, %d,'%s')", $fid, $file->nid, $file->file_hash); } $_SESSION['project_release_update_6002']++; } if ($_SESSION['project_release_update_6002'] >= $_SESSION['project_release_update_6002_max']) { $count = $_SESSION['project_release_update_6002_max']; unset($_SESSION['project_release_update_6002']); unset($_SESSION['project_release_update_6002_max']); return array(array('success' => TRUE, 'query' => t('Converted release file attachments for @count releases', array('@count' => $count)))); } return array('#finished' => $_SESSION['project_release_update_6002'] / $_SESSION['project_release_update_6002_max']); } /** * Drop unused fields from {project_release_nodes}. */ function project_release_update_6003() { $ret = array(); db_drop_field($ret, 'project_release_nodes', 'file_path'); db_drop_field($ret, 'project_release_nodes', 'file_date'); db_drop_field($ret, 'project_release_nodes', 'file_hash'); return $ret; } /** * Add new columns to {project_release_supported_versions}. */ function project_release_update_6004() { $ret = array('#finished' => 0); if (!isset($_SESSION['project_release_update_6004'])) { $spec = array('type' => 'int', 'unsigned' => TRUE, 'default' => NULL, 'not null' => FALSE); db_add_field($ret, 'project_release_supported_versions', 'recommended_release', $spec); db_add_field($ret, 'project_release_supported_versions', 'latest_release', $spec); $_SESSION['project_release_update_6004'] = 0; $_SESSION['project_release_update_6004_max'] = db_result(db_query("SELECT COUNT(*) FROM {project_release_supported_versions}")); } // Number of rows to convert per batch. $limit = 20; while ($limit-- && $item = db_fetch_array(db_query_range("SELECT * FROM {project_release_supported_versions} WHERE latest_release IS NULL", 0, 1))) { // We have a branch we haven't processed yet (latest_release is still // NULL), so we invoke project_release_check_supported_versions() to // run some queries to determine the recommended and latest releases on // that branch. Normally, it updates {project_release_supported_versions} // with this data in which case it returns TRUE. However, if it returns // FALSE, it means we didn't find any releases on that branch, and it // didn't touch the table. In that case, we need to update the table // ourselves for this branch to mark the new fields as 0 (not NULL) so // that we don't check this branch again. if (!project_release_check_supported_versions($item['nid'], $item['tid'], $item['major'], FALSE)) { db_query("UPDATE {project_release_supported_versions} SET recommended_release = %d, latest_release = %d WHERE nid = %d AND tid = %d AND major = %d", 0, 0, $item['nid'], $item['tid'], $item['major']); } $_SESSION['project_release_update_6004']++; } if ($_SESSION['project_release_update_6004'] >= $_SESSION['project_release_update_6004_max']) { // We're done. Set our new columns to default to 0 from here on out. $ret[] = update_sql("ALTER TABLE {project_release_supported_versions} ALTER COLUMN recommended_release SET DEFAULT 0"); $ret[] = update_sql("ALTER TABLE {project_release_supported_versions} ALTER COLUMN latest_release SET DEFAULT 0"); unset($_SESSION['project_release_update_6004']); unset($_SESSION['project_release_update_6004_max']); $ret['#finished'] = 1; } else { $ret['#finished'] = $_SESSION['project_release_update_6004'] / $_SESSION['project_release_update_6004_max']; } return $ret; } /** * Add the 'version_api_tid' column to {project_release_nodes}. */ function project_release_update_6005() { $ret = array(); $spec = array( 'description' => 'The denormalized {term_node}.tid of the API compatibility term for this release, or 0 if the release has no such term.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => NULL, ); db_add_field($ret, 'project_release_nodes', 'version_api_tid', $spec); // Populate the new column from {term_node}. $api_vid = _project_release_get_api_vid(); $ret[] = update_sql("UPDATE {project_release_nodes} SET version_api_tid = (SELECT tn.tid FROM {node} n INNER JOIN {term_node} tn ON n.vid = tn.vid INNER JOIN {term_data} td ON tn.tid = td.tid WHERE n.nid = {project_release_nodes}.nid AND td.vid = $api_vid)"); return $ret; } /** * Add security_update and update_status columns to {project_release_nodes}. */ function project_release_update_6006() { $ret = array(); $spec = array( 'description' => 'Denormalized flag to record if this release has the "project_release_security_update_tid" taxonomy term set or not', 'type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ); db_add_field($ret, 'project_release_nodes', 'security_update', $spec); if (module_exists('taxonomy')) { // Populate the new column from {term_node}. _project_release_check_security_updates($ret); } $spec = array( 'description' => 'Denormalized flag to record the update status for this release. Allowed values: PROJECT_RELEASE_UPDATE_STATUS_CURRENT (0), PROJECT_RELEASE_UPDATE_STATUS_NOT_CURRENT (1), PROJECT_RELEASE_UPDATE_STATUS_NOT_SECURE (2)', 'type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0, ); db_add_field($ret, 'project_release_nodes', 'update_status', $spec); // This will be initialized by project_release_check_supported_versions() // in project_release_update_6008(), so we don't need to do that here. return $ret; } /** * Add the 'version_extra_weight' column to {project_release_nodes}. */ function project_release_update_6007() { $ret = array(); $spec = array( 'type' => 'int', 'not null' => TRUE, 'default' => 0, ); db_add_field($ret, 'project_release_nodes', 'version_extra_weight', $spec); // Initialize the values in the DB based on the existing weights. _project_release_update_version_extra_weights($ret); return $ret; } /** * Add the {project_release_supported_versions}.latest_security_release field. */ function project_release_update_6008() { $ret = array('#finished' => 0); if (!isset($_SESSION['project_release_update_6008'])) { $spec = array( 'type' => 'int', 'unsigned' => TRUE, 'default' => NULL, 'not null' => FALSE, ); db_add_field($ret, 'project_release_supported_versions', 'latest_security_release', $spec); $_SESSION['project_release_update_6008'] = 0; $_SESSION['project_release_update_6008_max'] = db_result(db_query("SELECT COUNT(*) FROM {project_release_supported_versions}")); } // Number of rows to convert per batch. $limit = 20; while ($limit-- && $item = db_fetch_array(db_query_range("SELECT * FROM {project_release_supported_versions} WHERE latest_security_release IS NULL", 0, 1))) { // We have a branch we haven't processed yet (latest_security_release is // still NULL), so we invoke project_release_check_supported_versions() to // run some queries to determine the recommended, latest, and latest // security releases on that branch. Normally, it updates // {project_release_supported_versions} with this data in which case it // returns TRUE. However, if it returns FALSE, it means we didn't find any // releases on that branch, and it didn't touch the table. In that case, // we need to update the table ourselves for this branch to mark // latest_security_release as 0 (not NULL) so that we don't check this // branch again. if (!project_release_check_supported_versions($item['nid'], $item['tid'], $item['major'], FALSE)) { db_query("UPDATE {project_release_supported_versions} SET latest_security_release = %d WHERE nid = %d AND tid = %d AND major = %d", 0, $item['nid'], $item['tid'], $item['major']); // If project_release_check_supported_versions() returned FALSE, there // are no releases on this branch, so there's nothing to initialize // {project_release_nodes}.update_status for. } $_SESSION['project_release_update_6008']++; } if ($_SESSION['project_release_update_6008'] >= $_SESSION['project_release_update_6008_max']) { // We're done. Set our new columns to default to 0 from here on out. $ret[] = update_sql("ALTER TABLE {project_release_supported_versions} ALTER COLUMN latest_security_release SET DEFAULT 0"); unset($_SESSION['project_release_update_6008']); unset($_SESSION['project_release_update_6008_max']); $ret['#finished'] = 1; } else { $ret['#finished'] = $_SESSION['project_release_update_6008'] / $_SESSION['project_release_update_6008_max']; } return $ret; } /** * Add the {project_release_nodes}.version_extra_delta field. * * Also recompute the latest and recommended releases on each branch, and * therefore the update_status field, since version_extra_delta is needed * to properly order alpha10 vs. alpha9, etc. */ function project_release_update_6009() { $ret = array('#finished' => 0); if (!isset($_SESSION['project_release_update_6009'])) { $spec = array( 'type' => 'int', 'not null' => TRUE, 'default' => 0, 'description' => 'The first span of digits found in version_extra. This is needed because we cannot natural sort natively without a stored procedure.', ); db_add_field($ret, 'project_release_nodes', 'version_extra_delta', $spec); // Initialize version_extra_delta to -1 to identify the rows to process. db_query('UPDATE {project_release_nodes} SET version_extra_delta = -1 WHERE version_extra IS NOT NULL'); $_SESSION['project_release_update_6009'] = 0; $_SESSION['project_release_update_6009_max'] = db_result(db_query("SELECT COUNT(*) FROM {project_release_nodes} WHERE version_extra IS NOT NULL")); } // Number of rows to convert per batch. $limit = 20; while ($limit-- && $item = db_fetch_array(db_query_range("SELECT nid, pid, version_major, version_api_tid, version_extra FROM {project_release_nodes} WHERE version_extra_delta = -1", 0, 1))) { // Due to the new sorting method, the "recommended" and "latest" releases // will change on any releases affected by http://drupal.org/node/649254. // Determine the correct version_extra_delta and update it. $match = array(); $nmatch = preg_match('/(\d+)/', $item['version_extra'], $match); db_query('UPDATE {project_release_nodes} SET version_extra_delta = %d WHERE nid = %d', ($nmatch) ? $match[1] : 0, $item['nid']); // Finally, recheck the branch. // Note: this is ineffecient: we only really need to call this // once per unique branch we're touching, not for every single // release, but optimizing this isn't worth the effort, and would // potentially require an enormous array in $_SESSION that could // cause its own problems. project_release_check_supported_versions($item['pid'], $item['version_api_tid'], $item['version_major'], FALSE); $_SESSION['project_release_update_6009']++; } if ($_SESSION['project_release_update_6009'] >= $_SESSION['project_release_update_6009_max']) { // Done. Clean up. unset($_SESSION['project_release_update_6009']); unset($_SESSION['project_release_update_6009_max']); $ret['#finished'] = 1; } else { $ret['#finished'] = $_SESSION['project_release_update_6009'] / $_SESSION['project_release_update_6009_max']; } return $ret; }