# -*- coding: iso8859-1 -*-
#
# Copyright (C) 2005 Edgewall Software
# Copyright (C) 2005 Christopher Lenz <cmlenz@gmx.de>
#
# Trac is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# Trac is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# Author: Christopher Lenz <cmlenz@gmx.de>

from __future__ import generators

from trac.util import TracError

import os
import os.path
from threading import Condition, Lock


class IterableCursor(object):
    """
    Wrapper for DB-API cursor objects that makes the cursor iterable. Iteration
    will generate the rows of a SELECT query one by one.
    """

    __slots__ = 'cursor'

    def __init__(self, cursor):
        self.cursor = cursor

    def __del__(self):
        try:
            self.cursor.close()
        except:
            pass

    def __getattr__(self, name):
        return getattr(self.cursor, name)

    def __iter__(self):
        while 1:
            row = self.cursor.fetchone()
            if not row:
                return
            yield row


class ConnectionWrapper(object):
    """
    Generic wrapper around connection objects. This wrapper makes cursor
    produced by the connection iterable using IterableCursor.
    """

    __slots__ = 'cnx'

    def __init__(self, cnx):
        self.cnx = cnx

    def __del__(self):
        try:
            self.cnx.close()
        except:
            pass

    def __getattr__(self, name):
        return getattr(self.cnx, name)

    def cursor(self):
        cursor = self.cnx.cursor()
        return IterableCursor(cursor)


class SQLiteConnection(ConnectionWrapper):
    """
    Connection wrapper for SQLite.
    """

    __slots__ = ['cnx']

    def __init__(self, dbpath, timeout=10000):
        if dbpath != ':memory:':
            if not os.access(dbpath, os.F_OK):
                raise TracError, 'Database "%s" not found.' % dbpath
            
            dbdir = os.path.dirname(dbpath)
            if not os.access(dbpath, os.R_OK + os.W_OK) or \
                   not os.access(dbdir, os.R_OK + os.W_OK):
                raise TracError, 'The web server user requires read _and_ ' \
                                 'write permission to the database %s and ' \
                                 'the directory this file is located in.' \
                                 % dbpath

        import sqlite
        cnx = sqlite.connect(dbpath, timeout=timeout)
        ConnectionWrapper.__init__(self, cnx)

    def get_last_id(self):
        return self.cnx.db.sqlite_last_insert_rowid()


def get_cnx(env):
    db_str = env.get_config('trac', 'database', 'sqlite:db/trac.db')
    scheme, rest = db_str.split(':', 1)

    if scheme == 'sqlite':
        if not rest.startswith('/'):
            rest = os.path.join(env.path, rest)
        return SQLiteConnection(rest)

    raise TracError, 'Unsupported database type "%s"' % scheme


__all__ = ['get_cnx']

@@ -1252,32 +1252,60 @@
  __del__(self):%0A
+        if self.cursor:%0A    
         try:%0A   
@@ -1305,32 +1305,36 @@
 ry:%0A            
+    
 self.cursor.clos
@@ -1341,32 +1341,55 @@
 e()%0A        
+    
 except
-:%0A
+ ReferenceError:%0A    
             
@@ -1824,21 +1824,23 @@
 ots__ = 
+%5B
 'cnx'
+%5D
 %0A%0A    de
@@ -1901,32 +1901,57 @@
  __del__(self):%0A
+        if self.cnx:%0A    
         try:%0A   
@@ -1951,32 +1951,36 @@
 ry:%0A            
+ 
+   
 self.cnx.close()
@@ -1992,16 +1992,39 @@
     
+    
 except
-:%0A
+ ReferenceError:%0A    
     
@@ -2319,32 +2319,32 @@
 ts__ = %5B'cnx'%5D%0A%0A
-
     def __init__
@@ -2374,16 +2374,40 @@
 10000):%0A
+        self.cnx = None%0A
         

Fix to the database connection wrapper in {{{trac/db.py}}}, which would cause infinite recursion when initialization failed. Closes #1327. Thanks to Mark Rowe for the patch.