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.
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
+