do_sqlite3 0.10.0-java → 0.10.1-java
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/ChangeLog.markdown +32 -0
- data/LICENSE +1 -1
- data/README.markdown +101 -3
- data/Rakefile +56 -9
- data/ext/do_sqlite3/compat.h +55 -0
- data/ext/do_sqlite3/do_sqlite3.c +793 -0
- data/ext/do_sqlite3/error.h +106 -0
- data/ext/do_sqlite3/extconf.rb +26 -0
- data/lib/do_sqlite3.rb +19 -11
- data/lib/do_sqlite3/do_sqlite3.jar +0 -0
- data/lib/do_sqlite3/version.rb +1 -1
- data/spec/command_spec.rb +1 -1
- data/spec/connection_spec.rb +8 -6
- data/spec/reader_spec.rb +1 -1
- data/spec/result_spec.rb +3 -3
- data/spec/spec_helper.rb +19 -32
- data/spec/typecast/array_spec.rb +1 -1
- data/spec/typecast/bigdecimal_spec.rb +1 -1
- data/spec/typecast/boolean_spec.rb +1 -1
- data/spec/typecast/byte_array_spec.rb +1 -2
- data/spec/typecast/class_spec.rb +1 -1
- data/spec/typecast/date_spec.rb +1 -1
- data/spec/typecast/datetime_spec.rb +1 -1
- data/spec/typecast/float_spec.rb +2 -2
- data/spec/typecast/integer_spec.rb +1 -1
- data/spec/typecast/nil_spec.rb +4 -4
- data/spec/typecast/other_spec.rb +8 -0
- data/spec/typecast/range_spec.rb +1 -1
- data/spec/typecast/string_spec.rb +1 -1
- data/spec/typecast/time_spec.rb +1 -1
- data/tasks/compile.rake +64 -0
- data/tasks/release.rake +12 -71
- data/tasks/retrieve.rake +1 -1
- data/tasks/spec.rake +19 -15
- metadata +118 -107
- data/HISTORY.markdown +0 -22
- data/Manifest.txt +0 -31
- data/lib/do_sqlite3_ext.jar +0 -0
- data/spec/lib/rspec_immediate_feedback_formatter.rb +0 -3
- data/tasks/gem.rake +0 -8
- data/tasks/install.rake +0 -15
- data/tasks/native.rake +0 -35
data/ChangeLog.markdown
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
## 0.10.1 (unreleased, in git)
|
2
|
+
|
3
|
+
* Support for Ruby 1.8 and 1.9 on Windows.
|
4
|
+
* Switch to Jeweler for Gem building tasks (this change may be temporary).
|
5
|
+
* Switch to using Bacon for running specs: This should make specs friendlier to
|
6
|
+
new Ruby implementations that are not yet 100% MRI-compatible, and in turn,
|
7
|
+
prepared the road for our own IronRuby and MacRuby support.
|
8
|
+
* Switch to the newly added rake-compiler `JavaExtensionTask` for compiling
|
9
|
+
JRuby extensions, instead of our (broken) home-grown solution.
|
10
|
+
|
11
|
+
## 0.10.0 2009-09-15
|
12
|
+
* Improvements
|
13
|
+
* JRuby Support (using *do_jdbc*)
|
14
|
+
|
15
|
+
## 0.9.12 2009-05-17
|
16
|
+
* Improvements
|
17
|
+
* rake-compiler for Windows support
|
18
|
+
|
19
|
+
## 0.9.11 2009-01-19
|
20
|
+
* Improvements
|
21
|
+
* Ruby 1.9 support
|
22
|
+
* Fixes
|
23
|
+
* Fix Windows gem
|
24
|
+
|
25
|
+
## 0.9.9 2008-11-27
|
26
|
+
* Improvements
|
27
|
+
* Added cross compile rake tasks for Windows gems [Jonathan Stott, Luis Lavena]
|
28
|
+
* Added initial support for Ruby 1.9 [John Harrison]
|
29
|
+
|
30
|
+
* Bug Fixes
|
31
|
+
* Removed sqlite3.dll from source gem [Dan Kubb]
|
32
|
+
* Removed hard coded .bundle from source [Dirkjan Bussink]
|
data/LICENSE
CHANGED
data/README.markdown
CHANGED
@@ -1,4 +1,102 @@
|
|
1
|
-
do_sqlite3
|
2
|
-
==========
|
1
|
+
# do_sqlite3
|
3
2
|
|
4
|
-
|
3
|
+
* <http://dataobjects.info>
|
4
|
+
|
5
|
+
## Description
|
6
|
+
|
7
|
+
A SQLite3 driver for DataObjects.
|
8
|
+
|
9
|
+
## Features/Problems
|
10
|
+
|
11
|
+
This driver implements the DataObjects API for the SQLite3 relational database.
|
12
|
+
|
13
|
+
## Synopsis
|
14
|
+
|
15
|
+
An example of usage:
|
16
|
+
|
17
|
+
@connection = DataObjects::Connection.new("sqlite3://employees")
|
18
|
+
@reader = @connection.create_command('SELECT * FROM users').execute_reader
|
19
|
+
@reader.next!
|
20
|
+
|
21
|
+
In the future, the `Connection` constructor will be able to be passed either a
|
22
|
+
DataObjects-style URL or JDBC style URL, when using do\_sqlite3 on JRuby.
|
23
|
+
However, this feature is not currently working reliably and is a known issue.
|
24
|
+
|
25
|
+
## Requirements
|
26
|
+
|
27
|
+
This driver is provided for the following platforms:
|
28
|
+
* Ruby MRI (1.8.6/7), 1.9: tested on Linux, Mac OS X and Windows platforms.
|
29
|
+
* JRuby 1.3.1 + (1.4+ recommended).
|
30
|
+
* Rubinius (experimental).
|
31
|
+
|
32
|
+
Additionally you should have the following prerequisites:
|
33
|
+
* `data_objects` gem
|
34
|
+
* `do_jdbc` gem (shared library), if running on JRuby.
|
35
|
+
|
36
|
+
## Install
|
37
|
+
|
38
|
+
To install the gem:
|
39
|
+
|
40
|
+
gem install do_sqlite3
|
41
|
+
|
42
|
+
To compile and install from source:
|
43
|
+
|
44
|
+
* Install rake-compiler: `gem install rake-compiler`.
|
45
|
+
|
46
|
+
* For MRI/Rubinius extensions:
|
47
|
+
* Install the `gcc` compiler. On OS X, you should install XCode tools. On
|
48
|
+
Ubuntu, run `apt-get install build-essential`.
|
49
|
+
* Install Ruby and SQLite3.
|
50
|
+
* Install the Ruby and SQLite3 development headers.
|
51
|
+
* On Debian-Linux distributions, you can install the following packages
|
52
|
+
with `apt`: `ruby-dev` `libsqlite3-dev`.
|
53
|
+
* If you want to cross-compile for Windows:
|
54
|
+
* Install MinGW:
|
55
|
+
* On Debian-Linux distributions, you can install the following package
|
56
|
+
with `apt`: `mingw32`.
|
57
|
+
* On OS X, this can install the following package with MacPorts: `i386-mingw32-gcc`.
|
58
|
+
* Run `rake-compiler cross-ruby`.
|
59
|
+
* Run `rake-compiler update-config`.
|
60
|
+
|
61
|
+
* For JRuby extensions:
|
62
|
+
* Install the Java Development Kit (provided if you are
|
63
|
+
on a recent version of Mac OS X) from <http://java.sun.com>.
|
64
|
+
* Install a recent version of JRuby. Ensure `jruby` is in your `PATH` and/or
|
65
|
+
you have configured the `JRUBY_HOME` environment variable to point to your
|
66
|
+
JRuby installation.
|
67
|
+
* Install `data_objects` and `do_jdbc` with `jruby -S rake install`.
|
68
|
+
|
69
|
+
* Then, install this driver with `(jruby -S) rake install`.
|
70
|
+
|
71
|
+
For more information, see the SQLite3 driver wiki page:
|
72
|
+
<http://wiki.github.com/datamapper/do/sqlite3>.
|
73
|
+
|
74
|
+
## Developers
|
75
|
+
|
76
|
+
Follow the above installation instructions. Additionally, you'll need:
|
77
|
+
* `bacon` gem for running specs.
|
78
|
+
* `YARD` gem for generating documentation.
|
79
|
+
|
80
|
+
See the DataObjects wiki for more comprehensive information on installing and
|
81
|
+
contributing to the JRuby-variant of this driver:
|
82
|
+
<http://wiki.github.com/datamapper/do/jruby>.
|
83
|
+
|
84
|
+
To run specs:
|
85
|
+
|
86
|
+
rake spec
|
87
|
+
|
88
|
+
To run specs without compiling extensions first:
|
89
|
+
|
90
|
+
rake spec_no_compile
|
91
|
+
|
92
|
+
To run individual specs:
|
93
|
+
|
94
|
+
rake spec TEST=spec/connection_spec.rb
|
95
|
+
|
96
|
+
(Note that the `rake` task uses a `TEST` parameter, not `SPEC`. This is because
|
97
|
+
the `Rake::TestTask` is used for executing the Bacon specs).
|
98
|
+
|
99
|
+
## License
|
100
|
+
|
101
|
+
This code is licensed under an **MIT (X11) License**. Please see the
|
102
|
+
accompanying `LICENSE` file.
|
data/Rakefile
CHANGED
@@ -1,16 +1,63 @@
|
|
1
|
+
require 'pathname'
|
1
2
|
require 'rubygems'
|
2
3
|
require 'rake'
|
3
4
|
require 'rake/clean'
|
4
5
|
|
5
|
-
|
6
|
-
|
6
|
+
ROOT = Pathname(__FILE__).dirname.expand_path
|
7
|
+
|
8
|
+
require ROOT + 'lib/do_sqlite3/version'
|
9
|
+
|
10
|
+
JRUBY = RUBY_PLATFORM =~ /java/
|
11
|
+
IRONRUBY = defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ironruby'
|
12
|
+
WINDOWS = Gem.win_platform? || (JRUBY && ENV_JAVA['os.name'] =~ /windows/i)
|
13
|
+
SUDO = WINDOWS ? '' : ('sudo' unless ENV['SUDOLESS'])
|
14
|
+
BINARY_VERSION = '3_6_21'
|
15
|
+
|
16
|
+
CLEAN.include(%w[ {tmp,pkg}/ **/*.{o,so,bundle,jar,log,a,gem,dSYM,obj,pdb,exp,DS_Store,rbc,db} ext/do_sqlite3/Makefile ext-java/target ])
|
17
|
+
|
18
|
+
begin
|
19
|
+
gem 'jeweler', '~> 1.4'
|
20
|
+
require 'jeweler'
|
21
|
+
|
22
|
+
Jeweler::Tasks.new do |gem|
|
23
|
+
gem.name = 'do_sqlite3'
|
24
|
+
gem.version = DataObjects::Sqlite3::VERSION
|
25
|
+
gem.summary = 'DataObjects Sqlite3 Driver'
|
26
|
+
gem.description = 'Implements the DataObjects API for Sqlite3'
|
27
|
+
gem.platform = Gem::Platform::RUBY
|
28
|
+
gem.files = FileList['lib/**/*.rb', 'spec/**/*.rb', 'tasks/**/*.rake',
|
29
|
+
'ext/**/*.{rb,c,h}', 'LICENSE', 'Rakefile',
|
30
|
+
'*.{markdown,rdoc,txt,yml}']
|
31
|
+
gem.extra_rdoc_files = FileList['README*', 'ChangeLog*', 'LICENSE']
|
32
|
+
gem.test_files = FileList['spec/**/*.rb']
|
33
|
+
|
34
|
+
# rake-compiler should generate gemspecs for other platforms (e.g. 'java')
|
35
|
+
# and modify dependencies and extensions appropriately
|
36
|
+
gem.extensions << 'ext/do_sqlite3/extconf.rb'
|
37
|
+
|
38
|
+
gem.add_dependency 'data_objects', DataObjects::Sqlite3::VERSION
|
39
|
+
|
40
|
+
gem.add_development_dependency 'bacon', '~>1.1'
|
41
|
+
gem.add_development_dependency 'rake-compiler', '~>0.7'
|
42
|
+
|
43
|
+
gem.has_rdoc = false
|
44
|
+
gem.rubyforge_project = 'dorb'
|
45
|
+
gem.authors = [ 'Dirkjan Bussink' ]
|
46
|
+
gem.email = 'd.bussink@gmail.com'
|
47
|
+
end
|
7
48
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
49
|
+
if JRUBY
|
50
|
+
Rake::Task['build'].clear_actions if Rake::Task.task_defined?('build')
|
51
|
+
Rake::Task['install'].clear_actions if Rake::Task.task_defined?('install')
|
52
|
+
task :build => [ :java, :gem ]
|
53
|
+
task :install do
|
54
|
+
sh "#{Config::CONFIG['RUBY_INSTALL_NAME']} -S gem install pkg/do_sqlite3-#{DataObjects::Sqlite3::VERSION}-java.gem"
|
55
|
+
end
|
56
|
+
end
|
13
57
|
|
14
|
-
|
58
|
+
Jeweler::GemcutterTasks.new
|
15
59
|
|
16
|
-
|
60
|
+
FileList['tasks/**/*.rake'].each { |task| import task }
|
61
|
+
rescue LoadError
|
62
|
+
puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler'
|
63
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#ifndef RUBY_COMPAT_H
|
2
|
+
#define RUBY_COMPAT_H
|
3
|
+
|
4
|
+
/*
|
5
|
+
* Rules for better ruby C extensions:
|
6
|
+
*
|
7
|
+
* Never use the R<TYPE> macros directly, always use R<TYPE>_<FIELD>
|
8
|
+
*
|
9
|
+
* Never compare with RBASIC(obj)->klass, always use
|
10
|
+
* rb_obj_is_instance_of()
|
11
|
+
*
|
12
|
+
* Never use RHASH(obj)->tbl or RHASH_TBL().
|
13
|
+
*
|
14
|
+
*/
|
15
|
+
|
16
|
+
|
17
|
+
// Array
|
18
|
+
#ifndef RARRAY_PTR
|
19
|
+
#define RARRAY_PTR(obj) RARRAY(obj)->ptr
|
20
|
+
#endif
|
21
|
+
|
22
|
+
#ifndef RARRAY_LEN
|
23
|
+
#define RARRAY_LEN(obj) RARRAY(obj)->len
|
24
|
+
#endif
|
25
|
+
|
26
|
+
// String
|
27
|
+
#ifndef RSTRING_PTR
|
28
|
+
#define RSTRING_PTR(obj) RSTRING(obj)->ptr
|
29
|
+
#endif
|
30
|
+
|
31
|
+
#ifndef RSTRING_LEN
|
32
|
+
#define RSTRING_LEN(obj) RSTRING(obj)->len
|
33
|
+
#endif
|
34
|
+
|
35
|
+
#ifndef rb_str_ptr
|
36
|
+
#define rb_str_ptr(str) RSTRING_PTR(str)
|
37
|
+
#endif
|
38
|
+
|
39
|
+
#ifndef rb_str_ptr_readonly
|
40
|
+
#define rb_str_ptr_readonly(str) RSTRING_PTR(str)
|
41
|
+
#endif
|
42
|
+
|
43
|
+
#ifndef rb_str_flush
|
44
|
+
#define rb_str_flush(str)
|
45
|
+
#endif
|
46
|
+
|
47
|
+
#ifndef rb_str_update
|
48
|
+
#define rb_str_update(str)
|
49
|
+
#endif
|
50
|
+
|
51
|
+
#ifndef rb_str_len
|
52
|
+
#define rb_str_len(str) RSTRING_LEN(str)
|
53
|
+
#endif
|
54
|
+
|
55
|
+
#endif
|
@@ -0,0 +1,793 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <string.h>
|
3
|
+
#include <math.h>
|
4
|
+
#include <time.h>
|
5
|
+
#include <locale.h>
|
6
|
+
#include <sqlite3.h>
|
7
|
+
#include "compat.h"
|
8
|
+
#include "error.h"
|
9
|
+
|
10
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
11
|
+
#include <ruby/encoding.h>
|
12
|
+
|
13
|
+
#define DO_STR_NEW2(str, encoding) \
|
14
|
+
({ \
|
15
|
+
VALUE _string = rb_str_new2((const char *)str); \
|
16
|
+
if(encoding != -1) { \
|
17
|
+
rb_enc_associate_index(_string, encoding); \
|
18
|
+
} \
|
19
|
+
_string; \
|
20
|
+
})
|
21
|
+
|
22
|
+
#define DO_STR_NEW(str, len, encoding) \
|
23
|
+
({ \
|
24
|
+
VALUE _string = rb_str_new((const char *)str, (long)len); \
|
25
|
+
if(encoding != -1) { \
|
26
|
+
rb_enc_associate_index(_string, encoding); \
|
27
|
+
} \
|
28
|
+
_string; \
|
29
|
+
})
|
30
|
+
|
31
|
+
#else
|
32
|
+
|
33
|
+
#define DO_STR_NEW2(str, encoding) \
|
34
|
+
rb_str_new2((const char *)str)
|
35
|
+
|
36
|
+
#define DO_STR_NEW(str, len, encoding) \
|
37
|
+
rb_str_new((const char *)str, (long)len)
|
38
|
+
#endif
|
39
|
+
|
40
|
+
|
41
|
+
#define ID_CONST_GET rb_intern("const_get")
|
42
|
+
#define ID_PATH rb_intern("path")
|
43
|
+
#define ID_NEW rb_intern("new")
|
44
|
+
#define ID_ESCAPE rb_intern("escape_sql")
|
45
|
+
#define ID_QUERY rb_intern("query")
|
46
|
+
|
47
|
+
#define RUBY_CLASS(name) rb_const_get(rb_cObject, rb_intern(name))
|
48
|
+
#define CONST_GET(scope, constant) (rb_funcall(scope, ID_CONST_GET, 1, rb_str_new2(constant)))
|
49
|
+
#define SQLITE3_CLASS(klass, parent) (rb_define_class_under(mSqlite3, klass, parent))
|
50
|
+
|
51
|
+
#ifdef _WIN32
|
52
|
+
#define do_int64 signed __int64
|
53
|
+
#else
|
54
|
+
#define do_int64 signed long long int
|
55
|
+
#endif
|
56
|
+
|
57
|
+
#ifndef HAVE_SQLITE3_PREPARE_V2
|
58
|
+
#define sqlite3_prepare_v2 sqlite3_prepare
|
59
|
+
#endif
|
60
|
+
|
61
|
+
// To store rb_intern values
|
62
|
+
static ID ID_NEW_DATE;
|
63
|
+
static ID ID_RATIONAL;
|
64
|
+
static ID ID_LOGGER;
|
65
|
+
static ID ID_DEBUG;
|
66
|
+
static ID ID_LEVEL;
|
67
|
+
|
68
|
+
static VALUE mExtlib;
|
69
|
+
|
70
|
+
static VALUE mDO;
|
71
|
+
static VALUE cDO_Quoting;
|
72
|
+
static VALUE cDO_Connection;
|
73
|
+
static VALUE cDO_Command;
|
74
|
+
static VALUE cDO_Result;
|
75
|
+
static VALUE cDO_Reader;
|
76
|
+
|
77
|
+
static VALUE rb_cDate;
|
78
|
+
static VALUE rb_cDateTime;
|
79
|
+
static VALUE rb_cBigDecimal;
|
80
|
+
static VALUE rb_cByteArray;
|
81
|
+
|
82
|
+
static VALUE mSqlite3;
|
83
|
+
static VALUE cConnection;
|
84
|
+
static VALUE cCommand;
|
85
|
+
static VALUE cResult;
|
86
|
+
static VALUE cReader;
|
87
|
+
|
88
|
+
static VALUE eArgumentError;
|
89
|
+
static VALUE eConnectionError;
|
90
|
+
static VALUE eDataError;
|
91
|
+
|
92
|
+
static VALUE OPEN_FLAG_READONLY;
|
93
|
+
static VALUE OPEN_FLAG_READWRITE;
|
94
|
+
static VALUE OPEN_FLAG_CREATE;
|
95
|
+
static VALUE OPEN_FLAG_NO_MUTEX;
|
96
|
+
static VALUE OPEN_FLAG_FULL_MUTEX;
|
97
|
+
|
98
|
+
// Find the greatest common denominator and reduce the provided numerator and denominator.
|
99
|
+
// This replaces calles to Rational.reduce! which does the same thing, but really slowly.
|
100
|
+
static void reduce( do_int64 *numerator, do_int64 *denominator ) {
|
101
|
+
do_int64 a, b, c = 0;
|
102
|
+
a = *numerator;
|
103
|
+
b = *denominator;
|
104
|
+
while ( a != 0 ) {
|
105
|
+
c = a; a = b % a; b = c;
|
106
|
+
}
|
107
|
+
*numerator = *numerator / b;
|
108
|
+
*denominator = *denominator / b;
|
109
|
+
}
|
110
|
+
|
111
|
+
// Generate the date integer which Date.civil_to_jd returns
|
112
|
+
static int jd_from_date(int year, int month, int day) {
|
113
|
+
int a, b;
|
114
|
+
if ( month <= 2 ) {
|
115
|
+
year -= 1;
|
116
|
+
month += 12;
|
117
|
+
}
|
118
|
+
a = year / 100;
|
119
|
+
b = 2 - a + (a / 4);
|
120
|
+
return (int) (floor(365.25 * (year + 4716)) + floor(30.6001 * (month + 1)) + day + b - 1524);
|
121
|
+
}
|
122
|
+
|
123
|
+
static void data_objects_debug(VALUE string, struct timeval* start) {
|
124
|
+
struct timeval stop;
|
125
|
+
char *message;
|
126
|
+
|
127
|
+
const char *query = rb_str_ptr_readonly(string);
|
128
|
+
size_t length = rb_str_len(string);
|
129
|
+
char total_time[32];
|
130
|
+
do_int64 duration = 0;
|
131
|
+
|
132
|
+
VALUE logger = rb_funcall(mSqlite3, ID_LOGGER, 0);
|
133
|
+
int log_level = NUM2INT(rb_funcall(logger, ID_LEVEL, 0));
|
134
|
+
|
135
|
+
if (0 == log_level) {
|
136
|
+
gettimeofday(&stop, NULL);
|
137
|
+
|
138
|
+
duration = (stop.tv_sec - start->tv_sec) * 1000000 + stop.tv_usec - start->tv_usec;
|
139
|
+
|
140
|
+
snprintf(total_time, 32, "%.6f", duration / 1000000.0);
|
141
|
+
message = (char *)calloc(length + strlen(total_time) + 4, sizeof(char));
|
142
|
+
snprintf(message, length + strlen(total_time) + 4, "(%s) %s", total_time, query);
|
143
|
+
rb_funcall(logger, ID_DEBUG, 1, rb_str_new(message, length + strlen(total_time) + 3));
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
static void raise_error(VALUE self, sqlite3 *result, VALUE query) {
|
148
|
+
VALUE exception;
|
149
|
+
const char *message = sqlite3_errmsg(result);
|
150
|
+
const char *exception_type = "SQLError";
|
151
|
+
int sqlite3_errno = sqlite3_errcode(result);
|
152
|
+
|
153
|
+
struct errcodes *errs;
|
154
|
+
|
155
|
+
for (errs = errors; errs->error_name; errs++) {
|
156
|
+
if(errs->error_no == sqlite3_errno) {
|
157
|
+
exception_type = errs->exception;
|
158
|
+
break;
|
159
|
+
}
|
160
|
+
}
|
161
|
+
|
162
|
+
|
163
|
+
VALUE uri = rb_funcall(rb_iv_get(self, "@connection"), rb_intern("to_s"), 0);
|
164
|
+
|
165
|
+
exception = rb_funcall(CONST_GET(mDO, exception_type), ID_NEW, 5,
|
166
|
+
rb_str_new2(message),
|
167
|
+
INT2NUM(sqlite3_errno),
|
168
|
+
rb_str_new2(""),
|
169
|
+
query,
|
170
|
+
uri);
|
171
|
+
rb_exc_raise(exception);
|
172
|
+
}
|
173
|
+
|
174
|
+
static VALUE parse_date(char *date) {
|
175
|
+
int year, month, day;
|
176
|
+
int jd, ajd;
|
177
|
+
VALUE rational;
|
178
|
+
|
179
|
+
sscanf(date, "%4d-%2d-%2d", &year, &month, &day);
|
180
|
+
|
181
|
+
jd = jd_from_date(year, month, day);
|
182
|
+
|
183
|
+
// Math from Date.jd_to_ajd
|
184
|
+
ajd = jd * 2 - 1;
|
185
|
+
rational = rb_funcall(rb_mKernel, ID_RATIONAL, 2, INT2NUM(ajd), INT2NUM(2));
|
186
|
+
return rb_funcall(rb_cDate, ID_NEW_DATE, 3, rational, INT2NUM(0), INT2NUM(2299161));
|
187
|
+
}
|
188
|
+
|
189
|
+
// Creates a Rational for use as a Timezone offset to be passed to DateTime.new!
|
190
|
+
static VALUE seconds_to_offset(do_int64 num) {
|
191
|
+
do_int64 den = 86400;
|
192
|
+
reduce(&num, &den);
|
193
|
+
return rb_funcall(rb_mKernel, ID_RATIONAL, 2, rb_ll2inum(num), rb_ll2inum(den));
|
194
|
+
}
|
195
|
+
|
196
|
+
static VALUE timezone_to_offset(int hour_offset, int minute_offset) {
|
197
|
+
do_int64 seconds = 0;
|
198
|
+
|
199
|
+
seconds += hour_offset * 3600;
|
200
|
+
seconds += minute_offset * 60;
|
201
|
+
|
202
|
+
return seconds_to_offset(seconds);
|
203
|
+
}
|
204
|
+
|
205
|
+
static VALUE parse_date_time(char *date) {
|
206
|
+
VALUE ajd, offset;
|
207
|
+
|
208
|
+
int year, month, day, hour, min, sec, usec, hour_offset, minute_offset;
|
209
|
+
int jd;
|
210
|
+
do_int64 num, den;
|
211
|
+
|
212
|
+
long int gmt_offset;
|
213
|
+
int is_dst;
|
214
|
+
|
215
|
+
time_t rawtime;
|
216
|
+
struct tm * timeinfo;
|
217
|
+
|
218
|
+
int tokens_read, max_tokens;
|
219
|
+
|
220
|
+
if ( strcmp(date, "") == 0 ) {
|
221
|
+
return Qnil;
|
222
|
+
}
|
223
|
+
|
224
|
+
if (0 != strchr(date, '.')) {
|
225
|
+
// This is a datetime with sub-second precision
|
226
|
+
tokens_read = sscanf(date, "%4d-%2d-%2d%*c%2d:%2d:%2d.%d%3d:%2d", &year, &month, &day, &hour, &min, &sec, &usec, &hour_offset, &minute_offset);
|
227
|
+
max_tokens = 9;
|
228
|
+
} else {
|
229
|
+
// This is a datetime second precision
|
230
|
+
tokens_read = sscanf(date, "%4d-%2d-%2d%*c%2d:%2d:%2d%3d:%2d", &year, &month, &day, &hour, &min, &sec, &hour_offset, &minute_offset);
|
231
|
+
max_tokens = 8;
|
232
|
+
}
|
233
|
+
|
234
|
+
if (max_tokens == tokens_read) {
|
235
|
+
// We read the Date, Time, and Timezone info
|
236
|
+
minute_offset *= hour_offset < 0 ? -1 : 1;
|
237
|
+
} else if ((max_tokens - 1) == tokens_read) {
|
238
|
+
// We read the Date and Time, but no Minute Offset
|
239
|
+
minute_offset = 0;
|
240
|
+
} else if (tokens_read == 3 || tokens_read >= (max_tokens - 3)) {
|
241
|
+
if (tokens_read == 3) {
|
242
|
+
hour = 0;
|
243
|
+
min = 0;
|
244
|
+
hour_offset = 0;
|
245
|
+
minute_offset = 0;
|
246
|
+
sec = 0;
|
247
|
+
}
|
248
|
+
// We read the Date and Time, default to the current locale's offset
|
249
|
+
|
250
|
+
// Get localtime
|
251
|
+
time(&rawtime);
|
252
|
+
timeinfo = localtime(&rawtime);
|
253
|
+
|
254
|
+
is_dst = timeinfo->tm_isdst * 3600;
|
255
|
+
|
256
|
+
// Reset to GM Time
|
257
|
+
timeinfo = gmtime(&rawtime);
|
258
|
+
|
259
|
+
gmt_offset = mktime(timeinfo) - rawtime;
|
260
|
+
|
261
|
+
if ( is_dst > 0 )
|
262
|
+
gmt_offset -= is_dst;
|
263
|
+
|
264
|
+
hour_offset = -((int)gmt_offset / 3600);
|
265
|
+
minute_offset = -((int)gmt_offset % 3600 / 60);
|
266
|
+
|
267
|
+
} else {
|
268
|
+
// Something went terribly wrong
|
269
|
+
rb_raise(eDataError, "Couldn't parse date: %s", date);
|
270
|
+
}
|
271
|
+
|
272
|
+
jd = jd_from_date(year, month, day);
|
273
|
+
|
274
|
+
// Generate ajd with fractional days for the time
|
275
|
+
// Extracted from Date#jd_to_ajd, Date#day_fraction_to_time, and Rational#+ and #-
|
276
|
+
num = (hour * 1440) + (min * 24);
|
277
|
+
|
278
|
+
// Modify the numerator so when we apply the timezone everything works out
|
279
|
+
num -= (hour_offset * 1440) + (minute_offset * 24);
|
280
|
+
|
281
|
+
den = (24 * 1440);
|
282
|
+
reduce(&num, &den);
|
283
|
+
|
284
|
+
num = (num * 86400) + (sec * den);
|
285
|
+
den = den * 86400;
|
286
|
+
reduce(&num, &den);
|
287
|
+
|
288
|
+
num = (jd * den) + num;
|
289
|
+
|
290
|
+
num = num * 2;
|
291
|
+
num = num - den;
|
292
|
+
den = den * 2;
|
293
|
+
|
294
|
+
reduce(&num, &den);
|
295
|
+
|
296
|
+
ajd = rb_funcall(rb_mKernel, ID_RATIONAL, 2, rb_ull2inum(num), rb_ull2inum(den));
|
297
|
+
offset = timezone_to_offset(hour_offset, minute_offset);
|
298
|
+
|
299
|
+
return rb_funcall(rb_cDateTime, ID_NEW_DATE, 3, ajd, offset, INT2NUM(2299161));
|
300
|
+
}
|
301
|
+
|
302
|
+
static VALUE parse_time(char *date) {
|
303
|
+
|
304
|
+
int year, month, day, hour, min, sec, usec, tokens, hour_offset, minute_offset;
|
305
|
+
|
306
|
+
if (0 != strchr(date, '.')) {
|
307
|
+
// This is a datetime with sub-second precision
|
308
|
+
tokens = sscanf(date, "%4d-%2d-%2d%*c%2d:%2d:%2d.%d%3d:%2d", &year, &month, &day, &hour, &min, &sec, &usec, &hour_offset, &minute_offset);
|
309
|
+
} else {
|
310
|
+
// This is a datetime second precision
|
311
|
+
tokens = sscanf(date, "%4d-%2d-%2d%*c%2d:%2d:%2d%3d:%2d", &year, &month, &day, &hour, &min, &sec, &hour_offset, &minute_offset);
|
312
|
+
usec = 0;
|
313
|
+
if(tokens == 3) {
|
314
|
+
hour = 0;
|
315
|
+
min = 0;
|
316
|
+
sec = 0;
|
317
|
+
hour_offset = 0;
|
318
|
+
minute_offset = 0;
|
319
|
+
}
|
320
|
+
}
|
321
|
+
|
322
|
+
return rb_funcall(rb_cTime, rb_intern("local"), 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), INT2NUM(usec));
|
323
|
+
}
|
324
|
+
|
325
|
+
static VALUE typecast(sqlite3_stmt *stmt, int i, VALUE type, int encoding) {
|
326
|
+
VALUE ruby_value = Qnil;
|
327
|
+
int original_type = sqlite3_column_type(stmt, i);
|
328
|
+
int length = sqlite3_column_bytes(stmt, i);
|
329
|
+
if ( original_type == SQLITE_NULL ) {
|
330
|
+
return ruby_value;
|
331
|
+
}
|
332
|
+
|
333
|
+
if(type == Qnil) {
|
334
|
+
switch(original_type) {
|
335
|
+
case SQLITE_INTEGER: {
|
336
|
+
type = rb_cInteger;
|
337
|
+
break;
|
338
|
+
}
|
339
|
+
case SQLITE_FLOAT: {
|
340
|
+
type = rb_cFloat;
|
341
|
+
break;
|
342
|
+
}
|
343
|
+
case SQLITE_BLOB: {
|
344
|
+
type = rb_cByteArray;
|
345
|
+
break;
|
346
|
+
}
|
347
|
+
default: {
|
348
|
+
type = rb_cString;
|
349
|
+
break;
|
350
|
+
}
|
351
|
+
}
|
352
|
+
}
|
353
|
+
|
354
|
+
if (type == rb_cInteger) {
|
355
|
+
return LL2NUM(sqlite3_column_int64(stmt, i));
|
356
|
+
} else if (type == rb_cString) {
|
357
|
+
return DO_STR_NEW((char*)sqlite3_column_text(stmt, i), length, encoding);
|
358
|
+
} else if (type == rb_cFloat) {
|
359
|
+
return rb_float_new(sqlite3_column_double(stmt, i));
|
360
|
+
} else if (type == rb_cBigDecimal) {
|
361
|
+
return rb_funcall(rb_cBigDecimal, ID_NEW, 1, rb_str_new((char*)sqlite3_column_text(stmt, i), length));
|
362
|
+
} else if (type == rb_cDate) {
|
363
|
+
return parse_date((char*)sqlite3_column_text(stmt, i));
|
364
|
+
} else if (type == rb_cDateTime) {
|
365
|
+
return parse_date_time((char*)sqlite3_column_text(stmt, i));
|
366
|
+
} else if (type == rb_cTime) {
|
367
|
+
return parse_time((char*)sqlite3_column_text(stmt, i));
|
368
|
+
} else if (type == rb_cTrueClass) {
|
369
|
+
return strcmp((char*)sqlite3_column_text(stmt, i), "t") == 0 ? Qtrue : Qfalse;
|
370
|
+
} else if (type == rb_cByteArray) {
|
371
|
+
return rb_funcall(rb_cByteArray, ID_NEW, 1, rb_str_new((char*)sqlite3_column_blob(stmt, i), length));
|
372
|
+
} else if (type == rb_cClass) {
|
373
|
+
return rb_funcall(mDO, rb_intern("full_const_get"), 1, rb_str_new((char*)sqlite3_column_text(stmt, i), length));
|
374
|
+
} else if (type == rb_cObject) {
|
375
|
+
return rb_marshal_load(rb_str_new((char*)sqlite3_column_text(stmt, i), length));
|
376
|
+
} else if (type == rb_cNilClass) {
|
377
|
+
return Qnil;
|
378
|
+
} else {
|
379
|
+
return DO_STR_NEW((char*)sqlite3_column_text(stmt, i), length, encoding);
|
380
|
+
}
|
381
|
+
}
|
382
|
+
|
383
|
+
#ifdef HAVE_SQLITE3_OPEN_V2
|
384
|
+
|
385
|
+
#define FLAG_PRESENT(query_values, flag) !NIL_P(rb_hash_aref(query_values, flag))
|
386
|
+
|
387
|
+
static int flags_from_uri(VALUE uri) {
|
388
|
+
VALUE query_values = rb_funcall(uri, ID_QUERY, 0);
|
389
|
+
|
390
|
+
int flags = 0;
|
391
|
+
|
392
|
+
if (!NIL_P(query_values) && TYPE(query_values) == T_HASH) {
|
393
|
+
/// scan for flags
|
394
|
+
#ifdef SQLITE_OPEN_READONLY
|
395
|
+
if (FLAG_PRESENT(query_values, OPEN_FLAG_READONLY)) {
|
396
|
+
flags |= SQLITE_OPEN_READONLY;
|
397
|
+
} else {
|
398
|
+
flags |= SQLITE_OPEN_READWRITE;
|
399
|
+
}
|
400
|
+
#endif
|
401
|
+
#ifdef SQLITE_OPEN_NOMUTEX
|
402
|
+
if (FLAG_PRESENT(query_values, OPEN_FLAG_NO_MUTEX)) {
|
403
|
+
flags |= SQLITE_OPEN_NOMUTEX;
|
404
|
+
}
|
405
|
+
#endif
|
406
|
+
#ifdef SQLITE_OPEN_FULLMUTEX
|
407
|
+
if (FLAG_PRESENT(query_values, OPEN_FLAG_FULL_MUTEX)) {
|
408
|
+
flags |= SQLITE_OPEN_FULLMUTEX;
|
409
|
+
}
|
410
|
+
#endif
|
411
|
+
flags |= SQLITE_OPEN_CREATE;
|
412
|
+
} else {
|
413
|
+
flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
|
414
|
+
}
|
415
|
+
|
416
|
+
return flags;
|
417
|
+
}
|
418
|
+
|
419
|
+
#endif
|
420
|
+
|
421
|
+
/****** Public API ******/
|
422
|
+
|
423
|
+
static VALUE cConnection_initialize(VALUE self, VALUE uri) {
|
424
|
+
int ret;
|
425
|
+
VALUE path;
|
426
|
+
sqlite3 *db;
|
427
|
+
|
428
|
+
path = rb_funcall(uri, ID_PATH, 0);
|
429
|
+
|
430
|
+
#ifdef HAVE_SQLITE3_OPEN_V2
|
431
|
+
ret = sqlite3_open_v2(rb_str_ptr_readonly(path), &db, flags_from_uri(uri), 0);
|
432
|
+
#else
|
433
|
+
ret = sqlite3_open(rb_str_ptr_readonly(path), &db);
|
434
|
+
#endif
|
435
|
+
|
436
|
+
if ( ret != SQLITE_OK ) {
|
437
|
+
raise_error(self, db, Qnil);
|
438
|
+
}
|
439
|
+
|
440
|
+
rb_iv_set(self, "@uri", uri);
|
441
|
+
rb_iv_set(self, "@connection", Data_Wrap_Struct(rb_cObject, 0, 0, db));
|
442
|
+
// Sqlite3 only supports UTF-8, so this is the standard encoding
|
443
|
+
rb_iv_set(self, "@encoding", rb_str_new2("UTF-8"));
|
444
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
445
|
+
rb_iv_set(self, "@encoding_id", INT2FIX(rb_enc_find_index("UTF-8")));
|
446
|
+
#endif
|
447
|
+
|
448
|
+
return Qtrue;
|
449
|
+
}
|
450
|
+
|
451
|
+
static VALUE cConnection_dispose(VALUE self) {
|
452
|
+
VALUE connection_container = rb_iv_get(self, "@connection");
|
453
|
+
|
454
|
+
sqlite3 *db;
|
455
|
+
|
456
|
+
if (Qnil == connection_container)
|
457
|
+
return Qfalse;
|
458
|
+
|
459
|
+
db = DATA_PTR(connection_container);
|
460
|
+
|
461
|
+
if (NULL == db)
|
462
|
+
return Qfalse;
|
463
|
+
|
464
|
+
sqlite3_close(db);
|
465
|
+
rb_iv_set(self, "@connection", Qnil);
|
466
|
+
|
467
|
+
return Qtrue;
|
468
|
+
|
469
|
+
}
|
470
|
+
|
471
|
+
static VALUE cCommand_set_types(int argc, VALUE *argv, VALUE self) {
|
472
|
+
VALUE type_strings = rb_ary_new();
|
473
|
+
VALUE array = rb_ary_new();
|
474
|
+
|
475
|
+
int i, j;
|
476
|
+
|
477
|
+
for ( i = 0; i < argc; i++) {
|
478
|
+
rb_ary_push(array, argv[i]);
|
479
|
+
}
|
480
|
+
|
481
|
+
for (i = 0; i < RARRAY_LEN(array); i++) {
|
482
|
+
VALUE entry = rb_ary_entry(array, i);
|
483
|
+
if(TYPE(entry) == T_CLASS) {
|
484
|
+
rb_ary_push(type_strings, entry);
|
485
|
+
} else if (TYPE(entry) == T_ARRAY) {
|
486
|
+
for (j = 0; j < RARRAY_LEN(entry); j++) {
|
487
|
+
VALUE sub_entry = rb_ary_entry(entry, j);
|
488
|
+
if(TYPE(sub_entry) == T_CLASS) {
|
489
|
+
rb_ary_push(type_strings, sub_entry);
|
490
|
+
} else {
|
491
|
+
rb_raise(eArgumentError, "Invalid type given");
|
492
|
+
}
|
493
|
+
}
|
494
|
+
} else {
|
495
|
+
rb_raise(eArgumentError, "Invalid type given");
|
496
|
+
}
|
497
|
+
}
|
498
|
+
|
499
|
+
rb_iv_set(self, "@field_types", type_strings);
|
500
|
+
|
501
|
+
return array;
|
502
|
+
}
|
503
|
+
|
504
|
+
static VALUE cConnection_quote_boolean(VALUE self, VALUE value) {
|
505
|
+
return rb_str_new2(value == Qtrue ? "'t'" : "'f'");
|
506
|
+
}
|
507
|
+
|
508
|
+
static VALUE cConnection_quote_string(VALUE self, VALUE string) {
|
509
|
+
const char *source = rb_str_ptr_readonly(string);
|
510
|
+
char *escaped_with_quotes;
|
511
|
+
VALUE result;
|
512
|
+
|
513
|
+
// Wrap the escaped string in single-quotes, this is DO's convention
|
514
|
+
escaped_with_quotes = sqlite3_mprintf("%Q", source);
|
515
|
+
|
516
|
+
result = rb_str_new2(escaped_with_quotes);
|
517
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
518
|
+
rb_enc_associate_index(result, FIX2INT(rb_iv_get(self, "@encoding_id")));
|
519
|
+
#endif
|
520
|
+
sqlite3_free(escaped_with_quotes);
|
521
|
+
return result;
|
522
|
+
}
|
523
|
+
|
524
|
+
static VALUE cConnection_quote_byte_array(VALUE self, VALUE string) {
|
525
|
+
VALUE source = StringValue(string);
|
526
|
+
VALUE array = rb_funcall(source, rb_intern("unpack"), 1, rb_str_new2("H*"));
|
527
|
+
rb_ary_unshift(array, rb_str_new2("X'"));
|
528
|
+
rb_ary_push(array, rb_str_new2("'"));
|
529
|
+
return rb_ary_join(array, Qnil);
|
530
|
+
}
|
531
|
+
|
532
|
+
static VALUE cConnection_character_set(VALUE self) {
|
533
|
+
return rb_iv_get(self, "@encoding");
|
534
|
+
}
|
535
|
+
|
536
|
+
static VALUE build_query_from_args(VALUE klass, int count, VALUE *args) {
|
537
|
+
VALUE query = rb_iv_get(klass, "@text");
|
538
|
+
int i;
|
539
|
+
VALUE array = rb_ary_new();
|
540
|
+
for ( i = 0; i < count; i++) {
|
541
|
+
rb_ary_push(array, (VALUE)args[i]);
|
542
|
+
}
|
543
|
+
query = rb_funcall(klass, ID_ESCAPE, 1, array);
|
544
|
+
return query;
|
545
|
+
}
|
546
|
+
|
547
|
+
static VALUE cCommand_execute_non_query(int argc, VALUE *argv, VALUE self) {
|
548
|
+
sqlite3 *db;
|
549
|
+
char *error_message;
|
550
|
+
int status;
|
551
|
+
int affected_rows;
|
552
|
+
do_int64 insert_id;
|
553
|
+
VALUE connection, sqlite3_connection;
|
554
|
+
VALUE query;
|
555
|
+
struct timeval start;
|
556
|
+
|
557
|
+
query = build_query_from_args(self, argc, argv);
|
558
|
+
|
559
|
+
connection = rb_iv_get(self, "@connection");
|
560
|
+
sqlite3_connection = rb_iv_get(connection, "@connection");
|
561
|
+
if (Qnil == sqlite3_connection) {
|
562
|
+
rb_raise(eConnectionError, "This connection has already been closed.");
|
563
|
+
}
|
564
|
+
|
565
|
+
Data_Get_Struct(sqlite3_connection, sqlite3, db);
|
566
|
+
|
567
|
+
gettimeofday(&start, NULL);
|
568
|
+
status = sqlite3_exec(db, rb_str_ptr_readonly(query), 0, 0, &error_message);
|
569
|
+
|
570
|
+
if ( status != SQLITE_OK ) {
|
571
|
+
raise_error(self, db, query);
|
572
|
+
}
|
573
|
+
data_objects_debug(query, &start);
|
574
|
+
|
575
|
+
affected_rows = sqlite3_changes(db);
|
576
|
+
insert_id = sqlite3_last_insert_rowid(db);
|
577
|
+
|
578
|
+
return rb_funcall(cResult, ID_NEW, 3, self, INT2NUM(affected_rows), INT2NUM(insert_id));
|
579
|
+
}
|
580
|
+
|
581
|
+
static VALUE cCommand_execute_reader(int argc, VALUE *argv, VALUE self) {
|
582
|
+
sqlite3 *db;
|
583
|
+
sqlite3_stmt *sqlite3_reader;
|
584
|
+
int status;
|
585
|
+
int field_count;
|
586
|
+
int i;
|
587
|
+
VALUE reader;
|
588
|
+
VALUE connection, sqlite3_connection;
|
589
|
+
VALUE query;
|
590
|
+
VALUE field_names, field_types;
|
591
|
+
struct timeval start;
|
592
|
+
|
593
|
+
connection = rb_iv_get(self, "@connection");
|
594
|
+
sqlite3_connection = rb_iv_get(connection, "@connection");
|
595
|
+
if (Qnil == sqlite3_connection) {
|
596
|
+
rb_raise(eConnectionError, "This connection has already been closed.");
|
597
|
+
}
|
598
|
+
|
599
|
+
Data_Get_Struct(sqlite3_connection, sqlite3, db);
|
600
|
+
|
601
|
+
query = build_query_from_args(self, argc, argv);
|
602
|
+
|
603
|
+
gettimeofday(&start, NULL);
|
604
|
+
status = sqlite3_prepare_v2(db, rb_str_ptr_readonly(query), -1, &sqlite3_reader, 0);
|
605
|
+
data_objects_debug(query, &start);
|
606
|
+
|
607
|
+
if ( status != SQLITE_OK ) {
|
608
|
+
raise_error(self, db, query);
|
609
|
+
}
|
610
|
+
|
611
|
+
field_count = sqlite3_column_count(sqlite3_reader);
|
612
|
+
reader = rb_funcall(cReader, ID_NEW, 0);
|
613
|
+
|
614
|
+
rb_iv_set(reader, "@reader", Data_Wrap_Struct(rb_cObject, 0, 0, sqlite3_reader));
|
615
|
+
rb_iv_set(reader, "@field_count", INT2NUM(field_count));
|
616
|
+
rb_iv_set(reader, "@connection", connection);
|
617
|
+
|
618
|
+
field_names = rb_ary_new();
|
619
|
+
field_types = rb_iv_get(self, "@field_types");
|
620
|
+
|
621
|
+
if ( field_types == Qnil || 0 == RARRAY_LEN(field_types) ) {
|
622
|
+
field_types = rb_ary_new();
|
623
|
+
} else if (RARRAY_LEN(field_types) != field_count) {
|
624
|
+
// Whoops... wrong number of types passed to set_types. Close the reader and raise
|
625
|
+
// and error
|
626
|
+
rb_funcall(reader, rb_intern("close"), 0);
|
627
|
+
rb_raise(eArgumentError, "Field-count mismatch. Expected %ld fields, but the query yielded %d", RARRAY_LEN(field_types), field_count);
|
628
|
+
}
|
629
|
+
|
630
|
+
for ( i = 0; i < field_count; i++ ) {
|
631
|
+
rb_ary_push(field_names, rb_str_new2((char *)sqlite3_column_name(sqlite3_reader, i)));
|
632
|
+
}
|
633
|
+
|
634
|
+
rb_iv_set(reader, "@fields", field_names);
|
635
|
+
rb_iv_set(reader, "@field_types", field_types);
|
636
|
+
|
637
|
+
return reader;
|
638
|
+
}
|
639
|
+
|
640
|
+
static VALUE cReader_close(VALUE self) {
|
641
|
+
VALUE reader_obj = rb_iv_get(self, "@reader");
|
642
|
+
|
643
|
+
if ( reader_obj != Qnil ) {
|
644
|
+
sqlite3_stmt *reader;
|
645
|
+
Data_Get_Struct(reader_obj, sqlite3_stmt, reader);
|
646
|
+
sqlite3_finalize(reader);
|
647
|
+
rb_iv_set(self, "@reader", Qnil);
|
648
|
+
return Qtrue;
|
649
|
+
}
|
650
|
+
else {
|
651
|
+
return Qfalse;
|
652
|
+
}
|
653
|
+
}
|
654
|
+
|
655
|
+
static VALUE cReader_next(VALUE self) {
|
656
|
+
sqlite3_stmt *reader;
|
657
|
+
int field_count;
|
658
|
+
int result;
|
659
|
+
int i;
|
660
|
+
size_t ft_length;
|
661
|
+
VALUE arr = rb_ary_new();
|
662
|
+
VALUE field_types;
|
663
|
+
VALUE field_type;
|
664
|
+
VALUE value;
|
665
|
+
|
666
|
+
Data_Get_Struct(rb_iv_get(self, "@reader"), sqlite3_stmt, reader);
|
667
|
+
field_count = NUM2INT(rb_iv_get(self, "@field_count"));
|
668
|
+
|
669
|
+
field_types = rb_iv_get(self, "@field_types");
|
670
|
+
ft_length = RARRAY_LEN(field_types);
|
671
|
+
|
672
|
+
result = sqlite3_step(reader);
|
673
|
+
|
674
|
+
rb_iv_set(self, "@state", INT2NUM(result));
|
675
|
+
|
676
|
+
if ( result != SQLITE_ROW ) {
|
677
|
+
rb_iv_set(self, "@values", Qnil);
|
678
|
+
return Qfalse;
|
679
|
+
}
|
680
|
+
|
681
|
+
int enc = -1;
|
682
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
683
|
+
VALUE encoding_id = rb_iv_get(rb_iv_get(self, "@connection"), "@encoding_id");
|
684
|
+
if (encoding_id != Qnil) {
|
685
|
+
enc = FIX2INT(encoding_id);
|
686
|
+
}
|
687
|
+
#endif
|
688
|
+
|
689
|
+
|
690
|
+
for ( i = 0; i < field_count; i++ ) {
|
691
|
+
field_type = rb_ary_entry(field_types, i);
|
692
|
+
value = typecast(reader, i, field_type, enc);
|
693
|
+
rb_ary_push(arr, value);
|
694
|
+
}
|
695
|
+
|
696
|
+
rb_iv_set(self, "@values", arr);
|
697
|
+
|
698
|
+
return Qtrue;
|
699
|
+
}
|
700
|
+
|
701
|
+
static VALUE cReader_values(VALUE self) {
|
702
|
+
VALUE state = rb_iv_get(self, "@state");
|
703
|
+
if ( state == Qnil || NUM2INT(state) != SQLITE_ROW ) {
|
704
|
+
rb_raise(eDataError, "Reader is not initialized");
|
705
|
+
return Qnil;
|
706
|
+
}
|
707
|
+
else {
|
708
|
+
return rb_iv_get(self, "@values");
|
709
|
+
}
|
710
|
+
}
|
711
|
+
|
712
|
+
static VALUE cReader_fields(VALUE self) {
|
713
|
+
return rb_iv_get(self, "@fields");
|
714
|
+
}
|
715
|
+
|
716
|
+
static VALUE cReader_field_count(VALUE self) {
|
717
|
+
return rb_iv_get(self, "@field_count");
|
718
|
+
}
|
719
|
+
|
720
|
+
void Init_do_sqlite3() {
|
721
|
+
rb_require("bigdecimal");
|
722
|
+
rb_require("date");
|
723
|
+
|
724
|
+
// Get references classes needed for Date/Time parsing
|
725
|
+
rb_cDate = RUBY_CLASS("Date");
|
726
|
+
rb_cDateTime = RUBY_CLASS( "DateTime");
|
727
|
+
rb_cBigDecimal = RUBY_CLASS("BigDecimal");
|
728
|
+
|
729
|
+
rb_funcall(rb_mKernel, rb_intern("require"), 1, rb_str_new2("data_objects"));
|
730
|
+
|
731
|
+
#ifdef RUBY_LESS_THAN_186
|
732
|
+
ID_NEW_DATE = rb_intern("new0");
|
733
|
+
#else
|
734
|
+
ID_NEW_DATE = rb_intern("new!");
|
735
|
+
#endif
|
736
|
+
ID_RATIONAL = rb_intern("Rational");
|
737
|
+
ID_LOGGER = rb_intern("logger");
|
738
|
+
ID_DEBUG = rb_intern("debug");
|
739
|
+
ID_LEVEL = rb_intern("level");
|
740
|
+
|
741
|
+
// Get references to the Extlib module
|
742
|
+
mExtlib = CONST_GET(rb_mKernel, "Extlib");
|
743
|
+
rb_cByteArray = CONST_GET(mExtlib, "ByteArray");
|
744
|
+
|
745
|
+
// Get references to the DataObjects module and its classes
|
746
|
+
mDO = CONST_GET(rb_mKernel, "DataObjects");
|
747
|
+
cDO_Quoting = CONST_GET(mDO, "Quoting");
|
748
|
+
cDO_Connection = CONST_GET(mDO, "Connection");
|
749
|
+
cDO_Command = CONST_GET(mDO, "Command");
|
750
|
+
cDO_Result = CONST_GET(mDO, "Result");
|
751
|
+
cDO_Reader = CONST_GET(mDO, "Reader");
|
752
|
+
|
753
|
+
// Initialize the DataObjects::Sqlite3 module, and define its classes
|
754
|
+
mSqlite3 = rb_define_module_under(mDO, "Sqlite3");
|
755
|
+
|
756
|
+
eArgumentError = CONST_GET(rb_mKernel, "ArgumentError");
|
757
|
+
eConnectionError = CONST_GET(mDO, "ConnectionError");
|
758
|
+
eDataError = CONST_GET(mDO, "DataError");
|
759
|
+
|
760
|
+
cConnection = SQLITE3_CLASS("Connection", cDO_Connection);
|
761
|
+
rb_define_method(cConnection, "initialize", cConnection_initialize, 1);
|
762
|
+
rb_define_method(cConnection, "dispose", cConnection_dispose, 0);
|
763
|
+
rb_define_method(cConnection, "quote_boolean", cConnection_quote_boolean, 1);
|
764
|
+
rb_define_method(cConnection, "quote_string", cConnection_quote_string, 1);
|
765
|
+
rb_define_method(cConnection, "quote_byte_array", cConnection_quote_byte_array, 1);
|
766
|
+
rb_define_method(cConnection, "character_set", cConnection_character_set, 0);
|
767
|
+
|
768
|
+
cCommand = SQLITE3_CLASS("Command", cDO_Command);
|
769
|
+
rb_define_method(cCommand, "set_types", cCommand_set_types, -1);
|
770
|
+
rb_define_method(cCommand, "execute_non_query", cCommand_execute_non_query, -1);
|
771
|
+
rb_define_method(cCommand, "execute_reader", cCommand_execute_reader, -1);
|
772
|
+
|
773
|
+
cResult = SQLITE3_CLASS("Result", cDO_Result);
|
774
|
+
|
775
|
+
cReader = SQLITE3_CLASS("Reader", cDO_Reader);
|
776
|
+
rb_define_method(cReader, "close", cReader_close, 0);
|
777
|
+
rb_define_method(cReader, "next!", cReader_next, 0);
|
778
|
+
rb_define_method(cReader, "values", cReader_values, 0);
|
779
|
+
rb_define_method(cReader, "fields", cReader_fields, 0);
|
780
|
+
rb_define_method(cReader, "field_count", cReader_field_count, 0);
|
781
|
+
|
782
|
+
OPEN_FLAG_READONLY = rb_str_new2("read_only");
|
783
|
+
rb_global_variable(&OPEN_FLAG_READONLY);
|
784
|
+
OPEN_FLAG_READWRITE = rb_str_new2("read_write");
|
785
|
+
rb_global_variable(&OPEN_FLAG_READWRITE);
|
786
|
+
OPEN_FLAG_CREATE = rb_str_new2("create");
|
787
|
+
rb_global_variable(&OPEN_FLAG_CREATE);
|
788
|
+
OPEN_FLAG_NO_MUTEX = rb_str_new2("no_mutex");
|
789
|
+
rb_global_variable(&OPEN_FLAG_NO_MUTEX);
|
790
|
+
OPEN_FLAG_FULL_MUTEX = rb_str_new2("full_mutex");
|
791
|
+
rb_global_variable(&OPEN_FLAG_FULL_MUTEX);
|
792
|
+
|
793
|
+
}
|