amalgalite 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY CHANGED
@@ -1,4 +1,21 @@
1
1
  = Changelog
2
- == Version 0.1.0 - 2008-06-20
2
+ == Version 0.2.0 - 2008-07-04
3
+
4
+ * Major Enhancements
5
+ * blob support, both incremental access and normal access
6
+
7
+ * Minor Enhancements
8
+ * added examples/gem_db.rb script demonstrating taps and prepared statements
9
+ * added examples/schema-info.rb script demonstrating meta information
10
+ * added examples/blob.rb demonstrating incremental blob IO
11
+ * added acces to the SQLite3 errcode and errmsg api
12
+
13
+ * Bugfixes
14
+ * added taps.rb for requiring
15
+ * fixed prepared statement reset
16
+ * caught an error in executing prepared statements earlier in the process so
17
+ the correct error is reported
18
+
19
+ == Version 0.1.0 - 2008-06-21
3
20
 
4
21
  * Initial public release
data/README CHANGED
@@ -1,8 +1,9 @@
1
1
  == Amalgalite
2
2
 
3
- * Homepage[http://www.copiousfreetime.org/projects/amalgalite]
3
+ * Homepage[http://copiousfreetime.rubyforge.org/amalgalite]
4
4
  * {Rubyforge Project}[http://rubyforge.org/projects/copiousfreetime/]
5
5
  * email jeremy at copiousfreetime dot org
6
+ * git clone url git://github.com/copiousfreetime/amalgalite.git
6
7
 
7
8
  == INSTALL
8
9
 
@@ -10,10 +11,17 @@
10
11
 
11
12
  == DESCRIPTION
12
13
 
13
- Amalgalite embeds the SQLite database engine in a ruby extension.
14
+ Amalgalite embeds the SQLite database engine in a ruby extension. There is no
15
+ need to install SQLite separately.
14
16
 
15
- Scroll through Amalgalite::Database for a quick example, and a general overview
16
- of the API.
17
+ Look in the examples/ directory to see
18
+
19
+ * general usage
20
+ * blob io
21
+ * schema information
22
+
23
+ Also Scroll through Amalgalite::Database for a quick example, and a general
24
+ overview of the API.
17
25
 
18
26
  == CREDITS
19
27
 
data/ext/amalgalite3.c CHANGED
@@ -185,6 +185,7 @@ void Init_amalgalite3()
185
185
  Init_amalgalite3_constants( );
186
186
  Init_amalgalite3_database( );
187
187
  Init_amalgalite3_statement( );
188
+ Init_amalgalite3_blob( );
188
189
 
189
190
  }
190
191
 
data/ext/amalgalite3.h CHANGED
@@ -24,6 +24,14 @@ typedef struct am_sqlite3_stmt {
24
24
  VALUE remaining_sql;
25
25
  } am_sqlite3_stmt;
26
26
 
27
+ /* wrapper struct around the sqlite3_blob opaque ponter */
28
+ typedef struct am_sqlite3_blob {
29
+ sqlite3_blob *blob;
30
+ sqlite3 *db;
31
+ int length;
32
+ int current_offset;
33
+ } am_sqlite3_blob;
34
+
27
35
 
28
36
  /** module and classes **/
29
37
  extern VALUE mA; /* module Amalgalite */
@@ -55,7 +63,7 @@ extern VALUE am_sqlite3_database_register_profile_tap(VALUE self, VALUE tap);
55
63
  /*----------------------------------------------------------------------
56
64
  * Prototype for Amalgalite::SQLite3::Statement
57
65
  *---------------------------------------------------------------------*/
58
- extern VALUE cAS_Statement; /* class Amalgliate::SQLite3::Statement */
66
+ extern VALUE cAS_Statement; /* class Amalgalite::SQLite3::Statement */
59
67
 
60
68
  extern VALUE am_sqlite3_statement_alloc(VALUE klass);
61
69
  extern void am_sqlite3_statement_free(am_sqlite3_stmt* );
@@ -66,6 +74,7 @@ extern VALUE am_sqlite3_statement_column_count(VALUE self);
66
74
  extern VALUE am_sqlite3_statement_column_name(VALUE self, VALUE index);
67
75
  extern VALUE am_sqlite3_statement_column_decltype(VALUE self, VALUE index);
68
76
  extern VALUE am_sqlite3_statement_column_type(VALUE self, VALUE index);
77
+ extern VALUE am_sqlite3_statement_column_blob(VALUE self, VALUE index);
69
78
  extern VALUE am_sqlite3_statement_column_text(VALUE self, VALUE index);
70
79
  extern VALUE am_sqlite3_statement_column_int(VALUE self, VALUE index);
71
80
  extern VALUE am_sqlite3_statement_column_int64(VALUE self, VALUE index);
@@ -81,11 +90,26 @@ extern VALUE am_sqlite3_statement_bind_parameter_count(VALUE self);
81
90
  extern VALUE am_sqlite3_statement_bind_parameter_index(VALUE self, VALUE parameter_name);
82
91
  extern VALUE am_sqlite3_statement_remaining_sql(VALUE self);
83
92
  extern VALUE am_sqlite3_statement_bind_text(VALUE self, VALUE position, VALUE value);
93
+ extern VALUE am_sqlite3_statement_bind_blob(VALUE self, VALUE position, VALUE value);
94
+ extern VALUE am_sqlite3_statement_bind_zeroblob(VALUE self, VALUE position, VALUE value);
84
95
  extern VALUE am_sqlite3_statement_bind_int(VALUE self, VALUE position, VALUE value);
85
96
  extern VALUE am_sqlite3_statement_bind_int64(VALUE self, VALUE position, VALUE value);
86
97
  extern VALUE am_sqlite3_statement_bind_double(VALUE self, VALUE position, VALUE value);
87
98
  extern VALUE am_sqlite3_statement_bind_null(VALUE self, VALUE position);
88
99
 
100
+ /*----------------------------------------------------------------------
101
+ * Prototype for Amalgalite::SQLite3::Blob
102
+ *---------------------------------------------------------------------*/
103
+ extern VALUE cAS_Blob; /* class Amalgalite::SQLite3::Blob */
104
+
105
+ extern VALUE am_sqlite3_blob_alloc(VALUE klass);
106
+ extern VALUE am_sqlite3_blob_initialize( VALUE self, VALUE db, VALUE db_name, VALUE table_name, VALUE column_name, VALUE rowid, VALUE flag) ;
107
+ extern void am_sqlite3_blob_free(am_sqlite3_blob* );
108
+ extern VALUE am_sqlite3_blob_read(VALUE self, VALUE length);
109
+ extern VALUE am_sqlite3_blob_write(VALUE self, VALUE buffer);
110
+ extern VALUE am_sqlite3_blob_close(VALUE self);
111
+ extern VALUE am_sqlite3_blob_length(VALUE self);
112
+
89
113
  /***********************************************************************
90
114
  * Type conversion macros between sqlite data types and ruby types
91
115
  **********************************************************************/
@@ -0,0 +1,238 @@
1
+ #include "amalgalite3.h"
2
+ /**
3
+ * Copyright (c) 2008 Jeremy Hinegardner
4
+ * All rights reserved. See LICENSE and/or COPYING for details.
5
+ *
6
+ * vim: shiftwidth=4
7
+ */
8
+
9
+ VALUE cAS_Blob; /* class Amalgliate::SQLite3::Blob */
10
+
11
+ /**
12
+ * call-seq:
13
+ * Blob.new( database, table_name, column_name, row_id, flag ) -> Blob
14
+ *
15
+ * Create a new Blob object and associate it with the approriate, database,
16
+ * table, column and row. +flag+ indicates if the Blob is to be opened for
17
+ * writing "w" or reading "r".
18
+ *
19
+ */
20
+ VALUE am_sqlite3_blob_initialize( VALUE self, VALUE db, VALUE db_name, VALUE table_name, VALUE column_name, VALUE rowid, VALUE flag)
21
+ {
22
+ am_sqlite3_blob *am_blob;
23
+ int rc;
24
+ am_sqlite3 *am_db;
25
+ char *zDb = StringValuePtr( db_name );
26
+ char *zTable = StringValuePtr( table_name );
27
+ char *zColumn = StringValuePtr( column_name );
28
+ sqlite3_int64 iRow = NUM2SQLINT64( rowid ) ;
29
+ VALUE flag_str = StringValue( flag );
30
+ int flags = 0;
31
+
32
+ /* extract the blob struct */
33
+ Data_Get_Struct(self, am_sqlite3_blob, am_blob);
34
+
35
+ /* extract the sqlite3 db struct */
36
+ Data_Get_Struct(db, am_sqlite3, am_db);
37
+
38
+ /* make sure that the flags are valid, only 'r' or 'w' are allowed */
39
+ if ( ( RSTRING( flag_str )->len != 1) ||
40
+ ( ( 'r' != RSTRING( flag_str )->ptr[0] ) &&
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);
44
+ }
45
+
46
+ /* switch to write mode */
47
+ if ( 'w' == RSTRING( flag_str )->ptr[0] ) {
48
+ flags = 1;
49
+ }
50
+
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 ));
56
+ }
57
+ am_blob->length = sqlite3_blob_bytes( am_blob->blob );
58
+ am_blob->db = am_db->db;
59
+
60
+ /* if a block is given then yield self and close the blob when done */
61
+ if ( rb_block_given_p() ) {
62
+ rb_yield( self );
63
+ am_sqlite3_blob_close( self );
64
+ return Qnil;
65
+ } else {
66
+ return self;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * call-seq:
72
+ * blob.close -> nil
73
+ *
74
+ * Closes the blob.
75
+ */
76
+ VALUE am_sqlite3_blob_close( VALUE self )
77
+ {
78
+ am_sqlite3_blob *am_blob;
79
+ int rc;
80
+
81
+ Data_Get_Struct(self, am_sqlite3_blob, am_blob);
82
+ rc = sqlite3_blob_close( am_blob->blob );
83
+ if ( SQLITE_OK != rc ) {
84
+ rb_raise(eAS_Error, "Error closing blob: [SQLITE_ERROR %d] %s\n",
85
+ rc, sqlite3_errmsg( am_blob->db ));
86
+ }
87
+
88
+
89
+ return Qnil;
90
+ }
91
+
92
+
93
+ /**
94
+ * call-seq:
95
+ * blob.length -> length in bytes of the blob
96
+ *
97
+ * Returns the number of bytes in the blob.
98
+ */
99
+ VALUE am_sqlite3_blob_length( VALUE self )
100
+ {
101
+ am_sqlite3_blob *am_blob;
102
+ int n;
103
+
104
+ Data_Get_Struct(self, am_sqlite3_blob, am_blob);
105
+
106
+ return INT2FIX( am_blob->length );
107
+ }
108
+
109
+ /**
110
+ * call-seq:
111
+ * blob.read( int ) -> String containting int number of bytes or nil if eof.
112
+ *
113
+ * returns int number of bytes as a String from the database
114
+ */
115
+ VALUE am_sqlite3_blob_read( VALUE self, VALUE length )
116
+ {
117
+ am_sqlite3_blob *am_blob;
118
+ int rc;
119
+ int n = NUM2INT( length );
120
+ void *buf = NULL;
121
+ VALUE result;
122
+
123
+ Data_Get_Struct(self, am_sqlite3_blob, am_blob);
124
+
125
+ /* we have to be exact on the number of bytes to read. n + current_offset
126
+ * cannot be larger than the blob's length
127
+ */
128
+ if ( (n + am_blob->current_offset > am_blob->length)) {
129
+ n = am_blob->length - am_blob->current_offset;
130
+ }
131
+
132
+ if ( am_blob->current_offset == am_blob->length ) {
133
+ return Qnil;
134
+ }
135
+
136
+ buf = (void *)malloc( n );
137
+ rc = sqlite3_blob_read( am_blob->blob, buf, n, am_blob->current_offset);
138
+
139
+ if ( rc != SQLITE_OK ) {
140
+ rb_raise(eAS_Error, "Error reading %d bytes blob at offset %d: [SQLITE_ERROR %d] %s\n",
141
+ n, am_blob->current_offset, rc, sqlite3_errmsg( am_blob->db ));
142
+ }
143
+
144
+ am_blob->current_offset += n;
145
+
146
+ result = rb_str_new( (char*)buf, n );
147
+ free( buf );
148
+ return result;
149
+
150
+ }
151
+
152
+ /**
153
+ * call-seq:
154
+ * blob.write( buf ) -> int
155
+ *
156
+ * writes the contents of the string buffer to the blob and returns the number
157
+ * of bytes written.
158
+ *
159
+ */
160
+ VALUE am_sqlite3_blob_write( VALUE self, VALUE buf )
161
+ {
162
+ am_sqlite3_blob *am_blob;
163
+ int rc;
164
+ VALUE str = StringValue( buf );
165
+ int n = RSTRING( str )->len;
166
+ char *chk_buf = NULL;
167
+
168
+ Data_Get_Struct(self, am_sqlite3_blob, am_blob);
169
+
170
+ rc = sqlite3_blob_write( am_blob->blob, RSTRING(str)->ptr, n, am_blob->current_offset);
171
+
172
+ if ( rc != SQLITE_OK ) {
173
+ rb_raise(eAS_Error, "Error writing %d bytes blob at offset %d: [SQLITE_ERROR %d] %s\n",
174
+ n, am_blob->current_offset, rc, sqlite3_errmsg( am_blob->db ));
175
+ }
176
+
177
+ chk_buf = (char *) malloc( n + 1);
178
+ chk_buf[n] = '\0';
179
+ sqlite3_blob_read( am_blob->blob, chk_buf, n, 0);
180
+
181
+ am_blob->current_offset += n;
182
+
183
+ return INT2FIX( n );
184
+
185
+ }
186
+
187
+
188
+ /***********************************************************************
189
+ * Ruby life cycle methods
190
+ ***********************************************************************/
191
+
192
+ /*
193
+ * garbage collector free method for the am_sqlite3_blob structure
194
+ */
195
+ void am_sqlite3_blob_free(am_sqlite3_blob* wrapper)
196
+ {
197
+ free(wrapper);
198
+ return;
199
+ }
200
+
201
+ /*
202
+ * allocate the am_blob structure
203
+ */
204
+ VALUE am_sqlite3_blob_alloc(VALUE klass)
205
+ {
206
+ am_sqlite3_blob *wrapper = ALLOC(am_sqlite3_blob);
207
+ VALUE obj = (VALUE)NULL;
208
+
209
+ wrapper->current_offset = 0;
210
+ wrapper->db = NULL;
211
+ obj = Data_Wrap_Struct(klass, NULL, am_sqlite3_blob_free, wrapper);
212
+ return obj;
213
+ }
214
+
215
+
216
+ void Init_amalgalite3_blob( )
217
+ {
218
+
219
+ /** :stopdoc:
220
+ * These calls are here just to allow for rdoc generation
221
+ * :startdoc:
222
+ */
223
+ VALUE ma = rb_define_module("Amalgalite");
224
+ VALUE mas = rb_define_module_under(ma, "SQLite3");
225
+
226
+ /*
227
+ * Encapsulate the SQLite3 Statement handle in a class
228
+ */
229
+ cAS_Blob = rb_define_class_under( mas, "Blob", rb_cObject );
230
+ rb_define_alloc_func(cAS_Blob, am_sqlite3_blob_alloc);
231
+ rb_define_method(cAS_Blob, "initialize", am_sqlite3_blob_initialize, 6);
232
+ rb_define_method(cAS_Blob, "close", am_sqlite3_blob_close, 0);
233
+ rb_define_method(cAS_Blob, "read", am_sqlite3_blob_read, 1);
234
+ rb_define_method(cAS_Blob, "write", am_sqlite3_blob_write, 1);
235
+ rb_define_method(cAS_Blob, "length", am_sqlite3_blob_length, 0);
236
+ }
237
+
238
+
@@ -163,6 +163,42 @@ VALUE am_sqlite3_database_row_changes(VALUE self)
163
163
  return INT2FIX(rc);
164
164
  }
