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