nuodb 1.0.0.rc.1 → 1.0.0.rc.2
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/.gitignore +4 -1
- data/.rspec +2 -0
- data/AUTHORS +2 -2
- data/Gemfile +1 -5
- data/LICENSE +8 -9
- data/README.rdoc +21 -22
- data/Rakefile +42 -13
- data/ext/nuodb/atomic.c +11 -0
- data/ext/nuodb/atomic.h +62 -0
- data/ext/nuodb/extconf.rb +22 -1
- data/ext/nuodb/nuodb.cpp +2140 -753
- data/lib/nuodb/column.rb +131 -0
- data/lib/nuodb/error.rb +1 -29
- data/lib/nuodb/version.rb +1 -1
- data/nuodb.gemspec +26 -18
- data/spec/data/.gitignore +0 -0
- data/spec/functional/column_spec.rb +0 -0
- data/spec/functional/connection_spec.rb +110 -0
- data/spec/functional/datatypes_spec.rb +165 -0
- data/spec/functional/generated_keys_spec.rb +137 -0
- data/spec/functional/prepared_statement_spec.rb +160 -0
- data/spec/functional/statement_spec.rb +200 -0
- data/spec/functional/transaction_spec.rb +0 -0
- data/spec/rcov.opts +2 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/config.rb +42 -0
- data/spec/support/config.yml +10 -0
- data/spec/support/connection.rb +15 -0
- data/spec/support/datetime.rb +25 -0
- data/spec/unit/.gitignore +0 -0
- data/tasks/rdoc.rb +19 -0
- data/tasks/rspec.rb +56 -0
- data/tasks/stats.rb +24 -0
- metadata +253 -8
- data/COPYING +0 -25
- data/test/test_nuodb.rb +0 -267
data/.gitignore
CHANGED
data/.rspec
ADDED
data/AUTHORS
CHANGED
data/Gemfile
CHANGED
data/LICENSE
CHANGED
@@ -4,14 +4,14 @@ All rights reserved.
|
|
4
4
|
Redistribution and use in source and binary forms, with or without
|
5
5
|
modification, are permitted provided that the following conditions are met:
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
7
|
+
* Redistributions of source code must retain the above copyright
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
9
|
+
* Redistributions in binary form must reproduce the above copyright
|
10
|
+
notice, this list of conditions and the following disclaimer in the
|
11
|
+
documentation and/or other materials provided with the distribution.
|
12
|
+
* Neither the name of NuoDB, Inc. nor the names of its contributors may
|
13
|
+
be used to endorse or promote products derived from this software
|
14
|
+
without specific prior written permission.
|
15
15
|
|
16
16
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
17
17
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
@@ -23,4 +23,3 @@ OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
23
23
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
24
24
|
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
25
25
|
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
26
|
-
|
data/README.rdoc
CHANGED
@@ -2,39 +2,40 @@
|
|
2
2
|
|
3
3
|
== DESCRIPTION
|
4
4
|
|
5
|
-
This is the
|
6
|
-
|
5
|
+
This is the official Ruby Gem for NuoDB. It is written as a Ruby extension that
|
6
|
+
wraps the NuoDB C++ API, providing a natural API for Ruby developers.
|
7
7
|
|
8
8
|
== BUILDING THE GEM
|
9
9
|
|
10
10
|
To compile and test run this command:
|
11
11
|
|
12
|
-
rake build
|
12
|
+
rake clean build rdoc spec
|
13
13
|
|
14
14
|
== INSTALLING THE GEM
|
15
15
|
|
16
|
-
NUODB_ROOT=/Users/rbuck/tmp/nuodb gem install nuodb-1.0.0
|
16
|
+
NUODB_ROOT=/Users/rbuck/tmp/nuodb gem install nuodb-1.0.0.rc.2.gem
|
17
17
|
|
18
18
|
Or from the source tree:
|
19
19
|
|
20
|
-
NUODB_ROOT=/Users/rbuck/tmp/nuodb gem install pkg/nuodb-1.0.0
|
20
|
+
NUODB_ROOT=/Users/rbuck/tmp/nuodb gem install pkg/nuodb-1.0.0.rc.2.gem
|
21
21
|
|
22
22
|
== TESTING THE GEM
|
23
23
|
|
24
24
|
Start up a minimal chorus as follows:
|
25
25
|
|
26
|
-
java -jar nuoagent.jar --broker &
|
27
|
-
|
26
|
+
java -jar ${NUODB_ROOT}/jar/nuoagent.jar --broker &
|
27
|
+
${NUODB_ROOT}/bin/nuodb --chorus test --password bar --dba-user dba --dba-password baz --verbose debug --archive /var/tmp/nuodb --initialize --force &
|
28
|
+
${NUODB_ROOT}/bin/nuodb --chorus test --password bar --dba-user dba --dba-password baz &
|
28
29
|
|
29
30
|
Create a user in the database:
|
30
31
|
|
31
|
-
|
32
|
+
${NUODB_ROOT}/bin/nuosql test@localhost --user dba --password baz
|
32
33
|
> create user cloud password 'user';
|
33
34
|
> exit
|
34
35
|
|
35
36
|
Run the tests:
|
36
37
|
|
37
|
-
rake
|
38
|
+
rake spec
|
38
39
|
|
39
40
|
== PUBLISHING THE GEM
|
40
41
|
|
@@ -42,36 +43,34 @@ Run the tests:
|
|
42
43
|
|
43
44
|
Tag the product using tags per the SemVer specification; our tags have a v-prefix:
|
44
45
|
|
45
|
-
git tag -a v1.0.0
|
46
|
+
git tag -a v1.0.0-rc.2 -m "SemVer Version: v1.0.0-rc.2"
|
46
47
|
|
47
48
|
If you make a mistake, take it back quickly:
|
48
49
|
|
49
|
-
git tag -d v1.0.0
|
50
|
-
git push origin :refs/tags/v1.0.0
|
50
|
+
git tag -d v1.0.0-rc.2
|
51
|
+
git push origin :refs/tags/v1.0.0-rc.2
|
51
52
|
|
52
53
|
===PUBLISHING
|
53
54
|
|
54
55
|
Here are the commands used to publish:
|
55
56
|
|
56
|
-
gem push pkg/nuodb-1.0.0.rc.
|
57
|
-
|
58
|
-
If you bugger it up, pull it back quickly, or cause grief for users:
|
59
|
-
|
60
|
-
npm unpublish db-nuodb@1.0.0.RC.1
|
61
|
-
|
62
|
-
To view published versions:
|
63
|
-
|
64
|
-
npm view db-nuodb
|
57
|
+
gem push pkg/nuodb-1.0.0.rc.2.gem
|
65
58
|
|
66
59
|
== INSPECTING THE GEM
|
67
60
|
|
68
61
|
It is often useful to inspect the contents of a Gem before distribution.
|
69
62
|
To do this you dump the contents of a gem thus:
|
70
63
|
|
71
|
-
gem unpack pkg/nuodb-1.0.0.rc.
|
64
|
+
gem unpack pkg/nuodb-1.0.0.rc.2.gem
|
72
65
|
|
73
66
|
== INSPECTING THE EXPORTED SYMBOLS
|
74
67
|
|
68
|
+
To inspec the symbols on Linux:
|
69
|
+
|
75
70
|
nm -gU ext/nuodb/nuodb.bundle
|
76
71
|
|
72
|
+
To inspec the symbols on Mac:
|
73
|
+
|
74
|
+
otool -l ext/nuodb/nuodb.bundle
|
75
|
+
|
77
76
|
== REFERENCES
|
data/Rakefile
CHANGED
@@ -33,6 +33,16 @@ require 'rake/testtask'
|
|
33
33
|
require 'rdoc/task'
|
34
34
|
require 'date'
|
35
35
|
|
36
|
+
require 'bundler'
|
37
|
+
require 'bundler/gem_tasks'
|
38
|
+
|
39
|
+
require File.expand_path(File.dirname(__FILE__)) + "/spec/support/config"
|
40
|
+
require File.expand_path(File.dirname(__FILE__)) + "/tasks/rspec"
|
41
|
+
|
42
|
+
Bundler::GemHelper.install_tasks
|
43
|
+
|
44
|
+
load 'nuodb.gemspec'
|
45
|
+
|
36
46
|
#############################################################################
|
37
47
|
#
|
38
48
|
# Helper functions
|
@@ -93,20 +103,16 @@ end
|
|
93
103
|
#
|
94
104
|
#############################################################################
|
95
105
|
|
106
|
+
CLEAN.include('doc/ri')
|
107
|
+
CLEAN.include('doc/site')
|
96
108
|
CLEAN.include('pkg')
|
97
109
|
CLEAN.include('ext/**/*{.o,.log,.so,.bundle}')
|
98
110
|
CLEAN.include('ext/**/Makefile')
|
99
111
|
CLEAN.include('lib/**/*{.so,.bundle}')
|
100
112
|
|
101
|
-
|
102
|
-
Rake::RDocTask.new do |rdoc|
|
103
|
-
rdoc.rdoc_dir = 'rdoc'
|
104
|
-
rdoc.title = "#{name} #{version}"
|
105
|
-
rdoc.rdoc_files.include('README*')
|
106
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
107
|
-
end
|
113
|
+
Dir['tasks/**/*.rb'].each { |file| load file }
|
108
114
|
|
109
|
-
file "lib/#{name}/#{name}.#{so_ext}" => Dir.glob("ext/#{name}/*{.rb,.
|
115
|
+
file "lib/#{name}/#{name}.#{so_ext}" => Dir.glob("ext/#{name}/*{.rb,.cpp}") do
|
110
116
|
Dir.chdir("ext/#{name}") do
|
111
117
|
# this does essentially the same thing
|
112
118
|
# as what RubyGems does
|
@@ -116,13 +122,23 @@ file "lib/#{name}/#{name}.#{so_ext}" => Dir.glob("ext/#{name}/*{.rb,.c}") do
|
|
116
122
|
cp "ext/#{name}/#{name}.#{so_ext}", "lib/#{name}"
|
117
123
|
end
|
118
124
|
|
119
|
-
|
125
|
+
namespace :nuodb do
|
126
|
+
task :create_user do
|
127
|
+
#puts %x( echo "create user arunit password 'arunit';" | nuosql arunit@localhost --user dba --password baz )
|
128
|
+
end
|
129
|
+
|
130
|
+
task :start_server do
|
131
|
+
end
|
120
132
|
|
121
|
-
|
122
|
-
|
133
|
+
task :stop_server do
|
134
|
+
end
|
135
|
+
|
136
|
+
task :restart_server => [:stop_server, :start_server, :create_user]
|
123
137
|
end
|
124
138
|
|
125
|
-
task :
|
139
|
+
task :spec => "lib/#{name}/#{name}.#{so_ext}"
|
140
|
+
|
141
|
+
task :default => :spec
|
126
142
|
|
127
143
|
#############################################################################
|
128
144
|
#
|
@@ -138,9 +154,22 @@ task :build do
|
|
138
154
|
end
|
139
155
|
|
140
156
|
task :install => :build do
|
141
|
-
sh %{gem install pkg/#{name}-#{version}
|
157
|
+
sh %{gem install pkg/#{name}-#{version}}
|
142
158
|
end
|
143
159
|
|
144
160
|
task :uninstall do
|
145
161
|
sh %{gem uninstall #{name} -x -v #{version}}
|
146
162
|
end
|
163
|
+
|
164
|
+
desc "Tags git with the latest gem version"
|
165
|
+
task :tag do
|
166
|
+
sh %{git tag v#{version}}
|
167
|
+
end
|
168
|
+
|
169
|
+
desc "Push gem packages"
|
170
|
+
task :push => :build do
|
171
|
+
sh "gem push pkg/#{name}*.gem"
|
172
|
+
end
|
173
|
+
|
174
|
+
desc "Release version #{version}"
|
175
|
+
task :release => [:tag, :push]
|
data/ext/nuodb/atomic.c
ADDED
data/ext/nuodb/atomic.h
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
#ifndef RUBY_ATOMIC_H
|
2
|
+
#define RUBY_ATOMIC_H
|
3
|
+
|
4
|
+
#ifdef _WIN32
|
5
|
+
#if defined _MSC_VER && _MSC_VER > 1200
|
6
|
+
#pragma intrinsic(_InterlockedOr)
|
7
|
+
#endif
|
8
|
+
typedef LONG rb_atomic_t;
|
9
|
+
|
10
|
+
# define ATOMIC_SET(var, val) InterlockedExchange(&(var), (val))
|
11
|
+
# define ATOMIC_INC(var) InterlockedIncrement(&(var))
|
12
|
+
# define ATOMIC_DEC(var) InterlockedDecrement(&(var))
|
13
|
+
|
14
|
+
#if defined __GNUC__
|
15
|
+
# define ATOMIC_OR(var, val) __asm__("lock\n\t" "orl\t%1, %0" : "=m"(var) : "Ir"(val))
|
16
|
+
#elif defined _MSC_VER && _MSC_VER <= 1200
|
17
|
+
# define ATOMIC_OR(var, val) rb_w32_atomic_or(&(var), (val))
|
18
|
+
static inline void
|
19
|
+
rb_w32_atomic_or(volatile rb_atomic_t *var, rb_atomic_t val)
|
20
|
+
{
|
21
|
+
#ifdef _M_IX86
|
22
|
+
__asm mov eax, var;
|
23
|
+
__asm mov ecx, val;
|
24
|
+
__asm lock or [eax], ecx;
|
25
|
+
#else
|
26
|
+
#error unsupported architecture
|
27
|
+
#endif
|
28
|
+
}
|
29
|
+
#else
|
30
|
+
# define ATOMIC_OR(var, val) _InterlockedOr(&(var), (val))
|
31
|
+
#endif
|
32
|
+
|
33
|
+
# define ATOMIC_EXCHANGE(var, val) InterlockedExchange(&(var), (val))
|
34
|
+
|
35
|
+
#elif defined HAVE_GCC_ATOMIC_BUILTINS
|
36
|
+
|
37
|
+
|
38
|
+
/* @shyouhei hack to support atomic operations in case of gcc. Gcc
|
39
|
+
* has its own pseudo-insns to support them. See info, or
|
40
|
+
* http://gcc.gnu.org/onlinedocs/gcc/Atomic-Builtins.html */
|
41
|
+
|
42
|
+
typedef unsigned int rb_atomic_t; /* Anything OK */
|
43
|
+
# define ATOMIC_SET(var, val) __sync_lock_test_and_set(&(var), (val))
|
44
|
+
# define ATOMIC_INC(var) __sync_add_and_fetch(&(var), 1)
|
45
|
+
# define ATOMIC_DEC(var) __sync_sub_and_fetch(&(var), 1)
|
46
|
+
# define ATOMIC_OR(var, val) __sync_or_and_fetch(&(var), (val))
|
47
|
+
# define ATOMIC_EXCHANGE(var, val) __sync_lock_test_and_set(&(var), (val))
|
48
|
+
|
49
|
+
#else
|
50
|
+
|
51
|
+
typedef int rb_atomic_t;
|
52
|
+
extern rb_atomic_t ruby_atomic_exchange(rb_atomic_t *ptr, rb_atomic_t val);
|
53
|
+
|
54
|
+
# define ATOMIC_SET(var, val) ((var) = (val))
|
55
|
+
# define ATOMIC_INC(var) (++(var))
|
56
|
+
# define ATOMIC_DEC(var) (--(var))
|
57
|
+
# define ATOMIC_OR(var, val) ((var) |= (val))
|
58
|
+
# define ATOMIC_EXCHANGE(var, val) ruby_atomic_exchange(&(var), (val))
|
59
|
+
|
60
|
+
#endif
|
61
|
+
|
62
|
+
#endif /* RUBY_ATOMIC_H */
|
data/ext/nuodb/extconf.rb
CHANGED
@@ -34,12 +34,23 @@ require 'find'
|
|
34
34
|
nuodb_include = nil
|
35
35
|
nuodb_lib64 = nil
|
36
36
|
|
37
|
+
if ENV['MAINTAINER_MODE']
|
38
|
+
$stderr.puts "Maintainer mode enabled."
|
39
|
+
warning_flags = %w(-Wall -g -g -ggdb -pedantic -Wextra -Wshadow -Wpointer-arith -fno-strict-aliasing -fno-builtin -Wcast-qual -Wcast-align -fstrict-aliasing -Wno-long-long -Wwrite-strings)
|
40
|
+
warning_flags.inject($CFLAGS) do |flags, option|
|
41
|
+
flags << " #{option}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
$CFLAGS << " -fexceptions"
|
46
|
+
|
37
47
|
case RUBY_PLATFORM
|
38
48
|
when /solaris|sunos/i
|
39
49
|
have_library('stdc++')
|
40
50
|
$CC = "gcc"
|
41
51
|
$CXX = "g++"
|
42
52
|
$LIBS << " -lstdc++ -lc"
|
53
|
+
$CFLAGS += " -g"
|
43
54
|
end
|
44
55
|
|
45
56
|
def dylib_extension
|
@@ -99,7 +110,9 @@ end
|
|
99
110
|
# section below handles this special case so that we can run our test suites.
|
100
111
|
|
101
112
|
if nuodb_include.nil? and nuodb_lib64.nil?
|
102
|
-
|
113
|
+
unless ENV['NUODB_ROOT'].nil?
|
114
|
+
nuodb_include, nuodb_lib64 = nuodb_srcs? ENV['NUODB_ROOT']
|
115
|
+
end
|
103
116
|
end
|
104
117
|
|
105
118
|
# Lastly we fall back to detecting the location of installed product against
|
@@ -149,4 +162,12 @@ case RUBY_PLATFORM
|
|
149
162
|
else
|
150
163
|
end
|
151
164
|
|
165
|
+
add_define 'HAVE_GCC_ATOMIC_BUILTINS' if try_link(<<SRC)
|
166
|
+
unsigned char atomic_var;
|
167
|
+
__sync_lock_test_and_set(&atomic_var, 0);
|
168
|
+
__sync_lock_test_and_set(&atomic_var, 1);
|
169
|
+
__sync_fetch_and_add(&atomic_var, 1);
|
170
|
+
__sync_fetch_and_sub(&atomic_var, 1);
|
171
|
+
SRC
|
172
|
+
|
152
173
|
create_makefile('nuodb/nuodb')
|
data/ext/nuodb/nuodb.cpp
CHANGED
@@ -31,79 +31,35 @@
|
|
31
31
|
*/
|
32
32
|
|
33
33
|
#include <ruby.h>
|
34
|
+
#include "atomic.h"
|
35
|
+
#include <assert.h>
|
34
36
|
#include <time.h>
|
35
37
|
#include <stdio.h>
|
38
|
+
#include <typeinfo>
|
36
39
|
|
37
|
-
|
38
|
-
|
39
|
-
//------------------------------------------------------------------------------
|
40
|
-
// class building macros
|
41
|
-
|
42
|
-
#define WRAPPER_COMMON(WT, RT) \
|
43
|
-
private: \
|
44
|
-
static VALUE type; \
|
45
|
-
RT* ptr; \
|
46
|
-
public: \
|
47
|
-
WT(RT* arg): ptr(arg) {} \
|
48
|
-
static VALUE getRubyType() { return type; } \
|
49
|
-
static void release(WT* self) \
|
50
|
-
{ \
|
51
|
-
/*delete self->ptr;*/ \
|
52
|
-
delete self; \
|
53
|
-
} \
|
54
|
-
static RT* asPtr(VALUE value) \
|
55
|
-
{ \
|
56
|
-
Check_Type(value, T_DATA); \
|
57
|
-
return ((WT*)DATA_PTR(value))->ptr; \
|
58
|
-
} \
|
59
|
-
static VALUE wrap(RT* value) \
|
60
|
-
{ \
|
61
|
-
if (value == NULL) return Qnil; \
|
62
|
-
WT* w = new WT(value); \
|
63
|
-
VALUE obj = Data_Wrap_Struct(WT::getRubyType(), 0, WT::release, w); \
|
64
|
-
rb_obj_call_init(obj, 0, 0); \
|
65
|
-
return obj; \
|
66
|
-
}
|
67
|
-
|
68
|
-
#define WRAPPER_DEFINITION(WT) \
|
69
|
-
VALUE WT::type = 0;
|
70
|
-
|
71
|
-
#define AS_QBOOL(value)((value)? Qtrue : Qfalse)
|
72
|
-
|
73
|
-
//------------------------------------------------------------------------------
|
74
|
-
// utility function macros
|
75
|
-
|
76
|
-
#define SYMBOL_OF(value) \
|
77
|
-
ID2SYM(rb_intern(#value))
|
78
|
-
|
79
|
-
#define INIT_TYPE(name) \
|
80
|
-
type = rb_define_class_under(module, name, rb_cObject)
|
81
|
-
|
82
|
-
#define DEFINE_SINGLE(func, count) \
|
83
|
-
rb_define_singleton_method(type, #func, RUBY_METHOD_FUNC(func), count)
|
40
|
+
#define HAVE_CXA_DEMANGLE
|
84
41
|
|
85
|
-
#
|
86
|
-
|
87
|
-
|
88
|
-
//------------------------------------------------------------------------------
|
89
|
-
// exception mapper
|
90
|
-
|
91
|
-
static VALUE m_nuodb, c_nuodb_error;
|
92
|
-
static ID c_error_code_assignment;
|
93
|
-
|
94
|
-
static void rb_raise_nuodb_error(int code, const char * fmt, ...)
|
42
|
+
#ifdef HAVE_CXA_DEMANGLE
|
43
|
+
#include <cxxabi.h>
|
44
|
+
const char * demangle(const char * name)
|
95
45
|
{
|
96
|
-
|
97
|
-
|
46
|
+
char buf[1024];
|
47
|
+
size_t size = 1024;
|
48
|
+
int status;
|
49
|
+
char * res = abi::__cxa_demangle (name,
|
50
|
+
buf, &size, &status);
|
51
|
+
return res;
|
52
|
+
}
|
53
|
+
#else
|
54
|
+
const char* demangle(const char* name)
|
55
|
+
{
|
56
|
+
return name;
|
57
|
+
}
|
58
|
+
#endif
|
98
59
|
|
99
|
-
|
100
|
-
vsnprintf(text, BUFSIZ, fmt, args);
|
101
|
-
va_end(args);
|
60
|
+
extern "C" struct timeval rb_time_timeval(VALUE time);
|
102
61
|
|
103
|
-
|
104
|
-
rb_funcall(error, c_error_code_assignment, 1, UINT2NUM(code));
|
105
|
-
rb_exc_raise(error);
|
106
|
-
}
|
62
|
+
#define AS_QBOOL(value)((value)? Qtrue : Qfalse)
|
107
63
|
|
108
64
|
//------------------------------------------------------------------------------
|
109
65
|
|
@@ -130,1001 +86,2432 @@ static void rb_raise_nuodb_error(int code, const char * fmt, ...)
|
|
130
86
|
#include "SqlDate.h"
|
131
87
|
#include "SqlTime.h"
|
132
88
|
|
133
|
-
|
89
|
+
// ----------------------------------------------------------------------------
|
90
|
+
// M O D U L E S
|
134
91
|
|
135
|
-
|
136
|
-
{
|
137
|
-
WRAPPER_COMMON(WrapDatabaseMetaData, DatabaseMetaData)
|
92
|
+
static VALUE m_nuodb;
|
138
93
|
|
139
|
-
|
140
|
-
|
141
|
-
static VALUE getIndexInfo(VALUE self, VALUE schemaValue, VALUE tableValue, VALUE uniqueValue, VALUE approxValue);
|
142
|
-
static VALUE getColumns(VALUE self, VALUE schemaValue, VALUE tablePattern);
|
143
|
-
static VALUE getTables(VALUE self, VALUE schemaValue, VALUE table);
|
144
|
-
};
|
94
|
+
// ----------------------------------------------------------------------------
|
95
|
+
// C O N S T A N T S
|
145
96
|
|
146
|
-
|
97
|
+
static VALUE c_nuodb_error;
|
147
98
|
|
148
|
-
|
99
|
+
// ----------------------------------------------------------------------------
|
100
|
+
// C L A S S E S
|
149
101
|
|
150
|
-
|
151
|
-
|
152
|
-
|
102
|
+
static VALUE nuodb_connection_klass;
|
103
|
+
static VALUE nuodb_statement_klass;
|
104
|
+
static VALUE nuodb_prepared_statement_klass;
|
105
|
+
static VALUE nuodb_result_klass;
|
153
106
|
|
154
|
-
|
155
|
-
|
156
|
-
static VALUE getColumnName(VALUE self, VALUE columnValue);
|
157
|
-
static VALUE getScale(VALUE self, VALUE columnValue);
|
158
|
-
static VALUE getPrecision(VALUE self, VALUE columnValue);
|
159
|
-
static VALUE isNullable(VALUE self, VALUE columnValue);
|
160
|
-
static VALUE getColumnTypeName(VALUE self, VALUE columnValue);
|
161
|
-
static VALUE getType(VALUE self, VALUE columnValue);
|
162
|
-
};
|
107
|
+
// ----------------------------------------------------------------------------
|
108
|
+
// S Y M B O L S
|
163
109
|
|
164
|
-
|
110
|
+
static VALUE sym_database, sym_username, sym_password, sym_schema, sym_timezone;
|
165
111
|
|
166
|
-
|
112
|
+
// ----------------------------------------------------------------------------
|
113
|
+
// B E H A V I O R S
|
167
114
|
|
168
|
-
|
169
|
-
{
|
170
|
-
WRAPPER_COMMON(WrapResultSet, ResultSet)
|
171
|
-
|
172
|
-
static void init(VALUE module);
|
173
|
-
static VALUE next(VALUE self);
|
174
|
-
static VALUE getColumnCount(VALUE self);
|
175
|
-
static VALUE getMetaData(VALUE self);
|
176
|
-
static VALUE getBoolean(VALUE self, VALUE columnValue);
|
177
|
-
static VALUE getInteger(VALUE self, VALUE columnValue);
|
178
|
-
static VALUE getDouble(VALUE self, VALUE columnValue);
|
179
|
-
static VALUE getString(VALUE self, VALUE columnValue);
|
180
|
-
static VALUE getDate(VALUE self, VALUE columnValue);
|
181
|
-
static VALUE getTime(VALUE self, VALUE columnValue);
|
182
|
-
static VALUE getTimestamp(VALUE self, VALUE columnValue);
|
183
|
-
static VALUE getChar(VALUE self, VALUE columnValue);
|
184
|
-
};
|
115
|
+
static const bool ENABLE_CLOSE_HOOK = true;
|
185
116
|
|
186
|
-
|
117
|
+
// ----------------------------------------------------------------------------
|
118
|
+
// L O G G I N G A N D T R A C I N G
|
187
119
|
|
188
|
-
|
120
|
+
#define ENABLE_LOGGING
|
189
121
|
|
190
|
-
|
122
|
+
enum LogLevel
|
191
123
|
{
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
static VALUE executeUpdate(VALUE self, VALUE sqlValue);
|
199
|
-
static VALUE getResultSet(VALUE self);
|
200
|
-
static VALUE getUpdateCount(VALUE self);
|
201
|
-
static VALUE getGeneratedKeys(VALUE self);
|
202
|
-
};
|
203
|
-
|
204
|
-
WRAPPER_DEFINITION(WrapStatement);
|
205
|
-
|
206
|
-
//------------------------------------------------------------------------------
|
207
|
-
|
208
|
-
class WrapPreparedStatement
|
209
|
-
{
|
210
|
-
WRAPPER_COMMON(WrapPreparedStatement, PreparedStatement);
|
211
|
-
|
212
|
-
static void init(VALUE module);
|
213
|
-
static VALUE close(VALUE self);
|
214
|
-
static VALUE setBoolean(VALUE self, VALUE indexValue, VALUE valueValue);
|
215
|
-
static VALUE setInteger(VALUE self, VALUE indexValue, VALUE valueValue);
|
216
|
-
static VALUE setDouble(VALUE self, VALUE indexValue, VALUE valueValue);
|
217
|
-
static VALUE setString(VALUE self, VALUE indexValue, VALUE valueValue);
|
218
|
-
static VALUE setTime(VALUE self, VALUE indexValue, VALUE valueValue);
|
219
|
-
static VALUE execute(VALUE self);
|
220
|
-
static VALUE executeQuery(VALUE self);
|
221
|
-
static VALUE executeUpdate(VALUE self);
|
222
|
-
static VALUE getResultSet(VALUE self);
|
223
|
-
static VALUE getUpdateCount(VALUE self);
|
224
|
-
static VALUE getGeneratedKeys(VALUE self);
|
124
|
+
ERROR,
|
125
|
+
WARN,
|
126
|
+
INFO,
|
127
|
+
TRACE,
|
128
|
+
DEBUG,
|
129
|
+
NONE
|
225
130
|
};
|
226
131
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
//------------------------------------------------------------------------------
|
256
|
-
|
257
|
-
void WrapDatabaseMetaData::init(VALUE module)
|
258
|
-
{
|
259
|
-
INIT_TYPE("DatabaseMetaData");
|
260
|
-
DEFINE_METHOD(getDatabaseVersion, 0);
|
261
|
-
DEFINE_METHOD(getIndexInfo, 4);
|
262
|
-
DEFINE_METHOD(getColumns, 2);
|
263
|
-
DEFINE_METHOD(getTables, 2);
|
132
|
+
static LogLevel logLevel = NONE;
|
133
|
+
|
134
|
+
static char const * log_level_name(LogLevel level)
|
135
|
+
{
|
136
|
+
char const * level_name = NULL;
|
137
|
+
switch(level)
|
138
|
+
{
|
139
|
+
case NONE:
|
140
|
+
level_name = "NONE";
|
141
|
+
break;
|
142
|
+
case ERROR:
|
143
|
+
level_name = "ERROR";
|
144
|
+
break;
|
145
|
+
case WARN:
|
146
|
+
level_name = "WARN";
|
147
|
+
break;
|
148
|
+
case INFO:
|
149
|
+
level_name = "INFO";
|
150
|
+
break;
|
151
|
+
case DEBUG:
|
152
|
+
level_name = "DEBUG";
|
153
|
+
break;
|
154
|
+
case TRACE:
|
155
|
+
level_name = "TRACE";
|
156
|
+
break;
|
157
|
+
}
|
158
|
+
return level_name;
|
264
159
|
}
|
265
160
|
|
266
|
-
|
161
|
+
static void log(LogLevel level, char const * message)
|
267
162
|
{
|
268
|
-
|
163
|
+
if (level >= logLevel)
|
269
164
|
{
|
270
|
-
|
271
|
-
}
|
272
|
-
catch (SQLException & e)
|
273
|
-
{
|
274
|
-
rb_raise_nuodb_error(e.getSqlcode(), "Get database metadata database version failed: %s", e.getText());
|
165
|
+
printf("[%s] %s\n", log_level_name(level), message);
|
275
166
|
}
|
276
167
|
}
|
277
168
|
|
278
|
-
|
169
|
+
static void trace(char const * message)
|
279
170
|
{
|
280
|
-
|
281
|
-
const char* table = StringValuePtr(tableValue);
|
282
|
-
bool unique = !(RB_TYPE_P(uniqueValue, T_FALSE) || RB_TYPE_P(uniqueValue, T_NIL));
|
283
|
-
bool approx = !(RB_TYPE_P(approxValue, T_FALSE) || RB_TYPE_P(approxValue, T_NIL));
|
284
|
-
try
|
285
|
-
{
|
286
|
-
return WrapResultSet::wrap(asPtr(self)->getIndexInfo(NULL, schema, table, unique, approx));
|
287
|
-
}
|
288
|
-
catch (SQLException & e)
|
289
|
-
{
|
290
|
-
rb_raise_nuodb_error(e.getSqlcode(), "Get database metadata index info failed: %s", e.getText());
|
291
|
-
}
|
171
|
+
log(TRACE, message);
|
292
172
|
}
|
293
173
|
|
294
|
-
|
174
|
+
static void print_address(char const * context, void * address)
|
295
175
|
{
|
296
|
-
|
297
|
-
const char* table = StringValuePtr(tableValue);
|
298
|
-
try
|
176
|
+
if (DEBUG >= logLevel)
|
299
177
|
{
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
{
|
304
|
-
rb_raise_nuodb_error(e.getSqlcode(), "Get columns failed: %s", e.getText());
|
178
|
+
#if defined(__APPLE__)
|
179
|
+
printf("%s: %016" PRIxPTR "\n", context, (uintptr_t) address);
|
180
|
+
#endif
|
305
181
|
}
|
306
182
|
}
|
307
183
|
|
308
|
-
|
184
|
+
// ----------------------------------------------------------------------------
|
185
|
+
// NEXT GEN GC INTEGRATION SERVICES
|
186
|
+
|
187
|
+
#ifdef ENABLE_NEXTGEN_GC_INTEGRATION
|
188
|
+
|
189
|
+
/*
|
190
|
+
* GC Notes:
|
191
|
+
*
|
192
|
+
* 1. gc_entities are placed in the Ruby object table and are freed via mark
|
193
|
+
* and sweep.
|
194
|
+
* 2. gc_entities may be freed in any order.
|
195
|
+
* 3. gc_entities may be freed either before shutdown or during shutdown.
|
196
|
+
* 4. gc_entities may no longer be accessed once freed by the garbage
|
197
|
+
* collector; once gc_entities are freed they no longer appear in the
|
198
|
+
* object table. As such, calls to Data_Get_Struct on previously garbage
|
199
|
+
* collected entities will cause the Ruby VM to crash; viz. SIGABRT via
|
200
|
+
* EXC_BAD_ACCESS.
|
201
|
+
*
|
202
|
+
* a. So a general observation of this is thusly: if e.g. with graphs of
|
203
|
+
* child to parent relationships an orderly de-allocation is required,
|
204
|
+
* as parents (gc_entities) may be freed prior to childrens (gc_entities)
|
205
|
+
* it becomes imperative that the logic between freeing gc_entity objects
|
206
|
+
* be kept completely and distinctly apart from the logic of the managed
|
207
|
+
* entities themselves.
|
208
|
+
*/
|
209
|
+
|
210
|
+
typedef void (*callback)(void *);
|
211
|
+
|
212
|
+
/*
|
213
|
+
* Application Rules:
|
214
|
+
*
|
215
|
+
* 1. Assign a callback for the increment and decrement refers counter so that
|
216
|
+
* context specific logging and tracking may be implemented.
|
217
|
+
* 2. Direct access to refers should never occur.
|
218
|
+
* 3. The freed bit indicates that the resources associated to the data_handle
|
219
|
+
* has been freed, and NOT that the os_entity struct has been freed.
|
220
|
+
*
|
221
|
+
* Logic:
|
222
|
+
*
|
223
|
+
* 1. The incr_func is called on a child when it is first created.
|
224
|
+
* 2. The incr_func is called on a parent when its child is first created.
|
225
|
+
* 3. When incr_func is called, ATOMIC_INC increments the refers field.
|
226
|
+
* 4. When decr_func is called, ATOMIC_DEC decrements the refers field.
|
227
|
+
* 5. When the refers field reaches zero (inside decr_func):
|
228
|
+
* 5.1. ATOMIC-CAS the freed bit, if the prior freed bit is not set:
|
229
|
+
* 5.1.1. ATOMIC-OR all parents freed bits and if the result does not have
|
230
|
+
* the freed bit set:
|
231
|
+
* 5.1.1.1. Call the free function, passing the os_entity itself as a parameter.
|
232
|
+
* The free function releases any resources related to the data
|
233
|
+
* handle itself and sets the handle to null.
|
234
|
+
* 5.2. Call the decr_func on its parent (this may act recursively).
|
235
|
+
* 5.3. Call xfree on the os_entity structure itself.
|
236
|
+
* 6. When a user calls finish on an object:
|
237
|
+
* 6.1. ATOMIC-CAS the freed bit, if the prior freed bit is not set
|
238
|
+
* perform operations 5.1.1 through 5.1.1.1.
|
239
|
+
*
|
240
|
+
* Details:
|
241
|
+
*
|
242
|
+
* 1. Regarding 5a, above, we neither want to doubly release an object, nor
|
243
|
+
* call free_function when a parent has already released all the resources.
|
244
|
+
* This optionally is configurable, for cases when parents forcibly close
|
245
|
+
* child resources, or for cases where they don't. This is a behavioral
|
246
|
+
* configuration parameter to the resource manager. But the above documentation
|
247
|
+
* is tailored for cases where parents automatically release child resources
|
248
|
+
* when they themselves are closed.
|
249
|
+
* 2. A race condition may occur between 5.1 and 5.2, above, where a parent
|
250
|
+
* resource is released just after 5.1.1.1 and before 5.2; the result will
|
251
|
+
* be a SEGV. This may prompt the use of a global lock for the reference
|
252
|
+
* manager.
|
253
|
+
*
|
254
|
+
* Interface:
|
255
|
+
*
|
256
|
+
* 1. Structures as defined below.
|
257
|
+
* 2. Function to release a reference:
|
258
|
+
* void rc_decr_refs (void * handle, int * err_code)
|
259
|
+
* 3. Function to acquire a reference:
|
260
|
+
* void rc_incr_refs (void * handle, int * err_code)
|
261
|
+
* 4. Function to wrap an entity:
|
262
|
+
* void * rc_add_object (void * object, int * err_code)
|
263
|
+
* 5. Function to get wrapped pointer:
|
264
|
+
* void * rc_get_object (void * handle, int * err_code)
|
265
|
+
*
|
266
|
+
* Questions:
|
267
|
+
*
|
268
|
+
* 1. Do we go opaque so that the data type definition is malleable? We don't
|
269
|
+
* want this any more type specific than it is.
|
270
|
+
*/
|
271
|
+
struct os_entity
|
309
272
|
{
|
310
|
-
|
311
|
-
|
273
|
+
void * data_handle;
|
274
|
+
unsigned int flags;
|
275
|
+
rb_atomic_t refers;
|
312
276
|
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
}
|
317
|
-
catch (SQLException & e)
|
318
|
-
{
|
319
|
-
rb_raise_nuodb_error(e.getSqlcode(), "Get tables failed: %s", e.getText());
|
320
|
-
}
|
321
|
-
}
|
277
|
+
callback incr_func;
|
278
|
+
callback decr_func;
|
279
|
+
callback free_func;
|
322
280
|
|
323
|
-
|
281
|
+
os_entity * parent;
|
282
|
+
};
|
324
283
|
|
325
|
-
|
284
|
+
/*
|
285
|
+
* Rules:
|
286
|
+
*
|
287
|
+
* 1. When mark is called, traverse marks list till a null is reached, for
|
288
|
+
* each value call rb_gc_mark.
|
289
|
+
* 2. When the garbage collector frees an object, the decr_function is called
|
290
|
+
* on the entity passing the entity as its parameter.
|
291
|
+
*/
|
292
|
+
struct gc_entity
|
326
293
|
{
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
294
|
+
os_entity * entity;
|
295
|
+
VALUE ** mark_refs;
|
296
|
+
};
|
297
|
+
|
298
|
+
/*
|
299
|
+
* A strict release strategy only permits free calls after the reference count
|
300
|
+
* drops to zero. A lenient release strategy permits free calls at any time,
|
301
|
+
* yet the entity itself is not freed until its reference count drops to zero.
|
302
|
+
*/
|
303
|
+
typedef enum {strict, lenient} free_strategy;
|
304
|
+
|
305
|
+
#endif /* ENABLE_NEXTGEN_GC_INTEGRATION */
|
339
306
|
|
340
|
-
|
307
|
+
// ----------------------------------------------------------------------------
|
308
|
+
// H A N D L E S
|
309
|
+
|
310
|
+
struct nuodb_handle
|
341
311
|
{
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
{
|
348
|
-
rb_raise_nuodb_error(e.getSqlcode(), "Failed to move the cursor forward one row: %s", e.getText());
|
349
|
-
}
|
350
|
-
}
|
312
|
+
RUBY_DATA_FUNC free_func;
|
313
|
+
rb_atomic_t atomic;
|
314
|
+
nuodb_handle * parent_handle;
|
315
|
+
VALUE parent;
|
316
|
+
};
|
351
317
|
|
352
|
-
|
318
|
+
struct nuodb_connection_handle : nuodb_handle
|
353
319
|
{
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
}
|
320
|
+
VALUE database;
|
321
|
+
VALUE username;
|
322
|
+
VALUE password;
|
323
|
+
VALUE schema;
|
324
|
+
VALUE timezone;
|
325
|
+
|
326
|
+
NuoDB::Connection * pointer;
|
327
|
+
};
|
363
328
|
|
364
|
-
|
329
|
+
struct nuodb_prepared_statement_handle : nuodb_handle
|
365
330
|
{
|
366
|
-
|
367
|
-
|
368
|
-
{
|
369
|
-
bool value = asPtr(self)->getBoolean(column);
|
370
|
-
return AS_QBOOL(value);
|
371
|
-
}
|
372
|
-
catch (SQLException & e)
|
373
|
-
{
|
374
|
-
rb_raise_nuodb_error(e.getSqlcode(), "Failed to get boolean value for the designated column (%d): %s", column, e.getText());
|
375
|
-
}
|
376
|
-
}
|
331
|
+
NuoDB::PreparedStatement * pointer;
|
332
|
+
};
|
377
333
|
|
378
|
-
|
334
|
+
struct nuodb_statement_handle : nuodb_handle
|
379
335
|
{
|
380
|
-
|
381
|
-
|
382
|
-
{
|
383
|
-
return INT2NUM(asPtr(self)->getInt(column));
|
384
|
-
}
|
385
|
-
catch (SQLException & e)
|
386
|
-
{
|
387
|
-
rb_raise_nuodb_error(e.getSqlcode(), "Failed to get integer value for the designated column (%d): %s", column, e.getText());
|
388
|
-
}
|
389
|
-
}
|
336
|
+
NuoDB::Statement * pointer;
|
337
|
+
};
|
390
338
|
|
391
|
-
|
339
|
+
struct nuodb_result_handle : nuodb_handle
|
392
340
|
{
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
341
|
+
NuoDB::ResultSet * pointer;
|
342
|
+
NuoDB::Connection * connection;
|
343
|
+
};
|
344
|
+
|
345
|
+
template<typename handle_type>
|
346
|
+
handle_type * cast_handle(VALUE value)
|
347
|
+
{
|
348
|
+
Check_Type(value, T_DATA);
|
349
|
+
return static_cast<handle_type*>(DATA_PTR(value));
|
402
350
|
}
|
403
351
|
|
404
|
-
|
352
|
+
template<typename handle_type, typename return_type>
|
353
|
+
return_type * cast_pointer_member(VALUE value)
|
405
354
|
{
|
406
|
-
|
407
|
-
|
408
|
-
{
|
409
|
-
return rb_str_new2(asPtr(self)->getString(column));
|
410
|
-
}
|
411
|
-
catch (SQLException & e)
|
412
|
-
{
|
413
|
-
rb_raise_nuodb_error(e.getSqlcode(), "Failed to get string value for the designated column (%d): %s", column, e.getText());
|
414
|
-
}
|
355
|
+
Check_Type(value, T_DATA);
|
356
|
+
return cast_handle<handle_type>(value)->pointer;
|
415
357
|
}
|
416
|
-
|
358
|
+
|
359
|
+
static void track_ref_count(char const * context, nuodb_handle * handle)
|
417
360
|
{
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
return rb_str_new2(asPtr(self)->getString(column));
|
422
|
-
//Date* date = asPtr(self)->getDate(column);
|
423
|
-
//SqlDate timestamp(date->getMilliseconds());
|
424
|
-
//struct tm utc;
|
425
|
-
//timestamp.getDate(&utc);
|
426
|
-
//char buffer[250];
|
427
|
-
//::strftime(buffer,sizeof(buffer),"%Y%m%dT%I%M%S+0000",&utc);
|
428
|
-
//return rb_str_new2(buffer);
|
429
|
-
}
|
430
|
-
catch (SQLException & e)
|
361
|
+
trace("track_ref_count");
|
362
|
+
|
363
|
+
if (handle != 0)
|
431
364
|
{
|
432
|
-
|
365
|
+
int parent_count = -10;
|
366
|
+
if (handle->parent_handle != 0)
|
367
|
+
{
|
368
|
+
parent_count = handle->parent_handle->atomic;
|
369
|
+
}
|
370
|
+
if (logLevel <= DEBUG)
|
371
|
+
{
|
372
|
+
#if defined(__APPLE__)
|
373
|
+
nuodb_handle * parent = handle->parent_handle;
|
374
|
+
printf("[REFERENCE COUNT][%s] (%s @ %016" PRIxPTR "): %d (%s @ %016" PRIxPTR "): %d\n",
|
375
|
+
context, demangle(typeid(*handle).name()), (uintptr_t) handle,
|
376
|
+
handle->atomic, demangle(typeid(*parent).name()), (uintptr_t) parent, parent_count);
|
377
|
+
#endif
|
378
|
+
}
|
433
379
|
}
|
434
380
|
}
|
435
381
|
|
436
|
-
|
382
|
+
void incr_reference_count(nuodb_handle * handle)
|
437
383
|
{
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
//struct tm utc;
|
445
|
-
//timestamp.getTime(&utc);
|
446
|
-
//char buffer[250];
|
447
|
-
//::strftime(buffer,sizeof(buffer),"%Y%m%dT%I%M%S+0000",&utc);
|
448
|
-
// Workaround until we fix the Date/Time support
|
449
|
-
// Convert time to local time?
|
450
|
-
//snprintf(buffer, sizeof(buffer), "%u:%u:%u", utc.tm_hour, utc.tm_min, utc.tm_sec);
|
451
|
-
//return rb_str_new2(buffer);
|
452
|
-
}
|
453
|
-
catch (SQLException & e)
|
384
|
+
trace("incr_reference_count");
|
385
|
+
|
386
|
+
track_ref_count("I INCR", handle);
|
387
|
+
|
388
|
+
ATOMIC_INC(handle->atomic);
|
389
|
+
if (handle->parent_handle != 0)
|
454
390
|
{
|
455
|
-
|
391
|
+
log(DEBUG, "incrementing parent");
|
392
|
+
ATOMIC_INC(handle->parent_handle->atomic);
|
456
393
|
}
|
394
|
+
|
395
|
+
track_ref_count("O INCR", handle);
|
457
396
|
}
|
458
397
|
|
459
|
-
|
398
|
+
void decr_reference_count(nuodb_handle * handle)
|
460
399
|
{
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
//SqlTimestamp timestamp(ts->getMilliseconds());
|
467
|
-
//struct tm utc;
|
468
|
-
//timestamp.getTimestamp(&utc);
|
469
|
-
//char buffer[250];
|
470
|
-
//::strftime(buffer,sizeof(buffer),"%Y%m%dT%I%M%S+0000",&utc);
|
471
|
-
//return rb_str_new2(buffer);
|
472
|
-
}
|
473
|
-
catch (SQLException & e)
|
400
|
+
trace("decr_reference_count");
|
401
|
+
|
402
|
+
track_ref_count("I DECR", handle);
|
403
|
+
|
404
|
+
if (handle->atomic == 0)
|
474
405
|
{
|
475
|
-
|
406
|
+
return;
|
476
407
|
}
|
477
|
-
}
|
478
408
|
|
479
|
-
|
480
|
-
{
|
481
|
-
int column = NUM2UINT(columnValue);
|
482
|
-
try
|
409
|
+
if (ATOMIC_DEC(handle->atomic) == 0)
|
483
410
|
{
|
484
|
-
|
411
|
+
(*(handle->free_func))(handle);
|
412
|
+
if (handle->parent_handle != 0)
|
413
|
+
{
|
414
|
+
log(DEBUG, "decrementing parent");
|
415
|
+
decr_reference_count(handle->parent_handle);
|
416
|
+
handle->parent = Qnil;
|
417
|
+
handle->parent_handle = 0;
|
418
|
+
assert(NIL_P(handle->parent));
|
419
|
+
}
|
420
|
+
track_ref_count("O DECR", handle);
|
421
|
+
|
422
|
+
print_address("[DELETING HANDLE]", handle);
|
423
|
+
xfree(handle);
|
424
|
+
handle = NULL;
|
485
425
|
}
|
486
|
-
|
426
|
+
else
|
487
427
|
{
|
488
|
-
|
428
|
+
track_ref_count("O DECR", handle);
|
489
429
|
}
|
490
430
|
}
|
491
431
|
|
492
432
|
//------------------------------------------------------------------------------
|
433
|
+
// exception mapper
|
493
434
|
|
494
|
-
|
495
|
-
{
|
496
|
-
INIT_TYPE("ResultMetaData");
|
497
|
-
DEFINE_METHOD(getColumnCount, 0);
|
498
|
-
DEFINE_METHOD(getColumnName, 1);
|
499
|
-
DEFINE_METHOD(getType, 1);
|
500
|
-
DEFINE_METHOD(getColumnTypeName, 1);
|
501
|
-
DEFINE_METHOD(getScale, 1);
|
502
|
-
DEFINE_METHOD(getPrecision, 1);
|
503
|
-
DEFINE_METHOD(isNullable, 1);
|
504
|
-
}
|
435
|
+
static ID c_error_code_assignment;
|
505
436
|
|
506
|
-
|
437
|
+
static void rb_raise_nuodb_error(int code, const char * fmt, ...)
|
507
438
|
{
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
439
|
+
va_list args;
|
440
|
+
char text[BUFSIZ];
|
441
|
+
|
442
|
+
va_start(args, fmt);
|
443
|
+
vsnprintf(text, BUFSIZ, fmt, args);
|
444
|
+
va_end(args);
|
445
|
+
|
446
|
+
VALUE error = rb_exc_new2(c_nuodb_error, text);
|
447
|
+
rb_funcall(error, c_error_code_assignment, 1, UINT2NUM(code));
|
448
|
+
rb_exc_raise(error);
|
516
449
|
}
|
517
450
|
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
451
|
+
//------------------------------------------------------------------------------
|
452
|
+
|
453
|
+
using namespace NuoDB;
|
454
|
+
|
455
|
+
//------------------------------------------------------------------------------
|
456
|
+
|
457
|
+
static
|
458
|
+
VALUE nuodb_result_free_protect(VALUE value)
|
459
|
+
{
|
460
|
+
trace("nuodb_result_free_protect");
|
461
|
+
nuodb_result_handle * handle = reinterpret_cast<nuodb_result_handle *>(value);
|
462
|
+
if (handle != NULL)
|
463
|
+
{
|
464
|
+
if (handle->pointer != NULL)
|
465
|
+
{
|
466
|
+
try
|
467
|
+
{
|
468
|
+
track_ref_count("CLOSE RESULT", handle);
|
469
|
+
log(INFO, "closing result");
|
470
|
+
handle->pointer->close();
|
471
|
+
handle->pointer = NULL;
|
472
|
+
}
|
473
|
+
catch (SQLException & e)
|
474
|
+
{
|
475
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to successfully close result: %s", e.getText());
|
476
|
+
}
|
477
|
+
}
|
478
|
+
}
|
479
|
+
return Qnil;
|
529
480
|
}
|
530
481
|
|
531
|
-
|
482
|
+
static
|
483
|
+
void nuodb_result_free(void * ptr)
|
532
484
|
{
|
533
|
-
|
534
|
-
|
485
|
+
trace("nuodb_result_free");
|
486
|
+
if (ptr != NULL)
|
535
487
|
{
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
488
|
+
int exception = 0;
|
489
|
+
rb_protect(nuodb_result_free_protect, reinterpret_cast<VALUE>(ptr), &exception);
|
490
|
+
if (exception)
|
491
|
+
{
|
492
|
+
rb_jump_tag(exception);
|
493
|
+
}
|
541
494
|
}
|
542
495
|
}
|
543
496
|
|
544
|
-
|
497
|
+
static
|
498
|
+
void nuodb_result_mark(void * ptr)
|
545
499
|
{
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
return UINT2NUM(asPtr(self)->getPrecision(column));
|
550
|
-
}
|
551
|
-
catch (SQLException & e)
|
552
|
-
{
|
553
|
-
rb_raise_nuodb_error(e.getSqlcode(), "Failed to get precision from the result set metadata: %s", e.getText());
|
554
|
-
}
|
500
|
+
trace("nuodb_result_mark");
|
501
|
+
nuodb_result_handle * handle = static_cast<nuodb_result_handle *>(ptr);
|
502
|
+
rb_gc_mark(handle->parent);
|
555
503
|
}
|
556
504
|
|
557
|
-
|
505
|
+
static
|
506
|
+
void nuodb_result_decr_reference_count(nuodb_handle * handle)
|
558
507
|
{
|
559
|
-
|
560
|
-
|
561
|
-
{
|
562
|
-
return AS_QBOOL(asPtr(self)->isNullable(column));
|
563
|
-
}
|
564
|
-
catch (SQLException & e)
|
565
|
-
{
|
566
|
-
rb_raise_nuodb_error(e.getSqlcode(), "Failed to determine nullability from the result set metadata: %s", e.getText());
|
567
|
-
}
|
508
|
+
trace("nuodb_result_decr_reference_count");
|
509
|
+
decr_reference_count(handle);
|
568
510
|
}
|
569
511
|
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
512
|
+
/*
|
513
|
+
* call-seq:
|
514
|
+
* finish()
|
515
|
+
*
|
516
|
+
* Releases the result set and any associated resources.
|
517
|
+
*/
|
518
|
+
static
|
519
|
+
VALUE nuodb_result_finish(VALUE self)
|
520
|
+
{
|
521
|
+
trace("nuodb_result_free_protect");
|
522
|
+
nuodb_result_handle * handle = cast_handle<nuodb_result_handle>(self);//reinterpret_cast<nuodb_result_handle *>(value);
|
523
|
+
nuodb_result_free(handle);
|
524
|
+
// if (handle != NULL)
|
525
|
+
// {
|
526
|
+
// if (handle->pointer != NULL)
|
527
|
+
// {
|
528
|
+
// try
|
529
|
+
// {
|
530
|
+
// //handle->pointer->close();
|
531
|
+
// //handle->pointer = NULL;
|
532
|
+
// }
|
533
|
+
// catch (SQLException & e)
|
534
|
+
// {
|
535
|
+
// rb_raise_nuodb_error(e.getSqlcode(), "Failed to successfully close result: %s", e.getText());
|
536
|
+
// }
|
537
|
+
// }
|
538
|
+
// }
|
539
|
+
return Qnil;
|
540
|
+
//
|
541
|
+
//
|
542
|
+
//
|
543
|
+
// trace("nuodb_result_finish");
|
544
|
+
// if (ENABLE_CLOSE_HOOK)
|
545
|
+
// {
|
546
|
+
// //nuodb_result_decr_reference_count(cast_handle<nuodb_result_handle>(self));
|
547
|
+
// }
|
548
|
+
// nuodb_result_handle * handle = ;
|
549
|
+
// if (handle != NULL && handle->pointer != NULL)
|
550
|
+
// {
|
551
|
+
// if (ATOMIC_DEC(handle->atomic) == 0)
|
552
|
+
// {
|
553
|
+
// try
|
554
|
+
// {
|
555
|
+
// //handle->pointer->close();
|
556
|
+
// //handle->pointer = NULL;
|
557
|
+
// }
|
558
|
+
// catch (SQLException & e)
|
559
|
+
// {
|
560
|
+
// rb_raise_nuodb_error(e.getSqlcode(), "Failed to successfully close result set: %s", e.getText());
|
561
|
+
// }
|
562
|
+
// }
|
563
|
+
// }
|
564
|
+
// return Qnil;
|
581
565
|
}
|
582
566
|
|
583
|
-
|
567
|
+
static
|
568
|
+
VALUE nuodb_map_sql_type(int type)
|
584
569
|
{
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
switch(t)
|
570
|
+
VALUE symbol = Qnil;
|
571
|
+
switch(type)
|
589
572
|
{
|
590
|
-
case NUOSQL_NULL:
|
591
|
-
return SYMBOL_OF(SQL_NULL);
|
592
|
-
case NUOSQL_BIT:
|
593
|
-
return SYMBOL_OF(SQL_BIT);
|
594
573
|
case NUOSQL_TINYINT:
|
595
|
-
return SYMBOL_OF(SQL_TINYINT);
|
596
574
|
case NUOSQL_SMALLINT:
|
597
|
-
return SYMBOL_OF(SQL_SMALLINT);
|
598
575
|
case NUOSQL_INTEGER:
|
599
|
-
return SYMBOL_OF(SQL_INTEGER);
|
600
576
|
case NUOSQL_BIGINT:
|
601
|
-
|
577
|
+
symbol = ID2SYM(rb_intern("integer"));
|
578
|
+
break;
|
579
|
+
|
602
580
|
case NUOSQL_FLOAT:
|
603
|
-
return SYMBOL_OF(SQL_FLOAT);
|
604
581
|
case NUOSQL_DOUBLE:
|
605
|
-
|
582
|
+
symbol = ID2SYM(rb_intern("float"));
|
583
|
+
break;
|
584
|
+
|
606
585
|
case NUOSQL_CHAR:
|
607
|
-
return SYMBOL_OF(SQL_CHAR);
|
608
586
|
case NUOSQL_VARCHAR:
|
609
|
-
return SYMBOL_OF(SQL_STRING);
|
610
587
|
case NUOSQL_LONGVARCHAR:
|
611
|
-
|
588
|
+
symbol = ID2SYM(rb_intern("string"));
|
589
|
+
break;
|
590
|
+
|
591
|
+
case NUOSQL_BIT:
|
592
|
+
case NUOSQL_BOOLEAN:
|
593
|
+
symbol = ID2SYM(rb_intern("boolean"));
|
594
|
+
break;
|
595
|
+
|
612
596
|
case NUOSQL_DATE:
|
613
|
-
|
614
|
-
|
615
|
-
|
597
|
+
symbol = ID2SYM(rb_intern("date"));
|
598
|
+
break;
|
599
|
+
|
616
600
|
case NUOSQL_TIMESTAMP:
|
617
|
-
|
601
|
+
symbol = ID2SYM(rb_intern("timestamp"));
|
602
|
+
break;
|
603
|
+
|
604
|
+
case NUOSQL_TIME:
|
605
|
+
symbol = ID2SYM(rb_intern("time"));
|
606
|
+
break;
|
607
|
+
|
608
|
+
case NUOSQL_DECIMAL:
|
609
|
+
symbol = ID2SYM(rb_intern("decimal"));
|
610
|
+
break;
|
611
|
+
|
612
|
+
case NUOSQL_NUMERIC:
|
613
|
+
symbol = ID2SYM(rb_intern("numeric"));
|
614
|
+
break;
|
615
|
+
|
616
|
+
// case NUOSQL_BLOB:
|
617
|
+
// symbol = ID2SYM(rb_intern("blob"));
|
618
|
+
// break;
|
619
|
+
//
|
620
|
+
// case NUOSQL_CLOB:
|
621
|
+
// symbol = ID2SYM(rb_intern("clob"));
|
622
|
+
// break;
|
623
|
+
|
624
|
+
case NUOSQL_NULL:
|
618
625
|
case NUOSQL_BLOB:
|
619
|
-
return SYMBOL_OF(SQL_BLOB);
|
620
626
|
case NUOSQL_CLOB:
|
621
|
-
return SYMBOL_OF(SQL_CLOB);
|
622
|
-
case NUOSQL_NUMERIC:
|
623
|
-
return SYMBOL_OF(SQL_NUMERIC);
|
624
|
-
case NUOSQL_DECIMAL:
|
625
|
-
return SYMBOL_OF(SQL_DECIMAL);
|
626
|
-
case NUOSQL_BOOLEAN:
|
627
|
-
return SYMBOL_OF(SQL_BOOLEAN);
|
628
627
|
case NUOSQL_BINARY:
|
629
|
-
return SYMBOL_OF(SQL_BINARY);
|
630
628
|
case NUOSQL_LONGVARBINARY:
|
631
|
-
return SYMBOL_OF(SQL_LONGVARBINARY);
|
632
629
|
default:
|
633
|
-
|
634
|
-
|
630
|
+
rb_raise(rb_eNotImpError, "Unsupported SQL type: %d", type);
|
631
|
+
}
|
632
|
+
return symbol;
|
633
|
+
}
|
634
|
+
static
|
635
|
+
VALUE nuodb_result_alloc(VALUE parent, NuoDB::ResultSet * results, NuoDB::Connection * connection)
|
636
|
+
{
|
637
|
+
trace("nuodb_result_alloc");
|
638
|
+
nuodb_handle * parent_handle = cast_handle<nuodb_handle>(parent);
|
639
|
+
if (parent_handle != NULL)
|
640
|
+
{
|
641
|
+
nuodb_result_handle * handle = ALLOC(struct nuodb_result_handle);
|
642
|
+
handle->free_func = RUBY_DATA_FUNC(nuodb_result_free);
|
643
|
+
handle->atomic = 0;
|
644
|
+
handle->parent = parent;
|
645
|
+
handle->parent_handle = parent_handle;
|
646
|
+
handle->pointer = results;
|
647
|
+
handle->connection = connection;
|
648
|
+
incr_reference_count(handle);
|
649
|
+
VALUE self = Data_Wrap_Struct(nuodb_result_klass, nuodb_result_mark, nuodb_result_decr_reference_count, handle);
|
650
|
+
|
651
|
+
rb_iv_set(self, "@columns", Qnil);
|
652
|
+
rb_iv_set(self, "@rows", Qnil);
|
653
|
+
|
654
|
+
if (!rb_block_given_p()) {
|
655
|
+
trace("nuodb_result_alloc: no block");
|
656
|
+
|
657
|
+
return self;
|
658
|
+
}
|
659
|
+
|
660
|
+
trace("nuodb_result_alloc: begin block");
|
661
|
+
|
662
|
+
int exception = 0;
|
663
|
+
VALUE result = rb_protect(rb_yield, self, &exception);
|
664
|
+
|
665
|
+
trace("nuodb_result_alloc: end block");
|
666
|
+
|
667
|
+
//nuodb_result_finish(self);
|
668
|
+
|
669
|
+
trace("nuodb_result_alloc: auto finish");
|
670
|
+
|
671
|
+
if (exception)
|
672
|
+
{
|
673
|
+
rb_jump_tag(exception);
|
674
|
+
}
|
675
|
+
else
|
676
|
+
{
|
677
|
+
return result;
|
678
|
+
}
|
635
679
|
}
|
680
|
+
else
|
681
|
+
{
|
682
|
+
rb_raise(rb_eArgError, "invalid state: statement handle nil");
|
683
|
+
}
|
684
|
+
return Qnil;
|
685
|
+
}
|
686
|
+
|
687
|
+
static int64_t
|
688
|
+
nuodb_get_rb_timezone_offset()
|
689
|
+
{
|
690
|
+
VALUE time = rb_funcall(rb_cTime, rb_intern("at"), 1, rb_float_new(0));
|
691
|
+
VALUE offset = rb_funcall(time, rb_intern("utc_offset"), 0);
|
692
|
+
return NUM2LONG(offset);
|
693
|
+
}
|
694
|
+
|
695
|
+
static VALUE
|
696
|
+
nuodb_get_rb_value(int column, SqlType type, ResultSet * results)
|
697
|
+
{
|
698
|
+
VALUE value = Qnil;
|
699
|
+
|
700
|
+
switch (type)
|
701
|
+
{
|
702
|
+
case NUOSQL_BIT:
|
703
|
+
case NUOSQL_BOOLEAN:
|
704
|
+
{
|
705
|
+
// try-catch b.c. http://tools/jira/browse/DB-2379
|
706
|
+
try
|
707
|
+
{
|
708
|
+
bool field = results->getBoolean(column);
|
709
|
+
if (!results->wasNull())
|
710
|
+
{
|
711
|
+
value = AS_QBOOL(field);
|
712
|
+
}
|
713
|
+
}
|
714
|
+
catch (SQLException & e)
|
715
|
+
{
|
716
|
+
// see JDBC spec, DB-2379, however, according to RoR rules this
|
717
|
+
// should return nil. See the following test case:
|
718
|
+
// test_default_values_on_empty_strings(BasicsTest) [test/cases/base_test.rb:]
|
719
|
+
}
|
720
|
+
break;
|
721
|
+
}
|
722
|
+
case NUOSQL_FLOAT:
|
723
|
+
case NUOSQL_DOUBLE:
|
724
|
+
{
|
725
|
+
double field = results->getDouble(column);
|
726
|
+
if (!results->wasNull())
|
727
|
+
{
|
728
|
+
value = rb_float_new(field);
|
729
|
+
}
|
730
|
+
break;
|
731
|
+
}
|
732
|
+
case NUOSQL_TINYINT:
|
733
|
+
case NUOSQL_SMALLINT:
|
734
|
+
case NUOSQL_INTEGER:
|
735
|
+
{
|
736
|
+
int field = results->getInt(column);
|
737
|
+
if (!results->wasNull())
|
738
|
+
{
|
739
|
+
value = INT2NUM(field);
|
740
|
+
}
|
741
|
+
break;
|
742
|
+
}
|
743
|
+
case NUOSQL_BIGINT:
|
744
|
+
{
|
745
|
+
int64_t field = results->getLong(column);
|
746
|
+
if (!results->wasNull())
|
747
|
+
{
|
748
|
+
value = LONG2NUM(field);
|
749
|
+
}
|
750
|
+
break;
|
751
|
+
}
|
752
|
+
case NUOSQL_CHAR:
|
753
|
+
case NUOSQL_VARCHAR:
|
754
|
+
case NUOSQL_LONGVARCHAR:
|
755
|
+
{
|
756
|
+
char const * field = results->getString(column);
|
757
|
+
if (!results->wasNull())
|
758
|
+
{
|
759
|
+
value = rb_str_new2(field);
|
760
|
+
}
|
761
|
+
break;
|
762
|
+
}
|
763
|
+
case NUOSQL_DATE:
|
764
|
+
{
|
765
|
+
NuoDB::Date * field = results->getDate(column);
|
766
|
+
if (!results->wasNull())
|
767
|
+
{
|
768
|
+
double secs = (double) field->getSeconds();
|
769
|
+
VALUE time = rb_funcall(rb_cTime, rb_intern("at"), 1, rb_float_new(secs - nuodb_get_rb_timezone_offset()));
|
770
|
+
value = rb_funcall(time, rb_intern("to_date"), 0);
|
771
|
+
}
|
772
|
+
break;
|
773
|
+
}
|
774
|
+
case NUOSQL_TIME:
|
775
|
+
case NUOSQL_TIMESTAMP:
|
776
|
+
{
|
777
|
+
NuoDB::Timestamp * field = results->getTimestamp(column);
|
778
|
+
if (!results->wasNull())
|
779
|
+
{
|
780
|
+
double secs = ((double) field->getSeconds()) + (((double) field->getNanos()) / 1000000);
|
781
|
+
value = rb_funcall(rb_cTime, rb_intern("at"), 1, rb_float_new(secs)); // - nuodb_get_rb_timezone_offset()
|
782
|
+
}
|
783
|
+
break;
|
784
|
+
}
|
785
|
+
case NUOSQL_NUMERIC:
|
786
|
+
{
|
787
|
+
char const * field = results->getString(column);
|
788
|
+
if (!results->wasNull())
|
789
|
+
{
|
790
|
+
rb_require("bigdecimal");
|
791
|
+
VALUE klass = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
|
792
|
+
VALUE args[1];
|
793
|
+
args[0] = rb_str_new2(field);
|
794
|
+
value = rb_class_new_instance(1, args, klass);
|
795
|
+
}
|
796
|
+
break;
|
797
|
+
}
|
798
|
+
default:
|
799
|
+
{
|
800
|
+
rb_raise(rb_eTypeError, "not a supported ruby type: %d", type);
|
801
|
+
break;
|
802
|
+
}
|
803
|
+
}
|
804
|
+
return value;
|
805
|
+
}
|
806
|
+
|
807
|
+
/*
|
808
|
+
* call-seq:
|
809
|
+
* result.columns -> ary
|
810
|
+
*
|
811
|
+
* Returns an array of Column objects.
|
812
|
+
*
|
813
|
+
* results = statement.results
|
814
|
+
* ...
|
815
|
+
* results.columns.each do |column|
|
816
|
+
* puts "#{column.name}, #{column.default}, #{column.type}, #{column.null}"
|
817
|
+
* end
|
818
|
+
*/
|
819
|
+
static VALUE
|
820
|
+
nuodb_result_columns(VALUE self)
|
821
|
+
{
|
822
|
+
trace("nuodb_result_columns");
|
823
|
+
nuodb_result_handle * handle = cast_handle<nuodb_result_handle>(self);
|
824
|
+
if (handle != NULL && handle->pointer != NULL)
|
825
|
+
{
|
826
|
+
VALUE columns = rb_iv_get(self, "@columns");
|
827
|
+
if (NIL_P(columns))
|
828
|
+
{
|
829
|
+
try
|
830
|
+
{
|
831
|
+
ResultSetMetaData * result_metadata = handle->pointer->getMetaData();
|
832
|
+
DatabaseMetaData * database_metadata = handle->connection->getMetaData();
|
833
|
+
|
834
|
+
VALUE array = rb_ary_new();
|
835
|
+
rb_require("nuodb/column");
|
836
|
+
VALUE column_klass = rb_const_get(m_nuodb, rb_intern("Column"));
|
837
|
+
|
838
|
+
int column_count = result_metadata->getColumnCount();
|
839
|
+
for (int column_index = 1; column_index < column_count + 1; ++column_index)
|
840
|
+
{
|
841
|
+
char const * schema_name = result_metadata->getSchemaName(column_index);
|
842
|
+
char const * table_name = result_metadata->getTableName(column_index);
|
843
|
+
char const * column_name = result_metadata->getColumnName(column_index);
|
844
|
+
|
845
|
+
// args: [name, default, sql_type, null]
|
846
|
+
VALUE args[4];
|
847
|
+
|
848
|
+
args[0] = rb_str_new2(result_metadata->getColumnLabel(column_index));
|
849
|
+
|
850
|
+
ResultSet * database_metadata_results = database_metadata->getColumns(NULL,
|
851
|
+
schema_name, table_name, column_name);
|
852
|
+
|
853
|
+
if(database_metadata_results->next())
|
854
|
+
{
|
855
|
+
try
|
856
|
+
{
|
857
|
+
args[1] = nuodb_get_rb_value(database_metadata_results->findColumn("COLUMN_DEF"),
|
858
|
+
(SqlType) result_metadata->getColumnType(column_index), database_metadata_results);
|
859
|
+
}
|
860
|
+
catch (SQLException & e)
|
861
|
+
{
|
862
|
+
args[1] = Qnil;
|
863
|
+
}
|
864
|
+
}
|
865
|
+
else
|
866
|
+
{
|
867
|
+
args[1] = Qnil;
|
868
|
+
}
|
869
|
+
database_metadata_results->close();
|
870
|
+
|
871
|
+
args[2] = nuodb_map_sql_type(result_metadata->getColumnType(column_index));
|
872
|
+
args[3] = result_metadata->isNullable(column_index) ? Qtrue : Qfalse;
|
873
|
+
|
874
|
+
VALUE column = rb_class_new_instance(4, args, column_klass);
|
875
|
+
rb_ary_push(array, column);
|
876
|
+
}
|
877
|
+
|
878
|
+
rb_iv_set(self, "@columns", array);
|
879
|
+
|
880
|
+
return array;
|
881
|
+
}
|
882
|
+
catch (SQLException & e)
|
883
|
+
{
|
884
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to create column info: %s", e.getText());
|
885
|
+
}
|
886
|
+
}
|
887
|
+
else
|
888
|
+
{
|
889
|
+
return columns;
|
890
|
+
}
|
891
|
+
}
|
892
|
+
else
|
893
|
+
{
|
894
|
+
rb_raise(rb_eArgError, "invalid state: result handle nil");
|
895
|
+
}
|
896
|
+
return Qnil;
|
897
|
+
}
|
898
|
+
|
899
|
+
/*
|
900
|
+
* call-seq:
|
901
|
+
* result.rows -> ary
|
902
|
+
*
|
903
|
+
* Returns an array of rows, each of which is an array of values.
|
904
|
+
*
|
905
|
+
* Note that calling #rows for large result sets is sub-optimal as it will load
|
906
|
+
* the entire dataset into memory. Users should prefer calling #each over #rows.
|
907
|
+
*
|
908
|
+
* results = statement.results
|
909
|
+
* ...
|
910
|
+
* results.rows.each do |row|
|
911
|
+
* row.each do |value|
|
912
|
+
* puts value
|
913
|
+
* end
|
914
|
+
* end
|
915
|
+
*/
|
916
|
+
static VALUE
|
917
|
+
nuodb_result_rows(VALUE self)
|
918
|
+
{
|
919
|
+
trace("nuodb_result_rows");
|
920
|
+
nuodb_result_handle * handle = cast_handle<nuodb_result_handle>(self);
|
921
|
+
if (handle != NULL && handle->pointer != NULL)
|
922
|
+
{
|
923
|
+
VALUE rows = rb_iv_get(self, "@rows");
|
924
|
+
if (NIL_P(rows))
|
925
|
+
{
|
926
|
+
try
|
927
|
+
{
|
928
|
+
rows = rb_ary_new();
|
929
|
+
|
930
|
+
while (handle->pointer->next())
|
931
|
+
{
|
932
|
+
VALUE row = rb_ary_new();
|
933
|
+
|
934
|
+
NuoDB::ResultSetMetaData * metadata = handle->pointer->getMetaData();
|
935
|
+
int32_t column_count = metadata->getColumnCount();
|
936
|
+
for (int32_t column = 1; column < column_count + 1; column++)
|
937
|
+
{
|
938
|
+
SqlType type = (SqlType) metadata->getColumnType(column);
|
939
|
+
rb_ary_push(row, nuodb_get_rb_value(column, type, handle->pointer));
|
940
|
+
}
|
941
|
+
|
942
|
+
rb_ary_push(rows, row);
|
943
|
+
}
|
944
|
+
|
945
|
+
rb_iv_set(self, "@rows", rows);
|
946
|
+
}
|
947
|
+
catch (SQLException & e)
|
948
|
+
{
|
949
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to create a rows array: %s", e.getText());
|
950
|
+
}
|
951
|
+
}
|
952
|
+
return rows;
|
953
|
+
}
|
954
|
+
else
|
955
|
+
{
|
956
|
+
rb_raise(rb_eArgError, "invalid state: result handle nil");
|
957
|
+
}
|
958
|
+
return Qnil;
|
959
|
+
}
|
960
|
+
|
961
|
+
/*
|
962
|
+
* call-seq:
|
963
|
+
* result.each { |tuple| ... }
|
964
|
+
*
|
965
|
+
* Invokes the block for each tuple in the result set.
|
966
|
+
*
|
967
|
+
* connection.prepare select_dml do |select|
|
968
|
+
* ...
|
969
|
+
* if select.execute
|
970
|
+
* select.results.each do |row|
|
971
|
+
* ...
|
972
|
+
* end
|
973
|
+
* end
|
974
|
+
* end
|
975
|
+
*/
|
976
|
+
static VALUE
|
977
|
+
nuodb_result_each(VALUE self)
|
978
|
+
{
|
979
|
+
trace("nuodb_result_each");
|
980
|
+
nuodb_result_handle * handle = cast_handle<nuodb_result_handle>(self);
|
981
|
+
if (handle != NULL && handle->pointer != NULL)
|
982
|
+
{
|
983
|
+
VALUE rows = rb_iv_get(self, "@rows");
|
984
|
+
if (NIL_P(rows))
|
985
|
+
{
|
986
|
+
while (handle->pointer->next())
|
987
|
+
{
|
988
|
+
VALUE array = rb_ary_new();
|
989
|
+
|
990
|
+
NuoDB::ResultSetMetaData * metadata = handle->pointer->getMetaData();
|
991
|
+
int32_t column_count = metadata->getColumnCount();
|
992
|
+
for (int32_t column = 1; column < column_count + 1; column++)
|
993
|
+
{
|
994
|
+
SqlType type = (SqlType) metadata->getColumnType(column);
|
995
|
+
rb_ary_push(array, nuodb_get_rb_value(column, type, handle->pointer));
|
996
|
+
}
|
997
|
+
|
998
|
+
rb_yield(array);
|
999
|
+
}
|
1000
|
+
}
|
1001
|
+
else
|
1002
|
+
{
|
1003
|
+
for (int i = 0; i < RARRAY_LEN(rows); i++)
|
1004
|
+
{
|
1005
|
+
rb_yield(rb_ary_entry(rows, i));
|
1006
|
+
}
|
1007
|
+
}
|
1008
|
+
return self;
|
1009
|
+
}
|
1010
|
+
return Qnil;
|
636
1011
|
}
|
637
1012
|
|
638
|
-
|
1013
|
+
static
|
1014
|
+
void nuodb_define_result_api()
|
1015
|
+
{
|
1016
|
+
/*
|
1017
|
+
* A Result object maintains a connection to a specific database. SQL
|
1018
|
+
* statements are executed and results are returned within the context of
|
1019
|
+
* a connection.
|
1020
|
+
*/
|
1021
|
+
nuodb_result_klass = rb_define_class_under(m_nuodb, "Result", rb_cObject);
|
1022
|
+
rb_include_module(nuodb_result_klass, rb_mEnumerable);
|
1023
|
+
|
1024
|
+
rb_define_attr(nuodb_result_klass, "columns", 1, 0);
|
1025
|
+
rb_define_attr(nuodb_result_klass, "rows", 1, 0);
|
1026
|
+
|
1027
|
+
// DBI
|
1028
|
+
|
1029
|
+
rb_define_method(nuodb_result_klass, "each", RUBY_METHOD_FUNC(nuodb_result_each), 0);
|
1030
|
+
rb_define_method(nuodb_result_klass, "columns", RUBY_METHOD_FUNC(nuodb_result_columns), 0);
|
1031
|
+
rb_define_method(nuodb_result_klass, "rows", RUBY_METHOD_FUNC(nuodb_result_rows), 0);
|
1032
|
+
//rb_define_method(nuodb_result_klass, "finish", RUBY_METHOD_FUNC(nuodb_result_finish), 0);
|
1033
|
+
}
|
1034
|
+
|
1035
|
+
//------------------------------------------------------------------------------
|
1036
|
+
|
1037
|
+
static
|
1038
|
+
VALUE nuodb_statement_free_protect(VALUE value)
|
1039
|
+
{
|
1040
|
+
trace("nuodb_statement_free_protect");
|
1041
|
+
nuodb_statement_handle * handle = reinterpret_cast<nuodb_statement_handle*>(value);
|
1042
|
+
if (handle != NULL)
|
1043
|
+
{
|
1044
|
+
if (handle->pointer != NULL)
|
1045
|
+
{
|
1046
|
+
try
|
1047
|
+
{
|
1048
|
+
log(INFO, "closing statement");
|
1049
|
+
handle->pointer->close();
|
1050
|
+
handle->pointer = NULL;
|
1051
|
+
}
|
1052
|
+
catch (SQLException & e)
|
1053
|
+
{
|
1054
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to successfully close statement: %s", e.getText());
|
1055
|
+
}
|
1056
|
+
}
|
1057
|
+
}
|
1058
|
+
return Qnil;
|
1059
|
+
}
|
1060
|
+
|
1061
|
+
static
|
1062
|
+
void nuodb_statement_free(void * ptr)
|
1063
|
+
{
|
1064
|
+
trace("nuodb_statement_free");
|
1065
|
+
if (ptr != NULL)
|
1066
|
+
{
|
1067
|
+
int exception = 0;
|
1068
|
+
rb_protect(nuodb_statement_free_protect, reinterpret_cast<VALUE>(ptr), &exception);
|
1069
|
+
if (exception)
|
1070
|
+
{
|
1071
|
+
rb_jump_tag(exception);
|
1072
|
+
}
|
1073
|
+
}
|
1074
|
+
}
|
1075
|
+
|
1076
|
+
static
|
1077
|
+
void nuodb_statement_mark(void * ptr)
|
1078
|
+
{
|
1079
|
+
trace("nuodb_statement_mark");
|
1080
|
+
|
1081
|
+
nuodb_statement_handle * handle = static_cast<nuodb_statement_handle *>(ptr);
|
1082
|
+
rb_gc_mark(handle->parent);
|
1083
|
+
}
|
639
1084
|
|
640
|
-
|
1085
|
+
static
|
1086
|
+
void nuodb_statement_decr_reference_count(void * ptr)
|
641
1087
|
{
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
1088
|
+
trace("nuodb_statement_decr_reference_count");
|
1089
|
+
decr_reference_count(static_cast<nuodb_statement_handle *>(ptr));
|
1090
|
+
}
|
1091
|
+
|
1092
|
+
/*
|
1093
|
+
* call-seq:
|
1094
|
+
* finish()
|
1095
|
+
*
|
1096
|
+
* Releases the statement and any associated resources.
|
1097
|
+
*/
|
1098
|
+
static
|
1099
|
+
VALUE nuodb_statement_finish(VALUE self)
|
1100
|
+
{
|
1101
|
+
trace("nuodb_statement_finish");
|
1102
|
+
nuodb_statement_handle * handle = cast_handle<nuodb_statement_handle>(self);//reinterpret_cast<nuodb_statement_handle*>(value);
|
1103
|
+
nuodb_statement_free(handle);
|
1104
|
+
// if (handle != NULL)
|
1105
|
+
// {
|
1106
|
+
// if (handle->pointer != NULL)
|
1107
|
+
// {
|
1108
|
+
// try
|
1109
|
+
// {
|
1110
|
+
// log(INFO, "closing statement");
|
1111
|
+
// handle->pointer->close();
|
1112
|
+
// handle->pointer = NULL;
|
1113
|
+
// }
|
1114
|
+
// catch (SQLException & e)
|
1115
|
+
// {
|
1116
|
+
// rb_raise_nuodb_error(e.getSqlcode(), "Failed to successfully close statement: %s", e.getText());
|
1117
|
+
// }
|
1118
|
+
// }
|
1119
|
+
// }
|
1120
|
+
return Qnil;
|
1121
|
+
//
|
1122
|
+
// if (ENABLE_CLOSE_HOOK)
|
1123
|
+
// {
|
1124
|
+
// nuodb_statement_handle * handle = cast_handle<nuodb_statement_handle>(self);
|
1125
|
+
// if (handle != NULL && handle->pointer != NULL)
|
1126
|
+
// {
|
1127
|
+
// nuodb_statement_decr_reference_count(handle);
|
1128
|
+
// }
|
1129
|
+
// else
|
1130
|
+
// {
|
1131
|
+
// rb_raise(rb_eArgError, "invalid state: statement handle nil");
|
1132
|
+
// }
|
1133
|
+
// }
|
1134
|
+
// return Qnil;
|
650
1135
|
}
|
651
1136
|
|
652
|
-
|
1137
|
+
static
|
1138
|
+
VALUE nuodb_statement_initialize(VALUE parent)
|
653
1139
|
{
|
654
|
-
|
1140
|
+
trace("nuodb_statement_initialize");
|
1141
|
+
|
1142
|
+
nuodb_connection_handle * parent_handle = cast_handle<nuodb_connection_handle>(parent);
|
1143
|
+
if (parent_handle != NULL && parent_handle->pointer != NULL)
|
655
1144
|
{
|
656
|
-
|
657
|
-
|
1145
|
+
NuoDB::Statement * statement = NULL;
|
1146
|
+
try
|
1147
|
+
{
|
1148
|
+
statement = parent_handle->pointer->createStatement();
|
1149
|
+
}
|
1150
|
+
catch (SQLException & e)
|
1151
|
+
{
|
1152
|
+
log(ERROR, "rb_raise");
|
1153
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to create statement: %s", e.getText());
|
1154
|
+
}
|
1155
|
+
|
1156
|
+
nuodb_statement_handle * handle = ALLOC(nuodb_statement_handle);
|
1157
|
+
|
1158
|
+
handle->free_func = RUBY_DATA_FUNC(&nuodb_statement_free);
|
1159
|
+
handle->atomic = 0;
|
1160
|
+
handle->parent = parent;
|
1161
|
+
handle->parent_handle = parent_handle;
|
1162
|
+
handle->pointer = statement;
|
1163
|
+
incr_reference_count(handle);
|
1164
|
+
assert(handle->atomic = 1);
|
1165
|
+
VALUE self = Data_Wrap_Struct(nuodb_statement_klass, nuodb_statement_mark, nuodb_statement_decr_reference_count, handle);
|
1166
|
+
if (!rb_block_given_p()) {
|
1167
|
+
trace("nuodb_statement_initialize: no block");
|
1168
|
+
track_ref_count("ALLOC STMT S", cast_handle<nuodb_handle>(self));
|
1169
|
+
return self;
|
1170
|
+
}
|
1171
|
+
|
1172
|
+
trace("nuodb_statement_initialize: begin block");
|
1173
|
+
|
1174
|
+
int exception = 0;
|
1175
|
+
VALUE result = rb_protect(rb_yield, self, &exception);
|
1176
|
+
|
1177
|
+
trace("nuodb_statement_initialize: end block");
|
1178
|
+
|
1179
|
+
// n.b. don't do this as it may introduce crashes of the ruby process !!!
|
1180
|
+
//nuodb_statement_finish(self);
|
1181
|
+
|
1182
|
+
trace("nuodb_statement_initialize: auto finish");
|
1183
|
+
|
1184
|
+
if (exception)
|
1185
|
+
{
|
1186
|
+
rb_jump_tag(exception);
|
1187
|
+
}
|
1188
|
+
else
|
1189
|
+
{
|
1190
|
+
return result;
|
1191
|
+
}
|
658
1192
|
}
|
659
|
-
|
1193
|
+
else
|
660
1194
|
{
|
661
|
-
|
1195
|
+
rb_raise(rb_eArgError, "invalid state: statement handle nil");
|
662
1196
|
}
|
1197
|
+
return Qnil;
|
663
1198
|
}
|
664
1199
|
|
665
|
-
|
1200
|
+
/*
|
1201
|
+
* call-seq:
|
1202
|
+
* execute(sql) -> Bool
|
1203
|
+
*
|
1204
|
+
* Executes a statement.
|
1205
|
+
*
|
1206
|
+
* Returns true if the result is a Result object; returns false if the result is
|
1207
|
+
* an update count or there is no result. For the latter case, count() should be
|
1208
|
+
* called next, for the former case each() should be called.
|
1209
|
+
*/
|
1210
|
+
static
|
1211
|
+
VALUE nuodb_statement_execute(VALUE self, VALUE sql)
|
666
1212
|
{
|
667
|
-
|
668
|
-
try
|
1213
|
+
if (TYPE(sql) != T_STRING)
|
669
1214
|
{
|
670
|
-
|
1215
|
+
rb_raise(rb_eTypeError, "wrong sql argument type %s (String expected)", rb_class2name(CLASS_OF(sql)));
|
671
1216
|
}
|
672
|
-
catch (SQLException & e)
|
673
|
-
{
|
674
|
-
rb_raise_nuodb_error(e.getSqlcode(), "Failed to execute SQL statement (\"%s\"): %s", sql, e.getText());
|
675
|
-
}
|
676
|
-
}
|
677
1217
|
|
678
|
-
|
679
|
-
|
680
|
-
const char* sql = StringValuePtr(sqlValue);
|
681
|
-
try
|
1218
|
+
nuodb_statement_handle * handle = cast_handle<nuodb_statement_handle>(self);
|
1219
|
+
if (handle != NULL && handle->pointer != NULL)
|
682
1220
|
{
|
683
|
-
|
1221
|
+
try
|
1222
|
+
{
|
1223
|
+
return AS_QBOOL(handle->pointer->execute(StringValuePtr(sql),
|
1224
|
+
NuoDB::RETURN_GENERATED_KEYS));
|
1225
|
+
}
|
1226
|
+
catch (SQLException & e)
|
1227
|
+
{
|
1228
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to execute SQL statement: %s", e.getText());
|
1229
|
+
}
|
684
1230
|
}
|
685
|
-
|
1231
|
+
else
|
686
1232
|
{
|
687
|
-
|
1233
|
+
rb_raise(rb_eArgError, "invalid state: prepared statement handle nil");
|
688
1234
|
}
|
1235
|
+
return Qnil;
|
689
1236
|
}
|
690
1237
|
|
691
|
-
|
1238
|
+
/*
|
1239
|
+
* call-seq:
|
1240
|
+
* count() -> Number
|
1241
|
+
*
|
1242
|
+
* Returns the update count for an update statement.
|
1243
|
+
*/
|
1244
|
+
static
|
1245
|
+
VALUE nuodb_statement_update_count(VALUE self)
|
692
1246
|
{
|
693
|
-
|
694
|
-
|
1247
|
+
trace("nuodb_statement_update_count");
|
1248
|
+
|
1249
|
+
nuodb_statement_handle * handle = cast_handle<nuodb_statement_handle>(self);
|
1250
|
+
if (handle != NULL && handle->pointer != NULL)
|
695
1251
|
{
|
696
|
-
|
1252
|
+
try
|
1253
|
+
{
|
1254
|
+
return INT2NUM(handle->pointer->getUpdateCount());
|
1255
|
+
}
|
1256
|
+
catch (SQLException & e)
|
1257
|
+
{
|
1258
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to get the update count for the statement: %s", e.getText());
|
1259
|
+
}
|
697
1260
|
}
|
698
|
-
|
1261
|
+
else
|
699
1262
|
{
|
700
|
-
|
1263
|
+
rb_raise(rb_eArgError, "invalid state: prepared statement handle nil");
|
701
1264
|
}
|
1265
|
+
return Qnil;
|
702
1266
|
}
|
703
1267
|
|
704
|
-
|
1268
|
+
/*
|
1269
|
+
* call-seq:
|
1270
|
+
* results() -> Results
|
1271
|
+
*
|
1272
|
+
* Retrieves a result set containing rows for the related query.
|
1273
|
+
*/
|
1274
|
+
static VALUE nuodb_statement_results(VALUE self)
|
705
1275
|
{
|
706
|
-
|
1276
|
+
trace("nuodb_statement_results");
|
1277
|
+
|
1278
|
+
nuodb_statement_handle * handle = cast_handle<nuodb_statement_handle>(self);
|
1279
|
+
if (handle != NULL && handle->pointer != NULL)
|
707
1280
|
{
|
708
|
-
|
1281
|
+
try
|
1282
|
+
{
|
1283
|
+
return nuodb_result_alloc(self, handle->pointer->getResultSet(), handle->pointer->getConnection());
|
1284
|
+
}
|
1285
|
+
catch (SQLException & e)
|
1286
|
+
{
|
1287
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to get the result set for the statement: %s", e.getText());
|
1288
|
+
}
|
709
1289
|
}
|
710
|
-
|
1290
|
+
else
|
711
1291
|
{
|
712
|
-
|
1292
|
+
rb_raise(rb_eArgError, "invalid state: statement handle nil");
|
713
1293
|
}
|
1294
|
+
return Qnil;
|
714
1295
|
}
|
715
1296
|
|
716
|
-
|
1297
|
+
/*
|
1298
|
+
* call-seq:
|
1299
|
+
* generated_keys() -> Results
|
1300
|
+
*
|
1301
|
+
* Retrieves a result set containing the generated keys related to the
|
1302
|
+
* previously executed insert.
|
1303
|
+
*/
|
1304
|
+
static VALUE nuodb_statement_generated_keys(VALUE self)
|
717
1305
|
{
|
718
|
-
|
1306
|
+
trace("nuodb_statement_generated_keys");
|
1307
|
+
|
1308
|
+
nuodb_statement_handle * handle = cast_handle<nuodb_statement_handle>(self);
|
1309
|
+
if (handle != NULL && handle->pointer != NULL)
|
719
1310
|
{
|
720
|
-
|
1311
|
+
try
|
1312
|
+
{
|
1313
|
+
// this hack should not have been necessary; it should never have
|
1314
|
+
// returned null, this is a product defect.
|
1315
|
+
ResultSet * results = handle->pointer->getGeneratedKeys();
|
1316
|
+
if (results != NULL)
|
1317
|
+
{
|
1318
|
+
return nuodb_result_alloc(self, results, handle->pointer->getConnection());
|
1319
|
+
}
|
1320
|
+
}
|
1321
|
+
catch (SQLException & e)
|
1322
|
+
{
|
1323
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to get the generated keys for the statement: %s", e.getText());
|
1324
|
+
}
|
721
1325
|
}
|
722
|
-
|
1326
|
+
else
|
723
1327
|
{
|
724
|
-
|
1328
|
+
rb_raise(rb_eArgError, "invalid state: statement handle nil");
|
725
1329
|
}
|
1330
|
+
return Qnil;
|
726
1331
|
}
|
727
1332
|
|
728
|
-
|
1333
|
+
static
|
1334
|
+
void nuodb_define_statement_api()
|
729
1335
|
{
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
1336
|
+
/*
|
1337
|
+
* Document-class: NuoDB::Statement
|
1338
|
+
*
|
1339
|
+
* A Statement object maintains a connection to a specific database. SQL
|
1340
|
+
* statements are executed and results are returned within the context of
|
1341
|
+
* a connection.
|
1342
|
+
*/
|
1343
|
+
nuodb_statement_klass = rb_define_class_under(m_nuodb, "Statement", rb_cObject);
|
1344
|
+
|
1345
|
+
// DBI
|
1346
|
+
|
1347
|
+
rb_define_method(nuodb_statement_klass, "execute", RUBY_METHOD_FUNC(nuodb_statement_execute), 1);
|
1348
|
+
//rb_define_method(nuodb_statement_klass, "finish", RUBY_METHOD_FUNC(nuodb_statement_finish), 0);
|
1349
|
+
|
1350
|
+
// NUODB EXTENSIONS
|
1351
|
+
|
1352
|
+
rb_define_method(nuodb_statement_klass, "count", RUBY_METHOD_FUNC(nuodb_statement_update_count), 0);
|
1353
|
+
rb_define_method(nuodb_statement_klass, "generated_keys", RUBY_METHOD_FUNC(nuodb_statement_generated_keys), 0);
|
1354
|
+
rb_define_method(nuodb_statement_klass, "results", RUBY_METHOD_FUNC(nuodb_statement_results), 0);
|
738
1355
|
}
|
739
1356
|
|
740
1357
|
//------------------------------------------------------------------------------
|
741
1358
|
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
1359
|
+
static
|
1360
|
+
VALUE nuodb_prepared_statement_free_protect(VALUE value)
|
1361
|
+
{
|
1362
|
+
trace("nuodb_prepared_statement_free_protect");
|
1363
|
+
nuodb_prepared_statement_handle * handle = reinterpret_cast<nuodb_prepared_statement_handle *>(value);
|
1364
|
+
track_ref_count("PS FREE PROTECT", handle);
|
1365
|
+
if (handle != NULL)
|
1366
|
+
{
|
1367
|
+
if (handle->pointer != NULL)
|
1368
|
+
{
|
1369
|
+
try
|
1370
|
+
{
|
1371
|
+
log(INFO, "closing prepared statement");
|
1372
|
+
handle->pointer->close();
|
1373
|
+
handle->pointer = NULL;
|
1374
|
+
}
|
1375
|
+
catch (SQLException & e)
|
1376
|
+
{
|
1377
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to successfully close statement: %s", e.getText());
|
1378
|
+
}
|
1379
|
+
}
|
1380
|
+
}
|
1381
|
+
return Qnil;
|
757
1382
|
}
|
758
1383
|
|
759
|
-
|
1384
|
+
static
|
1385
|
+
void nuodb_prepared_statement_free(void * ptr)
|
760
1386
|
{
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
return Qnil;
|
765
|
-
}
|
766
|
-
catch (SQLException & e)
|
1387
|
+
trace("nuodb_prepared_statement_free");
|
1388
|
+
|
1389
|
+
if (ptr != NULL)
|
767
1390
|
{
|
768
|
-
|
1391
|
+
int exception = 0;
|
1392
|
+
rb_protect(nuodb_prepared_statement_free_protect, reinterpret_cast<VALUE>(ptr), &exception);
|
1393
|
+
if (exception)
|
1394
|
+
{
|
1395
|
+
rb_jump_tag(exception);
|
1396
|
+
}
|
769
1397
|
}
|
770
1398
|
}
|
771
1399
|
|
772
|
-
|
1400
|
+
static
|
1401
|
+
void nuodb_prepared_statement_mark(void * ptr)
|
773
1402
|
{
|
774
|
-
|
775
|
-
struct timeval tv = rb_time_timeval(valueValue);
|
1403
|
+
trace("nuodb_prepared_statement_mark");
|
776
1404
|
|
777
|
-
|
778
|
-
|
779
|
-
{
|
780
|
-
asPtr(self)->setDate(index, &d);
|
781
|
-
return Qnil;
|
782
|
-
}
|
783
|
-
catch (SQLException & e)
|
784
|
-
{
|
785
|
-
rb_raise_nuodb_error(e.getSqlcode(), "Failed to set prepared statement setTime(%d, %lld) failed: %s",
|
786
|
-
index, d.getMilliseconds(), e.getText());
|
787
|
-
}
|
1405
|
+
nuodb_prepared_statement_handle * handle = static_cast<nuodb_prepared_statement_handle *>(ptr);
|
1406
|
+
rb_gc_mark(handle->parent);
|
788
1407
|
}
|
789
1408
|
|
790
|
-
|
1409
|
+
static
|
1410
|
+
void nuodb_prepared_statement_decr_reference_count(nuodb_handle * handle)
|
791
1411
|
{
|
792
|
-
|
793
|
-
|
794
|
-
try
|
795
|
-
{
|
796
|
-
asPtr(self)->setInt(index, value);
|
797
|
-
return Qnil;
|
798
|
-
}
|
799
|
-
catch (SQLException & e)
|
800
|
-
{
|
801
|
-
rb_raise_nuodb_error(e.getSqlcode(), "Failed to set prepared statement boolean value (%d, %s): %s",
|
802
|
-
index, (value ? "true" : "false"), e.getText());
|
803
|
-
}
|
1412
|
+
trace("nuodb_prepared_statement_decr_reference_count");
|
1413
|
+
decr_reference_count(handle);
|
804
1414
|
}
|
805
1415
|
|
806
|
-
|
1416
|
+
/*
|
1417
|
+
* call-seq:
|
1418
|
+
* finish()
|
1419
|
+
*
|
1420
|
+
* Releases the prepared statement and any associated resources.
|
1421
|
+
*/
|
1422
|
+
static
|
1423
|
+
VALUE nuodb_prepared_statement_finish(VALUE self)
|
1424
|
+
{
|
1425
|
+
trace("nuodb_prepared_statement_finish");
|
1426
|
+
nuodb_prepared_statement_handle * handle = cast_handle<nuodb_prepared_statement_handle>(self);//reinterpret_cast<nuodb_prepared_statement_handle *>(value);
|
1427
|
+
track_ref_count("FINISH PSTMT", handle);
|
1428
|
+
nuodb_prepared_statement_free(handle);
|
1429
|
+
// if (handle != NULL)
|
1430
|
+
// {
|
1431
|
+
// if (handle->pointer != NULL)
|
1432
|
+
// {
|
1433
|
+
// try
|
1434
|
+
// {
|
1435
|
+
// track_ref_count("CLOSE PSTMT", handle);
|
1436
|
+
// log(INFO, "closing prepared statement");
|
1437
|
+
// handle->pointer->close();
|
1438
|
+
// handle->pointer = NULL;
|
1439
|
+
// }
|
1440
|
+
// catch (SQLException & e)
|
1441
|
+
// {
|
1442
|
+
// rb_raise_nuodb_error(e.getSqlcode(), "Failed to successfully close statement: %s", e.getText());
|
1443
|
+
// }
|
1444
|
+
// }
|
1445
|
+
// }
|
1446
|
+
return Qnil;
|
1447
|
+
//
|
1448
|
+
// if (ENABLE_CLOSE_HOOK)
|
1449
|
+
// {
|
1450
|
+
// nuodb_prepared_statement_handle * handle = cast_handle<nuodb_prepared_statement_handle>(self);
|
1451
|
+
// if (handle != NULL && handle->pointer != NULL)
|
1452
|
+
// {
|
1453
|
+
// nuodb_prepared_statement_decr_reference_count(handle);
|
1454
|
+
// }
|
1455
|
+
// else
|
1456
|
+
// {
|
1457
|
+
// rb_raise(rb_eArgError, "invalid state: prepared statement handle nil");
|
1458
|
+
// }
|
1459
|
+
// }
|
1460
|
+
// return Qnil;
|
1461
|
+
}
|
1462
|
+
|
1463
|
+
static
|
1464
|
+
VALUE nuodb_prepared_statement_initialize(VALUE parent, VALUE sql)
|
807
1465
|
{
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
{
|
812
|
-
asPtr(self)->setInt(index, value);
|
813
|
-
return Qnil;
|
814
|
-
}
|
815
|
-
catch (SQLException & e)
|
1466
|
+
trace("nuodb_prepared_statement_initialize");
|
1467
|
+
|
1468
|
+
if (TYPE(sql) != T_STRING)
|
816
1469
|
{
|
817
|
-
|
1470
|
+
rb_raise(rb_eTypeError, "wrong sql argument type %s (String expected)", rb_class2name(CLASS_OF(sql)));
|
818
1471
|
}
|
819
|
-
}
|
820
1472
|
|
821
|
-
|
822
|
-
|
823
|
-
int32_t index = NUM2UINT(indexValue);
|
824
|
-
double value = NUM2DBL(valueValue);
|
825
|
-
try
|
1473
|
+
nuodb_connection_handle * parent_handle = cast_handle<nuodb_connection_handle>(parent);
|
1474
|
+
if (parent_handle != NULL && parent_handle->pointer != NULL)
|
826
1475
|
{
|
827
|
-
|
828
|
-
|
1476
|
+
NuoDB::PreparedStatement * statement = NULL;
|
1477
|
+
try
|
1478
|
+
{
|
1479
|
+
statement = parent_handle->pointer->prepareStatement(StringValuePtr(sql),
|
1480
|
+
NuoDB::RETURN_GENERATED_KEYS);
|
1481
|
+
}
|
1482
|
+
catch (SQLException & e)
|
1483
|
+
{
|
1484
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to create prepared statement (%s): %s", sql, e.getText());
|
1485
|
+
}
|
1486
|
+
|
1487
|
+
nuodb_prepared_statement_handle * handle = ALLOC(struct nuodb_prepared_statement_handle);
|
1488
|
+
handle->free_func = RUBY_DATA_FUNC(&nuodb_prepared_statement_free);
|
1489
|
+
handle->atomic = 0;
|
1490
|
+
handle->parent = parent;
|
1491
|
+
handle->parent_handle = parent_handle;
|
1492
|
+
handle->pointer = statement;
|
1493
|
+
incr_reference_count(handle);
|
1494
|
+
assert(handle->atomic == 1);
|
1495
|
+
VALUE self = Data_Wrap_Struct(nuodb_prepared_statement_klass, nuodb_prepared_statement_mark, nuodb_prepared_statement_decr_reference_count, handle);
|
1496
|
+
|
1497
|
+
if (!rb_block_given_p()) {
|
1498
|
+
trace("nuodb_prepared_statement_initialize: no block");
|
1499
|
+
|
1500
|
+
return self;
|
1501
|
+
}
|
1502
|
+
|
1503
|
+
trace("nuodb_prepared_statement_initialize: begin block");
|
1504
|
+
|
1505
|
+
int exception = 0;
|
1506
|
+
VALUE result = rb_protect(rb_yield, self, &exception);
|
1507
|
+
|
1508
|
+
trace("nuodb_prepared_statement_initialize: end block");
|
1509
|
+
|
1510
|
+
// n.b. don't do this as it may introduce crashes of the ruby process !!!
|
1511
|
+
//nuodb_prepared_statement_finish(self);
|
1512
|
+
|
1513
|
+
trace("nuodb_prepared_statement_initialize: auto finish");
|
1514
|
+
|
1515
|
+
if (exception)
|
1516
|
+
{
|
1517
|
+
rb_jump_tag(exception);
|
1518
|
+
}
|
1519
|
+
else
|
1520
|
+
{
|
1521
|
+
return result;
|
1522
|
+
}
|
829
1523
|
}
|
830
|
-
|
1524
|
+
else
|
831
1525
|
{
|
832
|
-
|
1526
|
+
rb_raise(rb_eArgError, "invalid state: prepared statement handle nil");
|
833
1527
|
}
|
1528
|
+
return Qnil;
|
834
1529
|
}
|
835
1530
|
|
836
|
-
|
1531
|
+
static
|
1532
|
+
void raise_unsupported_type_at_index(char const * type_name, int32_t index)
|
837
1533
|
{
|
838
|
-
|
839
|
-
char const* value = RSTRING_PTR(valueValue);
|
840
|
-
try
|
841
|
-
{
|
842
|
-
asPtr(self)->setString(index, value);
|
843
|
-
return Qnil;
|
844
|
-
}
|
845
|
-
catch (SQLException & e)
|
846
|
-
{
|
847
|
-
rb_raise_nuodb_error(e.getSqlcode(), "Failed to set prepared statement string value (%d, \"%s\"): %s", index, value, e.getText());
|
848
|
-
}
|
1534
|
+
rb_raise(rb_eTypeError, "unsupported type %s at %d", type_name, index);
|
849
1535
|
}
|
850
1536
|
|
851
|
-
|
1537
|
+
/*
|
1538
|
+
* call-seq:
|
1539
|
+
* bind_param(param, value)
|
1540
|
+
*
|
1541
|
+
* Attempts to bind the value to this statement as the parameter number specified.
|
1542
|
+
*
|
1543
|
+
* connection.prepare insert_dml do |statement|
|
1544
|
+
* statement.bind_param(1, "Joe Smith")
|
1545
|
+
* statement.execute
|
1546
|
+
* end #=> implicit statement.finish
|
1547
|
+
*/
|
1548
|
+
static
|
1549
|
+
VALUE nuodb_prepared_statement_bind_param(VALUE self, VALUE param, VALUE value)
|
852
1550
|
{
|
1551
|
+
trace("nuodb_prepared_statement_bind_param");
|
1552
|
+
|
1553
|
+
if (TYPE(param) != T_FIXNUM)
|
1554
|
+
{
|
1555
|
+
rb_raise(rb_eTypeError, "index must be a number");
|
1556
|
+
}
|
1557
|
+
int32_t index = NUM2UINT(param);
|
1558
|
+
|
1559
|
+
NuoDB::PreparedStatement * statement = cast_pointer_member<
|
1560
|
+
nuodb_prepared_statement_handle, NuoDB::PreparedStatement>(self);
|
1561
|
+
|
853
1562
|
try
|
854
1563
|
{
|
855
|
-
|
1564
|
+
switch (TYPE(value))
|
1565
|
+
{
|
1566
|
+
case T_FLOAT: // 0x04
|
1567
|
+
{
|
1568
|
+
log(DEBUG, "supported: T_FLOAT");
|
1569
|
+
double real_value = NUM2DBL(value);
|
1570
|
+
statement->setDouble(index, real_value);
|
1571
|
+
}
|
1572
|
+
break;
|
1573
|
+
case T_STRING: // 0x05
|
1574
|
+
{
|
1575
|
+
log(DEBUG, "supported: T_STRING");
|
1576
|
+
char const * real_value = RSTRING_PTR(value);
|
1577
|
+
statement->setString(index, real_value);
|
1578
|
+
}
|
1579
|
+
break;
|
1580
|
+
case T_NIL: // 0x11
|
1581
|
+
{
|
1582
|
+
log(DEBUG, "supported: T_NIL");
|
1583
|
+
statement->setNull(index, 0);
|
1584
|
+
}
|
1585
|
+
break;
|
1586
|
+
case T_TRUE: // 0x12
|
1587
|
+
{
|
1588
|
+
log(DEBUG, "supported: T_TRUE");
|
1589
|
+
statement->setBoolean(index, true);
|
1590
|
+
}
|
1591
|
+
break;
|
1592
|
+
case T_FALSE: // 0x13
|
1593
|
+
{
|
1594
|
+
log(DEBUG, "supported: T_FALSE");
|
1595
|
+
statement->setBoolean(index, false);
|
1596
|
+
}
|
1597
|
+
break;
|
1598
|
+
case T_FIXNUM: // 0x15
|
1599
|
+
{
|
1600
|
+
log(DEBUG, "supported: T_FIXNUM");
|
1601
|
+
int64_t real_value = NUM2LONG(value);
|
1602
|
+
statement->setLong(index, real_value);
|
1603
|
+
}
|
1604
|
+
break;
|
1605
|
+
case T_DATA: // 0x22
|
1606
|
+
{
|
1607
|
+
log(DEBUG, "supported: T_DATA");
|
1608
|
+
if (rb_obj_is_instance_of(value, rb_cTime))
|
1609
|
+
{
|
1610
|
+
log(DEBUG, "supported Time");
|
1611
|
+
VALUE sec = rb_funcall(value, rb_intern("tv_sec"), 0);
|
1612
|
+
//VALUE offset = rb_funcall(value, rb_intern("utc_offset"), 0);
|
1613
|
+
VALUE usec = rb_funcall(value, rb_intern("tv_usec"), 0);
|
1614
|
+
SqlTimestamp sqlTimestamp(NUM2INT(sec), NUM2INT(usec) * 1000); // + NUM2INT(offset)
|
1615
|
+
statement->setTimestamp(index, &sqlTimestamp);
|
1616
|
+
break;
|
1617
|
+
}
|
1618
|
+
VALUE cDate = rb_const_get(rb_cObject, rb_intern("Date"));
|
1619
|
+
if (rb_obj_is_instance_of(value, cDate))
|
1620
|
+
{
|
1621
|
+
log(DEBUG, "supported Date");
|
1622
|
+
VALUE time = rb_funcall(value, rb_intern("to_time"), 0);
|
1623
|
+
VALUE sec = rb_funcall(time, rb_intern("tv_sec"), 0);
|
1624
|
+
//VALUE offset = rb_funcall(time, rb_intern("utc_offset"), 0);
|
1625
|
+
VALUE usec = rb_funcall(time, rb_intern("tv_usec"), 0);
|
1626
|
+
SqlTimestamp sqlTimestamp(NUM2LONG(sec), NUM2INT(usec) * 1000);// + NUM2INT(offset)
|
1627
|
+
statement->setTimestamp(index, &sqlTimestamp);
|
1628
|
+
break;
|
1629
|
+
}
|
1630
|
+
break;
|
1631
|
+
}
|
1632
|
+
case T_OBJECT: // 0x01
|
1633
|
+
{
|
1634
|
+
log(WARN, "unsupported: T_OBJECT");
|
1635
|
+
raise_unsupported_type_at_index("T_OBJECT", index);
|
1636
|
+
}
|
1637
|
+
break;
|
1638
|
+
case T_BIGNUM: // 0x0a
|
1639
|
+
{
|
1640
|
+
log(DEBUG, "supported: T_BIGNUM");
|
1641
|
+
int64_t real_value = NUM2LONG(value);
|
1642
|
+
statement->setLong(index, real_value);
|
1643
|
+
}
|
1644
|
+
break;
|
1645
|
+
case T_ARRAY: // 0x07
|
1646
|
+
{
|
1647
|
+
log(WARN, "unsupported: T_ARRAY");
|
1648
|
+
raise_unsupported_type_at_index("T_ARRAY", index);
|
1649
|
+
}
|
1650
|
+
break;
|
1651
|
+
case T_HASH: // 0x08
|
1652
|
+
{
|
1653
|
+
log(WARN, "unsupported: T_HASH");
|
1654
|
+
raise_unsupported_type_at_index("T_HASH", index);
|
1655
|
+
}
|
1656
|
+
break;
|
1657
|
+
case T_STRUCT: // 0x09
|
1658
|
+
{
|
1659
|
+
log(WARN, "unsupported: T_STRUCT");
|
1660
|
+
raise_unsupported_type_at_index("T_STRUCT", index);
|
1661
|
+
}
|
1662
|
+
break;
|
1663
|
+
case T_FILE: // 0x0e
|
1664
|
+
{
|
1665
|
+
log(WARN, "unsupported: T_FILE");
|
1666
|
+
raise_unsupported_type_at_index("T_FILE", index);
|
1667
|
+
}
|
1668
|
+
break;
|
1669
|
+
case T_MATCH: // 0x23
|
1670
|
+
{
|
1671
|
+
log(WARN, "unsupported: T_MATCH");
|
1672
|
+
raise_unsupported_type_at_index("T_MATCH", index);
|
1673
|
+
}
|
1674
|
+
break;
|
1675
|
+
case T_SYMBOL: // 0x24
|
1676
|
+
{
|
1677
|
+
log(WARN, "unsupported: T_SYMBOL");
|
1678
|
+
raise_unsupported_type_at_index("T_SYMBOL", index);
|
1679
|
+
break;
|
1680
|
+
}
|
1681
|
+
break;
|
1682
|
+
default:
|
1683
|
+
rb_raise(rb_eTypeError, "unsupported type: %d", TYPE(value));
|
1684
|
+
break;
|
1685
|
+
}
|
856
1686
|
}
|
857
1687
|
catch (SQLException & e)
|
858
1688
|
{
|
859
|
-
rb_raise_nuodb_error(e.getSqlcode(), "Failed to
|
1689
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to set prepared statement parameter(%d, %lld) failed: %s",
|
1690
|
+
index, param, e.getText());
|
860
1691
|
}
|
1692
|
+
return Qnil;
|
861
1693
|
}
|
862
1694
|
|
863
|
-
|
1695
|
+
/*
|
1696
|
+
* call-seq:
|
1697
|
+
* bind_params(*binds)
|
1698
|
+
*
|
1699
|
+
* Attempts to bind the list of values successively using bind_param.
|
1700
|
+
*
|
1701
|
+
* connection.prepare insert_dml do |statement|
|
1702
|
+
* statement.bind_params([56, 6.7, "String", Date.new(2001, 12, 3), Time.new])
|
1703
|
+
* statement.execute
|
1704
|
+
* end #=> implicit statement.finish
|
1705
|
+
*/
|
1706
|
+
static
|
1707
|
+
VALUE nuodb_prepared_statement_bind_params(VALUE self, VALUE array)
|
864
1708
|
{
|
865
|
-
|
1709
|
+
trace("nuodb_prepared_statement_bind_params");
|
1710
|
+
|
1711
|
+
nuodb_prepared_statement_handle * handle = cast_handle<nuodb_prepared_statement_handle>(self);
|
1712
|
+
if (handle != NULL && handle->pointer != NULL)
|
866
1713
|
{
|
867
|
-
|
1714
|
+
for (int i = 0; i < RARRAY_LEN(array); ++i)
|
1715
|
+
{
|
1716
|
+
VALUE value = RARRAY_PTR(array)[i];
|
1717
|
+
nuodb_prepared_statement_bind_param(self, UINT2NUM(i+1), value);
|
1718
|
+
}
|
868
1719
|
}
|
869
|
-
|
1720
|
+
else
|
870
1721
|
{
|
871
|
-
|
1722
|
+
rb_raise(rb_eArgError, "invalid state: prepared statement handle nil");
|
872
1723
|
}
|
1724
|
+
return Qnil;
|
873
1725
|
}
|
874
1726
|
|
875
|
-
|
1727
|
+
/*
|
1728
|
+
* call-seq:
|
1729
|
+
* execute() -> Bool
|
1730
|
+
*
|
1731
|
+
* Executes a prepared statement. Returns true if the result is a Result object;
|
1732
|
+
* returns false if the result is an update count or there is no result. For the
|
1733
|
+
* latter case, count() should be called next, for the former case each() should
|
1734
|
+
* be called.
|
1735
|
+
*/
|
1736
|
+
static
|
1737
|
+
VALUE nuodb_prepared_statement_execute(VALUE self)
|
876
1738
|
{
|
877
|
-
|
1739
|
+
trace("nuodb_prepared_statement_execute");
|
1740
|
+
|
1741
|
+
nuodb_prepared_statement_handle * handle = cast_handle<nuodb_prepared_statement_handle>(self);
|
1742
|
+
if (handle != NULL && handle->pointer != NULL)
|
878
1743
|
{
|
879
|
-
|
1744
|
+
try
|
1745
|
+
{
|
1746
|
+
return AS_QBOOL(handle->pointer->execute());
|
1747
|
+
}
|
1748
|
+
catch (SQLException & e)
|
1749
|
+
{
|
1750
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to execute SQL prepared statement: %s", e.getText());
|
1751
|
+
}
|
880
1752
|
}
|
881
|
-
|
1753
|
+
else
|
882
1754
|
{
|
883
|
-
|
1755
|
+
rb_raise(rb_eArgError, "invalid state: prepared statement handle nil");
|
884
1756
|
}
|
1757
|
+
return Qnil;
|
885
1758
|
}
|
886
1759
|
|
887
|
-
|
1760
|
+
/*
|
1761
|
+
* call-seq:
|
1762
|
+
* count() -> Number
|
1763
|
+
*
|
1764
|
+
* Returns the update count for an update statement.
|
1765
|
+
*/
|
1766
|
+
static
|
1767
|
+
VALUE nuodb_prepared_statement_update_count(VALUE self)
|
888
1768
|
{
|
889
|
-
|
1769
|
+
trace("nuodb_prepared_statement_update_count");
|
1770
|
+
|
1771
|
+
nuodb_prepared_statement_handle * handle = cast_handle<nuodb_prepared_statement_handle>(self);
|
1772
|
+
if (handle != NULL && handle->pointer != NULL)
|
890
1773
|
{
|
891
|
-
|
1774
|
+
try
|
1775
|
+
{
|
1776
|
+
return INT2NUM(handle->pointer->getUpdateCount());
|
1777
|
+
}
|
1778
|
+
catch (SQLException & e)
|
1779
|
+
{
|
1780
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to get the update count for the prepared statement: %s", e.getText());
|
1781
|
+
}
|
892
1782
|
}
|
893
|
-
|
1783
|
+
else
|
894
1784
|
{
|
895
|
-
|
1785
|
+
rb_raise(rb_eArgError, "invalid state: prepared statement handle nil");
|
896
1786
|
}
|
1787
|
+
return Qnil;
|
897
1788
|
}
|
898
1789
|
|
899
|
-
|
1790
|
+
/*
|
1791
|
+
* call-seq:
|
1792
|
+
* results() -> Results
|
1793
|
+
*
|
1794
|
+
* Retrieves a result set containing rows for the related query.
|
1795
|
+
*/
|
1796
|
+
static VALUE nuodb_prepared_statement_results(VALUE self)
|
900
1797
|
{
|
901
|
-
|
1798
|
+
trace("nuodb_prepared_statement_results");
|
1799
|
+
|
1800
|
+
nuodb_prepared_statement_handle * handle = cast_handle<nuodb_prepared_statement_handle>(self);
|
1801
|
+
if (handle != NULL && handle->pointer != NULL)
|
902
1802
|
{
|
903
|
-
|
1803
|
+
try
|
1804
|
+
{
|
1805
|
+
return nuodb_result_alloc(self, handle->pointer->getResultSet(), handle->pointer->getConnection());
|
1806
|
+
}
|
1807
|
+
catch (SQLException & e)
|
1808
|
+
{
|
1809
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to get the result set for the prepared statement: %s", e.getText());
|
1810
|
+
}
|
904
1811
|
}
|
905
|
-
|
1812
|
+
else
|
906
1813
|
{
|
907
|
-
|
1814
|
+
rb_raise(rb_eArgError, "invalid state: prepared statement handle nil");
|
908
1815
|
}
|
1816
|
+
return Qnil;
|
909
1817
|
}
|
910
1818
|
|
911
|
-
|
1819
|
+
/*
|
1820
|
+
* call-seq:
|
1821
|
+
* generated_keys() -> Results
|
1822
|
+
*
|
1823
|
+
* Retrieves a result set containing the generated keys related to the
|
1824
|
+
* previously executed insert.
|
1825
|
+
*/
|
1826
|
+
static VALUE nuodb_prepared_statement_generated_keys(VALUE self)
|
912
1827
|
{
|
913
|
-
|
1828
|
+
trace("nuodb_prepared_statement_generated_keys");
|
1829
|
+
|
1830
|
+
nuodb_prepared_statement_handle * handle = cast_handle<nuodb_prepared_statement_handle>(self);
|
1831
|
+
if (handle != NULL && handle->pointer != NULL)
|
914
1832
|
{
|
915
|
-
|
1833
|
+
try
|
1834
|
+
{
|
1835
|
+
// this hack should not have been necessary; it should never have
|
1836
|
+
// returned null, this is a product defect.
|
1837
|
+
ResultSet * results = handle->pointer->getGeneratedKeys();
|
1838
|
+
if (results != NULL)
|
1839
|
+
{
|
1840
|
+
return nuodb_result_alloc(self, results, handle->pointer->getConnection());
|
1841
|
+
}
|
1842
|
+
}
|
1843
|
+
catch (SQLException & e)
|
1844
|
+
{
|
1845
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to get the generated keys for the prepared statement: %s", e.getText());
|
1846
|
+
}
|
916
1847
|
}
|
917
|
-
|
1848
|
+
else
|
918
1849
|
{
|
919
|
-
|
1850
|
+
rb_raise(rb_eArgError, "invalid state: prepared statement handle nil");
|
920
1851
|
}
|
1852
|
+
return Qnil;
|
921
1853
|
}
|
922
1854
|
|
923
|
-
|
924
|
-
|
925
|
-
void WrapConnection::init(VALUE module)
|
1855
|
+
static
|
1856
|
+
void nuodb_define_prepared_statement_api()
|
926
1857
|
{
|
927
|
-
|
1858
|
+
/*
|
1859
|
+
* Document-class: NuoDB::PreparedStatement
|
1860
|
+
*
|
1861
|
+
* A PreparedStatement object maintains a connection to a specific database.
|
1862
|
+
* SQL statements are executed and results are returned within the context of a
|
1863
|
+
* connection.
|
1864
|
+
*/
|
1865
|
+
nuodb_prepared_statement_klass = rb_define_class_under(m_nuodb, "PreparedStatement", rb_cObject);
|
928
1866
|
|
929
1867
|
// DBI
|
930
1868
|
|
931
|
-
|
932
|
-
|
933
|
-
rb_define_method(
|
934
|
-
rb_define_method(
|
935
|
-
rb_define_method(type, "ping", RUBY_METHOD_FUNC(ping), 0);
|
936
|
-
rb_define_method(type, "prepare", RUBY_METHOD_FUNC(createPreparedStatement), 1);
|
937
|
-
rb_define_method(type, "rollback", RUBY_METHOD_FUNC(rollback), 0);
|
938
|
-
// todo add .tables, definitely!
|
1869
|
+
rb_define_method(nuodb_prepared_statement_klass, "bind_param", RUBY_METHOD_FUNC(nuodb_prepared_statement_bind_param), 2);
|
1870
|
+
rb_define_method(nuodb_prepared_statement_klass, "bind_params", RUBY_METHOD_FUNC(nuodb_prepared_statement_bind_params), 1);
|
1871
|
+
rb_define_method(nuodb_prepared_statement_klass, "execute", RUBY_METHOD_FUNC(nuodb_prepared_statement_execute), 0);
|
1872
|
+
//rb_define_method(nuodb_prepared_statement_klass, "finish", RUBY_METHOD_FUNC(nuodb_prepared_statement_finish), 0);
|
939
1873
|
|
940
1874
|
// NUODB EXTENSIONS
|
941
1875
|
|
942
|
-
rb_define_method(
|
943
|
-
rb_define_method(
|
1876
|
+
rb_define_method(nuodb_prepared_statement_klass, "count", RUBY_METHOD_FUNC(nuodb_prepared_statement_update_count), 0);
|
1877
|
+
rb_define_method(nuodb_prepared_statement_klass, "generated_keys", RUBY_METHOD_FUNC(nuodb_prepared_statement_generated_keys), 0);
|
1878
|
+
rb_define_method(nuodb_prepared_statement_klass, "results", RUBY_METHOD_FUNC(nuodb_prepared_statement_results), 0);
|
1879
|
+
}
|
1880
|
+
|
1881
|
+
//------------------------------------------------------------------------------
|
944
1882
|
|
945
|
-
|
1883
|
+
/*
|
1884
|
+
* Class NuoDB::Connection
|
1885
|
+
*/
|
946
1886
|
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
1887
|
+
static
|
1888
|
+
VALUE nuodb_connection_free_protect(VALUE value)
|
1889
|
+
{
|
1890
|
+
trace("nuodb_connection_free_protect");
|
1891
|
+
|
1892
|
+
nuodb_connection_handle * handle = reinterpret_cast<nuodb_connection_handle *>(value);
|
1893
|
+
track_ref_count("FREE CONN CHECK", handle);
|
1894
|
+
if (handle != NULL)
|
1895
|
+
{
|
1896
|
+
track_ref_count("FREE CONN", handle);
|
1897
|
+
if (handle->pointer != NULL)
|
1898
|
+
{
|
1899
|
+
try
|
1900
|
+
{
|
1901
|
+
track_ref_count("CLOSE CONN", handle);
|
1902
|
+
log(INFO, "closing connection");
|
1903
|
+
handle->pointer->close();
|
1904
|
+
handle->pointer = NULL;
|
1905
|
+
}
|
1906
|
+
catch (SQLException & e)
|
1907
|
+
{
|
1908
|
+
log(DEBUG, "rb_raise");
|
1909
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to successfully close connection: %s", e.getText());
|
1910
|
+
}
|
1911
|
+
}
|
1912
|
+
}
|
1913
|
+
return Qnil;
|
956
1914
|
}
|
957
1915
|
|
958
|
-
|
1916
|
+
static
|
1917
|
+
void nuodb_connection_free(void * ptr)
|
959
1918
|
{
|
960
|
-
|
961
|
-
|
962
|
-
return rb_str_new2(asPtr(self)->getSchema());
|
963
|
-
}
|
964
|
-
catch (SQLException & e)
|
1919
|
+
trace("nuodb_connection_free");
|
1920
|
+
if (ptr != NULL)
|
965
1921
|
{
|
966
|
-
|
1922
|
+
int exception = 0;
|
1923
|
+
rb_protect(nuodb_connection_free_protect, reinterpret_cast<VALUE>(ptr), &exception);
|
1924
|
+
if (exception)
|
1925
|
+
{
|
1926
|
+
rb_jump_tag(exception);
|
1927
|
+
}
|
967
1928
|
}
|
968
1929
|
}
|
969
1930
|
|
970
|
-
|
1931
|
+
static
|
1932
|
+
void nuodb_connection_mark(void * ptr)
|
971
1933
|
{
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
1934
|
+
trace("nuodb_connection_mark");
|
1935
|
+
|
1936
|
+
nuodb_connection_handle * handle = static_cast<nuodb_connection_handle *>(ptr);
|
1937
|
+
track_ref_count("MARK CONN", handle);
|
1938
|
+
|
1939
|
+
rb_gc_mark(handle->database);
|
1940
|
+
rb_gc_mark(handle->username);
|
1941
|
+
rb_gc_mark(handle->password);
|
1942
|
+
rb_gc_mark(handle->schema);
|
1943
|
+
}
|
1944
|
+
|
1945
|
+
static
|
1946
|
+
void nuodb_connection_decr_reference_count(nuodb_handle * handle)
|
1947
|
+
{
|
1948
|
+
trace("nuodb_connection_decr_reference_count");
|
1949
|
+
decr_reference_count(handle);
|
1950
|
+
}
|
1951
|
+
|
1952
|
+
static
|
1953
|
+
VALUE nuodb_connection_alloc(VALUE klass)
|
1954
|
+
{
|
1955
|
+
trace("nuodb_connection_alloc");
|
1956
|
+
|
1957
|
+
nuodb_connection_handle * handle = ALLOC(struct nuodb_connection_handle);
|
1958
|
+
handle->database = Qnil;
|
1959
|
+
handle->username = Qnil;
|
1960
|
+
handle->password = Qnil;
|
1961
|
+
handle->schema = Qnil;
|
1962
|
+
handle->timezone = Qnil;
|
1963
|
+
|
1964
|
+
handle->free_func = RUBY_DATA_FUNC(&nuodb_connection_free);
|
1965
|
+
handle->atomic = 0;
|
1966
|
+
handle->parent = Qnil;
|
1967
|
+
handle->parent_handle = 0;
|
1968
|
+
handle->pointer = 0;
|
1969
|
+
incr_reference_count(handle);
|
1970
|
+
|
1971
|
+
print_address("[ALLOC] connection", handle);
|
1972
|
+
|
1973
|
+
return Data_Wrap_Struct(klass, nuodb_connection_mark, nuodb_connection_decr_reference_count, handle);
|
1974
|
+
}
|
1975
|
+
|
1976
|
+
static void internal_connection_connect_or_raise(nuodb_connection_handle * handle)
|
1977
|
+
{
|
1978
|
+
trace("internal_connection_connect_or_raise");
|
1979
|
+
|
1980
|
+
if (handle->schema != Qnil)
|
1981
|
+
{
|
1982
|
+
try
|
1983
|
+
{
|
1984
|
+
handle->pointer = Connection::create();
|
1985
|
+
Properties * props = handle->pointer->allocProperties();
|
1986
|
+
props->putValue("user", StringValuePtr(handle->username));
|
1987
|
+
props->putValue("password", StringValuePtr(handle->password));
|
1988
|
+
props->putValue("schema", StringValuePtr(handle->schema));
|
1989
|
+
if (!NIL_P(handle->timezone))
|
1990
|
+
{
|
1991
|
+
props->putValue("TimeZone", StringValuePtr(handle->timezone));
|
1992
|
+
}
|
1993
|
+
handle->pointer->openDatabase(StringValuePtr(handle->database), props);
|
1994
|
+
}
|
1995
|
+
catch (SQLException & e)
|
1996
|
+
{
|
1997
|
+
rb_raise_nuodb_error(e.getSqlcode(),
|
1998
|
+
"Failed to create database connection (\"%s\", \"%s\", ********, \"%s\"): %s",
|
1999
|
+
StringValuePtr(handle->database),
|
2000
|
+
StringValuePtr(handle->username),
|
2001
|
+
StringValuePtr(handle->schema),
|
2002
|
+
e.getText());
|
2003
|
+
}
|
2004
|
+
}
|
2005
|
+
else
|
2006
|
+
{
|
2007
|
+
try
|
2008
|
+
{
|
2009
|
+
handle->pointer = Connection::create();
|
2010
|
+
Properties * props = handle->pointer->allocProperties();
|
2011
|
+
props->putValue("user", StringValuePtr(handle->username));
|
2012
|
+
props->putValue("password", StringValuePtr(handle->password));
|
2013
|
+
if (!NIL_P(handle->timezone))
|
2014
|
+
{
|
2015
|
+
props->putValue("TimeZone", StringValuePtr(handle->timezone));
|
2016
|
+
}
|
2017
|
+
handle->pointer->openDatabase(StringValuePtr(handle->database), props);
|
2018
|
+
}
|
2019
|
+
catch (SQLException & e)
|
2020
|
+
{
|
2021
|
+
rb_raise_nuodb_error(e.getSqlcode(),
|
2022
|
+
"Failed to create database connection (\"%s\", \"%s\", ********): %s",
|
2023
|
+
StringValuePtr(handle->database),
|
2024
|
+
StringValuePtr(handle->username),
|
2025
|
+
e.getText());
|
2026
|
+
}
|
980
2027
|
}
|
981
2028
|
}
|
982
2029
|
|
983
|
-
|
2030
|
+
/*
|
2031
|
+
* call-seq:
|
2032
|
+
* commit()
|
2033
|
+
*
|
2034
|
+
* Commit the database transaction.
|
2035
|
+
*
|
2036
|
+
* NuoDB::Connection.new (hash) do |connection|
|
2037
|
+
* ...
|
2038
|
+
* connection.commit
|
2039
|
+
* end #=> automatically disconnected connection
|
2040
|
+
*/
|
2041
|
+
static VALUE nuodb_connection_commit(VALUE self)
|
984
2042
|
{
|
985
|
-
|
2043
|
+
trace("nuodb_connection_commit");
|
2044
|
+
|
2045
|
+
nuodb_connection_handle * handle = cast_handle<nuodb_connection_handle>(self);
|
2046
|
+
if (handle != NULL && handle->pointer != NULL)
|
986
2047
|
{
|
987
|
-
|
988
|
-
|
2048
|
+
try
|
2049
|
+
{
|
2050
|
+
handle->pointer->commit();
|
2051
|
+
}
|
2052
|
+
catch (SQLException & e)
|
2053
|
+
{
|
2054
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to commit transaction: %s", e.getText());
|
2055
|
+
}
|
989
2056
|
}
|
990
|
-
|
2057
|
+
else
|
991
2058
|
{
|
992
|
-
|
2059
|
+
rb_raise(rb_eArgError, "invalid state: connection handle nil");
|
993
2060
|
}
|
2061
|
+
return Qnil;
|
2062
|
+
}
|
2063
|
+
|
2064
|
+
/*
|
2065
|
+
* call-seq:
|
2066
|
+
* disconnect()
|
2067
|
+
*
|
2068
|
+
* Disconnects the connection.
|
2069
|
+
*/
|
2070
|
+
static VALUE nuodb_connection_disconnect(VALUE self)
|
2071
|
+
{
|
2072
|
+
trace("nuodb_connection_disconnect");
|
2073
|
+
nuodb_connection_handle * handle = cast_handle<nuodb_connection_handle>(self);//;
|
2074
|
+
track_ref_count("CONN DISCONNECT", handle);
|
2075
|
+
nuodb_connection_free(handle);
|
2076
|
+
// if (handle != NULL)
|
2077
|
+
// {
|
2078
|
+
// track_ref_count("FREE CONN", handle);
|
2079
|
+
// if (handle->pointer != NULL)
|
2080
|
+
// {
|
2081
|
+
// try
|
2082
|
+
// {
|
2083
|
+
// track_ref_count("CLOSE CONN", handle);
|
2084
|
+
// handle->pointer->close();
|
2085
|
+
// handle->pointer = NULL;
|
2086
|
+
// }
|
2087
|
+
// catch (SQLException & e)
|
2088
|
+
// {
|
2089
|
+
// log(DEBUG, "rb_raise");
|
2090
|
+
// rb_raise_nuodb_error(e.getSqlcode(), "Failed to successfully close connection: %s", e.getText());
|
2091
|
+
// }
|
2092
|
+
// }
|
2093
|
+
// }
|
2094
|
+
return Qnil;
|
2095
|
+
//
|
2096
|
+
//
|
2097
|
+
//
|
2098
|
+
// if (ENABLE_CLOSE_HOOK)
|
2099
|
+
// {
|
2100
|
+
// nuodb_connection_handle * handle = cast_handle<nuodb_connection_handle>(self);
|
2101
|
+
// if (handle != NULL && handle->pointer != NULL)
|
2102
|
+
// {
|
2103
|
+
// nuodb_connection_decr_reference_count(handle);
|
2104
|
+
// }
|
2105
|
+
// else
|
2106
|
+
// {
|
2107
|
+
// rb_raise(rb_eArgError, "invalid state: connection handle nil");
|
2108
|
+
// }
|
2109
|
+
// }
|
2110
|
+
// return Qnil;
|
994
2111
|
}
|
995
2112
|
|
996
|
-
|
2113
|
+
/*
|
2114
|
+
* call-seq:
|
2115
|
+
* connection.ping -> boolean
|
2116
|
+
* connection.connected? -> boolean
|
2117
|
+
*
|
2118
|
+
* Returns true if the connection is still alive, otherwise false.
|
2119
|
+
*
|
2120
|
+
* connection.connected? #=> true
|
2121
|
+
*/
|
2122
|
+
static VALUE nuodb_connection_ping(VALUE self)
|
997
2123
|
{
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
catch (SQLException & e)
|
2124
|
+
trace("nuodb_connection_ping");
|
2125
|
+
|
2126
|
+
nuodb_connection_handle * handle = cast_handle<nuodb_connection_handle>(self);
|
2127
|
+
if (handle != NULL && handle->pointer != NULL)
|
1003
2128
|
{
|
1004
|
-
|
2129
|
+
try
|
2130
|
+
{
|
2131
|
+
handle->pointer->ping();
|
2132
|
+
return Qtrue;
|
2133
|
+
}
|
2134
|
+
catch (SQLException & e)
|
2135
|
+
{
|
2136
|
+
}
|
1005
2137
|
}
|
2138
|
+
return Qfalse;
|
1006
2139
|
}
|
1007
2140
|
|
1008
|
-
|
2141
|
+
/*
|
2142
|
+
* call-seq:
|
2143
|
+
* prepare(sql) -> PreparedStatement
|
2144
|
+
*
|
2145
|
+
* Creates a prepared statement.
|
2146
|
+
*
|
2147
|
+
* NuoDB::Connection.new (hash) do |connection|
|
2148
|
+
* connection.prepare 'insert into foo (f1,f2) values (?, ?)' do |statement|
|
2149
|
+
* statement.bind_params [...]
|
2150
|
+
* statement.execute
|
2151
|
+
* end
|
2152
|
+
* end #=> automatically disconnected connection
|
2153
|
+
*/
|
2154
|
+
static VALUE nuodb_connection_prepare(VALUE self, VALUE sql)
|
1009
2155
|
{
|
1010
|
-
|
1011
|
-
|
2156
|
+
trace("nuodb_connection_prepare");
|
2157
|
+
|
2158
|
+
return nuodb_prepared_statement_initialize(self, sql);
|
2159
|
+
}
|
2160
|
+
|
2161
|
+
/*
|
2162
|
+
* call-seq:
|
2163
|
+
* statement -> Statement
|
2164
|
+
*
|
2165
|
+
* Creates a statement.
|
2166
|
+
*
|
2167
|
+
* <b>This is a NuoDB-specific extension.</b>
|
2168
|
+
*
|
2169
|
+
* NuoDB::Connection.new (hash) do |connection|
|
2170
|
+
* connection.statement do |statement|
|
2171
|
+
* statement.execute sql
|
2172
|
+
* end
|
2173
|
+
* end #=> automatically disconnected connection
|
2174
|
+
*/
|
2175
|
+
static VALUE nuodb_connection_statement(VALUE self)
|
2176
|
+
{
|
2177
|
+
trace("nuodb_connection_statement");
|
2178
|
+
|
2179
|
+
return nuodb_statement_initialize(self);
|
2180
|
+
}
|
2181
|
+
|
2182
|
+
/*
|
2183
|
+
* call-seq:
|
2184
|
+
* rollback()
|
2185
|
+
*
|
2186
|
+
* Rollback the database transaction.
|
2187
|
+
*
|
2188
|
+
* NuoDB::Connection.new (hash) do |connection|
|
2189
|
+
* ...
|
2190
|
+
* connection.rollback
|
2191
|
+
* end #=> automatically disconnected connection
|
2192
|
+
*/
|
2193
|
+
static VALUE nuodb_connection_rollback(VALUE self)
|
2194
|
+
{
|
2195
|
+
trace("nuodb_connection_rollback");
|
2196
|
+
|
2197
|
+
nuodb_connection_handle * handle = cast_handle<nuodb_connection_handle>(self);
|
2198
|
+
if (handle != NULL && handle->pointer != NULL)
|
1012
2199
|
{
|
1013
|
-
|
2200
|
+
try
|
2201
|
+
{
|
2202
|
+
handle->pointer->rollback();
|
2203
|
+
}
|
2204
|
+
catch (SQLException & e)
|
2205
|
+
{
|
2206
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to rollback transaction: %s", e.getText());
|
2207
|
+
}
|
1014
2208
|
}
|
1015
|
-
|
2209
|
+
else
|
1016
2210
|
{
|
1017
|
-
|
2211
|
+
rb_raise(rb_eArgError, "invalid state: connection handle nil");
|
1018
2212
|
}
|
2213
|
+
return Qnil;
|
1019
2214
|
}
|
1020
2215
|
|
1021
|
-
|
2216
|
+
/*
|
2217
|
+
* call-seq:
|
2218
|
+
* autocommit= boolean
|
2219
|
+
*
|
2220
|
+
* Sets the connections autocommit state.
|
2221
|
+
*
|
2222
|
+
* NuoDB::Connection.new (hash) do |connection|
|
2223
|
+
* connection.autocommit = false
|
2224
|
+
* ...
|
2225
|
+
* connection.commit
|
2226
|
+
* end #=> automatically disconnected connection
|
2227
|
+
*
|
2228
|
+
* <b>This is a NuoDB-specific extension.</b>
|
2229
|
+
*/
|
2230
|
+
static VALUE nuodb_connection_autocommit_set(VALUE self, VALUE value)
|
1022
2231
|
{
|
1023
|
-
|
1024
|
-
|
2232
|
+
trace("nuodb_connection_autocommit_set");
|
2233
|
+
|
2234
|
+
nuodb_connection_handle * handle = cast_handle<nuodb_connection_handle>(self);
|
2235
|
+
if (handle != NULL && handle->pointer != NULL)
|
1025
2236
|
{
|
1026
|
-
|
1027
|
-
|
2237
|
+
bool auto_commit = !(RB_TYPE_P(value, T_FALSE) || RB_TYPE_P(value, T_NIL));
|
2238
|
+
try
|
2239
|
+
{
|
2240
|
+
handle->pointer->setAutoCommit(auto_commit);
|
2241
|
+
}
|
2242
|
+
catch (SQLException & e)
|
2243
|
+
{
|
2244
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to set autocommit (%d) for connection: %s", auto_commit, e.getText());
|
2245
|
+
}
|
1028
2246
|
}
|
1029
|
-
|
2247
|
+
else
|
1030
2248
|
{
|
1031
|
-
|
2249
|
+
rb_raise(rb_eArgError, "invalid state: connection handle nil");
|
1032
2250
|
}
|
2251
|
+
return Qnil;
|
1033
2252
|
}
|
1034
2253
|
|
1035
|
-
|
2254
|
+
/*
|
2255
|
+
* call-seq:
|
2256
|
+
* autocommit? -> boolean
|
2257
|
+
*
|
2258
|
+
* Gets the connections autocommit state.
|
2259
|
+
*
|
2260
|
+
* unless connection.autocommit? do #=> false
|
2261
|
+
* connection.commit
|
2262
|
+
* end
|
2263
|
+
*
|
2264
|
+
* <b>This is a NuoDB-specific extension.</b>
|
2265
|
+
*/
|
2266
|
+
static VALUE nuodb_connection_autocommit_get(VALUE self)
|
1036
2267
|
{
|
1037
|
-
|
2268
|
+
trace("nuodb_connection_autocommit_get");
|
2269
|
+
|
2270
|
+
nuodb_connection_handle * handle = cast_handle<nuodb_connection_handle>(self);
|
2271
|
+
if (handle != NULL && handle->pointer != NULL)
|
1038
2272
|
{
|
1039
|
-
|
2273
|
+
try
|
2274
|
+
{
|
2275
|
+
bool current = handle->pointer->getAutoCommit();
|
2276
|
+
return AS_QBOOL(current);
|
2277
|
+
}
|
2278
|
+
catch (SQLException & e)
|
2279
|
+
{
|
2280
|
+
rb_raise_nuodb_error(e.getSqlcode(), "Failed to determine autocommit state for connection: %s", e.getText());
|
2281
|
+
}
|
1040
2282
|
}
|
1041
|
-
|
2283
|
+
else
|
1042
2284
|
{
|
1043
|
-
|
2285
|
+
rb_raise(rb_eArgError, "invalid state: connection handle nil");
|
1044
2286
|
}
|
2287
|
+
return Qnil;
|
1045
2288
|
}
|
1046
2289
|
|
1047
|
-
|
2290
|
+
/*
|
2291
|
+
* call-seq:
|
2292
|
+
*
|
2293
|
+
* Connection.new(key, value, ...) -> new_connection
|
2294
|
+
* Connection.new(key, value, ...) { |connection| block }
|
2295
|
+
*
|
2296
|
+
* Creates a new connection using the specified connection parameters. In the
|
2297
|
+
* first form a new connection is created but the caller is responsible for
|
2298
|
+
* calling disconnect. In the second form a new connection is created but the
|
2299
|
+
* caller is not responsible for calling disconnect; the connection is
|
2300
|
+
* automatically closed after the block exits.
|
2301
|
+
*
|
2302
|
+
* NuoDB::Connection.new (
|
2303
|
+
* :database => 'hockey',
|
2304
|
+
* :username => 'gretzky',
|
2305
|
+
* :password => 'goal!',
|
2306
|
+
* :schema => 'players') #=> connection
|
2307
|
+
* NuoDB::Connection.new (
|
2308
|
+
* :database => 'hockey',
|
2309
|
+
* :username => 'gretzky',
|
2310
|
+
* :password => 'goal!',
|
2311
|
+
* :schema => 'players') { |connection| ... } #=> automatically disconnected connection
|
2312
|
+
*/
|
2313
|
+
static VALUE nuodb_connection_initialize(VALUE self, VALUE hash)
|
1048
2314
|
{
|
1049
|
-
|
2315
|
+
trace("nuodb_connection_initialize");
|
2316
|
+
|
2317
|
+
if (TYPE(hash) != T_HASH)
|
1050
2318
|
{
|
1051
|
-
|
1052
|
-
return Qnil;
|
2319
|
+
rb_raise(rb_eTypeError, "wrong argument type %s (Hash expected)", rb_class2name(CLASS_OF(hash)));
|
1053
2320
|
}
|
1054
|
-
|
2321
|
+
|
2322
|
+
nuodb_connection_handle * handle = cast_handle<nuodb_connection_handle>(self);
|
2323
|
+
if (NIL_P(handle->database))
|
1055
2324
|
{
|
1056
|
-
|
2325
|
+
VALUE value = rb_hash_aref(hash, sym_database);
|
2326
|
+
if (value == Qnil)
|
2327
|
+
{
|
2328
|
+
rb_raise(rb_eArgError, "missing database argument for connection: please specify :database => 'your_db_name'");
|
2329
|
+
}
|
2330
|
+
if (TYPE(value) != T_STRING)
|
2331
|
+
{
|
2332
|
+
rb_raise(rb_eTypeError, "wrong database argument type %s (String expected)", rb_class2name(CLASS_OF(value)));
|
2333
|
+
}
|
2334
|
+
handle->database = value;
|
1057
2335
|
}
|
1058
|
-
|
1059
|
-
|
1060
|
-
VALUE WrapConnection::rollback(VALUE self)
|
1061
|
-
{
|
1062
|
-
try
|
2336
|
+
if (handle->username == Qnil)
|
1063
2337
|
{
|
1064
|
-
|
1065
|
-
|
2338
|
+
VALUE value = rb_hash_aref(hash, sym_username);
|
2339
|
+
if (value == Qnil)
|
2340
|
+
{
|
2341
|
+
rb_raise(rb_eArgError, "missing username argument for connection: please specify :username => 'your_db_username'");
|
2342
|
+
}
|
2343
|
+
if (TYPE(value) != T_STRING)
|
2344
|
+
{
|
2345
|
+
rb_raise(rb_eTypeError, "wrong username argument type %s (String expected)", rb_class2name(CLASS_OF(value)));
|
2346
|
+
}
|
2347
|
+
handle->username = value;
|
1066
2348
|
}
|
1067
|
-
|
2349
|
+
if (handle->password == Qnil)
|
1068
2350
|
{
|
1069
|
-
|
2351
|
+
VALUE value = rb_hash_aref(hash, sym_password);
|
2352
|
+
if (value == Qnil)
|
2353
|
+
{
|
2354
|
+
rb_raise(rb_eArgError, "missing password argument for connection: please specify :password => 'your_db_password'");
|
2355
|
+
}
|
2356
|
+
if (TYPE(value) != T_STRING)
|
2357
|
+
{
|
2358
|
+
rb_raise(rb_eTypeError, "wrong password argument type %s (String expected)", rb_class2name(CLASS_OF(value)));
|
2359
|
+
}
|
2360
|
+
handle->password = value;
|
1070
2361
|
}
|
1071
|
-
|
1072
|
-
|
1073
|
-
VALUE WrapConnection::getMetaData(VALUE self)
|
1074
|
-
{
|
1075
|
-
try
|
2362
|
+
if (handle->schema == Qnil)
|
1076
2363
|
{
|
1077
|
-
|
2364
|
+
VALUE value = rb_hash_aref(hash, sym_schema);
|
2365
|
+
if (TYPE(value) != T_STRING)
|
2366
|
+
{
|
2367
|
+
rb_raise(rb_eTypeError, "wrong schema argument type %s (String expected)", rb_class2name(CLASS_OF(value)));
|
2368
|
+
}
|
2369
|
+
handle->schema = value;
|
1078
2370
|
}
|
1079
|
-
|
2371
|
+
if (handle->timezone == Qnil)
|
1080
2372
|
{
|
1081
|
-
|
2373
|
+
VALUE value = rb_hash_aref(hash, sym_timezone);
|
2374
|
+
if (value != Qnil)
|
2375
|
+
{
|
2376
|
+
if (TYPE(value) != T_STRING)
|
2377
|
+
{
|
2378
|
+
rb_raise(rb_eTypeError, "wrong timezone argument type %s (String expected)", rb_class2name(CLASS_OF(value)));
|
2379
|
+
}
|
2380
|
+
handle->timezone = value;
|
2381
|
+
}
|
1082
2382
|
}
|
1083
|
-
}
|
1084
2383
|
|
1085
|
-
|
1086
|
-
VALUE databaseValue,
|
1087
|
-
VALUE schemaValue,
|
1088
|
-
VALUE usernameValue,
|
1089
|
-
VALUE passwordValue)
|
1090
|
-
{
|
1091
|
-
char const* database = StringValuePtr(databaseValue);
|
1092
|
-
char const* schema = StringValuePtr(schemaValue);
|
1093
|
-
char const* username = StringValuePtr(usernameValue);
|
1094
|
-
char const* password = StringValuePtr(passwordValue);
|
2384
|
+
internal_connection_connect_or_raise(handle);
|
1095
2385
|
|
1096
|
-
|
2386
|
+
if (!rb_block_given_p()) {
|
2387
|
+
|
2388
|
+
trace("nuodb_connection_initialize: no block");
|
2389
|
+
|
2390
|
+
return self;
|
2391
|
+
}
|
2392
|
+
|
2393
|
+
trace("nuodb_connection_initialize: begin block");
|
2394
|
+
|
2395
|
+
int state = 0;
|
2396
|
+
VALUE result = rb_protect(rb_yield, self, &state);
|
2397
|
+
|
2398
|
+
trace("nuodb_connection_initialize: end block");
|
2399
|
+
|
2400
|
+
// n.b. don't do this as it may introduce crashes of the ruby process !!!
|
2401
|
+
//nuodb_connection_disconnect(self);
|
2402
|
+
|
2403
|
+
trace("nuodb_connection_initialize: auto disconnect");
|
2404
|
+
|
2405
|
+
if (state)
|
1097
2406
|
{
|
1098
|
-
|
1099
|
-
username,
|
1100
|
-
password,
|
1101
|
-
1,
|
1102
|
-
"schema",
|
1103
|
-
schema));
|
2407
|
+
rb_jump_tag(state);
|
1104
2408
|
}
|
1105
|
-
|
2409
|
+
else
|
1106
2410
|
{
|
1107
|
-
|
1108
|
-
"Failed to create database connection (\"%s\", \"%s\", ********, \"%s\"): %s",
|
1109
|
-
database,
|
1110
|
-
username,
|
1111
|
-
schema,
|
1112
|
-
e.getText());
|
2411
|
+
return result;
|
1113
2412
|
}
|
2413
|
+
|
2414
|
+
return Qnil;
|
2415
|
+
}
|
2416
|
+
|
2417
|
+
///*
|
2418
|
+
// * call-seq:
|
2419
|
+
// * tables(schema = nil) -> Results
|
2420
|
+
// *
|
2421
|
+
// * Retrieves a result set containing the tables associated to the specified schema,
|
2422
|
+
// * or if the specified schema is nil, return a result set of tables for the schema
|
2423
|
+
// * associated with the connection.
|
2424
|
+
// */
|
2425
|
+
//static VALUE nuodb_connection_tables(VALUE self, VALUE schema)
|
2426
|
+
//{
|
2427
|
+
// trace("nuodb_connection_tables");
|
2428
|
+
//
|
2429
|
+
// nuodb_connection_handle * handle = cast_handle<nuodb_connection_handle>(self);
|
2430
|
+
// if (handle != NULL && handle->pointer != NULL)
|
2431
|
+
// {
|
2432
|
+
// if (schema == Qnil)
|
2433
|
+
// {
|
2434
|
+
// schema = handle->schema;
|
2435
|
+
// }
|
2436
|
+
//// char const * sql = "SELECT tablename FROM system.tables WHERE schema = ?"
|
2437
|
+
//// try
|
2438
|
+
//// {
|
2439
|
+
//// return nuodb_result_alloc(self, handle->pointer->getGeneratedKeys());
|
2440
|
+
//// }
|
2441
|
+
//// catch (SQLException & e)
|
2442
|
+
//// {
|
2443
|
+
//// rb_raise_nuodb_error(e.getSqlcode(), "Failed to get the tables keys for the schema: %s", e.getText());
|
2444
|
+
//// }
|
2445
|
+
// }
|
2446
|
+
// else
|
2447
|
+
// {
|
2448
|
+
// rb_raise(rb_eArgError, "invalid state: connection handle nil");
|
2449
|
+
// }
|
2450
|
+
// return Qnil;
|
2451
|
+
//}
|
2452
|
+
|
2453
|
+
void nuodb_define_connection_api()
|
2454
|
+
{
|
2455
|
+
/*
|
2456
|
+
* Document-class: NuoDB::Connection
|
2457
|
+
*
|
2458
|
+
* A Connection object maintains a connection to a specific database.
|
2459
|
+
* SQL statements are executed and results are returned within the context of a
|
2460
|
+
* connection.
|
2461
|
+
*/
|
2462
|
+
nuodb_connection_klass = rb_define_class_under(m_nuodb, "Connection", rb_cObject);
|
2463
|
+
|
2464
|
+
rb_define_alloc_func(nuodb_connection_klass, nuodb_connection_alloc);
|
2465
|
+
rb_define_method(nuodb_connection_klass, "initialize", RUBY_METHOD_FUNC(nuodb_connection_initialize), 1);
|
2466
|
+
|
2467
|
+
sym_username = ID2SYM(rb_intern("username"));
|
2468
|
+
sym_password = ID2SYM(rb_intern("password"));
|
2469
|
+
sym_database = ID2SYM(rb_intern("database"));
|
2470
|
+
sym_schema = ID2SYM(rb_intern("schema"));
|
2471
|
+
sym_timezone = ID2SYM(rb_intern("timezone"));
|
2472
|
+
|
2473
|
+
// DBI
|
2474
|
+
|
2475
|
+
rb_define_method(nuodb_connection_klass, "commit", RUBY_METHOD_FUNC(nuodb_connection_commit), 0);
|
2476
|
+
//rb_define_method(nuodb_connection_klass, "disconnect", RUBY_METHOD_FUNC(nuodb_connection_disconnect), 0);
|
2477
|
+
rb_define_method(nuodb_connection_klass, "ping", RUBY_METHOD_FUNC(nuodb_connection_ping), 0);
|
2478
|
+
rb_define_method(nuodb_connection_klass, "prepare", RUBY_METHOD_FUNC(nuodb_connection_prepare), 1);
|
2479
|
+
rb_define_method(nuodb_connection_klass, "rollback", RUBY_METHOD_FUNC(nuodb_connection_rollback), 0);
|
2480
|
+
// todo add .tables, definitely!
|
2481
|
+
// todo add .columns, or not? If we did: .columns(table_name)
|
2482
|
+
// todo and tie this into the SchemaCache on the Ruby side.
|
2483
|
+
|
2484
|
+
// NUODB EXTENSIONS
|
2485
|
+
|
2486
|
+
//rb_define_method(nuodb_connection_klass, "tables", RUBY_METHOD_FUNC(nuodb_connection_tables), 1);
|
2487
|
+
rb_define_method(nuodb_connection_klass, "autocommit=", RUBY_METHOD_FUNC(nuodb_connection_autocommit_set), 1);
|
2488
|
+
rb_define_method(nuodb_connection_klass, "autocommit?", RUBY_METHOD_FUNC(nuodb_connection_autocommit_get), 0);
|
2489
|
+
rb_define_method(nuodb_connection_klass, "statement", RUBY_METHOD_FUNC(nuodb_connection_statement), 0);
|
2490
|
+
rb_define_method(nuodb_connection_klass, "connected?", RUBY_METHOD_FUNC(nuodb_connection_ping), 0);
|
1114
2491
|
}
|
1115
2492
|
|
1116
2493
|
//------------------------------------------------------------------------------
|
1117
2494
|
|
2495
|
+
/*
|
2496
|
+
* The NuoDB package provides a Ruby interface to the NuoDB database.
|
2497
|
+
*/
|
1118
2498
|
extern "C" void Init_nuodb(void)
|
1119
2499
|
{
|
2500
|
+
/*
|
2501
|
+
* The NuoDB module contains classes and function definitions enabling use
|
2502
|
+
* of the NuoDB database.
|
2503
|
+
*/
|
1120
2504
|
m_nuodb = rb_define_module("NuoDB");
|
1121
|
-
|
2505
|
+
|
2506
|
+
c_nuodb_error = rb_const_get(m_nuodb, rb_intern("DatabaseError"));
|
2507
|
+
|
1122
2508
|
c_error_code_assignment = rb_intern("error_code=");
|
1123
2509
|
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
2510
|
+
nuodb_define_connection_api();
|
2511
|
+
|
2512
|
+
nuodb_define_statement_api();
|
2513
|
+
|
2514
|
+
nuodb_define_prepared_statement_api();
|
2515
|
+
|
2516
|
+
nuodb_define_result_api();
|
1130
2517
|
}
|