Index: database/updates.inc =================================================================== RCS file: /cvs/drupal/drupal/database/updates.inc,v retrieving revision 1.78 diff -u -r1.78 updates.inc --- database/updates.inc 7 Dec 2004 16:55:38 -0000 1.78 +++ database/updates.inc 4 Jan 2005 09:15:56 -0000 @@ -90,7 +90,8 @@ "2004-11-07" => "update_111", "2004-11-15" => "update_112", "2004-11-28" => "update_113", - "2004-12-05" => "update_114" + "2004-12-05" => "update_114", + "2004-12-13" => "update_115" ); function update_32() { @@ -2064,6 +2065,74 @@ $ret[] = update_sql("ALTER TABLE {node} DROP users"); return $ret; +} + +function update_115() { + $ret = array(); + + if ($GLOBALS['db_type'] == 'mysql') { + $ret[] = update_sql("ALTER TABLE {node} DROP INDEX node_title_type"); + $ret[] = update_sql("CREATE TABLE {node_revisions} ( + nid int(10) unsigned NOT NULL, + vid int(10) unsigned NOT NULL, + uid int(10) NOT NULL default '0', + title varchar(128) NOT NULL default '', + body longtext NOT NULL, + teaser longtext NOT NULL, + timestamp int(11) NOT NULL default '0', + format int(4) NOT NULL default '0', + PRIMARY KEY (vid), + KEY nid (nid), + KEY uid (uid) + )"); + $ret[] = update_sql("ALTER TABLE {node} ADD vid int(10) unsigned NOT NULL default '0'"); + } + else { + $ret[] = update_sql("ALTER TABLE {node} DROP INDEX node_title_idx"); + $ret[] = update_sql("CREATE TABLE {node_revisions} ( + vid integer NOT NULL default '0', + nid integer NOT NULL default '0', + uid integer NOT NULL default '0', + title varchar(128) NOT NULL default '', + body text NOT NULL default '', + teaser text NOT NULL default '', + timestamp integer NOT NULL default '0', + format smallint NOT NULL default '0', + PRIMARY KEY (vid) + )"); + $ret[] = update_sql("ALTER TABLE {node} ADD vid integer NOT NULL default '0'"); + $ret[] = update_sql("CREATE INDEX node_revisions_uid_idx ON node_revisions(uid)"); + $ret[] = update_sql("CREATE INDEX node_revisions_nid_idx ON node_revisions(nid)"); + } + + $vid = 0; + $result = db_query("SELECT title, body, teaser, changed, format, nid, revisions FROM {node}"); + while ($row = db_fetch_array($result)) { + db_query("INSERT INTO {node_revisions} (nid, vid, uid, title, body, teaser, timestamp, format) VALUES (%d, %d, %d, '%s', '%s', '%s', %d, %d)", $row['nid'], ++$vid, $row['uid'], $row['title'], $row['body'], $row['teaser'], $row['changed'], $row['format']); + db_query("UPDATE {node} SET vid = %d WHERE nid = %d", $vid, $row['nid']); + if (strlen($row['revisions']) > 0) { + $revisions = unserialize($row['revisions']); + foreach ($revisions as $version) { + $revision = array(); + $revision['uid'] = $version['uid']; + $revision['timestamp'] = $version['timestamp']; + $revision['title'] = $version['node']->title; + $revision['body'] = $version['node']->body; + $revision['teaser'] = $version['node']->teaser; + $revision['format'] = $version['node']->format; + db_query("INSERT INTO {node_revisions} (nid, vid, uid, title, body, teaser, timestamp, format) VALUES (%d, %d, %d, '%s', '%s', '%s', %d, %d)", $row['nid'], ++$vid, $revision['uid'], $revision['title'], $revision['body'], $revision['teaser'], $revision['timestamp'], $revision['format']); + } + } + } + + $ret[] = update_sql("INSERT INTO {sequences} (name, id) VALUES ('{node_revisions}_vid', $vid)"); + $ret[] = update_sql("ALTER TABLE {node} DROP title"); + $ret[] = update_sql("ALTER TABLE {node} DROP teaser"); + $ret[] = update_sql("ALTER TABLE {node} DROP body"); + $ret[] = update_sql("ALTER TABLE {node} DROP format"); + + return $ret; + } function update_sql($sql) { Index: includes/database.mysql.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database.mysql.inc,v retrieving revision 1.27 diff -u -r1.27 database.mysql.inc --- includes/database.mysql.inc 29 Nov 2004 13:10:43 -0000 1.27 +++ includes/database.mysql.inc 4 Jan 2005 09:15:56 -0000 @@ -236,6 +236,49 @@ } /** + * Begins a transaction/lock + * + * Use this when you need to update multiple tables in such a way that + * either all of the updates go through or none do at all. This is + * especially useful for the improved revisions system. + * + * In order to accomodate older versions of MySQL that don't support + * table types that have transactions, rollback is not used, and we + * must specify which tables are going to be + * + * So, your code will look something like this: + * db_begin_transaction(array('table1', 'table2')); + * // Check if conditions are okay for this update + * if ($conditions_ok) { + * // run queries + * } + * db_commit_transaction(); + * + * You code should ALWAYS call both db_begin_transaction() and + * db_commit_transaction() and ALWAYS call them in the same order + * + * @param $tables array of tables to lock + * @return a DB_Result object or a DB_Error + */ +function db_begin_transaction($tables) { + foreach ($tables as $num => $table) { + $tables[$num]= $table .' WRITE'; + } + return db_query('LOCK TABLES '. implode(', ', $tables)); +} + +/** + * Commits a transaction/lock + * + * See db_begin_transaction() for usage details + * + * @return a DB_Result object or a DB_Error + */ +function db_commit_transaction() { + return db_query('UNLOCK TABLES'); +} + +/** * @} End of "ingroup database". */ Index: includes/database.pgsql.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database.pgsql.inc,v retrieving revision 1.5 diff -u -r1.5 database.pgsql.inc --- includes/database.pgsql.inc 29 Nov 2004 13:10:43 -0000 1.5 +++ includes/database.pgsql.inc 4 Jan 2005 09:15:56 -0000 @@ -231,6 +231,45 @@ } /** + * Begins a transaction/lock + * + * Use this when you need to update multiple tables in such a way that + * either all of the updates go through or none do at all. This is + * especially useful for the improved revisions system. + * + * In order to accomodate older versions of MySQL that don't support + * table types that have transactions, rollback is not used, and we + * must specify which tables are going to be + * + * So, your code will look something like this: + * db_begin_transaction(array('table1', 'table2')); + * // Check if conditions are okay for this update + * if ($conditions_ok) { + * // run queries + * } + * db_commit_transaction(); + * + * You code should ALWAYS call both db_begin_transaction() and + * db_commit_transaction() and ALWAYS call them in the same order + * + * @param $tables array of tables to lock + * @return a DB_Result object or a DB_Error + */ +function db_begin_transaction($tables) { + return db_query('START TRANSACTION'); +} + +/** + * Commits a transaction/lock + * + * See db_begin_transaction() for usage details + * + * @return a DB_Result object or a DB_Error + */ +function db_commit_transaction() { + return db_query('COMMIT'); +} +/** * @} End of "ingroup database". */ Index: modules/blog.module =================================================================== RCS file: /cvs/drupal/drupal/modules/blog.module,v retrieving revision 1.208 diff -u -r1.208 blog.module --- modules/blog.module 15 Dec 2004 21:19:41 -0000 1.208 +++ modules/blog.module 4 Jan 2005 09:15:56 -0000 @@ -102,7 +102,7 @@ $account = $user; } - $result = db_query_range('SELECT n.nid, n.title, n.teaser, n.created, u.name, u.uid FROM {node} n '. node_access_join_sql() .' INNER JOIN {users} u ON n.uid = u.uid WHERE '. node_access_where_sql() ." AND n.type = 'blog' AND u.uid = %d AND n.status = 1 ORDER BY n.created DESC", $uid, 0, 15); + $result = db_query_range('SELECT n.nid, r.title, r.teaser, n.created, u.name, u.uid FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid '. node_access_join_sql() .' INNER JOIN {users} u ON n.uid = u.uid WHERE '. node_access_where_sql() ." AND n.type = 'blog' AND u.uid = %d AND n.status = 1 ORDER BY n.created DESC", $uid, 0, 15); $channel['title'] = $account->name ."'s blog"; $channel['link'] = url("blog/$uid", NULL, NULL, TRUE); $channel['description'] = $term->description; @@ -113,7 +113,7 @@ * Displays an RSS feed containing recent blog entries of all users. */ function blog_feed_last() { - $result = db_query_range('SELECT n.nid, n.title, n.teaser, n.created, u.name, u.uid FROM {node} n '. node_access_join_sql() .' INNER JOIN {users} u ON n.uid = u.uid WHERE '. node_access_where_sql() ." AND n.type = 'blog' AND n.status = 1 ORDER BY n.created DESC", 0, 15); + $result = db_query_range('SELECT n.nid, r.title, r.teaser, n.created, u.name, u.uid FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid '. node_access_join_sql() .' INNER JOIN {users} u ON n.uid = u.uid WHERE '. node_access_where_sql() ." AND n.type = 'blog' AND n.status = 1 ORDER BY n.created DESC", 0, 15); $channel['title'] = variable_get('site_name', 'drupal') .' blogs'; $channel['link'] = url('blog', NULL, NULL, TRUE); $channel['description'] = $term->description; @@ -304,7 +304,7 @@ } else if ($op == 'view') { if (user_access('access content')) { - $block['content'] = node_title_list(db_query_range('SELECT DISTINCT(n.nid), n.title, n.created FROM {node} n '. node_access_join_sql() ." WHERE n.type = 'blog' AND n.status = 1 AND ". node_access_where_sql() .' ORDER BY n.created DESC', 0, 10)); + $block['content'] = node_title_list(db_query_range('SELECT DISTINCT(n.nid), r.title, n.created FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid '. node_access_join_sql() ." WHERE n.type = 'blog' AND n.status = 1 AND ". node_access_where_sql() .' ORDER BY n.created DESC', 0, 10)); $block['content'] .= ''; $block['subject'] = t('Recent blog posts'); } Index: modules/blogapi.module =================================================================== RCS file: /cvs/drupal/drupal/modules/blogapi.module,v retrieving revision 1.34 diff -u -r1.34 blogapi.module --- modules/blogapi.module 23 Nov 2004 22:20:41 -0000 1.34 +++ modules/blogapi.module 4 Jan 2005 09:15:56 -0000 @@ -361,7 +361,7 @@ return blogapi_error($user); } - $result = db_query_range('SELECT n.nid, n.title,'. ($bodies ? ' n.body,' : '') ." n.created, u.name FROM {node} n, {users} u WHERE n.uid=u.uid AND n.type = 'blog' AND n.uid = %d ORDER BY n.created DESC", $user->uid, 0, $params[3]); + $result = db_query_range('SELECT n.nid, r.title,'. ($bodies ? ' r.body,' : '') ." n.created, u.name FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid INNER JOIN {users} u WHERE n.uid = u.uid AND n.type = 'blog' AND n.uid = %d ORDER BY n.created DESC", $user->uid, 0, $params[3]); while ($blog = db_fetch_object($result)) { $xmlrpcval = _blogapi_get_post($blog, $bodies); $blogs[] = $xmlrpcval; Index: modules/book.module =================================================================== RCS file: /cvs/drupal/drupal/modules/book.module,v retrieving revision 1.276 diff -u -r1.276 book.module --- modules/book.module 22 Dec 2004 20:47:47 -0000 1.276 +++ modules/book.module 4 Jan 2005 09:15:56 -0000 @@ -106,7 +106,7 @@ // We don't want to cache these menu items because they could change whenever // a book page or outline node is edited. if (arg(0) == 'admin' && arg(1) == 'node' && arg(2) == 'book') { - $result = db_query('SELECT n.nid, n.title FROM {node} n '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE '. node_access_where_sql() .' AND b.parent = 0 ORDER BY b.weight, n.title'); + $result = db_query('SELECT n.nid, r.title FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE '. node_access_where_sql() .' AND b.parent = 0 ORDER BY b.weight, r.title'); while ($book = db_fetch_object($result)) { $items[] = array('path' => 'admin/node/book/'. $book->nid, 'title' => t('"%title" book', array('%title' => $book->title))); } @@ -131,7 +131,7 @@ else if ($op == 'view') { // Only display this block when the user is browsing a book: if (arg(0) == 'node' && is_numeric(arg(1))) { - $result = db_query('SELECT n.nid, n.title, b.parent FROM {node} n '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE '. node_access_where_sql() .' AND n.nid = %d', arg(1)); + $result = db_query('SELECT n.nid, r.title, b.parent FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE '. node_access_where_sql() .' AND n.nid = %d', arg(1)); if (db_num_rows($result) > 0) { $node = db_fetch_object($result); @@ -320,7 +320,7 @@ * Return the path (call stack) to a certain book page. */ function book_location($node, $nodes = array()) { - $parent = db_fetch_object(db_query('SELECT n.nid, n.title, b.parent, b.weight FROM {node} n '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE '. node_access_where_sql() .' AND n.nid = %d', $node->parent)); + $parent = db_fetch_object(db_query('SELECT n.nid, r.title, b.parent, b.weight FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE '. node_access_where_sql() .' AND n.nid = %d', $node->parent)); if ($parent->title) { $nodes = book_location($parent, $nodes); array_push($nodes, $parent); @@ -329,7 +329,7 @@ } function book_location_down($node, $nodes = array()) { - $last_direct_child = db_fetch_object(db_query('SELECT n.nid, n.title, b.parent, b.weight FROM {node} n '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE '. node_access_where_sql() .' AND b.parent = %d ORDER BY b.weight DESC, n.title DESC', $node->nid)); + $last_direct_child = db_fetch_object(db_query('SELECT n.nid, r.title, b.parent, b.weight FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE '. node_access_where_sql() .' AND b.parent = %d ORDER BY b.weight DESC, r.title DESC', $node->nid)); if ($last_direct_child) { array_push($nodes, $last_direct_child); $nodes = book_location_down($last_direct_child, $nodes); @@ -347,7 +347,7 @@ } // Previous on the same level: - $direct_above = db_fetch_object(db_query('SELECT n.nid, n.title FROM {node} n '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE '. node_access_where_sql() ." AND b.parent = %d AND n.status = 1 AND n.moderate = 0 AND (b.weight < %d OR (b.weight = %d AND n.title < '%s')) ORDER BY b.weight DESC, n.title DESC", $node->parent, $node->weight, $node->weight, $node->title)); + $direct_above = db_fetch_object(db_query('SELECT n.nid, r.title FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE '. node_access_where_sql() ." AND b.parent = %d AND n.status = 1 AND n.moderate = 0 AND (b.weight < %d OR (b.weight = %d AND r.title < '%s')) ORDER BY b.weight DESC, r.title DESC", $node->parent, $node->weight, $node->weight, $node->title)); if ($direct_above) { // Get last leaf of $above. $path = book_location_down($direct_above); @@ -356,7 +356,7 @@ } else { // Direct parent: - $prev = db_fetch_object(db_query('SELECT n.nid, n.title FROM {node} n '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE '. node_access_where_sql() ." AND n.nid = %d AND n.status = 1 AND n.moderate = 0", $node->parent)); + $prev = db_fetch_object(db_query('SELECT n.nid, r.title FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE '. node_access_where_sql() ." AND n.nid = %d AND n.status = 1 AND n.moderate = 0", $node->parent)); return $prev; } } @@ -366,7 +366,7 @@ */ function book_next($node) { // get first direct child - $child = db_fetch_object(db_query("SELECT DISTINCT(n.nid), n.title, b.weight FROM {node} n ". node_access_join_sql() ." INNER JOIN {book} b ON n.nid = b.nid WHERE b.parent = %d AND n.status = 1 AND ". node_access_where_sql() ." AND n.moderate = 0 ORDER BY b.weight ASC, n.title ASC", $node->nid)); + $child = db_fetch_object(db_query("SELECT DISTINCT(n.nid), r.title, b.weight FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid ". node_access_join_sql() ." INNER JOIN {book} b ON n.nid = b.nid WHERE b.parent = %d AND n.status = 1 AND ". node_access_where_sql() ." AND n.moderate = 0 ORDER BY b.weight ASC, r.title ASC", $node->nid)); if ($child) { return $child; } @@ -375,7 +375,7 @@ array_push($path = book_location($node), $node); // Path to top-level node including this one. while (($leaf = array_pop($path)) && count($path)) { - $next = db_fetch_object(db_query("SELECT DISTINCT(n.nid), n.title, b.weight FROM {node} n ". node_access_join_sql() ." INNER JOIN {book} b ON n.nid = b.nid WHERE b.parent = %d AND n.status = 1 AND ". node_access_where_sql() ." AND n.moderate = 0 AND (b.weight > %d OR (b.weight = %d AND n.title > '%s')) ORDER BY b.weight ASC, n.title ASC", $leaf->parent, $leaf->weight, $leaf->weight, $leaf->title)); + $next = db_fetch_object(db_query("SELECT DISTINCT(n.nid), r.title, b.weight FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid ". node_access_join_sql() ." INNER JOIN {book} b ON n.nid = b.nid WHERE b.parent = %d AND n.status = 1 AND ". node_access_where_sql() ." AND n.moderate = 0 AND (b.weight > %d OR (b.weight = %d AND r.title > '%s')) ORDER BY b.weight ASC, r.title ASC", $leaf->parent, $leaf->weight, $leaf->weight, $leaf->title)); if ($next) { return $next; } @@ -513,7 +513,7 @@ } function book_toc($exclude = 0) { - $result = db_query('SELECT DISTINCT(n.nid), n.title, b.parent, b.weight FROM {node} n '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE n.status = 1 AND '. node_access_where_sql() .' ORDER BY b.weight, n.title'); + $result = db_query('SELECT DISTINCT(n.nid), r.title, b.parent, b.weight FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE n.status = 1 AND '. node_access_where_sql() .' ORDER BY b.weight, r.title'); while ($node = db_fetch_object($result)) { if (!$children[$node->parent]) { @@ -566,7 +566,7 @@ } function book_tree($parent = 0, $depth = 3, $unfold = array()) { - $result = db_query('SELECT DISTINCT(n.nid), n.title, b.parent, b.weight FROM {node} n '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE n.status = 1 AND '. node_access_where_sql() .' AND n.moderate = 0 ORDER BY b.weight, n.title'); + $result = db_query('SELECT DISTINCT(n.nid), r.title, b.parent, b.weight FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE n.status = 1 AND '. node_access_where_sql() .' AND n.moderate = 0 ORDER BY b.weight, r.title'); while ($node = db_fetch_object($result)) { $list = $children[$node->parent] ? $children[$node->parent] : array(); @@ -583,7 +583,7 @@ * Menu callback; prints a listing of all books. */ function book_render() { - $result = db_query('SELECT n.nid FROM {node} n '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE '. node_access_where_sql() .' AND b.parent = 0 AND n.status = 1 AND n.moderate = 0 ORDER BY b.weight, n.title'); + $result = db_query('SELECT n.nid, r.title FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE '. node_access_where_sql() .' AND b.parent = 0 AND n.status = 1 AND n.moderate = 0 ORDER BY b.weight, r.title'); while ($page = db_fetch_object($result)) { // Load the node: @@ -609,7 +609,7 @@ */ function book_print($nid = 0, $depth = 1) { global $base_url; - $result = db_query('SELECT DISTINCT(n.nid), n.title, b.weight FROM {node} n '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE n.status = 1 AND '. node_access_where_sql() .' AND n.nid = %d AND n.moderate = 0 ORDER BY b.weight, n.title', $nid); + $result = db_query('SELECT DISTINCT(n.nid), r.title, b.weight FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE n.status = 1 AND '. node_access_where_sql() .' AND n.nid = %d AND n.moderate = 0 ORDER BY b.weight, r.title', $nid); while ($page = db_fetch_object($result)) { // load the node: @@ -639,7 +639,7 @@ } function book_print_recurse($parent = '', $depth = 1) { - $result = db_query("SELECT DISTINCT(n.nid), n.title, b.weight FROM {node} n ". node_access_join_sql() ." INNER JOIN {book} b ON n.nid = b.nid WHERE n.status = 1 AND ". node_access_where_sql() ." AND b.parent = '$parent' AND n.moderate = 0 ORDER BY b.weight, n.title"); + $result = db_query("SELECT DISTINCT(n.nid), r.title, b.weight FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid ". node_access_join_sql() ." INNER JOIN {book} b ON n.nid = b.nid WHERE n.status = 1 AND ". node_access_where_sql() ." AND b.parent = '$parent' AND n.moderate = 0 ORDER BY b.weight, r.title"); while ($page = db_fetch_object($result)) { // Load the node: @@ -673,7 +673,7 @@ } function book_admin_view_book($nid, $depth = 1) { - $result = db_query('SELECT n.nid FROM {node} n '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE '. node_access_where_sql() .' AND b.parent = %d ORDER BY b.weight, n.title', $nid); + $result = db_query('SELECT n.nid, r.title FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE '. node_access_where_sql() .' AND b.parent = %d ORDER BY b.weight, r.title', $nid); while ($node = db_fetch_object($result)) { $node = node_load(array('nid' => $node->nid)); @@ -733,7 +733,7 @@ * Menu callback; displays a listing of all orphaned book pages. */ function book_admin_orphan() { - $result = db_query('SELECT n.nid, n.title, n.status, b.parent FROM {node} n '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE '. node_access_where_sql()); + $result = db_query('SELECT n.nid, r.title, n.status, b.parent FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid '. node_access_join_sql() .' INNER JOIN {book} b ON n.nid = b.nid WHERE '. node_access_where_sql()); while ($page = db_fetch_object($result)) { $pages[$page->nid] = $page; Index: modules/forum.module =================================================================== RCS file: /cvs/drupal/drupal/modules/forum.module,v retrieving revision 1.218 diff -u -r1.218 forum.module --- modules/forum.module 15 Dec 2004 21:19:42 -0000 1.218 +++ modules/forum.module 4 Jan 2005 09:15:56 -0000 @@ -135,9 +135,9 @@ case 'view': if (user_access('access content')) { - $content = node_title_list(db_query_range("SELECT DISTINCT(n.nid), n.title, l.last_comment_timestamp, l.comment_count FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid ". node_access_join_sql() ." WHERE n.status = 1 AND n.type='forum' AND ". node_access_where_sql() ." ORDER BY l.last_comment_timestamp DESC", 0, variable_get('forum_block_num', '5')), t('Active forum topics:')); + $content = node_title_list(db_query_range("SELECT DISTINCT(n.nid), r.title, l.last_comment_timestamp, l.comment_count FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid INNER JOIN {node_comment_statistics} l ON n.nid = l.nid ". node_access_join_sql() ." WHERE n.status = 1 AND n.type = 'forum' AND ". node_access_where_sql() ." ORDER BY l.last_comment_timestamp DESC", 0, variable_get('forum_block_num', '5')), t('Active forum topics:')); - $content .= node_title_list(db_query_range("SELECT DISTINCT(n.nid), n.title, l.comment_count FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid ". node_access_join_sql() ." WHERE n.type = 'forum' AND n.status = 1 AND ". node_access_where_sql() ." ORDER BY n.nid DESC", 0, variable_get('forum_block_num', '5')), t('New forum topics:')); + $content .= node_title_list(db_query_range("SELECT DISTINCT(n.nid), r.title, l.comment_count FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid INNER JOIN {node_comment_statistics} l ON n.nid = l.nid ". node_access_join_sql() ." WHERE n.type = 'forum' AND n.status = 1 AND ". node_access_where_sql() ." ORDER BY n.nid DESC", 0, variable_get('forum_block_num', '5')), t('New forum topics:')); if ($content) { $content .= ''; @@ -162,7 +162,7 @@ if (!$main && $type == 'node' && $node->type == 'forum') { // get previous and next topic - $result = db_query("SELECT DISTINCT(n.nid), n.title, n.sticky, l.comment_count, l.last_comment_timestamp FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid " . node_access_join_sql() . " INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d WHERE n.status = 1 AND n.type='forum' AND " . node_access_where_sql() . ' ORDER BY n.sticky DESC, '. _forum_get_topic_order_sql(variable_get('forum_order', 1)), $node->tid); + $result = db_query("SELECT DISTINCT(n.nid), r.title, n.sticky, l.comment_count, l.last_comment_timestamp FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid INNER JOIN {node_comment_statistics} l ON n.nid = l.nid " . node_access_join_sql() . " INNER JOIN {term_node} t ON n.nid = r.nid AND t.tid = %d WHERE n.status = 1 AND n.type = 'forum' AND " . node_access_where_sql() . ' ORDER BY n.sticky DESC, '. _forum_get_topic_order_sql(variable_get('forum_order', 1)), $node->tid); while ($topic = db_fetch_object($result)) { if ($stop == 1) { @@ -404,7 +404,7 @@ $forum_topic_list_header = array( array('data' => ' '), - array('data' => t('Topic'), 'field' => 'n.title'), + array('data' => t('Topic'), 'field' => 'nr.title'), array('data' => t('Replies'), 'field' => 'l.comment_count'), array('data' => t('Created'), 'field' => 'n.created'), array('data' => t('Last reply'), 'field' => 'l.last_comment_timestamp'), @@ -420,7 +420,7 @@ $term = taxonomy_get_term($tid); $check_tid = $tid ? "'". db_escape_string($tid) ."'" : 'NULL'; - $sql = "SELECT DISTINCT(n.nid), f.tid, n.title, n.sticky, u.name, u.uid, n.created AS timestamp, n.comment AS comment_mode, l.last_comment_timestamp, IF(l.last_comment_uid, cu.name, l.last_comment_name) as last_comment_name, l.last_comment_uid, l.comment_count AS num_comments FROM {node} n ". node_access_join_sql() .", {node_comment_statistics} l, {users} cu, {term_node} r, {users} u, {forum} f WHERE n.status = 1 AND l.last_comment_uid = cu.uid AND n.nid = l.nid AND n.nid = r.nid AND r.tid = $check_tid AND n.uid = u.uid AND n.nid = f.nid AND ". node_access_where_sql(); + $sql = "SELECT DISTINCT(n.nid), f.tid, nr.title, n.sticky, u.name, u.uid, n.created AS timestamp, n.comment AS comment_mode, l.last_comment_timestamp, IF(l.last_comment_uid, cu.name, l.last_comment_name) as last_comment_name, l.last_comment_uid, l.comment_count AS num_comments FROM {node} n INNER JOIN {node_revisions} nr ON n.vid = nr.vid ". node_access_join_sql() .", {node_comment_statistics} l, {users} cu, {term_node} r, {users} u, {forum} f WHERE n.status = 1 AND l.last_comment_uid = cu.uid AND n.nid = l.nid AND n.nid = r.nid AND r.tid = $check_tid AND n.uid = u.uid AND n.nid = f.nid AND ". node_access_where_sql(); $sql .= tablesort_sql($forum_topic_list_header, 'n.sticky DESC,'); $sql_count = "SELECT COUNT(DISTINCT(n.nid)) FROM {node} n ". node_access_join_sql() ." INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = $check_tid WHERE n.status = 1 AND n.type = 'forum' AND ". node_access_where_sql(); Index: modules/node.module =================================================================== RCS file: /cvs/drupal/drupal/modules/node.module,v retrieving revision 1.436 diff -u -r1.436 node.module --- modules/node.module 31 Dec 2004 09:30:12 -0000 1.436 +++ modules/node.module 4 Jan 2005 09:15:56 -0000 @@ -383,31 +383,27 @@ } // Retrieve the node. - $node = db_fetch_object(db_query('SELECT n.*, u.uid, u.name, u.picture, u.data FROM {node} n INNER JOIN {users} u ON u.uid = n.uid WHERE '. implode(' AND ', $cond))); - $node = drupal_unpack($node); - - // Unserialize the revisions and user data fields. - if ($node->revisions) { - $node->revisions = unserialize($node->revisions); + if ($revision) { + $node = db_fetch_object(db_query('SELECT n.*, r.timestamp AS revision_timestamp, r.title, r.body, r.teaser, r.format, u.uid, u.name, u.picture, u.data FROM {node} n INNER JOIN {users} u ON u.uid = n.uid INNER JOIN {node_revisions} r ON r.nid = n.nid AND r.vid = %d WHERE '. implode(' AND ', $cond), $revision)); } - - // Call the node specific callback (if any) and piggy-back the - // results to the node or overwrite some values. - if ($extra = node_invoke($node, 'load')) { - foreach ($extra as $key => $value) { - $node->$key = $value; - } + else { + $node = db_fetch_object(db_query('SELECT n.*, r.timestamp AS revision_timestamp, r.title, r.body, r.teaser, r.format, u.uid, u.name, u.picture, u.data FROM {node} n INNER JOIN {users} u ON u.uid = n.uid INNER JOIN {node_revisions} r ON r.vid = n.vid WHERE '. implode(' AND ', $cond))); } - if ($extra = node_invoke_nodeapi($node, 'load')) { - foreach ($extra as $key => $value) { - $node->$key = $value; + if ($node->nid) { + // Call the node specific callback (if any) and piggy-back the + // results to the node or overwrite some values. + if ($extra = node_invoke($node, 'load')) { + foreach ($extra as $key => $value) { + $node->$key = $value; + } + } + + if ($extra = node_invoke_nodeapi($node, 'load')) { + foreach ($extra as $key => $value) { + $node->$key = $value; + } } - } - - // Return the desired revision. - if (!is_null($revision) && is_array($node->revisions[$revision])) { - $node = $node->revisions[$revision]['node']; } if ($cachable) { @@ -421,65 +417,98 @@ * Save a node object into the database. */ function node_save($node) { - // Fetch fields to save to node table: - $fields = node_invoke_nodeapi($node, 'fields'); + global $user; - // Serialize the revisions field: - if ($node->revisions) { - $node->revisions = serialize($node->revisions); - } + $newnode = false; // Apply filters to some default node fields: if (empty($node->nid)) { // Insert a new node. + $newnode = true; // Set some required fields: if (!$node->created) { $node->created = time(); } - if (!$node->changed) { - $node->changed = time(); - } $node->nid = db_next_id('{node}_nid'); + $node->vid = db_next_id('{node_revisions}_vid');; + } + else { + // We need to ensure that all node fields are filled. + $node_current = node_load(array('nid' => $node->nid)); + foreach ($node as $field => $data) { + $node_current->$field = $data; + } + $node = $node_current; - // Prepare the query: - foreach ($node as $key => $value) { - if (in_array((string) $key, $fields)) { - $k[] = db_escape_string($key); - $v[] = $value; - $s[] = "'%s'"; - } + if ($node->revision) { + $node->vid = db_next_id('{node_revisions}_vid');; } + } - $keysfmt = implode(', ', $s); - // We need to quote the placeholders for the values. - $valsfmt = "'". implode("', '", $s) ."'"; - // Insert the node into the database: - db_query("INSERT INTO {node} (". implode(", ", $k) .") VALUES(". implode(", ", $s) .")", $v); + if (!$node->changed) { + $node->changed = time(); + } - // Call the node specific callback (if any): - node_invoke($node, 'insert'); - node_invoke_nodeapi($node, 'insert'); + // Split off revisions data to another structure + $revisions_table_values = array('nid' => $node->nid, 'vid' => $node->vid, + 'title' => $node->title, 'body' => $node->body, + 'teaser' => $node->teaser, 'changed' => $node->changed, + 'uid' => $user->uid, 'format' => $node->format); + $revisions_table_types = array('nid' => '%d', 'vid' => '%d', + 'title' => "'%s'", 'body' => "'%s'", + 'teaser' => "'%s'", 'changed' => '%d', + 'uid' => '%d', 'format' => '%d'); + $node_table_values = array('nid' => $node->nid, 'vid' => $node->vid, + 'type' => $node->type, 'uid' => $node->uid, + 'status' => $node->status, 'created' => $node->created, + 'changed' => $node->changed, 'comment' => $node->comment, + 'promote' => $node->promote, 'moderate' => $node->moderate, + 'sticky' => $node->sticky); + $node_table_types = array('nid' => '%d', 'vid' => '%d', + 'type' => "'%s'", 'uid' => '%d', + 'status' => '%d', 'created' => '%d', + 'changed' => '%d', 'comment' => '%d', + 'promote' => '%d', 'moderate' => '%d', + 'sticky' => '%d'); + + //Generate the node table query + if ($newnode) { + $node_query = 'INSERT INTO {node} ('. implode(', ', array_keys($node_table_types)) .') VALUES ('. implode(', ', $node_table_types) .')'; + $revisions_query = 'INSERT INTO {node_revisions} ('. implode(', ', array_keys($revisions_table_types)) .') VALUES ('. implode(', ', array_keys($revisions_table_types)) .')'; } else { - // Update an existing node. - - // Set some required fields: - $node->changed = time(); - - // Prepare the query: - foreach ($node as $key => $value) { - if (in_array($key, $fields)) { - $q[] = db_escape_string($key) ." = '%s'"; - $v[] = $value; + $arr = array(); + foreach ($node_table_types as $key => $value) { + $arr[] = $key .' = '. $value; + } + $node_query = 'UPDATE {node} SET '. implode(', ', $arr) ." WHERE nid = $node->nid"; + if ($node->revision) { + $revisions_query = 'INSERT INTO {node_revisions} ('. implode(', ', array_keys($revisions_table_types)) .') VALUES ('. implode(', ', array_keys($revisions_table_types)) .')'; + } + else { + foreach ($revisions_table_types as $key => $value) { + $arr[] = $key .' = '. $value; } + $revisions_query = 'UPDATE {node_revisions} SET '. implode(', ', $arr) ." WHERE vid = $node->vid"; } + } - // Update the node in the database: - db_query("UPDATE {node} SET ". implode(', ', $q) ." WHERE nid = '$node->nid'", $v); + //Generate the node_revisions table query - // Call the node specific callback (if any): + // Insert the node into the database: + db_begin_transaction(array('node', 'node_revisions', 'watchdog', 'sessions')); + db_query($node_query, $node_table_values); + db_query($revisions_query, $revisions_table_values); + db_commit_transaction(); + + // Call the node specific callback (if any): + if ($newnode) { + node_invoke($node, 'insert'); + node_invoke_nodeapi($node, 'insert'); + } + else { node_invoke($node, 'update'); node_invoke_nodeapi($node, 'update'); } @@ -695,7 +724,7 @@ 'weight' => 1, 'type' => MENU_LOCAL_TASK); - if ($node->revisions) { + if ($node->vid) { $items[] = array('path' => 'node/'. arg(1) .'/revisions', 'title' => t('revisions'), 'callback' => 'node_page', 'access' => user_access('administer nodes'), @@ -767,7 +796,7 @@ $output .= "
$form
"; // Render operations form: - $result = pager_query('SELECT n.*, u.name, u.uid FROM {node} n INNER JOIN {users} u ON n.uid = u.uid '. $filters[$filter][1], 50); + $result = pager_query('SELECT n.*, r.title, r.body, r.teaser, r.format, r.timestamp, u.name, u.uid FROM {node} n INNER JOIN {node_revisions} r ON r.nid = n.nid AND r.vid = n.vid INNER JOIN {users} u ON n.uid = u.uid '. $filters[$filter][1], 50); // Make sure the update controls are disabled if we don't have any rows to select from. $disabled = !db_num_rows($result); @@ -854,11 +883,18 @@ drupal_set_title($node->title); - if ($node->revisions) { + if ($node->vid) { $header = array(t('Older revisions'), array('colspan' => '3', 'data' => t('Operations'))); - foreach ($node->revisions as $key => $revision) { - $rows[] = array(t('revision #%r revised by %u on %d', array('%r' => $key, '%u' => format_name(user_load(array('uid' => $revision['uid']))), '%d' => format_date($revision['timestamp'], 'small'))) . ($revision['history'] ? '
'. $revision['history'] .'' : ''), l(t('view'), "node/$node->nid", array(), "revision=$key"), l(t('rollback'), "node/$node->nid/rollback-revision/$key"), l(t('delete'), "node/$node->nid/delete-revision/$key")); + $revisions = node_revision_list($node); + + $i = 0; + foreach ($revisions as $key => $revision) { + $rows[] = array( + t('revision #%r revised by %u on %d', array('%r' => ++$i, '%u' => format_name($revision), '%d' => format_date($revision->timestamp, 'small'))) . ($revision->title ? '
'. $revision->title .'' : ''), + l(t('view'), "node/$node->nid/revision/". $revision->vid), + l(t('rollback'), "node/$node->nid/rollback-revision/". $revision->vid), + l(t('delete'), "node/$node->nid/delete-revision/". $revision->vid)); } $output .= theme('table', $header, $rows); } @@ -867,32 +903,6 @@ return $output; } - -/** - * Return the revision with the specified revision number. - */ -function node_revision_load($node, $revision) { - return $node->revisions[$revision]['node']; -} - -/** - * Create and return a new revision of the given node. - */ -function node_revision_create($node) { - global $user; - - // "Revision" is the name of the field used to indicate that we have to - // create a new revision of a node. - if ($node->nid && $node->revision) { - $prev = node_load(array('nid' => $node->nid)); - $node->revisions = $prev->revisions; - unset($prev->revisions); - $node->revisions[] = array('uid' => $user->uid, 'timestamp' => time(), 'node' => $prev, 'history' => $node->history); - } - - return $node; -} - /** * Roll back to the revision with the specified revision number. */ @@ -900,29 +910,13 @@ global $user; if (user_access('administer nodes')) { - $node = node_load(array('nid' => $nid)); - - // Extract the specified revision: - $rev = $node->revisions[$revision]['node']; - - // Inherit all the past revisions: - $rev->revisions = $node->revisions; - // Save the original/current node: - $rev->revisions[] = array('uid' => $user->uid, 'timestamp' => time(), 'node' => $node); + $node = node_load(array('nid' => $nid), $revision); - // Remove the specified revision: - unset($rev->revisions[$revision]); + node_save($node); - // Save the node: - foreach ($node as $key => $value) { - $filter[] = $key; - } - - node_save($rev, $filter); - - drupal_set_message(t('Rolled back to revision %revision of %title', array('%revision' => "#$revision", '%title' => "$node->title"))); - drupal_goto('node/'. $nid .'/revisions'); + drupal_set_message(t('Rolled back to the revision with the ID %revision of %title', array('%revision' => "$revision", '%title' => "$node->title"))); + drupal_goto("node/$nid/revisions"); } } @@ -931,14 +925,22 @@ */ function node_revision_delete($nid, $revision) { if (user_access('administer nodes')) { + // Start a transaction so that the current node and node count + // don't change between the time we load it from the database and + // delete the node + db_begin_transaction(array('node', 'node_revisions', 'watchdog', 'sessions')); $node = node_load(array('nid' => $nid)); + $revisions = node_revision_list($node); - unset($node->revisions[$revision]); + // Don't delete the last revision of the node or the current revision + if ((count($revisions) > 0) && ($revision != $node->vid)) { + db_query("DELETE FROM {node_revisions} WHERE nid = %d AND vid = %d", array($nid, $revision)); + } - node_save($node, array('nid', 'revisions')); + db_commit_transaction(); - drupal_set_message(t('Deleted revision %revision of %title', array('%revision' => "#$revision", '%title' => "$node->title"))); - drupal_goto('node/'. $nid . (count($node->revisions) ? '/revisions' : '')); + drupal_set_message(t('Deleted revision with the ID %revision of %title', array('%revision' => "$revision", '%title' => "$node->title"))); + drupal_goto("node/$nid/revisions"); } } @@ -946,12 +948,13 @@ * Return a list of all the existing revision numbers. */ function node_revision_list($node) { - if (is_array($node->revisions)) { - return array_keys($node->revisions); - } - else { - return array(); + $revisions = array(); + $result = db_query('SELECT r.vid, r.title, r.uid, r.timestamp, u.name FROM {node_revisions} r INNER JOIN {users} u ON u.uid = r.uid WHERE r.nid = %d AND r.vid != %d ORDER BY r.timestamp ASC', $node->nid, $node->vid); + while ($revision = db_fetch_object($result)) { + $revisions[] = $revision; } + + return $revisions; } /** @@ -1076,9 +1079,6 @@ $node->teaser = node_teaser($node->body); } - // Create a new revision when required. - $node = node_revision_create($node); - if (user_access('administer nodes')) { // Set up default values, if required. if (!$node->created) { @@ -1429,8 +1429,12 @@ if (node_access('delete', $node)) { if ($edit['confirm']) { - // Delete the specified node: + // Delete the specified node: + // We don't need to lock this in a transaction because once the + // entry in node disappears, the entry in node_revisions won't be + // accessable db_query('DELETE FROM {node} WHERE nid = %d', $node->nid); + db_query('DELETE FROM {node_revisions} WHERE nid = %d', $node->nid); // Call the node-specific callback (if any): node_invoke($node, 'delete'); @@ -1516,9 +1520,21 @@ case 'delete-revision': node_revision_delete(arg(1), arg(3)); break; + case 'revision': + if (is_numeric(arg(1)) && is_numeric(arg(3))) { + $node = node_load(array('nid' => arg(1)), arg(3)); + if ($node->nid) { + drupal_set_title($node->title); + print theme('page', node_show($node, arg(2))); + } + else { + drupal_not_found(); + } + } + break; case 'view': if (is_numeric(arg(1))) { - $node = node_load(array('nid' => arg(1)), $_GET['revision']); + $node = node_load(array('nid' => arg(1))); if ($node->nid) { drupal_set_title($node->title); print theme('page', node_show($node, arg(2))); @@ -1611,8 +1627,6 @@ $output[t('sticky')] = form_checkbox('', "node_sticky_$node->type", 1, variable_get("node_sticky_$node->type", 0)); $output[t('revision')] = form_checkbox('', "node_revision_$node->type", 1, variable_get("node_revision_$node->type", 0)); return $output; - case 'fields': - return array('nid', 'uid', 'type', 'title', 'teaser', 'body', 'revisions', 'status', 'promote', 'moderate', 'sticky', 'created', 'changed', 'format'); } } Index: modules/poll.module =================================================================== RCS file: /cvs/drupal/drupal/modules/poll.module,v retrieving revision 1.152 diff -u -r1.152 poll.module --- modules/poll.module 15 Dec 2004 21:19:42 -0000 1.152 +++ modules/poll.module 4 Jan 2005 09:15:56 -0000 @@ -264,7 +264,7 @@ function poll_page() { // List all polls - $result = pager_query("SELECT DISTINCT(n.nid), n.title, p.active, n.created, SUM(c.chvotes) AS votes FROM {node} n ". node_access_join_sql() ." INNER JOIN {poll} p ON n.nid=p.nid INNER JOIN {poll_choices} c ON n.nid=c.nid WHERE type = 'poll' AND status = 1 AND ". node_access_where_sql() ." AND moderate = 0 GROUP BY n.nid, n.title, p.active, n.created ORDER BY n.created DESC", 15); + $result = pager_query("SELECT DISTINCT(n.nid), r.title, p.active, n.created, SUM(c.chvotes) AS votes FROM {node} n INNER JOIN {node_revisions} r ON n.vid = r.vid ". node_access_join_sql() ." INNER JOIN {poll} p ON n.nid = p.nid INNER JOIN {poll_choices} c ON n.nid = c.nid WHERE type = 'poll' AND n.status = 1 AND ". node_access_where_sql() ." AND n.moderate = 0 GROUP BY n.nid, r.title, p.active, n.created ORDER BY n.created DESC", 15); $output = '