rq-ruby1.8 3.4.3 → 3.4.5
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/Gemfile +1 -9
- data/INSTALL +57 -62
- data/README +99 -53
- data/VERSION +1 -1
- data/bin/rq +1 -1
- data/ext/extconf.rb +7 -0
- data/ext/sqlite.c +862 -0
- data/lib/rq/sqlite.rb +5 -0
- data/lib/rq/usage.rb +98 -52
- data/rq-ruby1.8.gemspec +5 -2
- metadata +8 -6
data/Gemfile
CHANGED
@@ -1,22 +1,14 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
|
-
# Add dependencies required to use your gem here.
|
3
|
-
# Example:
|
4
|
-
# gem "activesupport", ">= 2.3.5"
|
5
2
|
|
6
3
|
# Runtime dependencies
|
7
|
-
# gem "bio", ">= 1.3.1"
|
8
|
-
# gem "bio-logger", "> 0.8.0"
|
9
|
-
# gem "nokogiri", ">= 1.4.4"
|
10
4
|
gem "posixlock"
|
11
5
|
gem "arrayfields"
|
12
6
|
gem "lockfile"
|
13
|
-
# gem "sqlite-1.3.1" #
|
7
|
+
# gem "sqlite-1.3.1" # now included with rq-ruby1.8
|
14
8
|
|
15
9
|
# Add dependencies to develop your gem here.
|
16
10
|
# Include everything needed to run rake, tests, features, etc.
|
17
11
|
group :development do
|
18
|
-
# gem "rspec", "~> 2.3.0"
|
19
12
|
gem "bundler", "~> 1.0.15"
|
20
13
|
gem "jeweler", "~> 1.6.4"
|
21
|
-
# gem "rcov", ">= 0"
|
22
14
|
end
|
data/INSTALL
CHANGED
@@ -4,85 +4,66 @@ The current version of rq runs on ruby1.8. A gem is available on rubygems:
|
|
4
4
|
|
5
5
|
https://rubygems.org/gems/rq-ruby1.8
|
6
6
|
|
7
|
-
|
7
|
+
simply with
|
8
|
+
|
9
|
+
install sqlite2 (Debian apt-get install libsqlite0-dev)
|
10
|
+
|
11
|
+
and
|
8
12
|
|
9
13
|
gem1.8 install rq-ruby1.8
|
10
14
|
|
11
|
-
|
15
|
+
The gem includes sqlite-1.3.1 for Ruby. And should include:
|
12
16
|
|
13
17
|
- gem1.8 install posixlock
|
14
18
|
- gem1.8 install arrayfields
|
15
19
|
- gem1.8 install lockfile
|
16
20
|
|
17
|
-
|
18
|
-
|
19
|
-
- install sqlite2 (Debian apt-get install libsqlite0-dev)
|
20
|
-
- wget http://rubyforge.org/frs/download.php/1070/sqlite-1.3.1.gem
|
21
|
-
- gem1.8 install sqlite-1.3.1.gem
|
22
|
-
- gem1.8 install rq-ruby1.8 (or run from source)
|
21
|
+
gems also available from http://bio4.dnsalias.net/download/gem/ruby1.8/
|
23
22
|
|
24
|
-
|
23
|
+
Find rq
|
25
24
|
|
26
|
-
|
25
|
+
gem1.8 contents rq-ruby1.8|grep bin/rq$
|
26
|
+
|
27
|
+
Maybe link it
|
27
28
|
|
28
|
-
ln -
|
29
|
+
ln -sf `gem1.8 contents rq-ruby1.8|grep bin/rq$` /usr/local/bin/rq
|
29
30
|
rq --help
|
30
31
|
|
31
32
|
=== Debian
|
32
33
|
|
33
34
|
On Debian systems, the recommended procedure is to use Debian apt on all
|
34
|
-
machines. For now, use the
|
35
|
+
machines. For now, use the gem install, as documented in the README!
|
35
36
|
|
36
37
|
This has been tested on Debian Squeeze (BioLinux Minimal):
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
Installing
|
50
|
-
Installing
|
51
|
-
Installing
|
52
|
-
Installing
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
Saving to: `sqlite-1.3.1.gem'
|
61
|
-
100%[======================================>] 41,278 --.-K/s in 0.1s
|
62
|
-
2011-07-21 11:36:21 (347 KB/s) - `sqlite-1.3.1.gem' saved [41278/41278]
|
63
|
-
|
64
|
-
root@vagrant-debian-squeeze:/home/vagrant# gem1.8 install sqlite-1.3.1.gem
|
65
|
-
Building native extensions. This could take a while...
|
66
|
-
Successfully installed sqlite-1.3.1
|
67
|
-
1 gem installed
|
68
|
-
Installing ri documentation for sqlite-1.3.1...
|
69
|
-
Installing RDoc documentation for sqlite-1.3.1...
|
39
|
+
apt-get install libsqlite0-dev
|
40
|
+
Setting up libsqlite0 (2.8.17-6) ...
|
41
|
+
Setting up libsqlite0-dev (2.8.17-6) ...
|
42
|
+
|
43
|
+
gem1.8 install rq-ruby1.8
|
44
|
+
Building native extensions. This could take a while...
|
45
|
+
Successfully installed posixlock-0.0.1
|
46
|
+
Successfully installed arrayfields-4.7.4
|
47
|
+
Successfully installed lockfile-1.4.3
|
48
|
+
Successfully installed rq-ruby1.8-3.4.4
|
49
|
+
4 gems installed
|
50
|
+
Installing ri documentation for posixlock-0.0.1...
|
51
|
+
Installing ri documentation for arrayfields-4.7.4...
|
52
|
+
Installing ri documentation for lockfile-1.4.3...
|
53
|
+
Installing ri documentation for rq-ruby1.8-3.4.4...
|
54
|
+
Installing RDoc documentation for posixlock-0.0.1...
|
55
|
+
Installing RDoc documentation for arrayfields-4.7.4...
|
56
|
+
Installing RDoc documentation for lockfile-1.4.3...
|
57
|
+
Installing RDoc documentation for rq-ruby1.8-3.4.4...
|
58
|
+
|
59
|
+
ln -sf `gem1.8 contents rq-ruby1.8|grep bin/rq$` /usr/local/bin/rq
|
60
|
+
|
70
61
|
root@vagrant-debian-squeeze:/home/vagrant# rq --help
|
71
62
|
NAME
|
72
63
|
|
73
|
-
rq v3.4.
|
64
|
+
rq v3.4.4
|
74
65
|
|
75
|
-
(
|
76
|
-
|
77
|
-
(below will become available later)
|
78
|
-
|
79
|
-
* apt-get install rq-ruby1.8
|
80
|
-
|
81
|
-
which resolves all dependencies, or alternatively
|
82
|
-
|
83
|
-
* Download the rq deb file from http://bio4.dnsalias.net/download/Debian/
|
84
|
-
* apt-get install libposixlock-ruby1.8 libsqlite3-ruby1.8 ruby1.8
|
85
|
-
* dpkg --force-architecture -i rq-ruby1.8-ver.deb
|
66
|
+
(...)
|
86
67
|
|
87
68
|
which works on on 32-bits and 64-bits systems. The commands
|
88
69
|
|
@@ -135,12 +116,6 @@ version >4.4
|
|
135
116
|
install which all cluster nodes can use. The other install methods mean
|
136
117
|
you will have to install rq on __each__ node you plan to use it on.
|
137
118
|
|
138
|
-
=== RUBYGEMS
|
139
|
-
|
140
|
-
(currently defunct, awaits updating rq to Ruby 1.9.x and sqlite3)
|
141
|
-
|
142
|
-
* gem install rq
|
143
|
-
|
144
119
|
=== STANDARD
|
145
120
|
|
146
121
|
(in rq version <=3.4.0)
|
@@ -164,3 +139,23 @@ The current version of rq runs on ruby1.8, using bundler and jeweler:
|
|
164
139
|
rake gemspec
|
165
140
|
rake build # creates gem in ./pkg
|
166
141
|
|
142
|
+
== Trouble shooting
|
143
|
+
|
144
|
+
=== rubyio.h error
|
145
|
+
|
146
|
+
Building native extensions. This could take a while...
|
147
|
+
ERROR: Error installing rq-ruby1.8-3.4.5.gem:
|
148
|
+
ERROR: Failed to build gem native extension.
|
149
|
+
|
150
|
+
/usr/local/include/ruby-1.9.1/ruby/backward/rubyio.h:2:2: warning: #warning use "ruby/io.h" instead of "rubyio.h"
|
151
|
+
|
152
|
+
Solution: you are trying to build against ruby1.9. Use gem1.8 instead. If you
|
153
|
+
have problems mixing gems, take a look at 'rvm'.
|
154
|
+
|
155
|
+
=== can not find rq, after successful install
|
156
|
+
|
157
|
+
gem1.8 stores gems in dirs named /var/lib/gems/1.8/gems/rq-ruby1.8-3.4.5/. You
|
158
|
+
may have to create a symbolic link, e.g.
|
159
|
+
|
160
|
+
ln -sf `gem1.8 contents rq-ruby1.8|grep bin/rq$` /usr/local/bin/rq
|
161
|
+
|
data/README
CHANGED
@@ -1,41 +1,108 @@
|
|
1
1
|
NAME
|
2
2
|
|
3
|
-
rq v3.4.
|
3
|
+
rq v3.4.5
|
4
4
|
|
5
5
|
SYNOPSIS
|
6
6
|
|
7
|
-
rq
|
7
|
+
rq queue mode [mode_args]* [options]*
|
8
8
|
|
9
|
+
ruby queue (rq) is a zero-admin zero-configuration tool used to create
|
10
|
+
instant unix clusters on a multi-core machine, and/or multiple nodes in a
|
11
|
+
network, or in the Cloud. rq requires only a centrally mounted directory
|
12
|
+
(e.g. NFS) in order to manage a simple sqlite database as a distributed
|
13
|
+
priority work queue. See QUICK START below.
|
9
14
|
|
10
|
-
URIS
|
11
15
|
|
12
|
-
|
13
|
-
http://www.linuxjournal.com/article/7922
|
16
|
+
DESCRIPTION
|
14
17
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
18
|
+
ruby queue (rq) is a zero-admin zero-configuration tool used to create
|
19
|
+
instant unix clusters. the simple design allows researchers with minimal unix
|
20
|
+
experience to install and configure, in only a few minutes and without root
|
21
|
+
privileges, a robust unix cluster capable of distributing processes to many
|
22
|
+
nodes - bringing dozens of powerful cpus to their knees with a single blow.
|
23
|
+
clearly this software should be kept out of the hands of free radicals, seti
|
24
|
+
enthusiasts, and one mr. j safran.
|
19
25
|
|
20
|
-
|
26
|
+
the central concept of rq is that n nodes work in isolation to pull jobs
|
27
|
+
from an centrally mounted nfs priority work queue in a synchronized fashion.
|
28
|
+
the nodes have absolutely no knowledge of each other and all communication
|
29
|
+
is done via the queue meaning that, so long as the queue is available via
|
30
|
+
nfs and a single node is running jobs from it, the system will continue to
|
31
|
+
process jobs. there is no centralized process whatsoever - all nodes work
|
32
|
+
to take jobs from the queue and run them as fast as possible. this creates
|
33
|
+
a system which load balances automatically and is robust in face of node
|
34
|
+
failures.
|
21
35
|
|
22
|
-
|
36
|
+
although the rq system is simple in it's design it features powerful
|
37
|
+
functionality such as priority management, predicate and sql query, compact
|
38
|
+
streaming command-line processing, programmable api, hot-backup, and
|
39
|
+
input/capture of the stdin/stdout/stderr io streams of remote jobs. to date
|
40
|
+
rq has had no reported runtime failures and is in operation at
|
41
|
+
dozens of research centers around the world. while rq is written in
|
42
|
+
the Ruby programming language, there is no Ruby programming
|
43
|
+
involved in using rq.
|
44
|
+
|
45
|
+
QUICK START
|
46
|
+
|
47
|
+
install rq using rubygems
|
48
|
+
|
49
|
+
gem1.8 install rq-ruby1.8
|
50
|
+
ln -sf `gem1.8 contents rq-ruby1.8|grep bin/rq$` /usr/local/bin/rq
|
51
|
+
rq --help
|
52
|
+
|
53
|
+
set up a directory for the queue - this can be a local, or an NFS/sshfs
|
54
|
+
mounted drive:
|
55
|
+
|
56
|
+
rq dir create
|
57
|
+
|
58
|
+
on every node create a queue runner, specifying the number of cores (here 8)
|
59
|
+
|
60
|
+
rq dir feed --daemon --log=rq.log --max_feed=8
|
61
|
+
|
62
|
+
submit two jobs - shell style
|
63
|
+
|
64
|
+
rq dir submit 'sleep 10'
|
65
|
+
rq dir submit 'sleep 9'
|
66
|
+
|
67
|
+
check status
|
68
|
+
|
69
|
+
rq dir status
|
70
|
+
|
71
|
+
shows
|
72
|
+
|
73
|
+
---
|
74
|
+
jobs:
|
75
|
+
pending: 0
|
76
|
+
holding: 0
|
77
|
+
running: 2
|
78
|
+
finished: 0
|
79
|
+
dead: 0
|
80
|
+
total: 2
|
81
|
+
temporal:
|
82
|
+
running:
|
83
|
+
min: {2: 00h00m03.49s}
|
84
|
+
max: {1: 00h00m03.60s}
|
85
|
+
performance:
|
86
|
+
avg_time_per_job: 00h00m00.00s
|
87
|
+
n_jobs_in_last_hrs:
|
88
|
+
1: 0
|
89
|
+
12: 0
|
90
|
+
24: 0
|
91
|
+
exit_status:
|
92
|
+
successes: 0
|
93
|
+
failures: 0
|
94
|
+
ok: 0
|
95
|
+
|
96
|
+
Now, that was easy!!
|
23
97
|
|
24
98
|
INSTALL
|
25
99
|
|
26
100
|
See the ./INSTALL file, but quickly
|
27
101
|
|
28
|
-
|
29
|
-
|
30
|
-
gem >=3.4.3:
|
102
|
+
gem >=3.4.5:
|
31
103
|
|
32
|
-
|
33
|
-
|
34
|
-
- gem1.8 install sqlite-1.3.1.gem
|
35
|
-
- gem1.8 install posixlock
|
36
|
-
- gem1.8 install arrayfields
|
37
|
-
- gem1.8 install lockfile
|
38
|
-
- gem1.8 install rq-ruby1.8 (or run from source)
|
104
|
+
- install sqlite2 (Debian apt-get install libsqlite0-dev)
|
105
|
+
- gem1.8 install rq-ruby1.8
|
39
106
|
|
40
107
|
Also available from http://bio4.dnsalias.net/download/gem/ruby1.8/
|
41
108
|
|
@@ -52,37 +119,6 @@ INSTALL
|
|
52
119
|
|
53
120
|
see ./INSTALL file for latest
|
54
121
|
|
55
|
-
DESCRIPTION
|
56
|
-
|
57
|
-
ruby queue (rq) is a zero-admin zero-configuration tool used to create instant
|
58
|
-
unix clusters. rq requires only a central nfs filesystem in order to manage a
|
59
|
-
simple sqlite database as a distributed priority work queue. this simple
|
60
|
-
design allows researchers with minimal unix experience to install and
|
61
|
-
configure, in only a few minutes and without root privileges, a robust unix
|
62
|
-
cluster capable of distributing processes to many nodes - bringing dozens of
|
63
|
-
powerful cpus to their knees with a single blow. clearly this software should
|
64
|
-
be kept out of the hands of free radicals, seti enthusiasts, and one mr. j
|
65
|
-
safran.
|
66
|
-
|
67
|
-
the central concept of rq is that n nodes work in isolation to pull jobs
|
68
|
-
from an centrally mounted nfs priority work queue in a synchronized fashion.
|
69
|
-
the nodes have absolutely no knowledge of each other and all communication
|
70
|
-
is done via the queue meaning that, so long as the queue is available via
|
71
|
-
nfs and a single node is running jobs from it, the system will continue to
|
72
|
-
process jobs. there is no centralized process whatsoever - all nodes work
|
73
|
-
to take jobs from the queue and run them as fast as possible. this creates
|
74
|
-
a system which load balances automatically and is robust in face of node
|
75
|
-
failures.
|
76
|
-
|
77
|
-
although the rq system is simple in it's design it features powerful
|
78
|
-
functionality such as priority management, predicate and sql query, compact
|
79
|
-
streaming command-line processing, programmable api, hot-backup, and
|
80
|
-
input/capture of the stdin/stdout/stderr io streams of remote jobs. to date
|
81
|
-
rq has had no reported runtime failures and is in operation at
|
82
|
-
dozens of research centers around the world. while rq is written in
|
83
|
-
the Ruby programming language, there is no Ruby programming
|
84
|
-
involved in using rq.
|
85
|
-
|
86
122
|
INVOCATION
|
87
123
|
|
88
124
|
the first argument to any rq command is the always the name of the queue
|
@@ -1086,6 +1122,16 @@ DIAGNOSTICS
|
|
1086
1122
|
success : $? == 0
|
1087
1123
|
failure : $? != 0
|
1088
1124
|
|
1125
|
+
URIS
|
1126
|
+
|
1127
|
+
https://github.com/pjotrp/rq - main website
|
1128
|
+
http://www.linuxjournal.com/article/7922
|
1129
|
+
http://rubyforge.org/projects/codeforpeople/ (original)
|
1130
|
+
|
1131
|
+
LICENSE
|
1132
|
+
|
1133
|
+
rq is distributed under the BSD license, see the ./LICENSE file
|
1134
|
+
|
1089
1135
|
CREDITS
|
1090
1136
|
|
1091
1137
|
- kim baugh : patient tester and design input
|
@@ -1098,7 +1144,7 @@ CREDITS
|
|
1098
1144
|
|
1099
1145
|
INSTALL
|
1100
1146
|
|
1101
|
-
|
1147
|
+
gem1.8 install rq-ruby1.8 (see top of page)
|
1102
1148
|
|
1103
1149
|
TEST
|
1104
1150
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.4.
|
1
|
+
3.4.5
|
data/bin/rq
CHANGED
data/ext/extconf.rb
ADDED
data/ext/sqlite.c
ADDED
@@ -0,0 +1,862 @@
|
|
1
|
+
/* --------------------------------------------------------------------------
|
2
|
+
* sqlite.c -- glue code between for the SQLite database and Ruby.
|
3
|
+
* Copyright (C) 2003 Jamis Buck (jgb3@email.byu.edu)
|
4
|
+
* --------------------------------------------------------------------------
|
5
|
+
* The SQLite/Ruby module is free software; you can redistribute it and/or
|
6
|
+
* modify it under the terms of the GNU General Public License as published
|
7
|
+
* by the Free Software Foundation; either version 2 of the License, or
|
8
|
+
* (at your option) any later version.
|
9
|
+
*
|
10
|
+
* The SQLite/Ruby Interface is free software; you can redistribute it and/or
|
11
|
+
* modify it under the terms of the BSD License as published by the Free
|
12
|
+
* Software Foundation. See also the rq LICENSE file.
|
13
|
+
*
|
14
|
+
* The license was changed for older sqlite-1.3.1 in agreement with Jamis Buck.
|
15
|
+
*
|
16
|
+
* The SQLite/Ruby Interface is distributed in the hope that it will be useful,
|
17
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
18
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
19
|
+
* --------------------------------------------------------------------------
|
20
|
+
* This file defines the glue code between the SQLite database engine and
|
21
|
+
* the Ruby interpreter.
|
22
|
+
*
|
23
|
+
* Author: Jamis Buck (jgb3@email.byu.edu)
|
24
|
+
* Date: June 2003
|
25
|
+
* --------------------------------------------------------------------------
|
26
|
+
* NOTE: rq still users the older sqlite2 engine and bindings. This will
|
27
|
+
* change.
|
28
|
+
*/
|
29
|
+
|
30
|
+
#include <sqlite.h>
|
31
|
+
|
32
|
+
#include <stdio.h>
|
33
|
+
#include "ruby.h"
|
34
|
+
#include "stdarg.h"
|
35
|
+
|
36
|
+
/* these constants defines the current version of the SQLite/Ruby module */
|
37
|
+
|
38
|
+
#define LIB_VERSION_MAJOR 1
|
39
|
+
#define LIB_VERSION_MINOR 3
|
40
|
+
#define LIB_VERSION_TINY 1
|
41
|
+
|
42
|
+
/* this wraps the sqlite db pointer. We need to do it this way so that we
|
43
|
+
* can keep the object alive even after we've closed the DB handle, and we
|
44
|
+
* need to be able to tell the difference between an open and a closed DB
|
45
|
+
* handle. This way, if the 'db' member is NULL, we know the database is
|
46
|
+
* not open. */
|
47
|
+
|
48
|
+
typedef struct
|
49
|
+
{
|
50
|
+
sqlite *db;
|
51
|
+
int use_array;
|
52
|
+
} SQLITE_RUBY_DATA;
|
53
|
+
|
54
|
+
|
55
|
+
/* This represents the information for a callback during query execution. */
|
56
|
+
|
57
|
+
typedef struct
|
58
|
+
{
|
59
|
+
VALUE callback; /* the Method object to invoke for each row */
|
60
|
+
VALUE arg; /* the application-defined cookie value to pass to the method */
|
61
|
+
VALUE columns; /* a ruby array of all of the column names */
|
62
|
+
VALUE types; /* a ruby hash of all of the column types (may be null) */
|
63
|
+
int built_columns; /* whether or not the 'columns' member is valid yet */
|
64
|
+
int do_translate; /* whether or not to do type translation */
|
65
|
+
SQLITE_RUBY_DATA *self; /* a reference to the database instance being queried */
|
66
|
+
} SQLITE_RUBY_CALLBACK;
|
67
|
+
|
68
|
+
/* This represents the information for a callback on a custom SQL function */
|
69
|
+
|
70
|
+
typedef struct
|
71
|
+
{
|
72
|
+
VALUE callback; /* the Method object to invoke for each function invocation */
|
73
|
+
VALUE finalize; /* the Method object to invoke when an aggregate function is finished */
|
74
|
+
VALUE arg; /* the app-defined cookie value */
|
75
|
+
} SQLITE_CUSTOM_FUNCTION_CB;
|
76
|
+
|
77
|
+
|
78
|
+
/* global variables for defining the classes, modules, and symbols that are used by this
|
79
|
+
* module. */
|
80
|
+
|
81
|
+
static VALUE mSQLite;
|
82
|
+
static VALUE cSQLite;
|
83
|
+
static VALUE cSQLiteTypeTranslator;
|
84
|
+
static VALUE cSQLiteException;
|
85
|
+
static VALUE cSQLiteQueryContext;
|
86
|
+
static VALUE oSQLiteQueryAbort;
|
87
|
+
static ID idCallMethod;
|
88
|
+
static ID idInstanceEvalMethod;
|
89
|
+
static ID idTranslate;
|
90
|
+
static ID idFields;
|
91
|
+
static ID idFieldsEqual;
|
92
|
+
|
93
|
+
static struct {
|
94
|
+
char *name;
|
95
|
+
VALUE object;
|
96
|
+
} g_sqlite_exceptions[] = {
|
97
|
+
{ "OK", 0 },
|
98
|
+
{ "SQL", 0 },
|
99
|
+
{ "Internal", 0 },
|
100
|
+
{ "Permissions", 0 },
|
101
|
+
{ "Abort", 0 },
|
102
|
+
{ "Busy", 0 },
|
103
|
+
{ "Locked", 0 },
|
104
|
+
{ "OutOfMemory", 0 },
|
105
|
+
{ "ReadOnly", 0 },
|
106
|
+
{ "Interrupt", 0 },
|
107
|
+
{ "IOError", 0 },
|
108
|
+
{ "Corrupt", 0 },
|
109
|
+
{ "NotFound", 0 },
|
110
|
+
{ "Full", 0 },
|
111
|
+
{ "CantOpen", 0 },
|
112
|
+
{ "Protocol", 0 },
|
113
|
+
{ "Empty", 0 },
|
114
|
+
{ "SchemaChanged", 0 },
|
115
|
+
{ "TooBig", 0 },
|
116
|
+
{ "Constraint", 0 },
|
117
|
+
{ "Mismatch", 0 },
|
118
|
+
{ "Misuse", 0 },
|
119
|
+
{ "UnsupportedOSFeature", 0 },
|
120
|
+
{ "Authorization", 0 },
|
121
|
+
{ NULL, 0 }
|
122
|
+
};
|
123
|
+
|
124
|
+
|
125
|
+
/* free's the given database handle when the Ruby engine garbage collects it. */
|
126
|
+
static void static_free_database_handle( SQLITE_RUBY_DATA *hdb );
|
127
|
+
|
128
|
+
/* called when a query is processing another row */
|
129
|
+
static int static_ruby_sqlite_callback( void *pArg, int argc, char **argv, char **columns );
|
130
|
+
|
131
|
+
/* called when a custom SQL function is invoked */
|
132
|
+
static void static_custom_function_callback( sqlite_func* ctx, int argc, const char **argv );
|
133
|
+
|
134
|
+
/* called when a custom aggregate SQL function is invoked */
|
135
|
+
static void static_custom_aggregate_callback( sqlite_func* ctx, int argc, const char **argv );
|
136
|
+
|
137
|
+
/* called when the custom aggregate SQL finalize function is invoked */
|
138
|
+
static void static_custom_finalize_callback( sqlite_func* ctx );
|
139
|
+
|
140
|
+
/* determines whether the given pragma is enabled or not */
|
141
|
+
static int static_pragma_enabled( sqlite* db, const char *pragma );
|
142
|
+
|
143
|
+
/* configure the various exception subclasses used by the module */
|
144
|
+
static void static_configure_exception_classes();
|
145
|
+
|
146
|
+
/* raise an exception */
|
147
|
+
static void static_raise_db_error( int code, char *msg, ... );
|
148
|
+
|
149
|
+
|
150
|
+
static VALUE static_database_new( VALUE klass,
|
151
|
+
VALUE dbname,
|
152
|
+
VALUE mode );
|
153
|
+
|
154
|
+
static VALUE static_database_close( VALUE self );
|
155
|
+
|
156
|
+
static VALUE static_database_exec( VALUE self,
|
157
|
+
VALUE sql,
|
158
|
+
VALUE callback,
|
159
|
+
VALUE parm );
|
160
|
+
|
161
|
+
static VALUE static_last_insert_rowid( VALUE self );
|
162
|
+
|
163
|
+
static VALUE static_changes( VALUE self );
|
164
|
+
|
165
|
+
static VALUE static_interrupt( VALUE self );
|
166
|
+
|
167
|
+
static VALUE static_complete( VALUE self,
|
168
|
+
VALUE sql );
|
169
|
+
|
170
|
+
static VALUE static_create_function( VALUE self,
|
171
|
+
VALUE name,
|
172
|
+
VALUE argc,
|
173
|
+
VALUE callback,
|
174
|
+
VALUE parm );
|
175
|
+
|
176
|
+
static VALUE static_create_aggregate( VALUE self,
|
177
|
+
VALUE name,
|
178
|
+
VALUE argc,
|
179
|
+
VALUE step,
|
180
|
+
VALUE finalize,
|
181
|
+
VALUE parm );
|
182
|
+
|
183
|
+
static VALUE static_aggregate_count( VALUE self );
|
184
|
+
|
185
|
+
static VALUE static_get_properties( VALUE self );
|
186
|
+
|
187
|
+
static VALUE static_is_doing_type_translation( VALUE self );
|
188
|
+
|
189
|
+
static VALUE static_set_type_translation( VALUE self, VALUE value );
|
190
|
+
|
191
|
+
|
192
|
+
static void static_free_database_handle( SQLITE_RUBY_DATA *hdb )
|
193
|
+
{
|
194
|
+
if( hdb )
|
195
|
+
{
|
196
|
+
if( hdb->db )
|
197
|
+
{
|
198
|
+
sqlite_close( hdb->db );
|
199
|
+
hdb->db = NULL;
|
200
|
+
}
|
201
|
+
|
202
|
+
free( hdb );
|
203
|
+
}
|
204
|
+
}
|
205
|
+
|
206
|
+
|
207
|
+
static int static_ruby_sqlite_callback( void *pArg, int argc, char **argv, char **columns )
|
208
|
+
{
|
209
|
+
SQLITE_RUBY_CALLBACK *hook = (SQLITE_RUBY_CALLBACK*)pArg;
|
210
|
+
VALUE result;
|
211
|
+
int i;
|
212
|
+
VALUE rc;
|
213
|
+
|
214
|
+
if( hook->self->use_array )
|
215
|
+
{
|
216
|
+
result = rb_ary_new();
|
217
|
+
}
|
218
|
+
else
|
219
|
+
{
|
220
|
+
result = rb_hash_new();
|
221
|
+
}
|
222
|
+
|
223
|
+
if( !hook->built_columns )
|
224
|
+
{
|
225
|
+
hook->columns = rb_ary_new2( argc );
|
226
|
+
}
|
227
|
+
|
228
|
+
for( i = 0; i < argc; i++ )
|
229
|
+
{
|
230
|
+
VALUE val;
|
231
|
+
|
232
|
+
if( !hook->built_columns )
|
233
|
+
{
|
234
|
+
rb_ary_push( hook->columns, rb_str_new2( columns[i] ) );
|
235
|
+
if( hook->types != Qnil )
|
236
|
+
{
|
237
|
+
VALUE type;
|
238
|
+
char *type_name = columns[ i + argc ];
|
239
|
+
|
240
|
+
type = rb_str_new2( type_name == NULL ? "STRING" : type_name );
|
241
|
+
rb_hash_aset( hook->types, rb_ary_entry( hook->columns, i ), type );
|
242
|
+
rb_hash_aset( hook->types, INT2FIX(i), type );
|
243
|
+
}
|
244
|
+
}
|
245
|
+
|
246
|
+
if( argv != NULL )
|
247
|
+
{
|
248
|
+
val = ( argv[i] ? rb_str_new2( argv[i] ) : Qnil );
|
249
|
+
|
250
|
+
if( hook->do_translate )
|
251
|
+
{
|
252
|
+
VALUE type = rb_hash_aref( hook->types, INT2FIX(i) );
|
253
|
+
val = rb_funcall( cSQLiteTypeTranslator, idTranslate, 2, type, val );
|
254
|
+
}
|
255
|
+
|
256
|
+
if( hook->self->use_array )
|
257
|
+
{
|
258
|
+
rb_ary_store( result, i, val );
|
259
|
+
}
|
260
|
+
else
|
261
|
+
{
|
262
|
+
rb_hash_aset( result, rb_ary_entry( hook->columns, i ), val );
|
263
|
+
rb_hash_aset( result, INT2FIX(i), val );
|
264
|
+
}
|
265
|
+
}
|
266
|
+
}
|
267
|
+
|
268
|
+
if( hook->self->use_array )
|
269
|
+
{
|
270
|
+
if( rb_respond_to( result, idFieldsEqual ) )
|
271
|
+
{
|
272
|
+
rb_funcall( result, idFieldsEqual, 1, hook->columns );
|
273
|
+
}
|
274
|
+
else
|
275
|
+
{
|
276
|
+
rb_iv_set( result, "@fields", hook->columns );
|
277
|
+
if( !rb_respond_to( result, idFields ) )
|
278
|
+
rb_funcall( result, idInstanceEvalMethod, 1, rb_str_new2( "def fields;@fields;end" ) );
|
279
|
+
}
|
280
|
+
}
|
281
|
+
|
282
|
+
hook->built_columns = 1;
|
283
|
+
|
284
|
+
rb_iv_set( result, "@argument", hook->arg );
|
285
|
+
rb_funcall( result, idInstanceEvalMethod, 1, rb_str_new2( "def argument;@argument;end" ) );
|
286
|
+
|
287
|
+
if( hook->types != Qnil )
|
288
|
+
{
|
289
|
+
rb_iv_set( result, "@column_types", hook->types );
|
290
|
+
rb_funcall( result, idInstanceEvalMethod, 1, rb_str_new2( "def column_types;@column_types;end" ) );
|
291
|
+
}
|
292
|
+
|
293
|
+
rc = rb_funcall( hook->callback, idCallMethod, 1, result );
|
294
|
+
|
295
|
+
return ( rc == oSQLiteQueryAbort ? 1 : 0 );
|
296
|
+
}
|
297
|
+
|
298
|
+
|
299
|
+
static void static_custom_function_callback( sqlite_func* ctx, int argc, const char **argv )
|
300
|
+
{
|
301
|
+
SQLITE_CUSTOM_FUNCTION_CB *data;
|
302
|
+
VALUE rc;
|
303
|
+
VALUE args;
|
304
|
+
int i;
|
305
|
+
|
306
|
+
data = (SQLITE_CUSTOM_FUNCTION_CB*)sqlite_user_data( ctx );
|
307
|
+
|
308
|
+
args = rb_ary_new2( argc );
|
309
|
+
rb_ary_push( args, Data_Wrap_Struct( cSQLiteQueryContext, 0, 0, ctx ) );
|
310
|
+
|
311
|
+
for( i = 0; i < argc; i++ )
|
312
|
+
{
|
313
|
+
if( argv[i] )
|
314
|
+
rb_ary_push( args, rb_str_new2( argv[i] ) );
|
315
|
+
else
|
316
|
+
rb_ary_push( args, Qnil );
|
317
|
+
}
|
318
|
+
|
319
|
+
rc = rb_apply( data->callback, idCallMethod, args );
|
320
|
+
|
321
|
+
switch( TYPE(rc) )
|
322
|
+
{
|
323
|
+
case T_STRING:
|
324
|
+
sqlite_set_result_string( ctx, STR2CSTR(rc), RSTRING(rc)->len );
|
325
|
+
break;
|
326
|
+
case T_FIXNUM:
|
327
|
+
sqlite_set_result_int( ctx, FIX2INT(rc) );
|
328
|
+
break;
|
329
|
+
case T_FLOAT:
|
330
|
+
sqlite_set_result_double( ctx, NUM2DBL(rc) );
|
331
|
+
break;
|
332
|
+
}
|
333
|
+
}
|
334
|
+
|
335
|
+
|
336
|
+
static void static_custom_aggregate_callback( sqlite_func* ctx, int argc, const char **argv )
|
337
|
+
{
|
338
|
+
SQLITE_CUSTOM_FUNCTION_CB *data;
|
339
|
+
VALUE args;
|
340
|
+
VALUE *hash;
|
341
|
+
int i;
|
342
|
+
|
343
|
+
hash = (VALUE*)sqlite_aggregate_context( ctx, sizeof( VALUE ) );
|
344
|
+
if( *hash == 0 ) *hash = rb_hash_new();
|
345
|
+
|
346
|
+
data = (SQLITE_CUSTOM_FUNCTION_CB*)sqlite_user_data( ctx );
|
347
|
+
|
348
|
+
args = rb_ary_new2( argc );
|
349
|
+
rb_ary_push( args, Data_Wrap_Struct( cSQLiteQueryContext, 0, 0, ctx ) );
|
350
|
+
|
351
|
+
for( i = 0; i < argc; i++ )
|
352
|
+
{
|
353
|
+
if( argv[i] )
|
354
|
+
rb_ary_push( args, rb_str_new2( argv[i] ) );
|
355
|
+
else
|
356
|
+
rb_ary_push( args, Qnil );
|
357
|
+
}
|
358
|
+
|
359
|
+
rb_apply( data->callback, idCallMethod, args );
|
360
|
+
}
|
361
|
+
|
362
|
+
|
363
|
+
static void static_custom_finalize_callback( sqlite_func* ctx )
|
364
|
+
{
|
365
|
+
SQLITE_CUSTOM_FUNCTION_CB *data;
|
366
|
+
VALUE rc;
|
367
|
+
int i;
|
368
|
+
|
369
|
+
data = (SQLITE_CUSTOM_FUNCTION_CB*)sqlite_user_data( ctx );
|
370
|
+
rc = rb_funcall( data->finalize, idCallMethod, 1, Data_Wrap_Struct( cSQLiteQueryContext, 0, 0, ctx ) );
|
371
|
+
|
372
|
+
switch( TYPE(rc) )
|
373
|
+
{
|
374
|
+
case T_STRING:
|
375
|
+
sqlite_set_result_string( ctx, STR2CSTR(rc), RSTRING(rc)->len );
|
376
|
+
break;
|
377
|
+
case T_FIXNUM:
|
378
|
+
sqlite_set_result_int( ctx, FIX2INT(rc) );
|
379
|
+
break;
|
380
|
+
case T_FLOAT:
|
381
|
+
sqlite_set_result_double( ctx, NUM2DBL(rc) );
|
382
|
+
break;
|
383
|
+
}
|
384
|
+
}
|
385
|
+
|
386
|
+
static int static_pragma_enabled_callback( void *arg, int argc, char **argv, char **columnNames )
|
387
|
+
{
|
388
|
+
int *result = (int*)arg;
|
389
|
+
|
390
|
+
if( ( strcmp( argv[0], "ON" ) == 0 ) || ( strcmp( argv[0], "1" ) == 0 ) )
|
391
|
+
{
|
392
|
+
*result = 1;
|
393
|
+
}
|
394
|
+
else
|
395
|
+
{
|
396
|
+
*result = 0;
|
397
|
+
}
|
398
|
+
|
399
|
+
return SQLITE_OK;
|
400
|
+
}
|
401
|
+
|
402
|
+
static int static_pragma_enabled( sqlite* db, const char *pragma )
|
403
|
+
{
|
404
|
+
int result;
|
405
|
+
int return_code;
|
406
|
+
char sql[ 256 ];
|
407
|
+
char *msg;
|
408
|
+
|
409
|
+
sprintf( sql, "PRAGMA %s", pragma );
|
410
|
+
return_code = sqlite_exec( db, sql, static_pragma_enabled_callback, &result, &msg );
|
411
|
+
|
412
|
+
if( return_code != SQLITE_OK )
|
413
|
+
{
|
414
|
+
VALUE err = rb_str_new2( msg );
|
415
|
+
free( msg );
|
416
|
+
|
417
|
+
static_raise_db_error( return_code,
|
418
|
+
"could not determine status of pragma '%s' (%s)",
|
419
|
+
pragma, STR2CSTR(err) );
|
420
|
+
}
|
421
|
+
|
422
|
+
return result;
|
423
|
+
}
|
424
|
+
|
425
|
+
|
426
|
+
/**
|
427
|
+
* Opens a SQLite database. If the database does not exist, it will be
|
428
|
+
* created. The mode parameter is currently unused and should be set to 0.
|
429
|
+
*/
|
430
|
+
static VALUE static_database_new( VALUE klass,
|
431
|
+
VALUE dbname,
|
432
|
+
VALUE mode )
|
433
|
+
{
|
434
|
+
SQLITE_RUBY_DATA *hdb;
|
435
|
+
sqlite *db;
|
436
|
+
char *s_dbname;
|
437
|
+
int i_mode;
|
438
|
+
char *errmsg;
|
439
|
+
VALUE v_db;
|
440
|
+
|
441
|
+
Check_Type( dbname, T_STRING );
|
442
|
+
Check_Type( mode, T_FIXNUM );
|
443
|
+
|
444
|
+
s_dbname = STR2CSTR(dbname);
|
445
|
+
i_mode = FIX2INT(mode);
|
446
|
+
|
447
|
+
db = sqlite_open( s_dbname, i_mode, &errmsg );
|
448
|
+
if( db == NULL )
|
449
|
+
{
|
450
|
+
VALUE err = rb_str_new2( errmsg );
|
451
|
+
free( errmsg );
|
452
|
+
|
453
|
+
static_raise_db_error( -1, "%s", STR2CSTR( err ) );
|
454
|
+
}
|
455
|
+
|
456
|
+
hdb = ALLOC( SQLITE_RUBY_DATA );
|
457
|
+
hdb->db = db;
|
458
|
+
hdb->use_array = 0; /* default to FALSE */
|
459
|
+
|
460
|
+
v_db = Data_Wrap_Struct( klass, NULL, static_free_database_handle, hdb );
|
461
|
+
|
462
|
+
static_set_type_translation( v_db, Qfalse );
|
463
|
+
|
464
|
+
return v_db;
|
465
|
+
}
|
466
|
+
|
467
|
+
|
468
|
+
/**
|
469
|
+
* Closes an open database. No further methods should be invoked on the database
|
470
|
+
* after closing it.
|
471
|
+
*/
|
472
|
+
static VALUE static_database_close( VALUE self )
|
473
|
+
{
|
474
|
+
SQLITE_RUBY_DATA *hdb;
|
475
|
+
|
476
|
+
Data_Get_Struct( self, SQLITE_RUBY_DATA, hdb );
|
477
|
+
if( hdb->db != NULL )
|
478
|
+
{
|
479
|
+
sqlite_close( hdb->db );
|
480
|
+
hdb->db = NULL;
|
481
|
+
}
|
482
|
+
|
483
|
+
return Qnil;
|
484
|
+
}
|
485
|
+
|
486
|
+
|
487
|
+
/**
|
488
|
+
* This is the base method used for querying the database. You will rarely use this method
|
489
|
+
* directly; rather, you should use the #execute method, instead. The +sql+ parameter is
|
490
|
+
* the text of the sql to execute, +callback+ is a proc object to be invoked for each row
|
491
|
+
* of the result set, and +parm+ is an application-specific cookie value that will be passed to
|
492
|
+
* the +callback+.
|
493
|
+
*/
|
494
|
+
static VALUE static_database_exec( VALUE self,
|
495
|
+
VALUE sql,
|
496
|
+
VALUE callback,
|
497
|
+
VALUE parm )
|
498
|
+
{
|
499
|
+
SQLITE_RUBY_DATA *hdb;
|
500
|
+
SQLITE_RUBY_CALLBACK hook;
|
501
|
+
char *s_sql;
|
502
|
+
int i;
|
503
|
+
char *err = NULL;
|
504
|
+
VALUE v_err;
|
505
|
+
|
506
|
+
Check_Type( sql, T_STRING );
|
507
|
+
s_sql = STR2CSTR(sql);
|
508
|
+
|
509
|
+
Data_Get_Struct( self, SQLITE_RUBY_DATA, hdb );
|
510
|
+
if( hdb->db == NULL )
|
511
|
+
static_raise_db_error( -1, "attempt to access a closed database" );
|
512
|
+
|
513
|
+
hook.callback = callback;
|
514
|
+
hook.arg = parm;
|
515
|
+
hook.built_columns = 0;
|
516
|
+
hook.columns = Qnil;
|
517
|
+
hook.self = hdb;
|
518
|
+
hook.do_translate = ( rb_iv_get( self, "@type_translation" ) == Qtrue );
|
519
|
+
|
520
|
+
if( static_pragma_enabled( hdb->db, "show_datatypes" ) )
|
521
|
+
{
|
522
|
+
hook.types = rb_hash_new();
|
523
|
+
}
|
524
|
+
else
|
525
|
+
{
|
526
|
+
hook.types = Qnil;
|
527
|
+
hook.do_translate = 0; /* show_datatypes must be anbled for type translation */
|
528
|
+
}
|
529
|
+
|
530
|
+
i = sqlite_exec( hdb->db,
|
531
|
+
s_sql,
|
532
|
+
static_ruby_sqlite_callback,
|
533
|
+
&hook,
|
534
|
+
&err );
|
535
|
+
|
536
|
+
if( err != 0 )
|
537
|
+
{
|
538
|
+
v_err = rb_str_new2( err );
|
539
|
+
free( err );
|
540
|
+
}
|
541
|
+
|
542
|
+
switch( i )
|
543
|
+
{
|
544
|
+
case SQLITE_OK:
|
545
|
+
case SQLITE_ABORT:
|
546
|
+
break;
|
547
|
+
default:
|
548
|
+
static_raise_db_error( i, "%s", STR2CSTR(v_err) );
|
549
|
+
}
|
550
|
+
|
551
|
+
return INT2FIX(0);
|
552
|
+
}
|
553
|
+
|
554
|
+
|
555
|
+
/**
|
556
|
+
* Returns the key value of the last inserted row.
|
557
|
+
*/
|
558
|
+
static VALUE static_last_insert_rowid( VALUE self )
|
559
|
+
{
|
560
|
+
SQLITE_RUBY_DATA *hdb;
|
561
|
+
|
562
|
+
Data_Get_Struct( self, SQLITE_RUBY_DATA, hdb );
|
563
|
+
|
564
|
+
if( hdb->db == NULL )
|
565
|
+
static_raise_db_error( -1, "attempt to access a closed database" );
|
566
|
+
|
567
|
+
return INT2FIX( sqlite_last_insert_rowid( hdb->db ) );
|
568
|
+
}
|
569
|
+
|
570
|
+
|
571
|
+
/**
|
572
|
+
* Returns the number of rows that were affected by the last query.
|
573
|
+
*/
|
574
|
+
static VALUE static_changes( VALUE self )
|
575
|
+
{
|
576
|
+
SQLITE_RUBY_DATA *hdb;
|
577
|
+
|
578
|
+
Data_Get_Struct( self, SQLITE_RUBY_DATA, hdb );
|
579
|
+
|
580
|
+
if( hdb->db == NULL )
|
581
|
+
static_raise_db_error( -1, "attempt to access a closed database" );
|
582
|
+
|
583
|
+
return INT2FIX( sqlite_changes( hdb->db ) );
|
584
|
+
}
|
585
|
+
|
586
|
+
|
587
|
+
/**
|
588
|
+
* Interrupts the currently executing query, causing it to abort. If there
|
589
|
+
* is no current query, this does nothing.
|
590
|
+
*/
|
591
|
+
static VALUE static_interrupt( VALUE self )
|
592
|
+
{
|
593
|
+
SQLITE_RUBY_DATA *hdb;
|
594
|
+
|
595
|
+
Data_Get_Struct( self, SQLITE_RUBY_DATA, hdb );
|
596
|
+
|
597
|
+
if( hdb->db == NULL )
|
598
|
+
static_raise_db_error( -1, "attempt to access a closed database" );
|
599
|
+
|
600
|
+
sqlite_interrupt( hdb->db );
|
601
|
+
|
602
|
+
return Qnil;
|
603
|
+
}
|
604
|
+
|
605
|
+
|
606
|
+
/**
|
607
|
+
* Queries whether or not the given SQL statement is complete or not.
|
608
|
+
* This is primarly useful in interactive environments where you are
|
609
|
+
* prompting the user for a query, line-by-line.
|
610
|
+
*/
|
611
|
+
static VALUE static_complete( VALUE self,
|
612
|
+
VALUE sql )
|
613
|
+
{
|
614
|
+
Check_Type( sql, T_STRING );
|
615
|
+
|
616
|
+
return ( sqlite_complete( STR2CSTR( sql ) ) ? Qtrue : Qfalse );
|
617
|
+
}
|
618
|
+
|
619
|
+
/**
|
620
|
+
* Causes the database instance to use an array to represent rows
|
621
|
+
* in query results, if 'boolean' is not Qnil or Qfalse.
|
622
|
+
*/
|
623
|
+
static VALUE static_set_use_array( VALUE self, VALUE boolean )
|
624
|
+
{
|
625
|
+
int use_array = RTEST( boolean );
|
626
|
+
SQLITE_RUBY_DATA *hdb;
|
627
|
+
|
628
|
+
Data_Get_Struct( self, SQLITE_RUBY_DATA, hdb );
|
629
|
+
hdb->use_array = use_array;
|
630
|
+
|
631
|
+
return boolean;
|
632
|
+
}
|
633
|
+
|
634
|
+
/**
|
635
|
+
* Queries the database instance to determine whether or not arrays
|
636
|
+
* are being used to represent rows in query results.
|
637
|
+
*/
|
638
|
+
static VALUE static_is_use_array( VALUE self )
|
639
|
+
{
|
640
|
+
SQLITE_RUBY_DATA *hdb;
|
641
|
+
|
642
|
+
Data_Get_Struct( self, SQLITE_RUBY_DATA, hdb );
|
643
|
+
return ( hdb->use_array ? Qtrue : Qfalse );
|
644
|
+
}
|
645
|
+
|
646
|
+
/**
|
647
|
+
* Defines a custom SQL function with the given name and expected parameter count.
|
648
|
+
* The +callback+ must be a proc object, which will be invoked for each row of the
|
649
|
+
* result set. The +parm+ will be passed to the callback as well.
|
650
|
+
*/
|
651
|
+
static VALUE static_create_function( VALUE self,
|
652
|
+
VALUE name,
|
653
|
+
VALUE argc,
|
654
|
+
VALUE callback,
|
655
|
+
VALUE parm )
|
656
|
+
{
|
657
|
+
SQLITE_RUBY_DATA *hdb;
|
658
|
+
SQLITE_CUSTOM_FUNCTION_CB *data;
|
659
|
+
char *s_name;
|
660
|
+
int i_argc;
|
661
|
+
int rc;
|
662
|
+
|
663
|
+
Data_Get_Struct( self, SQLITE_RUBY_DATA, hdb );
|
664
|
+
s_name = STR2CSTR( name );
|
665
|
+
i_argc = FIX2INT( argc );
|
666
|
+
|
667
|
+
if( hdb->db == NULL )
|
668
|
+
static_raise_db_error( -1, "attempt to access a closed database" );
|
669
|
+
|
670
|
+
data = ALLOC( SQLITE_CUSTOM_FUNCTION_CB );
|
671
|
+
data->callback = callback;
|
672
|
+
data->arg = parm;
|
673
|
+
|
674
|
+
rc = sqlite_create_function( hdb->db,
|
675
|
+
s_name,
|
676
|
+
i_argc,
|
677
|
+
static_custom_function_callback,
|
678
|
+
data );
|
679
|
+
|
680
|
+
if( rc != 0 )
|
681
|
+
static_raise_db_error( rc, "error registering custom function" );
|
682
|
+
|
683
|
+
return Qnil;
|
684
|
+
}
|
685
|
+
|
686
|
+
|
687
|
+
/**
|
688
|
+
* Defines a custom aggregate SQL function with the given name and argument count
|
689
|
+
* (+argc+). The +step+ parameter must be a proc object, which will be called for
|
690
|
+
* each iteration during the processing of the aggregate. The +finalize+ parameter
|
691
|
+
* must also be a proc object, and is called at the end of the processing of the
|
692
|
+
* aggreate. The +parm+ parameter will be passed to both callbacks.
|
693
|
+
*/
|
694
|
+
static VALUE static_create_aggregate( VALUE self,
|
695
|
+
VALUE name,
|
696
|
+
VALUE argc,
|
697
|
+
VALUE step,
|
698
|
+
VALUE finalize,
|
699
|
+
VALUE parm )
|
700
|
+
{
|
701
|
+
SQLITE_RUBY_DATA *hdb;
|
702
|
+
SQLITE_CUSTOM_FUNCTION_CB *data;
|
703
|
+
char *s_name;
|
704
|
+
int i_argc;
|
705
|
+
int rc;
|
706
|
+
|
707
|
+
Data_Get_Struct( self, SQLITE_RUBY_DATA, hdb );
|
708
|
+
s_name = STR2CSTR( name );
|
709
|
+
i_argc = FIX2INT( argc );
|
710
|
+
|
711
|
+
if( hdb->db == NULL )
|
712
|
+
static_raise_db_error( -1, "attempt to access a closed database" );
|
713
|
+
|
714
|
+
data = ALLOC( SQLITE_CUSTOM_FUNCTION_CB );
|
715
|
+
data->callback = step;
|
716
|
+
data->finalize = finalize;
|
717
|
+
data->arg = parm;
|
718
|
+
|
719
|
+
rc = sqlite_create_aggregate( hdb->db,
|
720
|
+
s_name,
|
721
|
+
i_argc,
|
722
|
+
static_custom_aggregate_callback,
|
723
|
+
static_custom_finalize_callback,
|
724
|
+
data );
|
725
|
+
|
726
|
+
if( rc != 0 )
|
727
|
+
static_raise_db_error( rc, "error registering custom function" );
|
728
|
+
|
729
|
+
return Qnil;
|
730
|
+
}
|
731
|
+
|
732
|
+
|
733
|
+
/**
|
734
|
+
* Returns the number of rows in the aggregate context.
|
735
|
+
*/
|
736
|
+
static VALUE static_aggregate_count( VALUE self )
|
737
|
+
{
|
738
|
+
sqlite_func *ctx;
|
739
|
+
|
740
|
+
Data_Get_Struct( self, sqlite_func, ctx );
|
741
|
+
|
742
|
+
return INT2FIX( sqlite_aggregate_count( ctx ) );
|
743
|
+
}
|
744
|
+
|
745
|
+
|
746
|
+
/**
|
747
|
+
* Returns the properties attribute of the aggregate context. This will always
|
748
|
+
* be a Hash object, which your custom functions may use to accumulate information
|
749
|
+
* into.
|
750
|
+
*/
|
751
|
+
static VALUE static_get_properties( VALUE self )
|
752
|
+
{
|
753
|
+
sqlite_func *ctx;
|
754
|
+
|
755
|
+
Data_Get_Struct( self, sqlite_func, ctx );
|
756
|
+
|
757
|
+
return *(VALUE*)sqlite_aggregate_context( ctx, sizeof( VALUE ) );
|
758
|
+
}
|
759
|
+
|
760
|
+
/**
|
761
|
+
* Query whether or not the database is doing automatic type translation between
|
762
|
+
* the SQLite type (always a string) and the corresponding Ruby type.
|
763
|
+
*/
|
764
|
+
static VALUE static_is_doing_type_translation( VALUE self )
|
765
|
+
{
|
766
|
+
return rb_iv_get( self, "@type_translation" );
|
767
|
+
}
|
768
|
+
|
769
|
+
/**
|
770
|
+
* Specify whether or not the database should do automatic type translation.
|
771
|
+
*/
|
772
|
+
static VALUE static_set_type_translation( VALUE self, VALUE value )
|
773
|
+
{
|
774
|
+
if( value == Qnil || value == Qfalse )
|
775
|
+
{
|
776
|
+
value = Qfalse;
|
777
|
+
}
|
778
|
+
else
|
779
|
+
{
|
780
|
+
value = Qtrue;
|
781
|
+
}
|
782
|
+
|
783
|
+
return rb_iv_set( self, "@type_translation", value );
|
784
|
+
}
|
785
|
+
|
786
|
+
static void static_configure_exception_classes()
|
787
|
+
{
|
788
|
+
int i;
|
789
|
+
|
790
|
+
for( i = 1; g_sqlite_exceptions[ i ].name != NULL; i++ )
|
791
|
+
{
|
792
|
+
char name[ 128 ];
|
793
|
+
|
794
|
+
sprintf( name, "%sException", g_sqlite_exceptions[ i ].name );
|
795
|
+
g_sqlite_exceptions[ i ].object = rb_define_class_under( mSQLite, name, cSQLiteException );
|
796
|
+
}
|
797
|
+
}
|
798
|
+
|
799
|
+
static void static_raise_db_error( int code, char *msg, ... )
|
800
|
+
{
|
801
|
+
va_list args;
|
802
|
+
char message[ 2048 ];
|
803
|
+
VALUE exc;
|
804
|
+
|
805
|
+
va_start( args, msg );
|
806
|
+
vsnprintf( message, sizeof( message ), msg, args );
|
807
|
+
va_end( args );
|
808
|
+
|
809
|
+
exc = ( code <= 0 ? cSQLiteException : g_sqlite_exceptions[ code ].object );
|
810
|
+
|
811
|
+
rb_raise( exc, message );
|
812
|
+
}
|
813
|
+
|
814
|
+
void Init__sqlite()
|
815
|
+
{
|
816
|
+
VALUE version;
|
817
|
+
|
818
|
+
mSQLite = rb_define_module( "SQLite" );
|
819
|
+
|
820
|
+
idCallMethod = rb_intern( "call" );
|
821
|
+
idInstanceEvalMethod = rb_intern("instance_eval");
|
822
|
+
idTranslate = rb_intern("translate");
|
823
|
+
idFields = rb_intern("fields");
|
824
|
+
idFieldsEqual = rb_intern("fields=");
|
825
|
+
|
826
|
+
cSQLite = rb_define_class_under( mSQLite, "Database", rb_cObject );
|
827
|
+
cSQLiteException = rb_define_class_under( mSQLite, "DatabaseException", rb_eStandardError );
|
828
|
+
cSQLiteQueryContext = rb_define_class_under( mSQLite, "QueryContext", rb_cHash );
|
829
|
+
cSQLiteTypeTranslator = rb_define_class_under( mSQLite, "TypeTranslator", rb_cObject );
|
830
|
+
|
831
|
+
static_configure_exception_classes();
|
832
|
+
|
833
|
+
rb_define_singleton_method( cSQLite, "new", static_database_new, 2 );
|
834
|
+
|
835
|
+
rb_define_method( cSQLite, "close", static_database_close, 0 );
|
836
|
+
rb_define_method( cSQLite, "exec", static_database_exec, 3 );
|
837
|
+
rb_define_method( cSQLite, "last_insert_rowid", static_last_insert_rowid, 0 );
|
838
|
+
rb_define_method( cSQLite, "changes", static_changes, 0 );
|
839
|
+
rb_define_method( cSQLite, "interrupt", static_interrupt, 0 );
|
840
|
+
rb_define_method( cSQLite, "complete?", static_complete, 1 );
|
841
|
+
rb_define_method( cSQLite, "create_function", static_create_function, 4 );
|
842
|
+
rb_define_method( cSQLite, "create_aggregate", static_create_aggregate, 5 );
|
843
|
+
rb_define_method( cSQLite, "type_translation?", static_is_doing_type_translation, 0 );
|
844
|
+
rb_define_method( cSQLite, "type_translation=", static_set_type_translation, 1 );
|
845
|
+
rb_define_method( cSQLite, "use_array=", static_set_use_array, 1 );
|
846
|
+
rb_define_method( cSQLite, "use_array?", static_is_use_array, 0 );
|
847
|
+
|
848
|
+
rb_define_const( cSQLite, "VERSION", rb_str_new2( sqlite_libversion() ) );
|
849
|
+
rb_define_const( cSQLite, "ENCODING", rb_str_new2( sqlite_libencoding() ) );
|
850
|
+
|
851
|
+
version = rb_ary_new();
|
852
|
+
rb_ary_push( version, INT2FIX( LIB_VERSION_MAJOR ) );
|
853
|
+
rb_ary_push( version, INT2FIX( LIB_VERSION_MINOR ) );
|
854
|
+
rb_ary_push( version, INT2FIX( LIB_VERSION_TINY ) );
|
855
|
+
rb_define_const( mSQLite, "LIB_VERSION", version );
|
856
|
+
|
857
|
+
oSQLiteQueryAbort = rb_funcall( rb_cObject, rb_intern( "new" ), 0 );
|
858
|
+
rb_define_const( mSQLite, "ABORT", oSQLiteQueryAbort );
|
859
|
+
|
860
|
+
rb_define_method( cSQLiteQueryContext, "aggregate_count", static_aggregate_count, 0 );
|
861
|
+
rb_define_method( cSQLiteQueryContext, "properties", static_get_properties, 0 );
|
862
|
+
}
|