~mordred/drizzle-interface/cython-interface

« back to all changes in this revision

Viewing changes to drizzle/db.pyx

  • Committer: Monty Taylor
  • Date: 2009-12-24 05:27:28 UTC
  • Revision ID: mordred@inaugust.com-20091224052728-hcqjuilck9l94cxn
Don't know if this even works...

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
cimport drizzle
2
 
 
3
 
 
4
 
libdrizzle = None
 
1
cimport libdrizzle
 
2
 
 
3
from itertools import izip, islice
 
4
from drizzle import errors, libdrizzle as _libdrizzle
 
5
from drizzle.column_types import from_db, to_db, STRING, BINARY, NUMBER, DATETIME, ROWID
 
6
 
 
7
 
 
8
 
 
9
_drizzle = None
5
10
 
6
11
apilevel = "2.0"
7
12
#FIXME: threadsafety =
9
14
 
10
15
def connect(*args, **kwargs):
11
16
    """Connect to a database, returning a new Connection object."""
12
 
    if libdrizzle is None:
13
 
        libdrizzle = Drizzle()
14
 
    connection = libdrizzle.create_client_connection(*args, **kwargs)
 
17
    if _drizzle is None:
 
18
        _drizzle = Drizzle()
 
19
    connection = _drizzle.create_client_connection(*args, **kwargs)
15
20
    connection._connect()
16
21
    return connection
17
22
 
18
23
def version():
19
 
  return drizzle.drizzle_version()
 
24
  return libdrizzle.drizzle_version()
20
25
 
 
26
def _columns_description(columns):
 
27
    """Given a list of libdrizzle Column objects, generate a list of 
 
28
    tuples suitable for the Python DB-API Cursor.descriptions field.
 
29
    
 
30
    """
 
31
    # TODO: preliminary code. Ensure that correct values are being returned here.
 
32
    description = list()
 
33
    for column in columns:
 
34
        # see http://bazaar.launchpad.net/~habnabit/oursql/master/annotate/head%3A/oursqlx/util.pyx#L26
 
35
        name = column.name()
 
36
        column_type = column.column_type()
 
37
        display_size = column.size()
 
38
        internal_size = None    # FIXME: bit length here
 
39
        precision = None        # FIXME: precision of float/decimal 
 
40
        scale = None            # FIXME: column.decimals()?
 
41
        null_ok = (column.flags() &
 
42
                   _libdrizzle.DRIZZLE_COLUMN_FLAGS_NOT_NULL) == 0
 
43
                    
 
44
        description.append((name, column_type, display_size, internal_size, 
 
45
                                precision, scale, null_ok))
 
46
    return description
21
47
cdef class Drizzle:
22
48
 
23
 
  cdef drizzle.drizzle_st * _st_drizzle
 
49
  cdef libdrizzle.drizzle_st * _st_drizzle
24
50
  def __cinit__(self, Drizzle fromdrizz= None):
25
51
    if fromdrizz is None:
26
 
      self._st_drizzle = drizzle.drizzle_create(NULL)
 
52
      self._st_drizzle = libdrizzle.drizzle_create(NULL)
27
53
    else:
28
 
      self._st_drizzle = drizzle.drizzle_clone(NULL, fromdrizz._st_drizzle)
 
54
      self._st_drizzle = libdrizzle.drizzle_clone(NULL, fromdrizz._st_drizzle)
29
55
 
30
56
  def __dealloc__(self):
31
 
    cdef drizzle.drizzle_st *st_drizzle = self._st_drizzle
 
57
    cdef libdrizzle.drizzle_st *st_drizzle = self._st_drizzle
32
58
    if st_drizzle != NULL:
33
 
      drizzle.drizzle_free(self._st_drizzle)
 
59
      libdrizzle.drizzle_free(self._st_drizzle)
34
60
 
35
61
  def create_client_connection(self, *args, **kwargs):
36
62
    conn= Connection(self, *args, **kwargs)