165
165
 
166
+ /**
167
+ * call-seq:
168
+ * database.last_error_code -> Integer
169
+ *
170
+ * return the last error code that happened in the database
171
+ *
172
+ */
173
+ VALUE am_sqlite3_database_last_error_code(VALUE self)
174
+ {
175
+ am_sqlite3 *am_db;
176
+ int code;
177
+
178
+ Data_Get_Struct(self, am_sqlite3, am_db);
179
+ code = sqlite3_errcode( am_db->db );
180
+
181
+ return INT2FIX( code );
182
+ }
183
+
184
+ /**
185
+ * call-seq:
186
+ * database.last_error_message -> String
187
+ *
188
+ * return the last error message that happened in the database
189
+ *
190
+ */
191
+ VALUE am_sqlite3_database_last_error_message(VALUE self)
192
+ {
193
+ am_sqlite3 *am_db;
194
+ const char *message;
195
+
196
+ Data_Get_Struct(self, am_sqlite3, am_db);
197
+ message = sqlite3_errmsg( am_db->db );
198
+
199
+ return rb_str_new2( message );
200
+ }
201
+
166
202
  /**
167
203
  * call-seq:
168
204
  * database.total_changes -> Integer
@@ -211,6 +247,7 @@ VALUE am_sqlite3_database_prepare(VALUE self, VALUE rSQL)
211
247
 
212
248
  if ( tail != NULL ) {
213
249
  am_stmt->remaining_sql = rb_str_new2( tail );
250
+ rb_gc_register_address( &(am_stmt->remaining_sql) );
214
251
  } else {
215
252
  am_stmt->remaining_sql = Qnil;
216
253
  }
@@ -453,6 +490,8 @@ void Init_amalgalite3_database( )
453
490
  rb_define_method(cAS_Database, "table_column_metadata", am_sqlite3_database_table_column_metadata, 3); /* in amalgalite3_database.c */
