class Amalgalite::SQLite3::Database

The ruby extension wrapper around the core sqlite3 database object.

Public Class Methods

Amalgalite::SQLite3::Database.open( filename, flags = READWRITE | CREATE ) → Database click to toggle source

Create a new SQLite2 database with a UTF-8 encoding.

VALUE am_sqlite3_database_open(int argc, VALUE *argv, VALUE class)
{
    VALUE  self = am_sqlite3_database_alloc(class);
    VALUE  rFlags;
    VALUE  rFilename;
    int     flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
    char*   filename;
    int     rc;
    am_sqlite3* am_db;

    /* at least a filename argument is required */
    rb_scan_args( argc, argv, "11", &rFilename, &rFlags );

    /* convert flags to the sqlite version */
    flags  = ( Qnil == rFlags ) ? flags : FIX2INT(rFlags);
    filename = StringValuePtr(rFilename);

    /* extract the sqlite3 wrapper struct */
    Data_Get_Struct(self, am_sqlite3, am_db);

    /* open the sqlite3 database */
    rc = sqlite3_open_v2( filename, &(am_db->db), flags, 0);
    if ( SQLITE_OK != rc ) {
        rb_raise(eAS_Error, "Failure to open database %s : [SQLITE_ERROR %d] : %s\n",
                filename, rc, sqlite3_errmsg(am_db->db));
    }

    /* by default turn on the extended result codes */
    rc = sqlite3_extended_result_codes( am_db->db, 1);
    if ( SQLITE_OK != rc ) {
        rb_raise(eAS_Error, "Failure to set extended result codes %s : [SQLITE_ERROR %d] : %s\n",
                filename, rc, sqlite3_errmsg(am_db->db));
    }

    return self;
}
Amalgalite::SQLite3::Database.open16( filename ) → SQLite3::Database click to toggle source

Create a new SQLite3 database with a UTF-16 encoding

VALUE am_sqlite3_database_open16(VALUE class, VALUE rFilename)
{
    VALUE       self = am_sqlite3_database_alloc(class);
    char*       filename = StringValuePtr(rFilename);
    am_sqlite3* am_db;
    int         rc;

    Data_Get_Struct(self, am_sqlite3, am_db);
    rc = sqlite3_open16( filename, &(am_db->db) );
    if ( SQLITE_OK != rc ) {
        rb_raise(eAS_Error, "Failure to open UTF-16 database %s : [SQLITE_ERROR %d] : %s\n",
                filename, rc, sqlite3_errmsg( am_db->db ));
    }

    /* by default turn on the extended result codes */
    rc = sqlite3_extended_result_codes( am_db->db, 1);
    if ( SQLITE_OK != rc ) {
        rb_raise(eAS_Error, "Failure to set extended result codes on UTF-16 database %s : [SQLITE_ERROR %d] : %s\n",
                filename, rc, (char*)sqlite3_errmsg16(am_db->db));
    }

    return self;
}

Public Instance Methods

autocommit? → true or false click to toggle source

return true if the database is in autocommit mode, otherwise return false

VALUE am_sqlite3_database_is_autocommit(VALUE self)
{
    am_sqlite3   *am_db;
    int           rc;

    Data_Get_Struct(self, am_sqlite3, am_db);
    rc = sqlite3_get_autocommit( am_db->db );

    return ( 0 == rc ) ? Qfalse : Qtrue ;
}
busy_handler( proc_like or nil ) click to toggle source

register a busy handler. If the argument is nil, then an existing busy handler is removed. Otherwise the argument is registered as the busy handler.

