00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040 #include "asterisk.h"
00041
00042 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 299130 $")
00043
00044 #include <time.h>
00045
00046 #include <libpq-fe.h>
00047
00048 #include "asterisk/config.h"
00049 #include "asterisk/channel.h"
00050 #include "asterisk/cdr.h"
00051 #include "asterisk/module.h"
00052
00053 #define DATE_FORMAT "'%Y-%m-%d %T'"
00054
00055 static char *name = "pgsql";
00056 static char *config = "cdr_pgsql.conf";
00057 static char *pghostname = NULL, *pgdbname = NULL, *pgdbuser = NULL, *pgpassword = NULL, *pgdbport = NULL, *table = NULL, *encoding = NULL;
00058 static int connected = 0;
00059 static int maxsize = 512, maxsize2 = 512;
00060
00061 AST_MUTEX_DEFINE_STATIC(pgsql_lock);
00062
00063 static PGconn *conn = NULL;
00064
00065 struct columns {
00066 char *name;
00067 char *type;
00068 int len;
00069 unsigned int notnull:1;
00070 unsigned int hasdefault:1;
00071 AST_RWLIST_ENTRY(columns) list;
00072 };
00073
00074 static AST_RWLIST_HEAD_STATIC(psql_columns, columns);
00075
00076 #define LENGTHEN_BUF1(size) \
00077 do { \
00078 \
00079 if (ast_str_strlen(sql) + size + 1 > ast_str_size(sql)) { \
00080 if (ast_str_make_space(&sql, ((ast_str_size(sql) + size + 3) / 512 + 1) * 512) != 0) { \
00081 ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR failed.\n"); \
00082 ast_free(sql); \
00083 ast_free(sql2); \
00084 AST_RWLIST_UNLOCK(&psql_columns); \
00085 return -1; \
00086 } \
00087 } \
00088 } while (0)
00089
00090 #define LENGTHEN_BUF2(size) \
00091 do { \
00092 if (ast_str_strlen(sql2) + size + 1 > ast_str_size(sql2)) { \
00093 if (ast_str_make_space(&sql2, ((ast_str_size(sql2) + size + 3) / 512 + 1) * 512) != 0) { \
00094 ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR failed.\n"); \
00095 ast_free(sql); \
00096 ast_free(sql2); \
00097 AST_RWLIST_UNLOCK(&psql_columns); \
00098 return -1; \
00099 } \
00100 } \
00101 } while (0)
00102
00103 static int pgsql_log(struct ast_cdr *cdr)
00104 {
00105 struct ast_tm tm;
00106 char *pgerror;
00107 PGresult *result;
00108
00109 ast_mutex_lock(&pgsql_lock);
00110
00111 if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) {
00112 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
00113 if (PQstatus(conn) != CONNECTION_BAD) {
00114 connected = 1;
00115 if (PQsetClientEncoding(conn, encoding)) {
00116 #ifdef HAVE_PGSQL_pg_encoding_to_char
00117 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn)));
00118 #else
00119 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default.\n", encoding);
00120 #endif
00121 }
00122 } else {
00123 pgerror = PQerrorMessage(conn);
00124 ast_log(LOG_ERROR, "Unable to connect to database server %s. Calls will not be logged!\n", pghostname);
00125 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00126 PQfinish(conn);
00127 conn = NULL;
00128 }
00129 }
00130
00131 if (connected) {
00132 struct columns *cur;
00133 struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
00134 char buf[257], escapebuf[513], *value;
00135 int first = 1;
00136
00137 if (!sql || !sql2) {
00138 if (sql) {
00139 ast_free(sql);
00140 }
00141 if (sql2) {
00142 ast_free(sql2);
00143 }
00144 return -1;
00145 }
00146
00147 ast_str_set(&sql, 0, "INSERT INTO %s (", table);
00148 ast_str_set(&sql2, 0, " VALUES (");
00149
00150 AST_RWLIST_RDLOCK(&psql_columns);
00151 AST_RWLIST_TRAVERSE(&psql_columns, cur, list) {
00152
00153 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00154 if (strcmp(cur->name, "calldate") == 0 && !value) {
00155 ast_cdr_getvar(cdr, "start", &value, buf, sizeof(buf), 0, 0);
00156 }
00157 if (!value) {
00158 if (cur->notnull && !cur->hasdefault) {
00159
00160 LENGTHEN_BUF1(strlen(cur->name) + 2);
00161 ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
00162 LENGTHEN_BUF2(3);
00163 ast_str_append(&sql2, 0, "%s''", first ? "" : ",");
00164 first = 0;
00165 }
00166 continue;
00167 }
00168
00169 LENGTHEN_BUF1(strlen(cur->name) + 2);
00170 ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
00171
00172 if (strcmp(cur->name, "start") == 0 || strcmp(cur->name, "calldate") == 0) {
00173 if (strncmp(cur->type, "int", 3) == 0) {
00174 LENGTHEN_BUF2(13);
00175 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->start.tv_sec);
00176 } else if (strncmp(cur->type, "float", 5) == 0) {
00177 LENGTHEN_BUF2(31);
00178 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->start.tv_sec + (double)cdr->start.tv_usec / 1000000.0);
00179 } else {
00180
00181 LENGTHEN_BUF2(31);
00182 ast_localtime(&cdr->start, &tm, NULL);
00183 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00184 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00185 }
00186 } else if (strcmp(cur->name, "answer") == 0) {
00187 if (strncmp(cur->type, "int", 3) == 0) {
00188 LENGTHEN_BUF2(13);
00189 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->answer.tv_sec);
00190 } else if (strncmp(cur->type, "float", 5) == 0) {
00191 LENGTHEN_BUF2(31);
00192 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->answer.tv_sec + (double)cdr->answer.tv_usec / 1000000.0);
00193 } else {
00194
00195 LENGTHEN_BUF2(31);
00196 ast_localtime(&cdr->answer, &tm, NULL);
00197 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00198 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00199 }
00200 } else if (strcmp(cur->name, "end") == 0) {
00201 if (strncmp(cur->type, "int", 3) == 0) {
00202 LENGTHEN_BUF2(13);
00203 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->end.tv_sec);
00204 } else if (strncmp(cur->type, "float", 5) == 0) {
00205 LENGTHEN_BUF2(31);
00206 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->end.tv_sec + (double)cdr->end.tv_usec / 1000000.0);
00207 } else {
00208
00209 LENGTHEN_BUF2(31);
00210 ast_localtime(&cdr->end, &tm, NULL);
00211 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00212 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00213 }
00214 } else if (strcmp(cur->name, "duration") == 0 || strcmp(cur->name, "billsec") == 0) {
00215 if (cur->type[0] == 'i') {
00216
00217 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00218 LENGTHEN_BUF2(13);
00219 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
00220 } else if (strncmp(cur->type, "float", 5) == 0) {
00221 struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer;
00222 LENGTHEN_BUF2(31);
00223 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->end.tv_sec - when->tv_sec + cdr->end.tv_usec / 1000000.0 - when->tv_usec / 1000000.0);
00224 } else {
00225
00226 struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer;
00227 LENGTHEN_BUF2(31);
00228 ast_str_append(&sql2, 0, "%s'%f'", first ? "" : ",", (double)cdr->end.tv_sec - when->tv_sec + cdr->end.tv_usec / 1000000.0 - when->tv_usec / 1000000.0);
00229 }
00230 } else if (strcmp(cur->name, "disposition") == 0 || strcmp(cur->name, "amaflags") == 0) {
00231 if (strncmp(cur->type, "int", 3) == 0) {
00232
00233 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 1);
00234 LENGTHEN_BUF2(13);
00235 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
00236 } else {
00237
00238 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00239 LENGTHEN_BUF2(31);
00240 ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", value);
00241 }
00242 } else {
00243
00244 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00245 if (strncmp(cur->type, "int", 3) == 0) {
00246 long long whatever;
00247 if (value && sscanf(value, "%30lld", &whatever) == 1) {
00248 LENGTHEN_BUF2(26);
00249 ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", whatever);
00250 } else {
00251 LENGTHEN_BUF2(2);
00252 ast_str_append(&sql2, 0, "%s0", first ? "" : ",");
00253 }
00254 } else if (strncmp(cur->type, "float", 5) == 0) {
00255 long double whatever;
00256 if (value && sscanf(value, "%30Lf", &whatever) == 1) {
00257 LENGTHEN_BUF2(51);
00258 ast_str_append(&sql2, 0, "%s%30Lf", first ? "" : ",", whatever);
00259 } else {
00260 LENGTHEN_BUF2(2);
00261 ast_str_append(&sql2, 0, "%s0", first ? "" : ",");
00262 }
00263
00264 } else {
00265 if (value)
00266 PQescapeStringConn(conn, escapebuf, value, strlen(value), NULL);
00267 else
00268 escapebuf[0] = '\0';
00269 LENGTHEN_BUF2(strlen(escapebuf) + 3);
00270 ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", escapebuf);
00271 }
00272 }
00273 first = 0;
00274 }
00275 AST_RWLIST_UNLOCK(&psql_columns);
00276 LENGTHEN_BUF1(ast_str_strlen(sql2) + 2);
00277 ast_str_append(&sql, 0, ")%s)", ast_str_buffer(sql2));
00278 ast_verb(11, "[%s]\n", ast_str_buffer(sql));
00279
00280 ast_debug(2, "inserting a CDR record.\n");
00281
00282
00283
00284
00285 if (PQstatus(conn) == CONNECTION_OK) {
00286 connected = 1;
00287 } else {
00288 ast_log(LOG_ERROR, "Connection was lost... attempting to reconnect.\n");
00289 PQreset(conn);
00290 if (PQstatus(conn) == CONNECTION_OK) {
00291 ast_log(LOG_ERROR, "Connection reestablished.\n");
00292 connected = 1;
00293 } else {
00294 pgerror = PQerrorMessage(conn);
00295 ast_log(LOG_ERROR, "Unable to reconnect to database server %s. Calls will not be logged!\n", pghostname);
00296 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00297 PQfinish(conn);
00298 conn = NULL;
00299 connected = 0;
00300 ast_mutex_unlock(&pgsql_lock);
00301 ast_free(sql);
00302 ast_free(sql2);
00303 return -1;
00304 }
00305 }
00306 result = PQexec(conn, ast_str_buffer(sql));
00307 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
00308 pgerror = PQresultErrorMessage(result);
00309 ast_log(LOG_ERROR, "Failed to insert call detail record into database!\n");
00310 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00311 ast_log(LOG_ERROR, "Connection may have been lost... attempting to reconnect.\n");
00312 PQreset(conn);
00313 if (PQstatus(conn) == CONNECTION_OK) {
00314 ast_log(LOG_ERROR, "Connection reestablished.\n");
00315 connected = 1;
00316 PQclear(result);
00317 result = PQexec(conn, ast_str_buffer(sql));
00318 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
00319 pgerror = PQresultErrorMessage(result);
00320 ast_log(LOG_ERROR, "HARD ERROR! Attempted reconnection failed. DROPPING CALL RECORD!\n");
00321 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00322 }
00323 }
00324 ast_mutex_unlock(&pgsql_lock);
00325 PQclear(result);
00326 ast_free(sql);
00327 ast_free(sql2);
00328 return -1;
00329 }
00330 PQclear(result);
00331 ast_free(sql);
00332 ast_free(sql2);
00333 }
00334 ast_mutex_unlock(&pgsql_lock);
00335 return 0;
00336 }
00337
00338 static int unload_module(void)
00339 {
00340 struct columns *current;
00341 ast_cdr_unregister(name);
00342
00343
00344 usleep(1);
00345 PQfinish(conn);
00346
00347 if (pghostname)
00348 ast_free(pghostname);
00349 if (pgdbname)
00350 ast_free(pgdbname);
00351 if (pgdbuser)
00352 ast_free(pgdbuser);
00353 if (pgpassword)
00354 ast_free(pgpassword);
00355 if (pgdbport)
00356 ast_free(pgdbport);
00357 if (table)
00358 ast_free(table);
00359
00360 AST_RWLIST_WRLOCK(&psql_columns);
00361 while ((current = AST_RWLIST_REMOVE_HEAD(&psql_columns, list))) {
00362 ast_free(current);
00363 }
00364 AST_RWLIST_UNLOCK(&psql_columns);
00365
00366 return 0;
00367 }
00368
00369 static int config_module(int reload)
00370 {
00371 struct ast_variable *var;
00372 char *pgerror;
00373 struct columns *cur;
00374 PGresult *result;
00375 const char *tmp;
00376 struct ast_config *cfg;
00377 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00378
00379 if ((cfg = ast_config_load(config, config_flags)) == NULL || cfg == CONFIG_STATUS_FILEINVALID) {
00380 ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config);
00381 return -1;
00382 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
00383 return 0;
00384
00385 if (!(var = ast_variable_browse(cfg, "global"))) {
00386 ast_config_destroy(cfg);
00387 return 0;
00388 }
00389
00390 if (!(tmp = ast_variable_retrieve(cfg, "global", "hostname"))) {
00391 ast_log(LOG_WARNING, "PostgreSQL server hostname not specified. Assuming unix socket connection\n");
00392 tmp = "";
00393 }
00394
00395 if (pghostname)
00396 ast_free(pghostname);
00397 if (!(pghostname = ast_strdup(tmp))) {
00398 ast_config_destroy(cfg);
00399 return -1;
00400 }
00401
00402 if (!(tmp = ast_variable_retrieve(cfg, "global", "dbname"))) {
00403 ast_log(LOG_WARNING, "PostgreSQL database not specified. Assuming asterisk\n");
00404 tmp = "asteriskcdrdb";
00405 }
00406
00407 if (pgdbname)
00408 ast_free(pgdbname);
00409 if (!(pgdbname = ast_strdup(tmp))) {
00410 ast_config_destroy(cfg);
00411 return -1;
00412 }
00413
00414 if (!(tmp = ast_variable_retrieve(cfg, "global", "user"))) {
00415 ast_log(LOG_WARNING, "PostgreSQL database user not specified. Assuming asterisk\n");
00416 tmp = "asterisk";
00417 }
00418
00419 if (pgdbuser)
00420 ast_free(pgdbuser);
00421 if (!(pgdbuser = ast_strdup(tmp))) {
00422 ast_config_destroy(cfg);
00423 return -1;
00424 }
00425
00426 if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) {
00427 ast_log(LOG_WARNING, "PostgreSQL database password not specified. Assuming blank\n");
00428 tmp = "";
00429 }
00430
00431 if (pgpassword)
00432 ast_free(pgpassword);
00433 if (!(pgpassword = ast_strdup(tmp))) {
00434 ast_config_destroy(cfg);
00435 return -1;
00436 }
00437
00438 if (!(tmp = ast_variable_retrieve(cfg, "global", "port"))) {
00439 ast_log(LOG_WARNING, "PostgreSQL database port not specified. Using default 5432.\n");
00440 tmp = "5432";
00441 }
00442
00443 if (pgdbport)
00444 ast_free(pgdbport);
00445 if (!(pgdbport = ast_strdup(tmp))) {
00446 ast_config_destroy(cfg);
00447 return -1;
00448 }
00449
00450 if (!(tmp = ast_variable_retrieve(cfg, "global", "table"))) {
00451 ast_log(LOG_WARNING, "CDR table not specified. Assuming cdr\n");
00452 tmp = "cdr";
00453 }
00454
00455 if (table)
00456 ast_free(table);
00457 if (!(table = ast_strdup(tmp))) {
00458 ast_config_destroy(cfg);
00459 return -1;
00460 }
00461
00462 if (!(tmp = ast_variable_retrieve(cfg, "global", "encoding"))) {
00463 ast_log(LOG_WARNING, "Encoding not specified. Assuming LATIN9\n");
00464 tmp = "LATIN9";
00465 }
00466
00467 if (encoding) {
00468 ast_free(encoding);
00469 }
00470 if (!(encoding = ast_strdup(tmp))) {
00471 ast_config_destroy(cfg);
00472 return -1;
00473 }
00474
00475 if (option_debug) {
00476 if (ast_strlen_zero(pghostname)) {
00477 ast_debug(1, "using default unix socket\n");
00478 } else {
00479 ast_debug(1, "got hostname of %s\n", pghostname);
00480 }
00481 ast_debug(1, "got port of %s\n", pgdbport);
00482 ast_debug(1, "got user of %s\n", pgdbuser);
00483 ast_debug(1, "got dbname of %s\n", pgdbname);
00484 ast_debug(1, "got password of %s\n", pgpassword);
00485 ast_debug(1, "got sql table name of %s\n", table);
00486 }
00487
00488 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
00489 if (PQstatus(conn) != CONNECTION_BAD) {
00490 char sqlcmd[768];
00491 char *fname, *ftype, *flen, *fnotnull, *fdef;
00492 int i, rows, version;
00493 ast_debug(1, "Successfully connected to PostgreSQL database.\n");
00494 connected = 1;
00495 if (PQsetClientEncoding(conn, encoding)) {
00496 #ifdef HAVE_PGSQL_pg_encoding_to_char
00497 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn)));
00498 #else
00499 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default.\n", encoding);
00500 #endif
00501 }
00502 version = PQserverVersion(conn);
00503
00504 if (version >= 70300) {
00505 char *schemaname, *tablename;
00506 if (strchr(table, '.')) {
00507 schemaname = ast_strdupa(table);
00508 tablename = strchr(schemaname, '.');
00509 *tablename++ = '\0';
00510 } else {
00511 schemaname = "";
00512 tablename = table;
00513 }
00514
00515
00516 if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) {
00517 char *tmp = schemaname, *ptr;
00518
00519 ptr = schemaname = alloca(strlen(tmp) * 2 + 1);
00520 for (; *tmp; tmp++) {
00521 if (strchr("\\'", *tmp)) {
00522 *ptr++ = *tmp;
00523 }
00524 *ptr++ = *tmp;
00525 }
00526 *ptr = '\0';
00527 }
00528
00529 if (strchr(tablename, '\\') || strchr(tablename, '\'')) {
00530 char *tmp = tablename, *ptr;
00531
00532 ptr = tablename = alloca(strlen(tmp) * 2 + 1);
00533 for (; *tmp; tmp++) {
00534 if (strchr("\\'", *tmp)) {
00535 *ptr++ = *tmp;
00536 }
00537 *ptr++ = *tmp;
00538 }
00539 *ptr = '\0';
00540 }
00541
00542 snprintf(sqlcmd, sizeof(sqlcmd), "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM (((pg_catalog.pg_class c INNER JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace AND c.relname = '%s' AND n.nspname = %s%s%s) INNER JOIN pg_catalog.pg_attribute a ON (NOT a.attisdropped) AND a.attnum > 0 AND a.attrelid = c.oid) INNER JOIN pg_catalog.pg_type t ON t.oid = a.atttypid) LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum ORDER BY n.nspname, c.relname, attnum",
00543 tablename,
00544 ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'");
00545 } else {
00546 snprintf(sqlcmd, sizeof(sqlcmd), "SELECT a.attname, t.typname, a.attlen, a.attnotnull, d.adsrc, a.atttypmod FROM pg_class c, pg_type t, pg_attribute a LEFT OUTER JOIN pg_attrdef d ON a.atthasdef AND d.adrelid = a.attrelid AND d.adnum = a.attnum WHERE c.oid = a.attrelid AND a.atttypid = t.oid AND (a.attnum > 0) AND c.relname = '%s' ORDER BY c.relname, attnum", table);
00547 }
00548
00549 result = PQexec(conn, sqlcmd);
00550 if (PQresultStatus(result) != PGRES_TUPLES_OK) {
00551 pgerror = PQresultErrorMessage(result);
00552 ast_log(LOG_ERROR, "Failed to query database columns: %s\n", pgerror);
00553 PQclear(result);
00554 unload_module();
00555 return AST_MODULE_LOAD_DECLINE;
00556 }
00557
00558 rows = PQntuples(result);
00559 if (rows == 0) {
00560 ast_log(LOG_ERROR, "cdr_pgsql: Failed to query database columns. No columns found, does the table exist?\n");
00561 PQclear(result);
00562 unload_module();
00563 return AST_MODULE_LOAD_DECLINE;
00564 }
00565
00566 for (i = 0; i < rows; i++) {
00567 fname = PQgetvalue(result, i, 0);
00568 ftype = PQgetvalue(result, i, 1);
00569 flen = PQgetvalue(result, i, 2);
00570 fnotnull = PQgetvalue(result, i, 3);
00571 fdef = PQgetvalue(result, i, 4);
00572 if (atoi(flen) == -1) {
00573
00574 flen = PQgetvalue(result, i, 5);
00575 }
00576 ast_verb(4, "Found column '%s' of type '%s'\n", fname, ftype);
00577 cur = ast_calloc(1, sizeof(*cur) + strlen(fname) + strlen(ftype) + 2);
00578 if (cur) {
00579 sscanf(flen, "%30d", &cur->len);
00580 cur->name = (char *)cur + sizeof(*cur);
00581 cur->type = (char *)cur + sizeof(*cur) + strlen(fname) + 1;
00582 strcpy(cur->name, fname);
00583 strcpy(cur->type, ftype);
00584 if (*fnotnull == 't') {
00585 cur->notnull = 1;
00586 } else {
00587 cur->notnull = 0;
00588 }
00589 if (!ast_strlen_zero(fdef)) {
00590 cur->hasdefault = 1;
00591 } else {
00592 cur->hasdefault = 0;
00593 }
00594 AST_RWLIST_INSERT_TAIL(&psql_columns, cur, list);
00595 }
00596 }
00597 PQclear(result);
00598 } else {
00599 pgerror = PQerrorMessage(conn);
00600 ast_log(LOG_ERROR, "Unable to connect to database server %s. CALLS WILL NOT BE LOGGED!!\n", pghostname);
00601 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00602 connected = 0;
00603 }
00604
00605 ast_config_destroy(cfg);
00606
00607 return ast_cdr_register(name, ast_module_info->description, pgsql_log);
00608 }
00609
00610 static int load_module(void)
00611 {
00612 return config_module(0) ? AST_MODULE_LOAD_DECLINE : 0;
00613 }
00614
00615 static int reload(void)
00616 {
00617 return config_module(1);
00618 }
00619
00620 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "PostgreSQL CDR Backend",
00621 .load = load_module,
00622 .unload = unload_module,
00623 .reload = reload,
00624 );