amalgalite 0.2.4 → 0.4.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.
- 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
|
+
|