Merge kamailio modules into sip-router master branch
[sip-router] / modules / db_postgres / km_res.c
1 /*
2  * $Id$
3  *
4  * Copyright (C) 2003 August.Net Services, LLC
5  * Copyright (C) 2006 Norman Brandinger
6  * Copyright (C) 2008 1&1 Internet AG
7  *
8  * This file is part of Kamailio, a free SIP server.
9  *
10  * Kamailio is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version
14  *
15  * Kamailio is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License 
21  * along with this program; if not, write to the Free Software 
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  *
24  * History
25  * -------
26  * 2003-04-06 initial code written (Greg Fausak/Andy Fullford)
27  *
28  * 2006-07-26 added BPCHAROID as a valid type for DB1_STRING conversions
29  *            this removes the "unknown type 1042" log messages (norm)
30  *
31  * 2006-10-27 Added fetch support (norm)
32  *            Removed dependency on aug_* memory routines (norm)
33  *            Added connection pooling support (norm)
34  *            Standardized API routines to pg_* names (norm)
35  */
36
37 /*! \file
38  *  \brief DB_POSTGRES :: Core
39  *  \ingroup db_postgres
40  *  Module: \ref db_postgres
41  */
42
43 #include <stdlib.h>
44 #include <string.h>
45 #include "../../lib/srdb1/db_id.h"
46 #include "../../lib/srdb1/db_res.h"
47 #include "../../lib/srdb1/db_con.h"
48 #include "../../dprint.h"
49 #include "../../mem/mem.h"
50 #include "km_res.h"
51 #include "km_val.h"
52 #include "km_pg_con.h"
53 #include "km_pg_type.h"
54
55
56 /*!
57  * \brief Fill the result structure with data from the query
58  * \param _h database connection
59  * \param _r result set
60  * \return 0 on success, negative on error
61  */
62 int db_postgres_convert_result(const db1_con_t* _h, db1_res_t* _r)
63 {
64         if (!_h || !_r)  {
65                 LM_ERR("invalid parameter value\n");
66                 return -1;
67         }
68
69         if (db_postgres_get_columns(_h, _r) < 0) {
70                 LM_ERR("failed to get column names\n");
71                 return -2;
72         }
73
74         if (db_postgres_convert_rows(_h, _r) < 0) {
75                 LM_ERR("failed to convert rows\n");
76                 db_free_columns(_r);
77                 return -3;
78         }
79         return 0;
80 }
81
82
83 /*!
84  * \brief Get and convert columns from a result set
85  * \param _h database connection
86  * \param _r result set
87  * \return 0 on success, negative on error
88  */
89 int db_postgres_get_columns(const db1_con_t* _h, db1_res_t* _r)
90 {
91         int col, datatype;
92
93         if (!_h || !_r)  {
94                 LM_ERR("invalid parameter value\n");
95                 return -1;
96         }
97
98         /* Get the number of rows (tuples) in the query result. */
99         RES_ROW_N(_r) = PQntuples(CON_RESULT(_h));
100
101         /* Get the number of columns (fields) in each row of the query result. */
102         RES_COL_N(_r) = PQnfields(CON_RESULT(_h));
103
104         if (!RES_COL_N(_r)) {
105                 LM_DBG("no columns returned from the query\n");
106                 return -2;
107         } else {
108                 LM_DBG("%d columns returned from the query\n", RES_COL_N(_r));
109         }
110
111         if (db_allocate_columns(_r, RES_COL_N(_r)) != 0) {
112                 LM_ERR("could not allocate columns\n");
113                 return -3;
114         }
115
116         /* For each column both the name and the OID number of the data type are saved. */
117         for(col = 0; col < RES_COL_N(_r); col++) {
118
119                 RES_NAMES(_r)[col] = (str*)pkg_malloc(sizeof(str));
120                 if (! RES_NAMES(_r)[col]) {
121                         LM_ERR("no private memory left\n");
122                         db_free_columns(_r);
123                         return -4;
124                 }
125                 LM_DBG("allocate %d bytes for RES_NAMES[%d] at %p\n", (unsigned int) sizeof(str), col,
126                                 RES_NAMES(_r)[col]);
127
128                 /* The pointer that is here returned is part of the result structure. */
129                 RES_NAMES(_r)[col]->s = PQfname(CON_RESULT(_h), col);
130                 RES_NAMES(_r)[col]->len = strlen(PQfname(CON_RESULT(_h), col));
131
132                 LM_DBG("RES_NAMES(%p)[%d]=[%.*s]\n", RES_NAMES(_r)[col], col,
133                                 RES_NAMES(_r)[col]->len, RES_NAMES(_r)[col]->s);
134
135                 /* get the datatype of the column */
136                 switch(datatype = PQftype(CON_RESULT(_h),col))
137                 {
138                         case INT2OID:
139                         case INT4OID:
140                                 LM_DBG("use DB1_INT result type\n");
141                                 RES_TYPES(_r)[col] = DB1_INT;
142                         break;
143
144                         case INT8OID:
145                                 LM_DBG("use DB1_BIGINT result type\n");
146                                 RES_TYPES(_r)[col] = DB1_BIGINT;
147
148                         case FLOAT4OID:
149                         case FLOAT8OID:
150                         case NUMERICOID:
151                                 LM_DBG("use DB1_DOUBLE result type\n");
152                                 RES_TYPES(_r)[col] = DB1_DOUBLE;
153                         break;
154
155                         case DATEOID:
156                         case TIMESTAMPOID:
157                         case TIMESTAMPTZOID:
158                                 LM_DBG("use DB1_DATETIME result type\n");
159                                 RES_TYPES(_r)[col] = DB1_DATETIME;
160                         break;
161
162                         case BOOLOID:
163                         case CHAROID:
164                         case VARCHAROID:
165                         case BPCHAROID:
166                                 LM_DBG("use DB1_STRING result type\n");
167                                 RES_TYPES(_r)[col] = DB1_STRING;
168                         break;
169
170                         case TEXTOID:
171                         case BYTEAOID:
172                                 LM_DBG("use DB1_BLOB result type\n");
173                                 RES_TYPES(_r)[col] = DB1_BLOB;
174                         break;
175
176                         case BITOID:
177                         case VARBITOID:
178                                 LM_DBG("use DB1_BITMAP result type\n");
179                                 RES_TYPES(_r)[col] = DB1_BITMAP;
180                         break;
181                                 
182                         default:
183                                 LM_WARN("unhandled data type column (%.*s) type id (%d), "
184                                                 "use DB1_STRING as default\n", RES_NAMES(_r)[col]->len,
185                                                 RES_NAMES(_r)[col]->s, datatype);
186                                 RES_TYPES(_r)[col] = DB1_STRING;
187                         break;
188                 }
189         }
190         return 0;
191 }
192
193
194 /*!
195  * \brief Convert rows from PostgreSQL to db API representation
196  * \param _h database connection
197  * \param _r result set
198  * \return 0 on success, negative on error
199  */
200 int db_postgres_convert_rows(const db1_con_t* _h, db1_res_t* _r)
201 {
202         char **row_buf, *s;
203         int row, col, len;
204
205         if (!_h || !_r)  {
206                 LM_ERR("invalid parameter\n");
207                 return -1;
208         }
209
210         if (!RES_ROW_N(_r)) {
211                 LM_DBG("no rows returned from the query\n");
212                 RES_ROWS(_r) = 0;
213                 return 0;
214         }
215         /*Allocate an array of pointers per column to holds the string representation */
216         len = sizeof(char *) * RES_COL_N(_r);
217         row_buf = (char**)pkg_malloc(len);
218         if (!row_buf) {
219                 LM_ERR("no private memory left\n");
220                 return -1;
221         }
222         LM_DBG("allocate for %d columns %d bytes in row buffer at %p\n", RES_COL_N(_r), len, row_buf);
223         memset(row_buf, 0, len);
224
225         if (db_allocate_rows(_r) < 0) {
226                 LM_ERR("could not allocate rows\n");
227                 LM_DBG("freeing row buffer at %p\n", row_buf);
228                 pkg_free(row_buf);
229                 return -2;
230         }
231
232         for(row = RES_LAST_ROW(_r); row < (RES_LAST_ROW(_r) + RES_ROW_N(_r)); row++) {
233                 for(col = 0; col < RES_COL_N(_r); col++) {
234                         /*
235                          * The row data pointer returned by PQgetvalue points to storage
236                          * that is part of the PGresult structure. One should not modify
237                          * the data it points to, and one must explicitly copy the data
238                          * into other storage if it is to be used past the lifetime of
239                          * the PGresult structure itself.
240                          */
241                         s = PQgetvalue(CON_RESULT(_h), row, col);
242                         LM_DBG("PQgetvalue(%p,%d,%d)=[%s]\n", _h, row, col, s);
243                         /*
244                          * A empty string can be a NULL value, or just an empty string.
245                          * This differs from the mysql behaviour, that further processing
246                          * steps expect. So we need to simulate this here unfortunally.
247                          */
248                         if (PQgetisnull(CON_RESULT(_h), row, col) == 0) {
249                                 row_buf[col] = s;
250                                 LM_DBG("[%d][%d] Column[%.*s]=[%s]\n",
251                                         row, col, RES_NAMES(_r)[col]->len, RES_NAMES(_r)[col]->s, row_buf[col]);
252                         }
253                 }
254
255                 /* ASSERT: row_buf contains an entire row in strings */
256                 if(db_postgres_convert_row(_h, _r, &(RES_ROWS(_r)[row - RES_LAST_ROW(_r)]), row_buf)<0){
257                         LM_ERR("failed to convert row #%d\n",  row);
258                         RES_ROW_N(_r) = row - RES_LAST_ROW(_r);
259                         LM_DBG("freeing row buffer at %p\n", row_buf);
260                         pkg_free(row_buf);
261                         db_free_rows(_r);
262                         return -4;
263                 }
264         }
265
266         LM_DBG("freeing row buffer at %p\n", row_buf);
267         pkg_free(row_buf);
268         row_buf = NULL;
269         return 0;
270 }
271
272
273 /*!
274  * \brief Convert a row from the result query into db API representation
275  * \param _h database connection
276  * \param _r result set
277  * \param _row row
278  * \param row_buf row buffer
279  * \return 0 on success, negative on error
280  */
281 int db_postgres_convert_row(const db1_con_t* _h, db1_res_t* _r, db_row_t* _row,
282                 char **row_buf)
283 {
284         int col, col_len;
285
286         if (!_h || !_r || !_row)  {
287                 LM_ERR("invalid parameter value\n");
288                 return -1;
289         }
290
291         if (db_allocate_row(_r, _row) != 0) {
292                 LM_ERR("could not allocate row\n");
293                 return -2;
294         }
295
296         /* For each column in the row */
297         for(col = 0; col < ROW_N(_row); col++) {
298                 /* because it can contain NULL */
299                 if (!row_buf[col]) {
300                         col_len = 0;
301                 } else {
302                         col_len = strlen(row_buf[col]);
303                 }
304                 /* Convert the string representation into the value representation */
305                 if (db_postgres_str2val(RES_TYPES(_r)[col], &(ROW_VALUES(_row)[col]),
306                 row_buf[col], col_len) < 0) {
307                         LM_ERR("failed to convert value\n");
308                         LM_DBG("free row at %p\n", _row);
309                         db_free_row(_row);
310                         return -3;
311                 }
312         }
313         return 0;
314 }