nuodb 1.0.0.rc.1 → 1.0.0.rc.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -3,6 +3,9 @@
3
3
  *.o
4
4
  *.so
5
5
  ext/nuodb/Makefile
6
- pkg
6
+ coverage/
7
+ doc/
8
+ rdoc/
9
+ pkg/
7
10
  tmp
8
11
  .idea
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ -fs
data/AUTHORS CHANGED
@@ -1,4 +1,4 @@
1
- # Authors ordered by first contribution.
1
+ # The following people have contributed to ruby-nuodb:
2
2
 
3
+ Robert Buck <rbuck@nuodb.com>
3
4
  Dave Meppelink <davemepp@gmail.com>
4
-
data/Gemfile CHANGED
@@ -2,8 +2,4 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- group(:development, :test) do
6
- gem 'rake'
7
- gem 'rake-compiler'
8
- gem 'rdoc'
9
- end
5
+ gem 'rake'
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
- * 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.
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 NuoDB API module for Ruby. It integrates the NuoDB C++
6
- library into Ruby.
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-rc.1.gem
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-rc.1.gem
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
- ./nuodb --chorus test --password bar --dba-user dba --dba-password baz &
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
- ./nuosql test@localhost --user dba --password baz
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 test
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.rc.1 -m "SemVer Version: v1.0.0-rc.1"
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.rc.1
50
- git push origin :refs/tags/v1.0.0.rc.1
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.1.gem
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.1.gem
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
- require 'rdoc/task'
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,.c}") do
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
- task :test => "lib/#{name}/#{name}.#{so_ext}"
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
- Rake::TestTask.new do |t|
122
- t.libs << 'test'
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 :default => :test
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} --no-rdoc --no-ri}
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]
@@ -0,0 +1,11 @@
1
+ #include "atomic.h"
2
+
3
+ #if !defined(_WIN32) && !defined(HAVE_GCC_ATOMIC_BUILTINS)
4
+ rb_atomic_t
5
+ ruby_atomic_exchange(rb_atomic_t *ptr, rb_atomic_t val)
6
+ {
7
+ rb_atomic_t old = *ptr;
8
+ *ptr = val;
9
+ return old;
10
+ }
11
+ #endif
@@ -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
- nuodb_include, nuodb_lib64 = nuodb_srcs? ENV['NUODB_ROOT']
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
- extern "C" struct timeval rb_time_timeval(VALUE time);
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
- #define DEFINE_METHOD(func, count) \
86
- rb_define_method(type, #func, RUBY_METHOD_FUNC(func), count)
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
- va_list args;
97
- char text[BUFSIZ];
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
- va_start(args, fmt);
100
- vsnprintf(text, BUFSIZ, fmt, args);
101
- va_end(args);
60
+ extern "C" struct timeval rb_time_timeval(VALUE time);
102
61
 
103
- VALUE error = rb_exc_new2(c_nuodb_error, text);
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
- using namespace NuoDB;
89
+ // ----------------------------------------------------------------------------
90
+ // M O D U L E S
134
91
 
135
- class WrapDatabaseMetaData
136
- {
137
- WRAPPER_COMMON(WrapDatabaseMetaData, DatabaseMetaData)
92
+ static VALUE m_nuodb;
138
93
 
139
- static void init(VALUE module);
140
- static VALUE getDatabaseVersion(VALUE self);
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
- WRAPPER_DEFINITION(WrapDatabaseMetaData);
97
+ static VALUE c_nuodb_error;
147
98
 
148
- //------------------------------------------------------------------------------
99
+ // ----------------------------------------------------------------------------
100
+ // C L A S S E S
149
101
 
150
- class WrapResultSetMetaData
151
- {
152
- WRAPPER_COMMON(WrapResultSetMetaData, ResultSetMetaData);
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
- static void init(VALUE module);
155
- static VALUE getColumnCount(VALUE self);
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
- WRAPPER_DEFINITION(WrapResultSetMetaData);
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
- class WrapResultSet
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
- WRAPPER_DEFINITION(WrapResultSet)
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
- class WrapStatement
122
+ enum LogLevel
191
123
  {
192
- WRAPPER_COMMON(WrapStatement, Statement);
193
-
194
- static void init(VALUE module);
195
- static VALUE close(VALUE self);
196
- static VALUE execute(VALUE self, VALUE sqlValue);
197
- static VALUE executeQuery(VALUE self, VALUE sqlValue);
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
- WRAPPER_DEFINITION(WrapPreparedStatement);
228
-
229
- //------------------------------------------------------------------------------
230
-
231
- class WrapConnection
232
- {
233
- WRAPPER_COMMON(WrapConnection, Connection);
234
-
235
- static void init(VALUE module);
236
- static VALUE close(VALUE self);
237
- static VALUE ping(VALUE self);
238
- static VALUE createStatement(VALUE self);
239
- static VALUE createPreparedStatement(VALUE self, VALUE sqlValue);
240
- static VALUE setAutoCommit(VALUE self, VALUE autoCommitValue);
241
- static VALUE hasAutoCommit(VALUE self);
242
- static VALUE commit(VALUE self);
243
- static VALUE rollback(VALUE self);
244
- static VALUE getMetaData(VALUE self);
245
- static VALUE getSchema(VALUE self);
246
- static VALUE createSqlConnection(VALUE self,
247
- VALUE databaseValue,
248
- VALUE schemaValue,
249
- VALUE usernameValue,
250
- VALUE passwordValue);
251
- };
252
-
253
- WRAPPER_DEFINITION(WrapConnection);
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
- VALUE WrapDatabaseMetaData::getDatabaseVersion(VALUE self)
161
+ static void log(LogLevel level, char const * message)
267
162
  {
268
- try
163
+ if (level >= logLevel)
269
164
  {
270
- return rb_str_new2(asPtr(self)->getDatabaseProductVersion());
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
- VALUE WrapDatabaseMetaData::getIndexInfo(VALUE self, VALUE schemaValue, VALUE tableValue, VALUE uniqueValue, VALUE approxValue)
169
+ static void trace(char const * message)
279
170
  {
280
- const char* schema = StringValuePtr(schemaValue);
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
- VALUE WrapDatabaseMetaData::getColumns(VALUE self, VALUE schemaValue, VALUE tableValue)
174
+ static void print_address(char const * context, void * address)
295
175
  {
296
- const char* schema = StringValuePtr(schemaValue);
297
- const char* table = StringValuePtr(tableValue);
298
- try
176
+ if (DEBUG >= logLevel)
299
177
  {
300
- return WrapResultSet::wrap(asPtr(self)->getColumns(NULL, schema, table, NULL));
301
- }
302
- catch (SQLException & e)
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
- VALUE WrapDatabaseMetaData::getTables(VALUE self, VALUE schemaValue, VALUE tableValue)
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
- const char* schema = StringValuePtr(schemaValue);
311
- const char* table = StringValuePtr(tableValue);
273
+ void * data_handle;
274
+ unsigned int flags;
275
+ rb_atomic_t refers;
312
276
 
313
- try
314
- {
315
- return WrapResultSet::wrap(asPtr(self)->getTables(NULL, schema, table, 0, NULL));
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
- void WrapResultSet::init(VALUE module)
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
- INIT_TYPE("ResultSet");
328
- DEFINE_METHOD(next, 0);
329
- DEFINE_METHOD(getMetaData, 0);
330
- DEFINE_METHOD(getBoolean, 1);
331
- DEFINE_METHOD(getInteger, 1);
332
- DEFINE_METHOD(getDouble, 1);
333
- DEFINE_METHOD(getString, 1);
334
- DEFINE_METHOD(getDate, 1);
335
- DEFINE_METHOD(getTime, 1);
336
- DEFINE_METHOD(getTimestamp, 1);
337
- DEFINE_METHOD(getChar, 1);
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
- VALUE WrapResultSet::next(VALUE self)
307
+ // ----------------------------------------------------------------------------
308
+ // H A N D L E S
309
+
310
+ struct nuodb_handle
341
311
  {
342
- try
343
- {
344
- return AS_QBOOL(asPtr(self)->next());
345
- }
346
- catch (SQLException & e)
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
- VALUE WrapResultSet::getMetaData(VALUE self)
318
+ struct nuodb_connection_handle : nuodb_handle
353
319
  {
354
- try
355
- {
356
- return WrapResultSetMetaData::wrap(asPtr(self)->getMetaData());
357
- }
358
- catch (SQLException & e)
359
- {
360
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to retrieve number, types, and properties of the result set: %s", e.getText());
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
- VALUE WrapResultSet::getBoolean(VALUE self, VALUE columnValue)
329
+ struct nuodb_prepared_statement_handle : nuodb_handle
365
330
  {
366
- int column = NUM2UINT(columnValue);
367
- try
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
- VALUE WrapResultSet::getInteger(VALUE self, VALUE columnValue)
334
+ struct nuodb_statement_handle : nuodb_handle
379
335
  {
380
- int column = NUM2UINT(columnValue);
381
- try
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
- VALUE WrapResultSet::getDouble(VALUE self, VALUE columnValue)
339
+ struct nuodb_result_handle : nuodb_handle
392
340
  {
393
- int column = NUM2UINT(columnValue);
394
- try
395
- {
396
- return rb_float_new(asPtr(self)->getDouble(column));
397
- }
398
- catch (SQLException & e)
399
- {
400
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to get double value for the designated column (%d): %s", column, e.getText());
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
- VALUE WrapResultSet::getString(VALUE self, VALUE columnValue)
352
+ template<typename handle_type, typename return_type>
353
+ return_type * cast_pointer_member(VALUE value)
405
354
  {
406
- int column = NUM2UINT(columnValue);
407
- try
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
- VALUE WrapResultSet::getDate(VALUE self, VALUE columnValue)
358
+
359
+ static void track_ref_count(char const * context, nuodb_handle * handle)
417
360
  {
418
- int column = NUM2UINT(columnValue);
419
- try
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
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to get date value for the designated column (%d): %s", column, e.getText());
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
- VALUE WrapResultSet::getTime(VALUE self, VALUE columnValue)
382
+ void incr_reference_count(nuodb_handle * handle)
437
383
  {
438
- int column = NUM2UINT(columnValue);
439
- try
440
- {
441
- return rb_str_new2(asPtr(self)->getString(column));
442
- //Time* time = asPtr(self)->getTime(column);
443
- //SqlTime timestamp(time->getMilliseconds());
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
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to get time value for the designated column (%d): %s", column, e.getText());
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
- VALUE WrapResultSet::getTimestamp(VALUE self, VALUE columnValue)
398
+ void decr_reference_count(nuodb_handle * handle)
460
399
  {
461
- int column = NUM2UINT(columnValue);
462
- try
463
- {
464
- return rb_str_new2(asPtr(self)->getString(column));
465
- //Timestamp* ts = asPtr(self)->getTimestamp(column);
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
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to get timestamp value for the designated column (%d): %s", column, e.getText());
406
+ return;
476
407
  }
477
- }
478
408
 
479
- VALUE WrapResultSet::getChar(VALUE self, VALUE columnValue)
480
- {
481
- int column = NUM2UINT(columnValue);
482
- try
409
+ if (ATOMIC_DEC(handle->atomic) == 0)
483
410
  {
484
- return rb_str_new2(asPtr(self)->getString(column));
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
- catch (SQLException & e)
426
+ else
487
427
  {
488
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to get char value for the designated column (%d): %s", column, e.getText());
428
+ track_ref_count("O DECR", handle);
489
429
  }
490
430
  }
491
431
 
492
432
  //------------------------------------------------------------------------------
433
+ // exception mapper
493
434
 
494
- void WrapResultSetMetaData::init(VALUE module)
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
- VALUE WrapResultSetMetaData::getColumnCount(VALUE self)
437
+ static void rb_raise_nuodb_error(int code, const char * fmt, ...)
507
438
  {
508
- try
509
- {
510
- return UINT2NUM(asPtr(self)->getColumnCount());
511
- }
512
- catch (SQLException & e)
513
- {
514
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to get column count from the result set metadata: %s", e.getText());
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
- VALUE WrapResultSetMetaData::getColumnName(VALUE self, VALUE columnValue)
519
- {
520
- int column = NUM2UINT(columnValue);
521
- try
522
- {
523
- return rb_str_new2(asPtr(self)->getColumnName(column));
524
- }
525
- catch (SQLException & e)
526
- {
527
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to get column name from the result set metadata: %s", e.getText());
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
- VALUE WrapResultSetMetaData::getScale(VALUE self, VALUE columnValue)
482
+ static
483
+ void nuodb_result_free(void * ptr)
532
484
  {
533
- int column = NUM2UINT(columnValue);
534
- try
485
+ trace("nuodb_result_free");
486
+ if (ptr != NULL)
535
487
  {
536
- return UINT2NUM(asPtr(self)->getScale(column));
537
- }
538
- catch (SQLException & e)
539
- {
540
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to get scale from the result set metadata: %s", e.getText());
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
- VALUE WrapResultSetMetaData::getPrecision(VALUE self, VALUE columnValue)
497
+ static
498
+ void nuodb_result_mark(void * ptr)
545
499
  {
546
- int column = NUM2UINT(columnValue);
547
- try
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
- VALUE WrapResultSetMetaData::isNullable(VALUE self, VALUE columnValue)
505
+ static
506
+ void nuodb_result_decr_reference_count(nuodb_handle * handle)
558
507
  {
559
- int column = NUM2UINT(columnValue);
560
- try
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
- VALUE WrapResultSetMetaData::getColumnTypeName(VALUE self, VALUE columnValue)
571
- {
572
- int column = NUM2UINT(columnValue);
573
- try
574
- {
575
- return rb_str_new2(asPtr(self)->getColumnTypeName(column));
576
- }
577
- catch (SQLException & e)
578
- {
579
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to get column type name from the result set metadata for the designated column (%d): %s", column, e.getText());
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
- VALUE WrapResultSetMetaData::getType(VALUE self, VALUE columnValue)
567
+ static
568
+ VALUE nuodb_map_sql_type(int type)
584
569
  {
585
- int column = NUM2UINT(columnValue);
586
- SqlType t = (SqlType)asPtr(self)->getColumnType(column);
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
- return SYMBOL_OF(SQL_BIGINT);
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
- return SYMBOL_OF(SQL_DOUBLE);
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
- return SYMBOL_OF(SQL_LONGVARCHAR);
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
- return SYMBOL_OF(SQL_DATE);
614
- case NUOSQL_TIME:
615
- return SYMBOL_OF(SQL_TIME);
597
+ symbol = ID2SYM(rb_intern("date"));
598
+ break;
599
+
616
600
  case NUOSQL_TIMESTAMP:
617
- return SYMBOL_OF(SQL_TIMESTAMP);
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
- // raise FEATURE_NOT_YET_IMPLEMENTED for unsupported types
634
- rb_raise_nuodb_error(-2, "Invalid SQL type: %d", t);
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
- void WrapStatement::init(VALUE module)
1085
+ static
1086
+ void nuodb_statement_decr_reference_count(void * ptr)
641
1087
  {
642
- INIT_TYPE("Statement");
643
- DEFINE_METHOD(close, 0);
644
- DEFINE_METHOD(execute, 1);
645
- DEFINE_METHOD(executeQuery, 1);
646
- DEFINE_METHOD(executeUpdate, 1);
647
- DEFINE_METHOD(getResultSet, 0);
648
- DEFINE_METHOD(getUpdateCount, 0);
649
- DEFINE_METHOD(getGeneratedKeys, 0);
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
- VALUE WrapStatement::close(VALUE self)
1137
+ static
1138
+ VALUE nuodb_statement_initialize(VALUE parent)
653
1139
  {
654
- try
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
- asPtr(self)->close();
657
- return Qnil;
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
- catch (SQLException & e)
1193
+ else
660
1194
  {
661
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to successfully close statement: %s", e.getText());
1195
+ rb_raise(rb_eArgError, "invalid state: statement handle nil");
662
1196
  }
1197
+ return Qnil;
663
1198
  }
664
1199
 
665
- VALUE WrapStatement::execute(VALUE self, VALUE sqlValue)
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
- const char* sql = StringValuePtr(sqlValue);
668
- try
1213
+ if (TYPE(sql) != T_STRING)
669
1214
  {
670
- return AS_QBOOL(asPtr(self)->execute(sql, NuoDB::RETURN_GENERATED_KEYS ));
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
- VALUE WrapStatement::executeQuery(VALUE self, VALUE sqlValue)
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
- return WrapResultSet::wrap(asPtr(self)->executeQuery(sql));
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
- catch (SQLException & e)
1231
+ else
686
1232
  {
687
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to execute SQL query statement (\"%s\"): %s", sql, e.getText());
1233
+ rb_raise(rb_eArgError, "invalid state: prepared statement handle nil");
688
1234
  }
1235
+ return Qnil;
689
1236
  }
690
1237
 
691
- VALUE WrapStatement::executeUpdate(VALUE self, VALUE sqlValue)
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
- const char* sql = StringValuePtr(sqlValue);
694
- try
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
- return INT2NUM(asPtr(self)->executeUpdate(sql));
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
- catch (SQLException & e)
1261
+ else
699
1262
  {
700
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to execute SQL update statement (\"%s\"): %s", sql, e.getText());
1263
+ rb_raise(rb_eArgError, "invalid state: prepared statement handle nil");
701
1264
  }
1265
+ return Qnil;
702
1266
  }
703
1267
 
704
- VALUE WrapStatement::getResultSet(VALUE self)
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
- try
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
- return WrapResultSet::wrap(asPtr(self)->getResultSet());
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
- catch (SQLException & e)
1290
+ else
711
1291
  {
712
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to get statement result set: %s", e.getText());
1292
+ rb_raise(rb_eArgError, "invalid state: statement handle nil");
713
1293
  }
1294
+ return Qnil;
714
1295
  }
715
1296
 
716
- VALUE WrapStatement::getUpdateCount(VALUE self)
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
- try
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
- return INT2NUM(asPtr(self)->getUpdateCount());
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
- catch (SQLException & e)
1326
+ else
723
1327
  {
724
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to get update count for statement: %s", e.getText());
1328
+ rb_raise(rb_eArgError, "invalid state: statement handle nil");
725
1329
  }
1330
+ return Qnil;
726
1331
  }
727
1332
 
728
- VALUE WrapStatement::getGeneratedKeys(VALUE self)
1333
+ static
1334
+ void nuodb_define_statement_api()
729
1335
  {
730
- try
731
- {
732
- return WrapResultSet::wrap(asPtr(self)->getGeneratedKeys());
733
- }
734
- catch (SQLException & e)
735
- {
736
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to get generated keys for statement: %s", e.getText());
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
- void WrapPreparedStatement::init(VALUE module)
743
- {
744
- INIT_TYPE("PreparedStatement");
745
- DEFINE_METHOD(close, 0);
746
- DEFINE_METHOD(setBoolean, 2);
747
- DEFINE_METHOD(setInteger, 2);
748
- DEFINE_METHOD(setDouble, 2);
749
- DEFINE_METHOD(setString, 2);
750
- DEFINE_METHOD(setTime, 2);
751
- DEFINE_METHOD(execute, 0);
752
- DEFINE_METHOD(executeQuery, 0);
753
- DEFINE_METHOD(executeUpdate, 0);
754
- DEFINE_METHOD(getResultSet, 0);
755
- DEFINE_METHOD(getUpdateCount, 0);
756
- DEFINE_METHOD(getGeneratedKeys, 0);
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
- VALUE WrapPreparedStatement::close(VALUE self)
1384
+ static
1385
+ void nuodb_prepared_statement_free(void * ptr)
760
1386
  {
761
- try
762
- {
763
- asPtr(self)->close();
764
- return Qnil;
765
- }
766
- catch (SQLException & e)
1387
+ trace("nuodb_prepared_statement_free");
1388
+
1389
+ if (ptr != NULL)
767
1390
  {
768
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to successfully close prepared statement: %s", e.getText());
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
- VALUE WrapPreparedStatement::setTime(VALUE self, VALUE indexValue, VALUE valueValue)
1400
+ static
1401
+ void nuodb_prepared_statement_mark(void * ptr)
773
1402
  {
774
- int32_t index = NUM2UINT(indexValue);
775
- struct timeval tv = rb_time_timeval(valueValue);
1403
+ trace("nuodb_prepared_statement_mark");
776
1404
 
777
- SqlDate d( (((int64_t)tv.tv_sec)* 1000)+ (((int64_t)tv.tv_usec)/ 1000));
778
- try
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
- VALUE WrapPreparedStatement::setBoolean(VALUE self, VALUE indexValue, VALUE valueValue)
1409
+ static
1410
+ void nuodb_prepared_statement_decr_reference_count(nuodb_handle * handle)
791
1411
  {
792
- int32_t index = NUM2UINT(indexValue);
793
- bool value = valueValue ? true : false;
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
- VALUE WrapPreparedStatement::setInteger(VALUE self, VALUE indexValue, VALUE valueValue)
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
- int32_t index = NUM2UINT(indexValue);
809
- int32_t value = NUM2INT(valueValue);
810
- try
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
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to set prepared statement integer value (%d, %d): %s", index, value, e.getText());
1470
+ rb_raise(rb_eTypeError, "wrong sql argument type %s (String expected)", rb_class2name(CLASS_OF(sql)));
818
1471
  }
819
- }
820
1472
 
821
- VALUE WrapPreparedStatement::setDouble(VALUE self, VALUE indexValue, VALUE valueValue)
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
- asPtr(self)->setDouble(index, value);
828
- return Qnil;
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
- catch (SQLException & e)
1524
+ else
831
1525
  {
832
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to set prepared statement double value (%d, %g): %s", index, value, e.getText());
1526
+ rb_raise(rb_eArgError, "invalid state: prepared statement handle nil");
833
1527
  }
1528
+ return Qnil;
834
1529
  }
835
1530
 
836
- VALUE WrapPreparedStatement::setString(VALUE self, VALUE indexValue, VALUE valueValue)
1531
+ static
1532
+ void raise_unsupported_type_at_index(char const * type_name, int32_t index)
837
1533
  {
838
- int32_t index = NUM2UINT(indexValue);
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
- VALUE WrapPreparedStatement::execute(VALUE self)
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
- return AS_QBOOL(asPtr(self)->execute());
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 execute SQL prepared statement: %s", e.getText());
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
- VALUE WrapPreparedStatement::executeQuery(VALUE self)
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
- try
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
- return WrapResultSet::wrap(asPtr(self)->executeQuery());
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
- catch (SQLException & e)
1720
+ else
870
1721
  {
871
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to execute SQL query prepared statement: %s", e.getText());
1722
+ rb_raise(rb_eArgError, "invalid state: prepared statement handle nil");
872
1723
  }
1724
+ return Qnil;
873
1725
  }
874
1726
 
875
- VALUE WrapPreparedStatement::executeUpdate(VALUE self)
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
- try
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
- return INT2NUM(asPtr(self)->executeUpdate());
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
- catch (SQLException & e)
1753
+ else
882
1754
  {
883
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to execute SQL update prepared statement: %s", e.getText());
1755
+ rb_raise(rb_eArgError, "invalid state: prepared statement handle nil");
884
1756
  }
1757
+ return Qnil;
885
1758
  }
886
1759
 
887
- VALUE WrapPreparedStatement::getResultSet(VALUE self)
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
- try
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
- return WrapResultSet::wrap(asPtr(self)->getResultSet());
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
- catch (SQLException & e)
1783
+ else
894
1784
  {
895
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to get the result set for the prepared statement: %s", e.getText());
1785
+ rb_raise(rb_eArgError, "invalid state: prepared statement handle nil");
896
1786
  }
1787
+ return Qnil;
897
1788
  }
898
1789
 
899
- VALUE WrapPreparedStatement::getUpdateCount(VALUE self)
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
- try
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
- return INT2NUM(asPtr(self)->getUpdateCount());
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
- catch (SQLException & e)
1812
+ else
906
1813
  {
907
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to get the update count for the prepared statement: %s", e.getText());
1814
+ rb_raise(rb_eArgError, "invalid state: prepared statement handle nil");
908
1815
  }
1816
+ return Qnil;
909
1817
  }
910
1818
 
911
- VALUE WrapPreparedStatement::getGeneratedKeys(VALUE self)
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
- try
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
- return WrapResultSet::wrap(asPtr(self)->getGeneratedKeys());
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
- catch (SQLException & e)
1848
+ else
918
1849
  {
919
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to get the generated keys for the prepared statement: %s", e.getText());
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
- type = rb_define_class_under(module, "Connection", rb_cObject);
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
- // todo add .columns, or not? If we did: .columns(table_name)
932
- // todo and tie this into the SchemaCache on the Ruby side.
933
- rb_define_method(type, "commit", RUBY_METHOD_FUNC(commit), 0);
934
- rb_define_method(type, "disconnect", RUBY_METHOD_FUNC(close), 0);
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(type, "autocommit=", RUBY_METHOD_FUNC(setAutoCommit), 1);
943
- rb_define_method(type, "autocommit?", RUBY_METHOD_FUNC(hasAutoCommit), 0);
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
- // DEPRECATED, going away shortly...
1883
+ /*
1884
+ * Class NuoDB::Connection
1885
+ */
946
1886
 
947
- // todo use .new and .initialize instead...
948
- DEFINE_SINGLE(createSqlConnection, 4);
949
- DEFINE_METHOD(createStatement, 0);
950
- DEFINE_METHOD(createPreparedStatement, 1);
951
- DEFINE_METHOD(setAutoCommit, 1);
952
- DEFINE_METHOD(hasAutoCommit, 0);
953
- DEFINE_METHOD(getMetaData, 0);
954
- DEFINE_METHOD(close, 0);
955
- DEFINE_METHOD(getSchema, 0);
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
- VALUE WrapConnection::getSchema(VALUE self)
1916
+ static
1917
+ void nuodb_connection_free(void * ptr)
959
1918
  {
960
- try
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
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to get schema for connection: %s", e.getText());
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
- VALUE WrapConnection::close(VALUE self)
1931
+ static
1932
+ void nuodb_connection_mark(void * ptr)
971
1933
  {
972
- try
973
- {
974
- asPtr(self)->close();
975
- return Qnil;
976
- }
977
- catch (SQLException & e)
978
- {
979
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to successfully disconnect connection: %s", e.getText());
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
- VALUE WrapConnection::ping(VALUE self)
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
- try
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
- asPtr(self)->ping();
988
- return Qtrue;
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
- catch (SQLException & e)
2057
+ else
991
2058
  {
992
- return Qfalse;
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
- VALUE WrapConnection::createStatement(VALUE self)
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
- try
999
- {
1000
- return WrapStatement::wrap(asPtr(self)->createStatement());
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
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to create statement: %s", e.getText());
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
- VALUE WrapConnection::createPreparedStatement(VALUE self, VALUE sqlValue)
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
- const char * sql = StringValuePtr(sqlValue);
1011
- try
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
- return WrapPreparedStatement::wrap( asPtr(self)->prepareStatement(sql, NuoDB::RETURN_GENERATED_KEYS));
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
- catch (SQLException & e)
2209
+ else
1016
2210
  {
1017
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to create prepared statement (%s): %s", sql, e.getText());
2211
+ rb_raise(rb_eArgError, "invalid state: connection handle nil");
1018
2212
  }
2213
+ return Qnil;
1019
2214
  }
1020
2215
 
1021
- VALUE WrapConnection::setAutoCommit(VALUE self, VALUE autoCommitValue)
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
- bool autoCommit = !(RB_TYPE_P(autoCommitValue, T_FALSE) || RB_TYPE_P(autoCommitValue, T_NIL));
1024
- try
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
- asPtr(self)->setAutoCommit(autoCommit);
1027
- return Qnil;
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
- catch (SQLException & e)
2247
+ else
1030
2248
  {
1031
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to set auto-commit (%d) for connection: %s", autoCommit, e.getText());
2249
+ rb_raise(rb_eArgError, "invalid state: connection handle nil");
1032
2250
  }
2251
+ return Qnil;
1033
2252
  }
1034
2253
 
1035
- VALUE WrapConnection::hasAutoCommit(VALUE self)
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
- try
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
- return AS_QBOOL(asPtr(self)->getAutoCommit());
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
- catch (SQLException & e)
2283
+ else
1042
2284
  {
1043
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to determine auto-commit state for connection: %s", e.getText());
2285
+ rb_raise(rb_eArgError, "invalid state: connection handle nil");
1044
2286
  }
2287
+ return Qnil;
1045
2288
  }
1046
2289
 
1047
- VALUE WrapConnection::commit(VALUE self)
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
- try
2315
+ trace("nuodb_connection_initialize");
2316
+
2317
+ if (TYPE(hash) != T_HASH)
1050
2318
  {
1051
- asPtr(self)->commit();
1052
- return Qnil;
2319
+ rb_raise(rb_eTypeError, "wrong argument type %s (Hash expected)", rb_class2name(CLASS_OF(hash)));
1053
2320
  }
1054
- catch (SQLException & e)
2321
+
2322
+ nuodb_connection_handle * handle = cast_handle<nuodb_connection_handle>(self);
2323
+ if (NIL_P(handle->database))
1055
2324
  {
1056
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to commit transaction: %s", e.getText());
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
- asPtr(self)->rollback();
1065
- return Qnil;
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
- catch (SQLException & e)
2349
+ if (handle->password == Qnil)
1068
2350
  {
1069
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to rollback transaction: %s", e.getText());
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
- return WrapDatabaseMetaData::wrap( asPtr(self)->getMetaData());
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
- catch (SQLException & e)
2371
+ if (handle->timezone == Qnil)
1080
2372
  {
1081
- rb_raise_nuodb_error(e.getSqlcode(), "Failed to get database metadata: %s", e.getText());
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
- VALUE WrapConnection::createSqlConnection(VALUE self,
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
- try
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
- return WrapConnection::wrap( getDatabaseConnection(database,
1099
- username,
1100
- password,
1101
- 1,
1102
- "schema",
1103
- schema));
2407
+ rb_jump_tag(state);
1104
2408
  }
1105
- catch (SQLException & e)
2409
+ else
1106
2410
  {
1107
- rb_raise_nuodb_error(e.getSqlcode(),
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
- c_nuodb_error = rb_const_get(m_nuodb, rb_intern("Error"));
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
- WrapConnection::init(m_nuodb);
1125
- WrapDatabaseMetaData::init(m_nuodb);
1126
- WrapStatement::init(m_nuodb);
1127
- WrapPreparedStatement::init(m_nuodb);
1128
- WrapResultSet::init(m_nuodb);
1129
- WrapResultSetMetaData::init(m_nuodb);
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
  }