amalgalite 0.2.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. data/HISTORY +30 -5
  2. data/bin/amalgalite-pack-into-db +155 -0
  3. data/examples/a.rb +9 -0
  4. data/examples/blob.rb +105 -0
  5. data/examples/bootstrap.rb +36 -0
  6. data/examples/filestore.db +0 -0
  7. data/examples/gem-db.rb +94 -0
  8. data/examples/requires.rb +54 -0
  9. data/examples/schema-info.rb +34 -0
  10. data/ext/amalgalite3.c +40 -31
  11. data/ext/amalgalite3_blob.c +7 -12
  12. data/ext/amalgalite3_constants.c +41 -4
  13. data/ext/amalgalite3_database.c +55 -5
  14. data/ext/amalgalite3_requires_bootstrap.c +204 -0
  15. data/ext/amalgalite3_statement.c +3 -4
  16. data/ext/extconf.rb +2 -0
  17. data/ext/gen_constants.rb +15 -4
  18. data/ext/sqlite3.c +68652 -59046
  19. data/ext/sqlite3.h +2613 -1939
  20. data/ext/sqlite3ext.h +13 -3
  21. data/gemspec.rb +0 -1
  22. data/lib/amalgalite.rb +22 -18
  23. data/lib/amalgalite/core_ext/kernel/require.rb +2 -2
  24. data/lib/amalgalite/database.rb +15 -6
  25. data/lib/amalgalite/index.rb +19 -3
  26. data/lib/amalgalite/requires.rb +37 -0
  27. data/lib/amalgalite/schema.rb +26 -5
  28. data/lib/amalgalite/sqlite3.rb +2 -0
  29. data/lib/amalgalite/sqlite3/constants.rb +51 -14
  30. data/lib/amalgalite/sqlite3/database/status.rb +69 -0
  31. data/lib/amalgalite/sqlite3/status.rb +61 -0
  32. data/lib/amalgalite/statement.rb +1 -1
  33. data/lib/amalgalite/table.rb +5 -5
  34. data/lib/amalgalite/type_map.rb +3 -0
  35. data/lib/amalgalite/version.rb +2 -2
  36. data/spec/blob_spec.rb +1 -1
  37. data/spec/boolean_spec.rb +0 -3
  38. data/spec/database_spec.rb +11 -3
  39. data/spec/schema_spec.rb +14 -0
  40. data/spec/sqlite3/constants_spec.rb +44 -4
  41. data/spec/sqlite3/database_status_spec.rb +36 -0
  42. data/spec/sqlite3/status_spec.rb +18 -0
  43. data/spec/sqlite3/version_spec.rb +3 -3
  44. data/spec/sqlite3_spec.rb +0 -12
  45. data/tasks/announce.rake +2 -1
  46. data/tasks/config.rb +2 -1
  47. data/tasks/distribution.rake +7 -0
  48. data/tasks/rubyforge.rake +14 -6
  49. metadata +53 -36
data/ext/sqlite3ext.h CHANGED
@@ -15,7 +15,7 @@
15
15
  ** as extensions by SQLite should #include this file instead of
16
16
  ** sqlite3.h.
17
17
  **
18
- ** @(#) $Id: sqlite3ext.h,v 1.21 2008/03/19 21:45:51 drh Exp $
18
+ ** @(#) $Id: sqlite3ext.h,v 1.24 2008/06/30 15:09:29 danielk1977 Exp $
19
19
  */
20
20
  #ifndef _SQLITE3EXT_H_
21
21
  #define _SQLITE3EXT_H_
@@ -78,7 +78,7 @@ struct sqlite3_api_routines {
78
78
  int (*complete)(const char*sql);
79
79
  int (*complete16)(const void*sql);
80
80
  int (*create_collation)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*));
81
- int (*create_collation16)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*));
81
+ int (*create_collation16)(sqlite3*,const void*,int,void*,int(*)(void*,int,const void*,int,const void*));
82
82
  int (*create_function)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*));
