diff -ur -ubB trac/trac/ticket/api.py trac/trac/ticket/api.py
--- trac/trac/ticket/api.py	2009-09-15 08:47:38.000000000 -0500
+++ trac/trac/ticket/api.py	2009-09-15 09:35:44.000000000 -0500
@@ -694,7 +695,7 @@
     def get_tickets_for_iteration(self, db, iteration_tickets):
         from trac.ticket import Ticket
         tickets = []
-        for ticket_id in iteration_tickets.split():
+        for ticket_id in iteration_tickets:
             ticket = Ticket(self.env, ticket_id, db)
             if ticket.exists:
                tickets.append(ticket)
@@ -710,8 +711,11 @@
         
         cursor = db.cursor()
         cursor.execute("SELECT ticket,time,author,field,oldvalue,newvalue "
-                       "FROM ticket_change WHERE time > %s AND time < %s "
-                       "ORDER BY ticket", (start_date, end_date))
+                       "FROM ticket_change, iteration_ticket "
+                       "WHERE time > %s AND time < %s "
+                       "AND ticket_change.ticket = iteration_ticket.ticket_id "
+                       "AND iteration_ticket.iteration_id = %s "
+                       "ORDER BY ticket", (start_date, end_date, iteration.id))
         
         for ticket, time, author, field, oldvalue, newvalue in cursor:
             if not changed_tickets.has_key( ticket ):
diff -ur -ubB trac/trac/ticket/model.py trac/trac/ticket/model.py
--- trac/trac/ticket/model.py	2009-09-15 08:47:38.000000000 -0500
+++ trac/trac/ticket/model.py	2009-09-15 09:37:52.000000000 -0500
@@ -981,7 +981,8 @@
             self._old_id = iteration_id
         else:
             self.id = self._old_id = self.start_date = self.end_date = None
-            self.summary = self.tickets = ''
+            self.summary = ''
+            self.tickets = []
 
     def _get_resource(self):
         return Resource('iteration', self.id) ### .version !!!
@@ -991,18 +992,20 @@
         if not db:
             db = self.env.get_db_cnx()
         cursor = db.cursor()
