amalgalite 0.4.2-x86-mswin32-60 → 0.5.0-x86-mswin32-60
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +12 -0
- data/bin/amalgalite-pack +121 -0
- data/examples/blob.rb +18 -35
- data/examples/bootstrap.rb +6 -6
- data/examples/require_me.rb +11 -0
- data/examples/requires.rb +23 -35
- data/ext/amalgalite3.h +3 -0
- data/ext/amalgalite3_requires_bootstrap.c +17 -15
- data/ext/sqlite3.c +4684 -3983
- data/ext/sqlite3.h +301 -96
- data/ext/sqlite3ext.h +9 -1
- data/lib/amalgalite/blob.rb +1 -1
- data/lib/amalgalite/core_ext/kernel/require.rb +11 -4
- data/lib/amalgalite/packer.rb +221 -0
- data/lib/amalgalite/requires.rb +60 -52
- data/lib/amalgalite/statement.rb +5 -2
- data/lib/amalgalite/version.rb +2 -2
- data/lib/amalgalite3.so +0 -0
- data/spec/packer_spec.rb +50 -0
- data/spec/requires_spec.rb +23 -0
- data/spec/sqlite3/version_spec.rb +2 -2
- data/spec/version_spec.rb +6 -0
- data/tasks/config.rb +4 -5
- data/tasks/extension.rake +4 -1
- metadata +10 -6
- data/bin/amalgalite-pack-into-db +0 -155
- data/ext/rbconfig-mingw.rb +0 -178
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.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
|
data/lib/amalgalite/blob.rb
CHANGED
@@ -11,7 +11,7 @@ module Amalgalite
|
|
11
11
|
#
|
12
12
|
# For instance during an insert:
|
13
13
|
#
|
14
|
-
# blob_column = db.schema.tables['blobs'].
|
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
|
-
|
9
|
-
|
10
|
-
|
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
|
data/lib/amalgalite/requires.rb
CHANGED
@@ -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
|
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
|
-
|
25
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
amalgalite
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
72
|
-
@table_name
|
73
|
-
@filename_column
|
74
|
-
@contents_column
|
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
|
-
#
|
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 $
|
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
|
-
|
102
|
-
|
103
|
-
|
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
|