Mon Mar 19 11:30:23 2012

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    <support_level>extended</support_level>
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             /* Lengthen buffer, if necessary */                       \
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          /* For fields not set, simply skip them */
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                /* Field is NOT NULL (but no default), must include it anyway */
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                /* char, hopefully */
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                /* char, hopefully */
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                /* char, hopefully */
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                /* Get integer, no need to escape anything */
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                /* Char field, probably */
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                /* Integer, no need to escape anything */
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                /* Although this is a char field, there are no special characters in the values for these fields */
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             /* Arbitrary field, could be anything */
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             /* XXX Might want to handle dates, times, and other misc fields here XXX */
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       /* Test to be sure we're still connected... */
00282       /* If we're connected, and connection is working, good. */
00283       /* Otherwise, attempt reconnect.  If it fails... sorry... */
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 = "";   /* connect via UNIX-socket by default */
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          /* Escape special characters in schemaname */
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          /* Escape special characters in tablename */
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       /* Query the columns */
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             /* For varchar columns, the maximum length is encoded in a different field */
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           );

Generated on Mon Mar 19 11:30:23 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7