sqlite3 1.7.3 → 2.5.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +292 -0
  3. data/CONTRIBUTING.md +33 -7
  4. data/FAQ.md +43 -77
  5. data/INSTALLATION.md +14 -6
  6. data/LICENSE +18 -22
  7. data/README.md +97 -9
  8. data/dependencies.yml +10 -11
  9. data/ext/sqlite3/aggregator.c +142 -145
  10. data/ext/sqlite3/aggregator.h +2 -4
  11. data/ext/sqlite3/backup.c +74 -65
  12. data/ext/sqlite3/backup.h +2 -2
  13. data/ext/sqlite3/database.c +621 -493
  14. data/ext/sqlite3/database.h +13 -4
  15. data/ext/sqlite3/exception.c +116 -92
  16. data/ext/sqlite3/exception.h +5 -1
  17. data/ext/sqlite3/extconf.rb +33 -24
  18. data/ext/sqlite3/sqlite3.c +176 -115
  19. data/ext/sqlite3/sqlite3_ruby.h +2 -2
  20. data/ext/sqlite3/statement.c +553 -300
  21. data/ext/sqlite3/statement.h +4 -3
  22. data/ext/sqlite3/timespec.h +20 -0
  23. data/lib/sqlite3/constants.rb +195 -47
  24. data/lib/sqlite3/database.rb +223 -187
  25. data/lib/sqlite3/errors.rb +54 -1
  26. data/lib/sqlite3/fork_safety.rb +66 -0
  27. data/lib/sqlite3/pragmas.rb +140 -136
  28. data/lib/sqlite3/resultset.rb +14 -97
  29. data/lib/sqlite3/statement.rb +58 -13
  30. data/lib/sqlite3/value.rb +17 -20
  31. data/lib/sqlite3/version.rb +2 -21
  32. data/lib/sqlite3/version_info.rb +17 -0
  33. data/lib/sqlite3.rb +8 -4
  34. data/ports/archives/sqlite-autoconf-3470200.tar.gz +0 -0
  35. metadata +9 -37
  36. data/API_CHANGES.md +0 -49
  37. data/ChangeLog.cvs +0 -88
  38. data/Gemfile +0 -10
  39. data/LICENSE-DEPENDENCIES +0 -20
  40. data/lib/sqlite3/translator.rb +0 -117
  41. data/ports/archives/sqlite-autoconf-3450200.tar.gz +0 -0
  42. data/test/helper.rb +0 -27
  43. data/test/test_backup.rb +0 -33
  44. data/test/test_collation.rb +0 -82
  45. data/test/test_database.rb +0 -668
  46. data/test/test_database_flags.rb +0 -95
  47. data/test/test_database_readonly.rb +0 -36
  48. data/test/test_database_readwrite.rb +0 -41
  49. data/test/test_deprecated.rb +0 -49
  50. data/test/test_encoding.rb +0 -165
  51. data/test/test_integration.rb +0 -507
  52. data/test/test_integration_aggregate.rb +0 -336
  53. data/test/test_integration_open_close.rb +0 -30
  54. data/test/test_integration_pending.rb +0 -115
  55. data/test/test_integration_resultset.rb +0 -142
  56. data/test/test_integration_statement.rb +0 -194
  57. data/test/test_pragmas.rb +0 -22
  58. data/test/test_result_set.rb +0 -47
  59. data/test/test_sqlite3.rb +0 -30
  60. data/test/test_statement.rb +0 -290
  61. data/test/test_statement_execute.rb +0 -39
data/README.md CHANGED
@@ -9,21 +9,20 @@ Note that this module is only compatible with SQLite 3.6.16 or newer.
9
9
  * Source code: https://github.com/sparklemotion/sqlite3-ruby
10
10
  * Mailing list: http://groups.google.com/group/sqlite3-ruby
11
11
  * Download: http://rubygems.org/gems/sqlite3
12
- * Documentation: http://www.rubydoc.info/gems/sqlite3
12
+ * Documentation: https://sparklemotion.github.io/sqlite3-ruby/
13
13
 