VALUE am_sqlite3_database_busy_handler( VALUE self, VALUE handler )
{
    am_sqlite3   *am_db;
    int           rc;

    Data_Get_Struct(self, am_sqlite3, am_db);

    /* Removing a busy handler case, remove it from sqlite and then remove it
     * from the garbage collector if it existed */
    if ( Qnil == handler ) {
        rc = sqlite3_busy_handler( am_db->db, NULL, NULL );
        if ( SQLITE_OK != rc ) {
            rb_raise(eAS_Error, "Failure removing busy handler : [SQLITE_ERROR %d] : %s\n", 
                    rc, sqlite3_errmsg( am_db->db ));
        }
        if ( Qnil != am_db->busy_handler_obj ) {
            rb_gc_unregister_address( &(am_db->busy_handler_obj) );
        }
    } else {
        /* installing a busy handler
         * - register it with sqlite
         * - keep a reference for ourselves with our database handle
         * - registere the handler reference with the garbage collector
         */
        rc = sqlite3_busy_handler( am_db->db, amalgalite_xBusy, (void*)handler );
        if ( SQLITE_OK != rc ) {
            rb_raise(eAS_Error, "Failure setting busy handler : [SQLITE_ERROR %d] : %s\n", 
                    rc, sqlite3_errmsg( am_db->db ));
        }
        am_db->busy_handler_obj = handler;
        rb_gc_register_address( &(am_db->busy_handler_obj) );
    }
    return Qnil;
}
close click to toggle source

Close the database

VALUE am_sqlite3_database_close(VALUE self)
{
    am_sqlite3   *am_db;
    int           rc = 0;

    Data_Get_Struct(self, am_sqlite3, am_db);
    rc = sqlite3_close( am_db->db );
    am_db->db = NULL;
    if ( SQLITE_OK != rc ) {
        rb_raise(eAS_Error, "Failure to close database : [SQLITE_ERROR %d] : %s\n",
                rc, sqlite3_errmsg( am_db->db ));
    }

    return self;

}
define_aggregate( name, arity, klass ) click to toggle source

register the given klass to be invoked as an sql aggregate.

VALUE am_sqlite3_database_define_aggregate( VALUE self, VALUE name, VALUE arity, VALUE klass )
{
    am_sqlite3   *am_db;
    int           rc;
    char*         zFunctionName = RSTRING_PTR(name);
    int           nArg = FIX2INT( arity );

    Data_Get_Struct(self, am_sqlite3, am_db);
    rc = sqlite3_create_function( am_db->db,
                                  zFunctionName, nArg,
                                  SQLITE_UTF8,
                                  (void *)klass,
                                  NULL, /* for scalar functions, not used here */
                                  amalgalite_xStep,
                                  amalgalite_xFinal);
    if ( SQLITE_OK != rc ) {
        /* in the case of SQLITE_MISUSE the error message in the database may
         * not be set.  In this case, hardcode the error. 
         * http://sqlite.org/c3ref/errcode.html
         *
         * This is a result of 3.6.15 which has sqlite3_create_function return
         * SQLITE_MISUSE intead of SQLITE_ERROR if called with incorrect
         * parameters.
         */
       if ( SQLITE_MISUSE == rc ) { 
         rb_raise(eAS_Error, "Failure defining SQL function '%s' with arity '%d' : [SQLITE_ERROR %d] : Library used incorrectly\n",
                zFunctionName, nArg, rc);
       } else {
         rb_raise(eAS_Error, "Failure defining SQL function '%s' with arity '%d' : [SQLITE_ERROR %d] : %s\n",
                zFunctionName, nArg, rc, sqlite3_errmsg( am_db->db ));
       }
    }
    rb_gc_register_address( &klass );
    return Qnil;
}
define_function( name, proc_like ) click to toggle source

register the given function to be invoked as an sql function.

