Index: database/updates.inc
===================================================================
RCS file: /cvs/drupal/drupal/database/updates.inc,v
retrieving revision 1.193
diff -u -r1.193 updates.inc
--- database/updates.inc	2 Feb 2006 01:27:57 -0000	1.193
+++ database/updates.inc	6 Feb 2006 21:44:24 -0000
@@ -1586,3 +1586,33 @@
   $ret[] = update_sql('DELETE FROM {users_roles} WHERE rid IN ('. DRUPAL_ANONYMOUS_RID. ', '. DRUPAL_AUTHENTICATED_RID. ')');
   return $ret;
 }
+
+function system_update_172() {
+  // Multi-part update
+  if (!isset($_SESSION['system_update_172'])) {
+    $_SESSION['system_update_172'] = 0;
+    $_SESSION['system_update_172_max'] = db_result(db_query('SELECT MAX(cid) FROM comments'));
+  }
+
+  include_once './modules/comment.module';
+
+  $limit = 20;
+  $result = db_query_range("SELECT cid, thread FROM {comments} WHERE cid > %d", $_SESSION['system_update_172'], 0, $limit);
+  while ($comment = db_fetch_object($result)) {
+    $_SESSION['system_update_172'] = $comment->cid;
+    $thread = explode('.', rtrim($comment->thread, '/'));
+    foreach ($thread as $i => $offset) {
+      // Decode old-style comment codes: 1,2,...,9,90,91,92,...,99,990,991,...
+      $thread[$i] = int2vancode((strlen($offset) - 1) * 10 + substr($offset, -1, 1));
+    }
+    $thread = implode('.', $thread) .'/';
+    db_query("UPDATE comments SET thread = '%s' WHERE cid = %d", $thread, $comment->cid);
+  }
+
+  if ($_SESSION['system_update_172'] == $_SESSION['system_update_172_max']) {
+    unset($_SESSION['system_update_172']);
+    unset($_SESSION['system_update_172_max']);
+    return array();
+  }
+  return array('#finished' => $_SESSION['system_update_172'] / $_SESSION['system_update_172_max']);
+}
Index: modules/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment.module,v
retrieving revision 1.427
diff -u -r1.427 comment.module
--- modules/comment.module	4 Feb 2006 09:54:16 -0000	1.427
+++ modules/comment.module	6 Feb 2006 21:45:11 -0000
@@ -573,25 +573,8 @@
           // Strip the "/" from the end of the thread.
           $max = rtrim($max, '/');
 
-          // Next, we increase this value by one.  Note that we can't
-          // use 1, 2, 3, ... 9, 10, 11 because we order by string and
-          // 10 would be right after 1.  We use 1, 2, 3, ..., 9, 91,
-          // 92, 93, ... instead.  Ugly but fast.
-          $decimals = (string) substr($max, 0, strlen($max) - 1);
-          $units = substr($max, -1, 1);
-          if ($units) {
-            $units++;
-          }
-          else {
-            $units = 1;
-          }
-
-          if ($units == 10) {
-            $units = '90';
-          }
-
           // Finally, build the thread field for this new comment.
-          $thread = $decimals . $units .'/';
+          $thread = int2vancode(vancode2int($max) + 1) .'/';
         }
         else {
           // This is comment with a parent comment: we increase
@@ -608,7 +591,7 @@
 
           if ($max == '') {
             // First child of this parent.
-            $thread = $parent->thread .'.1/';
+            $thread = $parent->thread .'.'. int2vancode(0) .'/';
           }
           else {
             // Strip the "/" at the end of the thread.
@@ -619,19 +602,8 @@
             $parent_depth = count(explode('.', $parent->thread));
             $last = $parts[$parent_depth];
 
-            // Next, we increase this value by one.  Note that we can't
-            // use 1, 2, 3, ... 9, 10, 11 because we order by string and
-            // 10 would be right after 1.  We use 1, 2, 3, ..., 9, 91,
-            // 92, 93, ... instead.  Ugly but fast.
-            $decimals = (string)substr($last, 0, strlen($last) - 1);
-            $units = substr($last, -1, 1);
-            $units++;
-            if ($units == 10) {
-              $units = '90';
-            }
-
             // Finally, build the thread field for this new comment.
-            $thread = $parent->thread .'.'. $decimals . $units .'/';
+            $thread = $parent->thread .'.'. int2vancode(vancode2int($last) + 1) .'/';
           }
         }
 
@@ -1717,3 +1689,54 @@
   }
   return $return;
 }
+
+/**
+ * Generate vancode.
+ *
+ * Consists of a leading character indicating length, followed by N digits
+ * with a numerical value in base 36. Vancodes can be sorted as strings
+ * without messing up numerical order.
+ *
+ * It goes:
+ * 00, 01, 02, ..., 0y, 0z,
+ * 100, 101, 102, ... , 1zy, 1zz,
+ * 2000, 2001, ..., 2zzy, 2zzz,
+ * 30000, ...
+ */
+function int2vancode($i = 0) {
+  $offsets = _vancodes(log($i + 1)/log(36) + 1);
+
+  $last = 0;
+  foreach ($offsets as $length => $offset) {
+    if ($offset > $i) {
+      return chr($length + ord('0') - 1) . str_pad(base_convert($i - $last, 10, 36), $length, '0', STR_PAD_LEFT);
+    }
+    $last = $offset;
+  }
+}
+
+/**
+ * Decode vancode back to an integer.
+ */
+function vancode2int($c = '00') {
+  $len = strlen($c) - 1;
+  $offsets = _vancodes($len);
+  return base_convert(substr($c, 1), 36, 10) + $offsets[$len - 1];
+}
+
+/**
+ * Generate the offsets for Vancode.
+ *
+ * These are the integer value of the codes 00, 100, 2000, 30000, ...
+ */
+function _vancodes($size) {
+  static $offsets = array(0);
+  $count = count($offsets);
+
+  while ($count <= $size) {
+    $offsets[$count] = $offsets[$count - 1] + base_convert('1'. str_repeat('0', $count), 36, 10);
+    $count++;
+  }
+
+  return $offsets;
+}
