Class: Amalgalite::Database

Inherits:
Object
  • Object
show all
Includes:
SQLite3::Constants
Defined in:
lib/amalgalite/database.rb

Overview

The encapsulation of a connection to an SQLite3 database.

Example opening and possibly creating a new database

db = Amalgalite::Database.new( "mydb.db" )
db.execute( "SELECT * FROM table" ) do |row|
  puts row
end

db.close

Open a database read only:

db = Amalgalite::Database.new( "mydb.db", "r" )

Open an in-memory database:

db = Amalgalite::MemoryDatabase.new

Direct Known Subclasses

MemoryDatabase

Defined Under Namespace

Classes: AggregateError, BusyHandlerError, FunctionError, InvalidModeError, ProgressHandlerError, TransactionBehavior

Constant Summary collapse

VALID_MODES =

list of valid modes for opening an Amalgalite::Database

{
  "r"  => Open::READONLY,
  "r+" => Open::READWRITE,
  "w+" => Open::READWRITE | Open::CREATE,
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(filename, mode = "w+", opts = {}) ⇒ Database

Create a new Amalgalite database

:call-seq:

Amalgalite::Database.new( filename, "w+", opts = {}) -> Database

The first parameter is the filename of the sqlite database. Specifying “:memory:” as the filename creates an in-memory database.

The second parameter is the standard file modes of how to open a file.

The modes are:

  • r - Read-only

  • r+ - Read/write, an error is thrown if the database does not already exist

  • w+ - Read/write, create a new database if it doesn’t exist

w+ is the default as this is how most databases will want to be utilized.

opts is a hash of available options for the database:

  • :utf16 option to set the database to a utf16 encoding if creating a database.

By default, databases are created with an encoding of utf8. Setting this to true and opening an already existing database has no effect.

NOTE Currently :utf16 is not supported by Amalgalite, it is planned for a later release



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/amalgalite/database.rb', line 139

def initialize( filename, mode = "w+", opts = {})
  @open           = false
  @profile_tap    = nil
  @trace_tap      = nil
  @type_map       = ::Amalgalite::TypeMaps::DefaultMap.new
  @functions      = Hash.new 
  @aggregates     = Hash.new
  @utf16          = false

  unless VALID_MODES.keys.include?( mode ) 
    raise InvalidModeError, "#{mode} is invalid, must be one of #{VALID_MODES.keys.join(', ')}" 
  end

  if not File.exist?( filename ) and opts[:utf16] then
    raise NotImplementedError, "Currently Amalgalite has not implemented utf16 support"
  else
    @api = Amalgalite::SQLite3::Database.open( filename, VALID_MODES[mode] )
  end
  @open = true
end

Instance Attribute Details

#aggregatesObject (readonly)

A list of the user defined aggregates



107
108
109
# File 'lib/amalgalite/database.rb', line 107

def aggregates
  @aggregates
end

#apiObject (readonly)

the low level Amalgalite::SQLite3::Database



91
92
93
# File 'lib/amalgalite/database.rb', line 91

def api
  @api
end

#functionsObject (readonly)

A list of the user defined functions



104
105
106
# File 'lib/amalgalite/database.rb', line 104

def functions
  @functions
end

#profile_tapObject (readonly)

An object that follows the ProfileTap protocol, or nil. By default this is nil



97
98
99
# File 'lib/amalgalite/database.rb', line 97

def profile_tap
  @profile_tap
end

#trace_tapObject

An object that follows the TraceTap protocol, or nil. By default this is nil



94
95
96
# File 'lib/amalgalite/database.rb', line 94

def trace_tap
  @trace_tap
end

#type_mapObject

An object that follows the TypeMap protocol, or nil.

By default this is an instances of TypeMaps::DefaultMap



101
102
103
# File 'lib/amalgalite/database.rb', line 101

def type_map
  @type_map
end

Instance Method Details

#autocommit?Boolean

Is the database in autocommit mode or not

Returns:



180
181
182
# File 'lib/amalgalite/database.rb', line 180

def autocommit?
  @api.autocommit?
end

#clear_taps!Object

clear all the current taps



340
341
342
# File 'lib/amalgalite/database.rb', line 340

def clear_taps!
  self.trace_tap = nil
end

#closeObject

Close the database



170
171
172
173
174
175
# File 'lib/amalgalite/database.rb', line 170

def close
  if open? then
    @api.close
    @open = false
  end
end

#commitObject

Commit a transaction



621
622
623
# File 'lib/amalgalite/database.rb', line 621

def commit
  execute( "COMMIT TRANSACTION" ) if in_transaction?
end

#define_aggregate(name, klass) ⇒ Object Also known as: aggregate

call-seq:

db.define_aggregate( 'name', MyAggregateClass )

Define an SQL aggregate function, these are functions like max(), min(), avg(), etc. SQL functions that would be used when a GROUP BY clause is in effect. See also ::Amalgalite::Aggregate.

A new instance of MyAggregateClass is created for each instance that the SQL aggregate is mentioned in SQL.

Raises:



718
719
720
721
722
723
724
725
726
# File 'lib/amalgalite/database.rb', line 718

def define_aggregate( name, klass )
  db_aggregate = klass
  a = klass.new
  raise AggregateError, "Use only mandatory or arbitrary parameters in an SQL Aggregate, not both" if a.arity < -1
  raise AggregateError, "Aggregate implementation name '#{a.name}' does not match defined name '#{name}'" if a.name != name
  @api.define_aggregate( name, a.arity, klass )
  @aggregates[a.signature] = db_aggregate
  nil
end

#define_busy_handler(callable = nil, &block) ⇒ Object Also known as: busy_handler

call-seq:

db.busy_handler( callable )
db.define_busy_handler do |count|
end
db.busy_handler( Amalgalite::BusyTimeout.new( 30 ) )

Register a busy handler for this database connection, the handler MUST follow the to_proc protocol indicating that is will respond_to?(:call). This is intrinsic to lambdas and blocks so those will work automatically.

This exposes the sqlite busy handler api to ruby.

The busy handler’s _call(N)_ method may be invoked whenever an attempt is made to open a database table that another thread or process has locked. N will be the number of times the _call(N)_ method has been invoked during this locking event.

The handler may or maynot be called based upon what SQLite determins.

If the handler returns nil or false then no more busy handler calls will be made in this lock event and you are probably going to see an SQLite::Error in your immediately future in another process or in another piece of code.

If the handler returns non-nil or non-false then another attempt will be made to obtain the lock, lather, rinse, repeat.

If an Exception happens in a busy handler, it will be the same as if the busy handler had returned nil or false. The exception itself will not be propogated further.

Raises:



804
805
806
807
808
809
# File 'lib/amalgalite/database.rb', line 804

def define_busy_handler( callable = nil, &block )
  handler = ( callable || block ).to_proc
  a = handler.arity
  raise BusyHandlerError, "A busy handler expects 1 and only 1 argument, not #{a}" if a != 1
  @api.busy_handler( handler )
end

#define_function(name, callable = nil, &block) ⇒ Object Also known as: function

call-seq:

db.define_function( "name", MyDBFunction.new )
db.define_function( "my_func", callable )
db.define_function( "my_func" ) do |x,y|
  .... 
  return result
end

register a callback to be exposed as an SQL function. There are multiple ways to register this function:

  1. db.define_function( “name” ) { |a| … }

    • pass define_function a name and a block.

    • The SQL function name taking arity parameters will be registered, where arity is the arity of the block.

    • The return value of the block is the return value of the registred SQL function

  2. db.define_function( “name”, callable )

    • pass function a name and something that responds_to?( :to_proc )

    • The SQL function name is registered taking arity parameters is registered where arity is the arity of callable.to_proc.call

    • The return value of the callable.to_proc.call is the return value of the SQL function

See also ::Amalgalite::Function

Raises:



659
660
661
662
663
664
665
666
# File 'lib/amalgalite/database.rb', line 659

def define_function( name, callable = nil, &block ) 
  p = ( callable || block ).to_proc
  raise FunctionError, "Use only mandatory or arbitrary parameters in an SQL Function, not both" if p.arity < -1
  db_function = ::Amalgalite::SQLite3::Database::Function.new( name, p )
  @api.define_function( db_function.name, db_function )
  @functions[db_function.signature] = db_function
  nil
end

#define_progress_handler(op_code_count = 25, callable = nil, &block) ⇒ Object Also known as: progress_handler

call-seq:

db.progress_handler( 50, MyProgressHandler.new )
db.progress_handler( 25 , callable )
db.progress_handler do
  ....
  return result
end

Register a progress handler for this database connection, the handler MUST follow the to_proc protocol indicating that is will respond_to?(:call). This is intrinsic to lambdas and blocks so those will work automatically.

This exposes the sqlite progress handler api to ruby.

The progress handler’s _call()_ method may be invoked ever N SQLite op codes. If the progress handler returns anything that can evaluate to true then current running sqlite statement is terminated at the earliest oppportunity.

You can use this to be notified that a thread is still processingn a request.



859
860
861
862
863
864
# File 'lib/amalgalite/database.rb', line 859

def define_progress_handler( op_code_count = 25, callable = nil, &block )
  handler  = ( callable || block ).to_proc
  a = handler.arity
  raise ProgressHandlerError, "A progress handler expects 0 arguments, not #{a}" if a != 0
  @api.progress_handler( op_code_count, handler )
end

#encodingObject

return the encoding of the database



220
221
222
# File 'lib/amalgalite/database.rb', line 220

def encoding
  @encoding ||= pragma( "encoding" ).first['encoding']
end

#escape(s) ⇒ Object

SQL escape the input string



194
195
196
# File 'lib/amalgalite/database.rb', line 194

def escape( s )
  Amalgalite::SQLite3.escape( s )
end

#exclusive_transaction(&block) ⇒ Object

helper for an exclusive transaction



543
544
545
# File 'lib/amalgalite/database.rb', line 543

def exclusive_transaction( &block )
  transaction( TransactionBehavior::EXCLUSIVE, &block )
end

#execute(sql, *bind_params) ⇒ Object

Execute a single SQL statement.

If called with a block and there are result rows, then they are iteratively yielded to the block.

If no block is passed, then all the results are returned as an arrayfields instance. This is an array with field name access.

If no block is passed, and there are no results, then an empty Array is returned.

On an error an exception is thrown

This is just a wrapper around the preparation of an Amalgalite Statement and iterating over the results.



294
295
296
297
298
299
300
301
302
303
304
# File 'lib/amalgalite/database.rb', line 294

def execute( sql, *bind_params )
  stmt = prepare( sql )
  stmt.bind( *bind_params )
  if block_given? then
    stmt.each { |row| yield row }
  else
    return stmt.all_rows
  end
ensure
  stmt.close if stmt
end

#execute_batch(sql, *bind_params) ⇒ Object

Execute a batch of statements, this will execute all the sql in the given string until no more sql can be found in the string. It will bind the same parameters to each statement. All data that would be returned from all of the statements is thrown away.

All statements to be executed in the batch must be terminated with a ‘;’ Returns the number of statements executed



316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/amalgalite/database.rb', line 316

def execute_batch( sql, *bind_params) 
  count = 0
  while sql
    prepare( sql ) do |stmt|
      stmt.execute( *bind_params )
      sql =  stmt.remaining_sql 
      sql = nil unless (sql.index(";") and Amalgalite::SQLite3.complete?( sql ))
    end
    count += 1
  end
  return count
end

#first_row_from(sql, *bind_params) ⇒ Object

Execute a sql statment, and only return the first row of results. This is a shorthand method when you only want a single row of results from a query. If there is no result, then return an empty array

It is in all other was, exactly like #execute()



351
352
353
354
355
356
357
# File 'lib/amalgalite/database.rb', line 351

def first_row_from( sql, *bind_params ) 
  stmt = prepare( sql )
  stmt.bind( *bind_params)
  row = stmt.next_row || []
  stmt.close
  return row
end

#first_value_from(sql, *bind_params) ⇒ Object

Execute an sql statement, and return only the first column of the first row. If there is no result, return nil.

It is in all other ways, exactly like #first_row_from()



365
366
367
# File 'lib/amalgalite/database.rb', line 365

def first_value_from( sql, *bind_params )
  return first_row_from( sql, *bind_params).first
end

#immediate_transaction(&block) ⇒ Object

helper for an immediate transaction



538
539
540
# File 'lib/amalgalite/database.rb', line 538

def immediate_transaction( &block )
  transaction( TransactionBehavior::IMMEDIATE, &block )
end

#import(sql) ⇒ Object

Execute a batch of statements via sqlite3_exec. This does the same as execute_batch, but doesn’t update the statement statistics.



333
334
335
# File 'lib/amalgalite/database.rb', line 333

def import(sql)
  @api.execute_batch(sql)
end

#import_csv_to_table(csv_path, table_name, options = {}) ⇒ Object

call-seq:

db.import_csv_to_table( "/some/location/data.csv", "my_table" )
db.import_csv_to_table( "countries.csv", "countries", :col_sep => "|", :headers => %w[ name two_letter id ] )

import_csv_to_table() takes 2 required arguments, and a hash of options. The first argument is the path to a CSV, the second is the table in which to load the data. The options has is a subset of those used by CSV

  • :col_sep - the string placed between each field. Default is “,”

  • :row_sep - the String appended to the end of each row. Default is :auto

  • :quote_char - The character used to quote fields. Default ‘“’

  • :headers - set to true or :first_row if there are headers in this CSV. Default is false.

    This may also be an Array.  If that is the case then the
    array is used as the fields in the CSV and the fields in the
    table in which to insert.  If this is set to an Array, it is
    assumed that all rows in the csv will be inserted.
    


927
928
929
930
# File 'lib/amalgalite/database.rb', line 927

def import_csv_to_table( csv_path, table_name, options = {} )
  importer = CSVTableImporter.new( csv_path, self, table_name, options )
  importer.run
end

#in_transaction?Boolean

return whether or not the database is currently in a transaction or not

Returns:



227
228
229
# File 'lib/amalgalite/database.rb', line 227

def in_transaction?
  not @api.autocommit?
end

#interrupt!Object

call-seq:

db.interrupt!

Cause another thread with a handle on this database to be interrupted and return at the earliest opportunity as interrupted. It is not safe to call this method if the database might be closed before interrupt! returns.



829
830
831
# File 'lib/amalgalite/database.rb', line 829

def interrupt!
  @api.interrupt!
end

#last_insert_rowidObject

Return the rowid of the last inserted row



187
188
189
# File 'lib/amalgalite/database.rb', line 187

def last_insert_rowid
  @api.last_insert_rowid
end

#open?Boolean

Is the database open or not

Returns:



163
164
165
# File 'lib/amalgalite/database.rb', line 163

def open?
  @open
end

#pragma(cmd, &block) ⇒ Object

Run a pragma command against the database

Returns the result set of the pragma



474
475
476
# File 'lib/amalgalite/database.rb', line 474

def pragma( cmd, &block )
  execute("PRAGMA #{cmd}", &block)
end

#prepare(sql) ⇒ Object

Prepare a statement for execution

If called with a block, the statement is yielded to the block and the statement is closed when the block is done.

db.prepare( "SELECT * FROM table WHERE c = ?" ) do |stmt|
  list_of_c_values.each do |c|
    stmt.execute( c ) do |row|
      puts "when c = #{c} : #{row.inspect}"
    end
  end
end

Or without a block:

stmt = db.prepare( "INSERT INTO t1(x, y, z) VALUES ( :


264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/amalgalite/database.rb', line 264

def prepare( sql )
  stmt = Amalgalite::Statement.new( self, sql )
  if block_given? then
    begin 
      yield stmt
    ensure
      stmt.close
      stmt = nil
    end
  end
  return stmt
end

#quote(s) ⇒ Object

Surround the give string with single-quotes and escape any single-quotes in the string



201
202
203
# File 'lib/amalgalite/database.rb', line 201

def quote( s )
  Amalgalite::SQLite3.quote( s )
end

#release(point_name) ⇒ Object

call-seq:

db.release( 'mypoint' )

Release a savepoint. This is similar to a commit but only for savepoints. All savepoints up the savepoint stack and include the name savepoint being released are ‘committed’ to the transaction. There are several ways of thinking about release and they are all detailed in the sqlite documentation: sqlite.org/lang_savepoint.html



604
605
606
# File 'lib/amalgalite/database.rb', line 604

def release( point_name )
  execute( "RELEASE SAVEPOINT #{point_name}" ) if in_transaction? 
end

#reload_schema!(dbname = "main") ⇒ Object

:call-seq:

db.reload_schema! -> Schema

By default once the schema is obtained, it is cached. This is here to force the schema to be reloaded.



465
466
467
468
# File 'lib/amalgalite/database.rb', line 465

def reload_schema!( dbname = "main" )
  @schema = nil
  schema( dbname )
end

#remove_aggregate(name, klass_or_arity = nil) ⇒ Object

call-seq:

db.remove_aggregate( 'name', MyAggregateClass )
db.remove_aggregate( 'name' )

Remove an aggregate from use in the database. Since the same aggregate may be refistered more than once with different arity, you may specify the arity, or the aggregate class, or nil. If nil is used for the arity then Amalgalite does its best to remove all aggregates of the given name



739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
# File 'lib/amalgalite/database.rb', line 739

def remove_aggregate( name, klass_or_arity = nil )
  klass = nil
  case klass_or_arity
  when Integer
    arity = klass_or_arity
  when NilClass
    arity = nil
  else
    klass = klass_or_arity
    arity = klass.new.arity
  end
  to_remove = []
  if arity then
    signature = ::Amalgalite::SQLite3::Database::Function.signature( name, arity )
    db_aggregate = @aggregates[ signature ]
    raise AggregateError, "db aggregate '#{name}' with arity #{arity} does not appear to be defined" unless db_aggregate
    to_remove << db_aggregate
  else
    possibles = @aggregates.values.select { |a| a.new.name == name }
    raise AggregateError, "no db aggregate '#{name}' appears to be defined" if possibles.empty?
    to_remove = possibles
  end

  to_remove.each do |db_agg|
    i = db_agg.new
    @api.remove_aggregate( i.name, i.arity, db_agg)
    @aggregates.delete( i.signature )
  end
end

#remove_busy_handlerObject

call-seq:

db.remove_busy_handler

Remove the busy handler for this database connection.



817
818
819
# File 'lib/amalgalite/database.rb', line 817

def remove_busy_handler
  @api.busy_handler( nil )
end

#remove_function(name, callable_or_arity = nil) ⇒ Object

call-seq:

db.remove_function( 'name', MyScalerFunctor.new )
db.remove_function( 'name', callable )
db.remove_function( 'name', arity )
db.remove_function( 'name' )

Remove a function from use in the database. Since the same function may be registered more than once with different arity, you may specify the arity, or the function object, or nil. If nil is used for the arity, then Amalgalite does its best to remove all functions of given name.



681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
# File 'lib/amalgalite/database.rb', line 681

def remove_function( name, callable_or_arity = nil )
  arity = nil
  if callable_or_arity.respond_to?( :to_proc ) then
    arity = callable_or_arity.to_proc.arity
  elsif callable_or_arity.respond_to?( :to_int ) then
    arity = callable_or_arity.to_int
  end
  to_remove = []

  if arity then
    signature = ::Amalgalite::SQLite3::Database::Function.signature( name, arity ) 
    db_function = @functions[ signature ]
    raise FunctionError, "db function '#{name}' with arity #{arity} does not appear to be defined" unless db_function
    to_remove << db_function
  else
    possibles = @functions.values.select { |f| f.name == name }
    raise FunctionError, "no db function '#{name}' appears to be defined" if possibles.empty?
    to_remove = possibles
  end

  to_remove.each do |db_func|
    @api.remove_function( db_func.name, db_func )
    @functions.delete( db_func.signature )
  end
end

#remove_progress_handlerObject

call-seq:

db.remove_progress_handler

Remove the progress handler for this database connection.



872
873
874
# File 'lib/amalgalite/database.rb', line 872

def remove_progress_handler
  @api.progress_handler( nil, nil )
end

#replicate_to(location) ⇒ Object

call-seq:

db.replicate_to( ":memory:" ) -> new_db
db.replicate_to( "/some/location/my.db" ) -> new_db
db.replicate_to( Amalgalite::Database.new( "/my/backup.db" ) ) -> new_db

replicate_to() takes a single argument, either a String or an Amalgalite::Database. It returns the replicated database object. If given a String, it will truncate that database if it already exists.

Replicate the current database to another location, this can be used for a number of purposes:

  • load an sqlite database from disk into memory

  • snaphost an in memory db and save it to disk

  • backup on sqlite database to another location



893
894
895
896
897
898
899
900
901
902
903
904
905
906
# File 'lib/amalgalite/database.rb', line 893

def replicate_to( location )
  to_db = nil
  case location 
  when String
    to_db = Amalgalite::Database.new( location )
  when Amalgalite::Database
    to_db = location
  else
    raise ArgumentError, "replicate_to( #{location} ) must be a String or a Database" 
  end

  @api.replicate_to( to_db.api )
  return to_db
end

#rollbackObject

Rollback a transaction



628
629
630
# File 'lib/amalgalite/database.rb', line 628

def rollback
  execute( "ROLLBACK TRANSACTION" ) if in_transaction?
end

#rollback_to(point_name) ⇒ Object

call-seq:

db.rollback_to( point_name )

Rollback to a savepoint. The transaction is not cancelled, the transaction is restarted.



614
615
616
# File 'lib/amalgalite/database.rb', line 614

def rollback_to( point_name )
  execute( "ROLLBACK TO SAVEPOINT #{point_name}" )
end

#row_changesObject

return how many rows changed in the last insert, update or delete statement.



234
235
236
# File 'lib/amalgalite/database.rb', line 234

def row_changes
  @api.row_changes
end

#savepoint(name) ⇒ Object

call-seq:

db.savepoint( 'mypoint' ) -> db
db.savepoint( 'mypoint' ) do |db_in_savepoint|
  ...
end

Much of the following documentation is para-phrased from sqlite.org/lang_savepoint.html

Savepoints are a method of creating transactions, similar to transaction except that they may be nested.

  • Every savepoint must have a name, to_s is called on the method argument

  • A savepoint does not need to be initialized inside a transaction. If it is not inside a transaction it behaves exactly as if a DEFERRED transaction had been started.

  • If a block is passed to saveponit then when the block exists, it is guaranteed that either a ‘RELEASE’ or ‘ROLLBACK TO name’ has been executed.

  • If any exception happens during the savepoint transaction, then a ‘ROLLOBACK TO’ is issued when the block closes.

  • If no exception happens during the transaction then a ‘RELEASE name’ is issued upon leaving the block

If no block is passed in then you are on your own.

Raises:



574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
# File 'lib/amalgalite/database.rb', line 574

def savepoint( name )
  point_name = name.to_s.strip
  raise Amalgalite::Error, "Invalid savepoint name '#{name}'" unless point_name and point_name.length > 1
  execute( "SAVEPOINT #{point_name};")
  if block_given? then
    begin
      return ( yield self )
    ensure
      if $! then
        rollback_to( point_name )
        raise $!
      else
        release( point_name )
      end
    end
  else
    return in_transaction?
  end
end

#schema(dbname = "main") ⇒ Object

:call-seq:

db.schema( dbname = "main" ) -> Schema

Returns a Schema object containing the table and column structure of the database.



450
451
452
453
454
455
456
# File 'lib/amalgalite/database.rb', line 450

def schema( dbname = "main" )
  @schema ||= ::Amalgalite::Schema.new( self, dbname )
  if @schema and @schema.dirty?
    reload_schema!( dbname )
  end
  return @schema
end

#total_changesObject

return how many rows have changed since this connection to the database was opened.



242
243
244
# File 'lib/amalgalite/database.rb', line 242

def total_changes
  @api.total_changes
end

#transaction(mode = TransactionBehavior::DEFERRED, &block) ⇒ Object Also known as: deferred_transaction

Begin a transaction. The valid transaction types are:

DEFERRED

no read or write locks are created until the first statement is executed that requries a read or a write

IMMEDIATE

a readlock is obtained immediately so that no other process can write to the database

EXCLUSIVE

a read+write lock is obtained, no other proces can read or write to the database

As a convenience, these are constants available in the Database::TransactionBehavior class.

Amalgalite Transactions are database level transactions, just as SQLite’s are.

If a block is passed in, then when the block exits, it is guaranteed that either ‘COMMIT’ or ‘ROLLBACK’ has been executed.

If any exception happens during the transaction that is caught by Amalgalite, then a ‘ROLLBACK’ is issued when the block closes.

If no exception happens during the transaction then a ‘COMMIT’ is issued upon leaving the block.

If no block is passed in then you are on your own.

Nesting a transaaction via the transaction method are no-ops. If you call transaction within a transaction, no new transaction is started, the current one is just continued.

True nexted transactions are available through the savepoint method.

Raises:



511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
# File 'lib/amalgalite/database.rb', line 511

def transaction( mode = TransactionBehavior::DEFERRED, &block )
  raise Amalgalite::Error, "Invalid transaction behavior mode #{mode}" unless TransactionBehavior.valid?( mode )

  # if already in a transaction, no need to start a new one.
  if not in_transaction? then
    execute( "BEGIN #{mode} TRANSACTION" )
  end

  if block_given? then
    begin
      previous_exception = $!
      return ( yield self )
    ensure
      if $! and ($! != previous_exception) then
        rollback
        raise $!
      else
        commit
      end
    end
  else
    return in_transaction?
  end
end

#utf16?Boolean

Is the database utf16 or not? A database is utf16 if the encoding is not UTF-8. Database can only be UTF-8 or UTF-16, and the default is UTF-8

Returns:



209
210
211
212
213
214
215
# File 'lib/amalgalite/database.rb', line 209

def utf16?
  return @utf16
  #if @utf16.nil?
  #  @utf16 = (encoding != "UTF-8") 
  #end
  #return @utf16
end