83
83
  int (*create_function16)(sqlite3*,const void*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*));
84
84
  int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
@@ -188,6 +188,11 @@ struct sqlite3_api_routines {
188
188
  int (*test_control)(int, ...);
189
189
  void (*randomness)(int,void*);
190
190
  sqlite3 *(*context_db_handle)(sqlite3_context*);
191
+ int (*extended_result_codes)(sqlite3*,int);
192
+ int (*limit)(sqlite3*,int,int);
193
+ sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
194
+ const char *(*sql)(sqlite3_stmt*);
195
+ int (*status)(int,int*,int*,int);
191
196
  };
192
197
 
193
198
  /*
@@ -354,9 +359,14 @@ struct sqlite3_api_routines {
354
359
  #define sqlite3_test_control sqlite3_api->test_control
355
360
  #define sqlite3_randomness sqlite3_api->randomness
356
361
  #define sqlite3_context_db_handle sqlite3_api->context_db_handle
362
+ #define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
363
+ #define sqlite3_limit sqlite3_api->limit
364
+ #define sqlite3_next_stmt sqlite3_api->next_stmt
365
+ #define sqlite3_sql sqlite3_api->sql
366
+ #define sqlite3_status sqlite3_api->status
357
367
  #endif /* SQLITE_CORE */
358
368
 
359
- #define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api;
369
+ #define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0;
360
370
  #define SQLITE_EXTENSION_INIT2(v) sqlite3_api = v;
361
371
 
362
372
  #endif /* _SQLITE3EXT_H_ */
data/gemspec.rb CHANGED
@@ -28,7 +28,6 @@ Amalgalite::GEM_SPEC = Gem::Specification.new do |spec|
28
28
  if ext_conf = Configuration.for_if_exist?("extension") then
29
29
  spec.extensions << ext_conf.configs
30
30
  spec.extensions.flatten!
31
- spec.require_paths << "ext"
32
31
  end
33
32
 
34
33
  if rdoc = Configuration.for_if_exist?('rdoc') then
data/lib/amalgalite.rb CHANGED
@@ -3,27 +3,31 @@
3
3
  # All rights reserved. See LICENSE and/or COPYING for details.
4
4
  #++
5
5
 
6
+ # check if sqlite3 has already been required. Amalgalite conflicts with system
7
+ # level sqlite3 libraries.
8
+ unless $LOADED_FEATURES.grep( /sqlite3/ ).empty? then
9
+ raise LoadError, "amalgalite conflicts with sqlite3-ruby, please choose one or the other."
10
+ end
11
+
6
12
  module Amalgalite
7
13
  #
8
14
  # Base class of all errors in Amalgalite
9
15
  #
10
16
  class Error < ::StandardError; end
11
17
  end
12
- %w[ blob
13
- boolean
14
- column
15
- database
16
- index
17
- paths
18
- profile_tap
19
- schema
20
- sqlite3
21
- statement
22
- table
23
- taps
24
- trace_tap
25
- type_map
26
- version
27
- view].each do |lib|
28
- require "amalgalite/#{lib}"
29
- end
18
+ require 'amalgalite/blob'
19
+ require 'amalgalite/boolean'
20
+ require 'amalgalite/column'
21
+ require 'amalgalite/database'
22
+ require 'amalgalite/index'
23
+ require 'amalgalite/paths'
24
+ require 'amalgalite/profile_tap'
25
+ require 'amalgalite/schema'
26
+ require 'amalgalite/sqlite3'
27
+ require 'amalgalite/statement'
28
+ require 'amalgalite/table'
29
+ require 'amalgalite/taps'
30
+ require 'amalgalite/trace_tap'
31
+ require 'amalgalite/type_map'
32
+ require 'amalgalite/version'
33
+ require 'amalgalite/view'
@@ -1,5 +1,5 @@
1
1
  module Kernel
2
- alias :original_require :require
2
+ alias :amalgalite_original_require :require
3
3
  #
4
4
  # hook into the system 'require' to allow for required text or blobs from an
