1 module mysql.mysql_result;
2 
3 import std.stdio;
4 import std.exception;
5 
6 import mysql.binding;
7 import mysql.mysql_row;
8 
9 class MysqlResult {
10     private int[string] mapping;
11     public MYSQL_RES* result;
12 
13     private int itemsTotal;
14     private int itemsUsed;
15 
16     bool[] columnIsNull;
17     MysqlRow row;
18 
19     string sql;
20 
21     this(MYSQL_RES* r, string sql) {
22         result = r;
23         itemsTotal = length();
24         itemsUsed = 0;
25 
26         this.sql = sql;
27 
28         // prime it
29         if (itemsTotal) {
30             fetchNext();
31         }
32     }
33 
34     ~this() {
35         if (result !is null) {
36             mysql_free_result(result);
37         }
38     }
39 
40 
41     MYSQL_FIELD[] fields() {
42         int numFields = mysql_num_fields(result);
43         auto fields = mysql_fetch_fields(result);
44         MYSQL_FIELD[] ret;
45         for(int i = 0; i < numFields; i++) {
46             ret ~= fields[i];
47         }
48 
49         return ret;
50     }
51 
52 
53     int length() {
54         if (result is null) {
55             return 0;
56         }
57         return cast(int) mysql_num_rows(result);
58     }
59 
60     bool empty() {
61         return itemsUsed == itemsTotal;
62     }
63 
64     MysqlRow front() {
65         return row;
66     }
67 
68     void popFront() {
69         itemsUsed++;
70         if (itemsUsed < itemsTotal) {
71             fetchNext();
72         }
73     }
74 
75     int getFieldIndex(string field) {
76         if (mapping is null) {
77             makeFieldMapping();
78         } debug {
79             if (field !in mapping) {
80                 throw new Exception(field ~ " not in result");
81             }
82         }
83         return mapping[field];
84     }
85 
86     string[] fieldNames() {
87         int numFields = mysql_num_fields(result);
88         auto fields = mysql_fetch_fields(result);
89 
90         string[] names;
91         for(int i = 0; i < numFields; i++) {
92             names ~= fromCstring(fields[i].name, fields[i].name_length);
93         }
94 
95         return names;
96     }
97 
98     private void makeFieldMapping() {
99         int numFields = mysql_num_fields(result);
100         auto fields = mysql_fetch_fields(result);
101 
102         if (fields is null) {
103             return;
104         }
105 
106         for(int i = 0; i < numFields; i++) {
107             if (fields[i].name !is null) {
108                 mapping[fromCstring(fields[i].name, fields[i].name_length)] = i;
109             }
110         }
111     }
112 
113     private void fetchNext() {
114         assert(result);
115         auto my_row = mysql_fetch_row(result);
116         if (my_row is null) {
117             throw new Exception("there is no next row");
118         }
119         uint numFields = mysql_num_fields(result);
120         uint* lengths = mysql_fetch_lengths(result);
121         string[] row;
122         // potential FIXME: not really binary safe
123 
124         columnIsNull.length = numFields;
125         for(uint numField = 0; numField < numFields; numField++) {
126           //writefln("numField %d", numField);
127             if (my_row[numField] is null) {
128                 row ~= null;
129                 columnIsNull[numField] = true;
130             } else {
131                 row ~= cast(string) cstr2dstr(my_row[numField], lengths[numField]);
132                 columnIsNull[numField] = false;
133             }
134         }
135 
136         this.row.row = row;
137         this.row.resultSet = this;
138     }
139 }