VALUE am_sqlite3_database_define_function( VALUE self, VALUE name, VALUE proc_like )
{
    am_sqlite3   *am_db;
    int           rc;
    VALUE         arity = rb_funcall( proc_like, rb_intern( "arity" ), 0 );
    char*         zFunctionName = RSTRING_PTR(name);
    int           nArg = FIX2INT( arity );

    Data_Get_Struct(self, am_sqlite3, am_db);
    rc = sqlite3_create_function( am_db->db,
                                  zFunctionName, nArg,
                                  SQLITE_UTF8,
                                  (void *)proc_like, amalgalite_xFunc,
                                  NULL, NULL);
    if ( SQLITE_OK != rc ) {
        /* in the case of SQLITE_MISUSE the error message in the database may
         * not be set.  In this case, hardcode the error. 
         * http://sqlite.org/c3ref/errcode.html
         *
         * This is a result of 3.6.15 which has sqlite3_create_function return
         * SQLITE_MISUSE intead of SQLITE_ERROR if called with incorrect
         * parameters.
         */
       if ( SQLITE_MISUSE == rc ) { 
         rb_raise(eAS_Error, "Failure defining SQL function '%s' with arity '%d' : [SQLITE_ERROR %d] : Library used incorrectly\n",
                zFunctionName, nArg, rc);
       } else {
         rb_raise(eAS_Error, "Failure defining SQL function '%s' with arity '%d' : [SQLITE_ERROR %d] : %s\n",
                zFunctionName, nArg, rc, sqlite3_errmsg( am_db->db ));
       }
    }
    rb_gc_register_address( &proc_like );
    return Qnil;
}
execute_batch(p1) click to toggle source

call-seqL

database.execute_batch( sqls ) -> Boolean

Execute the statements in a batch.

VALUE am_sqlite3_database_exec(VALUE self, VALUE rSQL)
{
  VALUE        sql = StringValue( rSQL );
  am_sqlite3  *am_db;
  int          rc;

  Data_Get_Struct(self, am_sqlite3, am_db);
  
  rc = sqlite3_exec( am_db->db, RSTRING_PTR(sql), NULL, NULL, NULL );

  if ( SQLITE_OK != rc ){
    rb_raise( eAS_Error, "Failed to execute bulk statements: [SQLITE_ERROR %d] : %s\n",
	      rc, sqlite3_errmsg(am_db->db));
  }

  /* Presume that nobody will want to batch execute
     more than a Fixnum's worth of statements */
  return Qtrue;
}
interrupt! click to toggle source

Cause another thread with a handle on this database to be interrupted and return at the earliest opportunity as interrupted.

VALUE am_sqlite3_database_interrupt_bang( VALUE self )
{
    am_sqlite3  *am_db;

    Data_Get_Struct(self, am_sqlite3, am_db);
    sqlite3_interrupt( am_db->db );
    return Qnil;
}
last_error_code → Integer click to toggle source

return the last error code that happened in the database

VALUE am_sqlite3_database_last_error_code(VALUE self)
{
    am_sqlite3   *am_db;
    int           code;

    Data_Get_Struct(self, am_sqlite3, am_db);
    code = sqlite3_errcode( am_db->db );

    return INT2FIX( code );
}
last_error_message → String click to toggle source

return the last error message that happened in the database

VALUE am_sqlite3_database_last_error_message(VALUE self)
{
    am_sqlite3   *am_db;
    const char   *message;

    Data_Get_Struct(self, am_sqlite3, am_db);
    message = sqlite3_errmsg( am_db->db );

    return rb_str_new2( message );
}
last_insert_rowid → Integer click to toggle source

Return the rowid of the last row inserted into the database from this database connection.

VALUE am_sqlite3_database_last_insert_rowid(VALUE self)
{
    am_sqlite3   *am_db;
    sqlite3_int64 last_id;

    Data_Get_Struct(self, am_sqlite3, am_db);
    last_id = sqlite3_last_insert_rowid( am_db->db );

    return SQLINT64_2NUM( last_id );
}
prepare( sql ) → SQLite3::Statement click to toggle source

Create a new SQLite3 statement.

