amalgalite 0.10.1-x86-mswin32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. data/HISTORY +201 -0
  2. data/LICENSE +29 -0
  3. data/README +51 -0
  4. data/bin/amalgalite-pack +126 -0
  5. data/examples/a.rb +9 -0
  6. data/examples/blob.rb +88 -0
  7. data/examples/bootstrap.rb +36 -0
  8. data/examples/define_aggregate.rb +75 -0
  9. data/examples/define_function.rb +104 -0
  10. data/examples/gem-db.rb +94 -0
  11. data/examples/gems.db +0 -0
  12. data/examples/require_me.rb +11 -0
  13. data/examples/requires.rb +42 -0
  14. data/examples/schema-info.rb +34 -0
  15. data/ext/amalgalite/amalgalite3.c +290 -0
  16. data/ext/amalgalite/amalgalite3.h +151 -0
  17. data/ext/amalgalite/amalgalite3_blob.c +240 -0
  18. data/ext/amalgalite/amalgalite3_constants.c +221 -0
  19. data/ext/amalgalite/amalgalite3_database.c +1148 -0
  20. data/ext/amalgalite/amalgalite3_requires_bootstrap.c +210 -0
  21. data/ext/amalgalite/amalgalite3_statement.c +639 -0
  22. data/ext/amalgalite/extconf.rb +36 -0
  23. data/ext/amalgalite/gen_constants.rb +130 -0
  24. data/ext/amalgalite/sqlite3.c +106729 -0
  25. data/ext/amalgalite/sqlite3.h +5626 -0
  26. data/ext/amalgalite/sqlite3_options.h +4 -0
  27. data/ext/amalgalite/sqlite3ext.h +380 -0
  28. data/gemspec.rb +60 -0
  29. data/lib/amalgalite.rb +43 -0
  30. data/lib/amalgalite/1.8/amalgalite3.so +0 -0
  31. data/lib/amalgalite/1.9/amalgalite3.so +0 -0
  32. data/lib/amalgalite/aggregate.rb +67 -0
  33. data/lib/amalgalite/blob.rb +186 -0
  34. data/lib/amalgalite/boolean.rb +42 -0
  35. data/lib/amalgalite/busy_timeout.rb +47 -0
  36. data/lib/amalgalite/column.rb +97 -0
  37. data/lib/amalgalite/core_ext/kernel/require.rb +21 -0
  38. data/lib/amalgalite/database.rb +947 -0
  39. data/lib/amalgalite/function.rb +61 -0
  40. data/lib/amalgalite/index.rb +43 -0
  41. data/lib/amalgalite/packer.rb +226 -0
  42. data/lib/amalgalite/paths.rb +70 -0
  43. data/lib/amalgalite/profile_tap.rb +131 -0
  44. data/lib/amalgalite/progress_handler.rb +21 -0
  45. data/lib/amalgalite/requires.rb +120 -0
  46. data/lib/amalgalite/schema.rb +191 -0
  47. data/lib/amalgalite/sqlite3.rb +6 -0
  48. data/lib/amalgalite/sqlite3/constants.rb +80 -0
  49. data/lib/amalgalite/sqlite3/database/function.rb +48 -0
  50. data/lib/amalgalite/sqlite3/database/status.rb +68 -0
  51. data/lib/amalgalite/sqlite3/status.rb +60 -0
  52. data/lib/amalgalite/sqlite3/version.rb +37 -0
  53. data/lib/amalgalite/statement.rb +414 -0
  54. data/lib/amalgalite/table.rb +90 -0
  55. data/lib/amalgalite/taps.rb +2 -0
  56. data/lib/amalgalite/taps/console.rb +27 -0
  57. data/lib/amalgalite/taps/io.rb +71 -0
  58. data/lib/amalgalite/trace_tap.rb +35 -0
  59. data/lib/amalgalite/type_map.rb +63 -0
  60. data/lib/amalgalite/type_maps/default_map.rb +167 -0
  61. data/lib/amalgalite/type_maps/storage_map.rb +40 -0
  62. data/lib/amalgalite/type_maps/text_map.rb +22 -0
  63. data/lib/amalgalite/version.rb +37 -0
  64. data/lib/amalgalite/view.rb +26 -0
  65. data/spec/aggregate_spec.rb +169 -0
  66. data/spec/amalgalite_spec.rb +4 -0
  67. data/spec/blob_spec.rb +81 -0
  68. data/spec/boolean_spec.rb +23 -0
  69. data/spec/busy_handler.rb +165 -0
  70. data/spec/database_spec.rb +494 -0
  71. data/spec/default_map_spec.rb +87 -0
  72. data/spec/function_spec.rb +94 -0
  73. data/spec/integeration_spec.rb +111 -0
  74. data/spec/packer_spec.rb +60 -0
  75. data/spec/paths_spec.rb +28 -0
  76. data/spec/progress_handler_spec.rb +105 -0
  77. data/spec/requires_spec.rb +23 -0
  78. data/spec/rtree_spec.rb +71 -0
  79. data/spec/schema_spec.rb +120 -0
  80. data/spec/spec_helper.rb +27 -0
  81. data/spec/sqlite3/constants_spec.rb +65 -0
  82. data/spec/sqlite3/database_status_spec.rb +36 -0
  83. data/spec/sqlite3/status_spec.rb +18 -0
  84. data/spec/sqlite3/version_spec.rb +14 -0
  85. data/spec/sqlite3_spec.rb +53 -0
  86. data/spec/statement_spec.rb +161 -0
  87. data/spec/storage_map_spec.rb +41 -0
  88. data/spec/tap_spec.rb +59 -0
  89. data/spec/text_map_spec.rb +23 -0
  90. data/spec/type_map_spec.rb +17 -0
  91. data/spec/version_spec.rb +15 -0
  92. data/tasks/announce.rake +43 -0
  93. data/tasks/config.rb +107 -0
  94. data/tasks/distribution.rake +77 -0
  95. data/tasks/documentation.rake +32 -0
  96. data/tasks/extension.rake +141 -0
  97. data/tasks/rspec.rake +33 -0
  98. data/tasks/rubyforge.rake +59 -0
  99. data/tasks/utils.rb +80 -0
  100. metadata +237 -0
