Index: setup.py
===================================================================
--- setup.py	(revision 6083)
+++ setup.py	(working copy)
@@ -66,6 +66,7 @@
         trac.db.mysql = trac.db.mysql_backend
         trac.db.postgres = trac.db.postgres_backend
         trac.db.sqlite = trac.db.sqlite_backend
+        trac.db.ingres = trac.db.ingres_backend
         trac.mimeview.enscript = trac.mimeview.enscript
         trac.mimeview.patch = trac.mimeview.patch
         trac.mimeview.php = trac.mimeview.php
Index: trac/db_default.py
===================================================================
--- trac/db_default.py	(revision 6083)
+++ trac/db_default.py	(working copy)
@@ -162,6 +162,7 @@
 ##
 ## Default Reports
 ##
+#Reports for Ingres since ORDER BY in its old form does not work.
 
 def get_reports(db):
     return (
@@ -202,7 +203,7 @@
   FROM ticket t
   LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority'
   WHERE status <> 'closed'
-  ORDER BY (version IS NULL),version, p.value, t.type, time
+  ORDER BY (CASE WHEN version IS NULL THEN 1 ELSE 2 END) DESC,version, p.value, t.type, time
 """),
 #----------------------------------------------------------------------------
 ('Active Tickets by Milestone',
@@ -224,7 +225,7 @@
   FROM ticket t
   LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority'
   WHERE status <> 'closed' 
-  ORDER BY (milestone IS NULL),milestone, p.value, t.type, time
+  ORDER BY (CASE WHEN milestone IS NULL THEN 1 ELSE 2 END) DESC,milestone, p.value, t.type, time
 """ % db.concat("'Milestone '", 'milestone')),
 #----------------------------------------------------------------------------
 ('Accepted, Active Tickets by Owner',
@@ -279,7 +280,7 @@
    time AS _time,reporter AS _reporter
   FROM ticket t
   LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority'
-  ORDER BY (milestone IS NULL), milestone DESC, (status = 'closed'), 
+  ORDER BY (CASE WHEN milestone IS NULL THEN 1 ELSE 2 END) DESC, milestone DESC, (CASE WHEN status = 'closed' THEN 1 ELSE 2 END) DESC, 
         (CASE status WHEN 'closed' THEN modified ELSE (-1)*p.value END) DESC
 """),
 #----------------------------------------------------------------------------
@@ -299,7 +300,7 @@
   FROM ticket t
   LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority'
   WHERE t.status <> 'closed' AND owner = $USER
-  ORDER BY (status = 'accepted') DESC, p.value, milestone, t.type, time
+  ORDER BY (CASE WHEN status = 'accepted' THEN 1 ELSE 2 END), p.value, milestone, t.type, time
 """),
 #----------------------------------------------------------------------------
 ('Active Tickets, Mine first',
@@ -321,7 +322,7 @@
   FROM ticket t
   LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority'
   WHERE status <> 'closed' 
-  ORDER BY (owner = $USER) DESC, p.value, milestone, t.type, time
+  ORDER BY (CASE WHEN owner = $USER THEN 1 ELSE 2 END), p.value, milestone, t.type, time
 """))
 
 
@@ -347,19 +348,19 @@
                 ('2.0', 0))),
            ('enum',
              ('type', 'name', 'value'),
-               (('resolution', 'fixed', 1),
-                ('resolution', 'invalid', 2),
-                ('resolution', 'wontfix', 3),
-                ('resolution', 'duplicate', 4),
-                ('resolution', 'worksforme', 5),
-                ('priority', 'blocker', 1),
-                ('priority', 'critical', 2),
-                ('priority', 'major', 3),
-                ('priority', 'minor', 4),
-                ('priority', 'trivial', 5),
-                ('ticket_type', 'defect', 1),
-                ('ticket_type', 'enhancement', 2),
-                ('ticket_type', 'task', 3))),
+               (('resolution', 'fixed', '1'),
+                ('resolution', 'invalid', '2'),
+                ('resolution', 'wontfix', '3'),
+                ('resolution', 'duplicate', '4'),
+                ('resolution', 'worksforme', '5'),
+                ('priority', 'blocker', '1'),
+                ('priority', 'critical', '2'),
+                ('priority', 'major', '3'),
+                ('priority', 'minor', '4'),
+                ('priority', 'trivial', '5'),
+                ('ticket_type', 'defect', '1'),
+                ('ticket_type', 'enhancement', '2'),
+                ('ticket_type', 'task', '3'))),
            ('permission',
              ('username', 'action'),
                (('anonymous', 'LOG_VIEW'),
Index: trac/ticket/query.py
===================================================================
--- trac/ticket/query.py	(revision 6083)
+++ trac/ticket/query.py	(working copy)
@@ -403,16 +403,21 @@
             # FIXME: This is a somewhat ugly hack.  Can we also have the
             #        column type for this?  If it's an integer, we do first
             #        one, if text, we do 'else'
+            #        changed to use CASE WHEN syntax to be compatible with Ingres
             if name in ('id', 'time', 'changetime'):
                 if desc:
-                    sql.append("COALESCE(%s,0)=0 DESC," % col)
+                    sql.append("(CASE WHEN COALESCE(%s,0)=0 THEN 1 ELSE 2 END)," % col)
+                    #sql.append("COALESCE(%s,0)=0 DESC," % col)
                 else:
-                    sql.append("COALESCE(%s,0)=0," % col)
+                    sql.append("(CASE WHEN COALESCE(%s,)=0 THEN 1 ELSE 2 END) DESC," % col)
+                    #sql.append("COALESCE(%s,0)=0," % col)
             else:
                 if desc:
-                    sql.append("COALESCE(%s,'')='' DESC," % col)
+                    sql.append("(CASE WHEN COALESCE(%s,'')='' THEN 1 ELSE 2 END)," % col)
+                    #sql.append("COALESCE(%s,'')='' DESC," % col)
                 else:
-                    sql.append("COALESCE(%s,'')=''," % col)
+                    sql.append("(CASE WHEN COALESCE(%s,'')='' THEN 1 ELSE 2 END) DESC," % col)
+                    #sql.append("COALESCE(%s,'')=''," % col)
             if name in enum_columns:
                 if desc:
                     sql.append("%s.value DESC" % name)
@@ -424,11 +429,13 @@
                 else:
                     time_col = 'version.time'
                 if desc:
-                    sql.append("COALESCE(%s,0)=0 DESC,%s DESC,%s DESC"
-                               % (time_col, time_col, col))
+                    sql.append("(CASE WHEN COALESCE(%s,0)=0 THEN 1 ELSE 2 END), %s DESC, %s DESC" % (time_col, time_col, col))
+                #    sql.append("COALESCE(%s,0)=0 DESC,%s DESC,%s DESC"
+                 #              % (time_col, time_col, col))
                 else:
-                    sql.append("COALESCE(%s,0)=0,%s,%s"
-                               % (time_col, time_col, col))
+                    sql.append("(CASE WHEN COALESCE(%s,0)=0 THEN 1 ELSE 2 END) DESC,%s,%s" % (time_col, time_col, col))
+                #    sql.append("COALESCE(%s,0)=0,%s,%s"
+                 #              % (time_col, time_col, col))
             else:
                 if desc:
                     sql.append("%s DESC" % col)
Index: trac/db/ingres_backend.py
===================================================================
--- trac/db/ingres_backend.py	(revision 0)
+++ trac/db/ingres_backend.py	(revision 0)
@@ -0,0 +1,235 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2007 Edgewall Software
+# Copyright (C) 2007 Alexander Trofast <alex.trofast@ingres.com>
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://trac.edgewall.org/wiki/TracLicense.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://trac.edgewall.org/log/.
+#
+# Author: Alexander Trofast <alex.trofast@ingres.com>
+
+
+import re
+
+from trac.core import *
+from trac.db.api import IDatabaseConnector
+from trac.db.util import ConnectionWrapper, IterableCursor
+from types import *
+
+_like_escape_re = re.compile(r'([/_%])')
+
+
+class IngresCursor(IterableCursor):
+	#Changes LIMIT to FIRST and moves it
+	def _convert_limit(self, query):
+		reglim = re.compile(r"SELECT(.+)LIMIT\s+(\d+)", re.I | re.M)
+		query = reglim.sub(r"SELECT FIRST \2 \1", query)
+		return query
+
+	# Hide away reserved word session within quotes for transportability
+	def _replace_session(self, query):
+		regses = re.compile(r"(.+)\s+SESSION\s+(.*)", re.I)
+		query = regses.sub(r'\1 t_session \2', query)
+		return query
+
+	#methods to hack out unicode
+	def _strip_unicode(self, arg):
+		if type(arg) is UnicodeType:
+			for char in arg:
+				if ord(char) > 128:
+					arg = arg.replace(char, '', 1)
+			arg = arg.encode('ascii')
+		return arg
+
+	def _parse_args(self, query):
+		args_replace = []
+		if type(query) is not ListType and type(query) is not TupleType:
+			args_replace.append(self._strip_unicode(query))
+		else:
+			for i in range(len(query)):
+				arg_replace = []
+				if type(query[i]) is not ListType and type(query[i]) is not TupleType:
+					args_replace.append(self._strip_unicode(query[i]))
+				else:
+					for k in range(len(query[i])):
+						arg_replace.append(self._strip_unicode(query[i][k]))
+					args_replace.append(arg_replace)
+		return args_replace
+
+	def execute(self, sql, args = None):
+		sql = self._convert_limit(sql)
+		sql = self._replace_session(sql)
+		if args:
+			sql = sql.replace('%s', '?')
+			args = self._parse_args(args)
+			return self.cursor.execute(sql, args)
+		return self.cursor.execute(sql)
+
+	def executemany(self, sql, args = None):
+		if args:
+			sql = sql.replace('%s', '?')
+			sql = self._convert_limit(sql)
+			sql = self._replace_session(sql)
+			args = self._parse_args(args)
+			return self.cursor.executemany(sql, args)
+		return self.cursor.execute(sql)
+
+class IngresConnector(Component):
+	"""Ingres backend database support
+	usage: ingres://user:password@vnode/database
+	"""
+	implements(IDatabaseConnector)
+
+        #check for whether a field is an index, and convert it to an appropriate data type
+        def _is_index(self, name, indices):
+                for ind in indices:
+                        if name in ind.columns:
+                                return True
+                else:
+                        return False
+	
+	def get_supported_schemes(self):
+		return [('ingres', 1)]
+
+	def get_connection(self, path, user=None, password=None, host=None, port=None, params={}):
+		return IngresConnection(path, user, password, host, port, params)
+
+	def init_db(self, path, user=None, password=None, host=None, port=None, params={}):
+		cnx = self.get_connection(path, user, password, host, port, params)
+		cursor = cnx.cursor()
+		from trac.db_default import schema
+		for table in schema:
+			for stmt in self.to_sql(table):
+				#print stmt
+				cursor.execute(stmt)
+		cnx.commit()
+
+	def to_sql(self, table):
+		sql = ['CREATE TABLE %s (' % table.name]
+		if table.name == 'session':
+			sql = ['CREATE TABLE t_session (']
+		coldefs = []
+		#find number of text keys
+		keys = 0
+		for clm in table.columns:
+			if (clm.name in table.key or self._is_index(clm.name, table.indices)) and clm.auto_increment == False:
+				keys += 1
+		key_length = 1
+		if keys:
+			key_length = 1950 / keys
+
+		#variable used to determine if there is a procedure to create
+		auto_inc = False
+		proc = []
+		rule = []
+
+		for column in table.columns:
+			ctype = column.type
+			if column.auto_increment:
+				ctype = 'int' #can't use a sequence on text
+				#yield "CREATE SEQUENCE %s_%s_seq START WITH 1 INCREMENT BY 1;" % (table.name, column.name)
+				proc.append("CREATE PROCEDURE %s_%s_proc AS DECLARE lastID integer; newID integer; BEGIN SELECT MAX(%s) INTO :lastID FROM %s; :newID = :lastID + 1; UPDATE %s SET %s = :newID WHERE %s = 0 END;" % (table.name, column.name, column.name, table.name, table.name, column.name, column.name))
+				rule.append("CREATE RULE %s_%s_rule AFTER INSERT ON %s EXECUTE PROCEDURE %s_%s_proc;" % (table.name, column.name, table.name, table.name, column.name))
+				auto_inc = True
+			if ctype == 'text':
+				if column.name in table.key or self._is_index(column.name, table.indices):
+					ctype = 'varchar(%s)' % key_length
+					#path can be really long, make sure it has more room
+					if table.name == 'node_change':
+						if column.name == 'path':
+							ctype = 'varchar(1000)'
+						elif column.name == 'rev':
+							ctype = 'varchar(500)'
+						elif column.name == 'change_type':
+							ctype = 'varchar(450)'
+				else:
+					ctype = 'varchar(1024)'
+				if column.name == 'description' and (table.name == 'ticket' or table.name =='milestone'):
+					ctype = 'long varchar'
+				elif column.name == 'text' and table.name == 'wiki':
+					ctype = 'long varchar'
+			if column.auto_increment:
+				default = " WITH DEFAULT 0"
+			else:
+				default = " "
+			""" appends NOT NULL for keys """
+			if column.name in table.key:
+				coldefs.append("  %s %s NOT NULL%s" % (column.name, ctype,default))
+			else:
+				coldefs.append("  %s %s%s" % (column.name, ctype, default))
+			
+		if len(table.key) > 0:
+			coldefs.append("  PRIMARY KEY (%s)" % ",".join(table.key))
+		sql.append(",\n".join(coldefs) + "\n) with page_size = 65536;")
+		yield "\n".join(sql)
+
+		#create procedure and rules here, after the table has been created.
+		if auto_inc == True:
+			for itm in proc:
+				yield itm
+			for itm in rule:
+				yield itm
+
+		for index in table.indices:
+			#Use tblname instead of table.name to hack session -> t_session
+			if table.name == 'session':
+				tblname = 't_session'
+			else:
+				tblname = table.name
+
+			yield "  CREATE INDEX %s_%s_idx ON %s (%s);" % (table.name, "_".join(index.columns), tblname, ",".join(index.columns))
+
+class IngresConnection(ConnectionWrapper):
+	"""Ingres Connection Wrapper"""
+
+	poolable = False
+
+	def __init__(self, path, user=None, password=None, host=None, port=None, params={}):
+		import ingresdbi
+		
+		if path.startswith('/'):
+			path = path[1:]
+		if host == None:
+			host = "(local)"
+		if user == None:
+			user = ''
+		if password == None:
+			password = ''	
+		cnx = ingresdbi.connect(vnode=host, database=path, uid=user, dbms_pwd=password)
+
+		ConnectionWrapper.__init__(self, cnx)
+		self._is_closed = False
+
+	def cast(self, column, type):
+		return "CAST(%s AS %s)" % (column, type)
+
+	def concat(self, *args):
+		return "concat(%s)" % ",".join(args)
+
+	def like(self):
+		return "LIKE %s ESCAPE '/'"
+
+	def like_escape(self, text):
+		return _like_escape_re.sub(r'/\1', text)
+
+	def get_last_id(self, cursor, table, column='id'):
+		"""Hack to get the last inserted ID, will always be max due to procedures."""
+		cursor.execute("SELECT MAX(%s) FROM %s" % (column, table))
+		return cursor.fetchone()[0]
+
+	def rollback(self):
+		self.cnx.rollback()
+
+	def close(self):
+		self.cnx.close()
+		self._is_closed = True
+
+	def cursor(self):
+		return IngresCursor(self.cnx.cursor())
+
Index: trac/search/api.py
===================================================================
--- trac/search/api.py	(revision 6083)
+++ trac/search/api.py	(working copy)
@@ -47,7 +47,8 @@
     """
     assert columns and terms
 
-    likes = ['%s %s' % (i, db.like()) for i in columns]
+    #need to cast i as char for ingres support
+    likes = ['%s %s' % (db.cast(i, 'char'), db.like()) for i in columns]
     c = ' OR '.join(likes)
     sql = '(' + ') AND ('.join([c] * len(terms)) + ')'
     args = []
