|
|
|
@ -76,11 +76,8 @@ static AST_RWLIST_HEAD_STATIC(psql_columns, columns);
|
|
|
|
|
#define LENGTHEN_BUF1(size) \
|
|
|
|
|
do { \
|
|
|
|
|
/* Lengthen buffer, if necessary */ \
|
|
|
|
|
if ((newsize = lensql + (size) + 3) > sizesql) { \
|
|
|
|
|
if ((tmp = ast_realloc(sql, (newsize / 512 + 1) * 512))) { \
|
|
|
|
|
sql = tmp; \
|
|
|
|
|
sizesql = (newsize / 512 + 1) * 512; \
|
|
|
|
|
} else { \
|
|
|
|
|
if (sql->used + size + 1 > sql->len) { \
|
|
|
|
|
if (ast_str_make_space(&sql, ((sql->len + size + 1) / 512 + 1) * 512) != 0) { \
|
|
|
|
|
ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR failed.\n"); \
|
|
|
|
|
ast_free(sql); \
|
|
|
|
|
ast_free(sql2); \
|
|
|
|
@ -92,11 +89,8 @@ static AST_RWLIST_HEAD_STATIC(psql_columns, columns);
|
|
|
|
|
|
|
|
|
|
#define LENGTHEN_BUF2(size) \
|
|
|
|
|
do { \
|
|
|
|
|
if ((newsize = lensql2 + (size) + 3) > sizesql2) { \
|
|
|
|
|
if ((tmp = ast_realloc(sql2, (newsize / 512 + 1) * 512))) { \
|
|
|
|
|
sql2 = tmp; \
|
|
|
|
|
sizesql2 = (newsize / 512 + 1) * 512; \
|
|
|
|
|
} else { \
|
|
|
|
|
if (sql2->used + size + 1 > sql2->len) { \
|
|
|
|
|
if (ast_str_make_space(&sql2, ((sql2->len + size + 3) / 512 + 1) * 512) != 0) { \
|
|
|
|
|
ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR failed.\n"); \
|
|
|
|
|
ast_free(sql); \
|
|
|
|
|
ast_free(sql2); \
|
|
|
|
@ -129,12 +123,21 @@ static int pgsql_log(struct ast_cdr *cdr)
|
|
|
|
|
|
|
|
|
|
if (connected) {
|
|
|
|
|
struct columns *cur;
|
|
|
|
|
int lensql, lensql2, sizesql = maxsize, sizesql2 = maxsize2, newsize;
|
|
|
|
|
char *sql = ast_calloc(sizeof(char), sizesql), *sql2 = ast_calloc(sizeof(char), sizesql2), *tmp, *value;
|
|
|
|
|
char buf[257], escapebuf[513];
|
|
|
|
|
struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
|
|
|
|
|
char buf[257], escapebuf[513], *value;
|
|
|
|
|
|
|
|
|
|
lensql = snprintf(sql, sizesql, "INSERT INTO %s (", table);
|
|
|
|
|
lensql2 = snprintf(sql2, sizesql2, " VALUES (");
|
|
|
|
|
if (!sql || !sql2) {
|
|
|
|
|
if (sql) {
|
|
|
|
|
ast_free(sql);
|
|
|
|
|
}
|
|
|
|
|
if (sql2) {
|
|
|
|
|
ast_free(sql2);
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast_str_set(&sql, 0, "INSERT INTO %s (", table);
|
|
|
|
|
ast_str_set(&sql2, 0, " VALUES (");
|
|
|
|
|
|
|
|
|
|
AST_RWLIST_RDLOCK(&psql_columns);
|
|
|
|
|
AST_RWLIST_TRAVERSE(&psql_columns, cur, list) {
|
|
|
|
@ -147,83 +150,85 @@ static int pgsql_log(struct ast_cdr *cdr)
|
|
|
|
|
if (cur->notnull && !cur->hasdefault) {
|
|
|
|
|
/* Field is NOT NULL (but no default), must include it anyway */
|
|
|
|
|
LENGTHEN_BUF1(strlen(cur->name) + 2);
|
|
|
|
|
lensql += snprintf(sql + lensql, sizesql - lensql, "\"%s\",", cur->name);
|
|
|
|
|
ast_str_append(&sql, 0, "\"%s\",", cur->name);
|
|
|
|
|
LENGTHEN_BUF2(3);
|
|
|
|
|
strcat(sql2, "'',");
|
|
|
|
|
lensql2 += 3;
|
|
|
|
|
ast_str_append(&sql2, 0, "'',");
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LENGTHEN_BUF1(strlen(cur->name) + 2);
|
|
|
|
|
lensql += snprintf(sql + lensql, sizesql - lensql, "\"%s\",", cur->name);
|
|
|
|
|
ast_str_append(&sql, 0, "\"%s\",", cur->name);
|
|
|
|
|
|
|
|
|
|
if (strcmp(cur->name, "start") == 0 || strcmp(cur->name, "calldate") == 0) {
|
|
|
|
|
if (strncmp(cur->type, "int", 3) == 0) {
|
|
|
|
|
LENGTHEN_BUF2(12);
|
|
|
|
|
lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%ld", cdr->start.tv_sec);
|
|
|
|
|
ast_str_append(&sql2, 0, "%ld", cdr->start.tv_sec);
|
|
|
|
|
} else if (strncmp(cur->type, "float", 5) == 0) {
|
|
|
|
|
LENGTHEN_BUF2(30);
|
|
|
|
|
lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%f", (double)cdr->start.tv_sec + (double)cdr->start.tv_usec / 1000000.0);
|
|
|
|
|
ast_str_append(&sql2, 0, "%f", (double)cdr->start.tv_sec + (double)cdr->start.tv_usec / 1000000.0);
|
|
|
|
|
} else {
|
|
|
|
|
/* char, hopefully */
|
|
|
|
|
LENGTHEN_BUF2(30);
|
|
|
|
|
ast_localtime(&cdr->start, &tm, NULL);
|
|
|
|
|
lensql2 += ast_strftime(sql2 + lensql2, sizesql2 - lensql2, DATE_FORMAT, &tm);
|
|
|
|
|
ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
|
|
|
|
|
ast_str_append(&sql2, 0, "%s", buf);
|
|
|
|
|
}
|
|
|
|
|
} else if (strcmp(cur->name, "answer") == 0) {
|
|
|
|
|
if (strncmp(cur->type, "int", 3) == 0) {
|
|
|
|
|
LENGTHEN_BUF2(12);
|
|
|
|
|
lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%ld", cdr->answer.tv_sec);
|
|
|
|
|
ast_str_append(&sql2, 0, "%ld", cdr->answer.tv_sec);
|
|
|
|
|
} else if (strncmp(cur->type, "float", 5) == 0) {
|
|
|
|
|
LENGTHEN_BUF2(30);
|
|
|
|
|
lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%f", (double)cdr->answer.tv_sec + (double)cdr->answer.tv_usec / 1000000.0);
|
|
|
|
|
ast_str_append(&sql2, 0, "%f", (double)cdr->answer.tv_sec + (double)cdr->answer.tv_usec / 1000000.0);
|
|
|
|
|
} else {
|
|
|
|
|
/* char, hopefully */
|
|
|
|
|
LENGTHEN_BUF2(30);
|
|
|
|
|
ast_localtime(&cdr->start, &tm, NULL);
|
|
|
|
|
lensql2 += ast_strftime(sql2 + lensql2, sizesql2 - lensql2, DATE_FORMAT, &tm);
|
|
|
|
|
ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
|
|
|
|
|
ast_str_append(&sql2, 0, "%s", buf);
|
|
|
|
|
}
|
|
|
|
|
} else if (strcmp(cur->name, "end") == 0) {
|
|
|
|
|
if (strncmp(cur->type, "int", 3) == 0) {
|
|
|
|
|
LENGTHEN_BUF2(12);
|
|
|
|
|
lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%ld", cdr->end.tv_sec);
|
|
|
|
|
ast_str_append(&sql2, 0, "%ld", cdr->end.tv_sec);
|
|
|
|
|
} else if (strncmp(cur->type, "float", 5) == 0) {
|
|
|
|
|
LENGTHEN_BUF2(30);
|
|
|
|
|
lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%f", (double)cdr->end.tv_sec + (double)cdr->end.tv_usec / 1000000.0);
|
|
|
|
|
ast_str_append(&sql2, 0, "%f", (double)cdr->end.tv_sec + (double)cdr->end.tv_usec / 1000000.0);
|
|
|
|
|
} else {
|
|
|
|
|
/* char, hopefully */
|
|
|
|
|
LENGTHEN_BUF2(30);
|
|
|
|
|
ast_localtime(&cdr->end, &tm, NULL);
|
|
|
|
|
lensql2 += ast_strftime(sql2 + lensql2, sizesql2 - lensql2, DATE_FORMAT, &tm);
|
|
|
|
|
ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
|
|
|
|
|
ast_str_append(&sql2, 0, "%s", buf);
|
|
|
|
|
}
|
|
|
|
|
} else if (strcmp(cur->name, "duration") == 0 || strcmp(cur->name, "billsec") == 0) {
|
|
|
|
|
if (cur->type[0] == 'i') {
|
|
|
|
|
/* Get integer, no need to escape anything */
|
|
|
|
|
ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
|
|
|
|
|
LENGTHEN_BUF2(12);
|
|
|
|
|
lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%s", value);
|
|
|
|
|
ast_str_append(&sql2, 0, "%s", value);
|
|
|
|
|
} else if (strncmp(cur->type, "float", 5) == 0) {
|
|
|
|
|
struct timeval *when = cur->name[0] == 'd' ? &cdr->start : &cdr->answer;
|
|
|
|
|
LENGTHEN_BUF2(30);
|
|
|
|
|
lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%f", (double)cdr->end.tv_sec - when->tv_sec + cdr->end.tv_usec / 1000000.0 - when->tv_usec / 1000000.0);
|
|
|
|
|
ast_str_append(&sql2, 0, "%f", (double)cdr->end.tv_sec - when->tv_sec + cdr->end.tv_usec / 1000000.0 - when->tv_usec / 1000000.0);
|
|
|
|
|
} else {
|
|
|
|
|
/* Char field, probably */
|
|
|
|
|
struct timeval *when = cur->name[0] == 'd' ? &cdr->start : &cdr->answer;
|
|
|
|
|
LENGTHEN_BUF2(30);
|
|
|
|
|
lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "'%f'", (double)cdr->end.tv_sec - when->tv_sec + cdr->end.tv_usec / 1000000.0 - when->tv_usec / 1000000.0);
|
|
|
|
|
ast_str_append(&sql2, 0, "'%f'", (double)cdr->end.tv_sec - when->tv_sec + cdr->end.tv_usec / 1000000.0 - when->tv_usec / 1000000.0);
|
|
|
|
|
}
|
|
|
|
|
} else if (strcmp(cur->name, "disposition") == 0 || strcmp(cur->name, "amaflags") == 0) {
|
|
|
|
|
if (strncmp(cur->type, "int", 3) == 0) {
|
|
|
|
|
/* Integer, no need to escape anything */
|
|
|
|
|
ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 1);
|
|
|
|
|
LENGTHEN_BUF2(12);
|
|
|
|
|
lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%s", value);
|
|
|
|
|
ast_str_append(&sql2, 0, "%s", value);
|
|
|
|
|
} else {
|
|
|
|
|
/* Although this is a char field, there are no special characters in the values for these fields */
|
|
|
|
|
ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
|
|
|
|
|
LENGTHEN_BUF2(30);
|
|
|
|
|
lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "'%s'", value);
|
|
|
|
|
ast_str_append(&sql2, 0, "'%s'", value);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* Arbitrary field, could be anything */
|
|
|
|
@ -232,19 +237,19 @@ static int pgsql_log(struct ast_cdr *cdr)
|
|
|
|
|
long long whatever;
|
|
|
|
|
if (value && sscanf(value, "%lld", &whatever) == 1) {
|
|
|
|
|
LENGTHEN_BUF2(25);
|
|
|
|
|
lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%lld", whatever);
|
|
|
|
|
ast_str_append(&sql2, 0, "%lld", whatever);
|
|
|
|
|
} else {
|
|
|
|
|
LENGTHEN_BUF2(1);
|
|
|
|
|
lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "0");
|
|
|
|
|
ast_str_append(&sql2, 0, "0");
|
|
|
|
|
}
|
|
|
|
|
} else if (strncmp(cur->type, "float", 5) == 0) {
|
|
|
|
|
long double whatever;
|
|
|
|
|
if (value && sscanf(value, "%Lf", &whatever) == 1) {
|
|
|
|
|
LENGTHEN_BUF2(50);
|
|
|
|
|
lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "%30Lf", whatever);
|
|
|
|
|
ast_str_append(&sql2, 0, "%30Lf", whatever);
|
|
|
|
|
} else {
|
|
|
|
|
LENGTHEN_BUF2(1);
|
|
|
|
|
lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "0");
|
|
|
|
|
ast_str_append(&sql2, 0, "0");
|
|
|
|
|
}
|
|
|
|
|
/* XXX Might want to handle dates, times, and other misc fields here XXX */
|
|
|
|
|
} else {
|
|
|
|
@ -253,19 +258,18 @@ static int pgsql_log(struct ast_cdr *cdr)
|
|
|
|
|
else
|
|
|
|
|
escapebuf[0] = '\0';
|
|
|
|
|
LENGTHEN_BUF2(strlen(escapebuf) + 2);
|
|
|
|
|
lensql2 += snprintf(sql2 + lensql2, sizesql2 - lensql2, "'%s'", escapebuf);
|
|
|
|
|
ast_str_append(&sql2, 0, "'%s'", escapebuf);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
LENGTHEN_BUF2(1);
|
|
|
|
|
strcat(sql2 + lensql2, ",");
|
|
|
|
|
lensql2++;
|
|
|
|
|
ast_str_append(&sql2, 0, ",");
|
|
|
|
|
}
|
|
|
|
|
AST_RWLIST_UNLOCK(&psql_columns);
|
|
|
|
|
LENGTHEN_BUF1(lensql2);
|
|
|
|
|
sql[lensql - 1] = ')';
|
|
|
|
|
sql2[lensql2 - 1] = ')';
|
|
|
|
|
strcat(sql + lensql, sql2);
|
|
|
|
|
ast_verb(11, "[%s]\n", sql);
|
|
|
|
|
LENGTHEN_BUF1(sql2->len);
|
|
|
|
|
sql->str[sql->len - 1] = ')';
|
|
|
|
|
sql2->str[sql2->len - 1] = ')';
|
|
|
|
|
ast_str_append(&sql, 0, "%s", sql2->str);
|
|
|
|
|
ast_verb(11, "[%s]\n", sql->str);
|
|
|
|
|
|
|
|
|
|
ast_debug(2, "inserting a CDR record.\n");
|
|
|
|
|
|
|
|
|
@ -291,7 +295,7 @@ static int pgsql_log(struct ast_cdr *cdr)
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result = PQexec(conn, sql);
|
|
|
|
|
result = PQexec(conn, sql->str);
|
|
|
|
|
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
|
|
|
|
|
pgerror = PQresultErrorMessage(result);
|
|
|
|
|
ast_log(LOG_ERROR, "Failed to insert call detail record into database!\n");
|
|
|
|
@ -302,7 +306,7 @@ static int pgsql_log(struct ast_cdr *cdr)
|
|
|
|
|
ast_log(LOG_ERROR, "Connection reestablished.\n");
|
|
|
|
|
connected = 1;
|
|
|
|
|
PQclear(result);
|
|
|
|
|
result = PQexec(conn, sql);
|
|
|
|
|
result = PQexec(conn, sql->str);
|
|
|
|
|
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
|
|
|
|
|
pgerror = PQresultErrorMessage(result);
|
|
|
|
|
ast_log(LOG_ERROR, "HARD ERROR! Attempted reconnection failed. DROPPING CALL RECORD!\n");
|
|
|
|
@ -311,9 +315,13 @@ static int pgsql_log(struct ast_cdr *cdr)
|
|
|
|
|
}
|
|
|
|
|
ast_mutex_unlock(&pgsql_lock);
|
|
|
|
|
PQclear(result);
|
|
|
|
|
ast_free(sql);
|
|
|
|
|
ast_free(sql2);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
PQclear(result);
|
|
|
|
|
ast_free(sql);
|
|
|
|
|
ast_free(sql2);
|
|
|
|
|
}
|
|
|
|
|
ast_mutex_unlock(&pgsql_lock);
|
|
|
|
|
return 0;
|
|
|
|
|