5
5
  # amalgalite database.
@@ -7,7 +7,7 @@ module Kernel
7
7
  def require( filename )
8
8
  found = Amalgalite::Requires.require( filename )
9
9
  unless found
10
- found = original_require( filename )
10
+ found = amalgalite_original_require( filename )
11
11
  end
12
12
  return found
13
13
  end
@@ -467,14 +467,22 @@ module Amalgalite
467
467
  # issued upon leaving the block.
468
468
  #
469
469
  # If no block is passed in then you are on your own.
470
+ #
471
+ # Nested transactions are not supported by SQLite, but they are faked here.
472
+ # If you call transaction within a transaction, no new transaction is
473
+ # started, the current one is just continued.
470
474
  #
471
475
  def transaction( mode = TransactionBehavior::DEFERRED )
472
476
  raise Amalgalite::Error, "Invalid transaction behavior mode #{mode}" unless TransactionBehavior.valid?( mode )
473
- raise Amalgalite::Error, "Nested Transactions are not supported" if in_transaction?
474
- execute( "BEGIN #{mode} TRANSACTION" )
477
+
478
+ # if already in a transaction, no need to start a new one.
479
+ if not in_transaction? then
480
+ execute( "BEGIN #{mode} TRANSACTION" )
481
+ end
482
+
475
483
  if block_given? then
476
484
  begin
477
- yield self
485
+ return ( yield self )
478
486
  ensure
479
487
  if $! then
480
488
  rollback
@@ -483,22 +491,23 @@ module Amalgalite
483
491
  commit
484
492
  end
485
493
  end
494
+ else
495
+ return in_transaction?
486
496
  end
487
- return in_transaction?
488
497
  end
489
498
 
490
499
  ##
491
500
  # Commit a transaction
492
501
  #
493
502
  def commit
494
- execute( "COMMIT" )
503
+ execute( "COMMIT" ) if in_transaction?
495
504
  end
496
505
 
497
506
  ##
498
507
  # Rollback a transaction
499
508
  #
500
509
  def rollback
501
- execute( "ROLLBACK" )
510
+ execute( "ROLLBACK" ) if in_transaction?
502
511
  end
503
512
  end
504
513
  end
@@ -17,10 +17,26 @@ module Amalgalite
17
17
  # the table the index is for
18
18
  attr_accessor :table
19
19
 
20
+ # the columns that make up this index, in index order
21
+ attr_accessor :columns
22
+
23
+ # sqlite sequence number of the index
24
+ attr_accessor :sequence_number
25
+
26
+ # is the index unique
27
+ attr_writer :unique
28
+
20
29
  def initialize( name, sql, table )
21
- @name = name
22
- @sql = sql
23
- @table = table
30
+ @name = name
31
+ @sql = sql
32
+ @table = table
33
+ @columns = []
34
+ @sequence_number = nil
35
+ @unique = nil
36
+ end
37
+
38
+ def unique?
39
+ return @unique
24
40
  end
25
41
  end
26
42
  end
@@ -1,3 +1,5 @@
1
+ require 'amalgalite'
2
+
1
3
  module Amalgalite
2
4
  #
3
5
  # Requires encapsulates requiring itesm from the database
@@ -22,6 +24,41 @@ module Amalgalite
22
24
  def require( filename )
23
25
  load_path.each { |lp| lp.require( filename ) }
24
26
  end
