rq-ruby1.8 3.4.3 → 3.4.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|