VALUE am_sqlite3_database_prepare(VALUE self, VALUE rSQL)
{
    VALUE            sql = StringValue( rSQL );
    VALUE            stmt = am_sqlite3_statement_alloc(cAS_Statement);
    am_sqlite3      *am_db;
    am_sqlite3_stmt *am_stmt;
    const char      *tail;
    int              rc;

    Data_Get_Struct(self, am_sqlite3, am_db);

    Data_Get_Struct(stmt, am_sqlite3_stmt, am_stmt);
    rc = sqlite3_prepare_v2( am_db->db, RSTRING_PTR(sql), (int)RSTRING_LEN(sql),
                            &(am_stmt->stmt), &tail);
    if ( SQLITE_OK != rc) {
        rb_raise(eAS_Error, "Failure to prepare statement %s : [SQLITE_ERROR %d] : %s\n",
                RSTRING_PTR(sql), rc, sqlite3_errmsg(am_db->db));
        am_sqlite3_statement_free( am_stmt );
    }

    if ( tail != NULL ) {
        am_stmt->remaining_sql = rb_str_new2( tail );
        rb_gc_register_address( &(am_stmt->remaining_sql) );
    } else {
        am_stmt->remaining_sql = Qnil;
    }

    return stmt;
}
progress_handler( op_count, proc_like or nil ) click to toggle source

register a progress handler. If the argument is nil, then an existing progress handler is removed. Otherwise the argument is registered as the progress handler.

VALUE am_sqlite3_database_progress_handler( VALUE self, VALUE op_count, VALUE handler )
{
    am_sqlite3   *am_db;

    Data_Get_Struct(self, am_sqlite3, am_db);

    /* Removing a progress handler, remove it from sqlite and then remove it
     * from the garbage collector if it existed */
    if ( Qnil == handler ) {
        sqlite3_progress_handler( am_db->db, -1, NULL, (void*)NULL );
        if ( Qnil != am_db->progress_handler_obj ) {
            rb_gc_unregister_address( &(am_db->progress_handler_obj) );
        }
    } else {
        int  op_codes = FIX2INT( op_count );
        /* installing a progress handler
         * - register it with sqlite
         * - keep a reference for ourselves with our database handle
         * - register the handler reference with the garbage collector
         */
        sqlite3_progress_handler( am_db->db, op_codes, amalgalite_xProgress, (void*)handler );
        am_db->progress_handler_obj = handler;
        rb_gc_register_address( &(am_db->progress_handler_obj) );
    }
    return Qnil;
}
register_profile_tap( tap_obj ) click to toggle source

This registers an object to be called with every profile event in SQLite.

This is an experimental api and is subject to change or removal.

VALUE am_sqlite3_database_register_profile_tap(VALUE self, VALUE tap)
{
    am_sqlite3   *am_db;

    Data_Get_Struct(self, am_sqlite3, am_db);

    /* Qnil, unregister the item and tell the garbage collector we are done with
     * it.
     */

    if ( tap == Qnil ) {
        sqlite3_profile( am_db->db, NULL, NULL );
        rb_gc_unregister_address( &(am_db->profile_obj) );
        am_db->profile_obj = Qnil;

    /* register the item and store the reference to the object in the am_db
     * structure.  We also have to tell the Ruby garbage collector that we
     * point to the Ruby object from C.
     */
    } else {
        am_db->profile_obj = tap;
        rb_gc_register_address( &(am_db->profile_obj) );
        sqlite3_profile( am_db->db, amalgalite_xProfile, (void *)am_db->profile_obj );
    }
    return Qnil;
}
register_trace_tap( tap_obj ) click to toggle source

This registers an object to be called with every trace event in SQLite.

This is an experimental api and is subject to change, or removal.

