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.
- data/HISTORY +30 -5
- data/bin/amalgalite-pack-into-db +155 -0
- data/examples/a.rb +9 -0
- data/examples/blob.rb +105 -0
- data/examples/bootstrap.rb +36 -0
- data/examples/filestore.db +0 -0
- data/examples/gem-db.rb +94 -0
- data/examples/requires.rb +54 -0
- data/examples/schema-info.rb +34 -0
- data/ext/amalgalite3.c +40 -31
- data/ext/amalgalite3_blob.c +7 -12
- data/ext/amalgalite3_constants.c +41 -4
- data/ext/amalgalite3_database.c +55 -5
- data/ext/amalgalite3_requires_bootstrap.c +204 -0
- data/ext/amalgalite3_statement.c +3 -4
- data/ext/extconf.rb +2 -0
- data/ext/gen_constants.rb +15 -4
- data/ext/sqlite3.c +68652 -59046
- data/ext/sqlite3.h +2613 -1939
- data/ext/sqlite3ext.h +13 -3
- data/gemspec.rb +0 -1
- data/lib/amalgalite.rb +22 -18
- data/lib/amalgalite/core_ext/kernel/require.rb +2 -2
- data/lib/amalgalite/database.rb +15 -6
- data/lib/amalgalite/index.rb +19 -3
- data/lib/amalgalite/requires.rb +37 -0
- data/lib/amalgalite/schema.rb +26 -5
- data/lib/amalgalite/sqlite3.rb +2 -0
- data/lib/amalgalite/sqlite3/constants.rb +51 -14
- data/lib/amalgalite/sqlite3/database/status.rb +69 -0
- data/lib/amalgalite/sqlite3/status.rb +61 -0
- data/lib/amalgalite/statement.rb +1 -1
- data/lib/amalgalite/table.rb +5 -5
- data/lib/amalgalite/type_map.rb +3 -0
- data/lib/amalgalite/version.rb +2 -2
- data/spec/blob_spec.rb +1 -1
- data/spec/boolean_spec.rb +0 -3
- data/spec/database_spec.rb +11 -3
- data/spec/schema_spec.rb +14 -0
- data/spec/sqlite3/constants_spec.rb +44 -4
- data/spec/sqlite3/database_status_spec.rb +36 -0
- data/spec/sqlite3/status_spec.rb +18 -0
- data/spec/sqlite3/version_spec.rb +3 -3
- data/spec/sqlite3_spec.rb +0 -12
- data/tasks/announce.rake +2 -1
- data/tasks/config.rb +2 -1
- data/tasks/distribution.rake +7 -0
- data/tasks/rubyforge.rake +14 -6
- 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.
|
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
|
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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 :
|
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 =
|
10
|
+
found = amalgalite_original_require( filename )
|
11
11
|
end
|
12
12
|
return found
|
13
13
|
end
|
data/lib/amalgalite/database.rb
CHANGED
@@ -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
|
-
|
474
|
-
|
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
|
data/lib/amalgalite/index.rb
CHANGED
@@ -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
|
22
|
-
@sql
|
23
|
-
@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
|
data/lib/amalgalite/requires.rb
CHANGED
@@ -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
|
data/lib/amalgalite/schema.rb
CHANGED
@@ -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
|
#
|
data/lib/amalgalite/sqlite3.rb
CHANGED
@@ -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
|