-        cursor.execute("SELECT id,summary,start_date,end_date,tickets "
+        cursor.execute("SELECT id,summary,start_date,end_date "
                        "FROM iteration WHERE id=%s", (iteration_id,))
         row = cursor.fetchone()
         if not row:
             raise ResourceNotFound('Iteration %s does not exist.' % iteration_id,
                                    'Invalid Iteration Id')
-        id, summary, start_date, end_date, tickets = row
+        id, summary, start_date, end_date = row
         self.id = id
         self.summary = summary or ''
         self.start_date = start_date and datetime.fromtimestamp(int(start_date), utc) or None
         self.end_date = end_date and datetime.fromtimestamp(int(end_date), utc) or None
-        self.tickets = tickets or ''
+        cursor.execute("SELECT ticket_id FROM iteration_ticket "
+                       "WHERE iteration_id=%s ORDER BY ticket_id", (iteration_id,))
+        self.tickets = [str(row[0]) for row in cursor.fetchall()]
 
     is_started = property(fget=lambda self: self.start_date and \
                                             self.start_date.date() < date.today())
@@ -1024,6 +1027,7 @@
         cursor = db.cursor()
         self.env.log.info('Deleting iteration %s' % self.id)
         cursor.execute("DELETE FROM iteration WHERE id=%s", (self.id,))
+        cursor.execute("DELETE FROM iteration_ticket WHERE iteration_id=%s" % (self.id,))
 
         if handle_ta:
             db.commit()
@@ -1037,11 +1041,14 @@
             handle_ta = False
 
         cursor = db.cursor()
-        cursor.execute("INSERT INTO iteration (summary,start_date,end_date,tickets) "
-                       "VALUES (%s,%s,%s,%s)",
-                       (self.summary, to_timestamp(self.start_date), to_timestamp(self.end_date),
-                        self.tickets))
+        cursor.execute("INSERT INTO iteration (summary,start_date,end_date) "
+                       "VALUES (%s,%s,%s)",
+                       (self.summary, to_timestamp(self.start_date), to_timestamp(self.end_date)))
         iteration_id = db.get_last_id(cursor, 'iteration')
+        for ticket_id in self.tickets:
+            cursor.execute("INSERT INTO iteration_ticket (iteration_id, ticket_id) "
+                           "VALUES (%s,%s)",
+                           (iteration_id, ticket_id))
 
         if handle_ta:
             db.commit()
@@ -1060,11 +1067,16 @@
         cursor = db.cursor()
         self.env.log.info('Updating iteration "%s"' % self.id)
         cursor.execute("UPDATE iteration SET summary=%s,start_date=%s,"
-                       "end_date=%s,tickets=%s WHERE id=%s",
+                       "end_date=%s WHERE id=%s",
                        (self.summary, to_timestamp(self.start_date), to_timestamp(self.end_date),
-                        self.tickets,
                         self.id))
 
+        cursor.execute("DELETE FROM iteration_ticket WHERE iteration_id=%s" % (self.id,))
+        for ticket_id in self.tickets:
+            cursor.execute("INSERT INTO iteration_ticket (iteration_id, ticket_id) "
+                           "VALUES (%s,%s)",
+                           (self.id, ticket_id))
+
         if handle_ta:
             db.commit()
 
@@ -1072,19 +1084,22 @@
         if not db:
             db = env.get_db_cnx()
         now = to_timestamp( datetime.now(utc) - timedelta(days=1) )
-        sql = "SELECT id,summary,start_date,end_date,tickets FROM iteration "
+        sql = "SELECT id,summary,start_date,end_date FROM iteration "
         if not include_finished:
             sql += "WHERE end_date >= %s " % now
         cursor = db.cursor()
         cursor.execute(sql)
+        iteration_rows = cursor.fetchall()
         iterations = []
-        for iteration_id,summary,start_date,end_date,tickets in cursor:
+        for iteration_id,summary,start_date,end_date in iteration_rows:
             iteration = Iteration(env)
             iteration.id = iteration._old_id = iteration_id
             iteration.summary = summary or ''
             iteration.start_date = start_date and datetime.fromtimestamp(int(start_date), utc) or None
             iteration.end_date = end_date and datetime.fromtimestamp(int(end_date), utc) or None
-            iteration.tickets = tickets or ''
+            cursor.execute("SELECT ticket_id FROM iteration_ticket "
+                                  "WHERE iteration_id = %d ORDER BY ticket_id" % (iteration_id,))
+            iteration.tickets = [str(row[0]) for row in cursor.fetchall()]
             iterations.append(iteration)
         def iteration_order(m):
             return (m.start_date or utcmax,
Index: trac/trac/ticket/roadmap.py
===================================================================
--- trac/trac/ticket/roadmap.py	(revision 306)
+++ trac/trac/ticket/roadmap.py	(working copy)
@@ -525,8 +525,7 @@
                     ticket_tokens.append(id_token)
             except ValueError:
                 invalid_ticket_tokens.append(id_token)
-        iteration_tickets = " ".join(ticket_tokens)
-        iteration.tickets = iteration_tickets
+        iteration.tickets = ticket_tokens
         # Instead of raising one single error, check all the constraints and
         # let the user fix them by going back to edit mode showing the warnings
         warnings = []
diff -ur -ubB trac/trac/ticket/templates/iteration_edit.html trac/trac/ticket/templates/iteration_edit.html
--- trac/trac/ticket/templates/iteration_edit.html	2009-09-15 08:47:38.000000000 -0500
+++ trac/trac/ticket/templates/iteration_edit.html	2009-09-15 08:54:37.000000000 -0500
@@ -212,7 +212,7 @@
           <div class="field">
             <label>List the tickets, by id, associated with this iteration, separated by a space:<br />
             <input type="text" id="iteration_tickets" name="iteration_tickets" size="60"
-                   value="${iteration.tickets}" />
+                   value="${' '.join(iteration.tickets)}" />
             <em>For example: 56 3 4 12</em>
             </label>
           </div>
diff -ur -ubB trac/trac/ticket/query.py trac/trac/ticket/query.py
--- trac/trac/ticket/query.py	2009-09-16 10:10:57.000000000 +0200
+++ trac/trac/ticket/query.py	2009-09-16 12:20:28.000000000 +0200
@@ -404,7 +404,7 @@
 
         sql = []
         sql.append("SELECT " + ",".join(['t.%s AS %s' % (c, c) for c in cols
-                                         if c not in custom_fields]))
+                                         if c not in custom_fields and c != 'iteration']))
         sql.append(",priority.value AS priority_value")
         for k in [k for k in cols if k in custom_fields]:
             sql.append(",%s.value AS %s" % (k, k))
