amalgalite 1.9.4 → 2.0.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.
- checksums.yaml +4 -4
- data/HISTORY.md +19 -2
- data/Manifest.txt +4 -54
- data/README.md +1 -1
- data/TODO.md +4 -3
- data/amalgalite.gemspec +39 -0
- data/ext/amalgalite/c/amalgalite.c +0 -1
- data/ext/amalgalite/c/amalgalite_constants.c +155 -10
- data/ext/amalgalite/c/extconf.rb +5 -2
- data/ext/amalgalite/c/gen_constants.rb +32 -1
- data/ext/amalgalite/c/sqlite3.c +19308 -9112
- data/ext/amalgalite/c/sqlite3.h +909 -296
- data/ext/amalgalite/c/sqlite3ext.h +11 -0
- data/lib/amalgalite/column.rb +20 -2
- data/lib/amalgalite/result/row.rb +83 -0
- data/lib/amalgalite/result.rb +15 -0
- data/lib/amalgalite/statement.rb +32 -24
- data/lib/amalgalite/type_maps/default_map.rb +48 -56
- data/lib/amalgalite/version.rb +1 -1
- metadata +32 -119
- data/Rakefile +0 -27
- data/bin/amalgalite-pack +0 -147
- data/examples/a.rb +0 -9
- data/examples/blob.rb +0 -88
- data/examples/bootstrap.rb +0 -36
- data/examples/define_aggregate.rb +0 -75
- data/examples/define_function.rb +0 -104
- data/examples/fts5.rb +0 -152
- data/examples/gem-db.rb +0 -94
- data/examples/require_me.rb +0 -11
- data/examples/requires.rb +0 -42
- data/examples/schema-info.rb +0 -34
- data/ext/amalgalite/c/amalgalite_requires_bootstrap.c +0 -283
- data/lib/amalgalite/core_ext/kernel/require.rb +0 -21
- data/lib/amalgalite/packer.rb +0 -231
- data/lib/amalgalite/requires.rb +0 -151
- data/spec/aggregate_spec.rb +0 -158
- data/spec/amalgalite_spec.rb +0 -4
- data/spec/blob_spec.rb +0 -78
- data/spec/boolean_spec.rb +0 -24
- data/spec/busy_handler.rb +0 -157
- data/spec/data/iso-3166-country.txt +0 -242
- data/spec/data/iso-3166-schema.sql +0 -22
- data/spec/data/iso-3166-subcountry.txt +0 -3995
- data/spec/data/make-iso-db.sh +0 -12
- data/spec/database_spec.rb +0 -505
- data/spec/default_map_spec.rb +0 -92
- data/spec/function_spec.rb +0 -78
- data/spec/integeration_spec.rb +0 -97
- data/spec/iso_3166_database.rb +0 -58
- data/spec/json_spec.rb +0 -24
- data/spec/packer_spec.rb +0 -60
- data/spec/paths_spec.rb +0 -28
- data/spec/progress_handler_spec.rb +0 -91
- data/spec/requires_spec.rb +0 -54
- data/spec/rtree_spec.rb +0 -66
- data/spec/schema_spec.rb +0 -131
- data/spec/spec_helper.rb +0 -48
- data/spec/sqlite3/constants_spec.rb +0 -108
- data/spec/sqlite3/database_status_spec.rb +0 -36
- data/spec/sqlite3/status_spec.rb +0 -22
- data/spec/sqlite3/version_spec.rb +0 -28
- data/spec/sqlite3_spec.rb +0 -53
- data/spec/statement_spec.rb +0 -168
- data/spec/storage_map_spec.rb +0 -38
- data/spec/tap_spec.rb +0 -57
- data/spec/text_map_spec.rb +0 -20
- data/spec/type_map_spec.rb +0 -14
- data/spec/version_spec.rb +0 -8
- data/tasks/custom.rake +0 -101
- data/tasks/default.rake +0 -257
- data/tasks/extension.rake +0 -28
- data/tasks/this.rb +0 -208
- /data/{LICENSE → LICENSE.txt} +0 -0
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) 2008 Jeremy Hinegardner
|
|
3
|
-
* All rights reserved. See LICENSE and/or COPYING for details.
|
|
4
|
-
*
|
|
5
|
-
* vim: shiftwidth=4
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
#include "amalgalite.h"
|
|
9
|
-
#include <stdio.h>
|
|
10
|
-
#include <stdnoreturn.h>
|
|
11
|
-
extern VALUE mA;
|
|
12
|
-
VALUE cAR;
|
|
13
|
-
VALUE cARB;
|
|
14
|
-
VALUE eARB_Error;
|
|
15
|
-
|
|
16
|
-
/*
|
|
17
|
-
* cleanup the datatbase and statment values if they are currently open and then
|
|
18
|
-
* raise the message. It is assumed that the *msg pointer passed in is NOT
|
|
19
|
-
* allocated via malloc() or the like. It should be a local static buffer
|
|
20
|
-
* that we do not have to worry about freeing.
|
|
21
|
-
*/
|
|
22
|
-
noreturn void am_bootstrap_cleanup_and_raise( const char* msg, sqlite3* db, sqlite3_stmt* stmt )
|
|
23
|
-
{
|
|
24
|
-
|
|
25
|
-
if ( NULL != stmt ) { sqlite3_finalize( stmt ); stmt = NULL; }
|
|
26
|
-
if ( NULL != db ) { sqlite3_close( db ); }
|
|
27
|
-
|
|
28
|
-
rb_raise(eARB_Error, "%s", msg );
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
void am_bootstrap_from_db( sqlite3* db, VALUE args )
|
|
33
|
-
{
|
|
34
|
-
sqlite3_stmt* stmt = NULL;
|
|
35
|
-
int rc;
|
|
36
|
-
char raise_msg[BUFSIZ];
|
|
37
|
-
int last_row_good;
|
|
38
|
-
|
|
39
|
-
VALUE am_tbl_c = rb_const_get( cARB, rb_intern("DEFAULT_BOOTSTRAP_TABLE") );
|
|
40
|
-
VALUE am_pk_c = rb_const_get( cARB, rb_intern("DEFAULT_ROWID_COLUMN") );
|
|
41
|
-
VALUE am_fname_c = rb_const_get( cARB, rb_intern("DEFAULT_FILENAME_COLUMN") );
|
|
42
|
-
VALUE am_content_c = rb_const_get( cARB, rb_intern("DEFAULT_CONTENTS_COLUMN") );
|
|
43
|
-
|
|
44
|
-
char* tbl_name = NULL;
|
|
45
|
-
char* pk_col = NULL;
|
|
46
|
-
char* fname_col = NULL;
|
|
47
|
-
char* content_col = NULL;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
char sql[BUFSIZ];
|
|
51
|
-
const char* sql_tail = NULL;
|
|
52
|
-
int sql_bytes = 0;
|
|
53
|
-
|
|
54
|
-
const unsigned char* result_text = NULL;
|
|
55
|
-
int result_length = 0;
|
|
56
|
-
|
|
57
|
-
VALUE require_name = Qnil; /* ruby string of the file name for use in eval */
|
|
58
|
-
VALUE eval_this_code = Qnil; /* ruby string of the code to eval from the db */
|
|
59
|
-
VALUE toplevel_binding = rb_const_get( rb_cObject, rb_intern("TOPLEVEL_BINDING") ) ;
|
|
60
|
-
VALUE tmp = Qnil;
|
|
61
|
-
|
|
62
|
-
ID eval_id = rb_intern("eval");
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
tbl_name = ( Qnil == (tmp = rb_hash_aref( args, rb_str_new2( "table_name" ) ) ) ) ? StringValuePtr( am_tbl_c ) : StringValuePtr( tmp );
|
|
67
|
-
pk_col = ( Qnil == (tmp = rb_hash_aref( args, rb_str_new2( "rowid_column" ) ) ) ) ? StringValuePtr( am_pk_c ) : StringValuePtr( tmp );
|
|
68
|
-
fname_col = ( Qnil == (tmp = rb_hash_aref( args, rb_str_new2( "filename_column" ) ) ) ) ? StringValuePtr( am_fname_c ) : StringValuePtr( tmp );
|
|
69
|
-
content_col = ( Qnil == (tmp = rb_hash_aref( args, rb_str_new2( "contents_column" ) ) ) ) ? StringValuePtr( am_content_c ) : StringValuePtr( tmp );
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
/* prepare the db query */
|
|
73
|
-
memset( sql, 0, BUFSIZ );
|
|
74
|
-
sql_bytes = snprintf( sql, BUFSIZ, "SELECT %s, %s FROM %s ORDER BY %s", fname_col, content_col, tbl_name, pk_col );
|
|
75
|
-
rc = sqlite3_prepare_v2( db, sql, sql_bytes, &stmt, &sql_tail ) ;
|
|
76
|
-
if ( SQLITE_OK != rc) {
|
|
77
|
-
memset( raise_msg, 0, BUFSIZ );
|
|
78
|
-
snprintf( raise_msg, BUFSIZ,
|
|
79
|
-
"Failure to prepare bootload select statement table = '%s', rowid col = '%s', filename col ='%s', contents col = '%s' : [SQLITE_ERROR %d] %s\n",
|
|
80
|
-
tbl_name, pk_col, fname_col, content_col, rc, sqlite3_errmsg( db ));
|
|
81
|
-
am_bootstrap_cleanup_and_raise( raise_msg, db, stmt );
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/* loop over the resulting rows, eval'ing and loading $LOADED_FEATURES */
|
|
85
|
-
last_row_good = -1;
|
|
86
|
-
while ( SQLITE_ROW == ( rc = sqlite3_step( stmt ) ) ) {
|
|
87
|
-
/* file name */
|
|
88
|
-
result_text = sqlite3_column_text( stmt, 0 );
|
|
89
|
-
result_length = sqlite3_column_bytes( stmt, 0 );
|
|
90
|
-
require_name = rb_str_new( (const char*)result_text, result_length );
|
|
91
|
-
|
|
92
|
-
/* ruby code */
|
|
93
|
-
result_text = sqlite3_column_text( stmt, 1 );
|
|
94
|
-
result_length = sqlite3_column_bytes( stmt, 1 );
|
|
95
|
-
eval_this_code = rb_str_new( (const char*)result_text, result_length );
|
|
96
|
-
|
|
97
|
-
/* Kernel.eval( code, TOPLEVEL_BINDING, filename, 1 ) */
|
|
98
|
-
rb_funcall(rb_mKernel, eval_id, 4, eval_this_code, toplevel_binding, require_name, INT2FIX(1) );
|
|
99
|
-
|
|
100
|
-
/* TODO: for ruby 1.9 -- put in ? sqlite3://path/to/database?tablename=tbl_name#require_name */
|
|
101
|
-
/* update $LOADED_FEATURES */
|
|
102
|
-
rb_ary_push( rb_gv_get( "$LOADED_FEATURES" ), require_name );
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/* if there was some sqlite error in the processing of the rows */
|
|
106
|
-
if ( SQLITE_DONE != rc ) {
|
|
107
|
-
memset( raise_msg, 0, BUFSIZ );
|
|
108
|
-
snprintf( raise_msg, BUFSIZ, "Failure in bootloading, last successfully loaded rowid was %d : [SQLITE_ERROR %d] %s\n",
|
|
109
|
-
last_row_good, rc, sqlite3_errmsg( db ) );
|
|
110
|
-
am_bootstrap_cleanup_and_raise( raise_msg, db, stmt );
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/* finalize the statement */
|
|
114
|
-
rc = sqlite3_finalize( stmt );
|
|
115
|
-
if ( SQLITE_OK != rc ) {
|
|
116
|
-
memset( raise_msg, 0, BUFSIZ );
|
|
117
|
-
snprintf( raise_msg, BUFSIZ, "Failure to finalize bootload statement : [SQLITE_ERROR %d] %s\n", rc, sqlite3_errmsg( db ) );
|
|
118
|
-
am_bootstrap_cleanup_and_raise( raise_msg, db, stmt );
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* call-seq:
|
|
126
|
-
* Amalgalite::Requires::Bootstrap.lift( 'dbfile' => "lib.db", 'table_name' => "bootload", 'rowid_column' => "id", 'filename_column' => "filename", 'content_column' => "contents" )
|
|
127
|
-
*
|
|
128
|
-
* *WARNING* *WARNING* *WARNING* *WARNING* *WARNING* *WARNING* *WARNING*
|
|
129
|
-
*
|
|
130
|
-
* This is a boostrap mechanism to eval all the code in a particular column in a
|
|
131
|
-
* specially formatted table in an sqlite database. It should only be used for
|
|
132
|
-
* a specific purpose, mainly loading the Amalgalite ruby code directly from an
|
|
133
|
-
* sqlite table.
|
|
134
|
-
*
|
|
135
|
-
* Amalgalite::Requires adds in the ability to _require_ code that is in an
|
|
136
|
-
* sqlite database. Since Amalgalite::Requires is itself ruby code, if
|
|
137
|
-
* Amalgalite::Requires was in an sqlite database, it could not _require_
|
|
138
|
-
* itself. Therefore this method is made available. It is a pure C extension
|
|
139
|
-
* method that directly calls the sqlite3 C functions directly and uses the ruby
|
|
140
|
-
* C api to eval the data in the table.
|
|
141
|
-
*
|
|
142
|
-
* This method attaches to an sqlite3 database (filename) and then does:
|
|
143
|
-
*
|
|
144
|
-
* SELECT filename_column_name, content_column_name
|
|
145
|
-
* FROM table_name
|
|
146
|
-
* ORDER BY rowid_column_name
|
|
147
|
-
*
|
|
148
|
-
* For each row returned it does an _eval_ on the code in the
|
|
149
|
-
* *content_column_name* and then updates _$LOADED_FEATURES_ directly with the value from
|
|
150
|
-
* *filename_column_name*.
|
|
151
|
-
*
|
|
152
|
-
* The database to be opened by _lift_ *must* be an sqlite3 UTF-8 database.
|
|
153
|
-
*
|
|
154
|
-
*/
|
|
155
|
-
VALUE am_bootstrap_lift( VALUE self, VALUE args )
|
|
156
|
-
{
|
|
157
|
-
sqlite3 *db = NULL;
|
|
158
|
-
int rc;
|
|
159
|
-
char raise_msg[BUFSIZ];
|
|
160
|
-
VALUE tmp = Qnil;
|
|
161
|
-
|
|
162
|
-
VALUE am_db_c = rb_const_get( cARB, rb_intern("DEFAULT_DB") );
|
|
163
|
-
|
|
164
|
-
char *dbfile = NULL;
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if ( Qnil == args ) {
|
|
168
|
-
args = rb_hash_new();
|
|
169
|
-
} else {
|
|
170
|
-
args = rb_ary_shift( args );
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
Check_Type( args, T_HASH );
|
|
174
|
-
|
|
175
|
-
/* get the arguments */
|
|
176
|
-
dbfile = ( Qnil == (tmp = rb_hash_aref( args, rb_str_new2( "dbfile" ) ) ) ) ? StringValuePtr( am_db_c ) : StringValuePtr( tmp );
|
|
177
|
-
|
|
178
|
-
/* open the database */
|
|
179
|
-
rc = sqlite3_open_v2( dbfile , &db, SQLITE_OPEN_READONLY, NULL);
|
|
180
|
-
if ( SQLITE_OK != rc ) {
|
|
181
|
-
memset( raise_msg, 0, BUFSIZ );
|
|
182
|
-
snprintf(raise_msg, BUFSIZ, "Failure to open database %s for bootload: [SQLITE_ERROR %d] : %s", dbfile, rc, sqlite3_errmsg( db ) );
|
|
183
|
-
am_bootstrap_cleanup_and_raise( raise_msg, db, NULL );
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
am_bootstrap_from_db( db, args );
|
|
187
|
-
|
|
188
|
-
/* close the database */
|
|
189
|
-
rc = sqlite3_close( db );
|
|
190
|
-
if ( SQLITE_OK != rc ) {
|
|
191
|
-
memset( raise_msg, 0, BUFSIZ );
|
|
192
|
-
snprintf( raise_msg, BUFSIZ, "Failure to close database : [SQLITE_ERROR %d] : %s\n", rc, sqlite3_errmsg( db )),
|
|
193
|
-
am_bootstrap_cleanup_and_raise( raise_msg, db, NULL );
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return Qnil;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* call-seq:
|
|
201
|
-
* Amalgalite::Requires::Bootstrap.lift_str( sql, 'table_name' => "bootload", 'rowid_column' => "id", 'filename_column' => "filename", 'content_column' => "contents" )
|
|
202
|
-
*
|
|
203
|
-
* Bootstrap Amalgalite from a string containing an SQL dump. See Amalgalite::Requires::Bootstrap.lift.
|
|
204
|
-
*
|
|
205
|
-
* For example:
|
|
206
|
-
*
|
|
207
|
-
* Amalgalite::Requires::Bootstrap.lifts(File.read("lib.sql"))
|
|
208
|
-
*/
|
|
209
|
-
VALUE am_bootstrap_lift_str( VALUE self, VALUE args )
|
|
210
|
-
{
|
|
211
|
-
sqlite3 *db = NULL;
|
|
212
|
-
int rc;
|
|
213
|
-
char raise_msg[BUFSIZ];
|
|
214
|
-
VALUE sql = Qnil;
|
|
215
|
-
VALUE args_hsh = Qnil;
|
|
216
|
-
|
|
217
|
-
sql = rb_ary_shift(args);
|
|
218
|
-
StringValue(sql);
|
|
219
|
-
if ( Qnil == sql ) { rb_raise(eARB_Error, "SQL required." ); }
|
|
220
|
-
|
|
221
|
-
args_hsh = rb_ary_shift(args);
|
|
222
|
-
if ( Qnil == args_hsh ) { args_hsh = rb_hash_new(); }
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
rc = sqlite3_open_v2( ":memory:", &db, SQLITE_OPEN_READWRITE, NULL );
|
|
226
|
-
if ( SQLITE_OK != rc ) {
|
|
227
|
-
memset( raise_msg, 0, BUFSIZ );
|
|
228
|
-
snprintf(raise_msg, BUFSIZ, "Failure to open database :memory: for bootload: [SQLITE_ERROR %d] : %s", rc, sqlite3_errmsg( db ) );
|
|
229
|
-
am_bootstrap_cleanup_and_raise( raise_msg, db, NULL );
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/* Load the bootstrap SQL into the database */
|
|
233
|
-
rc = sqlite3_exec( db, StringValuePtr( sql ), NULL, NULL, NULL );
|
|
234
|
-
|
|
235
|
-
if ( SQLITE_OK != rc ) {
|
|
236
|
-
memset( raise_msg, 0, BUFSIZ );
|
|
237
|
-
snprintf(raise_msg, BUFSIZ, "Failure to import bootload sql: [SQLITE_ERROR %d] : %s", rc, sqlite3_errmsg( db ) );
|
|
238
|
-
am_bootstrap_cleanup_and_raise( raise_msg, db, NULL );
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
am_bootstrap_from_db( db, args_hsh );
|
|
242
|
-
|
|
243
|
-
rc = sqlite3_close( db );
|
|
244
|
-
|
|
245
|
-
if ( SQLITE_OK != rc ) {
|
|
246
|
-
memset( raise_msg, 0, BUFSIZ );
|
|
247
|
-
snprintf( raise_msg, BUFSIZ, "Failure to close database : [SQLITE_ERROR %d] : %s\n", rc, sqlite3_errmsg( db )),
|
|
248
|
-
am_bootstrap_cleanup_and_raise( raise_msg, db, NULL );
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return Qnil;
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Bootstrapping module to help _require_ when Amalgalite::Requires is not
|
|
258
|
-
* availble in files.
|
|
259
|
-
*/
|
|
260
|
-
void Init_amalgalite_requires_bootstrap()
|
|
261
|
-
{
|
|
262
|
-
|
|
263
|
-
mA = rb_define_module("Amalgalite");
|
|
264
|
-
cAR = rb_define_class_under(mA, "Requires", rb_cObject);
|
|
265
|
-
cARB = rb_define_class_under(cAR, "Bootstrap", rb_cObject);
|
|
266
|
-
|
|
267
|
-
eARB_Error = rb_define_class_under(cARB, "Error", rb_eStandardError);
|
|
268
|
-
|
|
269
|
-
rb_define_module_function(cARB, "lift", am_bootstrap_lift, -2);
|
|
270
|
-
rb_define_module_function(cARB, "lifts", am_bootstrap_lift, -2);
|
|
271
|
-
|
|
272
|
-
/* constants for default db, table, column, rowid, contents */
|
|
273
|
-
rb_define_const(cARB, "DEFAULT_DB", rb_str_new2( "lib.db" ));
|
|
274
|
-
rb_define_const(cARB, "DEFAULT_TABLE", rb_str_new2( "rubylibs" ));
|
|
275
|
-
rb_define_const(cARB, "DEFAULT_BOOTSTRAP_TABLE", rb_str_new2( "bootstrap" ));
|
|
276
|
-
rb_define_const(cARB, "DEFAULT_ROWID_COLUMN", rb_str_new2( "id" ));
|
|
277
|
-
rb_define_const(cARB, "DEFAULT_FILENAME_COLUMN", rb_str_new2( "filename" ));
|
|
278
|
-
rb_define_const(cARB, "DEFAULT_CONTENTS_COLUMN", rb_str_new2( "contents" ));
|
|
279
|
-
rb_define_const(cARB, "DEFAULT_COMPRESSED_COLUMN", rb_str_new2( "compressed" ));
|
|
280
|
-
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
module Kernel
|
|
2
|
-
# alias the original require away to use later
|
|
3
|
-
alias :amalgalite_original_require :require
|
|
4
|
-
|
|
5
|
-
#
|
|
6
|
-
# hook into the system 'require' to allow for required text or blobs from an
|
|
7
|
-
# amalgalite database.
|
|
8
|
-
#
|
|
9
|
-
def 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
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
private :require
|
|
20
|
-
private :amalgalite_original_require
|
|
21
|
-
end
|
data/lib/amalgalite/packer.rb
DELETED
|
@@ -1,231 +0,0 @@
|
|
|
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/csv_table_importer.rb
|
|
72
|
-
amalgalite/database.rb
|
|
73
|
-
amalgalite/index.rb
|
|
74
|
-
amalgalite/memory_database.rb
|
|
75
|
-
amalgalite/paths.rb
|
|
76
|
-
amalgalite/table.rb
|
|
77
|
-
amalgalite/view.rb
|
|
78
|
-
amalgalite/schema.rb
|
|
79
|
-
amalgalite/version.rb
|
|
80
|
-
amalgalite/sqlite3/version.rb
|
|
81
|
-
amalgalite/sqlite3/constants.rb
|
|
82
|
-
amalgalite/sqlite3/status.rb
|
|
83
|
-
amalgalite/sqlite3/database/status.rb
|
|
84
|
-
amalgalite/sqlite3.rb
|
|
85
|
-
amalgalite/taps/io.rb
|
|
86
|
-
amalgalite/taps/console.rb
|
|
87
|
-
amalgalite/taps.rb
|
|
88
|
-
amalgalite/packer.rb
|
|
89
|
-
amalgalite/core_ext/kernel/require.rb
|
|
90
|
-
amalgalite/requires.rb
|
|
91
|
-
]
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
#
|
|
96
|
-
# Create a new packer instance with the list of items to pack and all the
|
|
97
|
-
# options
|
|
98
|
-
#
|
|
99
|
-
def initialize( options = {} )
|
|
100
|
-
@options = Packer.default_options.merge( options )
|
|
101
|
-
@dbfile = @options[:dbfile] || Requires::Bootstrap::DEFAULT_DB
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
#
|
|
105
|
-
# The SQL to create the table for storing ruby code
|
|
106
|
-
#
|
|
107
|
-
def create_table_sql
|
|
108
|
-
<<-create
|
|
109
|
-
CREATE TABLE #{options[:table_name]} (
|
|
110
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
111
|
-
#{options[:filename_column]} TEXT UNIQUE,
|
|
112
|
-
#{options[:compressed_column]} BOOLEAN,
|
|
113
|
-
#{options[:contents_column]} BLOB
|
|
114
|
-
);
|
|
115
|
-
create
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
#
|
|
119
|
-
# Make sure that the dbfile exists and has the appropriate schema.
|
|
120
|
-
#
|
|
121
|
-
def check_db( db )
|
|
122
|
-
if db.schema.tables[ options[:table_name] ] and options[:drop_table] then
|
|
123
|
-
STDERR.puts "Dropping table #{options[:table_name]}" if options[:verbose]
|
|
124
|
-
db.execute("DROP TABLE #{options[:table_name]}")
|
|
125
|
-
db.reload_schema!
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
unless db.schema.tables[ options[:table_name] ]
|
|
129
|
-
db.execute( create_table_sql )
|
|
130
|
-
db.reload_schema!
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
#
|
|
137
|
-
# Stores all the .rb files in the list into the given database. The prefix
|
|
138
|
-
# is the file system path to remove from the front of the path on each file
|
|
139
|
-
#
|
|
140
|
-
# manifest is an array of OpenStructs.
|
|
141
|
-
#
|
|
142
|
-
def pack_files( manifest )
|
|
143
|
-
db = Amalgalite::Database.new( dbfile )
|
|
144
|
-
check_db( db )
|
|
145
|
-
max_width = manifest.collect{ |m| m.require_path.length }.sort.last
|
|
146
|
-
contents_column = db.schema.tables[ options[:table_name] ].columns[ options[:contents_column] ]
|
|
147
|
-
db.transaction do |trans|
|
|
148
|
-
manifest.each do |file_info|
|
|
149
|
-
msg = " -> #{file_info.require_path.ljust( max_width )} : "
|
|
150
|
-
begin
|
|
151
|
-
if options[:merge] then
|
|
152
|
-
trans.execute( "DELETE FROM #{options[:table_name]} WHERE #{options[:filename_column]} = ?", file_info.require_path )
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
trans.prepare("INSERT INTO #{options[:table_name]}(#{options[:filename_column]}, #{options[:compressed_column]}, #{options[:contents_column]}) VALUES( $filename, $compressed, $contents)") do |stmt|
|
|
156
|
-
contents = IO.readlines( file_info.file_path )
|
|
157
|
-
if options[:self] then
|
|
158
|
-
contents.each { |l| l.gsub!( /^(\s*require .*)$/m, "# commented out by #{self.class.name} \\1") }
|
|
159
|
-
end
|
|
160
|
-
contents = contents.join
|
|
161
|
-
|
|
162
|
-
if options[:compressed] then
|
|
163
|
-
contents = Packer.gzip( contents )
|
|
164
|
-
end
|
|
165
|
-
content_io = StringIO.new( contents )
|
|
166
|
-
stmt.execute( "$filename" => file_info.require_path,
|
|
167
|
-
"$contents" => Amalgalite::Blob.new( :io => content_io,
|
|
168
|
-
:column => contents_column ),
|
|
169
|
-
"$compressed" => options[:compressed] )
|
|
170
|
-
STDERR.puts "#{msg} stored #{file_info.file_path}" if options[:verbose]
|
|
171
|
-
end
|
|
172
|
-
rescue => e
|
|
173
|
-
STDERR.puts "#{msg} error #{e}"
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
#
|
|
180
|
-
# given a file, see if it can be found in the ruby load path, if so, return that
|
|
181
|
-
# full path
|
|
182
|
-
#
|
|
183
|
-
def full_path_of( rb_file )
|
|
184
|
-
$LOAD_PATH.each do |load_path|
|
|
185
|
-
guess = File.expand_path( File.join( load_path, rb_file ) )
|
|
186
|
-
return guess if File.exist?( guess )
|
|
187
|
-
end
|
|
188
|
-
return nil
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
#
|
|
192
|
-
# Make the manifest for packing
|
|
193
|
-
#
|
|
194
|
-
def make_manifest( file_list )
|
|
195
|
-
manifest = []
|
|
196
|
-
prefix_path = ::Pathname.new( options[:strip_prefix] )
|
|
197
|
-
file_list.each do |f|
|
|
198
|
-
file_path = ::Pathname.new( File.expand_path( f ) )
|
|
199
|
-
m = ::OpenStruct.new
|
|
200
|
-
# if it is a directory then grab all the .rb files from it
|
|
201
|
-
if File.directory?( file_path ) then
|
|
202
|
-
manifest.concat( make_manifest( Dir.glob( File.join( f, "**", "*.rb" ) ) ) )
|
|
203
|
-
next
|
|
204
|
-
elsif File.readable?( file_path ) then
|
|
205
|
-
m.require_path = file_path.relative_path_from( prefix_path )
|
|
206
|
-
m.file_path = file_path.realpath.to_s
|
|
207
|
-
elsif lp = full_path_of( f ) then
|
|
208
|
-
m.require_path = f
|
|
209
|
-
m.file_path = lp
|
|
210
|
-
else
|
|
211
|
-
STDERR.puts "Unable to add #{f} to the manifest, cannot find the file on disk"
|
|
212
|
-
next
|
|
213
|
-
end
|
|
214
|
-
# Make sure that we can handle files without the .rb extension
|
|
215
|
-
# if we have to. This means bin/foo works as a require path
|
|
216
|
-
# without requiring bin/foo to actually be bin/foo.rb
|
|
217
|
-
m.require_path = m.require_path.to_s.sub(/\.rb\Z/,'')
|
|
218
|
-
manifest << m
|
|
219
|
-
end
|
|
220
|
-
return manifest
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
#
|
|
224
|
-
# Given a list of files pack them into the associated database and table.
|
|
225
|
-
#
|
|
226
|
-
def pack( file_list )
|
|
227
|
-
manifest = make_manifest( file_list )
|
|
228
|
-
pack_files( manifest )
|
|
229
|
-
end
|
|
230
|
-
end
|
|
231
|
-
end
|
data/lib/amalgalite/requires.rb
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
require 'amalgalite'
|
|
2
|
-
require 'pathname'
|
|
3
|
-
require 'zlib'
|
|
4
|
-
require 'amalgalite/packer'
|
|
5
|
-
|
|
6
|
-
module Amalgalite
|
|
7
|
-
#
|
|
8
|
-
# Requires encapsulates requiring items from the database
|
|
9
|
-
#
|
|
10
|
-
class Requires
|
|
11
|
-
class << self
|
|
12
|
-
def load_path_db_connections
|
|
13
|
-
@load_path_db_connections ||= {}
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def load_path
|
|
17
|
-
@load_path ||= []
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
#
|
|
21
|
-
# Allocate a database connection to the given filename. For
|
|
22
|
-
# file databases, this means giving the same connection
|
|
23
|
-
# back if you ask for a connection to the same file.
|
|
24
|
-
# For in-memory databases, you get a new one each time.
|
|
25
|
-
#
|
|
26
|
-
def db_connection_to( dbfile_name )
|
|
27
|
-
if dbfile_name == ":memory:"
|
|
28
|
-
return ::Amalgalite::Database.new( dbfile_name )
|
|
29
|
-
else
|
|
30
|
-
unless connection = load_path_db_connections[ dbfile_name ]
|
|
31
|
-
connection = ::Amalgalite::Database.new( dbfile_name )
|
|
32
|
-
load_path_db_connections[dbfile_name] = connection
|
|
33
|
-
end
|
|
34
|
-
return connection
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
#
|
|
39
|
-
# Setting a class level variable as a flag to know what we are currently
|
|
40
|
-
# in the middle of requiring
|
|
41
|
-
#
|
|
42
|
-
def requiring
|
|
43
|
-
@requiring ||= []
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def require( filename )
|
|
47
|
-
if load_path.empty? then
|
|
48
|
-
raise ::LoadError, "Amalgalite load path is empty -- #{filename}"
|
|
49
|
-
elsif $LOADED_FEATURES.include?( filename ) then
|
|
50
|
-
return false
|
|
51
|
-
elsif Requires.requiring.include?( filename ) then
|
|
52
|
-
return false
|
|
53
|
-
else
|
|
54
|
-
Requires.requiring << filename
|
|
55
|
-
load_path.each do |lp|
|
|
56
|
-
if lp.require( filename ) then
|
|
57
|
-
Requires.requiring.delete( filename )
|
|
58
|
-
return true
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
Requires.requiring.delete( filename )
|
|
62
|
-
raise ::LoadError, "amalgalite has no such file to load -- #{filename}"
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
attr_reader :dbfile_name
|
|
68
|
-
attr_reader :table_name
|
|
69
|
-
attr_reader :filename_column
|
|
70
|
-
attr_reader :contents_column
|
|
71
|
-
attr_reader :compressed_column
|
|
72
|
-
attr_reader :db_connection
|
|
73
|
-
|
|
74
|
-
def initialize( opts = {} )
|
|
75
|
-
@dbfile_name = opts[:dbfile_name] || Bootstrap::DEFAULT_DB
|
|
76
|
-
@table_name = opts[:table_name] || Bootstrap::DEFAULT_TABLE
|
|
77
|
-
@filename_column = opts[:filename_column] || Bootstrap::DEFAULT_FILENAME_COLUMN
|
|
78
|
-
@contents_column = opts[:contents_column] || Bootstrap::DEFAULT_CONTENTS_COLUMN
|
|
79
|
-
@compressed_column = opts[:compressed_column] || Bootstrap::DEFAULT_COMPRESSED_COLUMN
|
|
80
|
-
@db_connection = Requires.db_connection_to( dbfile_name )
|
|
81
|
-
Requires.load_path << self
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
#
|
|
85
|
-
# return the sql to find the file contents for a file in this requires
|
|
86
|
-
#
|
|
87
|
-
def sql
|
|
88
|
-
@sql ||= "SELECT #{filename_column}, #{compressed_column}, #{contents_column} FROM #{table_name} WHERE #{filename_column} = ?"
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
#
|
|
92
|
-
# load a file in this database table. This will check and see if the
|
|
93
|
-
# file is already required. If it isn't it will select the contents
|
|
94
|
-
# associated with the row identified by the filename and eval those contents
|
|
95
|
-
# within the context of TOPLEVEL_BINDING. The filename is then appended to
|
|
96
|
-
# $LOADED_FEATURES.
|
|
97
|
-
#
|
|
98
|
-
# if the file was required then true is returned, otherwise false
|
|
99
|
-
#
|
|
100
|
-
def require( filename )
|
|
101
|
-
if $LOADED_FEATURES.include?( filename ) then
|
|
102
|
-
return false
|
|
103
|
-
else
|
|
104
|
-
begin
|
|
105
|
-
filename = filename.gsub(/\.rb\Z/,'')
|
|
106
|
-
|
|
107
|
-
contents = file_contents(filename)
|
|
108
|
-
|
|
109
|
-
if contents
|
|
110
|
-
eval( contents, TOPLEVEL_BINDING, filename )
|
|
111
|
-
$LOADED_FEATURES << filename
|
|
112
|
-
return true
|
|
113
|
-
else
|
|
114
|
-
return false
|
|
115
|
-
end
|
|
116
|
-
rescue => e
|
|
117
|
-
raise ::LoadError, "Failure loading #{filename} from #{dbfile_name} : #{e}"
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
#
|
|
123
|
-
# Return the contents of the named file.
|
|
124
|
-
#
|
|
125
|
-
def file_contents(filename)
|
|
126
|
-
rows = db_connection.execute(sql, filename)
|
|
127
|
-
if rows.size > 0 then
|
|
128
|
-
row = rows.first
|
|
129
|
-
|
|
130
|
-
contents = row[contents_column].to_s
|
|
131
|
-
if row[compressed_column] then
|
|
132
|
-
contents = ::Amalgalite::Packer.gunzip( contents )
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
return contents
|
|
136
|
-
else
|
|
137
|
-
return nil
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
#
|
|
143
|
-
# Import an SQL dump into the fake file system.
|
|
144
|
-
#
|
|
145
|
-
def import(sql)
|
|
146
|
-
db_connection.import(sql)
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
require 'amalgalite/core_ext/kernel/require'
|