Edgewall Software

Ticket #6235: trac_ingres_support.diff

File trac_ingres_support.diff, 15.2 kB (added by AlexTrofast <alex.trofast@…>, 12 months ago)

Diff patch against revision 6083.

  • setup.py

     
    6666        trac.db.mysql = trac.db.mysql_backend 
    6767        trac.db.postgres = trac.db.postgres_backend 
    6868        trac.db.sqlite = trac.db.sqlite_backend 
     69        trac.db.ingres = trac.db.ingres_backend 
    6970        trac.mimeview.enscript = trac.mimeview.enscript 
    7071        trac.mimeview.patch = trac.mimeview.patch 
    7172        trac.mimeview.php = trac.mimeview.php 
  • trac/db_default.py

     
    162162## 
    163163## Default Reports 
    164164## 
     165#Reports for Ingres since ORDER BY in its old form does not work. 
    165166 
    166167def get_reports(db): 
    167168    return ( 
     
    202203  FROM ticket t 
    203204  LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' 
    204205  WHERE status <> 'closed' 
    205   ORDER BY (version IS NULL),version, p.value, t.type, time 
     206  ORDER BY (CASE WHEN version IS NULL THEN 1 ELSE 2 END) DESC,version, p.value, t.type, time 
    206207"""), 
    207208#---------------------------------------------------------------------------- 
    208209('Active Tickets by Milestone', 
     
    224225  FROM ticket t 
    225226  LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' 
    226227  WHERE status <> 'closed'  
    227   ORDER BY (milestone IS NULL),milestone, p.value, t.type, time 
     228  ORDER BY (CASE WHEN milestone IS NULL THEN 1 ELSE 2 END) DESC,milestone, p.value, t.type, time 
    228229""" % db.concat("'Milestone '", 'milestone')), 
    229230#---------------------------------------------------------------------------- 
    230231('Accepted, Active Tickets by Owner', 
     
    279280   time AS _time,reporter AS _reporter 
    280281  FROM ticket t 
    281282  LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' 
    282   ORDER BY (milestone IS NULL), milestone DESC, (status = 'closed'),  
     283  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,  
    283284        (CASE status WHEN 'closed' THEN modified ELSE (-1)*p.value END) DESC 
    284285"""), 
    285286#---------------------------------------------------------------------------- 
     
    299300  FROM ticket t 
    300301  LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' 
    301302  WHERE t.status <> 'closed' AND owner = $USER 
    302   ORDER BY (status = 'accepted') DESC, p.value, milestone, t.type, time 
     303  ORDER BY (CASE WHEN status = 'accepted' THEN 1 ELSE 2 END), p.value, milestone, t.type, time 
    303304"""), 
    304305#---------------------------------------------------------------------------- 
    305306('Active Tickets, Mine first', 
     
    321322  FROM ticket t 
    322323  LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' 
    323324  WHERE status <> 'closed'  
    324   ORDER BY (owner = $USER) DESC, p.value, milestone, t.type, time 
     325  ORDER BY (CASE WHEN owner = $USER THEN 1 ELSE 2 END), p.value, milestone, t.type, time 
    325326""")) 
    326327 
    327328 
     
    347348                ('2.0', 0))), 
    348349           ('enum', 
    349350             ('type', 'name', 'value'), 
    350                (('resolution', 'fixed', 1), 
    351                 ('resolution', 'invalid', 2), 
    352                 ('resolution', 'wontfix', 3), 
    353                 ('resolution', 'duplicate', 4), 
    354                 ('resolution', 'worksforme', 5), 
    355                 ('priority', 'blocker', 1), 
    356                 ('priority', 'critical', 2), 
    357                 ('priority', 'major', 3), 
    358                 ('priority', 'minor', 4), 
    359                 ('priority', 'trivial', 5), 
    360                 ('ticket_type', 'defect', 1), 
    361                 ('ticket_type', 'enhancement', 2), 
    362                 ('ticket_type', 'task', 3))), 
     351               (('resolution', 'fixed', '1'), 
     352                ('resolution', 'invalid', '2'), 
     353                ('resolution', 'wontfix', '3'), 
     354                ('resolution', 'duplicate', '4'), 
     355                ('resolution', 'worksforme', '5'), 
     356                ('priority', 'blocker', '1'), 
     357                ('priority', 'critical', '2'), 
     358                ('priority', 'major', '3'), 
     359                ('priority', 'minor', '4'), 
     360                ('priority', 'trivial', '5'), 
     361                ('ticket_type', 'defect', '1'), 
     362                ('ticket_type', 'enhancement', '2'), 
     363                ('ticket_type', 'task', '3'))), 
    363364           ('permission', 
    364365             ('username', 'action'), 
    365366               (('anonymous', 'LOG_VIEW'), 
  • trac/ticket/query.py

     
    403403            # FIXME: This is a somewhat ugly hack.  Can we also have the 
    404404            #        column type for this?  If it's an integer, we do first 
    405405            #        one, if text, we do 'else' 
     406            #        changed to use CASE WHEN syntax to be compatible with Ingres 
    406407            if name in ('id', 'time', 'changetime'): 
    407408                if desc: 
    408                     sql.append("COALESCE(%s,0)=0 DESC," % col) 
     409                    sql.append("(CASE WHEN COALESCE(%s,0)=0 THEN 1 ELSE 2 END)," % col) 
     410                    #sql.append("COALESCE(%s,0)=0 DESC," % col) 
    409411                else: 
    410                     sql.append("COALESCE(%s,0)=0," % col) 
     412                    sql.append("(CASE WHEN COALESCE(%s,)=0 THEN 1 ELSE 2 END) DESC," % col) 
     413                    #sql.append("COALESCE(%s,0)=0," % col) 
    411414            else: 
    412415                if desc: 
    413                     sql.append("COALESCE(%s,'')='' DESC," % col) 
     416                    sql.append("(CASE WHEN COALESCE(%s,'')='' THEN 1 ELSE 2 END)," % col) 
     417                    #sql.append("COALESCE(%s,'')='' DESC," % col) 
    414418                else: 
    415                     sql.append("COALESCE(%s,'')=''," % col) 
     419                    sql.append("(CASE WHEN COALESCE(%s,'')='' THEN 1 ELSE 2 END) DESC," % col) 
     420                    #sql.append("COALESCE(%s,'')=''," % col) 
    416421            if name in enum_columns: 
    417422                if desc: 
    418423                    sql.append("%s.value DESC" % name) 
     
    424429                else: 
    425430                    time_col = 'version.time' 
    426431                if desc: 
    427                     sql.append("COALESCE(%s,0)=0 DESC,%s DESC,%s DESC" 
    428                                % (time_col, time_col, col)) 
     432                    sql.append("(CASE WHEN COALESCE(%s,0)=0 THEN 1 ELSE 2 END), %s DESC, %s DESC" % (time_col, time_col, col)) 
     433                #    sql.append("COALESCE(%s,0)=0 DESC,%s DESC,%s DESC" 
     434                 #              % (time_col, time_col, col)) 
    429435                else: 
    430                     sql.append("COALESCE(%s,0)=0,%s,%s" 
    431                                % (time_col, time_col, col)) 
     436                    sql.append("(CASE WHEN COALESCE(%s,0)=0 THEN 1 ELSE 2 END) DESC,%s,%s" % (time_col, time_col, col)) 
     437                #    sql.append("COALESCE(%s,0)=0,%s,%s" 
     438                 #              % (time_col, time_col, col)) 
    432439            else: 
    433440                if desc: 
    434441                    sql.append("%s DESC" % col) 
  • trac/db/ingres_backend.py

     
     1# -*- coding: utf-8 -*- 
     2# 
     3# Copyright (C) 2007 Edgewall Software 
     4# Copyright (C) 2007 Alexander Trofast <alex.trofast@ingres.com> 
     5# All rights reserved. 
     6# 
     7# This software is licensed as described in the file COPYING, which 
     8# you should have received as part of this distribution. The terms 
     9# are also available at http://trac.edgewall.org/wiki/TracLicense. 
     10# 
     11# This software consists of voluntary contributions made by many 
     12# individuals. For the exact contribution history, see the revision 
     13# history and logs, available at http://trac.edgewall.org/log/. 
     14# 
     15# Author: Alexander Trofast <alex.trofast@ingres.com> 
     16 
     17 
     18import re 
     19 
     20from trac.core import * 
     21from trac.db.api import IDatabaseConnector 
     22from trac.db.util import ConnectionWrapper, IterableCursor 
     23from types import * 
     24 
     25_like_escape_re = re.compile(r'([/_%])') 
     26 
     27 
     28class IngresCursor(IterableCursor): 
     29        #Changes LIMIT to FIRST and moves it 
     30        def _convert_limit(self, query): 
     31                reglim = re.compile(r"SELECT(.+)LIMIT\s+(\d+)", re.I | re.M) 
     32                query = reglim.sub(r"SELECT FIRST \2 \1", query) 
     33                return query 
     34 
     35        # Hide away reserved word session within quotes for transportability 
     36        def _replace_session(self, query): 
     37                regses = re.compile(r"(.+)\s+SESSION\s+(.*)", re.I) 
     38                query = regses.sub(r'\1 t_session \2', query) 
     39                return query 
     40 
     41        #methods to hack out unicode 
     42        def _strip_unicode(self, arg): 
     43                if type(arg) is UnicodeType: 
     44                        for char in arg: 
     45                                if ord(char) > 128: 
     46                                        arg = arg.replace(char, '', 1) 
     47                        arg = arg.encode('ascii') 
     48                return arg 
     49 
     50        def _parse_args(self, query): 
     51                args_replace = [] 
     52                if type(query) is not ListType and type(query) is not TupleType: 
     53                        args_replace.append(self._strip_unicode(query)) 
     54                else: 
     55                        for i in range(len(query)): 
     56                                arg_replace = [] 
     57                                if type(query[i]) is not ListType and type(query[i]) is not TupleType: 
     58                                        args_replace.append(self._strip_unicode(query[i])) 
     59                                else: 
     60                                        for k in range(len(query[i])): 
     61                                                arg_replace.append(self._strip_unicode(query[i][k])) 
     62                                        args_replace.append(arg_replace) 
     63                return args_replace 
     64 
     65        def execute(self, sql, args = None): 
     66                sql = self._convert_limit(sql) 
     67                sql = self._replace_session(sql) 
     68                if args: 
     69                        sql = sql.replace('%s', '?') 
     70                        args = self._parse_args(args) 
     71                        return self.cursor.execute(sql, args) 
     72                return self.cursor.execute(sql) 
     73 
     74        def executemany(self, sql, args = None): 
     75                if args: 
     76                        sql = sql.replace('%s', '?') 
     77                        sql = self._convert_limit(sql) 
     78                        sql = self._replace_session(sql) 
     79                        args = self._parse_args(args) 
     80                        return self.cursor.executemany(sql, args) 
     81                return self.cursor.execute(sql) 
     82 
     83class IngresConnector(Component): 
     84        """Ingres backend database support 
     85        usage: ingres://user:password@vnode/database 
     86        """ 
     87        implements(IDatabaseConnector) 
     88 
     89        #check for whether a field is an index, and convert it to an appropriate data type 
     90        def _is_index(self, name, indices): 
     91                for ind in indices: 
     92                        if name in ind.columns: 
     93                                return True 
     94                else: 
     95                        return False 
     96         
     97        def get_supported_schemes(self): 
     98                return [('ingres', 1)] 
     99 
     100        def get_connection(self, path, user=None, password=None, host=None, port=None, params={}): 
     101                return IngresConnection(path, user, password, host, port, params) 
     102 
     103        def init_db(self, path, user=None, password=None, host=None, port=None, params={}): 
     104                cnx = self.get_connection(path, user, password, host, port, params) 
     105                cursor = cnx.cursor() 
     106                from trac.db_default import schema 
     107                for table in schema: 
     108                        for stmt in self.to_sql(table): 
     109                                #print stmt 
     110                                cursor.execute(stmt) 
     111                cnx.commit() 
     112 
     113        def to_sql(self, table): 
     114                sql = ['CREATE TABLE %s (' % table.name] 
     115                if table.name == 'session': 
     116                        sql = ['CREATE TABLE t_session ('] 
     117                coldefs = [] 
     118                #find number of text keys 
     119                keys = 0 
     120                for clm in table.columns: 
     121                        if (clm.name in table.key or self._is_index(clm.name, table.indices)) and clm.auto_increment == False: 
     122                                keys += 1 
     123                key_length = 1 
     124                if keys: 
     125                        key_length = 1950 / keys 
     126 
     127                #variable used to determine if there is a procedure to create 
     128                auto_inc = False 
     129                proc = [] 
     130                rule = [] 
     131 
     132                for column in table.columns: 
     133                        ctype = column.type 
     134                        if column.auto_increment: 
     135                                ctype = 'int' #can't use a sequence on text 
     136                                #yield "CREATE SEQUENCE %s_%s_seq START WITH 1 INCREMENT BY 1;" % (table.name, column.name) 
     137                                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)) 
     138                                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)) 
     139                                auto_inc = True 
     140                        if ctype == 'text': 
     141                                if column.name in table.key or self._is_index(column.name, table.indices): 
     142                                        ctype = 'varchar(%s)' % key_length 
     143                                        #path can be really long, make sure it has more room 
     144                                        if table.name == 'node_change': 
     145                                                if column.name == 'path': 
     146                                                        ctype = 'varchar(1000)' 
     147                                                elif column.name == 'rev': 
     148                                                        ctype = 'varchar(500)' 
     149                                                elif column.name == 'change_type': 
     150                                                        ctype = 'varchar(450)' 
     151                                else: 
     152                                        ctype = 'varchar(1024)' 
     153                                if column.name == 'description' and (table.name == 'ticket' or table.name =='milestone'): 
     154                                        ctype = 'long varchar' 
     155                                elif column.name == 'text' and table.name == 'wiki': 
     156                                        ctype = 'long varchar' 
     157                        if column.auto_increment: 
     158                                default = " WITH DEFAULT 0" 
     159                        else: 
     160                                default = " " 
     161                        """ appends NOT NULL for keys """ 
     162                        if column.name in table.key: 
     163                                coldefs.append("  %s %s NOT NULL%s" % (column.name, ctype,default)) 
     164                        else: 
     165                                coldefs.append("  %s %s%s" % (column.name, ctype, default)) 
     166                         
     167                if len(table.key) > 0: 
     168                        coldefs.append("  PRIMARY KEY (%s)" % ",".join(table.key)) 
     169                sql.append(",\n".join(coldefs) + "\n) with page_size = 65536;") 
     170                yield "\n".join(sql) 
     171 
     172                #create procedure and rules here, after the table has been created. 
     173                if auto_inc == True: 
     174                        for itm in proc: 
     175                                yield itm 
     176                        for itm in rule: 
     177                                yield itm 
     178 
     179                for index in table.indices: 
     180                        #Use tblname instead of table.name to hack session -> t_session 
     181                        if table.name == 'session': 
     182                                tblname = 't_session' 
     183                        else: 
     184                                tblname = table.name 
     185 
     186                        yield "  CREATE INDEX %s_%s_idx ON %s (%s);" % (table.name, "_".join(index.columns), tblname, ",".join(index.columns)) 
     187 
     188class IngresConnection(ConnectionWrapper): 
     189        """Ingres Connection Wrapper""" 
     190 
     191        poolable = False 
     192 
     193        def __init__(self, path, user=None, password=None, host=None, port=None, params={}): 
     194                import ingresdbi 
     195                 
     196                if path.startswith('/'): 
     197                        path = path[1:] 
     198                if host == None: 
     199                        host = "(local)" 
     200                if user == None: 
     201                        user = '' 
     202                if password == None: 
     203                        password = ''    
     204                cnx = ingresdbi.connect(vnode=host, database=path, uid=user, dbms_pwd=password) 
     205 
     206                ConnectionWrapper.__init__(self, cnx) 
     207                self._is_closed = False 
     208 
     209        def cast(self, column, type): 
     210                return "CAST(%s AS %s)" % (column, type) 
     211 
     212        def concat(self, *args): 
     213                return "concat(%s)" % ",".join(args) 
     214 
     215        def like(self): 
     216                return "LIKE %s ESCAPE '/'" 
     217 
     218        def like_escape(self, text): 
     219                return _like_escape_re.sub(r'/\1', text) 
     220 
     221        def get_last_id(self, cursor, table, column='id'): 
     222                """Hack to get the last inserted ID, will always be max due to procedures.""" 
     223                cursor.execute("SELECT MAX(%s) FROM %s" % (column, table)) 
     224                return cursor.fetchone()[0] 
     225 
     226        def rollback(self): 
     227                self.cnx.rollback() 
     228 
     229        def close(self): 
     230                self.cnx.close() 
     231                self._is_closed = True 
     232 
     233        def cursor(self): 
     234                return IngresCursor(self.cnx.cursor()) 
     235 
  • trac/search/api.py

     
    4747    """ 
    4848    assert columns and terms 
    4949 
    50     likes = ['%s %s' % (i, db.like()) for i in columns] 
     50    #need to cast i as char for ingres support 
     51    likes = ['%s %s' % (db.cast(i, 'char'), db.like()) for i in columns] 
    5152    c = ' OR '.join(likes) 
    5253    sql = '(' + ') AND ('.join([c] * len(terms)) + ')' 
    5354    args = []