amalgalite 0.1.0 → 0.2.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 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