VALUE am_sqlite3_database_register_trace_tap(VALUE self, VALUE tap)
{
    am_sqlite3   *am_db;

    Data_Get_Struct(self, am_sqlite3, am_db);

    /* Qnil, unregister the item and tell the garbage collector we are done with
     * it.
     */
    if ( Qnil == tap ) {

        sqlite3_trace( am_db->db, NULL, NULL );
        rb_gc_unregister_address( &(am_db->trace_obj) );
        am_db->trace_obj = Qnil;

    /* register the item and store the reference to the object in the am_db
     * structure.  We also have to tell the Ruby garbage collector that we
     * point to the Ruby object from C.
     */
    } else {

        am_db->trace_obj = tap;
        rb_gc_register_address( &(am_db->trace_obj) );
        sqlite3_trace( am_db->db, amalgalite_xTrace, (void *)am_db->trace_obj );
    }

    return Qnil;
}
remove_aggregate( name, arity, klass ) click to toggle source

remove the given klass from availability in SQL as an aggregate.

VALUE am_sqlite3_database_remove_aggregate( VALUE self, VALUE name, VALUE arity, VALUE klass )
{
    am_sqlite3    *am_db;
    int            rc;
    char*         zFunctionName = RSTRING_PTR(name);
    int           nArg = FIX2INT( arity );

    Data_Get_Struct(self, am_sqlite3, am_db);
    rc = sqlite3_create_function( am_db->db, 
                                  zFunctionName, nArg,
                                  SQLITE_UTF8,
                                  NULL, NULL,
                                  NULL, 
                                  NULL);
    if ( SQLITE_OK != rc ) {
       rb_raise(eAS_Error, "Failure removing SQL aggregate '%s' with arity '%d' : [SQLITE_ERROR %d] : %s\n",
                zFunctionName, nArg, rc, sqlite3_errmsg( am_db->db ));
    }
    rb_gc_unregister_address( &klass );
    return Qnil;
}
remove_function( name, proc_like ) click to toggle source

remove the given function from availability in SQL.

VALUE am_sqlite3_database_remove_function( VALUE self, VALUE name, VALUE proc_like )
{
    am_sqlite3    *am_db;
    int            rc;
    VALUE         arity = rb_funcall( proc_like, rb_intern( "arity" ), 0 );
    char*         zFunctionName = RSTRING_PTR(name);
    int           nArg = FIX2INT( arity );

    Data_Get_Struct(self, am_sqlite3, am_db);
    rc = sqlite3_create_function( am_db->db,
                                  zFunctionName, nArg,
                                  SQLITE_UTF8,
                                  NULL, NULL,
                                  NULL, NULL);
    if ( SQLITE_OK != rc ) {
       rb_raise(eAS_Error, "Failure removing SQL function '%s' with arity '%d' : [SQLITE_ERROR %d] : %s\n",
                zFunctionName, nArg, rc, sqlite3_errmsg( am_db->db ));
    }
    rb_gc_unregister_address( &proc_like );
    return Qnil;
}
replicate_to( other_db ) → other_db click to toggle source

Replicates the current database to the database passed in using the sqlite3_backup api

VALUE am_sqlite3_database_replicate_to( VALUE self, VALUE other )
{
    am_sqlite3  *am_src_db;
    am_sqlite3  *am_dest_db;

    sqlite3_backup *backup;
    sqlite3        *src;
    sqlite3        *dest;

    int             rc_s;
    int             rc_f;

    /* source database */
    Data_Get_Struct(self, am_sqlite3, am_src_db);
    src = am_src_db->db;

    /* destination database */
    Data_Get_Struct(other, am_sqlite3, am_dest_db);
    dest = am_dest_db->db;

    backup = sqlite3_backup_init( dest, "main", src, "main" );
    if ( NULL == backup ) {
        rb_raise(eAS_Error, "Failure to initialize replication:  [SQLITE_ERROR %d] : %s\n",
                 sqlite3_errcode( dest ), sqlite3_errmsg( dest ));
    }

    rc_s = sqlite3_backup_step( backup, -1 ); /* copy the whole thing at once */
    rc_f = sqlite3_backup_finish( backup ); 

    /* report the rc_s error if that one is bad, 
     * else raise the rc_f error, or nothing */
    if ( SQLITE_DONE != rc_s ) {
        rb_raise(eAS_Error, "Failure in replication : [SQLITE_ERROR %d] : %s\n",
                sqlite3_errcode( dest ), sqlite3_errmsg( dest ) );
    } else if ( SQLITE_OK != rc_f ) {
        rb_raise(eAS_Error, "Failure in finishing replication: [SQLITE_ERROR %d] : %s\n",
                sqlite3_errcode( dest ), sqlite3_errmsg( dest ) );
    } 

    return other;
}
row_changes → Integer click to toggle source

