3 docCopyright("Jeremy Cowgar", 2006)
4 docLicense("BSD revised")
5 docCategory("Databases")
7 docDescription("A DBI Result created by a call to DBIConn query.")
10 #include "IoMessage.h"
18 #include "IoDBIResult.h"
19 #include "IoDBIConn.h"
21 #define DATA(self) ((IoDBIResultData *)IoObject_dataPointer(self))
23 IoTag
*IoDBIResult_newTag(void *state
)
25 IoTag
*tag
= IoTag_newWithName_("DBIResult");
26 IoTag_state_(tag
, state
);
27 IoTag_cloneFunc_(tag
, (IoTagCloneFunc
*)IoDBIResult_rawClone
);
28 IoTag_freeFunc_(tag
, (IoTagFreeFunc
*)IoDBIResult_free
);
29 //IoTag_markFunc_(tag, (IoTagMarkFunc *)IoDBIResult_mark);
33 IoDBIResult
*IoDBIResult_proto(void *state
)
35 IoObject
*self
= IoObject_new(state
);
36 IoObject_tag_(self
, IoDBIResult_newTag(state
));
38 IoObject_setDataPointer_(self
, calloc(1, sizeof(IoDBIResultData
)));
39 DATA(self
)->conn
= NULL
;
40 DATA(self
)->result
= NULL
;
42 IoState_registerProtoWithFunc_(state
, self
, IoDBIResult_proto
);
45 IoMethodTable methodTable
[] = {
46 {"first", IoDBIResult_first
},
47 {"previous", IoDBIResult_previous
},
48 {"next", IoDBIResult_next
},
49 {"last", IoDBIResult_last
},
50 {"seek", IoDBIResult_seek
},
51 {"position", IoDBIResult_position
},
52 {"foreach", IoDBIResult_foreach
},
53 {"at", IoDBIResult_at
},
54 {"populate", IoDBIResult_populate
},
55 {"size", IoDBIResult_size
},
56 {"fields", IoDBIResult_fields
},
57 {"done", IoDBIResult_done
},
60 IoObject_addMethodTable_(self
, methodTable
);
66 IoDBIResult
*IoDBIResult_rawClone(IoDBIResult
*proto
)
68 IoDBIResult
*self
= IoObject_rawClonePrimitive(proto
);
69 IoObject_setDataPointer_(self
, calloc(1, sizeof(IoDBIResultData
)));
70 DATA(self
)->conn
= NULL
;
71 DATA(self
)->result
= NULL
;
75 void IoDBIResult_free(IoDBIResult
*self
)
77 free(IoObject_dataPointer(self
));
80 //void IoDBIResult_mark(IoDBIResult *self) {}
82 /* ---------------------------------------------------------------- */
84 IoDBIResult
*IoDBIResult_new(void *state
, dbi_result result
)
86 IoDBIResult
*self
= IOCLONE(IoState_protoWithInitFunction_(state
,
89 DATA(self
)->result
= result
;
90 DATA(self
)->conn
= dbi_result_get_conn(result
);
95 IoObject
*IoDBIResult_getIoObjectFromResult_(IoObject
*self
, dbi_result res
,
98 const char *val
= NULL
;
100 switch (dbi_result_get_field_type_idx(res
, index
))
102 case DBI_TYPE_INTEGER
:
103 return IONUMBER(dbi_result_get_int_idx(res
, index
));
104 case DBI_TYPE_DECIMAL
:
105 return IONUMBER(dbi_result_get_double_idx(res
, index
));
106 case DBI_TYPE_STRING
:
107 val
= dbi_result_get_string_idx(res
, index
);
108 /* Some DB's do not report is_null even when the are, :-( */
109 if (1 == dbi_result_field_is_null_idx(res
, index
) || val
== NULL
)
112 return IOSYMBOL(dbi_result_get_string_idx(res
, index
));
113 case DBI_TYPE_BINARY
:
114 return IOSYMBOL(dbi_result_get_binary_idx(res
, index
));
115 case DBI_TYPE_DATETIME
:
116 return IoDate_newWithTime_(IOSTATE
, dbi_result_get_datetime_idx(
123 IoObject
*IoDBIResult_rowToMap_(void *state
, IoDBIResult
*self
,
126 int fIdx
, fCount
= dbi_result_get_numfields(res
);
127 IoMap
*map
= IoMap_new(state
);
129 for (fIdx
= 1; fIdx
<= fCount
; fIdx
++)
132 IOSYMBOL(dbi_result_get_field_name(res
, fIdx
)),
133 IoDBIResult_getIoObjectFromResult_(self
, res
, fIdx
));
139 /* ---------------------------------------------------------------- */
141 IoObject
*IoDBIResult_size(IoDBIResult
*self
, IoObject
*locals
,
145 docSlot("size", "Returns the number of rows available")
147 return IONUMBER(dbi_result_get_numrows(DATA(self
)->result
));
150 IoObject
*IoDBIResult_fields(IoDBIResult
*self
, IoObject
*locals
,
154 docSlot("fields", "Returns a list of field names in the result")
157 IoList
*list
= IOREF(IoList_new(IOSTATE
));
159 for (idx
= 1; idx
<= dbi_result_get_numfields(DATA(self
)->result
); idx
++)
161 IoList_rawAppend_(list
, IOSYMBOL(dbi_result_get_field_name(
162 DATA(self
)->result
, idx
)));
168 IoObject
*IoDBIResult_first(IoDBIResult
*self
, IoObject
*locals
,
172 docSlot("first", "Move the cursor to the first record")
174 if (1 != dbi_result_first_row(DATA(self
)->result
))
176 ReportDBIError(DATA(self
)->conn
, IOSTATE
, m
);
179 return IOBOOL(self
, 1);
182 IoObject
*IoDBIResult_previous(IoDBIResult
*self
, IoObject
*locals
,
186 docSlot("previous", "Move the cursor to the previous record")
188 if (1 != dbi_result_prev_row(DATA(self
)->result
))
190 ReportDBIError(DATA(self
)->conn
, IOSTATE
, m
);
193 return IOBOOL(self
, 1);
196 IoObject
*IoDBIResult_next(IoDBIResult
*self
, IoObject
*locals
,
200 docSlot("next", "Move the cursor to the next record.")
202 dbi_result res
= DATA(self
)->result
;
204 if (0 == dbi_result_next_row(res
))
206 ReportDBIError(DATA(self
)->conn
, IOSTATE
, m
);
210 dbi_result_get_currow(res
) < dbi_result_get_numrows(res
));
213 IoObject
*IoDBIResult_last(IoDBIResult
*self
, IoObject
*locals
,
217 docSlot("last", "Move the cursor to the last record")
219 dbi_result res
= DATA(self
)->result
;
221 if (0 == dbi_result_last_row(res
))
223 ReportDBIError(DATA(self
)->conn
, IOSTATE
, m
);
226 return IOBOOL(self
, 1);
230 IoObject
*IoDBIResult_seek(IoDBIResult
*self
, IoObject
*locals
,
234 docSlot("seek(row_number)", "Move the cursor to the nth record")
237 dbi_result res
= DATA(self
)->result
;
238 IoObject
*row
= IoMessage_locals_valueArgAt_(m
, locals
, 0);
242 IoState_error_(IOSTATE
, m
,
243 "argument 0 to method '%s' must be a Number, not a '%s'\n",
244 CSTRING(IoMessage_name(m
)), IoObject_name(row
));
247 rowIdx
= IoNumber_asLong(row
);
248 if (1 != dbi_result_seek_row(res
, rowIdx
))
251 int errorCode
= dbi_conn_error(DATA(self
)->conn
,
256 IoState_error_(IOSTATE
, m
, "row index %i out of range (1,%i)\n",
257 rowIdx
, dbi_result_get_numrows(res
));
261 IoState_error_(IOSTATE
, m
, "libdbi: %i: %s\n", errorCode
, error
);
265 return IOBOOL(self
, 1);
268 IoObject
*IoDBIResult_position(
269 IoDBIResult
*self
, IoObject
*locals
, IoMessage
*m
)
272 docSlot("position", "Return the current row's position (or index).")
274 unsigned long long rowNum
= dbi_result_get_currow(DATA(self
)->result
);
278 ReportDBIError(DATA(self
)->conn
, IOSTATE
, m
);
281 return IONUMBER(rowNum
);
284 IoObject
*IoDBIResult_at(IoDBIResult
*self
, IoObject
*locals
, IoMessage
*m
)
287 docSlot("at(index_or_name)", "Return the contents of the given field. The
288 parameter can be a field index or a field name.")
290 unsigned int idx
= 0;
291 dbi_result res
= DATA(self
)->result
;
293 IoObject
*key
= IoMessage_locals_valueArgAt_(m
, locals
, 0);
296 idx
= IoNumber_asInt(key
);
298 else if (ISSYMBOL(key
))
300 idx
= dbi_result_get_field_idx(res
, CSTRING(key
));
303 ReportDBIError(DATA(self
)->conn
, IOSTATE
, m
);
308 IoState_error_(IOSTATE
, m
,
309 "argument 0 to method '%s' must be a Number or Symbol, not a '%s'\n",
310 CSTRING(IoMessage_name(m
)), IoObject_name(key
));
313 return IoDBIResult_getIoObjectFromResult_(self
, res
, idx
);
316 IoObject
*IoDBIResult_populate(IoDBIResult
*self
, IoObject
*locals
,
320 docSlot("populate(object)", "Populate a decendent of DBIRecord with the
321 current record's contents. See `DBIRecord' for further explanation and an
324 dbi_result res
= DATA(self
)->result
;
325 IoObject
*baseObject
= IoMessage_locals_valueArgAt_(m
, locals
, 0);
326 IoObject
*o
= IOCLONE(baseObject
);
327 o
= IoObject_initClone_(self
, locals
, m
, o
);
329 IoObject_setSlot_to_(o
, IOSYMBOL("_map"),
330 IoDBIResult_rowToMap_(IOSTATE
, self
, res
));
335 IoObject
*IoDBIResult_foreach(IoDBIResult
*self
, IoObject
*locals
,
339 docSlot("foreach([Object], value, message)", """Loops over the records in the
340 result starting at either the first result (if the cursor has never been
341 moved) or it's current location if moved. i.e.
344 r := conn query("SELECT * FROM people")
345 r foreach(r, r at(1))
348 The above would start at the first row, however, you can move around in the
349 result set and then foreach would pickup where you left off, for instance, say
350 you wanted to skip the first three rows:
353 r := conn query("SELECT * FROM people")
355 r foreach(r, r at (1))
358 The above would start at the record #4, not at the beginning.
360 The optional Object parameter would cause a decendent of DBIRecord to be
361 populate instead of the index being set. This allows for advanced
362 functionality. Please see `DBIRecord' for further information and an example.
365 dbi_result res
= DATA(self
)->result
;
366 IoObject
*result
= IONIL(self
);
367 IoMessage
*doMessage
;
368 IoSymbol
*baseObject
= NULL
;
369 IoSymbol
*resSlotName
;
371 unsigned int i
, count
= dbi_result_get_numrows(res
);
372 unsigned int fIdx
, fCount
= dbi_result_get_numfields(res
);
374 if (IoMessage_argCount(m
) == 2)
376 resSlotName
= IoMessage_name(IoMessage_rawArgAt_(m
, 0));
377 doMessage
= IoMessage_rawArgAt_(m
, 1);
379 else if (IoMessage_argCount(m
) == 3)
381 baseObject
= IoMessage_locals_valueArgAt_(m
, locals
, 0);
382 resSlotName
= IoMessage_name(IoMessage_rawArgAt_(m
, 1));
383 doMessage
= IoMessage_rawArgAt_(m
, 2);
387 IoState_error_(IOSTATE
, m
,
388 "method '%s' takes 2 or 3 parameters, you supplied %i\n",
389 IoMessage_argCount(m
));
392 IoState_pushRetainPool(IOSTATE
);
394 if (0 == dbi_result_get_currow(res
))
396 if (0 == dbi_result_first_row(res
))
398 ReportDBIError(DATA(self
)->conn
, IOSTATE
, m
);
402 for (i
= dbi_result_get_currow(res
); i
<= count
; i
++)
404 IoState_clearTopPool(IOSTATE
);
408 ReportDBIError(DATA(self
)->conn
, IOSTATE
, m
);
413 IoObject
*o
= IOCLONE(baseObject
);
414 o
= IoObject_initClone_(self
, locals
, m
, o
);
416 IoObject_setSlot_to_(o
, IOSYMBOL("_map"),
417 IoDBIResult_rowToMap_(IOSTATE
, self
, res
));
418 IoObject_setSlot_to_(locals
, resSlotName
, o
);
421 IoObject_setSlot_to_(locals
, resSlotName
, self
);
424 result
= IoMessage_locals_performOn_(doMessage
, locals
, locals
);
426 if (IoState_handleStatus(IOSTATE
))
431 if (i
!= count
&& 0 == dbi_result_next_row(DATA(self
)->result
))
433 ReportDBIError(DATA(self
)->conn
, IOSTATE
, m
);
438 IoState_popRetainPoolExceptFor_(IOSTATE
, result
);
443 IoObject
*IoDBIResult_done(IoDBIResult
*self
, IoObject
*locals
, IoMessage
*m
)
446 docSlot("done", "Close and free the result. This <b>must</b> be called on
447 each result. Failure to do so will cause memory leaks and open queries with
448 the database server.")
450 if (0 != dbi_result_free(DATA(self
)->result
))
452 ReportDBIError(DATA(self
)->conn
, IOSTATE
, m
);
455 DATA(self
)->result
= NULL
;