454
491
  rb_define_method(cAS_Database, "row_changes", am_sqlite3_database_row_changes, 0); /* in amalgalite3_database.c */
455
492
  rb_define_method(cAS_Database, "total_changes", am_sqlite3_database_total_changes, 0); /* in amalgalite3_database.c */
493
+ rb_define_method(cAS_Database, "last_error_code", am_sqlite3_database_last_error_code, 0); /* in amalgalite3_database.c */
494
+ rb_define_method(cAS_Database, "last_error_message", am_sqlite3_database_last_error_message, 0); /* in amalgalite3_database.c */
456
495
 
457
496
  }
458
497
 
@@ -10,7 +10,7 @@ VALUE cAS_Statement; /* class Amalgliate::SQLite3::Statement */
10
10
 
11
11
  /**
12
12
  * call-seq:
13
- * stmt.bind_null( position ) -> nil
13
+ * stmt.bind_null( position ) -> int
14
14
  *
15
15
  * bind a null value to the variable at postion.
16
16
  *
@@ -31,6 +31,58 @@ VALUE am_sqlite3_statement_bind_null(VALUE self, VALUE position )
31
31
 
32
32
  return INT2FIX(rc);
33
33
  }
34
+
35
+ /**
36
+ * call-seq:
37
+ * stmt.bind_zeroblob( position, length ) -> int
38
+ *
39
+ * bind a blob with +length+ filled with zeros to the position. This is a Blob
40
+ * that will later filled in with incremental IO routines.
41
+ */
42
+ VALUE am_sqlite3_statement_bind_zeroblob( VALUE self, VALUE position, VALUE length)
43
+ {
44
+ am_sqlite3_stmt *am_stmt;
45
+ int pos = FIX2INT( position );
46
+ int n = FIX2INT( length );
47
+ int rc;
48
+
49
+ Data_Get_Struct(self, am_sqlite3_stmt, am_stmt);
50
+ rc = sqlite3_bind_zeroblob( am_stmt->stmt, pos, n );
51
+ if ( SQLITE_OK != rc ) {
52
+ rb_raise(eAS_Error, "Error binding zeroblob of length %d at position %d in statement: [SQLITE_ERROR %d] : %s\n",
53
+ n, pos,
54
+ rc, sqlite3_errmsg( sqlite3_db_handle( am_stmt->stmt) ));
55
+ }
56
+
57
+ return INT2FIX(rc);
58
+ }
59
+
60
+
61
+ /**
62
+ * call-seq:
63
+ * stmt.bind_blob( position, blob ) -> int
64
+ *
65
+ * bind a blob to the variable at position. This is a blob that is fully held
66
+ * in memory
67
+ */
68
+ VALUE am_sqlite3_statement_bind_blob( VALUE self, VALUE position, VALUE blob )
69
+ {
70
+ am_sqlite3_stmt *am_stmt;
71
+ int pos = FIX2INT( position );
72
+ VALUE str = StringValue( blob );
73
+ int rc;
74
+
75
+ Data_Get_Struct(self, am_sqlite3_stmt, am_stmt);
76
+ rc = sqlite3_bind_blob( am_stmt->stmt, pos, RSTRING( str )->ptr, RSTRING( str )->len, SQLITE_TRANSIENT);
77
+ if ( SQLITE_OK != rc ) {
78
+ rb_raise(eAS_Error, "Error binding blob at position %d in statement: [SQLITE_ERROR %d] : %s\n",
79
+ pos,
80
+ rc, sqlite3_errmsg( sqlite3_db_handle( am_stmt->stmt) ));
81
+ }
82
+
83
+ return INT2FIX(rc);
84
+ }
85
+
34
86
  /**
35
87
  * call-seq:
36
88
  * stmt.bind_double( position, value ) -> nil
@@ -121,7 +173,7 @@ VALUE am_sqlite3_statement_bind_text(VALUE self, VALUE position, VALUE value)
121
173
  int rc;
122
174
 
123
175
  Data_Get_Struct(self, am_sqlite3_stmt, am_stmt);
124
- rc = sqlite3_bind_text( am_stmt->stmt, pos, RSTRING(str)->ptr, RSTRING(str)->len, NULL );
176
+ rc = sqlite3_bind_text( am_stmt->stmt, pos, RSTRING(str)->ptr, RSTRING(str)->len, SQLITE_TRANSIENT);
125
177
  if ( SQLITE_OK != rc ) {
126
178
  rb_raise(eAS_Error, "Error binding [%s] to text at position %d in statement: [SQLITE_ERROR %d] : %s\n",
127
179
  RSTRING(str)->ptr, pos,
@@ -324,6 +376,28 @@ VALUE am_sqlite3_statement_column_text(VALUE self, VALUE v_idx)
324
376
  return rb_str_new2( (const char*)sqlite3_column_text( am_stmt->stmt, idx ) );
325
377
  }
326
378
 
379
+ /**
380
+ * call-seq:
381
+ * stmt.column_blob( index ) -> String
382
+ *
383
+ * Return the data in ith column of the result as a String.
384
+ *
385
+ */
386
+ VALUE am_sqlite3_statement_column_blob(VALUE self, VALUE v_idx)
387
+ {
388
+ am_sqlite3_stmt *am_stmt;
389
+ int idx = FIX2INT( v_idx );
390
+ const char *data;
391
+ long length;
392
+
393
+ Data_Get_Struct(self, am_sqlite3_stmt, am_stmt);
394
+ data = sqlite3_column_blob( am_stmt->stmt, idx );
395
+ length = sqlite3_column_bytes( am_stmt->stmt, idx );
396
+ return rb_str_new( data, length );
397
+
398
+ }
399
+
400
+
327
401
  /**
328
402
  * call-seq:
329
403
  * stmt.column_double( index ) -> Float
@@ -470,6 +544,8 @@ VALUE am_sqlite3_statement_close( VALUE self )
470
544
  rb_raise(eAS_Error, "Failure to close statment : [SQLITE_ERROR %d] : %s\n",
471
545
  rc, sqlite3_errmsg( sqlite3_db_handle( am_stmt->stmt) ));
472
546
  }
547
+
548
+ return Qnil;
473
549
  }
474
550
 
475
551
  /***********************************************************************
@@ -483,6 +559,10 @@ VALUE am_sqlite3_statement_close( VALUE self )
483
559
  void am_sqlite3_statement_free(am_sqlite3_stmt* wrapper)
484
560
  {
485
561
 
562
+ if ( Qnil != wrapper->remaining_sql ) {
563
+ rb_gc_unregister_address( &(wrapper->remaining_sql) );
564
+ wrapper->remaining_sql = Qnil;
565
+ }
486
566
  free(wrapper);
487
567
  return;
488
568
  }
@@ -524,6 +604,7 @@ void Init_amalgalite3_statement( )
524
604
  rb_define_method(cAS_Statement, "column_declared_type", am_sqlite3_statement_column_decltype, 1);
525
605
  rb_define_method(cAS_Statement, "column_type", am_sqlite3_statement_column_type, 1);
526
606
  rb_define_method(cAS_Statement, "column_text", am_sqlite3_statement_column_text, 1);
607
+ rb_define_method(cAS_Statement, "column_blob", am_sqlite3_statement_column_blob, 1);
527
608
  rb_define_method(cAS_Statement, "column_int", am_sqlite3_statement_column_int, 1);
528
609
  rb_define_method(cAS_Statement, "column_int64", am_sqlite3_statement_column_int64, 1);
529
610
  rb_define_method(cAS_Statement, "column_double", am_sqlite3_statement_column_double, 1);
@@ -541,6 +622,8 @@ void Init_amalgalite3_statement( )
541
622
  rb_define_method(cAS_Statement, "bind_int64", am_sqlite3_statement_bind_int64, 2);
542
623
  rb_define_method(cAS_Statement, "bind_double", am_sqlite3_statement_bind_double, 2);
543
624
  rb_define_method(cAS_Statement, "bind_null", am_sqlite3_statement_bind_null, 1);
625
+ rb_define_method(cAS_Statement, "bind_blob", am_sqlite3_statement_bind_blob, 2);
626
+ rb_define_method(cAS_Statement, "bind_zeroblob", am_sqlite3_statement_bind_zeroblob, 2);
544
627
  }
545
628
 
546
629