|
@@ -0,0 +1,914 @@
|
|
|
+#include <stdio.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <string.h>
|
|
|
+#include <sqlite3.h>
|
|
|
+#include "sql.h"
|
|
|
+#include "common.h"
|
|
|
+
|
|
|
+/**
|
|
|
+ * NOTE: Remember to compile with linker flag
|
|
|
+ * TODO: Implement more functions from SQLite3 lib.
|
|
|
+ **/
|
|
|
+
|
|
|
+/**
|
|
|
+ * Structure: sql_db
|
|
|
+ * Typedef: sql_db_t
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Sections:
|
|
|
+ * char *db_file: Path to the DB-file
|
|
|
+ * sqlite3 *db_handle: SQLite3 handler
|
|
|
+ * sql_stmt_t *stmts: SQL-statemnts
|
|
|
+ * *lstmt: Last used SQL-statement (no function by now)
|
|
|
+ * int db_flags: Flags
|
|
|
+ * code: Status/error code
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Stores information about an SQL-handler and it relations.
|
|
|
+ **/
|
|
|
+struct sql_db {
|
|
|
+ char *db_file;
|
|
|
+ sqlite3 *db_handle;
|
|
|
+ sql_stmt_t *stmts,
|
|
|
+ *lstmt;
|
|
|
+ int db_flags,
|
|
|
+ code;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * Structure: sql_stmt
|
|
|
+ * Typedef: sql_stmt_t
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Sections:
|
|
|
+ * sqlite3_stmt *db_stmt: SQL-statement
|
|
|
+ * sql_db_t *sql: Related SQL-handler
|
|
|
+ * const char *db_query: Query string
|
|
|
+ * *tail: (Possibly) next query
|
|
|
+ * int cols: #of colums in result
|
|
|
+ * sql_stmt_t *next: Next statement
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Stores information about an SQL-statement and its relations.
|
|
|
+ *
|
|
|
+ **/
|
|
|
+struct sql_stmt {
|
|
|
+ sqlite3_stmt *db_stmt;
|
|
|
+ sql_db_t *sql;
|
|
|
+ const char *db_query,
|
|
|
+ *tail;
|
|
|
+ int cols;
|
|
|
+ sql_stmt_t *next;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_val_flags
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - int flags: Flags to check
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Validates the flag given, also between OR'd
|
|
|
+ * values. Returns 0 (to trigger error) on failure.
|
|
|
+ *
|
|
|
+ * Returns: int, the flag
|
|
|
+ **/
|
|
|
+static int sql_val_flags( int flags );
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_get_code
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_db_t *sql: The SQL-handler to read code from
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Returns the status code stored in the SQL-handler
|
|
|
+ *
|
|
|
+ * Returns: int
|
|
|
+ **/
|
|
|
+static int sql_get_code( sql_db_t *sql );
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_get_code_from_handle
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_db_t *sql: The SQL-handler to read code from
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Read status code internal (sqlite) SQL-handler.
|
|
|
+ *
|
|
|
+ * Returns: int
|
|
|
+ **/
|
|
|
+static int sql_get_code_from_handle( sql_db_t *sql );
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_set_code
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_db_t *sql: The SQL-handler to set code for
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Sets an new errorcode, based on sqlite.
|
|
|
+ *
|
|
|
+ * Returns: int, current (newly set) error code
|
|
|
+ **/
|
|
|
+static int sql_set_code( sql_db_t *sql );
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_set_internal_code
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_db_t *sql: The SQL-handerl to set code for
|
|
|
+ * - int code: Code to set
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Set internal (extended) error code.
|
|
|
+ *
|
|
|
+ * Returns: void
|
|
|
+ **/
|
|
|
+static int sql_set_internal_code( sql_db_t *sql, int code );
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_errstr
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - int code: Error code
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Returns the error string related to the given errcode.
|
|
|
+ *
|
|
|
+ * Returns: const char *, the error string
|
|
|
+ **/
|
|
|
+static const char *sql_errstr( int code );
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_add_stmt
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_db_t *sql: Owner (SQL-handler.)
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Adds a stement to the SQL-handler, to keep
|
|
|
+ * track of relation.
|
|
|
+ *
|
|
|
+ * Returns: sql_stmt_t *, added statement
|
|
|
+ **/
|
|
|
+static sql_stmt_t *sql_add_stmt( sql_db_t *sql );
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_create
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - char *db_file: The DB-file to open
|
|
|
+ * - int flags: The flags, doesnt support MUTEX and CACHE flags
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Creates an SQL-object; init the SQL
|
|
|
+ *
|
|
|
+ * Returns: sql_db_t *, db object
|
|
|
+ **/
|
|
|
+sql_db_t *sql_create( char *db_file, int flags ) {
|
|
|
+
|
|
|
+ sql_db_t *sql = ( sql_db_t *) malloc( sizeof( sql_db_t ) );
|
|
|
+
|
|
|
+ if ( sql == NULL )
|
|
|
+ errandend( "Unable to allocate memory for the database structure." );
|
|
|
+
|
|
|
+ sql->db_file = strdup( db_file );
|
|
|
+ sql->db_flags = sql_val_flags( flags );
|
|
|
+ sql->stmts = NULL;
|
|
|
+
|
|
|
+ sqlite3_open_v2( sql->db_file, &sql->db_handle, sql->db_flags, NULL );
|
|
|
+ sql_set_code( sql );
|
|
|
+
|
|
|
+ return sql;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_ok
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_db_t *sql: The SQL-handler to check.
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Checks if last activity agains the DB (e.g a query)
|
|
|
+ * was successfull.
|
|
|
+ *
|
|
|
+ * Returns: int, 1 on success, 0 on failure
|
|
|
+ **/
|
|
|
+int sql_ok( sql_db_t *sql ) {
|
|
|
+ return sql == NULL ? 0 : sql->code >= SQL_NOERR_LIMIT;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_done
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_db_t *sql: The SQL-handler to check.
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Determines if the SQL-task is done.
|
|
|
+ *
|
|
|
+ * Returns: int, 1 on success, 0 on failure
|
|
|
+ **/
|
|
|
+int sql_done( sql_db_t *sql ) {
|
|
|
+ return sql == NULL ? 1 : sql->code == SQL_DONE;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_errmsg
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_db_t *sql: The SQL-handler to look for errors in.
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Returns the corresponding SQL-error message.
|
|
|
+ * If sql_ok() returns an error, there is an
|
|
|
+ * error present.
|
|
|
+ *
|
|
|
+ * Returns: char *, the error message
|
|
|
+ **/
|
|
|
+const char *sql_errmsg( sql_db_t *sql ) {
|
|
|
+
|
|
|
+ int code = sql_get_code( sql );
|
|
|
+
|
|
|
+ /*printf( // Debugging purposes!!
|
|
|
+ "DB OBJECT:\t\t%p\n- Filename\t:\t%s\n- Flags:\t\t%d\n- Internal code:\t%d\n- SQLITE code:\t\t%d\n- Query:\n\"%s\"\n",
|
|
|
+ sql, sql->db_file, sql->db_flags, sql->code, code, sql->lstmt != NULL ? sql->lstmt->db_query : NULL
|
|
|
+ );*/
|
|
|
+
|
|
|
+ return code < SQL_ERR_LIMIT ? sql_errstr( code ) : sqlite3_errstr( code );
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_close
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_db_t *sql: The SQL-handler to close
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Close an SQL-handler. This will also free related
|
|
|
+ * statements for the handler, which as unessacary.
|
|
|
+ * Statements should be finalized by designer, but are
|
|
|
+ * implemented as a "gb-collection".
|
|
|
+ *
|
|
|
+ * Returns: void
|
|
|
+ **/
|
|
|
+void sql_close( sql_db_t *sql ) {
|
|
|
+
|
|
|
+ sql_stmt_t *stmt;
|
|
|
+
|
|
|
+ if ( sql == NULL )
|
|
|
+ return;
|
|
|
+
|
|
|
+ stmt = sql->stmts;
|
|
|
+
|
|
|
+ while( stmt != NULL ) {
|
|
|
+ sql_stmt_finalize( stmt );
|
|
|
+ stmt = stmt->next;
|
|
|
+ }
|
|
|
+
|
|
|
+ sqlite3_close_v2( sql->db_handle );
|
|
|
+ free( sql->db_file );
|
|
|
+ free( sql );
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_query
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_db_t *sql: The SQL-handler to query agains
|
|
|
+ * - const char *query: The SQL-statement/-query
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Creates an SQL-statement and queries the DB.
|
|
|
+ *
|
|
|
+ * Returns: sql_stmt_t *, the SQL-statement object.
|
|
|
+ **/
|
|
|
+sql_stmt_t *sql_query( sql_db_t *sql, const char *query ) {
|
|
|
+
|
|
|
+ sql_stmt_t *stmt;
|
|
|
+
|
|
|
+ if ( sql == NULL )
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ stmt = sql_add_stmt( sql );
|
|
|
+
|
|
|
+ if ( stmt == NULL ) {
|
|
|
+ sql_set_internal_code( sql, SQL_OUTOFMEM );
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ stmt->db_query = strdup( (char *)query );
|
|
|
+
|
|
|
+ sqlite3_prepare_v2( sql->db_handle, stmt->db_query, strlen( query ), &stmt->db_stmt, &stmt->tail );
|
|
|
+ sql_set_code( sql );
|
|
|
+
|
|
|
+ sql->lstmt = stmt;
|
|
|
+
|
|
|
+ stmt->cols = sqlite3_column_count( stmt->db_stmt );
|
|
|
+
|
|
|
+ return stmt;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_column_count
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_stmt_t *stmt: SQL-statement to check
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Returns the amount of colums returned from a query
|
|
|
+ *
|
|
|
+ * Returns: int, #colums returned
|
|
|
+ **/
|
|
|
+int sql_column_count( sql_stmt_t *stmt ) {
|
|
|
+ return stmt->cols;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_column_name
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_stmt_t *stmt: SQL-statment to read from
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Returns the columname specified by the index.
|
|
|
+ *
|
|
|
+ * Returns: char *, colum name
|
|
|
+ **/
|
|
|
+const char *sql_column_name( sql_stmt_t *stmt, int idx ) {
|
|
|
+ return idx <= stmt->cols ? sqlite3_column_name( stmt->db_stmt, idx ) : NULL;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_column_text
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_stmt_t *stmt: SQL-statement to read from
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Return the text stored in the column specified by the index.
|
|
|
+ *
|
|
|
+ * Returns: const unsigned char *, column text
|
|
|
+ **/
|
|
|
+const unsigned char *sql_column_text( sql_stmt_t *stmt, int idx ) {
|
|
|
+ return idx <= stmt->cols ? sqlite3_column_text( stmt->db_stmt, idx ) : NULL;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_stmt_step
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_stmt_t *stmt: SQL-statement to read from
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Go to next entry in the retured query.
|
|
|
+ *
|
|
|
+ * Returns: int, 1 on success, 0 on failure.
|
|
|
+ **/
|
|
|
+int sql_stmt_step( sql_stmt_t *stmt ) {
|
|
|
+
|
|
|
+ if ( stmt == NULL )
|
|
|
+ return SQL_DONE;
|
|
|
+
|
|
|
+ sqlite3_step( stmt->db_stmt );
|
|
|
+
|
|
|
+ return sql_set_code( sql_stmt_sql( stmt ) );
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_stmt_sql
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_stmt_t *stmt: SQL-statement to load handler from
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Returns the related SQL-handler for the SQL-statement.
|
|
|
+ *
|
|
|
+ * Returns: sql_db_t *, SQL-object
|
|
|
+ **/
|
|
|
+sql_db_t *sql_stmt_sql( sql_stmt_t *stmt ) {
|
|
|
+ return stmt == NULL ? NULL : stmt->sql;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_stmt_tail
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_stmt_t *stmt: The parent statement
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Queries the SQL with the next tail-query.
|
|
|
+ *
|
|
|
+ * Note: This finalizes the parent statement.
|
|
|
+ *
|
|
|
+ * Returns: sql_stmt_t *, tail statement
|
|
|
+ **/
|
|
|
+sql_stmt_t *sql_stmt_tail( sql_stmt_t *stmt ) {
|
|
|
+
|
|
|
+ sql_stmt_t *new_stmt;
|
|
|
+
|
|
|
+ if ( stmt == NULL || ( stmt != NULL && stmt->tail[0] == '\0' ) )
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ // Query tail
|
|
|
+ new_stmt = sql_query( sql_stmt_sql( stmt ), stmt->tail );
|
|
|
+
|
|
|
+ // End current.
|
|
|
+ sql_stmt_finalize( stmt );
|
|
|
+
|
|
|
+ return new_stmt;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_stmt_finalize
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_stmt_t *stmt: SQL-statement to finalize
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Finalizes an SQL-statement AKA closing it.
|
|
|
+ *
|
|
|
+ * Returns: void
|
|
|
+ **/
|
|
|
+void sql_stmt_finalize( sql_stmt_t *stmt ) {
|
|
|
+
|
|
|
+ sql_db_t *sql;
|
|
|
+ sql_stmt_t *tmp;
|
|
|
+
|
|
|
+ if ( stmt == NULL )
|
|
|
+ return;
|
|
|
+
|
|
|
+ sql = sql_stmt_sql( stmt );
|
|
|
+ tmp = sql->stmts;
|
|
|
+
|
|
|
+
|
|
|
+ if ( tmp == stmt ) // if stmt is head of list; make next to head
|
|
|
+ sql->stmts = tmp->next;
|
|
|
+ else {
|
|
|
+
|
|
|
+ // Find stmt in list
|
|
|
+ while( tmp != NULL && tmp->next != stmt )
|
|
|
+ tmp = tmp->next;
|
|
|
+
|
|
|
+ // Merge prepending with append aka remove stmt
|
|
|
+ if ( tmp != NULL )
|
|
|
+ tmp->next = tmp->next == NULL ? NULL : tmp->next->next;
|
|
|
+ }
|
|
|
+
|
|
|
+ free( (char *)stmt->db_query );
|
|
|
+ sqlite3_finalize( stmt->db_stmt );
|
|
|
+ free( stmt );
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_val_flags
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - int flags: Flags to check
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Validates the flag given, also between OR'd
|
|
|
+ * values. Returns 0 (to trigger error) on failure.
|
|
|
+ *
|
|
|
+ * Returns: int, the flag
|
|
|
+ **/
|
|
|
+static int sql_val_flags( int flags ) {
|
|
|
+
|
|
|
+ if ( !(flags ^ (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)) )
|
|
|
+ return (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
|
|
|
+
|
|
|
+ if ( !(flags ^ SQLITE_OPEN_READWRITE) )
|
|
|
+ return SQLITE_OPEN_READWRITE;
|
|
|
+
|
|
|
+ if ( !( flags ^ SQLITE_OPEN_READONLY ) )
|
|
|
+ return SQLITE_OPEN_READONLY;
|
|
|
+
|
|
|
+ return flags;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_get_code
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_db_t *sql: The SQL-handler to read code from
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Returns the status code stored in the SQL-handler
|
|
|
+ *
|
|
|
+ * Returns: int
|
|
|
+ **/
|
|
|
+static int sql_get_code( sql_db_t *sql ) {
|
|
|
+ return sql->code >= SQL_NOERR_LIMIT ? sql->code - SQL_NOERR_LIMIT : ( sql->code < SQL_ERR_LIMIT ? sql->code : sql->code + -(SQL_ERR_LIMIT) );
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_get_code_from_handle
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_db_t *sql: The SQL-handler to read code from
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Read status code internal (sqlite) SQL-handler.
|
|
|
+ *
|
|
|
+ * Returns: int
|
|
|
+ **/
|
|
|
+static int sql_get_code_from_handle( sql_db_t *sql ) {
|
|
|
+ return sqlite3_errcode( sql->db_handle );
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_set_code
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_db_t *sql: The SQL-handler to set code for
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Sets an new errorcode, based on sqlite.
|
|
|
+ *
|
|
|
+ * Returns: int, current (newly set) error code
|
|
|
+ **/
|
|
|
+static int sql_set_code( sql_db_t *sql ) {
|
|
|
+
|
|
|
+ int res;
|
|
|
+
|
|
|
+ res = sql_get_code_from_handle( sql );
|
|
|
+
|
|
|
+ switch ( res ) {
|
|
|
+
|
|
|
+ case SQLITE_OK: sql->code = SQL_OK; break;
|
|
|
+ case SQLITE_ROW: sql->code = SQL_ROW; break;
|
|
|
+ case SQLITE_DONE: sql->code = SQL_DONE; break;
|
|
|
+
|
|
|
+ case SQLITE_ERROR: sql->code = SQL_ERROR; break;
|
|
|
+ case SQLITE_INTERNAL: sql->code = SQL_INTERNAL; break;
|
|
|
+ case SQLITE_PERM: sql->code = SQL_PERM; break;
|
|
|
+ case SQLITE_ABORT: sql->code = SQL_ABORT; break;
|
|
|
+ case SQLITE_BUSY: sql->code = SQL_BUSY; break;
|
|
|
+ case SQLITE_LOCKED: sql->code = SQL_LOCKED; break;
|
|
|
+ case SQLITE_NOMEM: sql->code = SQL_NOMEM; break;
|
|
|
+ case SQLITE_READONLY: sql->code = SQL_READONLY; break;
|
|
|
+ case SQLITE_INTERRUPT: sql->code = SQL_INTERRUPT; break;
|
|
|
+ case SQLITE_IOERR: sql->code = SQL_IOERR; break;
|
|
|
+ case SQLITE_CORRUPT: sql->code = SQL_CORRUPT; break;
|
|
|
+ case SQLITE_NOTFOUND: sql->code = SQL_NOTFOUND; break;
|
|
|
+ case SQLITE_FULL: sql->code = SQL_FULL; break;
|
|
|
+ case SQLITE_CANTOPEN: sql->code = SQL_CANTOPEN; break;
|
|
|
+ case SQLITE_PROTOCOL: sql->code = SQL_PROTOCOL; break;
|
|
|
+ case SQLITE_EMPTY: sql->code = SQL_EMPTY; break;
|
|
|
+ case SQLITE_SCHEMA: sql->code = SQL_SCHEMA; break;
|
|
|
+ case SQLITE_TOOBIG: sql->code = SQL_TOOBIG; break;
|
|
|
+ case SQLITE_CONSTRAINT: sql->code = SQL_CONSTRAINT; break;
|
|
|
+ case SQLITE_MISMATCH: sql->code = SQL_MISMATCH; break;
|
|
|
+ case SQLITE_MISUSE: sql->code = SQL_MISUSE; break;
|
|
|
+ case SQLITE_NOLFS: sql->code = SQL_NOLFS; break;
|
|
|
+ case SQLITE_AUTH: sql->code = SQL_AUTH; break;
|
|
|
+ case SQLITE_FORMAT: sql->code = SQL_FORMAT; break;
|
|
|
+ case SQLITE_RANGE: sql->code = SQL_RANGE; break;
|
|
|
+ case SQLITE_NOTADB: sql->code = SQL_NOTADB; break;
|
|
|
+
|
|
|
+ default:
|
|
|
+
|
|
|
+ printf("OTHER CODE: %d\n", res );
|
|
|
+
|
|
|
+ if ( res > 101 )
|
|
|
+ sql->code = res + -(SQL_ERR_LIMIT);
|
|
|
+ else
|
|
|
+ errandend( "Undefined error-code specified.\n" );
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return sql->code;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_set_internal_code
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_db_t *sql: The SQL-handerl to set code for
|
|
|
+ * - int code: Code to set
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Set internal (extended) error code.
|
|
|
+ *
|
|
|
+ * Returns: void
|
|
|
+ **/
|
|
|
+static int sql_set_internal_code( sql_db_t *sql, int code ) {
|
|
|
+
|
|
|
+ switch( code ) {
|
|
|
+ case SQL_OUTOFMEM:
|
|
|
+ case SQL_NULL_ABORTION:
|
|
|
+ sql->code = code;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ errandend( "Implementation error: Code unknown." );
|
|
|
+ }
|
|
|
+
|
|
|
+ return sql->code;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_errstr
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - int code: Error code
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Returns the error string related to the given errcode.
|
|
|
+ *
|
|
|
+ * Returns: const char *, the error string
|
|
|
+ **/
|
|
|
+static const char *sql_errstr( int code ) {
|
|
|
+
|
|
|
+ char *errstr;
|
|
|
+
|
|
|
+ switch( code ) {
|
|
|
+
|
|
|
+ case SQL_OUTOFMEM:
|
|
|
+ errstr = "Out of memory";
|
|
|
+ break;
|
|
|
+
|
|
|
+ case SQL_NULL_ABORTION:
|
|
|
+ errstr = "Aborting: NULL pointer.";
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ errstr = "Undefined error [SQL WRAPPER]";
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return (const char *)errstr;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Function: sql_add_stmt
|
|
|
+ * Author: Joachim M. Giæver
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * - sql_db_t *sql: Owner (SQL-handler.)
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * Adds a stement to the SQL-handler, to keep
|
|
|
+ * track of relation.
|
|
|
+ *
|
|
|
+ * Returns: sql_stmt_t *, added statement
|
|
|
+ **/
|
|
|
+static sql_stmt_t *sql_add_stmt( sql_db_t *sql ) {
|
|
|
+
|
|
|
+ sql_stmt_t *tmp, *stmt;
|
|
|
+
|
|
|
+ tmp = sql->stmts;
|
|
|
+
|
|
|
+ stmt = ( sql_stmt_t *) malloc( sizeof( sql_stmt_t ) );
|
|
|
+
|
|
|
+ if ( stmt == NULL ) {
|
|
|
+ sql_set_internal_code( sql, SQL_OUTOFMEM );
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Put last in list.
|
|
|
+ stmt->next = NULL;
|
|
|
+ stmt->sql = sql;
|
|
|
+
|
|
|
+ if ( tmp == NULL ) {
|
|
|
+ sql->stmts = stmt;
|
|
|
+ return stmt;
|
|
|
+ }
|
|
|
+
|
|
|
+ while( tmp != NULL && tmp->next != NULL )
|
|
|
+ tmp = tmp->next;
|
|
|
+
|
|
|
+ tmp->next = stmt;
|
|
|
+
|
|
|
+ return stmt;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/***
|
|
|
+ * THIS IS FOR DEBUGGING OF THE SQL WRAPPER;
|
|
|
+ * To run this make project as:
|
|
|
+ * $ make sql-test
|
|
|
+ * and run by
|
|
|
+ * $ ./sql-test
|
|
|
+ **/
|
|
|
+
|
|
|
+#ifdef _DEBUG_SQL
|
|
|
+
|
|
|
+int main( int argc, char const *argv[] ) {
|
|
|
+
|
|
|
+ int i;
|
|
|
+
|
|
|
+ /* Query strings */
|
|
|
+ char *iquery, *query1, *query2, *query3, *query4;
|
|
|
+
|
|
|
+ /* DB statements */
|
|
|
+ sql_stmt_t *istmt, *stmt1, *stmt2, *stmt3, *stmt4;
|
|
|
+
|
|
|
+ /* DB Connection */
|
|
|
+ sql_db_t *sql = sql_create( "./sql.sqlite3", SQLITE_OPEN_READONLY );
|
|
|
+
|
|
|
+ if ( !sql_ok( sql ) ) {
|
|
|
+ printf( "Error: %s\n", sql_errmsg( sql ) );
|
|
|
+ goto end_test;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Various query strings, note query3 is multiple **/
|
|
|
+ query1 = "SELECT orderNumber,orderDate from Orders where status = 'Cancelled'";
|
|
|
+ query2 = "SELECT C.customerName AS Customer, SUM(OD.quantityOrdered) AS Total " \
|
|
|
+ "FROM Orders O, Customers C, OrderDetails OD " \
|
|
|
+ "WHERE O.customerNumber = C.customerNumber " \
|
|
|
+ " AND O.orderNumber = OD.orderNumber " \
|
|
|
+ "GROUP BY O.customerNumber " \
|
|
|
+ "ORDER BY Total DESC;";
|
|
|
+ query3 = "SELECT orderNumber,orderDate from Orders where status = 'Cancelled';"\
|
|
|
+ "SELECT orderNumber,orderDate from Orders where status != 'Cancelled';"\
|
|
|
+ "SELECT P.productName, T.totalQuantityOrdered " \
|
|
|
+ "FROM Products P NATURAL JOIN " \
|
|
|
+ " (SELECT productCode, SUM(quantityOrdered) AS totalQuantityOrdered " \
|
|
|
+ " FROM OrderDetails GROUP BY productCode) AS T " \
|
|
|
+ "WHERE T.totalQuantityOrdered >= 1000";
|
|
|
+ query4 = "SELECT customerName FROM Customers";
|
|
|
+ iquery = "INSERT INTO Customers (customerName, contactFirstName, contactLastName, phone, addressLine1, city, country) VALUES('Rotor klubben', 'Anna', 'Banana', 123456789, 'Flystripa', 'Tromsø', 'Noreg');";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Do insert
|
|
|
+ **/
|
|
|
+
|
|
|
+ puts("\nQUERY insert");
|
|
|
+ istmt = sql_query( sql, (const char *)iquery);
|
|
|
+
|
|
|
+ if ( !sql_ok( sql ) ) {
|
|
|
+ printf("Error on insert: %s\n", sql_errmsg( sql ));
|
|
|
+ goto query1_start;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Just to prevent notice about unused...
|
|
|
+ if ( istmt == NULL);
|
|
|
+ /**
|
|
|
+ * Do query 1:
|
|
|
+ **/
|
|
|
+query1_start:
|
|
|
+ puts("\nQUERY 1");
|
|
|
+ stmt1 = sql_query( sql, (const char *)query1 );
|
|
|
+
|
|
|
+ // Look for errors
|
|
|
+ if ( !sql_ok( sql ) ) {
|
|
|
+ printf("Error on query 1: %s\n", sql_errmsg( sql ) );
|
|
|
+ goto query2_start; // Jumt over rest in query 1
|
|
|
+ }
|
|
|
+
|
|
|
+ // Loop through all columns
|
|
|
+ for ( i = 0; i < sql_column_count( stmt1 ); i++ )
|
|
|
+ printf( "Col: %s\n", sql_column_name( stmt1, i ) );
|
|
|
+
|
|
|
+ // End statement
|
|
|
+ sql_stmt_finalize( stmt1 );
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Do query 2:
|
|
|
+ **/
|
|
|
+query2_start:
|
|
|
+ puts("\nQUERY 2");
|
|
|
+ stmt2 = sql_query( sql, (const char *)query2 );
|
|
|
+
|
|
|
+ // And do the same as query 1..
|
|
|
+
|
|
|
+ if ( !sql_ok( sql ) ) {
|
|
|
+ printf("Error on query 2: %s\n", sql_errmsg( sql ) );
|
|
|
+ goto query3_start; // Jumt over rest in query 2
|
|
|
+ }
|
|
|
+
|
|
|
+ for ( i = 0; i < sql_column_count( stmt2 ); i++ )
|
|
|
+ printf( "Col: %s\n", sql_column_name( stmt2, i ) );
|
|
|
+
|
|
|
+ sql_stmt_finalize( stmt2 );
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Do query 3: A multiple query string
|
|
|
+ **/
|
|
|
+query3_start:
|
|
|
+ puts("\nQUERY 3");
|
|
|
+ stmt3 = sql_query( sql, (const char *)query3 );
|
|
|
+
|
|
|
+ // Loop through all statement
|
|
|
+ while( stmt3 != NULL ) {
|
|
|
+
|
|
|
+ if ( !sql_ok( sql ) ) {
|
|
|
+ printf("Error on query 3: %s\n", sql_errmsg( sql ) );
|
|
|
+ goto query4_start;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Make some output **/
|
|
|
+ for ( i = 0; i < sql_column_count( stmt3 ); i++ ) {
|
|
|
+
|
|
|
+ printf( "%s:\n", sql_column_name( stmt3, i ) );
|
|
|
+
|
|
|
+ sql_stmt_step( stmt3 );
|
|
|
+
|
|
|
+ while( !sql_done( sql_stmt_sql( stmt3 ) ) ) {
|
|
|
+
|
|
|
+ printf("%s", sql_column_text( stmt3, i ) );
|
|
|
+
|
|
|
+ sql_stmt_step( stmt3 );
|
|
|
+
|
|
|
+ if ( !sql_done( sql_stmt_sql( stmt3 ) ) )
|
|
|
+ printf(", ");
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( sql_done( sql_stmt_sql( stmt3 ) ) )
|
|
|
+ printf("\n\n");
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ stmt3 = sql_stmt_tail( stmt3 );
|
|
|
+
|
|
|
+ i++;
|
|
|
+
|
|
|
+ }
|
|
|
+query4_start:
|
|
|
+ puts("\nQUERY 4");
|
|
|
+ stmt4 = sql_query( sql, (const char *)query4 );
|
|
|
+
|
|
|
+ if ( !sql_ok( sql ) ) {
|
|
|
+ printf("Error on query 4: %s\n", sql_errmsg( sql ) );
|
|
|
+ goto end_test; // End test
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Make some output **/
|
|
|
+ for ( i = 0; i < sql_column_count( stmt4 ); i++ ) {
|
|
|
+
|
|
|
+ printf( "%s:\n", sql_column_name( stmt4, i ) );
|
|
|
+
|
|
|
+ sql_stmt_step( stmt4 );
|
|
|
+
|
|
|
+ while( !sql_done( sql_stmt_sql( stmt4 ) ) ) {
|
|
|
+
|
|
|
+ printf("%s", sql_column_text( stmt4, i ) );
|
|
|
+
|
|
|
+ sql_stmt_step( stmt4 );
|
|
|
+
|
|
|
+ if ( !sql_done( sql_stmt_sql( stmt4 ) ) )
|
|
|
+ printf(", ");
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( sql_done( sql_stmt_sql( stmt4 ) ) )
|
|
|
+ printf("\n\n");
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ sql_stmt_finalize( stmt3 ):
|
|
|
+ SQL-close finalizes stmt3
|
|
|
+ */
|
|
|
+
|
|
|
+end_test:
|
|
|
+
|
|
|
+ sql_close( sql );
|
|
|
+
|
|
|
+ printf( "Exists. Test completed.\n" );
|
|
|
+
|
|
|
+ return EXIT_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+#endif
|