amalgalite 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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.24 2008/06/30 15:09:29 danielk1977 Exp $
18
+ ** @(#) $Id: sqlite3ext.h,v 1.25 2008/10/12 00:27:54 shane Exp $
19
19
  */
20
20
  #ifndef _SQLITE3EXT_H_
21
21
  #define _SQLITE3EXT_H_
@@ -208,7 +208,9 @@ struct sqlite3_api_routines {
208
208
  */
209
209
  #ifndef SQLITE_CORE
210
210
  #define sqlite3_aggregate_context sqlite3_api->aggregate_context
211
+ #ifndef SQLITE_OMIT_DEPRECATED
211
212
  #define sqlite3_aggregate_count sqlite3_api->aggregate_count
213
+ #endif
212
214
  #define sqlite3_bind_blob sqlite3_api->bind_blob
213
215
  #define sqlite3_bind_double sqlite3_api->bind_double
214
216
  #define sqlite3_bind_int sqlite3_api->bind_int
@@ -264,14 +266,18 @@ struct sqlite3_api_routines {
264
266
  #define sqlite3_errmsg sqlite3_api->errmsg
265
267
  #define sqlite3_errmsg16 sqlite3_api->errmsg16
266
268
  #define sqlite3_exec sqlite3_api->exec
269
+ #ifndef SQLITE_OMIT_DEPRECATED
267
270
  #define sqlite3_expired sqlite3_api->expired
271
+ #endif
268
272
  #define sqlite3_finalize sqlite3_api->finalize
269
273
  #define sqlite3_free sqlite3_api->free
270
274
  #define sqlite3_free_table sqlite3_api->free_table
271
275
  #define sqlite3_get_autocommit sqlite3_api->get_autocommit
272
276
  #define sqlite3_get_auxdata sqlite3_api->get_auxdata
273
277
  #define sqlite3_get_table sqlite3_api->get_table
278
+ #ifndef SQLITE_OMIT_DEPRECATED
274
279
  #define sqlite3_global_recover sqlite3_api->global_recover
280
+ #endif
275
281
  #define sqlite3_interrupt sqlite3_api->interruptx
276
282
  #define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
277
283
  #define sqlite3_libversion sqlite3_api->libversion
@@ -309,7 +315,9 @@ struct sqlite3_api_routines {
309
315
  #define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
310
316
  #define sqlite3_total_changes sqlite3_api->total_changes
311
317
  #define sqlite3_trace sqlite3_api->trace
318
+ #ifndef SQLITE_OMIT_DEPRECATED
312
319
  #define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
320
+ #endif
313
321
  #define sqlite3_update_hook sqlite3_api->update_hook
314
322
  #define sqlite3_user_data sqlite3_api->user_data
315
323
  #define sqlite3_value_blob sqlite3_api->value_blob
@@ -11,7 +11,7 @@ module Amalgalite
11
11
  #
12
12
  # For instance during an insert:
13
13
  #
14
- # blob_column = db.schema.tables['blobs'].columsn['data']
14
+ # blob_column = db.schema.tables['blobs'].columns['data']
15
15
  # db.execute("INSERT INTO blobs(name, data) VALUES ( $name, $blob )",
16
16
  # { "$name" => "/path/to/file",
17
17
  # "$blob" => Amalgalite::Blob.new( :file => '/path/to/file',
@@ -1,14 +1,21 @@
1
1
  module Kernel
2
+ # alias the original require away to use later
2
3
  alias :amalgalite_original_require :require
4
+
3
5
  #
4
6
  # hook into the system 'require' to allow for required text or blobs from an
5
7
  # amalgalite database.
6
8
  #
7
9
  def require( filename )
8
- found = Amalgalite::Requires.require( filename )
9
- unless found
10
- found = amalgalite_original_require( filename )
10
+ loaded = amalgalite_original_require( filename )
11
+ rescue LoadError => load_error
12
+ if load_error.message =~ /#{Regexp.escape filename}\z/ then
13
+ loaded = Amalgalite::Requires.require( filename )
14
+ else
15
+ raise load_error
11
16
  end
12
- return found
13
17
  end
18
+
19
+ private :require
20
+ private :amalgalite_original_require
14
21
  end
@@ -0,0 +1,221 @@
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/blob.rb
57
+ amalgalite/boolean.rb
58
+ amalgalite/column.rb
59
+ amalgalite/statement.rb
60
+ amalgalite/trace_tap.rb
61
+ amalgalite/profile_tap.rb
62
+ amalgalite/type_map.rb
63
+ amalgalite/type_maps/storage_map.rb
64
+ amalgalite/type_maps/text_map.rb
65
+ amalgalite/type_maps/default_map.rb
66
+ amalgalite/database.rb
67
+ amalgalite/index.rb
68
+ amalgalite/paths.rb
69
+ amalgalite/table.rb
70
+ amalgalite/view.rb
71
+ amalgalite/schema.rb
72
+ amalgalite/version.rb
73
+ amalgalite/sqlite3/version.rb
74
+ amalgalite/sqlite3/constants.rb
75
+ amalgalite/sqlite3/status.rb
76
+ amalgalite/sqlite3/database/status.rb
77
+ amalgalite/sqlite3.rb
78
+ amalgalite/taps/io.rb
79
+ amalgalite/taps/console.rb
80
+ amalgalite/taps.rb
81
+ amalgalite/packer.rb
82
+ amalgalite/core_ext/kernel/require.rb
83
+ amalgalite/requires.rb
84
+ ]
85
+ end
86
+ end
87
+
88
+ #
89
+ # Create a new packer instance with the list of items to pack and all the
90
+ # options
91
+ #
92
+ def initialize( options = {} )
93
+ @options = Packer.default_options.merge( options )
94
+ @dbfile = @options[:dbfile] || Requires::Bootstrap::DEFAULT_DB
95
+ end
96
+
97
+ #
98
+ # The SQL to create the table for storing ruby code
99
+ #
100
+ def create_table_sql
101
+ sql = <<-create
102
+ CREATE TABLE #{options[:table_name]} (
103
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
104
+ #{options[:filename_column]} TEXT UNIQUE,
105
+ #{options[:compressed_column]} BOOLEAN,
106
+ #{options[:contents_column]} BLOB
107
+ );
108
+ create
109
+ end
110
+
111
+ #
112
+ # Make sure that the dbfile exists and has the appropriate schema.
113
+ #
114
+ def check_db( db )
115
+ if db.schema.tables[ options[:table_name] ] and options[:drop_table] then
116
+ STDERR.puts "Dropping table #{options[:table_name]}" if options[:verbose]
117
+ db.execute("DROP TABLE #{options[:table_name]}")
118
+ db.reload_schema!
119
+ end
120
+
121
+ unless db.schema.tables[ options[:table_name] ]
122
+ db.execute( create_table_sql )
123
+ db.reload_schema!
124
+ end
125
+
126
+ end
127
+
128
+
129
+ #
130
+ # Stores all the .rb files in the list into the given database. The prefix
131
+ # is the file system path to remove from the front of the path on each file
132
+ #
133
+ # manifest is an array of OpenStructs.
134
+ #
135
+ def pack_files( manifest )
136
+ db = Amalgalite::Database.new( dbfile )
137
+ check_db( db )
138
+ max_width = manifest.collect{ |m| m.require_path.length }.sort.last
139
+ contents_column = db.schema.tables[ options[:table_name] ].columns[ options[:contents_column] ]
140
+ db.transaction do |trans|
141
+ manifest.each do |file_info|
142
+ msg = " -> #{file_info.require_path.ljust( max_width )} : "
143
+ begin
144
+ if options[:merge] then
145
+ trans.execute( "DELETE FROM #{options[:table_name]} WHERE #{options[:filename_column]} = ?", file_info.require_path )
146
+ end
147
+
148
+ trans.prepare("INSERT INTO #{options[:table_name]}(#{options[:filename_column]}, #{options[:compressed_column]}, #{options[:contents_column]}) VALUES( $filename, $compressed, $contents)") do |stmt|
149
+ contents = IO.readlines( file_info.file_path )
150
+ if options[:self] then
151
+ contents.each { |l| l.gsub!( /^(\s*require .*)$/m, "# commented out by #{self.class.name} \\1") }
152
+ end
153
+ contents = contents.join
154
+
155
+ if options[:compressed] then
156
+ contents = Packer.gzip( contents )
157
+ end
158
+ content_io = StringIO.new( contents )
159
+ stmt.execute( "$filename" => file_info.require_path,
160
+ "$contents" => Amalgalite::Blob.new( :io => content_io,
161
+ :column => contents_column ),
162
+ "$compressed" => options[:compressed] )
163
+ STDERR.puts "#{msg} stored #{file_info.file_path}" if options[:verbose]
164
+ end
165
+ rescue => e
166
+ STDERR.puts "#{msg} error #{e}"
167
+ end
168
+ end
169
+ end
170
+ end
171
+
172
+ #
173
+ # given a file, see if it can be found in the ruby load path, if so, return that
174
+ # full path
175
+ #
176
+ def full_path_of( rb_file )
177
+ $LOAD_PATH.each do |load_path|
178
+ guess = File.expand_path( File.join( load_path, rb_file ) )
179
+ return guess if File.exist?( guess )
180
+ end
181
+ return nil
182
+ end
183
+
184
+ #
185
+ # Make the manifest for packing
186
+ #
187
+ def make_manifest( file_list )
188
+ manifest = []
189
+ prefix_path = ::Pathname.new( options[:strip_prefix] )
190
+ file_list.each do |f|
191
+ file_path = ::Pathname.new( File.expand_path( f ) )
192
+ m = ::OpenStruct.new
193
+ # if it is a directory then grab all the .rb files from it
194
+ if File.directory?( file_path ) then
195
+ manifest.concat( make_manifest( Dir.glob( File.join( f, "**", "*.rb" ) ) ) )
196
+ next
197
+ elsif File.readable?( file_path ) then
198
+ m.require_path = file_path.relative_path_from( prefix_path )
199
+ m.file_path = file_path.realpath.to_s
200
+ elsif lp = full_path_of( f ) then
201
+ m.require_path = f
202
+ m.file_path = lp
203
+ else
204
+ STDERR.puts "Unable to add #{f} to the manifest, cannot find the file on disk"
205
+ next
206
+ end
207
+ m.require_path = m.require_path.to_s[ /\A(.*)\.rb\Z/, 1]
208
+ manifest << m
209
+ end
210
+ return manifest
211
+ end
212
+
213
+ #
214
+ # Given a list of files pack them into the associated database and table.
215
+ #
216
+ def pack( file_list )
217
+ manifest = make_manifest( file_list )
218
+ pack_files( manifest )
219
+ end
220
+ end
221
+ end
@@ -1,77 +1,75 @@
1
1
  require 'amalgalite'
2
+ require 'pathname'
3
+ require 'zlib'
4
+ require 'amalgalite/packer'
2
5
 
3
6
  module Amalgalite
4
7
  #
5
- # Requires encapsulates requiring itesm from the database
8
+ # Requires encapsulates requiring items from the database
9
+ #
6
10
  class Requires
7
11
  class << self
8
12
  def load_path_db_connections
9
13
  @load_path_db_connections ||= {}
10
14
  end
15
+
11
16
  def load_path
12
17
  @load_path ||= []
13
18
  end
14
19
 
20
+ #
21
+ # Allocate a database connection to the given filename
22
+ #
15
23
  def db_connection_to( dbfile_name )
16
24
  unless connection = load_path_db_connections[ dbfile_name ]
17
- puts "loading file #{dbfile_name}"
18
25
  connection = ::Amalgalite::Database.new( dbfile_name )
19
26
  load_path_db_connections[dbfile_name] = connection
20
27
  end
21
28
  return connection
22
29
  end
23
30
 
24
- def require( filename )
25
- load_path.each { |lp| lp.require( filename ) }
31
+ #
32
+ # Setting a class level variable as a flag to know what we are currently
33
+ # in the middle of requiring
34
+ #
35
+ def requiring
36
+ @requiring ||= []
26
37
  end
27
38
 
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
39
+ def require( filename )
40
+ if load_path.empty? then
41
+ raise ::LoadError, "Amalgalite load path is empty -- #{filename}"
42
+ elsif $LOADED_FEATURES.include?( filename ) then
43
+ return false
44
+ elsif Requires.requiring.include?( filename ) then
45
+ return false
46
+ else
47
+ Requires.requiring << filename
48
+ load_path.each do |lp|
49
+ if lp.require( filename ) then
50
+ Requires.requiring.delete( filename )
51
+ return true
52
+ end
53
+ end
54
+ Requires.requiring.delete( filename )
55
+ raise ::LoadError, "amalgalite has no such file to load -- #{filename}"
56
+ end
57
+ end
58
+ end
63
59
 
64
60
  attr_reader :dbfile_name
65
61
  attr_reader :table_name
66
62
  attr_reader :filename_column
67
63
  attr_reader :contents_column
64
+ attr_reader :compressed_column
68
65
  attr_reader :db_connection
69
66
 
70
67
  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"
68
+ @dbfile_name = opts[:dbfile_name] || Bootstrap::DEFAULT_DB
69
+ @table_name = opts[:table_name] || Bootstrap::DEFAULT_TABLE
70
+ @filename_column = opts[:filename_column] || Bootstrap::DEFAULT_FILENAME_COLUMN
71
+ @contents_column = opts[:contents_column] || Bootstrap::DEFAULT_CONTENTS_COLUMN
72
+ @compressed_column = opts[:compressed_column] || Bootstrap::DEFAULT_COMPRESSED_COLUMN
75
73
  @db_connection = Requires.db_connection_to( dbfile_name )
76
74
  Requires.load_path << self
77
75
  end
@@ -80,32 +78,42 @@ module Amalgalite
80
78
  # return the sql to find the file contents for a file in this requires
81
79
  #
82
80
  def sql
83
- @sql ||= "SELECT #{filename_column}, #{contents_column} FROM #{table_name} WHERE #{filename_column} = ?"
81
+ @sql ||= "SELECT #{filename_column}, #{compressed_column}, #{contents_column} FROM #{table_name} WHERE #{filename_column} = ?"
84
82
  end
85
83
 
86
84
  #
87
- # require a file in this database table. This will check and see if the
85
+ # load a file in this database table. This will check and see if the
88
86
  # file is already required. If it isn't it will select the contents
89
87
  # associated with the row identified by the filename and eval those contents
90
88
  # within the context of TOPLEVEL_BINDING. The filename is then appended to
91
- # $".
89
+ # $LOADED_FEATURES.
92
90
  #
93
91
  # if the file was required then true is returned, otherwise false
94
92
  #
95
93
  def require( filename )
96
- if $".include?( filename ) then
94
+ if $LOADED_FEATURES.include?( filename ) then
97
95
  return false
98
96
  else
99
97
  begin
98
+ filename = filename.gsub(/\.rb\Z/,'')
100
99
  rows = db_connection.execute(sql, filename)
101
- row = rows.first
102
- eval( row[contents_column].to_s, TOPLEVEL_BINDING)
103
- $" << row[filename_column]
100
+ if rows.size > 0 then
101
+ row = rows.first
102
+ contents = row[contents_column].to_s
103
+ if row[compressed_column] then
104
+ contents = ::Amalgalite::Packer.gunzip( contents )
105
+ end
106
+
107
+ eval( contents, TOPLEVEL_BINDING, row[filename_column] )
108
+ $LOADED_FEATURES << row[filename_column]
109
+ return true
110
+ else
111
+ return false
112
+ end
104
113
  rescue => e
105
- raise LoadError, "Failure loading #{filename} from #{dbfile_name} : #{e}"
114
+ raise ::LoadError, "Failure loading #{filename} from #{dbfile_name} : #{e}"
106
115
  end
107
116
  end
108
- return true
109
117
  end
110
118
  end
111
119
  end