diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 14:11:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 14:11:00 +0000 |
commit | af754e596a8dbb05ed8580c342e7fe02e08b28e0 (patch) | |
tree | b2f334c2b55ede42081aa6710a72da784547d8ea /src/modules/rlm_sql/drivers/rlm_sql_firebird/sql_fbapi.c | |
parent | Initial commit. (diff) | |
download | freeradius-af754e596a8dbb05ed8580c342e7fe02e08b28e0.tar.xz freeradius-af754e596a8dbb05ed8580c342e7fe02e08b28e0.zip |
Adding upstream version 3.2.3+dfsg.upstream/3.2.3+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/modules/rlm_sql/drivers/rlm_sql_firebird/sql_fbapi.c')
-rw-r--r-- | src/modules/rlm_sql/drivers/rlm_sql_firebird/sql_fbapi.c | 578 |
1 files changed, 578 insertions, 0 deletions
diff --git a/src/modules/rlm_sql/drivers/rlm_sql_firebird/sql_fbapi.c b/src/modules/rlm_sql/drivers/rlm_sql_firebird/sql_fbapi.c new file mode 100644 index 0000000..b217a78 --- /dev/null +++ b/src/modules/rlm_sql/drivers/rlm_sql_firebird/sql_fbapi.c @@ -0,0 +1,578 @@ +/* + * sql_fbapi.c Part of Firebird rlm_sql driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright 2006 The FreeRADIUS server project + * Copyright 2006 Vitaly Bodzhgua <vitaly@eastera.net> + */ + +RCSID("$Id$") + +#include "sql_fbapi.h" + +#include <stdarg.h> + +static void fb_set_tpb(rlm_sql_firebird_conn_t *conn, int count, ...) +{ + int i; + va_list arg; + + va_start(arg, count); + conn->tpb = malloc(count); + + for (i = 0; i < count; i++) { + conn->tpb[i] = (char) va_arg(arg, int); + } + + conn->tpb_len = count; + + va_end(arg); +} + + +static void fb_dpb_add_str(char **dpb, char name, char const *value) +{ + int l; + + if (!value) { + return; + } + + l = strlen(value); + + *(*dpb)++= name; + *(*dpb)++= (char) l; + + memmove(*dpb, value, l); + + *dpb += l; +} + +static void fb_set_sqlda(XSQLDA *sqlda) { + int i; + + for (i = 0; i < sqlda->sqld; i++) { + if ((sqlda->sqlvar[i].sqltype & ~1) == SQL_VARYING) { + sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen + sizeof(short)); + } else { + sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen); + } + + if (sqlda->sqlvar[i].sqltype & 1) { + sqlda->sqlvar[i].sqlind = (short*)calloc(sizeof(short), 1); + } else { + sqlda->sqlvar[i].sqlind = 0; + } + } +} + +DIAG_OFF(deprecated-declarations) +int fb_error(rlm_sql_firebird_conn_t *conn) +{ + ISC_SCHAR error[2048]; /* Only 1024 bytes should be written to this, but were playing it extra safe */ + ISC_STATUS *pstatus; + + conn->sql_code = 0; + + /* + * Free any previous errors. + */ + TALLOC_FREE(conn->error); + + /* + * Check if the status array contains an error + */ + if (IS_ISC_ERROR(conn->status)) { + conn->sql_code = isc_sqlcode(conn->status); + + /* + * pstatus is a pointer into the status array which is + * advanced by isc_interprete. It's initialised to the + * first element of the status array. + */ + pstatus = &conn->status[0]; + + /* + * It's deprecated because the size of the buffer isn't + * passed and this isn't safe. But as were passing a very + * large buffer it's unlikely this will be an issue, and + * allows us to maintain compatibility with the interbase + * API. + */ + isc_interprete(&error[0], &pstatus); + conn->error = talloc_typed_asprintf(conn, "%s. ", &error[0]); + + while (isc_interprete(&error[0], &pstatus)) { + conn->error = talloc_asprintf_append(conn->error, "%s. ", &error[0]); + } + + memset(&conn->status, 0, sizeof(conn->status)); + } + + return conn->sql_code; +} +DIAG_ON(deprecated-declarations) + +void fb_free_sqlda(XSQLDA *sqlda) +{ + int i; + for (i = 0; i < sqlda->sqld; i++) { + free(sqlda->sqlvar[i].sqldata); + free(sqlda->sqlvar[i].sqlind); + } + sqlda->sqld = 0; +} + + + +//Macro for NULLs check +#define IS_NULL(x) (x->sqltype & 1) && (*x->sqlind < 0) + +//Structure to manage a SQL_VARYING Firebird's data types +typedef struct vary_fb { + short vary_length; + char vary_string[1]; +} VARY; + +//function fb_store_row based on fiebird's apifull example +void fb_store_row(rlm_sql_firebird_conn_t *conn) +{ + int dtype; + struct tm times; + ISC_QUAD bid; + int i; + XSQLVAR *var; + VARY * vary; + + /* assumed: id, username, attribute, value, op */ + if (conn->row_fcount<conn->sqlda_out->sqld) { + i = conn->row_fcount; + conn->row_fcount = conn->sqlda_out->sqld; + conn->row = (char **) realloc(conn->row, conn->row_fcount * sizeof(char *)); + conn->row_sizes = (int *) realloc(conn->row_sizes, conn->row_fcount * sizeof(int)); + + while( i <conn->row_fcount) { + conn->row[i] = 0; + conn->row_sizes[i++] = 0; + } + } + + for (i = 0, var = conn->sqlda_out->sqlvar; i < conn->sqlda_out->sqld; var++, i++) { + /* + * Initial buffer size to store field's data is 256 bytes + */ + if (conn->row_sizes[i]<256) { + conn->row[i] = (char *) realloc(conn->row[i], 256); + conn->row_sizes[i] = 256; + } + + if (IS_NULL(var)) { + strcpy(conn->row[i], "NULL"); + continue; + } + + dtype = var->sqltype & ~1; + + switch (dtype) { + case SQL_TEXT: + if (conn->row_sizes[i]<= var->sqllen) { + conn->row_sizes[i] = var->sqllen + 1; + conn->row[i] = realloc(conn->row[i], + conn->row_sizes[i]); + } + + memmove(conn->row[i], var->sqldata, var->sqllen); + conn->row[i][var->sqllen] = 0; + break; + + case SQL_VARYING: + vary = (VARY*) var->sqldata; + if (conn->row_sizes[i] <= vary->vary_length) { + conn->row_sizes[i] = vary->vary_length + 1; + conn->row[i] = realloc(conn->row[i], + conn->row_sizes[i]); + } + memmove(conn->row[i], vary->vary_string, vary->vary_length); + conn->row[i][vary->vary_length] = 0; + break; + + case SQL_FLOAT: + snprintf(conn->row[i], conn->row_sizes[i], "%15g", + *(float ISC_FAR *) (var->sqldata)); + break; + + case SQL_SHORT: + case SQL_LONG: + case SQL_INT64: + { + ISC_INT64 value = 0; + short field_width = 0; + short dscale = 0; + char *p; + p = conn->row[i]; + + switch (dtype) { + case SQL_SHORT: + value = (ISC_INT64) *(short *)var->sqldata; + field_width = 6; + break; + + case SQL_LONG: + value = (ISC_INT64) *(int *)var->sqldata; + field_width = 11; + break; + + case SQL_INT64: + value = (ISC_INT64) *(ISC_INT64 *)var->sqldata; + field_width = 21; + break; + } + dscale = var->sqlscale; + + if (dscale < 0) { + ISC_INT64 tens; + short j; + + tens = 1; + for (j = 0; j > dscale; j--) { + tens *= 10; + } + + if (value >= 0) { + sprintf(p, "%*lld.%0*lld", + field_width - 1 + dscale, + (ISC_INT64) value / tens, + -dscale, + (ISC_INT64) value % tens); + } else if ((value / tens) != 0) { + sprintf (p, "%*lld.%0*lld", + field_width - 1 + dscale, + (ISC_INT64) (value / tens), + -dscale, + (ISC_INT64) -(value % tens)); + } else { + sprintf(p, "%*s.%0*lld", field_width - 1 + dscale, + "-0", -dscale, (ISC_INT64) - (value % tens)); + } + } else if (dscale) { + sprintf(p, "%*lld%0*d", field_width, + (ISC_INT64) value, dscale, 0); + } else { + sprintf(p, "%*lld", field_width, + (ISC_INT64) value); + } + } + break; + + case SQL_D_FLOAT: + case SQL_DOUBLE: + snprintf(conn->row[i], conn->row_sizes[i], "%24f", + *(double ISC_FAR *) (var->sqldata)); + break; + + case SQL_TIMESTAMP: + isc_decode_timestamp((ISC_TIMESTAMP ISC_FAR *)var->sqldata, ×); + snprintf(conn->row[i], conn->row_sizes[i], "%04d-%02d-%02d %02d:%02d:%02d.%04d", + times.tm_year + 1900, + times.tm_mon + 1, + times.tm_mday, + times.tm_hour, + times.tm_min, + times.tm_sec, + ((ISC_TIMESTAMP *)var->sqldata)->timestamp_time % 10000); + break; + + case SQL_TYPE_DATE: + isc_decode_sql_date((ISC_DATE ISC_FAR *)var->sqldata, ×); + snprintf(conn->row[i], conn->row_sizes[i], "%04d-%02d-%02d", + times.tm_year + 1900, + times.tm_mon + 1, + times.tm_mday); + break; + + case SQL_TYPE_TIME: + isc_decode_sql_time((ISC_TIME ISC_FAR *)var->sqldata, ×); + snprintf(conn->row[i], conn->row_sizes[i], "%02d:%02d:%02d.%04d", + times.tm_hour, + times.tm_min, + times.tm_sec, + (*((ISC_TIME *)var->sqldata)) % 10000); + break; + + case SQL_BLOB: + case SQL_ARRAY: + /* Print the blob id on blobs or arrays */ + bid = *(ISC_QUAD ISC_FAR *) var->sqldata; + snprintf(conn->row[i], conn->row_sizes[i], "%08" ISC_LONG_FMT "x:%08" ISC_LONG_FMT "x", + bid.gds_quad_high, bid.gds_quad_low); + break; + } + } +} + +int fb_init_socket(rlm_sql_firebird_conn_t *conn) +{ + memset(conn, 0, sizeof(*conn)); + conn->sqlda_out = (XSQLDA ISC_FAR *) calloc(XSQLDA_LENGTH (5), 1); + conn->sqlda_out->sqln = 5; + conn->sqlda_out->version = SQLDA_VERSION1; + conn->sql_dialect = 3; +#ifdef _PTHREAD_H + pthread_mutex_init (&conn->mut, NULL); + DEBUG("Init mutex %p\n", &conn->mut); +#endif + + /* + * Set tpb to read_committed/wait/no_rec_version + */ + fb_set_tpb(conn, 5, isc_tpb_version3, isc_tpb_wait, isc_tpb_write, + isc_tpb_read_committed, isc_tpb_no_rec_version); + if (!conn->tpb) { + return -1; + } + + return 0; +} + +int fb_connect(rlm_sql_firebird_conn_t * conn, rlm_sql_config_t *config) +{ + char *p; + char *database; + + conn->dpb_len = 4; + if (config->sql_login) { + conn->dpb_len+= strlen(config->sql_login) + 2; + } + + if (config->sql_password) { + conn->dpb_len += strlen(config->sql_password) + 2; + } + + conn->dpb = (char *) malloc(conn->dpb_len); + p = conn->dpb; + + *conn->dpb++= isc_dpb_version1; + *conn->dpb++= isc_dpb_num_buffers; + *conn->dpb++= 1; + *conn->dpb++= 90; + + fb_dpb_add_str(&conn->dpb, isc_dpb_user_name, config->sql_login); + fb_dpb_add_str(&conn->dpb, isc_dpb_password, config->sql_password); + + conn->dpb = p; + + /* + * Check if database and server in the form of server:database. + * If config->sql_server contains ':', then config->sql_db + * parameter ignored. + */ + if (strchr(config->sql_server, ':')) { + database = strdup(config->sql_server); + } else { + /* + * Make database and server to be in the form + * of server:database + */ + int ls = strlen(config->sql_server); + int ld = strlen(config->sql_db); + database = (char *) calloc(ls + ld + 2, 1); + strcpy(database, config->sql_server); + database[ls] = ':'; + memmove(database + ls + 1, config->sql_db, ld); + } + isc_attach_database(conn->status, 0, database, &conn->dbh, + conn->dpb_len, conn->dpb); + free(database); + + return fb_error(conn); +} + + +int fb_fetch(rlm_sql_firebird_conn_t *conn) +{ + long fetch_stat; + if (conn->statement_type!= isc_info_sql_stmt_select) { + return 100; + } + + fetch_stat = isc_dsql_fetch(conn->status, &conn->stmt, + SQL_DIALECT_V6, conn->sqlda_out); + if (fetch_stat) { + if (fetch_stat!= 100L) { + fb_error(conn); + } else { + conn->sql_code = 0; + } + } + + return fetch_stat; +} + +static int fb_prepare(rlm_sql_firebird_conn_t *conn, char const *query) +{ + static char stmt_info[] = { isc_info_sql_stmt_type }; + char info_buffer[128]; + short l; + + if (!conn->trh) { + isc_start_transaction(conn->status, &conn->trh, 1, &conn->dbh, + conn->tpb_len, conn->tpb); + if (!conn->trh) { + return -4; + } + } + + fb_free_statement(conn); + if (!conn->stmt) { + isc_dsql_allocate_statement(conn->status, &conn->dbh, + &conn->stmt); + if (!conn->stmt) { + return -1; + } + } + + fb_free_sqlda(conn->sqlda_out); + isc_dsql_prepare(conn->status, &conn->trh, &conn->stmt, 0, query, + conn->sql_dialect, conn->sqlda_out); + if (IS_ISC_ERROR(conn->status)) { + return -2; + } + + if (conn->sqlda_out->sqln<conn->sqlda_out->sqld) { + conn->sqlda_out->sqln = conn->sqlda_out->sqld; + conn->sqlda_out = (XSQLDA ISC_FAR *) realloc(conn->sqlda_out, + XSQLDA_LENGTH(conn->sqlda_out->sqld)); + isc_dsql_describe(conn->status, &conn->stmt, SQL_DIALECT_V6, + conn->sqlda_out); + + if (IS_ISC_ERROR(conn->status)) { + return -3; + } + } + /* + * Get statement type + */ + isc_dsql_sql_info(conn->status, &conn->stmt, sizeof(stmt_info), + stmt_info, sizeof(info_buffer), info_buffer); + if (IS_ISC_ERROR(conn->status)) return -4; + + l = (short) isc_vax_integer((char ISC_FAR *) info_buffer + 1, 2); + conn->statement_type = isc_vax_integer((char ISC_FAR *) info_buffer + 3, + l); + + if (conn->sqlda_out->sqld) { + fb_set_sqlda(conn->sqlda_out); //set out sqlda + } + + return 0; +} + + +int fb_sql_query(rlm_sql_firebird_conn_t *conn, char const *query) { + if (fb_prepare(conn, query)) { + return fb_error(conn); + } + + switch (conn->statement_type) { + case isc_info_sql_stmt_exec_procedure: + isc_dsql_execute2(conn->status, &conn->trh, &conn->stmt, + SQL_DIALECT_V6, 0, conn->sqlda_out); + break; + + default: + isc_dsql_execute(conn->status, &conn->trh, &conn->stmt, + SQL_DIALECT_V6, 0); + break; + } + return fb_error(conn); +} + +int fb_affected_rows(rlm_sql_firebird_conn_t *conn) { + static char count_info[] = {isc_info_sql_records}; + char info_buffer[128]; + char *p ; + int affected_rows = -1; + + if (!conn->stmt) return -1; + + isc_dsql_sql_info(conn->status, &conn->stmt, + sizeof (count_info), count_info, + sizeof (info_buffer), info_buffer); + + if (IS_ISC_ERROR(conn->status)) { + return fb_error(conn); + } + + p = info_buffer + 3; + while (*p != isc_info_end) { + p++; + short len = (short)isc_vax_integer(p, 2); + p += 2; + + affected_rows = isc_vax_integer(p, len); + if (affected_rows > 0) { + break; + } + p += len; + } + return affected_rows; +} + +int fb_close_cursor(rlm_sql_firebird_conn_t *conn) { + isc_dsql_free_statement(conn->status, &conn->stmt, DSQL_close); + + return fb_error(conn); +} + +void fb_free_statement(rlm_sql_firebird_conn_t *conn) { + if (conn->stmt) { + isc_dsql_free_statement(conn->status, &conn->stmt, DSQL_drop); + conn->stmt = 0; + } +} + +int fb_rollback(rlm_sql_firebird_conn_t *conn) { + conn->sql_code = 0; + if (conn->trh) { + isc_rollback_transaction(conn->status, &conn->trh); +// conn->in_use = 0; +#ifdef _PTHREAD_H + pthread_mutex_unlock(&conn->mut); +#endif + + if (IS_ISC_ERROR(conn->status)) { + return fb_error(conn); + } + } + return conn->sql_code; +} + +int fb_commit(rlm_sql_firebird_conn_t *conn) { + conn->sql_code = 0; + if (conn->trh) { + isc_commit_transaction (conn->status, &conn->trh); + if (IS_ISC_ERROR(conn->status)) { + fb_error(conn); + ERROR("Fail to commit. Error: %s. Try to rollback.", conn->error); + return fb_rollback(conn); + } + } +// conn->in_use = 0; +#ifdef _PTHREAD_H + pthread_mutex_unlock(&conn->mut); +#endif + return conn->sql_code; +} |