1 /*
2 * UNIXODBC module result related functions
3 *
4 * Copyright (C) 2005-2006 Marco Lorrai
5 * Copyright (C) 2007-2008 1&1 Internet AG
6 *
7 * This file is part of Kamailio, a free SIP server.
8 *
9 * Kamailio is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version
13 *
14 * Kamailio is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 *
23 */
24
25 #include "../../core/mem/mem.h"
26 #include "../../core/dprint.h"
27 #include "row.h"
28 #include "../../lib/srdb1/db_res.h"
29 #include "connection.h"
30 #include "res.h"
31 #include "list.h"
32 #include <stdlib.h>
33 #include <string.h>
34
35 /*
36 * Get and convert columns from a result
37 */
db_unixodbc_get_columns(const db1_con_t * _h,db1_res_t * _r)38 int db_unixodbc_get_columns(const db1_con_t* _h, db1_res_t* _r)
39 {
40 int col;
41 SQLSMALLINT cols; /* because gcc don't like RES_COL_N */
42
43 if ((!_h) || (!_r)) {
44 LM_ERR("invalid parameter\n");
45 return -1;
46 }
47
48 /* Save number of columns in the result structure */
49 SQLNumResultCols(CON_RESULT(_h), &cols);
50 RES_COL_N(_r) = cols;
51 if (!RES_COL_N(_r)) {
52 LM_ERR("no columns returned from the query\n");
53 return -2;
54 } else {
55 LM_DBG("%d columns returned from the query\n", RES_COL_N(_r));
56 }
57
58 if (db_allocate_columns(_r, RES_COL_N(_r)) != 0) {
59 LM_ERR("could not allocate columns\n");
60 return -3;
61 }
62
63 for(col = 0; col < RES_COL_N(_r); col++)
64 {
65 RES_NAMES(_r)[col] = (str*)pkg_malloc(sizeof(str));
66 if (! RES_NAMES(_r)[col]) {
67 LM_ERR("no private memory left\n");
68 db_free_columns(_r);
69 return -4;
70 }
71 LM_DBG("allocate %lu bytes for RES_NAMES[%d] at %p\n",
72 (unsigned long)sizeof(str),col, RES_NAMES(_r)[col]);
73
74 char columnname[80];
75 SQLRETURN ret;
76 SQLSMALLINT namelength, datatype, decimaldigits, nullable;
77 SQLULEN columnsize;
78
79 ret = SQLDescribeCol(CON_RESULT(_h), col + 1, (SQLCHAR *)columnname, 80,
80 &namelength, &datatype, &columnsize, &decimaldigits, &nullable);
81 if(!SQL_SUCCEEDED(ret)) {
82 LM_ERR("SQLDescribeCol failed: %d\n", ret);
83 db_unixodbc_extract_error("SQLExecDirect", CON_RESULT(_h), SQL_HANDLE_STMT,
84 NULL);
85 // FIXME should we fail here completly?
86 }
87 /* The pointer that is here returned is part of the result structure. */
88 RES_NAMES(_r)[col]->s = columnname;
89 RES_NAMES(_r)[col]->len = namelength;
90
91 LM_DBG("RES_NAMES(%p)[%d]=[%.*s]\n", RES_NAMES(_r)[col], col,
92 RES_NAMES(_r)[col]->len, RES_NAMES(_r)[col]->s);
93
94 switch(datatype)
95 {
96 case SQL_SMALLINT:
97 case SQL_INTEGER:
98 case SQL_TINYINT:
99 case SQL_DECIMAL:
100 case SQL_NUMERIC:
101 LM_DBG("use DB1_INT result type\n");
102 RES_TYPES(_r)[col] = DB1_INT;
103 break;
104
105 case SQL_BIGINT:
106 LM_DBG("use DB1_BIGINT result type\n");
107 RES_TYPES(_r)[col] = DB1_BIGINT;
108 break;
109
110 case SQL_REAL:
111 case SQL_FLOAT:
112 case SQL_DOUBLE:
113 LM_DBG("use DB1_DOUBLE result type\n");
114 RES_TYPES(_r)[col] = DB1_DOUBLE;
115 break;
116
117 case SQL_TYPE_TIMESTAMP:
118 case SQL_DATE:
119 case SQL_TIME:
120 case SQL_TIMESTAMP:
121 case SQL_TYPE_DATE:
122 case SQL_TYPE_TIME:
123 LM_DBG("use DB1_DATETIME result type\n");
124 RES_TYPES(_r)[col] = DB1_DATETIME;
125 break;
126
127 case SQL_CHAR:
128 case SQL_VARCHAR:
129 case SQL_WCHAR:
130 case SQL_WVARCHAR:
131 LM_DBG("use DB1_STRING result type\n");
132 RES_TYPES(_r)[col] = DB1_STRING;
133 break;
134
135 case SQL_BINARY:
136 case SQL_VARBINARY:
137 case SQL_LONGVARBINARY:
138 case SQL_BIT:
139 case SQL_LONGVARCHAR:
140 case SQL_WLONGVARCHAR:
141 LM_DBG("use DB1_BLOB result type\n");
142 RES_TYPES(_r)[col] = DB1_BLOB;
143 break;
144
145 default:
146 LM_WARN("unhandled data type column (%.*s) type id (%d), "
147 "use DB1_STRING as default\n", RES_NAMES(_r)[col]->len,
148 RES_NAMES(_r)[col]->s, datatype);
149 RES_TYPES(_r)[col] = DB1_STRING;
150 break;
151 }
152 }
153 return 0;
154 }
155
156 /*
157 * Convert rows from UNIXODBC to db API representation
158 */
db_unixodbc_convert_rows(const db1_con_t * _h,db1_res_t * _r)159 static inline int db_unixodbc_convert_rows(const db1_con_t* _h, db1_res_t* _r)
160 {
161 int i = 0, ret = 0;
162 SQLSMALLINT columns;
163 list* rows = NULL;
164 list* rowstart = NULL;
165 strn* temp_row = NULL;
166
167 if((!_h) || (!_r)) {
168 LM_ERR("invalid parameter\n");
169 return -1;
170 }
171
172 SQLNumResultCols(CON_RESULT(_h), (SQLSMALLINT *)&columns);
173
174 while(SQL_SUCCEEDED(ret = SQLFetch(CON_RESULT(_h))))
175 {
176 temp_row = db_unixodbc_new_cellrow(columns);
177 if (!temp_row) {
178 LM_ERR("no private memory left\n");
179 return -1;
180 }
181
182 for(i=0; i < columns; i++)
183 {
184 if (!db_unixodbc_load_cell(_h, i+1, temp_row + i, RES_TYPES(_r)[i])) {
185 db_unixodbc_free_cellrow(columns, temp_row);
186 return -5;
187 }
188 }
189
190 if (db_unixodbc_list_insert(&rowstart, &rows, columns, temp_row) < 0) {
191 LM_ERR("insert failed\n");
192 db_unixodbc_free_cellrow(columns, temp_row);
193 return -5;
194 }
195 RES_ROW_N(_r)++;
196
197 /* free temporary row data */
198 db_unixodbc_free_cellrow(columns, temp_row);
199 temp_row = NULL;
200 }
201 CON_ROW(_h) = NULL;
202
203 if (!RES_ROW_N(_r)) {
204 RES_ROWS(_r) = 0;
205 return 0;
206 }
207
208 if (db_allocate_rows(_r) != 0) {
209 LM_ERR("could not allocate rows");
210 db_unixodbc_list_destroy(rowstart);
211 return -2;
212 }
213
214 i = 0;
215 rows = rowstart;
216 while(rows)
217 {
218 CON_ROW(_h) = rows->data;
219 if (!CON_ROW(_h))
220 {
221 LM_ERR("string null\n");
222 RES_ROW_N(_r) = i;
223 db_free_rows(_r);
224 db_unixodbc_list_destroy(rowstart);
225 return -3;
226 }
227 if (db_unixodbc_convert_row(_h, _r, &(RES_ROWS(_r)[i]), rows->lengths) < 0) {
228 LM_ERR("converting row failed #%d\n", i);
229 RES_ROW_N(_r) = i;
230 db_free_rows(_r);
231 db_unixodbc_list_destroy(rowstart);
232 return -4;
233 }
234 i++;
235 rows = rows->next;
236 }
237 db_unixodbc_list_destroy(rowstart);
238 return 0;
239 }
240
241
242 /*
243 * Fill the structure with data from database
244 */
db_unixodbc_convert_result(const db1_con_t * _h,db1_res_t * _r)245 int db_unixodbc_convert_result(const db1_con_t* _h, db1_res_t* _r)
246 {
247 if (!_h || !_r) {
248 LM_ERR("invalid parameter\n");
249 return -1;
250 }
251
252 if (db_unixodbc_get_columns(_h, _r) < 0) {
253 LM_ERR("getting column names failed\n");
254 return -2;
255 }
256
257 if (db_unixodbc_convert_rows(_h, _r) < 0) {
258 LM_ERR("converting rows failed\n");
259 db_free_columns(_r);
260 return -3;
261 }
262 return 0;
263 }
264