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: 354263 $")
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 ast_mutex_unlock(&pgsql_lock); \
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 ast_mutex_unlock(&pgsql_lock); \
00099 return -1; \
00100 } \
00101 } \
00102 } while (0)
00103
00104 static int pgsql_log(struct ast_cdr *cdr)
00105 {
00106 struct ast_tm tm;
00107 char *pgerror;
00108 PGresult *result;
00109
00110 ast_mutex_lock(&pgsql_lock);
00111
00112 if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) {
00113 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
00114 if (PQstatus(conn) != CONNECTION_BAD) {
00115 connected = 1;
00116 if (PQsetClientEncoding(conn, encoding)) {
00117 #ifdef HAVE_PGSQL_pg_encoding_to_char
00118 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn)));
00119 #else
00120 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default.\n", encoding);
00121 #endif
00122 }
00123 } else {
00124 pgerror = PQerrorMessage(conn);
00125 ast_log(LOG_ERROR, "Unable to connect to database server %s. Calls will not be logged!\n", pghostname);
00126 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00127 PQfinish(conn);
00128 conn = NULL;
00129 }
00130 }
00131
00132 if (connected) {
00133 struct columns *cur;
00134 struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
00135 char buf[257], escapebuf[513], *value;
00136 int first = 1;
00137
00138 if (!sql || !sql2) {
00139 ast_free(sql);
00140 ast_free(sql2);
00141 return -1;
00142 }
00143
00144 ast_str_set(&sql, 0, "INSERT INTO %s (", table);
00145 ast_str_set(&sql2, 0, " VALUES (");
00146
00147 AST_RWLIST_RDLOCK(&psql_columns);
00148 AST_RWLIST_TRAVERSE(&psql_columns, cur, list) {
00149
00150 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00151 if (strcmp(cur->name, "calldate") == 0 && !value) {
00152 ast_cdr_getvar(cdr, "start", &value, buf, sizeof(buf), 0, 0);
00153 }
00154 if (!value) {
00155 if (cur->notnull && !cur->hasdefault) {
00156
00157 LENGTHEN_BUF1(strlen(cur->name) + 2);
00158 ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
00159 LENGTHEN_BUF2(3);
00160 ast_str_append(&sql2, 0, "%s''", first ? "" : ",");
00161 first = 0;
00162 }
00163 continue;
00164 }
00165
00166 LENGTHEN_BUF1(strlen(cur->name) + 2);
00167 ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
00168
00169 if (strcmp(cur->name, "start") == 0 || strcmp(cur->name, "calldate") == 0) {
00170 if (strncmp(cur->type, "int", 3) == 0) {
00171 LENGTHEN_BUF2(13);
00172 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->start.tv_sec);
00173 } else if (strncmp(cur->type, "float", 5) == 0) {
00174 LENGTHEN_BUF2(31);
00175 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->start.tv_sec + (double)cdr->start.tv_usec / 1000000.0);
00176 } else {
00177
00178 LENGTHEN_BUF2(31);
00179 ast_localtime(&cdr->start, &tm, tz);
00180 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00181 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00182 }
00183 } else if (strcmp(cur->name, "answer") == 0) {
00184 if (strncmp(cur->type, "int", 3) == 0) {
00185 LENGTHEN_BUF2(13);
00186 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->answer.tv_sec);
00187 } else if (strncmp(cur->type, "float", 5) == 0) {
00188 LENGTHEN_BUF2(31);
00189 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->answer.tv_sec + (double)cdr->answer.tv_usec / 1000000.0);
00190 } else {
00191
00192 LENGTHEN_BUF2(31);
00193 ast_localtime(&cdr->answer, &tm, tz);
00194 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00195 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00196 }
00197 } else if (strcmp(cur->name, "end") == 0) {
00198 if (strncmp(cur->type, "int", 3) == 0) {
00199 LENGTHEN_BUF2(13);
00200 ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->end.tv_sec);
00201 } else if (strncmp(cur->type, "float", 5) == 0) {
00202 LENGTHEN_BUF2(31);
00203 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->end.tv_sec + (double)cdr->end.tv_usec / 1000000.0);
00204 } else {
00205
00206 LENGTHEN_BUF2(31);
00207 ast_localtime(&cdr->end, &tm, tz);
00208 ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
00209 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
00210 }
00211 } else if (strcmp(cur->name, "duration") == 0 || strcmp(cur->name, "billsec") == 0) {
00212 if (cur->type[0] == 'i') {
00213
00214 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00215 LENGTHEN_BUF2(13);
00216 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
00217 } else if (strncmp(cur->type, "float", 5) == 0) {
00218 struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer;
00219 LENGTHEN_BUF2(31);
00220 ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
00221 } else {
00222
00223 struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer;
00224 LENGTHEN_BUF2(31);
00225 ast_str_append(&sql2, 0, "%s'%f'", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
00226 }
00227 } else if (strcmp(cur->name, "disposition") == 0 || strcmp(cur->name, "amaflags") == 0) {
00228 if (strncmp(cur->type, "int", 3) == 0) {
00229
00230 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 1);
00231 LENGTHEN_BUF2(13);
00232 ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
00233 } else {
00234
00235 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00236 LENGTHEN_BUF2(31);
00237 ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", value);
00238 }
00239 } else {
00240
00241 ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
00242 if (strncmp(cur->type, "int", 3) == 0) {
00243 long long whatever;
00244 if (value && sscanf(value, "%30lld", &whatever) == 1) {
00245 LENGTHEN_BUF2(26);
00246 ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", whatever);
00247 } else {
00248 LENGTHEN_BUF2(2);
00249 ast_str_append(&sql2, 0, "%s0", first ? "" : ",");
00250 }
00251 } else if (strncmp(cur->type, "float", 5) == 0) {
00252 long double whatever;
00253 if (value && sscanf(value, "%30Lf", &whatever) == 1) {
00254 LENGTHEN_BUF2(51);
00255 ast_str_append(&sql2, 0, "%s%30Lf", first ? "" : ",", whatever);
00256 } else {
00257 LENGTHEN_BUF2(2);
00258 ast_str_append(&sql2, 0, "%s0", first ? "" : ",");
00259 }
00260
00261 } else {
00262 if (value)
00263 PQescapeStringConn(conn, escapebuf, value, strlen(value), NULL);
00264 else
00265 escapebuf[0] = '\0';
00266 LENGTHEN_BUF2(strlen(escapebuf) + 3);
00267 ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", escapebuf);
00268 }
00269 }
00270 first = 0;
00271 }
00272
00273 LENGTHEN_BUF1(ast_str_strlen(sql2) + 2);
00274 AST_RWLIST_UNLOCK(&psql_columns);
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
00337 static void empty_columns(void)
00338 {
00339 struct columns *current;
00340 AST_RWLIST_WRLOCK(&psql_columns);
00341 while ((current = AST_RWLIST_REMOVE_HEAD(&psql_columns, list))) {
00342 ast_free(current);
00343 }
00344 AST_RWLIST_UNLOCK(&psql_columns);
00345
00346 }
00347
00348 static int unload_module(void)
00349 {
00350 ast_cdr_unregister(name);
00351
00352 PQfinish(conn);
00353
00354 ast_free(pghostname);
00355 ast_free(pgdbname);
00356 ast_free(pgdbuser);
00357 ast_free(pgpassword);
00358 ast_free(pgdbport);
00359 ast_free(table);
00360 ast_free(encoding);
00361 ast_free(tz);
00362
00363 empty_columns();
00364
00365 return 0;
00366 }
00367
00368 static int config_module(int reload)
00369 {
00370 struct ast_variable *var;
00371 char *pgerror;
00372 struct columns *cur;
00373 PGresult *result;
00374 const char *tmp;
00375 struct ast_config *cfg;
00376 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00377
00378 if ((cfg = ast_config_load(config, config_flags)) == NULL || cfg == CONFIG_STATUS_FILEINVALID) {
00379 ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config);
00380 return -1;
00381 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
00382 return 0;
00383 }
00384
00385 ast_mutex_lock(&pgsql_lock);
00386
00387 if (!(var = ast_variable_browse(cfg, "global"))) {
00388 ast_config_destroy(cfg);
00389 ast_mutex_unlock(&pgsql_lock);
00390 ast_log(LOG_NOTICE, "cdr_pgsql configuration contains no global section, skipping module %s.\n",
00391 reload ? "reload" : "load");
00392 return -1;
00393 }
00394
00395 if (!(tmp = ast_variable_retrieve(cfg, "global", "hostname"))) {
00396 ast_log(LOG_WARNING, "PostgreSQL server hostname not specified. Assuming unix socket connection\n");
00397 tmp = "";
00398 }
00399
00400 ast_free(pghostname);
00401 if (!(pghostname = ast_strdup(tmp))) {
00402 ast_config_destroy(cfg);
00403 ast_mutex_unlock(&pgsql_lock);
00404 return -1;
00405 }
00406
00407 if (!(tmp = ast_variable_retrieve(cfg, "global", "dbname"))) {
00408 ast_log(LOG_WARNING, "PostgreSQL database not specified. Assuming asterisk\n");
00409 tmp = "asteriskcdrdb";
00410 }
00411
00412 ast_free(pgdbname);
00413 if (!(pgdbname = ast_strdup(tmp))) {
00414 ast_config_destroy(cfg);
00415 ast_mutex_unlock(&pgsql_lock);
00416 return -1;
00417 }
00418
00419 if (!(tmp = ast_variable_retrieve(cfg, "global", "user"))) {
00420 ast_log(LOG_WARNING, "PostgreSQL database user not specified. Assuming asterisk\n");
00421 tmp = "asterisk";
00422 }
00423
00424 ast_free(pgdbuser);
00425 if (!(pgdbuser = ast_strdup(tmp))) {
00426 ast_config_destroy(cfg);
00427 ast_mutex_unlock(&pgsql_lock);
00428 return -1;
00429 }
00430
00431 if (!(tmp = ast_variable_retrieve(cfg, "global", "password"))) {
00432 ast_log(LOG_WARNING, "PostgreSQL database password not specified. Assuming blank\n");
00433 tmp = "";
00434 }
00435
00436 ast_free(pgpassword);
00437 if (!(pgpassword = ast_strdup(tmp))) {
00438 ast_config_destroy(cfg);
00439 ast_mutex_unlock(&pgsql_lock);
00440 return -1;
00441 }
00442
00443 if (!(tmp = ast_variable_retrieve(cfg, "global", "port"))) {
00444 ast_log(LOG_WARNING, "PostgreSQL database port not specified. Using default 5432.\n");
00445 tmp = "5432";
00446 }
00447
00448 ast_free(pgdbport);
00449 if (!(pgdbport = ast_strdup(tmp))) {
00450 ast_config_destroy(cfg);
00451 ast_mutex_unlock(&pgsql_lock);
00452 return -1;
00453 }
00454
00455 if (!(tmp = ast_variable_retrieve(cfg, "global", "table"))) {
00456 ast_log(LOG_WARNING, "CDR table not specified. Assuming cdr\n");
00457 tmp = "cdr";
00458 }
00459
00460 ast_free(table);
00461 if (!(table = ast_strdup(tmp))) {
00462 ast_config_destroy(cfg);
00463 ast_mutex_unlock(&pgsql_lock);
00464 return -1;
00465 }
00466
00467 if (!(tmp = ast_variable_retrieve(cfg, "global", "encoding"))) {
00468 ast_log(LOG_WARNING, "Encoding not specified. Assuming LATIN9\n");
00469 tmp = "LATIN9";
00470 }
00471
00472 ast_free(encoding);
00473 if (!(encoding = ast_strdup(tmp))) {
00474 ast_config_destroy(cfg);
00475 ast_mutex_unlock(&pgsql_lock);
00476 return -1;
00477 }
00478
00479 if (!(tmp = ast_variable_retrieve(cfg, "global", "timezone"))) {
00480 tmp = "";
00481 }
00482
00483 ast_free(tz);
00484 tz = NULL;
00485
00486 if (!ast_strlen_zero(tmp) && !(tz = ast_strdup(tmp))) {
00487 ast_config_destroy(cfg);
00488 ast_mutex_unlock(&pgsql_lock);
00489 return -1;
00490 }
00491
00492 if (option_debug) {
00493 if (ast_strlen_zero(pghostname)) {
00494 ast_debug(1, "using default unix socket\n");
00495 } else {
00496 ast_debug(1, "got hostname of %s\n", pghostname);
00497 }
00498 ast_debug(1, "got port of %s\n", pgdbport);
00499 ast_debug(1, "got user of %s\n", pgdbuser);
00500 ast_debug(1, "got dbname of %s\n", pgdbname);
00501 ast_debug(1, "got password of %s\n", pgpassword);
00502 ast_debug(1, "got sql table name of %s\n", table);
00503 ast_debug(1, "got encoding of %s\n", encoding);
00504 ast_debug(1, "got timezone of %s\n", tz);
00505 }
00506
00507 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
00508 if (PQstatus(conn) != CONNECTION_BAD) {
00509 char sqlcmd[768];
00510 char *fname, *ftype, *flen, *fnotnull, *fdef;
00511 int i, rows, version;
00512 ast_debug(1, "Successfully connected to PostgreSQL database.\n");
00513 connected = 1;
00514 if (PQsetClientEncoding(conn, encoding)) {
00515 #ifdef HAVE_PGSQL_pg_encoding_to_char
00516 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default '%s'\n", encoding, pg_encoding_to_char(PQclientEncoding(conn)));
00517 #else
00518 ast_log(LOG_WARNING, "Failed to set encoding to '%s'. Encoding set to default.\n", encoding);
00519 #endif
00520 }
00521 version = PQserverVersion(conn);
00522
00523 if (version >= 70300) {
00524 char *schemaname, *tablename;
00525 if (strchr(table, '.')) {
00526 schemaname = ast_strdupa(table);
00527 tablename = strchr(schemaname, '.');
00528 *tablename++ = '\0';
00529 } else {
00530 schemaname = "";
00531 tablename = table;
00532 }
00533
00534
00535 if (strchr(schemaname, '\\') || strchr(schemaname, '\'')) {
00536 char *tmp = schemaname, *ptr;
00537
00538 ptr = schemaname = alloca(strlen(tmp) * 2 + 1);
00539 for (; *tmp; tmp++) {
00540 if (strchr("\\'", *tmp)) {
00541 *ptr++ = *tmp;
00542 }
00543 *ptr++ = *tmp;
00544 }
00545 *ptr = '\0';
00546 }
00547
00548 if (strchr(tablename, '\\') || strchr(tablename, '\'')) {
00549 char *tmp = tablename, *ptr;
00550
00551 ptr = tablename = alloca(strlen(tmp) * 2 + 1);
00552 for (; *tmp; tmp++) {
00553 if (strchr("\\'", *tmp)) {
00554 *ptr++ = *tmp;
00555 }
00556 *ptr++ = *tmp;
00557 }
00558 *ptr = '\0';
00559 }
00560
00561 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",
00562 tablename,
00563 ast_strlen_zero(schemaname) ? "" : "'", ast_strlen_zero(schemaname) ? "current_schema()" : schemaname, ast_strlen_zero(schemaname) ? "" : "'");
00564 } else {
00565 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);
00566 }
00567
00568 result = PQexec(conn, sqlcmd);
00569 if (PQresultStatus(result) != PGRES_TUPLES_OK) {
00570 pgerror = PQresultErrorMessage(result);
00571 ast_log(LOG_ERROR, "Failed to query database columns: %s\n", pgerror);
00572 PQclear(result);
00573 unload_module();
00574 ast_mutex_unlock(&pgsql_lock);
00575 return AST_MODULE_LOAD_DECLINE;
00576 }
00577
00578 rows = PQntuples(result);
00579 if (rows == 0) {
00580 ast_log(LOG_ERROR, "cdr_pgsql: Failed to query database columns. No columns found, does the table exist?\n");
00581 PQclear(result);
00582 unload_module();
00583 ast_mutex_unlock(&pgsql_lock);
00584 return AST_MODULE_LOAD_DECLINE;
00585 }
00586
00587
00588 empty_columns();
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_WRLOCK(&psql_columns);
00619 AST_RWLIST_INSERT_TAIL(&psql_columns, cur, list);
00620 AST_RWLIST_UNLOCK(&psql_columns);
00621 }
00622 }
00623 PQclear(result);
00624 } else {
00625 pgerror = PQerrorMessage(conn);
00626 ast_log(LOG_ERROR, "Unable to connect to database server %s. CALLS WILL NOT BE LOGGED!!\n", pghostname);
00627 ast_log(LOG_ERROR, "Reason: %s\n", pgerror);
00628 connected = 0;
00629 }
00630
00631 ast_config_destroy(cfg);
00632
00633 ast_mutex_unlock(&pgsql_lock);
00634 return 0;
00635 }
00636
00637 static int load_module(void)
00638 {
00639 if (config_module(0)) {
00640 return AST_MODULE_LOAD_DECLINE;
00641 }
00642 return ast_cdr_register(name, ast_module_info->description, pgsql_log)
00643 ? AST_MODULE_LOAD_DECLINE : 0;
00644 }
00645
00646 static int reload(void)
00647 {
00648 return config_module(1);
00649 }
00650
00651 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PostgreSQL CDR Backend",
00652 .load = load_module,
00653 .unload = unload_module,
00654 .reload = reload,
00655 .load_pri = AST_MODPRI_CDR_DRIVER,
00656 );