amalgalite 0.2.4 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +30 -5
- 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/filestore.db +0 -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 +40 -31
- data/ext/amalgalite3_blob.c +7 -12
- data/ext/amalgalite3_constants.c +41 -4
- data/ext/amalgalite3_database.c +55 -5
- data/ext/amalgalite3_requires_bootstrap.c +204 -0
- data/ext/amalgalite3_statement.c +3 -4
- data/ext/extconf.rb +2 -0
- data/ext/gen_constants.rb +15 -4
- data/ext/sqlite3.c +68652 -59046
- data/ext/sqlite3.h +2613 -1939
- data/ext/sqlite3ext.h +13 -3
- data/gemspec.rb +0 -1
- data/lib/amalgalite.rb +22 -18
- data/lib/amalgalite/core_ext/kernel/require.rb +2 -2
- data/lib/amalgalite/database.rb +15 -6
- data/lib/amalgalite/index.rb +19 -3
- data/lib/amalgalite/requires.rb +37 -0
- data/lib/amalgalite/schema.rb +26 -5
- data/lib/amalgalite/sqlite3.rb +2 -0
- data/lib/amalgalite/sqlite3/constants.rb +51 -14
- data/lib/amalgalite/sqlite3/database/status.rb +69 -0
- data/lib/amalgalite/sqlite3/status.rb +61 -0
- data/lib/amalgalite/statement.rb +1 -1
- data/lib/amalgalite/table.rb +5 -5
- data/lib/amalgalite/type_map.rb +3 -0
- data/lib/amalgalite/version.rb +2 -2
- data/spec/blob_spec.rb +1 -1
- data/spec/boolean_spec.rb +0 -3
- data/spec/database_spec.rb +11 -3
- data/spec/schema_spec.rb +14 -0
- data/spec/sqlite3/constants_spec.rb +44 -4
- data/spec/sqlite3/database_status_spec.rb +36 -0
- data/spec/sqlite3/status_spec.rb +18 -0
- data/spec/sqlite3/version_spec.rb +3 -3
- data/spec/sqlite3_spec.rb +0 -12
- data/tasks/announce.rake +2 -1
- data/tasks/config.rb +2 -1
- data/tasks/distribution.rake +7 -0
- data/tasks/rubyforge.rake +14 -6
- metadata +53 -36
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#
|
4
|
+
# An Amalgalite example showing how to 'require' data in an amalgalite database
|
5
|
+
#
|
6
|
+
# We'll make a database with one table, that we store file contents in.
|
7
|
+
#
|
8
|
+
|
9
|
+
$: << "../lib"
|
10
|
+
$: << "../ext"
|
11
|
+
require 'rubygems'
|
12
|
+
require 'amalgalite'
|
13
|
+
|
14
|
+
#
|
15
|
+
# create the database
|
16
|
+
#
|
17
|
+
File.unlink( "lib.db" ) if File.exist?( "lib.db" )
|
18
|
+
db = Amalgalite::Database.new( "lib.db" )
|
19
|
+
STDERR.puts "Creating rubylibs table"
|
20
|
+
db.execute(<<-create)
|
21
|
+
CREATE TABLE rubylibs(
|
22
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
23
|
+
filename VARCHAR UNIQUE,
|
24
|
+
contents TEXT
|
25
|
+
)
|
26
|
+
create
|
27
|
+
|
28
|
+
|
29
|
+
#
|
30
|
+
# insert some source code into a row
|
31
|
+
#
|
32
|
+
db.execute("INSERT INTO rubylibs(filename, contents) VALUES ( $filename, $contents )",
|
33
|
+
{ "$filename" => "example",
|
34
|
+
"$contents" => <<code
|
35
|
+
class ExampleCode
|
36
|
+
def initialize( x )
|
37
|
+
puts "Initializing ExampleCode"
|
38
|
+
@x = x
|
39
|
+
end
|
40
|
+
|
41
|
+
def foo
|
42
|
+
puts @x
|
43
|
+
end
|
44
|
+
end
|
45
|
+
code
|
46
|
+
})
|
47
|
+
db.close
|
48
|
+
|
49
|
+
require 'amalgalite/requires'
|
50
|
+
Amalgalite::Requires.new( :dbfile_name => "lib.db" )
|
51
|
+
require 'example'
|
52
|
+
e = ExampleCode.new( 'it works!' )
|
53
|
+
e.foo
|
54
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'amalgalite'
|
5
|
+
|
6
|
+
db_name = ARGV.shift
|
7
|
+
unless db_name
|
8
|
+
puts "Usage: #{File.basename($0)} dbname"
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
db = Amalgalite::Database.new( db_name )
|
12
|
+
col_info = %w[ default_value declared_data_type collation_sequence_name not_null_constraint primary_key auto_increment ]
|
13
|
+
max_width = col_info.collect { |c| c.length }.sort.last
|
14
|
+
|
15
|
+
db.schema.tables.keys.sort.each do |table_name|
|
16
|
+
puts "Table: #{table_name}"
|
17
|
+
puts "=" * 42
|
18
|
+
db.schema.tables[table_name].columns.each_pair do |col_name, col|
|
19
|
+
puts " Column : #{col.name}"
|
20
|
+
col_info.each do |ci|
|
21
|
+
puts " |#{ci.rjust( max_width, "." )} : #{col.send( ci )}"
|
22
|
+
end
|
23
|
+
puts
|
24
|
+
end
|
25
|
+
|
26
|
+
db.schema.tables[table_name].indexes.each_pair do |idx_name, index|
|
27
|
+
puts " Index : #{index.name}"
|
28
|
+
puts " |#{"sequence_number".rjust( max_width, "." )} : #{index.sequence_number}"
|
29
|
+
puts " |#{"is unique?".rjust( max_width, ".")} : #{index.unique?}"
|
30
|
+
puts " |#{"columns".rjust( max_width, ".")} : #{index.columns.collect { |c| c.name }.join(',') }"
|
31
|
+
puts
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
data/ext/amalgalite3.c
CHANGED
@@ -12,6 +12,7 @@ VALUE mA; /* module Amalgalite */
|
|
12
12
|
VALUE mAS; /* module Amalgalite::SQLite3 */
|
13
13
|
VALUE mASV; /* module Amalgalite::SQLite3::Version */
|
14
14
|
VALUE eAS_Error; /* class Amalgalite::SQLite3::Error */
|
15
|
+
VALUE cAS_Stat; /* class Amalgalite::SQLite3::Stat */
|
15
16
|
|
16
17
|
/*----------------------------------------------------------------------
|
17
18
|
* module methods for Amalgalite::SQLite3
|
@@ -69,39 +70,40 @@ VALUE am_sqlite3_complete(VALUE self, VALUE args)
|
|
69
70
|
|
70
71
|
/*
|
71
72
|
* call-seq:
|
72
|
-
* Amalgalite::SQLite3.
|
73
|
+
* Amalgalite::SQLite3::Stat.update!( reset = false ) -> nil
|
73
74
|
*
|
74
|
-
*
|
75
|
-
|
76
|
-
|
77
|
-
{
|
78
|
-
return SQLINT64_2NUM(sqlite3_memory_used());
|
79
|
-
}
|
80
|
-
|
81
|
-
/*
|
82
|
-
* call-seq:
|
83
|
-
* Amalgalite::SQLite3.memory_highwater_mark -> Numeric
|
84
|
-
*
|
85
|
-
* Return the maximum value of Amalgalite::SQLite3.memory_used since the last
|
86
|
-
* time the highwater mark was reset.
|
75
|
+
* Populates the _@current_ and _@higwater_ instance variables of self
|
76
|
+
* object with the values from the sqlite3_status call. If reset it true then
|
77
|
+
* the highwater mark for the stat is reset
|
87
78
|
*
|
88
79
|
*/
|
89
|
-
VALUE
|
80
|
+
VALUE am_sqlite3_stat_update_bang( int argc, VALUE *argv, VALUE self )
|
90
81
|
{
|
91
|
-
|
92
|
-
|
82
|
+
int status_op = -1;
|
83
|
+
int current = -1;
|
84
|
+
int highwater = -1;
|
85
|
+
VALUE reset = Qfalse;
|
86
|
+
int reset_flag = 0;
|
87
|
+
int rc;
|
88
|
+
|
89
|
+
status_op = FIX2INT( rb_iv_get( self, "@code" ) );
|
90
|
+
if ( argc > 0 ) {
|
91
|
+
reset = argv[0];
|
92
|
+
reset_flag = ( Qtrue == reset ) ? 1 : 0 ;
|
93
|
+
}
|
93
94
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
95
|
+
rc = sqlite3_status( status_op, ¤t, &highwater, reset_flag );
|
96
|
+
|
97
|
+
if ( SQLITE_OK != rc ) {
|
98
|
+
VALUE n = rb_iv_get( self, "@name" ) ;
|
99
|
+
char* name = StringValuePtr( n );
|
100
|
+
rb_raise(eAS_Error, "Failure to retrieve status for %s : [SQLITE_ERROR %d] \n", name, rc);
|
101
|
+
}
|
102
|
+
|
103
|
+
rb_iv_set( self, "@current", INT2NUM( current ) );
|
104
|
+
rb_iv_set( self, "@highwater", INT2NUM( highwater) );
|
105
|
+
|
106
|
+
return Qnil;
|
105
107
|
}
|
106
108
|
|
107
109
|
/*
|
@@ -148,6 +150,9 @@ VALUE am_sqlite3_libversion_number(VALUE self)
|
|
148
150
|
return INT2FIX(sqlite3_libversion_number());
|
149
151
|
}
|
150
152
|
|
153
|
+
/**
|
154
|
+
* The Amalgalite ruby extension
|
155
|
+
*/
|
151
156
|
|
152
157
|
void Init_amalgalite3()
|
153
158
|
{
|
@@ -162,11 +167,14 @@ void Init_amalgalite3()
|
|
162
167
|
mAS = rb_define_module_under(mA, "SQLite3");
|
163
168
|
rb_define_module_function(mAS, "threadsafe?", am_sqlite3_threadsafe, 0);
|
164
169
|
rb_define_module_function(mAS, "complete?", am_sqlite3_complete, -2);
|
165
|
-
rb_define_module_function(mAS, "memory_used", am_sqlite3_memory_used,0);
|
166
|
-
rb_define_module_function(mAS, "memory_highwater_mark", am_sqlite3_memory_highwater,0);
|
167
|
-
rb_define_module_function(mAS, "memory_highwater_mark_reset!", am_sqlite3_memory_highwater_reset,0);
|
168
170
|
rb_define_module_function(mAS, "randomness", am_sqlite3_randomness,1);
|
169
171
|
|
172
|
+
/*
|
173
|
+
* class encapsulating a single Stat
|
174
|
+
*/
|
175
|
+
cAS_Stat = rb_define_class_under(mAS, "Stat", rb_cObject);
|
176
|
+
rb_define_method(cAS_Stat, "update!", am_sqlite3_stat_update_bang, -1);
|
177
|
+
|
170
178
|
/*
|
171
179
|
* Base class of all SQLite3 errors
|
172
180
|
*/
|
@@ -186,6 +194,7 @@ void Init_amalgalite3()
|
|
186
194
|
Init_amalgalite3_database( );
|
187
195
|
Init_amalgalite3_statement( );
|
188
196
|
Init_amalgalite3_blob( );
|
197
|
+
Init_amalgalite3_requires_bootstrap( );
|
189
198
|
|
190
199
|
}
|
191
200
|
|
data/ext/amalgalite3_blob.c
CHANGED
@@ -39,8 +39,8 @@ VALUE am_sqlite3_blob_initialize( VALUE self, VALUE db, VALUE db_name, VALUE tab
|
|
39
39
|
if ( ( RSTRING( flag_str )->len != 1) ||
|
40
40
|
( ( 'r' != RSTRING( flag_str )->ptr[0] ) &&
|
41
41
|
( 'w' != RSTRING( flag_str )->ptr[0] ))) {
|
42
|
-
rb_raise( eAS_Error, "Error opening Blob in db = %s, table = %s, column = %s, rowid = %
|
43
|
-
zDb, zTable, zColumn, iRow, RSTRING( flag_str )->ptr);
|
42
|
+
rb_raise( eAS_Error, "Error opening Blob in db = %s, table = %s, column = %s, rowid = %lu. Invalid flag '%s'. Must be either 'w' or 'r'\n",
|
43
|
+
zDb, zTable, zColumn, (unsigned long)iRow, RSTRING( flag_str )->ptr);
|
44
44
|
}
|
45
45
|
|
46
46
|
/* switch to write mode */
|
@@ -49,10 +49,9 @@ VALUE am_sqlite3_blob_initialize( VALUE self, VALUE db, VALUE db_name, VALUE tab
|
|
49
49
|
}
|
50
50
|
|
51
51
|
/* open the blob and associate the db to it */
|
52
|
-
rc = sqlite3_blob_open(am_db->db, zDb, zTable, zColumn, iRow, flags, &(am_blob->blob) );
|
53
|
-
if (
|
54
|
-
rb_raise( eAS_Error, "Error opening Blob in db = %s, table = %s, column = %s, rowid = %
|
55
|
-
zDb, zTable, zColumn, iRow, rc, sqlite3_errmsg( am_db->db ));
|
52
|
+
rc = sqlite3_blob_open( am_db->db, zDb, zTable, zColumn, iRow, flags, &( am_blob->blob ) );
|
53
|
+
if ( SQLITE_OK != rc ) {
|
54
|
+
rb_raise( eAS_Error, "Error opening Blob in db = %s, table = %s, column = %s, rowid = %lu : [SQLITE_ERROR %d] %s\n", zDb, zTable, zColumn, (unsigned long)iRow, rc, sqlite3_errmsg( am_db->db) );
|
56
55
|
}
|
57
56
|
am_blob->length = sqlite3_blob_bytes( am_blob->blob );
|
58
57
|
am_blob->db = am_db->db;
|
@@ -203,8 +202,8 @@ void am_sqlite3_blob_free(am_sqlite3_blob* wrapper)
|
|
203
202
|
*/
|
204
203
|
VALUE am_sqlite3_blob_alloc(VALUE klass)
|
205
204
|
{
|
206
|
-
am_sqlite3_blob *wrapper = ALLOC(am_sqlite3_blob);
|
207
|
-
VALUE obj
|
205
|
+
am_sqlite3_blob *wrapper = ALLOC( am_sqlite3_blob );
|
206
|
+
VALUE obj ;
|
208
207
|
|
209
208
|
wrapper->current_offset = 0;
|
210
209
|
wrapper->db = NULL;
|
@@ -216,10 +215,6 @@ VALUE am_sqlite3_blob_alloc(VALUE klass)
|
|
216
215
|
void Init_amalgalite3_blob( )
|
217
216
|
{
|
218
217
|
|
219
|
-
/** :stopdoc:
|
220
|
-
* These calls are here just to allow for rdoc generation
|
221
|
-
* :startdoc:
|
222
|
-
*/
|
223
218
|
VALUE ma = rb_define_module("Amalgalite");
|
224
219
|
VALUE mas = rb_define_module_under(ma, "SQLite3");
|
225
220
|
|
data/ext/amalgalite3_constants.c
CHANGED
@@ -3,10 +3,7 @@
|
|
3
3
|
#include "amalgalite3.h";
|
4
4
|
void Init_amalgalite3_constants( )
|
5
5
|
{
|
6
|
-
|
7
|
-
* These calls are here just to allow for rdoc generation
|
8
|
-
* :startdoc:
|
9
|
-
*/
|
6
|
+
|
10
7
|
VALUE ma = rb_define_module("Amalgalite");
|
11
8
|
VALUE mas = rb_define_module_under(ma, "SQLite3");
|
12
9
|
|
@@ -14,6 +11,14 @@ void Init_amalgalite3_constants( )
|
|
14
11
|
* module encapsulating all the SQLite C extension constants
|
15
12
|
*/
|
16
13
|
VALUE mC = rb_define_module_under( mas, "Constants");
|
14
|
+
/**
|
15
|
+
* module encapsulating the SQLite3 C extension constants for DBStatus
|
16
|
+
*/
|
17
|
+
VALUE mC_DBStatus = rb_define_module_under(mC, "DBStatus");
|
18
|
+
|
19
|
+
/* no meaningful autogenerated documentation -- constant is self explanatory ?*/
|
20
|
+
rb_define_const(mC_DBStatus, "LOOKASIDE_USED", INT2FIX(SQLITE_DBSTATUS_LOOKASIDE_USED));
|
21
|
+
|
17
22
|
/**
|
18
23
|
* module encapsulating the SQLite3 C extension constants for DataType
|
19
24
|
*/
|
@@ -176,4 +181,36 @@ void Init_amalgalite3_constants( )
|
|
176
181
|
/* 18 -- String or BLOB exceeds size limit */
|
177
182
|
rb_define_const(mC_ResultCode, "TOOBIG", INT2FIX(SQLITE_TOOBIG));
|
178
183
|
|
184
|
+
/**
|
185
|
+
* module encapsulating the SQLite3 C extension constants for Status
|
186
|
+
*/
|
187
|
+
VALUE mC_Status = rb_define_module_under(mC, "Status");
|
188
|
+
|
189
|
+
/* no meaningful autogenerated documentation -- constant is self explanatory ?*/
|
190
|
+
rb_define_const(mC_Status, "MALLOC_SIZE", INT2FIX(SQLITE_STATUS_MALLOC_SIZE));
|
191
|
+
|
192
|
+
/* no meaningful autogenerated documentation -- constant is self explanatory ?*/
|
193
|
+
rb_define_const(mC_Status, "MEMORY_USED", INT2FIX(SQLITE_STATUS_MEMORY_USED));
|
194
|
+
|
195
|
+
/* no meaningful autogenerated documentation -- constant is self explanatory ?*/
|
196
|
+
rb_define_const(mC_Status, "PAGECACHE_OVERFLOW", INT2FIX(SQLITE_STATUS_PAGECACHE_OVERFLOW));
|
197
|
+
|
198
|
+
/* no meaningful autogenerated documentation -- constant is self explanatory ?*/
|
199
|
+
rb_define_const(mC_Status, "PAGECACHE_SIZE", INT2FIX(SQLITE_STATUS_PAGECACHE_SIZE));
|
200
|
+
|
201
|
+
/* no meaningful autogenerated documentation -- constant is self explanatory ?*/
|
202
|
+
rb_define_const(mC_Status, "PAGECACHE_USED", INT2FIX(SQLITE_STATUS_PAGECACHE_USED));
|
203
|
+
|
204
|
+
/* no meaningful autogenerated documentation -- constant is self explanatory ?*/
|
205
|
+
rb_define_const(mC_Status, "PARSER_STACK", INT2FIX(SQLITE_STATUS_PARSER_STACK));
|
206
|
+
|
207
|
+
/* no meaningful autogenerated documentation -- constant is self explanatory ?*/
|
208
|
+
rb_define_const(mC_Status, "SCRATCH_OVERFLOW", INT2FIX(SQLITE_STATUS_SCRATCH_OVERFLOW));
|
209
|
+
|
210
|
+
/* no meaningful autogenerated documentation -- constant is self explanatory ?*/
|
211
|
+
rb_define_const(mC_Status, "SCRATCH_SIZE", INT2FIX(SQLITE_STATUS_SCRATCH_SIZE));
|
212
|
+
|
213
|
+
/* no meaningful autogenerated documentation -- constant is self explanatory ?*/
|
214
|
+
rb_define_const(mC_Status, "SCRATCH_USED", INT2FIX(SQLITE_STATUS_SCRATCH_USED));
|
215
|
+
|
179
216
|
}
|
data/ext/amalgalite3_database.c
CHANGED
@@ -6,7 +6,8 @@
|
|
6
6
|
* vim: shiftwidth=4
|
7
7
|
*/
|
8
8
|
|
9
|
-
VALUE cAS_Database;
|
9
|
+
VALUE cAS_Database; /* class Amalgliate::SQLite3::Database */
|
10
|
+
VALUE cAS_Database_Stat; /* class Amalgliate::SQLite3::Database::Stat */
|
10
11
|
|
11
12
|
/**
|
12
13
|
* Document-method: open
|
@@ -218,6 +219,50 @@ VALUE am_sqlite3_database_total_changes(VALUE self)
|
|
218
219
|
return INT2FIX(rc);
|
219
220
|
}
|
220
221
|
|
222
|
+
/*
|
223
|
+
* call-seq:
|
224
|
+
* stat.update!( reset = false ) -> nil
|
225
|
+
*
|
226
|
+
* Populates the _@current_ and _@higwater_ instance variables of the given
|
227
|
+
* Database Stat object with the values from the sqlite3_db_status call.
|
228
|
+
* If reset it true then the highwater mark for the stat is reset
|
229
|
+
*
|
230
|
+
*/
|
231
|
+
VALUE am_sqlite3_database_stat_update_bang( int argc, VALUE *argv, VALUE self )
|
232
|
+
{
|
233
|
+
int current = -1;
|
234
|
+
int highwater = -1;
|
235
|
+
int reset_flag = 0;
|
236
|
+
int status_op = FIX2INT( rb_iv_get( self, "@code" ) );
|
237
|
+
int rc;
|
238
|
+
|
239
|
+
am_sqlite3 *am_db;
|
240
|
+
|
241
|
+
VALUE reset = Qfalse;
|
242
|
+
VALUE db = rb_iv_get( self, "@api_db" );
|
243
|
+
|
244
|
+
Data_Get_Struct(db, am_sqlite3, am_db);
|
245
|
+
|
246
|
+
if ( argc > 0 ) {
|
247
|
+
reset = argv[0];
|
248
|
+
reset_flag = ( Qtrue == reset ) ? 1 : 0 ;
|
249
|
+
}
|
250
|
+
|
251
|
+
rc = sqlite3_db_status( am_db->db, status_op, ¤t, &highwater, reset_flag );
|
252
|
+
|
253
|
+
if ( SQLITE_OK != rc ) {
|
254
|
+
VALUE n = rb_iv_get( self, "@name");
|
255
|
+
char* name = StringValuePtr( n );
|
256
|
+
rb_raise(eAS_Error, "Failure to retrieve database status for %s : [SQLITE_ERROR %d] \n", name, rc);
|
257
|
+
}
|
258
|
+
|
259
|
+
rb_iv_set( self, "@current", INT2NUM( current ) );
|
260
|
+
rb_iv_set( self, "@highwater", INT2NUM( highwater) );
|
261
|
+
|
262
|
+
return Qnil;
|
263
|
+
}
|
264
|
+
|
265
|
+
|
221
266
|
|
222
267
|
/**
|
223
268
|
* call-seq:
|
@@ -463,13 +508,12 @@ VALUE am_sqlite3_database_alloc(VALUE klass)
|
|
463
508
|
return obj;
|
464
509
|
}
|
465
510
|
|
511
|
+
/**
|
512
|
+
* Amalgalite Database
|
513
|
+
*/
|
466
514
|
void Init_amalgalite3_database( )
|
467
515
|
{
|
468
516
|
|
469
|
-
/** :stopdoc:
|
470
|
-
* These calls are here just to allow for rdoc generation
|
471
|
-
* :startdoc:
|
472
|
-
*/
|
473
517
|
VALUE ma = rb_define_module("Amalgalite");
|
474
518
|
VALUE mas = rb_define_module_under(ma, "SQLite3");
|
475
519
|
|
@@ -493,5 +537,11 @@ void Init_amalgalite3_database( )
|
|
493
537
|
rb_define_method(cAS_Database, "last_error_code", am_sqlite3_database_last_error_code, 0); /* in amalgalite3_database.c */
|
494
538
|
rb_define_method(cAS_Database, "last_error_message", am_sqlite3_database_last_error_message, 0); /* in amalgalite3_database.c */
|
495
539
|
|
540
|
+
/*
|
541
|
+
* Ecapuslate a SQLite3 Database stat
|
542
|
+
*/
|
543
|
+
cAS_Database_Stat = rb_define_class_under( cAS_Database, "Stat", rb_cObject );
|
544
|
+
rb_define_method(cAS_Database_Stat, "update!", am_sqlite3_database_stat_update_bang, -1); /* in amalgalite3_database.c */
|
545
|
+
|
496
546
|
}
|
497
547
|
|
@@ -0,0 +1,204 @@
|
|
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 "amalgalite3.h"
|
9
|
+
extern VALUE mA;
|
10
|
+
static VALUE cAR;
|
11
|
+
static VALUE cARB;
|
12
|
+
static VALUE eARB_Error;
|
13
|
+
|
14
|
+
/*
|
15
|
+
* cleanup the datatbase and statment values if they are currently open and then
|
16
|
+
* raise the message. It converts the error message to a String so that the C
|
17
|
+
* string can be free'd and then raise with a ruby object in the hopes that
|
18
|
+
* there is no memory leak from the C allocation.
|
19
|
+
*/
|
20
|
+
void am_bootstrap_cleanup_and_raise( char* msg, sqlite3* db, sqlite3_stmt* stmt )
|
21
|
+
{
|
22
|
+
VALUE msg_obj = rb_str_new2( msg );
|
23
|
+
|
24
|
+
if ( NULL != stmt ) { sqlite3_finalize( stmt ); }
|
25
|
+
if ( NULL != db ) { sqlite3_close( db ); }
|
26
|
+
|
27
|
+
free( msg );
|
28
|
+
rb_raise(eARB_Error, msg );
|
29
|
+
}
|
30
|
+
|
31
|
+
|
32
|
+
/**
|
33
|
+
* call-seq:
|
34
|
+
* Amalgalite::Requires::Bootstrap.lift( 'dbfile' => "lib.db", 'table_name' => "bootload", 'rowid_column' => "id", 'filename_column' => "filename", 'content_column' => "contents" )
|
35
|
+
*
|
36
|
+
* *WARNING* *WARNING* *WARNING* *WARNING* *WARNING* *WARNING* *WARNING*
|
37
|
+
*
|
38
|
+
* This is a boostrap mechanism to eval all the code in a particular column in a
|
39
|
+
* specially formatted table in an sqlite database. It should only be used for
|
40
|
+
* a specific purpose, mainly loading the Amalgalite ruby code directly from an
|
41
|
+
* sqlite table.
|
42
|
+
*
|
43
|
+
* Amalgalite::Requires adds in the ability to _require_ code that is in an
|
44
|
+
* sqlite database. Since Amalgalite::Requires is itself ruby code, if
|
45
|
+
* Amalgalite::Requires was in an sqlite database, it could not _require_
|
46
|
+
* itself. Therefore this method is made available. It is a pure C extension
|
47
|
+
* method that directly calls the sqlite3 C functions directly and uses the ruby
|
48
|
+
* C api to eval the data in the table.
|
49
|
+
*
|
50
|
+
* This method attaches to an sqlite3 database (filename) and then does:
|
51
|
+
*
|
52
|
+
* SELECT filename_column_name, content_column_name
|
53
|
+
* FROM table_name
|
54
|
+
* ORDER BY rowid_column_name
|
55
|
+
*
|
56
|
+
* For each row returned it does an _eval_ on the code in the
|
57
|
+
* *content_column_name* and then updates _$"_ directly with the value from
|
58
|
+
* *filename_column_name*.
|
59
|
+
*
|
60
|
+
* The database to be opened by _lift_ *must* be an sqlite3 UTF-8 database.
|
61
|
+
*
|
62
|
+
*/
|
63
|
+
VALUE am_bootstrap_lift( VALUE self, VALUE args )
|
64
|
+
{
|
65
|
+
sqlite3* db = NULL;
|
66
|
+
sqlite3_stmt* stmt = NULL;
|
67
|
+
int rc;
|
68
|
+
int last_row_good;
|
69
|
+
char* raise_msg = NULL;
|
70
|
+
|
71
|
+
VALUE am_db_c = rb_const_get( cARB, rb_intern("DEFAULT_DB") );
|
72
|
+
VALUE am_tbl_c = rb_const_get( cARB, rb_intern("DEFAULT_TABLE") );
|
73
|
+
VALUE am_pk_c = rb_const_get( cARB, rb_intern("DEFAULT_ROWID_COLUMN") );
|
74
|
+
VALUE am_fname_c = rb_const_get( cARB, rb_intern("DEFAULT_FILENAME_COLUMN") );
|
75
|
+
VALUE am_content_c = rb_const_get( cARB, rb_intern("DEFAULT_CONTENTS_COLUMN") );
|
76
|
+
|
77
|
+
char* dbfile = NULL;
|
78
|
+
char* tbl_name = NULL;
|
79
|
+
char* pk_col = NULL;
|
80
|
+
char* fname_col = NULL;
|
81
|
+
char* content_col = NULL;
|
82
|
+
|
83
|
+
char* sql = NULL;
|
84
|
+
const char* sql_tail = NULL;
|
85
|
+
int sql_bytes = 0;
|
86
|
+
|
87
|
+
const unsigned char* result_text = NULL;
|
88
|
+
int result_length = 0;
|
89
|
+
|
90
|
+
VALUE require_name = Qnil; /* ruby string of the file name for use in eval */
|
91
|
+
VALUE eval_this_code = Qnil; /* ruby string of the code to eval from the db */
|
92
|
+
VALUE toplevel_binding = rb_const_get( rb_cObject, rb_intern("TOPLEVEL_BINDING") ) ;
|
93
|
+
VALUE sqlite_errmsg = Qnil;
|
94
|
+
VALUE tmp = Qnil;
|
95
|
+
|
96
|
+
ID eval_id = rb_intern("eval");
|
97
|
+
|
98
|
+
|
99
|
+
if ( Qnil == args ) {
|
100
|
+
args = rb_hash_new();
|
101
|
+
} else {
|
102
|
+
args = rb_ary_shift( args );
|
103
|
+
}
|
104
|
+
|
105
|
+
Check_Type( args, T_HASH );
|
106
|
+
|
107
|
+
/* get the arguments */
|
108
|
+
dbfile = ( Qnil == (tmp = rb_hash_aref( args, rb_str_new2( "dbfile" ) ) ) ) ? StringValuePtr( am_db_c ) : StringValuePtr( tmp );
|
109
|
+
tbl_name = ( Qnil == (tmp = rb_hash_aref( args, rb_str_new2( "table_name" ) ) ) ) ? StringValuePtr( am_tbl_c ) : StringValuePtr( tmp );
|
110
|
+
pk_col = ( Qnil == (tmp = rb_hash_aref( args, rb_str_new2( "rowid_column" ) ) ) ) ? StringValuePtr( am_pk_c ) : StringValuePtr( tmp );
|
111
|
+
fname_col = ( Qnil == (tmp = rb_hash_aref( args, rb_str_new2( "filename_column" ) ) ) ) ? StringValuePtr( am_fname_c ) : StringValuePtr( tmp );
|
112
|
+
content_col = ( Qnil == (tmp = rb_hash_aref( args, rb_str_new2( "contents_column" ) ) ) ) ? StringValuePtr( am_content_c ) : StringValuePtr( tmp );
|
113
|
+
|
114
|
+
|
115
|
+
/* open the database */
|
116
|
+
rc = sqlite3_open_v2( dbfile , &db, SQLITE_OPEN_READONLY, NULL);
|
117
|
+
if ( SQLITE_OK != rc ) {
|
118
|
+
asprintf(&raise_msg,"Failure to open database %s for bootload: [SQLITE_ERROR %d] : %s", dbfile, rc, sqlite3_errmsg( db ) );
|
119
|
+
am_bootstrap_cleanup_and_raise( raise_msg, db, stmt );
|
120
|
+
}
|
121
|
+
|
122
|
+
/* prepare the db query */
|
123
|
+
sql_bytes = asprintf( &sql, "SELECT %s, %s FROM %s ORDER BY %s", fname_col, content_col, tbl_name, pk_col );
|
124
|
+
rc = sqlite3_prepare_v2( db, sql, sql_bytes, &stmt, &sql_tail ) ;
|
125
|
+
free( sql );
|
126
|
+
if ( SQLITE_OK != rc) {
|
127
|
+
asprintf( &raise_msg,
|
128
|
+
"Failure to prepare bootload select statement table = '%s', rowid col = '%s', filename col ='%s', contents col = '%s' : [SQLITE_ERROR %d] %s\n",
|
129
|
+
tbl_name, pk_col, fname_col, content_col, rc, sqlite3_errmsg( db ));
|
130
|
+
am_bootstrap_cleanup_and_raise( raise_msg, db, stmt );
|
131
|
+
}
|
132
|
+
|
133
|
+
/* loop over the resulting rows, eval'ing and loading $" */
|
134
|
+
last_row_good = -1;
|
135
|
+
while ( SQLITE_ROW == ( rc = sqlite3_step( stmt ) ) ) {
|
136
|
+
/* file name */
|
137
|
+
result_text = sqlite3_column_text( stmt, 0 );
|
138
|
+
result_length = sqlite3_column_bytes( stmt, 0 );
|
139
|
+
require_name = rb_str_new( (const char*)result_text, result_length );
|
140
|
+
|
141
|
+
/* ruby code */
|
142
|
+
result_text = sqlite3_column_text( stmt, 1 );
|
143
|
+
result_length = sqlite3_column_bytes( stmt, 1 );
|
144
|
+
eval_this_code = rb_str_new( (const char*)result_text, result_length );
|
145
|
+
|
146
|
+
/* Kernel.eval( code, TOPLEVEL_BINDING, filename, 1 ) */
|
147
|
+
rb_funcall(rb_mKernel, eval_id, 4, eval_this_code, toplevel_binding, require_name, INT2FIX(1) );
|
148
|
+
|
149
|
+
/* TODO: for ruby 1.9 -- put in ? sqlite3://path/to/database?tablename=tbl_name#require_name */
|
150
|
+
/* update $LOADED_FEATURES */
|
151
|
+
rb_ary_push( rb_gv_get( "$LOADED_FEATURES" ), require_name );
|
152
|
+
}
|
153
|
+
|
154
|
+
/* if there was some sqlite error in the processing of the rows */
|
155
|
+
if ( SQLITE_DONE != rc ) {
|
156
|
+
asprintf( &raise_msg, "Failure in bootloading, last successfully loaded rowid was %d : [SQLITE_ERROR %d] %s\n",
|
157
|
+
last_row_good, rc, sqlite3_errmsg( db ) );
|
158
|
+
am_bootstrap_cleanup_and_raise( raise_msg, db, stmt );
|
159
|
+
}
|
160
|
+
|
161
|
+
/* finalize the statement */
|
162
|
+
rc = sqlite3_finalize( stmt );
|
163
|
+
if ( SQLITE_OK != rc ) {
|
164
|
+
asprintf( &raise_msg, "Failure to finalize bootload statement : [SQLITE_ERROR %d]\n", rc, sqlite3_errmsg( db ) );
|
165
|
+
am_bootstrap_cleanup_and_raise( raise_msg, db, stmt );
|
166
|
+
}
|
167
|
+
|
168
|
+
stmt = NULL;
|
169
|
+
|
170
|
+
/* close the database */
|
171
|
+
rc = sqlite3_close( db );
|
172
|
+
if ( SQLITE_OK != rc ) {
|
173
|
+
asprintf( &raise_msg, "Failure to close database : [SQLITE_ERROR %d] : %s\n", rc, sqlite3_errmsg( db )),
|
174
|
+
am_bootstrap_cleanup_and_raise( raise_msg, db,stmt );
|
175
|
+
}
|
176
|
+
|
177
|
+
return Qnil;
|
178
|
+
}
|
179
|
+
|
180
|
+
/**
|
181
|
+
* Bootstrapping module to help _require_ when Amalgalite::Requires is not
|
182
|
+
* availble in files.
|
183
|
+
*/
|
184
|
+
void Init_amalgalite3_requires_bootstrap()
|
185
|
+
{
|
186
|
+
|
187
|
+
mA = rb_define_module("Amalgalite");
|
188
|
+
cAR = rb_define_class_under(mA, "Requires", rb_cObject);
|
189
|
+
cARB = rb_define_class_under(cAR, "Bootstrap", rb_cObject);
|
190
|
+
|
191
|
+
eARB_Error = rb_define_class_under(cARB, "Error", rb_eStandardError);
|
192
|
+
|
193
|
+
rb_define_module_function(cARB, "lift", am_bootstrap_lift, -2);
|
194
|
+
|
195
|
+
/* constants for default db, table, column, rowid, contents */
|
196
|
+
rb_define_const(cARB, "DEFAULT_DB", rb_str_new2( "lib.db" ));
|
197
|
+
rb_define_const(cARB, "DEFAULT_TABLE", rb_str_new2( "bootstrap" ));
|
198
|
+
rb_define_const(cARB, "DEFAULT_ROWID_COLUMN", rb_str_new2( "id" ));
|
199
|
+
rb_define_const(cARB, "DEFAULT_FILENAME_COLUMN", rb_str_new2( "filename" ));
|
200
|
+
rb_define_const(cARB, "DEFAULT_CONTENTS_COLUMN", rb_str_new2( "contents" ));
|
201
|
+
|
202
|
+
return;
|
203
|
+
}
|
204
|
+
|