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