amalgalite 0.2.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/HISTORY +30 -5
  2. data/bin/amalgalite-pack-into-db +155 -0
  3. data/examples/a.rb +9 -0
  4. data/examples/blob.rb +105 -0
  5. data/examples/bootstrap.rb +36 -0
  6. data/examples/filestore.db +0 -0
  7. data/examples/gem-db.rb +94 -0
  8. data/examples/requires.rb +54 -0
  9. data/examples/schema-info.rb +34 -0
  10. data/ext/amalgalite3.c +40 -31
  11. data/ext/amalgalite3_blob.c +7 -12
  12. data/ext/amalgalite3_constants.c +41 -4
  13. data/ext/amalgalite3_database.c +55 -5
  14. data/ext/amalgalite3_requires_bootstrap.c +204 -0
  15. data/ext/amalgalite3_statement.c +3 -4
  16. data/ext/extconf.rb +2 -0
  17. data/ext/gen_constants.rb +15 -4
  18. data/ext/sqlite3.c +68652 -59046
  19. data/ext/sqlite3.h +2613 -1939
  20. data/ext/sqlite3ext.h +13 -3
  21. data/gemspec.rb +0 -1
  22. data/lib/amalgalite.rb +22 -18
  23. data/lib/amalgalite/core_ext/kernel/require.rb +2 -2
  24. data/lib/amalgalite/database.rb +15 -6
  25. data/lib/amalgalite/index.rb +19 -3
  26. data/lib/amalgalite/requires.rb +37 -0
  27. data/lib/amalgalite/schema.rb +26 -5
  28. data/lib/amalgalite/sqlite3.rb +2 -0
  29. data/lib/amalgalite/sqlite3/constants.rb +51 -14
  30. data/lib/amalgalite/sqlite3/database/status.rb +69 -0
  31. data/lib/amalgalite/sqlite3/status.rb +61 -0
  32. data/lib/amalgalite/statement.rb +1 -1
  33. data/lib/amalgalite/table.rb +5 -5
  34. data/lib/amalgalite/type_map.rb +3 -0
  35. data/lib/amalgalite/version.rb +2 -2
  36. data/spec/blob_spec.rb +1 -1
  37. data/spec/boolean_spec.rb +0 -3
  38. data/spec/database_spec.rb +11 -3
  39. data/spec/schema_spec.rb +14 -0
  40. data/spec/sqlite3/constants_spec.rb +44 -4
  41. data/spec/sqlite3/database_status_spec.rb +36 -0
  42. data/spec/sqlite3/status_spec.rb +18 -0
  43. data/spec/sqlite3/version_spec.rb +3 -3
  44. data/spec/sqlite3_spec.rb +0 -12
  45. data/tasks/announce.rake +2 -1
  46. data/tasks/config.rb +2 -1
  47. data/tasks/distribution.rake +7 -0
  48. data/tasks/rubyforge.rake +14 -6
  49. 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.memory_used -> Numeric
73
+ * Amalgalite::SQLite3::Stat.update!( reset = false ) -> nil
73
74
  *
74
- * Return the number of bytes of memory outstanding in the SQLite extension
75
- */
76
- VALUE am_sqlite3_memory_used(VALUE self)
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 am_sqlite3_memory_highwater(VALUE self)
80
+ VALUE am_sqlite3_stat_update_bang( int argc, VALUE *argv, VALUE self )
90
81
  {
91
- return SQLINT64_2NUM(sqlite3_memory_highwater(0));
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
- * call-seq:
96
- * Amalgalite::SQLite3.memory_highwater_mark_reset!
97
- *
98
- * Reset the memory highwater mark. The highwater mark becomes the current
99
- * value of memory_used.
100
- *
101
- */
102
- VALUE am_sqlite3_memory_highwater_reset(VALUE self)
103
- {
104
- return SQLINT64_2NUM(sqlite3_memory_highwater(1));
95
+ rc = sqlite3_status( status_op, &current, &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
 
@@ -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 = %d. Invalid flag '%s'. Must be either 'w' or 'r'\n",
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 ( rc != SQLITE_OK ) {
54
- rb_raise( eAS_Error, "Error opening Blob in db = %s, table = %s, column = %s, rowid = %ld : [SQLITE_ERROR %d] %s\n",
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 = (VALUE)NULL;
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
 
@@ -3,10 +3,7 @@
3
3
  #include "amalgalite3.h";
4
4
  void Init_amalgalite3_constants( )
5
5
  {
6
- /** :stopdoc:
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
  }
@@ -6,7 +6,8 @@
6
6
  * vim: shiftwidth=4
7
7
  */
8
8
 
9
- VALUE cAS_Database; /* class Amalgliate::SQLite3::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, &current, &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
+