amalgalite 0.2.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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