#endif
#ifndef HAVE_SQLITE3_INT64
-typedef sqlite3_int64 sqlite_int64
+typedef sqlite_int64 sqlite3_int64;
#endif
typedef struct rlm_sql_sqlite_conn {
static const CONF_PARSER driver_config[] = {
{ "filename", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED, rlm_sql_sqlite_config_t, filename), NULL },
{ "busy_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_sql_sqlite_config_t, busy_timeout), "200" },
- {NULL, -1, 0, NULL, NULL}
+ CONF_PARSER_TERMINATOR
};
-static sql_rcode_t sql_check_error(sqlite3 *db)
+/** Convert an sqlite status code to an sql_rcode_t
+ *
+ * @param status to convert.
+ * @return
+ * - RLM_SQL_OK - If no errors found.
+ * - RLM_SQL_ERROR - If a known, non-fatal, error occurred.
+ * - RLM_SQL_ALT_QUERY - If a constraints violation occurred.
+ * - RLM_SQL_RECONNECT - Anything else, we assume the connection can no longer be used.
+ */
+static sql_rcode_t sql_error_to_rcode(int status)
{
- int error = sqlite3_errcode(db);
-
/*
* Lowest byte is error category, other byte may contain
* the extended error, depending on version.
*/
- switch (error & 0xff) {
+ switch (status & 0xff) {
/*
* Not errors
*/
* Errors with the handle, that probably require reinitialisation
*/
default:
- ERROR("rlm_sql_sqlite: Handle is unusable, error (%d): %s", error, sqlite3_errmsg(db));
return RLM_SQL_RECONNECT;
}
}
+/** Determine if an error occurred, and what type of error it was
+ *
+ * @param db handle to extract error from (may be NULL).
+ * @param status to check (if unused, set to SQLITE_OK).
+ * @return
+ * - RLM_SQL_OK - If no errors found.
+ * - RLM_SQL_ERROR - If a known, non-fatal, error occurred.
+ * - RLM_SQL_ALT_QUERY - If a constraints violation occurred.
+ * - RLM_SQL_RECONNECT - Anything else. We assume the connection can no longer be used.
+ */
+static sql_rcode_t sql_check_error(sqlite3 *db, int status)
+{
+ int hstatus = SQLITE_OK;
+
+ if (db) {
+ hstatus = sqlite3_errcode(db);
+ switch (hstatus & 0xff) {
+ case SQLITE_OK:
+ case SQLITE_DONE:
+ case SQLITE_ROW:
+ hstatus = SQLITE_OK;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ switch (status & 0xff) {
+ case SQLITE_OK:
+ case SQLITE_DONE:
+ case SQLITE_ROW:
+ status = SQLITE_OK;
+ break;
+
+ default:
+ break;
+ }
+
+ if (status != SQLITE_OK) return sql_error_to_rcode(status);
+ if (hstatus != SQLITE_OK) return sql_error_to_rcode(status);
+
+ return RLM_SQL_OK;
+}
+
+/** Print an error to the global debug log
+ *
+ * If status does not indicate success, write an error to the global error log.
+ *
+ * @note The error code will be appended to the fmt string in the format ": code 0x<hex> (<int>)[: <string>]".
+ *
+ * @param db handle to extract error from (may be NULL).
+ * @param status to check (if unused, set to SQLITE_OK).
+ * @param fmt to preprend.
+ * @param ... arguments to fmt.
+ */
+static void sql_print_error(sqlite3 *db, int status, char const *fmt, ...)
+ CC_HINT(format (printf, 3, 4)) CC_HINT(nonnull (3));
+static void sql_print_error(sqlite3 *db, int status, char const *fmt, ...)
+{
+ va_list ap;
+ char *p;
+ int hstatus = SQLITE_OK;
+
+ if (db) {
+ hstatus = sqlite3_errcode(db);
+ switch (hstatus & 0xff) {
+ case SQLITE_OK:
+ case SQLITE_DONE:
+ case SQLITE_ROW:
+ hstatus = SQLITE_OK;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ switch (status & 0xff) {
+ case SQLITE_OK:
+ case SQLITE_DONE:
+ case SQLITE_ROW:
+ status = SQLITE_OK;
+ break;
+
+ default:
+ break;
+ }
+
+ /*
+ * No errors!
+ */
+ if ((hstatus == SQLITE_OK) && (status == SQLITE_OK)) return;
+
+ /*
+ * At least one error...
+ */
+ va_start(ap, fmt);
+ MEM(p = talloc_vasprintf(NULL, fmt, ap));
+ va_end(ap);
+
+ /*
+ * Disagreement between handle, and function return code,
+ * print them both.
+ */
+ if ((status != SQLITE_OK) && (status != hstatus)) {
+#ifdef HAVE_SQLITE3_ERRSTR
+ ERROR("rlm_sql_sqlite: %s: Code 0x%04x (%i): %s", p, status, status, sqlite3_errstr(status));
+#else
+ ERROR("rlm_sql_sqlite: %s: Code 0x%04x (%i)", p, status, status);
+#endif
+ }
+
+ if (hstatus != SQLITE_OK) ERROR("rlm_sql_sqlite: %s: Code 0x%04x (%i): %s",
+ p, hstatus, hstatus, sqlite3_errmsg(db));
+}
+
#ifdef HAVE_SQLITE3_OPEN_V2
static int sql_loadfile(TALLOC_CTX *ctx, sqlite3 *db, char const *filename)
{
- ssize_t len;
- char *buffer;
- char *p, *q, *s;
- int cl;
- FILE *f;
- struct stat finfo;
+ ssize_t len;
+ int statement_cnt = 0;
+ char *buffer;
+ char *p, *q, *s;
+ int cl;
+ FILE *f;
+ struct stat finfo;
int status;
sqlite3_stmt *statement;
if ((*p != 0x0a) && (*p != 0x0d) && (*p != '\t')) break;
cl = 1;
} else {
- cl = fr_utf8_char((uint8_t *) p);
+ cl = fr_utf8_char((uint8_t *) p, -1);
if (!cl) break;
}
}
while ((q = strchr(p, ';'))) {
if (q[1] != '\n') {
p = q + 1;
+ statement_cnt++;
continue;
}
*q = '\0';
#ifdef HAVE_SQLITE3_PREPARE_V2
- (void) sqlite3_prepare_v2(db, s, len, &statement, &z_tail);
+ status = sqlite3_prepare_v2(db, s, len, &statement, &z_tail);
#else
- (void) sqlite3_prepare(db, s, len, &>statement, &z_tail);
+ status = sqlite3_prepare(db, s, len, &statement, &z_tail);
#endif
- if (sql_check_error(db) != RLM_SQL_OK) {
+
+ if (sql_check_error(db, status) != RLM_SQL_OK) {
+ sql_print_error(db, status, "Failed preparing statement %i", statement_cnt);
talloc_free(buffer);
return -1;
}
- (void) sqlite3_step(statement);
- status = sql_check_error(db);
+ status = sqlite3_step(statement);
+ if (sql_check_error(db, status) != RLM_SQL_OK) {
+ sql_print_error(db, status, "Failed executing statement %i", statement_cnt);
+ sqlite3_finalize(statement);
+ talloc_free(buffer);
+ return -1;
+ }
- (void) sqlite3_finalize(statement);
- if ((status != RLM_SQL_OK) || sql_check_error(db)) {
+ status = sqlite3_finalize(statement);
+ if (sql_check_error(db, status) != RLM_SQL_OK) {
+ sql_print_error(db, status, "Failed finalizing statement %i", statement_cnt);
talloc_free(buffer);
return -1;
}
+ statement_cnt++;
p = s = q + 1;
}
if (cf_pair_find(conf, "bootstrap") && !exists) {
# ifdef HAVE_SQLITE3_OPEN_V2
- int status;
- int ret;
- char const *p;
- char *buff;
- sqlite3 *db = NULL;
- CONF_PAIR *cp;
+ int status;
+ int ret;
+ char const *p;
+ char *buff;
+ sqlite3 *db = NULL;
+ CONF_PAIR *cp;
INFO("rlm_sql_sqlite: Database doesn't exist, creating it and loading schema");
goto unlink;
}
- if (sql_check_error(db) != RLM_SQL_OK) {
+ if (sql_check_error(db, status) != RLM_SQL_OK) {
(void) sqlite3_close(db);
goto unlink;
INFO("rlm_sql_sqlite: Opening SQLite database \"%s\"", driver->filename);
#ifdef HAVE_SQLITE3_OPEN_V2
status = sqlite3_open_v2(driver->filename, &(conn->db), SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX, NULL);
+ sqlite3_busy_timeout( conn->db, 200); /*wait up to 200 ms for db locks*/
#else
+
status = sqlite3_open(driver->filename, &(conn->db));
#endif
- if (!conn->db) {
-#ifdef HAVE_SQLITE3_ERRSTR
- ERROR("rlm_sql_sqlite: Failed creating opening/creating SQLite: %s", sqlite3_errstr(status));
-#else
- ERROR("rlm_sql_sqlite: Failed creating opening/creating SQLite database error code (%i)",
- status);
-#endif
+ if (!conn->db || (sql_check_error(conn->db, status) != RLM_SQL_OK)) {
+ sql_print_error(conn->db, status, "Error opening SQLite database \"%s\"", driver->filename);
return RLM_SQL_ERROR;
}
- if (sql_check_error(conn->db) != RLM_SQL_OK) return RLM_SQL_ERROR;
-
status = sqlite3_busy_timeout(conn->db, driver->busy_timeout);
- if (status != SQLITE_OK) ERROR("rlm_sql_sqlite: Failed setting busy timeout");
+ if (sql_check_error(conn->db, status) != RLM_SQL_OK) {
+ sql_print_error(conn->db, status, "Error setting busy timeout");
+ return RLM_SQL_ERROR;
+ }
/*
* Enable extended return codes for extra debugging info.
*/
#ifdef HAVE_SQLITE3_EXTENDED_RESULT_CODES
- (void) sqlite3_extended_result_codes(conn->db, 1);
+ status = sqlite3_extended_result_codes(conn->db, 1);
+ if (sql_check_error(conn->db, status) != RLM_SQL_OK) {
+ sql_print_error(conn->db, status, "Error enabling extended result codes");
+ return RLM_SQL_ERROR;
+ }
#endif
- if (sql_check_error(conn->db) != RLM_SQL_OK) return RLM_SQL_ERROR;
#ifdef HAVE_SQLITE3_CREATE_FUNCTION_V2
status = sqlite3_create_function_v2(conn->db, "GREATEST", -1, SQLITE_ANY, NULL,
status = sqlite3_create_function(conn->db, "GREATEST", -1, SQLITE_ANY, NULL,
_sql_greatest, NULL, NULL);
#endif
- if (status != SQLITE_OK) {
- ERROR("rlm_sql_sqlite: Failed registering 'GREATEST' sql function: %s", sqlite3_errmsg(conn->db));
+ if (sql_check_error(conn->db, status) != RLM_SQL_OK) {
+ sql_print_error(conn->db, status, "Failed registering 'GREATEST' sql function");
+ return RLM_SQL_ERROR;
}
return RLM_SQL_OK;
static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config, char const *query)
{
- rlm_sql_sqlite_conn_t *conn = handle->conn;
- char const *z_tail;
+ rlm_sql_sqlite_conn_t *conn = handle->conn;
+ char const *z_tail;
+ int status;
#ifdef HAVE_SQLITE3_PREPARE_V2
- (void) sqlite3_prepare_v2(conn->db, query, strlen(query), &conn->statement, &z_tail);
+ status = sqlite3_prepare_v2(conn->db, query, strlen(query), &conn->statement, &z_tail);
#else
- (void) sqlite3_prepare(conn->db, query, strlen(query), &conn->statement, &z_tail);
+ status = sqlite3_prepare(conn->db, query, strlen(query), &conn->statement, &z_tail);
#endif
conn->col_count = 0;
- return sql_check_error(conn->db);
+ return sql_check_error(conn->db, status);
}
static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config, char const *query)
{
- int status;
- rlm_sql_sqlite_conn_t *conn = handle->conn;
- char const *z_tail;
+
+ sql_rcode_t rcode;
+ rlm_sql_sqlite_conn_t *conn = handle->conn;
+ char const *z_tail;
+ int status;
#ifdef HAVE_SQLITE3_PREPARE_V2
status = sqlite3_prepare_v2(conn->db, query, strlen(query), &conn->statement, &z_tail);
#else
status = sqlite3_prepare(conn->db, query, strlen(query), &conn->statement, &z_tail);
#endif
- if (status != SQLITE_OK) return sql_check_error(conn->db);
-
- (void) sqlite3_step(conn->statement);
+ rcode = sql_check_error(conn->db, status);
+ if (rcode != RLM_SQL_OK) return rcode;
- return sql_check_error(conn->db);
+ status = sqlite3_step(conn->statement);
+ return sql_check_error(conn->db, status);
}
static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
char **row;
+ TALLOC_FREE(handle->row);
+
/*
* Executes the SQLite query and interates over the results
*/
/*
* Error getting next row
*/
- if (sql_check_error(conn->db) != RLM_SQL_OK) return RLM_SQL_ERROR;
+ if (sql_check_error(conn->db, status) != RLM_SQL_OK) return RLM_SQL_ERROR;
/*
* No more rows to process (were done)
*/
- if (status == SQLITE_DONE) {
- return 1;
- }
+ if (status == SQLITE_DONE) return RLM_SQL_NO_MORE_ROWS;
/*
* We only need to do this once per result set, because
if (conn->col_count == 0) return RLM_SQL_ERROR;
}
- /*
- * Free the previous result (also gets called on finish_query)
- */
- talloc_free(handle->row);
-
MEM(row = handle->row = talloc_zero_array(handle->conn, char *, conn->col_count + 1));
for (i = 0; i < conn->col_count; i++) {
}
}
- return 0;
+ return RLM_SQL_OK;
}
static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
* It's just the last error that occurred processing the
* statement.
*/
- return 0;
+ return RLM_SQL_OK;
}
/** Retrieves any errors associated with the connection handle
{
rlm_sql_sqlite_conn_t *conn = handle->conn;
- if (conn->db) {
- return sqlite3_changes(conn->db);
- }
+ if (conn->db) return sqlite3_changes(conn->db);
return -1;
}