39
65
cdef class Connection:
40
66
 
41
67
  cdef Drizzle _st_drizzle
42
 
  cdef drizzle.drizzle_con_st * _st_conn
 
68
  cdef libdrizzle.drizzle_con_st * _st_conn
43
69
  cdef char *host
44
70
  cdef unsigned int port
45
71
  cdef char *username
55
81
 
56
82
  def __dealloc__(self):
57
83
    if self._st_conn != NULL:
58
 
      drizzle.drizzle_con_free(self._st_conn)
 
84
      libdrizzle.drizzle_con_free(self._st_conn)
59
85
 
60
86
  def _connect(self):
61
87
    if self._st_conn == NULL:
62
 
      self._st_conn = drizzle.drizzle_con_create(self._st_drizzle._st_drizzle, NULL)
63
 
    drizzle.drizzle_con_set_tcp(self._st_conn, self.host, self.port)
 
88
      self._st_conn = libdrizzle.drizzle_con_create(self._st_drizzle._st_drizzle, NULL)
 
89
    libdrizzle.drizzle_con_set_tcp(self._st_conn, self.host, self.port)
64
90
    #self._st_conn.drizzle_con_set_auth(self.username, self.password)
65
 
    drizzle.drizzle_con_set_db(self._st_conn, self.database)
66
 
    drizzle.drizzle_con_connect(self._st_conn)
 
91
    libdrizzle.drizzle_con_set_db(self._st_conn, self.database)
 
92
    libdrizzle.drizzle_con_connect(self._st_conn)
67
93
 
68
94
  def close(self):
69
95
    if self._st_conn != NULL:
70
 
      drizzle.drizzle_con_close(self._st_conn)
71
 
      drizzle.drizzle_con_free(self._st_conn)
 
96
      libdrizzle.drizzle_con_close(self._st_conn)
 
97
      libdrizzle.drizzle_con_free(self._st_conn)
72
98
      self._st_conn = NULL
73
99
 
74
 
  @property
75
 
  def is_closed(self):
 
100
  property is_closed:
76
101
    """Return true if the Connection is not open."""
77
 
    return self._st_conn == NULL
78
 
 
79
 
  #def cursor(self):
80
 
  #  """Return a new Cursor object using the connection."""
81
 
  #  return Cursor(self)
82
 
 
83
 
 
84
 
 
 
102
    def __get__(self):
 
103
      return self._st_conn == NULL
 
104
 
 
105
  def cursor(self):
 
106
    """Return a new Cursor object using the connection."""
 
107
    return Cursor(self)
 
108
 
 
109
  def query(self, char *sql):
 
110
    """Execute a query"""
 
111
    libdrizzle.drizzle_con_query(self._st_conn, sql);
 
112
 
 
113
 
 
114
cdef class Cursor:
 
115
 
 
116
    cdef libdrizzle.drizzle_result_st *_last_result
 
117
    def __init__(self, connection):
 
118
        self._connection = connection
 
119
        self.arraysize = 1
 
120
        self._reset()
 
121
 
 
122
    def __iter__(self):
 
123
        """Iterate over query result rows one-by-one."""
 
124
        return iter(self.fetchone, None)
 
125
 
 
126
    property connection:
 
127
        """The Connection object on which this cursor was created."""
 
128
        def __get__(self):
 
129
            return self._connection
 
130
 
 
131
    property _drizzle_connection:
 
132
        def __get__(self):
 
133
            return self.connection._drizzle_connection
 
134
 
 
135
    def _check_connected(self):
 
136
        """Raise a ProgrammingError if the Connection has been closed."""
 
137
        if self.connection.is_closed:
 
138
            raise errors.ProgrammingError("Connection closed")
 
139
 
 
140
    def _reset(self):
 
141
        # Note: the SWIG destructors should take care of freeing _last_result and _columns when we replace them.
 
142
        self._last_result = NULL
 
143
        self._columns = None
 
144
        self._has_rows_left = False
 