return the number of rows changed with the most recent INSERT, UPDATE or DELETE statement.

VALUE am_sqlite3_database_row_changes(VALUE self)
{
    am_sqlite3   *am_db;
    int           rc;

    Data_Get_Struct(self, am_sqlite3, am_db);
    rc = sqlite3_changes( am_db->db );

    return INT2FIX(rc);
}
status() click to toggle source

return the DBstatus object for the sqlite database

# File lib/amalgalite/sqlite3/database/status.rb, line 64
def status
  @status ||= DBStatus.new( self )
end
table_column_metadata( db_name, table_name, column_name) → Hash click to toggle source

Returns a hash containing the meta information about the column. The available keys are:

declared_data_type

the declared data type of the column

collation_sequence_name

the name of the collation sequence for the column

not_null_constraint

True if the column has a NOT NULL constraint

primary_key

True if the column is part of a primary key

auto_increment

True if the column is AUTO INCREMENT

VALUE am_sqlite3_database_table_column_metadata(VALUE self, VALUE db_name, VALUE tbl_name, VALUE col_name)
{
    am_sqlite3  *am_db;
    int         rc;

    /* input */
    const char  *zDbName      = StringValuePtr( db_name );
    const char  *zTableName   = StringValuePtr( tbl_name );
    const char  *zColumnName  = StringValuePtr( col_name );

    /* output */
    const char *pzDataType = NULL;
    const char *pzCollSeq  = NULL;
    int         pNotNull, pPrimaryKey, pAutoinc;
    VALUE       rHash      = rb_hash_new();
    VALUE       rStr       = Qnil;

    Data_Get_Struct(self, am_sqlite3, am_db);

    rc = sqlite3_table_column_metadata( am_db->db,
                                        zDbName, zTableName, zColumnName,
                                        &pzDataType, &pzCollSeq,
                                        &pNotNull, &pPrimaryKey, &pAutoinc);
    if ( SQLITE_OK != rc ) {
       rb_raise(eAS_Error, "Failure retrieveing column meta data for table '%s' column '%s' : [SQLITE_ERROR %d] : %s\n",
                zTableName, zColumnName, rc, sqlite3_errmsg( am_db-> db ));

    }

    rStr = ( NULL == pzDataType) ? Qnil : rb_str_new2( pzDataType );
    rb_hash_aset( rHash, rb_str_new2("declared_data_type"), rStr );

    rStr = ( NULL == pzCollSeq) ? Qnil : rb_str_new2( pzCollSeq );
    rb_hash_aset( rHash, rb_str_new2("collation_sequence_name"), rStr );

    rb_hash_aset( rHash, rb_str_new2("not_null_constraint"),     ( pNotNull    ? Qtrue : Qfalse ));
    rb_hash_aset( rHash, rb_str_new2("primary_key"),             ( pPrimaryKey ? Qtrue : Qfalse ));
    rb_hash_aset( rHash, rb_str_new2("auto_increment"),          ( pAutoinc   ? Qtrue : Qfalse ));

    return rHash;
}
total_changes → Integer click to toggle source

return the number of rows changed by INSERT, UPDATE or DELETE statements in the database connection since the connection was opened.

VALUE am_sqlite3_database_total_changes(VALUE self)
{
    am_sqlite3   *am_db;
    int           rc;

    Data_Get_Struct(self, am_sqlite3, am_db);
    rc = sqlite3_total_changes( am_db->db );

    return INT2FIX(rc);
}