amalgalite 0.4.2-x86-mswin32-60
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +81 -0
- data/LICENSE +29 -0
- data/README +40 -0
- 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/gem-db.rb +94 -0
- data/examples/requires.rb +54 -0
- data/examples/schema-info.rb +34 -0
- data/ext/amalgalite3.c +201 -0
- data/ext/amalgalite3.h +121 -0
- data/ext/amalgalite3_blob.c +241 -0
- data/ext/amalgalite3_constants.c +221 -0
- data/ext/amalgalite3_database.c +550 -0
- data/ext/amalgalite3_requires_bootstrap.c +210 -0
- data/ext/amalgalite3_statement.c +628 -0
- data/ext/extconf.rb +19 -0
- data/ext/gen_constants.rb +130 -0
- data/ext/rbconfig-mingw.rb +178 -0
- data/ext/sqlite3.c +97092 -0
- data/ext/sqlite3.h +6364 -0
- data/ext/sqlite3_options.h +4 -0
- data/ext/sqlite3ext.h +372 -0
- data/gemspec.rb +55 -0
- data/lib/amalgalite.rb +33 -0
- data/lib/amalgalite/blob.rb +186 -0
- data/lib/amalgalite/boolean.rb +42 -0
- data/lib/amalgalite/column.rb +86 -0
- data/lib/amalgalite/core_ext/kernel/require.rb +14 -0
- data/lib/amalgalite/database.rb +514 -0
- data/lib/amalgalite/index.rb +43 -0
- data/lib/amalgalite/paths.rb +70 -0
- data/lib/amalgalite/profile_tap.rb +130 -0
- data/lib/amalgalite/requires.rb +112 -0
- data/lib/amalgalite/schema.rb +115 -0
- data/lib/amalgalite/sqlite3.rb +6 -0
- data/lib/amalgalite/sqlite3/constants.rb +82 -0
- data/lib/amalgalite/sqlite3/database/status.rb +69 -0
- data/lib/amalgalite/sqlite3/status.rb +61 -0
- data/lib/amalgalite/sqlite3/version.rb +38 -0
- data/lib/amalgalite/statement.rb +394 -0
- data/lib/amalgalite/table.rb +36 -0
- data/lib/amalgalite/taps.rb +2 -0
- data/lib/amalgalite/taps/console.rb +27 -0
- data/lib/amalgalite/taps/io.rb +71 -0
- data/lib/amalgalite/trace_tap.rb +35 -0
- data/lib/amalgalite/type_map.rb +63 -0
- data/lib/amalgalite/type_maps/default_map.rb +167 -0
- data/lib/amalgalite/type_maps/storage_map.rb +41 -0
- data/lib/amalgalite/type_maps/text_map.rb +23 -0
- data/lib/amalgalite/version.rb +37 -0
- data/lib/amalgalite/view.rb +26 -0
- data/lib/amalgalite3.so +0 -0
- data/spec/amalgalite_spec.rb +4 -0
- data/spec/blob_spec.rb +81 -0
- data/spec/boolean_spec.rb +23 -0
- data/spec/database_spec.rb +238 -0
- data/spec/default_map_spec.rb +87 -0
- data/spec/integeration_spec.rb +111 -0
- data/spec/paths_spec.rb +28 -0
- data/spec/schema_spec.rb +60 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/sqlite3/constants_spec.rb +65 -0
- data/spec/sqlite3/database_status_spec.rb +36 -0
- data/spec/sqlite3/status_spec.rb +18 -0
- data/spec/sqlite3/version_spec.rb +14 -0
- data/spec/sqlite3_spec.rb +23 -0
- data/spec/statement_spec.rb +134 -0
- data/spec/storage_map_spec.rb +41 -0
- data/spec/tap_spec.rb +59 -0
- data/spec/text_map_spec.rb +23 -0
- data/spec/type_map_spec.rb +17 -0
- data/spec/version_spec.rb +9 -0
- data/tasks/announce.rake +39 -0
- data/tasks/config.rb +110 -0
- data/tasks/distribution.rake +53 -0
- data/tasks/documentation.rake +33 -0
- data/tasks/extension.rake +100 -0
- data/tasks/rspec.rake +32 -0
- data/tasks/rubyforge.rake +59 -0
- data/tasks/utils.rb +80 -0
- metadata +192 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
|
6
|
+
module Amalgalite
|
7
|
+
#
|
8
|
+
# a class representing the meta information about an SQLite index
|
9
|
+
#
|
10
|
+
class Index
|
11
|
+
# the name of the index
|
12
|
+
attr_reader :name
|
13
|
+
|
14
|
+
# the sql statement that created the index
|
15
|
+
attr_reader :sql
|
16
|
+
|
17
|
+
# the table the index is for
|
18
|
+
attr_accessor :table
|
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
|
+
|
29
|
+
def initialize( name, sql, 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
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
module Amalgalite
|
6
|
+
#
|
7
|
+
# Paths contains helpful methods to determine paths of files inside the
|
8
|
+
# Amalgalite library
|
9
|
+
#
|
10
|
+
module Paths
|
11
|
+
#
|
12
|
+
# The root directory of the project is considered to be the parent directory
|
13
|
+
# of the 'lib' directory.
|
14
|
+
#
|
15
|
+
# returns:: [String] The full expanded path of the parent directory of 'lib'
|
16
|
+
# going up the path from the current file. Trailing
|
17
|
+
# File::SEPARATOR is guaranteed.
|
18
|
+
#
|
19
|
+
def self.root_dir
|
20
|
+
unless @root_dir
|
21
|
+
path_parts = ::File.expand_path(__FILE__).split(::File::SEPARATOR)
|
22
|
+
lib_index = path_parts.rindex("lib")
|
23
|
+
@root_dir = path_parts[0...lib_index].join(::File::SEPARATOR) + ::File::SEPARATOR
|
24
|
+
end
|
25
|
+
return @root_dir
|
26
|
+
end
|
27
|
+
|
28
|
+
# returns:: [String] The full expanded path of the +config+ directory
|
29
|
+
# below _root_dir_. All parameters passed in are joined onto the
|
30
|
+
# result. Trailing File::SEPARATOR is guaranteed if _args_ are
|
31
|
+
# *not* present.
|
32
|
+
#
|
33
|
+
def self.config_path(*args)
|
34
|
+
self.sub_path("config", *args)
|
35
|
+
end
|
36
|
+
|
37
|
+
# returns:: [String] The full expanded path of the +data+ directory below
|
38
|
+
# _root_dir_. All parameters passed in are joined onto the
|
39
|
+
# result. Trailing File::SEPARATOR is guaranteed if
|
40
|
+
# _*args_ are *not* present.
|
41
|
+
#
|
42
|
+
def self.data_path(*args)
|
43
|
+
self.sub_path("data", *args)
|
44
|
+
end
|
45
|
+
|
46
|
+
# returns:: [String] The full expanded path of the +lib+ directory below
|
47
|
+
# _root_dir_. All parameters passed in are joined onto the
|
48
|
+
# result. Trailing File::SEPARATOR is guaranteed if
|
49
|
+
# _*args_ are *not* present.
|
50
|
+
#
|
51
|
+
def self.lib_path(*args)
|
52
|
+
self.sub_path("lib", *args)
|
53
|
+
end
|
54
|
+
|
55
|
+
# returns:: [String] The full expanded path of the +ext+ directory below
|
56
|
+
# _root_dir_. All parameters passed in are joined onto the
|
57
|
+
# result. Trailing File::SEPARATOR is guaranteed if
|
58
|
+
# _*args_ are *not* present.
|
59
|
+
#
|
60
|
+
def self.ext_path(*args)
|
61
|
+
self.sub_path("ext", *args)
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.sub_path(sub,*args)
|
65
|
+
sp = ::File.join(root_dir, sub) + File::SEPARATOR
|
66
|
+
sp = ::File.join(sp, *args) if args
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
@@ -0,0 +1,130 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
|
6
|
+
module Amalgalite
|
7
|
+
#
|
8
|
+
# A ProfileSampler is a sampler of profile times. It aggregates up profile
|
9
|
+
# events that happen for the same source. It is based upon the RFuzz::Sampler
|
10
|
+
# class from the rfuzz gem
|
11
|
+
#
|
12
|
+
class ProfileSampler
|
13
|
+
#
|
14
|
+
# create a new sampler with the given name
|
15
|
+
#
|
16
|
+
def initialize( name )
|
17
|
+
@name = name
|
18
|
+
reset!
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# reset the internal state so it may be used again
|
23
|
+
#
|
24
|
+
def reset!
|
25
|
+
@sum = 0.0
|
26
|
+
@sumsq = 0.0
|
27
|
+
@n = 0
|
28
|
+
@min = 0.0
|
29
|
+
@max = 0.0
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# add a sample to the calculations
|
34
|
+
#
|
35
|
+
def sample( value )
|
36
|
+
@sum += value
|
37
|
+
@sumsq += (value * value)
|
38
|
+
if @n == 0 then
|
39
|
+
@min = @max = value
|
40
|
+
else
|
41
|
+
@min = value if value < @min
|
42
|
+
@max = value if value > @max
|
43
|
+
end
|
44
|
+
@n += 1
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# return the mean of the data
|
49
|
+
#
|
50
|
+
def mean
|
51
|
+
@sum / @n
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# returns the standard deviation of the data
|
56
|
+
#
|
57
|
+
def stddev
|
58
|
+
begin
|
59
|
+
Math.sqrt( (@sumsq - ( @sum * @sum / @n)) / (@n-1) )
|
60
|
+
rescue Errno::EDOM
|
61
|
+
return 0.0
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# return all the values as an array
|
67
|
+
#
|
68
|
+
def to_a
|
69
|
+
[ @name, @sum, @sumsq, @n, mean, stddev, @min, @max ]
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# return all the values as a hash
|
74
|
+
#
|
75
|
+
def to_h
|
76
|
+
{ 'name' => @name, 'n' => @n,
|
77
|
+
'sum' => @sum, 'sumsq' => @sumsq, 'mean' => mean,
|
78
|
+
'stddev' => stddev, 'min' => @min, 'max' => @max }
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# return a string containing the sampler summary
|
83
|
+
#
|
84
|
+
def to_s
|
85
|
+
"[%s] => sum: %d, sumsq: %d, n: %d, mean: %0.6f, stddev: %0.6f, min: %d, max: %d" % to_a
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
#
|
91
|
+
# A Profile Tap recives +profile+ events from SQLite which involve the number of
|
92
|
+
# nanoseconds in wall-clock time it took for a particular thing to happen. In
|
93
|
+
# general this +thing+ is an SQL statement.
|
94
|
+
#
|
95
|
+
# It has a well known +profile+ method which when invoked will write the event
|
96
|
+
# to a delegate object.
|
97
|
+
#
|
98
|
+
#
|
99
|
+
class ProfileTap
|
100
|
+
|
101
|
+
attr_reader :samplers
|
102
|
+
|
103
|
+
#
|
104
|
+
# Create a new ProfileTap object that wraps the given object and calls the
|
105
|
+
# method named in +send_to+ ever time a profile event happens.
|
106
|
+
#
|
107
|
+
def initialize( wrapped_obj, send_to = 'profile' )
|
108
|
+
unless wrapped_obj.respond_to?( send_to )
|
109
|
+
raise Amalgalite::Error, "#{wrapped_obj.class.name} does not respond to #{send_to.to_s} "
|
110
|
+
end
|
111
|
+
|
112
|
+
@delegate_obj = wrapped_obj
|
113
|
+
@delegate_method = send_to
|
114
|
+
@samplers = {}
|
115
|
+
end
|
116
|
+
|
117
|
+
#
|
118
|
+
# Record the profile information and send the delegate object the msg and
|
119
|
+
# time information.
|
120
|
+
#
|
121
|
+
def profile( msg, time )
|
122
|
+
unless sampler = @samplers[msg]
|
123
|
+
msg = msg.gsub(/\s+/,' ')
|
124
|
+
sampler = @samplers[msg] = ProfileSampler.new( msg )
|
125
|
+
end
|
126
|
+
sampler.sample( time )
|
127
|
+
@delegate_obj.send( @delegate_method, msg, time )
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'amalgalite'
|
2
|
+
|
3
|
+
module Amalgalite
|
4
|
+
#
|
5
|
+
# Requires encapsulates requiring itesm from the database
|
6
|
+
class Requires
|
7
|
+
class << self
|
8
|
+
def load_path_db_connections
|
9
|
+
@load_path_db_connections ||= {}
|
10
|
+
end
|
11
|
+
def load_path
|
12
|
+
@load_path ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
def db_connection_to( dbfile_name )
|
16
|
+
unless connection = load_path_db_connections[ dbfile_name ]
|
17
|
+
puts "loading file #{dbfile_name}"
|
18
|
+
connection = ::Amalgalite::Database.new( dbfile_name )
|
19
|
+
load_path_db_connections[dbfile_name] = connection
|
20
|
+
end
|
21
|
+
return connection
|
22
|
+
end
|
23
|
+
|
24
|
+
def require( filename )
|
25
|
+
load_path.each { |lp| lp.require( filename ) }
|
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
|
62
|
+
end
|
63
|
+
|
64
|
+
attr_reader :dbfile_name
|
65
|
+
attr_reader :table_name
|
66
|
+
attr_reader :filename_column
|
67
|
+
attr_reader :contents_column
|
68
|
+
attr_reader :db_connection
|
69
|
+
|
70
|
+
def initialize( opts = {} )
|
71
|
+
@dbfile_name = opts[:dbfile_name] || "lib.db"
|
72
|
+
@table_name = opts[:table_name] || "rubylibs"
|
73
|
+
@filename_column = opts[:filename_column] || "filename"
|
74
|
+
@contents_column = opts[:contents_column] || "contents"
|
75
|
+
@db_connection = Requires.db_connection_to( dbfile_name )
|
76
|
+
Requires.load_path << self
|
77
|
+
end
|
78
|
+
|
79
|
+
#
|
80
|
+
# return the sql to find the file contents for a file in this requires
|
81
|
+
#
|
82
|
+
def sql
|
83
|
+
@sql ||= "SELECT #{filename_column}, #{contents_column} FROM #{table_name} WHERE #{filename_column} = ?"
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# require a file in this database table. This will check and see if the
|
88
|
+
# file is already required. If it isn't it will select the contents
|
89
|
+
# associated with the row identified by the filename and eval those contents
|
90
|
+
# within the context of TOPLEVEL_BINDING. The filename is then appended to
|
91
|
+
# $".
|
92
|
+
#
|
93
|
+
# if the file was required then true is returned, otherwise false
|
94
|
+
#
|
95
|
+
def require( filename )
|
96
|
+
if $".include?( filename ) then
|
97
|
+
return false
|
98
|
+
else
|
99
|
+
begin
|
100
|
+
rows = db_connection.execute(sql, filename)
|
101
|
+
row = rows.first
|
102
|
+
eval( row[contents_column].to_s, TOPLEVEL_BINDING)
|
103
|
+
$" << row[filename_column]
|
104
|
+
rescue => e
|
105
|
+
raise LoadError, "Failure loading #{filename} from #{dbfile_name} : #{e}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
return true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
require 'amalgalite/core_ext/kernel/require'
|
@@ -0,0 +1,115 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
|
6
|
+
require 'amalgalite/table'
|
7
|
+
require 'amalgalite/index'
|
8
|
+
require 'amalgalite/column'
|
9
|
+
require 'amalgalite/view'
|
10
|
+
|
11
|
+
module Amalgalite
|
12
|
+
#
|
13
|
+
# An object view of the schema in the SQLite database. If the schema changes
|
14
|
+
# after this class is created, it has no knowledge of that.
|
15
|
+
#
|
16
|
+
class Schema
|
17
|
+
|
18
|
+
attr_reader :catalog
|
19
|
+
attr_reader :schema
|
20
|
+
attr_reader :tables
|
21
|
+
attr_reader :views
|
22
|
+
attr_reader :db
|
23
|
+
|
24
|
+
#
|
25
|
+
# Create a new instance of Schema
|
26
|
+
#
|
27
|
+
def initialize( db, catalog = 'main', schema = 'sqlite')
|
28
|
+
@db = db
|
29
|
+
@catalog = catalog
|
30
|
+
@schema = schema
|
31
|
+
|
32
|
+
load_schema!
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# load the schema from the database
|
37
|
+
def load_schema!
|
38
|
+
load_tables
|
39
|
+
load_views
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# load all the tables
|
44
|
+
#
|
45
|
+
def load_tables
|
46
|
+
@tables = {}
|
47
|
+
@db.execute("SELECT tbl_name, sql FROM sqlite_master WHERE type = 'table'") do |table_info|
|
48
|
+
table = Amalgalite::Table.new( table_info['tbl_name'], table_info['sql'] )
|
49
|
+
table.columns = load_columns( table )
|
50
|
+
table.schema = self
|
51
|
+
table.indexes = load_indexes( table )
|
52
|
+
|
53
|
+
@tables[table.name] = table
|
54
|
+
end
|
55
|
+
|
56
|
+
@tables
|
57
|
+
end
|
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
|
+
|
84
|
+
##
|
85
|
+
# load all the columns for a particular table
|
86
|
+
#
|
87
|
+
def load_columns( table )
|
88
|
+
cols = {}
|
89
|
+
@db.execute("PRAGMA table_info(#{table.name})") do |row|
|
90
|
+
col = Amalgalite::Column.new( "main", table.name, row['name'] )
|
91
|
+
|
92
|
+
col.default_value = row['dflt_value']
|
93
|
+
@db.api.table_column_metadata( "main", table.name, col.name ).each_pair do |key, value|
|
94
|
+
col.send("#{key}=", value)
|
95
|
+
end
|
96
|
+
col.schema = self
|
97
|
+
cols[col.name] = col
|
98
|
+
end
|
99
|
+
cols
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# load all the views for the database
|
104
|
+
#
|
105
|
+
def load_views
|
106
|
+
@views = {}
|
107
|
+
@db.execute("SELECT name, sql FROM sqlite_master WHERE type = 'view'") do |view_info|
|
108
|
+
view = Amalgalite::View.new( view_info['name'], view_info['sql'] )
|
109
|
+
view.schema = self
|
110
|
+
@views[view.name] = view
|
111
|
+
end
|
112
|
+
@views
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|