27
+
28
+ #
29
+ # return the files in their dependency order for use for packing into a
30
+ # database
31
+ #
32
+ def require_order
33
+ @require_roder ||= %w[
34
+ amalgalite.rb
35
+ amalgalite/blob.rb
36
+ amalgalite/boolean.rb
37
+ amalgalite/column.rb
38
+ amalgalite/statement.rb
39
+ amalgalite/trace_tap.rb
40
+ amalgalite/profile_tap.rb
41
+ amalgalite/type_map.rb
42
+ amalgalite/type_maps/storage_map.rb
43
+ amalgalite/type_maps/text_map.rb
44
+ amalgalite/type_maps/default_map.rb
45
+ amalgalite/database.rb
46
+ amalgalite/index.rb
47
+ amalgalite/paths.rb
48
+ amalgalite/table.rb
49
+ amalgalite/view.rb
50
+ amalgalite/schema.rb
51
+ amalgalite/version.rb
52
+ amalgalite/sqlite3/version.rb
53
+ amalgalite/sqlite3/constants.rb
54
+ amalgalite/sqlite3.rb
55
+ amalgalite/taps/io.rb
56
+ amalgalite/taps/console.rb
57
+ amalgalite/taps.rb
58
+ amalgalite/core_ext/kernel/require.rb
59
+ amalgalite/requires.rb
60
+ ]
61
+ end
25
62
  end
26
63
 
27
64
  attr_reader :dbfile_name
@@ -48,18 +48,39 @@ module Amalgalite
48
48
  table = Amalgalite::Table.new( table_info['tbl_name'], table_info['sql'] )
49
49
  table.columns = load_columns( table )
50
50
  table.schema = self
51
+ table.indexes = load_indexes( table )
51
52
 
52
- @db.prepare("SELECT name, sql FROM sqlite_master WHERE type ='index' and tbl_name = @name") do |idx_stmt|
53
- idx_stmt.execute( "@name" => table.name) do |idx_info|
54
- table.indexes << Amalgalite::Index.new( idx_info['name'], idx_info['sql'], table )
55
- end
56
- end
57
53
  @tables[table.name] = table
58
54
  end
59
55
 
60
56
  @tables
61
57
  end
62
58
 
59
+ ##
60
+ # load all the indexes for a particular table
61
+ #
62
+ def load_indexes( table )
63
+ indexes = {}
64
+
65
+ @db.prepare("SELECT name, sql FROM sqlite_master WHERE type ='index' and tbl_name = $name") do |idx_stmt|
66
+ idx_stmt.execute( "$name" => table.name) do |idx_info|
67
+ indexes[idx_info['name']] = Amalgalite::Index.new( idx_info['name'], idx_info['sql'], table )
68
+ end
69
+ end
70
+
71
+ @db.execute("PRAGMA index_list( #{table.name} );") do |idx_list|
72
+ idx = indexes[idx_list['name']]
73
+
74
+ idx.sequence_number = idx_list['seq']
75
+ idx.unique = Boolean.to_bool( idx_list['unique'] )
76
+
77
+ @db.execute("PRAGMA index_info( #{idx.name} );") do |col_info|
78
+ idx.columns << table.columns[col_info['name']]
79
+ end
80
+ end
81
+ return indexes
82
+ end
83
+
63
84
  ##
64
85
  # load all the columns for a particular table
65
86
  #
@@ -2,3 +2,5 @@ require 'amalgalite3'
2
2
  require 'amalgalite/version'
3
3
  require 'amalgalite/sqlite3/version'
4
4
  require 'amalgalite/sqlite3/constants'
5
+ require 'amalgalite/sqlite3/status'
6
+ require 'amalgalite/sqlite3/database/status'
@@ -9,6 +9,39 @@ module Amalgalite::SQLite3
9
9
  # module containing all constants used from the SQLite C extension
10
10
  #
11
11
  module Constants
12
+ module Helpers
13
+ #
14
+ # convert an integer value into the string representation of the associated
15
+ # constant. this is a helper method used by some of the other modules
16
+ #
17
+ def name_from_value( value )
18
+ unless @const_map_from_value
19
+ @const_map_from_value = {}
20
+ constants.each do |const_name|
21
+ c_int = const_get( const_name )
22
+ @const_map_from_value[c_int] = const_name
23
+ end
24
+ end
25
+ return @const_map_from_value[ value ]
26
+ end
27
+
28
+ #
29
+ # convert a string into the constant value. This is helper method used by
30
+ # some of the other modules
31
+ #
32
+ def value_from_name( name )
33
+ unless @const_map_from_name
34
+ @const_map_from_name = {}
35
+ constants.each do |const_name|
36
+ c_int = const_get( const_name )
37
+ @const_map_from_name[ const_name ] = c_int
38
+ end
39
+ end
40
+ return @const_map_from_name[ name.upcase ]
41
+ end
42
+ end
43
+
44
+
12
45
  ##