@@ -429,6 +429,17 @@
                        % (col, col, col))
 
         def get_constraint_sql(name, value, mode, neg):
+            if name == "iteration":
+                name = 'iteration_ticket.iteration_id'
+                if mode != '':
+                    raise ValueError("Iteration searching only supports is or is not")
+                clause = "(SELECT DISTINCT ticket_id FROM iteration_ticket WHERE %s = %%s)" % (name,)
+                if neg:
+                    clause = "t.id NOT IN %s" % (clause,)
+                else:
+                    clause = "t.id IN %s" % (clause,)
+                value = int(value)
+                return (clause, value)
             if name not in custom_fields:
                 name = 't.' + name
             else:
@@ -486,14 +497,23 @@
                                                ' OR '.join(id_clauses)))
             # Special case for exact matches on multiple values
             elif not mode and len(v) > 1:
-                if k not in custom_fields:
+                if k == 'iteration':
+                    col = 'iteration_ticket.iteration_id'
+                elif k not in custom_fields:
                     col = 't.' + k
                 else:
                     col = k + '.value'
-                clauses.append("COALESCE(%s,'') %sIN (%s)"
-                               % (col, neg and 'NOT ' or '',
-                                  ','.join(['%s' for val in v])))
-                args += [val[neg:] for val in v]
+                if k == 'iteration':
+                    clause = "%s IN (%s)" % (col, ','.join(['%s' for val in v]))
+                    clause = "(SELECT DISTINCT ticket_id FROM iteration_ticket WHERE %s)" % (clause,)
+                    clause = "t.id %sIN %s" % (neg and 'NOT ' or '', clause)
+                    clauses.append(clause)
+                    args += [int(val[neg:]) for val in v]
+                else:
+                    clauses.append("COALESCE(%s,'') %sIN (%s)"
+                                  % (col, neg and 'NOT ' or '',
+                                     ','.join(['%s' for val in v])))
+                    args += [val[neg:] for val in v]
             elif len(v) > 1:
                 constraint_sql = filter(None,
                                         [get_constraint_sql(k, val, mode, neg)
@@ -585,6 +605,7 @@
         # TODO: remove after adding time/changetime to the api.py
         labels['changetime'] = _('Modified')
         labels['time'] = _('Created')
+        labels['iteration'] = _('Iteration')
 
         headers = [{
             'name': col, 'label': labels.get(col, _('Ticket')),
@@ -602,7 +623,7 @@
             field_data.update(field)
             del field_data['name']
             fields[field['name']] = field_data
-
+        fields['iteration'] = {'label': _('Iteration'), 'type': 'text', 'name': 'iteration'}
         modes = {}
         modes['text'] = [
             {'name': _("contains"), 'value': "~"},
@@ -840,6 +861,7 @@
         ticket_fields = [f['name'] for f in
                          TicketSystem(self.env).get_ticket_fields()]
         ticket_fields.append('id')
+        ticket_fields.append('iteration')
 
         # For clients without JavaScript, we remove constraints here if
         # requested