@@ -0,0 +1,61 @@
1
+ require 'amalgalite/sqlite3/database/function'
2
+ module Amalgalite
3
+ #
4
+ # A Base class to inherit from for creating your own SQL scalar functions
5
+ # in ruby.
6
+ #
7
+ # These are SQL functions similar to _abs(X)_, _length(X)_, _random()_. Items
8
+ # that take parameters and return value. They have no state between
9
+ # calls. Built in SQLite scalar functions are :
10
+ #
11
+ # * http://www.sqlite.org/lang_corefunc.html
12
+ # * http://www.sqlite.org/lang_datefunc.html
13
+ #
14
+ # Functions defined in Amalgalite databases conform to the Proc interface.
15
+ # Everything that is defined in an Amalgalite database using +define_function+
16
+ # has its +to_proc+ method called. As a result, any Function must also
17
+ # conform to the +to_proc+ protocol.
18
+ #
19
+ # If you choose to use Function as a parent class of your SQL scalar function
20
+ # implementation you should only have implement +call+ with the appropriate
21
+ # _arity_.
22
+ #
23
+ # For instance to implement a _sha1(X)_ SQL function you could implement it as
24
+ #
25
+ # class SQLSha1 < ::Amalgalite::Function
26
+ # def initialize
27
+ # super( 'md5', 1 )
28
+ # end
29
+ # def call( s )
30
+ # ::Digest::MD5.hexdigest( s.to_s )
31
+ # end
32
+ # end
33
+ #
34
+ class Function
35
+ # The name of the SQL function
36
+ attr_accessor :name
37
+
38
+ # The arity of the SQL function
39
+ attr_accessor :arity
40
+
41
+ # Initialize the function with a name and arity
42
+ def initialize( name, arity )
43
+ @name = name
44
+ @arity = arity
45
+ end
46
+
47
+ # All SQL functions defined foloow the +to_proc+ protocol
48
+ def to_proc
49
+ self
50
+ end
51
+
52
+ # <b>Do Not Override</b>
53
+ #
54
+ # The function signature for use by the Amaglaite datase in tracking
55
+ # function definition and removal.
56
+ #
57
+ def signature
58
+ @signature ||= ::Amalgalite::SQLite3::Database::Function.signature( self.name, self.arity )
59
+ end
60
+ end
61
+ end
@@ -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,226 @@
1
+ require 'optparse'
2
+ require 'ostruct'
3
+ require 'pathname'
4
+ require 'zlib'
5
+
6
+ require 'amalgalite'
7
+ module Amalgalite
8
+ #
9
+ # Pack items into an amalgalite database.
10
+ #
11
+ class Packer
12
+ attr_reader :packing_list
13
+ attr_reader :dbfile
14
+ attr_reader :options
15
+
16
+ class << self
17
+ def default_options
18
+ {
19
+ :table_name => Requires::Bootstrap::DEFAULT_TABLE,
20
+ :filename_column => Requires::Bootstrap::DEFAULT_FILENAME_COLUMN,
21
+ :contents_column => Requires::Bootstrap::DEFAULT_CONTENTS_COLUMN,
22
+ :compressed_column => Requires::Bootstrap::DEFAULT_COMPRESSED_COLUMN,
23
+ :strip_prefix => Dir.pwd,
24
+ :compressed => false,
25
+ :verbose => false,
26
+ }
27
+ end
28
+
29
+ #
30
+ # compress data
31
+ #
32
+ def gzip( data )
33
+ zipped = StringIO.new
34
+ Zlib::GzipWriter.wrap( zipped ) do |io|
35
+ io.write( data )
36
+ end
37
+ return zipped.string
38
+ end
39
+
40
+ #
41
+ # uncompress gzip data
42
+ #
43
+ def gunzip( data )
44
+ data = StringIO.new( data )
45
+ Zlib::GzipReader.new( data ).read
46
+ end
47
+
48
+
49
+ #
50
+ # return the files in their dependency order for use for packing into a
51
+ # database
52
+ #
53
+ def amalgalite_require_order
54
+ @require_order ||= %w[
55
+ amalgalite.rb
56
+ amalgalite/sqlite3/database/function.rb
57
+ amalgalite/aggregate.rb
58
+ amalgalite/blob.rb
59
+ amalgalite/boolean.rb
60
+ amalgalite/busy_timeout.rb
61
+ amalgalite/column.rb
62
+ amalgalite/statement.rb
63
+ amalgalite/trace_tap.rb
64
+ amalgalite/profile_tap.rb
65
+ amalgalite/type_map.rb
66
+ amalgalite/type_maps/storage_map.rb
67
+ amalgalite/type_maps/text_map.rb
68
+ amalgalite/type_maps/default_map.rb
69
+ amalgalite/function.rb
70
+ amalgalite/progress_handler.rb
71
+ amalgalite/database.rb
72
+ amalgalite/index.rb
73
+ amalgalite/paths.rb
74
+ amalgalite/table.rb
75
+ amalgalite/view.rb
76
+ amalgalite/schema.rb
77
+ amalgalite/version.rb
78
+ amalgalite/sqlite3/version.rb
79
+ amalgalite/sqlite3/constants.rb
80
+ amalgalite/sqlite3/status.rb
81
+ amalgalite/sqlite3/database/status.rb
82
+ amalgalite/sqlite3.rb
83
+ amalgalite/taps/io.rb
84
+ amalgalite/taps/console.rb
85
+ amalgalite/taps.rb
86
+ amalgalite/packer.rb
87
+ amalgalite/core_ext/kernel/require.rb
88
+ amalgalite/requires.rb
89
+ ]
90
+ end
91
+ end
92
+
93
+ #
94
+ # Create a new packer instance with the list of items to pack and all the
95
+ # options
96
+ #
97
+ def initialize( options = {} )
98
+ @options = Packer.default_options.merge( options )
99
+ @dbfile = @options[:dbfile] || Requires::Bootstrap::DEFAULT_DB
100
+ end
101
+
102
+ #
103
+ # The SQL to create the table for storing ruby code
104
+ #
105
+ def create_table_sql
106
+ sql = <<-create
107
+ CREATE TABLE #{options[:table_name]} (
108
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
109
+ #{options[:filename_column]} TEXT UNIQUE,
110
+ #{options[:compressed_column]} BOOLEAN,
111
+ #{options[:contents_column]} BLOB
112
+ );
113
+ create
114
+ end
115
+
116
+ #
117
+ # Make sure that the dbfile exists and has the appropriate schema.
118
+ #
119
+ def check_db( db )
120
+ if db.schema.tables[ options[:table_name] ] and options[:drop_table] then
121
+ STDERR.puts "Dropping table #{options[:table_name]}" if options[:verbose]
122
+ db.execute("DROP TABLE #{options[:table_name]}")
123
+ db.reload_schema!
124
+ end
125
+
126
+ unless db.schema.tables[ options[:table_name] ]
127
+ db.execute( create_table_sql )
128
+ db.reload_schema!
129
+ end
130
+
131
+ end
132
+
133
+
134
+ #
135
+ # Stores all the .rb files in the list into the given database. The prefix
136
+ # is the file system path to remove from the front of the path on each file
137
+ #
138
+ # manifest is an array of OpenStructs.
139
+ #
140
+ def pack_files( manifest )
141
+ db = Amalgalite::Database.new( dbfile )
142
+ check_db( db )
143
+ max_width = manifest.collect{ |m| m.require_path.length }.sort.last
144
+ contents_column = db.schema.tables[ options[:table_name] ].columns[ options[:contents_column] ]
145
+ db.transaction do |trans|
146
+ manifest.each do |file_info|
147
+ msg = " -> #{file_info.require_path.ljust( max_width )} : "
148
+ begin
149
+ if options[:merge] then
150
+ trans.execute( "DELETE FROM #{options[:table_name]} WHERE #{options[:filename_column]} = ?", file_info.require_path )
151
+ end
152
+
153
+ trans.prepare("INSERT INTO #{options[:table_name]}(#{options[:filename_column]}, #{options[:compressed_column]}, #{options[:contents_column]}) VALUES( $filename, $compressed, $contents)") do |stmt|
154
+ contents = IO.readlines( file_info.file_path )
155
+ if options[:self] then
156
+ contents.each { |l| l.gsub!( /^(\s*require .*)$/m, "# commented out by #{self.class.name} \\1") }
157
+ end
158
+ contents = contents.join
159
+
160
+ if options[:compressed] then
161
+ contents = Packer.gzip( contents )
162
+ end
163
+ content_io = StringIO.new( contents )
164
+ stmt.execute( "$filename" => file_info.require_path,
165
+ "$contents" => Amalgalite::Blob.new( :io => content_io,
166
+ :column => contents_column ),
167
+ "$compressed" => options[:compressed] )
168
+ STDERR.puts "#{msg} stored #{file_info.file_path}" if options[:verbose]
169
+ end
170
+ rescue => e
171
+ STDERR.puts "#{msg} error #{e}"
172
+ end
173
+ end
174
+ end
175
+ end
176
+
177
+ #
178
+ # given a file, see if it can be found in the ruby load path, if so, return that
179
+ # full path
180
+ #
181
+ def full_path_of( rb_file )
182
+ $LOAD_PATH.each do |load_path|
183
+ guess = File.expand_path( File.join( load_path, rb_file ) )
184
+ return guess if File.exist?( guess )
185
+ end
186
+ return nil
187
+ end
188
+
189
+ #
190
+ # Make the manifest for packing
191
+ #
192
+ def make_manifest( file_list )
193
+ manifest = []
194
+ prefix_path = ::Pathname.new( options[:strip_prefix] )
195
+ file_list.each do |f|
196
+ file_path = ::Pathname.new( File.expand_path( f ) )
197
+ m = ::OpenStruct.new
198
+ # if it is a directory then grab all the .rb files from it
199
+ if File.directory?( file_path ) then
200
+ manifest.concat( make_manifest( Dir.glob( File.join( f, "**", "*.rb" ) ) ) )
201
+ next
202
+ elsif File.readable?( file_path ) then
203
+ m.require_path = file_path.relative_path_from( prefix_path )
204
+ m.file_path = file_path.realpath.to_s
205
+ elsif lp = full_path_of( f ) then
206
+ m.require_path = f
207
+ m.file_path = lp
208
+ else
209
+ STDERR.puts "Unable to add #{f} to the manifest, cannot find the file on disk"
210
+ next
211
+ end
212
+ m.require_path = m.require_path.to_s[ /\A(.*)\.rb\Z/, 1]
213
+ manifest << m
214
+ end
215
+ return manifest
216
+ end
217
+
218
+ #
219
+ # Given a list of files pack them into the associated database and table.
220
+ #
221
+ def pack( file_list )
222
+ manifest = make_manifest( file_list )
223
+ pack_files( manifest )
224
+ end
225
+ end
226
+ end
@@ -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
+ @root_dir ||= (
21
+ path_parts = ::File.expand_path(__FILE__).split(::File::SEPARATOR)
22
+ lib_index = path_parts.rindex("lib")
23
+ path_parts[0...lib_index].join(::File::SEPARATOR) + ::File::SEPARATOR
24
+ )
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,131 @@
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
+ return 0.0 if ( 1 == @n )
60
+ Math.sqrt( (@sumsq - ( @sum * @sum / @n)) / (@n-1) )
61
+ rescue Errno::EDOM
62
+ return 0.0
63
+ end
64
+ end
65
+
66
+ ##
67
+ # return all the values as an array
68
+ #
69
+ def to_a
70
+ [ @name, @sum, @sumsq, @n, mean, stddev, @min, @max ]
71
+ end
72
+
73
+ ##
74
+ # return all the values as a hash
75
+ #
76
+ def to_h
77
+ { 'name' => @name, 'n' => @n,
78
+ 'sum' => @sum, 'sumsq' => @sumsq, 'mean' => mean,
79
+ 'stddev' => stddev, 'min' => @min, 'max' => @max }
80
+ end
81
+
82
+ ##
83
+ # return a string containing the sampler summary
84
+ #
85
+ def to_s
86
+ "[%s] => sum: %d, sumsq: %d, n: %d, mean: %0.6f, stddev: %0.6f, min: %d, max: %d" % self.to_a
87
+ end
88
+
89
+ end
90
+
91
+ #
92
+ # A Profile Tap recives +profile+ events from SQLite which involve the number of
93
+ # nanoseconds in wall-clock time it took for a particular thing to happen. In
94
+ # general this +thing+ is an SQL statement.
95
+ #
96
+ # It has a well known +profile+ method which when invoked will write the event
97
+ # to a delegate object.
98
+ #
99
+ #
100
+ class ProfileTap
101
+
102
+ attr_reader :samplers
103
+
104
+ #
105
+ # Create a new ProfileTap object that wraps the given object and calls the
106
+ # method named in +send_to+ ever time a profile event happens.
107
+ #
108
+ def initialize( wrapped_obj, send_to = 'profile' )
109
+ unless wrapped_obj.respond_to?( send_to )
110
+ raise Amalgalite::Error, "#{wrapped_obj.class.name} does not respond to #{send_to.to_s} "
111
+ end
112
+
113
+ @delegate_obj = wrapped_obj
114
+ @delegate_method = send_to
115
+ @samplers = {}
116
+ end
117
+
118
+ #
119
+ # Record the profile information and send the delegate object the msg and
120
+ # time information.
121
+ #
122
+ def profile( msg, time )
123
+ unless sampler = @samplers[msg]
124
+ msg = msg.gsub(/\s+/,' ')
125
+ sampler = @samplers[msg] = ProfileSampler.new( msg )
126
+ end
127
+ sampler.sample( time )
128
+ @delegate_obj.send( @delegate_method, msg, time )
129
+ end
130
+ end
131
+ end