13
46
  # DataType defines the namespace for all possible SQLite data types.
14
47
  #
@@ -24,25 +57,29 @@ module Amalgalite::SQLite3
24
57
  end
25
58
  Open.freeze
26
59
 
60
+ ##
61
+ # Status defines the namespace for all the possible status flags for
62
+ # Amalgalite::SQLite3::Status objects
63
+ #
64
+ module Status
65
+ extend Helpers
66
+ end
67
+
68
+
69
+ ##
70
+ # DBStatus defines the namespace for all the possible status codes for the
71
+ # Amalgalite::SQlite3::Database::Status objects.
72
+ #
73
+ module DBStatus
74
+ extend Helpers
75
+ end
76
+
27
77
  ##
28
78
  # ResultCode defines the namespace for all possible result codes from an
29
79
  # SQLite API call.
30
80
  #
31
81
  module ResultCode
32
- #
33
- # convert an integer value into the string representation of the associated
34
- # ResultCode constant.
35
- #
36
- def self.from_int( value )
37
- unless @const_map_from_int
38
- @const_map_from_int = {}
39
- constants.each do |const_name|
40
- c_int = const_get( const_name )
41
- @const_map_from_int[c_int] = const_name
42
- end
43
- end
44
- return @const_map_from_int[ value ]
45
- end
82
+ extend Helpers
46
83
  end # end ResultCode
47
84
  end
48
85
  end
@@ -0,0 +1,69 @@
1
+ require 'amalgalite3'
2
+ require 'amalgalite/sqlite3/constants'
3
+ module Amalgalite::SQLite3
4
+ class Database
5
+ #
6
+ # A Stat represents a single Database Status code and its current highwater mark.
7
+ #
8
+ # Some stats may not have a current or a highwater value, in those cases
9
+ # the associated _has_current?_ or _has_highwater?_ method returns false and the
10
+ # _current_ or _highwater_ method also returns +nil+.
11
+ #
12
+ class Stat
13
+ attr_reader :name
14
+ attr_reader :code
15
+
16
+ def initialize( api_db, name )
17
+ @name = name
18
+ @code = ::Amalgalite::SQLite3::Constants::DBStatus.value_from_name( name )
19
+ @current = nil
20
+ @highwater = nil
21
+ @api_db = api_db
22
+ end
23
+
24
+ def current
25
+ update!
26
+ return @current
27
+ end
28
+
29
+ def highwater
30
+ update!
31
+ return @highwater
32
+ end
33
+
34
+ #
35
+ # reset the given stat's highwater mark. This will also populate the
36
+ # _@current_ and _@highwater_ instance variables
37
+ #
38
+ def reset!
39
+ update!( true )
40
+ end
41
+ end
42
+
43
+ #
44
+ # Top level Status object holding all the Stat objects indicating the DBStatus
45
+ # of the SQLite3 C library.
46
+ #
47
+ class DBStatus
48
+ ::Amalgalite::SQLite3::Constants::DBStatus.constants.each do |const_name|
49
+ method_name = const_name.downcase
50
+ module_eval( <<-code, __FILE__, __LINE__ )
51
+ def #{method_name}
52
+ @#{method_name} ||= Amalgalite::SQLite3::Database::Stat.new( self.api_db, '#{method_name}' )
53
+ end
54
+ code
55
+ end
56
+
57
+ attr_reader :api_db
58
+
59
+ def initialize( api_db )
60
+ @api_db = api_db
61
+ end
62
+ end
63
+
64
+ # return the DBstatus object for the sqlite database
65
+ def status
66
+ @status ||= DBStatus.new( self )
67
+ end
68
+ end
69
+ end