sqlite3 2.0.0-x86-linux-gnu
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/CHANGELOG.md +800 -0
- data/CONTRIBUTING.md +56 -0
- data/FAQ.md +388 -0
- data/INSTALLATION.md +267 -0
- data/LICENSE +23 -0
- data/README.md +181 -0
- data/dependencies.yml +13 -0
- data/ext/sqlite3/aggregator.c +270 -0
- data/ext/sqlite3/aggregator.h +10 -0
- data/ext/sqlite3/backup.c +190 -0
- data/ext/sqlite3/backup.h +15 -0
- data/ext/sqlite3/database.c +931 -0
- data/ext/sqlite3/database.h +22 -0
- data/ext/sqlite3/exception.c +117 -0
- data/ext/sqlite3/exception.h +10 -0
- data/ext/sqlite3/extconf.rb +284 -0
- data/ext/sqlite3/sqlite3.c +208 -0
- data/ext/sqlite3/sqlite3_ruby.h +48 -0
- data/ext/sqlite3/statement.c +667 -0
- data/ext/sqlite3/statement.h +16 -0
- data/ext/sqlite3/timespec.h +20 -0
- data/lib/sqlite3/3.0/sqlite3_native.so +0 -0
- data/lib/sqlite3/3.1/sqlite3_native.so +0 -0
- data/lib/sqlite3/3.2/sqlite3_native.so +0 -0
- data/lib/sqlite3/3.3/sqlite3_native.so +0 -0
- data/lib/sqlite3/constants.rb +174 -0
- data/lib/sqlite3/database.rb +701 -0
- data/lib/sqlite3/errors.rb +60 -0
- data/lib/sqlite3/pragmas.rb +585 -0
- data/lib/sqlite3/resultset.rb +96 -0
- data/lib/sqlite3/statement.rb +190 -0
- data/lib/sqlite3/value.rb +54 -0
- data/lib/sqlite3/version.rb +3 -0
- data/lib/sqlite3.rb +17 -0
- metadata +101 -0
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (c) 2004-2024, Jamis Buck, Luis Lavena, Aaron Patterson, Mike Dalessio, et al.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted
|
4
|
+
provided that the following conditions are met:
|
5
|
+
|
6
|
+
1. Redistributions of source code must retain the above copyright notice, this list of conditions
|
7
|
+
and the following disclaimer.
|
8
|
+
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list of
|
10
|
+
conditions and the following disclaimer in the documentation and/or other materials provided with
|
11
|
+
the distribution.
|
12
|
+
|
13
|
+
3. Neither the name of the copyright holder nor the names of its contributors may be used to
|
14
|
+
endorse or promote products derived from this software without specific prior written permission.
|
15
|
+
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
17
|
+
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
19
|
+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
20
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
21
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
22
|
+
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
23
|
+
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
# Ruby Interface for SQLite3
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
|
5
|
+
This library allows Ruby programs to use the SQLite3 database engine (http://www.sqlite.org).
|
6
|
+
|
7
|
+
Note that this module is only compatible with SQLite 3.6.16 or newer.
|
8
|
+
|
9
|
+
* Source code: https://github.com/sparklemotion/sqlite3-ruby
|
10
|
+
* Mailing list: http://groups.google.com/group/sqlite3-ruby
|
11
|
+
* Download: http://rubygems.org/gems/sqlite3
|
12
|
+
* Documentation: http://www.rubydoc.info/gems/sqlite3
|
13
|
+
|
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)
|
15
|
+
|
16
|
+
|
17
|
+
## Quick start
|
18
|
+
|
19
|
+
For help understanding the SQLite3 Ruby API, please read the [FAQ](./FAQ.md) and the [full API documentation](https://rubydoc.info/gems/sqlite3).
|
20
|
+
|
21
|
+
A few key classes whose APIs are often-used are:
|
22
|
+
|
23
|
+
- SQLite3::Database ([rdoc](https://rubydoc.info/gems/sqlite3/SQLite3/Database))
|
24
|
+
- SQLite3::Statement ([rdoc](https://rubydoc.info/gems/sqlite3/SQLite3/Statement))
|
25
|
+
- SQLite3::ResultSet ([rdoc](https://rubydoc.info/gems/sqlite3/SQLite3/ResultSet))
|
26
|
+
|
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).
|
28
|
+
|
29
|
+
|
30
|
+
``` ruby
|
31
|
+
require "sqlite3"
|
32
|
+
|
33
|
+
# Open a database
|
34
|
+
db = SQLite3::Database.new "test.db"
|
35
|
+
|
36
|
+
# Create a table
|
37
|
+
rows = db.execute <<-SQL
|
38
|
+
create table numbers (
|
39
|
+
name varchar(30),
|
40
|
+
val int
|
41
|
+
);
|
42
|
+
SQL
|
43
|
+
|
44
|
+
# Execute a few inserts
|
45
|
+
{
|
46
|
+
"one" => 1,
|
47
|
+
"two" => 2,
|
48
|
+
}.each do |pair|
|
49
|
+
db.execute "insert into numbers values ( ?, ? )", pair
|
50
|
+
end
|
51
|
+
|
52
|
+
# Find a few rows
|
53
|
+
db.execute( "select * from numbers" ) do |row|
|
54
|
+
p row
|
55
|
+
end
|
56
|
+
# => ["one", 1]
|
57
|
+
# ["two", 2]
|
58
|
+
|
59
|
+
# Create another table with multiple columns
|
60
|
+
db.execute <<-SQL
|
61
|
+
create table students (
|
62
|
+
name varchar(50),
|
63
|
+
email varchar(50),
|
64
|
+
grade varchar(5),
|
65
|
+
blog varchar(50)
|
66
|
+
);
|
67
|
+
SQL
|
68
|
+
|
69
|
+
# Execute inserts with parameter markers
|
70
|
+
db.execute("INSERT INTO students (name, email, grade, blog)
|
71
|
+
VALUES (?, ?, ?, ?)", ["Jane", "me@janedoe.com", "A", "http://blog.janedoe.com"])
|
72
|
+
|
73
|
+
db.execute( "select * from students" ) do |row|
|
74
|
+
p row
|
75
|
+
end
|
76
|
+
# => ["Jane", "me@janedoe.com", "A", "http://blog.janedoe.com"]
|
77
|
+
```
|
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
|
+
## Support
|
152
|
+
|
153
|
+
### Installation or database extensions
|
154
|
+
|
155
|
+
If you're having trouble with installation, please first read [`INSTALLATION.md`](./INSTALLATION.md).
|
156
|
+
|
157
|
+
### General help requests
|
158
|
+
|
159
|
+
You can ask for help or support:
|
160
|
+
|
161
|
+
* by emailing the [sqlite3-ruby mailing list](http://groups.google.com/group/sqlite3-ruby)
|
162
|
+
* by opening a [discussion thread](https://github.com/sparklemotion/sqlite3-ruby/discussions/categories/q-a) on Github
|
163
|
+
|
164
|
+
### Bug reports
|
165
|
+
|
166
|
+
You can file the bug at the [github issues page](https://github.com/sparklemotion/sqlite3-ruby/issues).
|
167
|
+
|
168
|
+
|
169
|
+
## Contributing
|
170
|
+
|
171
|
+
See [`CONTRIBUTING.md`](./CONTRIBUTING.md).
|
172
|
+
|
173
|
+
|
174
|
+
## License
|
175
|
+
|
176
|
+
This library is licensed under `BSD-3-Clause`, see [`LICENSE`](./LICENSE).
|
177
|
+
|
178
|
+
### Dependencies
|
179
|
+
|
180
|
+
The source code of `sqlite` is distributed in the "ruby platform" gem. This code is public domain,
|
181
|
+
see https://www.sqlite.org/copyright.html for details.
|
data/dependencies.yml
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
sqlite3:
|
2
|
+
# checksum verified by first checking the published sha3(256) checksum against https://sqlite.org/download.html:
|
3
|
+
# cc1050780e0266de4d91b31c8deaf4638336908c12c21898e9f1fcae1e2ac303
|
4
|
+
#
|
5
|
+
# $ sha3sum -a 256 ports/archives/sqlite-autoconf-3450300.tar.gz
|
6
|
+
# cc1050780e0266de4d91b31c8deaf4638336908c12c21898e9f1fcae1e2ac303 ports/archives/sqlite-autoconf-3450300.tar.gz
|
7
|
+
#
|
8
|
+
# $ sha256sum ports/archives/sqlite-autoconf-3450300.tar.gz
|
9
|
+
# b2809ca53124c19c60f42bf627736eae011afdcc205bb48270a5ee9a38191531 ports/archives/sqlite-autoconf-3450300.tar.gz
|
10
|
+
version: "3.45.3"
|
11
|
+
files:
|
12
|
+
- url: "https://sqlite.org/2024/sqlite-autoconf-3450300.tar.gz"
|
13
|
+
sha256: "b2809ca53124c19c60f42bf627736eae011afdcc205bb48270a5ee9a38191531"
|
@@ -0,0 +1,270 @@
|
|
1
|
+
#include <aggregator.h>
|
2
|
+
#include <database.h>
|
3
|
+
|
4
|
+
/* wraps a factory "handler" class. The "-aggregators" instance variable of
|
5
|
+
* the SQLite3::Database holds an array of all AggrogatorWrappers.
|
6
|
+
*
|
7
|
+
* An AggregatorWrapper holds the following instance variables:
|
8
|
+
* -handler_klass: the handler that creates the instances.
|
9
|
+
* -instances: array of all the cAggregatorInstance objects currently
|
10
|
+
* in-flight for this aggregator. */
|
11
|
+
static VALUE cAggregatorWrapper;
|
12
|
+
|
13
|
+
/* wraps a instance of the "handler" class. Loses its reference at the end of
|
14
|
+
* the xFinal callback.
|
15
|
+
*
|
16
|
+
* An AggregatorInstance holds the following instance variables:
|
17
|
+
* -handler_instance: the instance to call `step` and `finalize` on.
|
18
|
+
* -exc_status: status returned by rb_protect.
|
19
|
+
* != 0 if an exception occurred. If an exception occurred
|
20
|
+
* `step` and `finalize` won't be called any more. */
|
21
|
+
static VALUE cAggregatorInstance;
|
22
|
+
|
23
|
+
typedef struct rb_sqlite3_protected_funcall_args {
|
24
|
+
VALUE self;
|
25
|
+
ID method;
|
26
|
+
int argc;
|
27
|
+
VALUE *params;
|
28
|
+
} protected_funcall_args_t;
|
29
|
+
|
30
|
+
/* why isn't there something like this in the ruby API? */
|
31
|
+
static VALUE
|
32
|
+
rb_sqlite3_protected_funcall_body(VALUE protected_funcall_args_ptr)
|
33
|
+
{
|
34
|
+
protected_funcall_args_t *args =
|
35
|
+
(protected_funcall_args_t *)protected_funcall_args_ptr;
|
36
|
+
|
37
|
+
return rb_funcall2(args->self, args->method, args->argc, args->params);
|
38
|
+
}
|
39
|
+
|
40
|
+
static VALUE
|
41
|
+
rb_sqlite3_protected_funcall(VALUE self, ID method, int argc, VALUE *params,
|
42
|
+
int *exc_status)
|
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);
|
48
|
+
}
|
49
|
+
|
50
|
+
/* called in rb_sqlite3_aggregator_step and rb_sqlite3_aggregator_final. It
|
51
|
+
* checks if the execution context already has an associated instance. If it
|
52
|
+
* has one, it returns it. If there is no instance yet, it creates one and
|
53
|
+
* associates it with the context. */
|
54
|
+
static VALUE
|
55
|
+
rb_sqlite3_aggregate_instance(sqlite3_context *ctx)
|
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));
|
61
|
+
|
62
|
+
if (!inst_ptr) {
|
63
|
+
rb_fatal("SQLite is out-of-merory");
|
64
|
+
}
|
65
|
+
|
66
|
+
inst = *inst_ptr;
|
67
|
+
|
68
|
+
if (inst == Qfalse) { /* Qfalse == 0 */
|
69
|
+
VALUE instances = rb_iv_get(aw, "-instances");
|
70
|
+
int exc_status;
|
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));
|
76
|
+
|
77
|
+
rb_ary_push(instances, inst);
|
78
|
+
|
79
|
+
*inst_ptr = inst;
|
80
|
+
}
|
81
|
+
|
82
|
+
if (inst == Qnil) {
|
83
|
+
rb_fatal("SQLite called us back on an already destroyed aggregate instance");
|
84
|
+
}
|
85
|
+
|
86
|
+
return inst;
|
87
|
+
}
|
88
|
+
|
89
|
+
/* called by rb_sqlite3_aggregator_final. Unlinks and frees the
|
90
|
+
* aggregator_instance_t, so the handler_instance won't be marked any more
|
91
|
+
* and Ruby's GC may free it. */
|
92
|
+
static void
|
93
|
+
rb_sqlite3_aggregate_instance_destroy(sqlite3_context *ctx)
|
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;
|
99
|
+
|
100
|
+
if (!inst_ptr || (inst = *inst_ptr)) {
|
101
|
+
return;
|
102
|
+
}
|
103
|
+
|
104
|
+
if (inst == Qnil) {
|
105
|
+
rb_fatal("attempt to destroy aggregate instance twice");
|
106
|
+
}
|
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
|
+
}
|
112
|
+
|
113
|
+
*inst_ptr = Qnil;
|
114
|
+
}
|
115
|
+
|
116
|
+
static void
|
117
|
+
rb_sqlite3_aggregator_step(sqlite3_context *ctx, int argc, sqlite3_value **argv)
|
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]);
|
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
|
+
}
|
148
|
+
|
149
|
+
/* we assume that this function is only called once per execution context */
|
150
|
+
static void
|
151
|
+
rb_sqlite3_aggregator_final(sqlite3_context *ctx)
|
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"));
|
156
|
+
|
157
|
+
if (!exc_status) {
|
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
|
+
}
|
163
|
+
}
|
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
|
+
}
|
171
|
+
|
172
|
+
rb_sqlite3_aggregate_instance_destroy(ctx);
|
173
|
+
}
|
174
|
+
|
175
|
+
/* call-seq: define_aggregator2(aggregator)
|
176
|
+
*
|
177
|
+
* Define an aggregrate function according to a factory object (the "handler")
|
178
|
+
* that knows how to obtain to all the information. The handler must provide
|
179
|
+
* the following class methods:
|
180
|
+
*
|
181
|
+
* +arity+:: corresponds to the +arity+ parameter of #create_aggregate. This
|
182
|
+
* message is optional, and if the handler does not respond to it,
|
183
|
+
* the function will have an arity of -1.
|
184
|
+
* +name+:: this is the name of the function. The handler _must_ implement
|
185
|
+
* this message.
|
186
|
+
* +new+:: this must be implemented by the handler. It should return a new
|
187
|
+
* instance of the object that will handle a specific invocation of
|
188
|
+
* the function.
|
189
|
+
*
|
190
|
+
* The handler instance (the object returned by the +new+ message, described
|
191
|
+
* above), must respond to the following messages:
|
192
|
+
*
|
193
|
+
* +step+:: this is the method that will be called for each step of the
|
194
|
+
* aggregate function's evaluation. It should take parameters according
|
195
|
+
* to the *arity* definition.
|
196
|
+
* +finalize+:: this is the method that will be called to finalize the
|
197
|
+
* aggregate function's evaluation. It should not take arguments.
|
198
|
+
*
|
199
|
+
* Note the difference between this function and #create_aggregate_handler
|
200
|
+
* is that no FunctionProxy ("ctx") object is involved. This manifests in two
|
201
|
+
* ways: The return value of the aggregate function is the return value of
|
202
|
+
* +finalize+ and neither +step+ nor +finalize+ take an additional "ctx"
|
203
|
+
* parameter.
|
204
|
+
*/
|
205
|
+
VALUE
|
206
|
+
rb_sqlite3_define_aggregator2(VALUE self, VALUE aggregator, VALUE ruby_name)
|
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) {
|
226
|
+
#ifdef PRIsVALUE
|
227
|
+
rb_raise(rb_eArgError, "%"PRIsVALUE" arity=%d out of range -1..127",
|
228
|
+
self, arity);
|
229
|
+
#else
|
230
|
+
rb_raise(rb_eArgError, "Aggregator arity=%d out of range -1..127", arity);
|
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
|
+
CHECK(ctx->db, status);
|
255
|
+
|
256
|
+
rb_ary_push(aggregators, aw);
|
257
|
+
|
258
|
+
return self;
|
259
|
+
}
|
260
|
+
|
261
|
+
void
|
262
|
+
rb_sqlite3_aggregator_init(void)
|
263
|
+
{
|
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);
|
267
|
+
|
268
|
+
cAggregatorInstance = rb_funcall(rb_cClass, rb_intern("new"), 0);
|
269
|
+
rb_gc_register_mark_object(cAggregatorInstance);
|
270
|
+
}
|
@@ -0,0 +1,190 @@
|
|
1
|
+
#ifdef HAVE_SQLITE3_BACKUP_INIT
|
2
|
+
|
3
|
+
#include <sqlite3_ruby.h>
|
4
|
+
|
5
|
+
#define REQUIRE_OPEN_BACKUP(_ctxt) \
|
6
|
+
if(!_ctxt->p) \
|
7
|
+
rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a closed backup");
|
8
|
+
|
9
|
+
VALUE cSqlite3Backup;
|
10
|
+
|
11
|
+
static size_t
|
12
|
+
backup_memsize(const void *data)
|
13
|
+
{
|
14
|
+
sqlite3BackupRubyPtr ctx = (sqlite3BackupRubyPtr)data;
|
15
|
+
// NB: can't account for ctx->p because the type is incomplete.
|
16
|
+
return sizeof(*ctx);
|
17
|
+
}
|
18
|
+
|
19
|
+
static const rb_data_type_t backup_type = {
|
20
|
+
"SQLite3::Backup",
|
21
|
+
{
|
22
|
+
NULL,
|
23
|
+
RUBY_TYPED_DEFAULT_FREE,
|
24
|
+
backup_memsize,
|
25
|
+
},
|
26
|
+
0,
|
27
|
+
0,
|
28
|
+
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
|
29
|
+
};
|
30
|
+
|
31
|
+
static VALUE
|
32
|
+
allocate(VALUE klass)
|
33
|
+
{
|
34
|
+
sqlite3BackupRubyPtr ctx;
|
35
|
+
return TypedData_Make_Struct(klass, sqlite3BackupRuby, &backup_type, ctx);
|
36
|
+
}
|
37
|
+
|
38
|
+
/* call-seq: SQLite3::Backup.new(dstdb, dstname, srcdb, srcname)
|
39
|
+
*
|
40
|
+
* Initialize backup the backup.
|
41
|
+
*
|
42
|
+
* dstdb:
|
43
|
+
* the destination SQLite3::Database object.
|
44
|
+
* dstname:
|
45
|
+
* the destination's database name.
|
46
|
+
* srcdb:
|
47
|
+
* the source SQLite3::Database object.
|
48
|
+
* srcname:
|
49
|
+
* the source's database name.
|
50
|
+
*
|
51
|
+
* The database name is "main", "temp", or the name specified in an
|
52
|
+
* ATTACH statement.
|
53
|
+
*
|
54
|
+
* This feature requires SQLite 3.6.11 or later.
|
55
|
+
*
|
56
|
+
* require 'sqlite3'
|
57
|
+
* sdb = SQLite3::Database.new('src.sqlite3')
|
58
|
+
*
|
59
|
+
* ddb = SQLite3::Database.new(':memory:')
|
60
|
+
* b = SQLite3::Backup.new(ddb, 'main', sdb, 'main')
|
61
|
+
* p [b.remaining, b.pagecount] # invalid value; for example [0, 0]
|
62
|
+
* begin
|
63
|
+
* p b.step(1) #=> OK or DONE
|
64
|
+
* p [b.remaining, b.pagecount]
|
65
|
+
* end while b.remaining > 0
|
66
|
+
* b.finish
|
67
|
+
*
|
68
|
+
* ddb = SQLite3::Database.new(':memory:')
|
69
|
+
* b = SQLite3::Backup.new(ddb, 'main', sdb, 'main')
|
70
|
+
* b.step(-1) #=> DONE
|
71
|
+
* b.finish
|
72
|
+
*
|
73
|
+
*/
|
74
|
+
static VALUE
|
75
|
+
initialize(VALUE self, VALUE dstdb, VALUE dstname, VALUE srcdb, VALUE srcname)
|
76
|
+
{
|
77
|
+
sqlite3BackupRubyPtr ctx;
|
78
|
+
sqlite3RubyPtr ddb_ctx, sdb_ctx;
|
79
|
+
sqlite3_backup *pBackup;
|
80
|
+
|
81
|
+
TypedData_Get_Struct(self, sqlite3BackupRuby, &backup_type, ctx);
|
82
|
+
ddb_ctx = sqlite3_database_unwrap(dstdb);
|
83
|
+
sdb_ctx = sqlite3_database_unwrap(srcdb);
|
84
|
+
|
85
|
+
if (!sdb_ctx->db) {
|
86
|
+
rb_raise(rb_eArgError, "cannot backup from a closed database");
|
87
|
+
}
|
88
|
+
if (!ddb_ctx->db) {
|
89
|
+
rb_raise(rb_eArgError, "cannot backup to a closed database");
|
90
|
+
}
|
91
|
+
|
92
|
+
pBackup = sqlite3_backup_init(ddb_ctx->db, StringValuePtr(dstname),
|
93
|
+
sdb_ctx->db, StringValuePtr(srcname));
|
94
|
+
if (pBackup) {
|
95
|
+
ctx->p = pBackup;
|
96
|
+
} else {
|
97
|
+
CHECK(ddb_ctx->db, sqlite3_errcode(ddb_ctx->db));
|
98
|
+
}
|
99
|
+
|
100
|
+
return self;
|
101
|
+
}
|
102
|
+
|
103
|
+
/* call-seq: SQLite3::Backup#step(nPage)
|
104
|
+
*
|
105
|
+
* Copy database pages up to +nPage+.
|
106
|
+
* If negative, copy all remaining source pages.
|
107
|
+
*
|
108
|
+
* If all pages are copied, it returns SQLite3::Constants::ErrorCode::DONE.
|
109
|
+
* When coping is not done, it returns SQLite3::Constants::ErrorCode::OK.
|
110
|
+
* When some errors occur, it returns the error code.
|
111
|
+
*/
|
112
|
+
static VALUE
|
113
|
+
step(VALUE self, VALUE nPage)
|
114
|
+
{
|
115
|
+
sqlite3BackupRubyPtr ctx;
|
116
|
+
int status;
|
117
|
+
|
118
|
+
TypedData_Get_Struct(self, sqlite3BackupRuby, &backup_type, ctx);
|
119
|
+
REQUIRE_OPEN_BACKUP(ctx);
|
120
|
+
status = sqlite3_backup_step(ctx->p, NUM2INT(nPage));
|
121
|
+
return INT2NUM(status);
|
122
|
+
}
|
123
|
+
|
124
|
+
/* call-seq: SQLite3::Backup#finish
|
125
|
+
*
|
126
|
+
* Destroy the backup object.
|
127
|
+
*/
|
128
|
+
static VALUE
|
129
|
+
finish(VALUE self)
|
130
|
+
{
|
131
|
+
sqlite3BackupRubyPtr ctx;
|
132
|
+
|
133
|
+
TypedData_Get_Struct(self, sqlite3BackupRuby, &backup_type, ctx);
|
134
|
+
REQUIRE_OPEN_BACKUP(ctx);
|
135
|
+
(void)sqlite3_backup_finish(ctx->p);
|
136
|
+
ctx->p = NULL;
|
137
|
+
return Qnil;
|
138
|
+
}
|
139
|
+
|
140
|
+
/* call-seq: SQLite3::Backup#remaining
|
141
|
+
*
|
142
|
+
* Returns the number of pages still to be backed up.
|
143
|
+
*
|
144
|
+
* Note that the value is only updated after step() is called,
|
145
|
+
* so before calling step() returned value is invalid.
|
146
|
+
*/
|
147
|
+
static VALUE
|
148
|
+
remaining(VALUE self)
|
149
|
+
{
|
150
|
+
sqlite3BackupRubyPtr ctx;
|
151
|
+
|
152
|
+
TypedData_Get_Struct(self, sqlite3BackupRuby, &backup_type, ctx);
|
153
|
+
REQUIRE_OPEN_BACKUP(ctx);
|
154
|
+
return INT2NUM(sqlite3_backup_remaining(ctx->p));
|
155
|
+
}
|
156
|
+
|
157
|
+
/* call-seq: SQLite3::Backup#pagecount
|
158
|
+
*
|
159
|
+
* Returns the total number of pages in the source database file.
|
160
|
+
*
|
161
|
+
* Note that the value is only updated after step() is called,
|
162
|
+
* so before calling step() returned value is invalid.
|
163
|
+
*/
|
164
|
+
static VALUE
|
165
|
+
pagecount(VALUE self)
|
166
|
+
{
|
167
|
+
sqlite3BackupRubyPtr ctx;
|
168
|
+
|
169
|
+
TypedData_Get_Struct(self, sqlite3BackupRuby, &backup_type, ctx);
|
170
|
+
REQUIRE_OPEN_BACKUP(ctx);
|
171
|
+
return INT2NUM(sqlite3_backup_pagecount(ctx->p));
|
172
|
+
}
|
173
|
+
|
174
|
+
void
|
175
|
+
init_sqlite3_backup(void)
|
176
|
+
{
|
177
|
+
#if 0
|
178
|
+
VALUE mSqlite3 = rb_define_module("SQLite3");
|
179
|
+
#endif
|
180
|
+
cSqlite3Backup = rb_define_class_under(mSqlite3, "Backup", rb_cObject);
|
181
|
+
|
182
|
+
rb_define_alloc_func(cSqlite3Backup, allocate);
|
183
|
+
rb_define_method(cSqlite3Backup, "initialize", initialize, 4);
|
184
|
+
rb_define_method(cSqlite3Backup, "step", step, 1);
|
185
|
+
rb_define_method(cSqlite3Backup, "finish", finish, 0);
|
186
|
+
rb_define_method(cSqlite3Backup, "remaining", remaining, 0);
|
187
|
+
rb_define_method(cSqlite3Backup, "pagecount", pagecount, 0);
|
188
|
+
}
|
189
|
+
|
190
|
+
#endif
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#if !defined(SQLITE3_BACKUP_RUBY) && defined(HAVE_SQLITE3_BACKUP_INIT)
|
2
|
+
#define SQLITE3_BACKUP_RUBY
|
3
|
+
|
4
|
+
#include <sqlite3_ruby.h>
|
5
|
+
|
6
|
+
struct _sqlite3BackupRuby {
|
7
|
+
sqlite3_backup *p;
|
8
|
+
};
|
9
|
+
|
10
|
+
typedef struct _sqlite3BackupRuby sqlite3BackupRuby;
|
11
|
+
typedef sqlite3BackupRuby *sqlite3BackupRubyPtr;
|
12
|
+
|
13
|
+
void init_sqlite3_backup();
|
14
|
+
|
15
|
+
#endif
|