145
        self._description = None
 
146
 
 
147
    def execute(self, sql):
 
148
        """Execute a database operation, making the results accessible
 
149
        using this cursor.
 
150
        
 
151
        """
 
152
        self._check_connected()
 
153
 
 
154
        self._reset()
 
155
        
 
156
        cdef libdrizzle.drizzle_return_t d_ret
 
157
        self._last_result = libdrizzle.drizzle_query(self._drizzle_connection._st_conn, sql[:-1], len(sql), &d_ret)
 
158
        
 
159
        if d_ret == libdrizzle.DRIZZLE_RETURN_OK:
 
160
            # No error
 
161
            if self._last_result.column_count() != 0:
 
162
                self._columns = [column for column in 
 
163
                                 iter(self._last_result.read_column, None)]
 
164
                self._has_rows_left = True
 
165
                self._description = _columns_description(self._columns)
 
166
        else:
 
167
            raise errors.DatabaseError(libdrizzle.drizzle_result_error_code(self._last_result), libdrizzle.drizzle_con_error(self._drizzle_connection._st_conn))
 
168
 
 
169
    def fetchone(self):
 
170
        """Fetch the next row of a query result set, returning a single
 
171
        sequence, or None when no more data is available.
 
172
        
 
173
        """
 
174
        self._check_connected()
 
175
        
 
176
        if self._last_result is None:
 
177
            raise ProgrammingError
 
178
        else:
 
179
            if self._has_rows_left:
 
180
                row = self._last_result.buffer_row()
 
181
                if row is not None:
 
182
                    row = tuple([from_db.convert(field, valuetype=descr[1])
 
183
                                for descr, field in izip(self.description, row)])
 
184
                else:
 
185
                    self._has_rows_left = False
 
186
                    
 
187
                return row
 
188
 
 
189
    def fetchmany(self, size=None):
 
190
        """Fetch the next set of rows of a query result, returning a
 
191
        sequence of sequences (e.g. a list of tuples). An empty sequence
 
192
        is returned when no more rows are available.
 
193
            
 
194
        The number of rows to fetch per call is specified by the
 
195
        parameter.  If it is not given, the cursor's arraysize
 
196
        determines the number of rows to be fetched.
 
197
        
 
198
        """
 
199
        if size is None:
 
200
            size = self.arraysize
 
201
            
 
202
        return list(islice(self, size))
 
203
 
 
204
    def fetchall(self):
 
205
        """Fetch all (remaining) rows of a query result, returning them
 
206
        as a sequence of sequences (e.g. a list of tuples).
 
207
        
 
208
        """        
 
209
        # TODO: test and compare buffer_all with the current buffer_row implementation.
 
210
        return list(self)
 
211
 
 
212
    @property
 
213
    def rowcount(self):
 
214
        """The number of rows that the last .execute*() produced.
 
215
        
 
216
        The attribute is -1 in case no .execute*() has been performed on
 
217
        the cursor or the rowcount of the last operation is cannot be
 
218
        determined.
 
219
        
 
220
        Note: for Drizzle, the row count of a query cannot be determined
 
221
        until all rows have been fetched.
 
222
        
 
223
        """
 
224
        if self._last_result is None or self._has_rows_left:
 
225
            return -1
 
226
        else:
 
227
            return self._last_result.row_count()
 
228
 
 
229
    @property
 
230
    def rownumber(self):
 
231
        """The current 0-based index of the cursor in the result set or
 
232
        None if the index cannot be determined.
 
233
        
 
234
        """
 
235
        # row_current() seems to be 1-indexed
 
236
        return self._last_result.row_current()-1
 
237
            
 
238
    @property
 
239
    def description(self):
 
240
        """A list of tuples describing the columns in the current result
 
241
        set:
 
242
        
 
243
        Tuples returned are of the form:
 
244
        (name, type, display_size, internal_size, precision, scale, null_ok)
 
245
        
 
246
        """
 
247
        return self._description