Removed all code that uses OpenGL from Image.
[io/quag.git] / addons / DBI / source / IoDBIResult.c
blobf34390e542718ad7446701af8e7fc5f03d10007f
1 /*#io
2 DBIResult ioDoc(
3 docCopyright("Jeremy Cowgar", 2006)
4 docLicense("BSD revised")
5 docCategory("Databases")
6 docObject("DBIResult")
7 docDescription("A DBI Result created by a call to DBIConn query.")
8 */
10 #include "IoMessage.h"
11 #include "IoState.h"
12 #include "IoNumber.h"
13 #include "IoList.h"
14 #include "IoDate.h"
15 #include "IoMap.h"
17 #include "IoDBI.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);
30 return tag;
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},
58 {NULL, NULL},
60 IoObject_addMethodTable_(self, methodTable);
63 return self;
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;
72 return self;
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,
87 IoDBIResult_proto));
89 DATA(self)->result = result;
90 DATA(self)->conn = dbi_result_get_conn(result);
92 return self;
95 IoObject *IoDBIResult_getIoObjectFromResult_(IoObject *self, dbi_result res,
96 int index)
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)
110 return IONIL(self);
111 else
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(
117 res, index));
120 return IONIL(self);
123 IoObject *IoDBIResult_rowToMap_(void *state, IoDBIResult *self,
124 dbi_result res)
126 int fIdx, fCount = dbi_result_get_numfields(res);
127 IoMap *map = IoMap_new(state);
129 for (fIdx = 1; fIdx <= fCount; fIdx++)
131 IoMap_rawAtPut(map,
132 IOSYMBOL(dbi_result_get_field_name(res, fIdx)),
133 IoDBIResult_getIoObjectFromResult_(self, res, fIdx));
136 return map;
139 /* ---------------------------------------------------------------- */
141 IoObject *IoDBIResult_size(IoDBIResult *self, IoObject *locals,
142 IoMessage *m)
144 /*#io
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,
151 IoMessage *m)
153 /*#io
154 docSlot("fields", "Returns a list of field names in the result")
156 int idx;
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)));
165 return list;
168 IoObject *IoDBIResult_first(IoDBIResult *self, IoObject *locals,
169 IoMessage *m)
171 /*#io
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,
183 IoMessage *m)
185 /*#io
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,
197 IoMessage *m)
199 /*#io
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);
209 return IOBOOL(self,
210 dbi_result_get_currow(res) < dbi_result_get_numrows(res));
213 IoObject *IoDBIResult_last(IoDBIResult *self, IoObject *locals,
214 IoMessage *m)
216 /*#io
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,
231 IoMessage *m)
233 /*#io
234 docSlot("seek(row_number)", "Move the cursor to the nth record")
236 long rowIdx;
237 dbi_result res = DATA(self)->result;
238 IoObject *row = IoMessage_locals_valueArgAt_(m, locals, 0);
240 if (!ISNUMBER(row))
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))
250 const char *error;
251 int errorCode = dbi_conn_error(DATA(self)->conn,
252 &error);
254 if (errorCode == 0)
256 IoState_error_(IOSTATE, m, "row index %i out of range (1,%i)\n",
257 rowIdx, dbi_result_get_numrows(res));
259 else
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)
271 /*#io
272 docSlot("position", "Return the current row's position (or index).")
274 unsigned long long rowNum = dbi_result_get_currow(DATA(self)->result);
276 if (0 == rowNum)
278 ReportDBIError(DATA(self)->conn, IOSTATE, m);
281 return IONUMBER(rowNum);
284 IoObject *IoDBIResult_at(IoDBIResult *self, IoObject *locals, IoMessage *m)
286 /*#io
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);
294 if (ISNUMBER(key))
296 idx = IoNumber_asInt(key);
298 else if (ISSYMBOL(key))
300 idx = dbi_result_get_field_idx(res, CSTRING(key));
301 if (0 == idx)
303 ReportDBIError(DATA(self)->conn, IOSTATE, m);
306 else
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,
317 IoMessage *m)
319 /*#io
320 docSlot("populate(object)", "Populate a decendent of DBIRecord with the
321 current record's contents. See `DBIRecord' for further explanation and an
322 example.")
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));
332 return o;
335 IoObject *IoDBIResult_foreach(IoDBIResult *self, IoObject *locals,
336 IoMessage *m)
338 /*#io
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.
343 <pre>
344 r := conn query("SELECT * FROM people")
345 r foreach(r, r at(1))
346 </pre>
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:
352 <pre>
353 r := conn query("SELECT * FROM people")
354 r seek(4)
355 r foreach(r, r at (1))
356 </pre>
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.
363 """)
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);
385 else
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);
406 if (i == 0)
408 ReportDBIError(DATA(self)->conn, IOSTATE, m);
411 if (baseObject)
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);
420 else {
421 IoObject_setSlot_to_(locals, resSlotName, self);
424 result = IoMessage_locals_performOn_(doMessage, locals, locals);
426 if (IoState_handleStatus(IOSTATE))
428 goto done;
431 if (i != count && 0 == dbi_result_next_row(DATA(self)->result))
433 ReportDBIError(DATA(self)->conn, IOSTATE, m);
437 done:
438 IoState_popRetainPoolExceptFor_(IOSTATE, result);
440 return result;
443 IoObject *IoDBIResult_done(IoDBIResult *self, IoObject *locals, IoMessage *m)
445 /*#io
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;
457 return IONIL(self);