Wed Apr 6 11:29:41 2011

Asterisk developer's documentation


cdr_pgsql.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2003 - 2006
00005  *
00006  * Matthew D. Hardeman <mhardemn@papersoft.com>
00007  * Adapted from the MySQL CDR logger originally by James Sharp
00008  *
00009  * Modified September 2003
00010  * Matthew D. Hardeman <mhardemn@papersoft.com>
00011  *
00012  * See http://www.asterisk.org for more information about
00013  * the Asterisk project. Please do not directly contact
00014  * any of the maintainers of this project for assistance;
00015  * the project provides a web site, mailing lists and IRC
00016  * channels for your use.
00017  *
00018  * This program is free software, distributed under the terms of
00019  * the GNU General Public License Version 2. See the LICENSE file
00020  * at the top of the source tree.
00021  */
00022 
00023 /*!
00024  * \file
00025  * \brief PostgreSQL CDR logger
00026  *
00027  * \author Matthew D. Hardeman <mhardemn@papersoft.com>
00028  * \extref PostgreSQL http://www.postgresql.org/
00029  *
00030  * See also
00031  * \arg \ref Config_cdr
00032  * \extref PostgreSQL http://www.postgresql.org/
00033  * \ingroup cdr_drivers
00034  */
00035 
00036 /*** MODULEINFO
00037    <depend>pgsql</depend>
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             /* Lengthen buffer, if necessary */                       \
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          /* For fields not set, simply skip them */
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                /* Field is NOT NULL (but no default), must include it anyway */
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                /* char, hopefully */
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                /* char, hopefully */
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                /* char, hopefully */
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                /* Get integer, no need to escape anything */
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                /* Char field, probably */
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                /* Integer, no need to escape anything */
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                /* Although this is a char field, there are no special characters in the values for these fields */
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             /* Arbitrary field, could be anything */
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             /* XXX Might want to handle dates, times, and other misc fields here XXX */
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       /* Test to be sure we're still connected... */
00281       /* If we're connected, and connection is working, good. */
00282       /* Otherwise, attempt reconnect.  If it fails... sorry... */
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 = "";   /* connect via UNIX-socket by default */
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          /* Escape special characters in schemaname */
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          /* Escape special characters in tablename */
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       /* Query the columns */
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             /* For varchar columns, the maximum length is encoded in a different field */
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           );

Generated on Wed Apr 6 11:29:41 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7