14
- [![Unit tests](https://github.com/sparklemotion/sqlite3-ruby/actions/workflows/sqlite3-ruby.yml/badge.svg)](https://github.com/sparklemotion/sqlite3-ruby/actions/workflows/sqlite3-ruby.yml)
15
- [![Native packages](https://github.com/sparklemotion/sqlite3-ruby/actions/workflows/gem-install.yml/badge.svg)](https://github.com/sparklemotion/sqlite3-ruby/actions/workflows/gem-install.yml)
14
+ [![Test suite](https://github.com/sparklemotion/sqlite3-ruby/actions/workflows/ci.yml/badge.svg)](https://github.com/sparklemotion/sqlite3-ruby/actions/workflows/ci.yml)
16
15
 
17
16
 
18
17
  ## Quick start
19
18
 
20
- For help understanding the SQLite3 Ruby API, please read the [FAQ](./FAQ.md) and the [full API documentation](https://rubydoc.info/gems/sqlite3).
19
+ For help understanding the SQLite3 Ruby API, please read the [FAQ](./FAQ.md) and the [full API documentation](https://sparklemotion.github.io/sqlite3-ruby/).
21
20
 
22
21
  A few key classes whose APIs are often-used are:
23
22
 
24
- - SQLite3::Database ([rdoc](https://rubydoc.info/gems/sqlite3/SQLite3/Database))
25
- - SQLite3::Statement ([rdoc](https://rubydoc.info/gems/sqlite3/SQLite3/Statement))
26
- - SQLite3::ResultSet ([rdoc](https://rubydoc.info/gems/sqlite3/SQLite3/ResultSet))
23
+ - SQLite3::Database ([rdoc](https://sparklemotion.github.io/sqlite3-ruby/SQLite3/Database.html))
24
+ - SQLite3::Statement ([rdoc](https://sparklemotion.github.io/sqlite3-ruby/SQLite3/Statement.html))
25
+ - SQLite3::ResultSet ([rdoc](https://sparklemotion.github.io/sqlite3-ruby/SQLite3/ResultSet.html))
27
26
 
28
27
  If you have any questions that you feel should be addressed in the FAQ, please send them to [the mailing list](http://groups.google.com/group/sqlite3-ruby) or open a [discussion thread](https://github.com/sparklemotion/sqlite3-ruby/discussions/categories/q-a).
29
28
 
@@ -77,6 +76,95 @@ end
77
76
  # => ["Jane", "me@janedoe.com", "A", "http://blog.janedoe.com"]
78
77
  ```
79
78
 
79
+ ## Thread Safety
80
+
81
+ When `SQLite3.threadsafe?` returns `true`, then SQLite3 has been compiled to
82
+ support running in a multithreaded environment. However, this doesn't mean
83
+ that all classes in the SQLite3 gem can be considered "thread safe".
84
+
85
+ When `SQLite3.threadsafe?` returns `true`, it is safe to share only
86
+ `SQLite3::Database` instances among threads without providing your own locking
87
+ mechanism. For example, the following code is fine because only the database
88
+ instance is shared among threads:
89
+
90
+ ```ruby
91
+ require 'sqlite3'
92
+
93
+ db = SQLite3::Database.new ":memory:"
94
+
95
+ latch = Queue.new
96
+
97
+ ts = 10.times.map {
98
+ Thread.new {
99
+ latch.pop
100
+ db.execute "SELECT '#{Thread.current.inspect}'"
101
+ }
102
+ }
103
+ 10.times { latch << nil }
104
+
105
+ p ts.map(&:value)
106
+ ```
107
+
108
+ Other instances can be shared among threads, but they require that you provide
109
+ your own locking for thread safety. For example, `SQLite3::Statement` objects
110
+ (prepared statements) are mutable, so applications must take care to add
111
+ appropriate locks to avoid data race conditions when sharing these objects
112
+ among threads.
113
+
114
+ Lets rewrite the above example but use a prepared statement and safely share
115
+ the prepared statement among threads:
116
+
117
+ ```ruby
118
+ db = SQLite3::Database.new ":memory:"
119
+
120
+ # Prepare a statement
121
+ stmt = db.prepare "SELECT :inspect"
122
+ stmt_lock = Mutex.new
123
+
124
+ latch = Queue.new
125
+
126
+ ts = 10.times.map {
127
+ Thread.new {
128
+ latch.pop
129
+
130
+ # Add a lock when using the prepared statement.
131
+ # Binding values, and walking over results will mutate the statement, so
132
+ # in order to prevent other threads from "seeing" this thread's data, we
133
+ # must lock when using the statement object
134
+ stmt_lock.synchronize do
135
+ stmt.execute(Thread.current.inspect).to_a
136
+ end
137
+ }
138
+ }
139
+
140
+ 10.times { latch << nil }
141
+
142
+ p ts.map(&:value)
143
+
144
+ stmt.close
145
+ ```
146
+
147
+ It is generally recommended that if applications want to share a database among
148
+ threads, they _only_ share the database instance object. Other objects are
149
+ fine to share, but may require manual locking for thread safety.
150
+
151
+
152
+ ## Fork Safety
153
+
154
+ [Sqlite is not fork
155
+ safe](https://www.sqlite.org/howtocorrupt.html#_carrying_an_open_database_connection_across_a_fork_)
156
+ and instructs users to not carry an open writable database connection across a `fork()`. Using an inherited
157
+ connection in the child may corrupt your database, leak memory, or cause other undefined behavior.
158
+
159
+ To help protect users of this gem from accidental corruption due to this lack of fork safety, the gem will immediately close any open writable databases in the child after a fork. Discarding writable
160
+ connections in the child will incur a small one-time memory leak per connection, but that's
161
+ preferable to potentially corrupting your database.
162
+
163
+ Whenever possible, close writable connections in the parent before forking. If absolutely necessary (and you know what you're doing), you may suppress the fork safety warnings by calling `SQLite3::ForkSafety.suppress_warnings!`.
164
+
165
+ See [./adr/2024-09-fork-safety.md](./adr/2024-09-fork-safety.md) for more information and context.
166
+
167
+
80
168
  ## Support
81
169
 
82
170
  ### Installation or database extensions
@@ -104,7 +192,7 @@ See [`CONTRIBUTING.md`](./CONTRIBUTING.md).
104
192
 
105
193
  This library is licensed under `BSD-3-Clause`, see [`LICENSE`](./LICENSE).
106
194
 
107
-
108
195
  ### Dependencies
109
196
 
110
- The source code of `sqlite` is distributed in the "ruby platform" gem. This code is public domain, see [`LICENSE-DEPENDENCIES`](./LICENSE-DEPENDENCIES) for details.
197
+ The source code of `sqlite` is distributed in the "ruby platform" gem. This code is public domain,
198
+ see https://www.sqlite.org/copyright.html for details.
data/dependencies.yml CHANGED
@@ -1,14 +1,13 @@
1
- # TODO: stop using symbols here once we no longer support Ruby 2.7 and can rely on symbolize_names
2
- :sqlite3:
1
+ sqlite3:
3
2
  # checksum verified by first checking the published sha3(256) checksum against https://sqlite.org/download.html:
4
- # 1b02c58a711d15b50da8a1089e0f8807ebbdf3e674c714100eda9a03a69ac758
3
+ # 52cd4a2304b627abbabe1a438ba853d0f6edb8e2774fcb5773c7af11077afe94
5
4
  #
6
- # $ sha3sum -a 256 ports/archives/sqlite-autoconf-3450200.tar.gz
7
- # 1b02c58a711d15b50da8a1089e0f8807ebbdf3e674c714100eda9a03a69ac758 ports/archives/sqlite-autoconf-3450200.tar.gz
5
+ # $ sha3sum -a 256 ports/archives/sqlite-autoconf-3470200.tar.gz
6
+ # 52cd4a2304b627abbabe1a438ba853d0f6edb8e2774fcb5773c7af11077afe94 ports/archives/sqlite-autoconf-3470200.tar.gz
8
7
  #
9
- # $ sha256sum ports/archives/sqlite-autoconf-3450200.tar.gz
10
- # bc9067442eedf3dd39989b5c5cfbfff37ae66cc9c99274e0c3052dc4d4a8f6ae ports/archives/sqlite-autoconf-3450200.tar.gz
11
- :version: "3.45.2"
12
- :files:
13
- - :url: "https://sqlite.org/2024/sqlite-autoconf-3450200.tar.gz"
14
- :sha256: "bc9067442eedf3dd39989b5c5cfbfff37ae66cc9c99274e0c3052dc4d4a8f6ae"
8
+ # $ sha256sum ports/archives/sqlite-autoconf-3470200.tar.gz
9
+ # f1b2ee412c28d7472bc95ba996368d6f0cdcf00362affdadb27ed286c179540b ports/archives/sqlite-autoconf-3470200.tar.gz
10
+ version: "3.47.2"
11
+ files:
12
+ - url: "https://sqlite.org/2024/sqlite-autoconf-3470200.tar.gz"
13
+ sha256: "f1b2ee412c28d7472bc95ba996368d6f0cdcf00362affdadb27ed286c179540b"
@@ -31,20 +31,20 @@ typedef struct rb_sqlite3_protected_funcall_args {
31
31
  static VALUE
32
32
  rb_sqlite3_protected_funcall_body(VALUE protected_funcall_args_ptr)
33
33
  {
34
- protected_funcall_args_t *args =
35
- (protected_funcall_args_t*)protected_funcall_args_ptr;
34
+ protected_funcall_args_t *args =
35
+ (protected_funcall_args_t *)protected_funcall_args_ptr;
36
36
 
37
- return rb_funcall2(args->self, args->method, args->argc, args->params);
37
+ return rb_funcall2(args->self, args->method, args->argc, args->params);
38
38
  }
39
39
 
40
40
  static VALUE
41
41
  rb_sqlite3_protected_funcall(VALUE self, ID method, int argc, VALUE *params,
42
- int* exc_status)
42
+ int *exc_status)
43
43
  {
44
- protected_funcall_args_t args = {
45
- .self = self, .method = method, .argc = argc, .params = params
46
- };
47
- return rb_protect(rb_sqlite3_protected_funcall_body, (VALUE)(&args), exc_status);
44
+ protected_funcall_args_t args = {
45
+ .self = self, .method = method, .argc = argc, .params = params
46
+ };
47
+ return rb_protect(rb_sqlite3_protected_funcall_body, (VALUE)(&args), exc_status);
48
48
  }
49
49
 
50
50
  /* called in rb_sqlite3_aggregator_step and rb_sqlite3_aggregator_final. It
@@ -54,36 +54,36 @@ rb_sqlite3_protected_funcall(VALUE self, ID method, int argc, VALUE *params,
54
54
  static VALUE
55
55
  rb_sqlite3_aggregate_instance(sqlite3_context *ctx)
56
56
  {
57
- VALUE aw = (VALUE) sqlite3_user_data(ctx);
58
- VALUE handler_klass = rb_iv_get(aw, "-handler_klass");
59
- VALUE inst;
60
- VALUE *inst_ptr = sqlite3_aggregate_context(ctx, (int)sizeof(VALUE));
57
+ VALUE aw = (VALUE) sqlite3_user_data(ctx);
58
+ VALUE handler_klass = rb_iv_get(aw, "-handler_klass");
59
+ VALUE inst;
60
+ VALUE *inst_ptr = sqlite3_aggregate_context(ctx, (int)sizeof(VALUE));
61
61
 
62
- if (!inst_ptr) {
63
- rb_fatal("SQLite is out-of-merory");
64
- }
62
+ if (!inst_ptr) {
63
+ rb_fatal("SQLite is out-of-merory");
64
+ }
65
65
 
66
- inst = *inst_ptr;
66
+ inst = *inst_ptr;
67
67
 
68
- if (inst == Qfalse) { /* Qfalse == 0 */
69
- VALUE instances = rb_iv_get(aw, "-instances");
70
- int exc_status;
68
+ if (inst == Qfalse) { /* Qfalse == 0 */
69
+ VALUE instances = rb_iv_get(aw, "-instances");
70
+ int exc_status;
71
71
 
72
- inst = rb_class_new_instance(0, NULL, cAggregatorInstance);
73
- rb_iv_set(inst, "-handler_instance", rb_sqlite3_protected_funcall(
74
- handler_klass, rb_intern("new"), 0, NULL, &exc_status));
75
- rb_iv_set(inst, "-exc_status", INT2NUM(exc_status));
72
+ inst = rb_class_new_instance(0, NULL, cAggregatorInstance);
73
+ rb_iv_set(inst, "-handler_instance", rb_sqlite3_protected_funcall(
74
+ handler_klass, rb_intern("new"), 0, NULL, &exc_status));
75
+ rb_iv_set(inst, "-exc_status", INT2NUM(exc_status));
76
76
 
77
- rb_ary_push(instances, inst);
77
+ rb_ary_push(instances, inst);
78
78
 
79
- *inst_ptr = inst;
80
- }
79
+ *inst_ptr = inst;
80
+ }
81
81
 
82
- if (inst == Qnil) {
83
- rb_fatal("SQLite called us back on an already destroyed aggregate instance");
84
- }
82
+ if (inst == Qnil) {
83
+ rb_fatal("SQLite called us back on an already destroyed aggregate instance");
84
+ }
85
85
 
86
- return inst;
86
+ return inst;
87
87
  }
88
88
 
89
89
  /* called by rb_sqlite3_aggregator_final. Unlinks and frees the
@@ -92,84 +92,84 @@ rb_sqlite3_aggregate_instance(sqlite3_context *ctx)
92
92
  static void
93
93
  rb_sqlite3_aggregate_instance_destroy(sqlite3_context *ctx)
94
94
  {
95
- VALUE aw = (VALUE) sqlite3_user_data(ctx);
96
- VALUE instances = rb_iv_get(aw, "-instances");
97
- VALUE *inst_ptr = sqlite3_aggregate_context(ctx, 0);
98
- VALUE inst;
95
+ VALUE aw = (VALUE) sqlite3_user_data(ctx);
96
+ VALUE instances = rb_iv_get(aw, "-instances");
97
+ VALUE *inst_ptr = sqlite3_aggregate_context(ctx, 0);
98
+ VALUE inst;
99
99
 
100
- if (!inst_ptr || (inst = *inst_ptr)) {
101
- return;
102
- }
100
+ if (!inst_ptr || (inst = *inst_ptr)) {
101
+ return;
102
+ }
103
103
 
104
- if (inst == Qnil) {
105
- rb_fatal("attempt to destroy aggregate instance twice");
106
- }
104
+ if (inst == Qnil) {
105
+ rb_fatal("attempt to destroy aggregate instance twice");
106
+ }
107
107
 
108
- rb_iv_set(inst, "-handler_instance", Qnil); // may catch use-after-free
109
- if (rb_ary_delete(instances, inst) == Qnil) {
110
- rb_fatal("must be in instances at that point");
111
- }
108
+ rb_iv_set(inst, "-handler_instance", Qnil); // may catch use-after-free
109
+ if (rb_ary_delete(instances, inst) == Qnil) {
110
+ rb_fatal("must be in instances at that point");
111
+ }
112
112
 
113
- *inst_ptr = Qnil;
113
+ *inst_ptr = Qnil;
114
114
  }
115
115
 
116
116
  static void
117
- rb_sqlite3_aggregator_step(sqlite3_context * ctx, int argc, sqlite3_value **argv)
117
+ rb_sqlite3_aggregator_step(sqlite3_context *ctx, int argc, sqlite3_value **argv)
118
118
  {
119
- VALUE inst = rb_sqlite3_aggregate_instance(ctx);
120
- VALUE handler_instance = rb_iv_get(inst, "-handler_instance");
121
- VALUE * params = NULL;
122
- VALUE one_param;
123
- int exc_status = NUM2INT(rb_iv_get(inst, "-exc_status"));
124
- int i;
125
-
126
- if (exc_status) {
127
- return;
128
- }
129
-
130
- if (argc == 1) {
131
- one_param = sqlite3val2rb(argv[0]);
132
- params = &one_param;
133
- }
134
- if (argc > 1) {
135
- params = xcalloc((size_t)argc, sizeof(VALUE));
136
- for(i = 0; i < argc; i++) {
137
- params[i] = sqlite3val2rb(argv[i]);
119
+ VALUE inst = rb_sqlite3_aggregate_instance(ctx);
120
+ VALUE handler_instance = rb_iv_get(inst, "-handler_instance");
121
+ VALUE *params = NULL;
122
+ VALUE one_param;
123
+ int exc_status = NUM2INT(rb_iv_get(inst, "-exc_status"));
124
+ int i;
125
+
126
+ if (exc_status) {
127
+ return;
128
+ }
129
+
130
+ if (argc == 1) {
131
+ one_param = sqlite3val2rb(argv[0]);
132
+ params = &one_param;
138
133
  }
139
- }
140
- rb_sqlite3_protected_funcall(
141
- handler_instance, rb_intern("step"), argc, params, &exc_status);
142
- if (argc > 1) {
143
- xfree(params);
144
- }
145
-
146
- rb_iv_set(inst, "-exc_status", INT2NUM(exc_status));
134
+ if (argc > 1) {
135
+ params = xcalloc((size_t)argc, sizeof(VALUE));
136
+ for (i = 0; i < argc; i++) {
137
+ params[i] = sqlite3val2rb(argv[i]);
138
+ }
139
+ }
140
+ rb_sqlite3_protected_funcall(
141
+ handler_instance, rb_intern("step"), argc, params, &exc_status);
142
+ if (argc > 1) {
143
+ xfree(params);
144
+ }
145
+
146
+ rb_iv_set(inst, "-exc_status", INT2NUM(exc_status));
147
147
  }
148
148
 
149
149
  /* we assume that this function is only called once per execution context */
150
150
  static void
151
- rb_sqlite3_aggregator_final(sqlite3_context * ctx)
151
+ rb_sqlite3_aggregator_final(sqlite3_context *ctx)
152
152
  {
153
- VALUE inst = rb_sqlite3_aggregate_instance(ctx);
154
- VALUE handler_instance = rb_iv_get(inst, "-handler_instance");
155
- int exc_status = NUM2INT(rb_iv_get(inst, "-exc_status"));
153
+ VALUE inst = rb_sqlite3_aggregate_instance(ctx);
154
+ VALUE handler_instance = rb_iv_get(inst, "-handler_instance");
155
+ int exc_status = NUM2INT(rb_iv_get(inst, "-exc_status"));
156
156
 
157
- if (!exc_status) {
158
- VALUE result = rb_sqlite3_protected_funcall(
159
- handler_instance, rb_intern("finalize"), 0, NULL, &exc_status);
160
157
  if (!exc_status) {
161
- set_sqlite3_func_result(ctx, result);
158
+ VALUE result = rb_sqlite3_protected_funcall(
159
+ handler_instance, rb_intern("finalize"), 0, NULL, &exc_status);
160
+ if (!exc_status) {
161
+ set_sqlite3_func_result(ctx, result);
162
+ }
162
163
  }
163
- }
164
164
 
165
- if (exc_status) {
166
- /* the user should never see this, as Statement.step() will pick up the
167
- * outstanding exception and raise it instead of generating a new one
168
- * for SQLITE_ERROR with message "Ruby Exception occurred" */
169
- sqlite3_result_error(ctx, "Ruby Exception occurred", -1);
170
- }
165
+ if (exc_status) {
166
+ /* the user should never see this, as Statement.step() will pick up the
167
+ * outstanding exception and raise it instead of generating a new one
168
+ * for SQLITE_ERROR with message "Ruby Exception occurred" */
169
+ sqlite3_result_error(ctx, "Ruby Exception occurred", -1);
170
+ }
171
171
 
172
- rb_sqlite3_aggregate_instance_destroy(ctx);
172
+ rb_sqlite3_aggregate_instance_destroy(ctx);
173
173
  }
174
174
 
175
175
  /* call-seq: define_aggregator2(aggregator)
@@ -205,69 +205,66 @@ rb_sqlite3_aggregator_final(sqlite3_context * ctx)
205
205
  VALUE
206
206
  rb_sqlite3_define_aggregator2(VALUE self, VALUE aggregator, VALUE ruby_name)
207
207
  {
208
- /* define_aggregator is added as a method to SQLite3::Database in database.c */
209
- sqlite3RubyPtr ctx = sqlite3_database_unwrap(self);
210
- int arity, status;
211
- VALUE aw;
212
- VALUE aggregators;
213
-
214
- if (!ctx->db) {
215
- rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a closed database");
216
- }
217
-
218
- if (rb_respond_to(aggregator, rb_intern("arity"))) {
219
- VALUE ruby_arity = rb_funcall(aggregator, rb_intern("arity"), 0);
220
- arity = NUM2INT(ruby_arity);
221
- } else {
222
- arity = -1;
223
- }
224
-
225
- if (arity < -1 || arity > 127) {
208
+ /* define_aggregator is added as a method to SQLite3::Database in database.c */
209
+ sqlite3RubyPtr ctx = sqlite3_database_unwrap(self);
210
+ int arity, status;
211
+ VALUE aw;
212
+ VALUE aggregators;
213
+
214
+ if (!ctx->db) {
215
+ rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a closed database");
216
+ }
217
+
218
+ if (rb_respond_to(aggregator, rb_intern("arity"))) {
219
+ VALUE ruby_arity = rb_funcall(aggregator, rb_intern("arity"), 0);
220
+ arity = NUM2INT(ruby_arity);
221
+ } else {
222
+ arity = -1;
223
+ }
224
+
225
+ if (arity < -1 || arity > 127) {
226
226
  #ifdef PRIsVALUE
227
- rb_raise(rb_eArgError, "%"PRIsVALUE" arity=%d out of range -1..127",
228
- self, arity);
227
+ rb_raise(rb_eArgError, "%"PRIsVALUE" arity=%d out of range -1..127",
228
+ self, arity);
229
229
  #else
230
- rb_raise(rb_eArgError, "Aggregator arity=%d out of range -1..127", arity);
230
+ rb_raise(rb_eArgError, "Aggregator arity=%d out of range -1..127", arity);
231
231
  #endif
232
- }
233
-
234
- if (!rb_ivar_defined(self, rb_intern("-aggregators"))) {
235
- rb_iv_set(self, "-aggregators", rb_ary_new());
236
- }
237
- aggregators = rb_iv_get(self, "-aggregators");
238
-
239
- aw = rb_class_new_instance(0, NULL, cAggregatorWrapper);
240
- rb_iv_set(aw, "-handler_klass", aggregator);
241
- rb_iv_set(aw, "-instances", rb_ary_new());
242
-
243
- status = sqlite3_create_function(
244
- ctx->db,
245
- StringValueCStr(ruby_name),
246
- arity,
247
- SQLITE_UTF8,
248
- (void*)aw,
249
- NULL,
250
- rb_sqlite3_aggregator_step,
251
- rb_sqlite3_aggregator_final
252
- );
253
-
254
- if (status != SQLITE_OK) {
255
- rb_sqlite3_raise(ctx->db, status);
256
- return self; // just in case rb_sqlite3_raise returns.
257
- }
258
-
259
- rb_ary_push(aggregators, aw);
260
-
261
- return self;
232
+ }
233
+
234
+ if (!rb_ivar_defined(self, rb_intern("-aggregators"))) {
235
+ rb_iv_set(self, "-aggregators", rb_ary_new());
236
+ }
237
+ aggregators = rb_iv_get(self, "-aggregators");
238
+
239
+ aw = rb_class_new_instance(0, NULL, cAggregatorWrapper);
240
+ rb_iv_set(aw, "-handler_klass", aggregator);
241
+ rb_iv_set(aw, "-instances", rb_ary_new());
242
+
243
+ status = sqlite3_create_function(
244
+ ctx->db,
245
+ StringValueCStr(ruby_name),
246
+ arity,
247
+ SQLITE_UTF8,
248
+ (void *)aw,
249
+ NULL,
250
+ rb_sqlite3_aggregator_step,
251
+ rb_sqlite3_aggregator_final
252
+ );
253
+
254
+ CHECK(ctx->db, status);
255
+
256
+ rb_ary_push(aggregators, aw);
257
+
258
+ return self;
262
259
  }
263
260
 
264
261
  void
265
262
  rb_sqlite3_aggregator_init(void)
266
263
  {
267
- /* rb_class_new generatos class with undefined allocator in ruby 1.9 */
268
- cAggregatorWrapper = rb_funcall(rb_cClass, rb_intern("new"), 0);
269
- rb_gc_register_mark_object(cAggregatorWrapper);
264
+ /* rb_class_new generatos class with undefined allocator in ruby 1.9 */
265
+ cAggregatorWrapper = rb_funcall(rb_cClass, rb_intern("new"), 0);
266
+ rb_gc_register_mark_object(cAggregatorWrapper);
270
267
 
271
- cAggregatorInstance = rb_funcall(rb_cClass, rb_intern("new"), 0);
272
- rb_gc_register_mark_object(cAggregatorInstance);
268
+ cAggregatorInstance = rb_funcall(rb_cClass, rb_intern("new"), 0);
269
+ rb_gc_register_mark_object(cAggregatorInstance);
273
270
  }
@@ -3,10 +3,8 @@
3
3
 
4
4
  #include <sqlite3_ruby.h>
5
5
 
6
- VALUE
7
- rb_sqlite3_define_aggregator2(VALUE self, VALUE aggregator, VALUE ruby_name);
6
+ VALUE rb_sqlite3_define_aggregator2(VALUE self, VALUE aggregator, VALUE ruby_name);
8
7
 
9
- void
10
- rb_sqlite3_aggregator_init(void);
8
+ void rb_sqlite3_aggregator_init(void);
11
9
 
12
10
  #endif