firebird 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8206ea5d3f56d1d12122f60fa8c3799283ac40722abd92869053d71edfd26a11
4
+ data.tar.gz: 1e972b28ce6fb02e4757136452f53c6471d8fc9f3212eaeb91e2bcdb4ded34c7
5
+ SHA512:
6
+ metadata.gz: 3088aa127c44b2edd400c975c301fdaf12e29eeddf380592309c54b7fcdda8cd65188a1a2418ad1c10e3b0236f23f66d7812c9a0c2694a4931cc8dc663a7c3f0
7
+ data.tar.gz: 169a3bbda6f11288543cba94ae6d6a96bfb448437883a3e38b6d2484d9ae16e0e5e77ae32144ee617576911a67000f1478c17cd3fd551f8644a952fc9a432873
@@ -0,0 +1,8 @@
1
+ Gemfile.lock
2
+ *.swp
3
+ *.gem
4
+ *.so
5
+ *.o
6
+ *.log
7
+ Makefile
8
+ tags
@@ -0,0 +1,27 @@
1
+ .base: &base
2
+ variables:
3
+ RAKE_VERSION: 10.4.2
4
+ before_script:
5
+ - apt-get update && apt-get install -y debconf-utils git-core
6
+ - echo "firebird2.5-super shared/firebird/sysdba_password/new_password password masterkey" | debconf-set-selections
7
+ - DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential patch ruby-dev zlib1g-dev liblzma-dev firebird-dev firebird2.5-super
8
+ - service firebird2.5-super start
9
+ - gem install bundler && bundle
10
+ script:
11
+ - bundle exec rake compile:fb_ext test
12
+
13
+ ruby-1.9.3:
14
+ <<: *base
15
+ image: zedtux/ruby-1.9.3:latest
16
+
17
+ ruby-2.2:
18
+ <<: *base
19
+ image: ruby:2.2-jessie
20
+
21
+ ruby-2.3:
22
+ <<: *base
23
+ image: ruby:2.3-jessie
24
+
25
+ ruby-2.4:
26
+ <<: *base
27
+ image: ruby:2.4-jessie
@@ -0,0 +1,20 @@
1
+ language: ruby
2
+ install:
3
+ - sudo apt-get update
4
+ # && sudo apt-get install debconf-utils
5
+ # - echo "firebird2.5-super shared/firebird/sysdba_password/new_password password masterkey" | sudo debconf-set-selections
6
+ - sudo apt-get install -y build-essential patch ruby-dev zlib1g-dev liblzma-dev firebird-dev firebird2.5-super
7
+ - bundle
8
+ before_script:
9
+ - export ISC_PASSWORD=$(sudo grep ISC_PASSWORD /etc/firebird/2.5/SYSDBA.password | ruby -e "puts STDIN.read.split(/[=\"]/)[2]")
10
+ - echo "modify sysdba -pw masterkey" | gsec -user SYSDBA -password $ISC_PASSWORD
11
+ - sudo mkdir -p /tmp/firebird
12
+ - sudo chown firebird.firebird /tmp/firebird
13
+ - sudo chmod 0777 /tmp/firebird
14
+ script:
15
+ - bundle exec rake compile:fb_ext test
16
+ rvm:
17
+ - 2.4
18
+ - 2.5
19
+ - 2.6
20
+ - 2.7
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'http://rubygems.org'
2
+ gemspec
@@ -0,0 +1,15 @@
1
+ # Fb - Ruby Firebird Extension Library
2
+
3
+ [![Build Status](https://travis-ci.org/mariuz/fb.svg?branch=master)](https://travis-ci.org/mariuz/fb)
4
+
5
+ This is a Ruby driver for the [Firebird](https://firebirdsql.org/) database.
6
+
7
+ # Sample usage
8
+
9
+ * See the [API Documentation](http://www.rubydoc.info/github/rowland/fb)
10
+ * See [USAGE.txt](USAGE.txt) for a brief tutorial on the usage.
11
+
12
+ # Running Tests
13
+
14
+ bundle exec rake compile:fb_ext
15
+ bundle exec rake
@@ -0,0 +1,13 @@
1
+ require "rake/testtask"
2
+ require "rake/extensiontask"
3
+
4
+ Rake::ExtensionTask.new "fb_ext" do |ext|
5
+ ext.ext_dir = 'ext/fb'
6
+ ext.lib_dir = 'lib/fb'
7
+ end
8
+
9
+ Rake::TestTask.new do |t|
10
+ t.test_files = FileList['test/**/*.rb'] - FileList['test/test_helper.rb']
11
+ end
12
+
13
+ task :default => :test
@@ -0,0 +1,180 @@
1
+ # Ruby Firebird Extension Library
2
+ #
3
+ # Please see the RDoc documentation for API details.
4
+ #
5
+ # What follows is a brief overview of how to use this library.
6
+ # This file is executable. However, you may have to adjust the database connection parameters.
7
+
8
+ # Load the library
9
+
10
+ require 'fb'
11
+
12
+ # The library contains on module, Fb.
13
+ # Within Fb are four primary classes, Fb::Database, Fb::Connection, Fb::Cursor and Fb::Error.
14
+ # For convenience, we'll include these classes into the current context.
15
+
16
+ include Fb
17
+
18
+ # The Database class acts as a factory for Connections.
19
+ # It can also create and drop databases.
20
+
21
+ db = Database.new(
22
+ :database => "localhost:c:/var/fbdata/readme.fdb",
23
+ :username => 'sysdba',
24
+ :password => 'masterkey')
25
+
26
+ # :database is the only parameter without a default.
27
+
28
+ # Let's connect to the database, creating it if it doesn't already exist.
29
+
30
+ conn = db.connect rescue db.create.connect
31
+
32
+ # We'll need to create the database schema if this is a new database.
33
+
34
+ conn.execute("CREATE TABLE TEST (ID INT NOT NULL PRIMARY KEY, NAME VARCHAR(20))") if !conn.table_names.include?("TEST")
35
+
36
+ # Let's insert some test data using a parameterized query. Note the use of question marks for place holders.
37
+
38
+ 10.times {|id| conn.execute("INSERT INTO TEST VALUES (?, ?)", id, "John #{id}") }
39
+
40
+ # Here we'll conduct a spot check of the data we have just inserted.
41
+
42
+ ary = conn.query("SELECT * FROM TEST WHERE ID = 0 OR ID = 9")
43
+ ary.each {|row| puts "ID: #{row[0]}, Name: #{row[1]}" }
44
+
45
+ # Don't like tying yourself down to column offsets?
46
+
47
+ ary = conn.query(:hash, "SELECT * FROM TEST WHERE ID = 0 OR ID = 9")
48
+ ary.each {|row| puts "ID: #{row['ID']}, Name: #{row['NAME']}" }
49
+
50
+ # Let's change all the names.
51
+
52
+ total_updated = 0
53
+ conn.execute("SELECT ID FROM TEST") do |cursor|
54
+ cursor.each do |row|
55
+ updated = conn.execute("UPDATE TEST SET NAME = ? WHERE ID = ?", "John Doe #{row[0]}", row[0])
56
+ total_updated += updated
57
+ end
58
+ end
59
+ puts "We updated a total of #{total_updated} rows."
60
+
61
+ # Actually, I only need the first and last rows.
62
+
63
+ deleted = conn.execute("DELETE FROM TEST WHERE ID > ? AND ID < ?", 0, 9)
64
+ puts "Expecting to delete 8 rows, we have deleted #{deleted}."
65
+
66
+ # Using a simple, per-connection transaction strategy, we'll demonstrate rollback and commit.
67
+
68
+ conn.transaction
69
+
70
+ for i in 10..1000
71
+ conn.execute("INSERT INTO TEST VALUES (?, ?)", i, "Jane #{i}")
72
+ end
73
+
74
+ # What was I thinking? Let's roll that back.
75
+
76
+ conn.rollback
77
+
78
+ # Are they still there?
79
+
80
+ janes = conn.query("SELECT * FROM TEST WHERE ID >= 10")
81
+ puts "Expecting zero rows, we find #{janes.size} Janes."
82
+
83
+ # Let's try again.
84
+
85
+ conn.transaction
86
+
87
+ 10.upto(19) do |i|
88
+ conn.execute("INSERT INTO TEST (ID, NAME) VALUES (?, ?)", i, "Sue #{i}")
89
+ end
90
+
91
+ # That's more like it.
92
+
93
+ conn.commit
94
+
95
+ # It's important to close your cursor when you're done with it.
96
+
97
+ cursor = conn.execute("SELECT * FROM TEST")
98
+ while row = cursor.fetch(:hash)
99
+ break if row['NAME'] =~ /e 13/
100
+ end
101
+ cursor.close
102
+
103
+ # You may find it easier to use a block.
104
+
105
+ conn.execute("SELECT * FROM TEST") do |cursor|
106
+ while row = cursor.fetch(:hash)
107
+ break if row['NAME'] =~ /e 13/
108
+ end
109
+ end
110
+
111
+ # That way the cursor always gets closed, even if an exception is raised.
112
+ # Transactions work the same way. Here's one that should work.
113
+
114
+ conn.transaction do
115
+ 20.upto(25) do |i|
116
+ conn.execute("INSERT INTO TEST VALUES (?, ?)", i, "George #{i}")
117
+ end
118
+ end
119
+
120
+ # The transaction is automatically committed if no exception is raised in the block.
121
+ # We expect trouble in this next example, on account of our primary key.
122
+
123
+ begin
124
+ conn.transaction do
125
+ execute("INSERT INTO TEST VALUES (0, 'Trouble')")
126
+ puts "This line should never be executed."
127
+ end
128
+ rescue
129
+ puts "Rescued."
130
+ end
131
+
132
+ # Is it there?
133
+
134
+ trouble = conn.query("SELECT * FROM TEST WHERE NAME = 'Trouble'")
135
+ puts "Expecting zero rows, we find #{trouble.size} 'Trouble' rows."
136
+
137
+ # How about demonstrating a more advanced transaction?
138
+ # First, we'll start a snapshot transaction.
139
+ # This should give us a consistent view of the database.
140
+
141
+ conn.transaction("SNAPSHOT") do
142
+
143
+ # Then, we'll open another connection to the database and insert some rows.
144
+
145
+ Database.connect(:database => "localhost:c:/var/fbdata/readme.fdb") do |conn2|
146
+ for i in 100...110
147
+ conn2.execute("INSERT INTO TEST VALUES (?, ?)", i, "Hi #{i}")
148
+ end
149
+ end
150
+
151
+ # Now, let's see if we see them.
152
+
153
+ hi = conn.query("SELECT * FROM TEST WHERE ID >= ?", 100)
154
+ puts "Expecting zero rows, we find #{hi.size} Hi rows."
155
+ end
156
+
157
+ # Now we will try our example again, only with a READ COMMITTED transaction.
158
+
159
+ conn.transaction("READ COMMITTED") do
160
+
161
+ # Then, we'll open another connection to the database and insert some rows.
162
+
163
+ Database.connect(:database => "localhost:c:/var/fbdata/readme.fdb") do |conn2|
164
+ for i in 200...210
165
+ conn2.execute("INSERT INTO TEST VALUES (?, ?)", i, "Hello #{i}")
166
+ end
167
+ end
168
+
169
+ # Now, let's see if we see them.
170
+
171
+ hello = conn.query("SELECT * FROM TEST WHERE ID >= ?", 200)
172
+ puts "Expecting ten rows, we find #{hello.size}."
173
+ end
174
+
175
+ # Don't forget to close up shop.
176
+
177
+ conn.close
178
+
179
+ # We could have called conn.drop.
180
+ # We could still call db.drop
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env ruby
2
+ # = Windows
3
+ # === Sample of Makefile creation:
4
+ # <tt>ruby extconf.rb --with-opt-dir=C:/Progra~1/Firebird/Firebird_2_5</tt>
5
+ # === Notes
6
+ # * Windows is known to build with Ruby from rubyinstaller.org.
7
+ # * New in this release is automatically finding your Firebird install under Program Files.
8
+ # * If your install is some place non-standard (or on a non-English version of Windows), you'll need to run extconf.rb manually as above.
9
+ # * mkmf doesn't like directories with spaces, hence the 8.3 notation in the example above.
10
+ # = Linux
11
+ # === Notes
12
+ # * Build seems to "just work."
13
+ # * Unit tests take about 10 times as long to complete using Firebird Classic. Default xinetd.conf settings may not allow the tests to complete due to the frequency with which new attachments are made.
14
+ # = Mac OS X (Intel)
15
+ # * Works
16
+
17
+ def unquote(string)
18
+ string.sub(/\A(['"])?(.*?)\1?\z/m, '\2') unless string.nil?
19
+ end
20
+
21
+ def key_exists?(path)
22
+ begin
23
+ Win32::Registry::HKEY_LOCAL_MACHINE.open(path, ::Win32::Registry::KEY_READ)
24
+ return true
25
+ rescue
26
+ return false
27
+ end
28
+ end
29
+
30
+ def read_firebird_registry
31
+ require 'win32/registry'
32
+ if key_exists?('SOFTWARE\Firebird Project\Firebird Server\Instances')
33
+ Win32::Registry::HKEY_LOCAL_MACHINE.open('SOFTWARE\Firebird Project\Firebird Server\Instances', Win32::Registry::Constants::KEY_READ) do |reg|
34
+ return reg.read_s('DefaultInstance') rescue nil
35
+ end
36
+ else
37
+ return false
38
+ end
39
+ end
40
+
41
+ def search_firebird_path
42
+ program_files = ENV['ProgramFiles'].gsub('\\', '/').gsub(/(\w+\s+[\w\s]+)/) { |s| s.size > 8 ? s[0,6] + '~1' : s }
43
+ program_files_x86 = ENV['ProgramFiles'].gsub('\\', '/').gsub(/(\w+\s+[\w\s]+)/) { |s| s.size > 8 ? s[0,6] + '~2' : s }
44
+ result = Dir["#{program_files}/Firebird/Firebird_*"].sort.last || Dir["#{program_files_x86}/Firebird/Firebird_*"].sort.last
45
+ end
46
+
47
+ if RUBY_PLATFORM =~ /(mingw32|mswin32)/ and ARGV.grep(/^--with-opt-dir=/).empty?
48
+ opt = unquote(ENV['FIREBIRD'])
49
+ opt = opt || read_firebird_registry
50
+ opt = opt || search_firebird_path
51
+ if opt
52
+ ARGV << "--with-opt-dir=#{opt}"
53
+ else
54
+ puts "No any Firebird instances found in system."
55
+ exit
56
+ end
57
+ end
58
+
59
+ require 'mkmf'
60
+
61
+ libs = %w/ fbclient gds /
62
+
63
+ case RUBY_PLATFORM
64
+ when /bccwin32/
65
+ libs.push "fbclient_bor"
66
+ when /mswin32/, /mingw32/
67
+ $CFLAGS = $CFLAGS + " -DOS_WIN32"
68
+ libs.push "fbclient_ms"
69
+ when /darwin/
70
+ # hosttype = `uname -m`.chomp
71
+ $CFLAGS += " -DOS_UNIX"
72
+ # $CFLAGS.gsub!(/-arch (\w+)/) { |m| $1 == hosttype ? m : '' }
73
+ # $LDFLAGS.gsub!(/-arch (\w+)/) { |m| $1 == hosttype ? m : '' }
74
+ # CONFIG['LDSHARED'].gsub!(/-arch (\w+)/) { |m| $1 == hosttype ? m : '' }
75
+ $CPPFLAGS += " -I/Library/Frameworks/Firebird.framework/Headers"
76
+ $LDFLAGS += " -framework Firebird"
77
+ when /linux/
78
+ $CFLAGS = $CFLAGS + " -DOS_UNIX"
79
+ end
80
+
81
+ dir_config("firebird")
82
+
83
+ test_func = "isc_attach_database"
84
+
85
+ case RUBY_PLATFORM
86
+ when /mswin32/, /mingw32/
87
+ libs.find {|lib| have_library(lib) } and
88
+ have_func(test_func, ["ibase.h"])
89
+ else
90
+ libs.find {|lib| have_library(lib, test_func) }
91
+ end
92
+
93
+ create_makefile("fb_ext")
@@ -0,0 +1,3022 @@
1
+ /*
2
+ * fb.c
3
+ * A module to access the Firebird database from Ruby.
4
+ * Fork of interbase.c to fb.c by Brent Rowland.
5
+ * All changes, improvements and associated bugs Copyright (C) 2006 Brent Rowland and Target Training International.
6
+ * License to all changes, improvements and bugs is granted under the same terms as the original and/or
7
+ * the Ruby license, whichever is most applicable.
8
+ * Based on interbase.c
9
+ *
10
+ * Copyright (C) 1999 by NaCl inc.
11
+ * Copyright (C) 1997,1998 by RIOS Corporation
12
+ *
13
+ * Permission to use, copy, modify, and distribute this software and its
14
+ * documentation for any purpose and without fee is hereby granted, provided
15
+ * that the above copyright notice appear in all copies.
16
+ * RIOS Corporation makes no representations about the suitability of
17
+ * this software for any purpose. It is provided "as is" without express
18
+ * or implied warranty. By use of this software the user agrees to
19
+ * indemnify and hold harmless RIOS Corporation from any claims or
20
+ * liability for loss arising out of such use.
21
+ */
22
+
23
+ #include "ruby.h"
24
+
25
+ #ifdef HAVE_RUBY_REGEX_H
26
+ # include "ruby/re.h"
27
+ #else
28
+ # include "re.h"
29
+ #endif
30
+
31
+ // this sucks. but for some reason these moved around between 1.8 and 1.9
32
+ #ifdef ONIGURUMA_H
33
+ #define IGNORECASE ONIG_OPTION_IGNORECASE
34
+ #else
35
+ #define IGNORECASE RE_OPTION_IGNORECASE
36
+ #endif
37
+
38
+ #include <stdio.h>
39
+ #include <string.h>
40
+ #include <limits.h>
41
+ #include <ibase.h>
42
+ #include <float.h>
43
+ #include <time.h>
44
+ #include <stdbool.h>
45
+
46
+
47
+ #define SQLDA_COLSINIT 50
48
+ #define SQLCODE_NOMORE 100
49
+ #define TPBBUFF_ALLOC 64
50
+ #define CMND_DELIMIT " \t\n\r\f"
51
+ #define LIST_DELIMIT ", \t\n\r\f"
52
+ #define META_NAME_MAX 31
53
+
54
+ /* Statement type */
55
+ #define STATEMENT_DDL 1
56
+ #define STATEMENT_DML 0
57
+
58
+ /* Execute process flag */
59
+ #define EXECF_EXECDML 0
60
+ #define EXECF_SETPARM 1
61
+
62
+ static VALUE rb_mFb;
63
+ static VALUE rb_cFbDatabase;
64
+ static VALUE rb_cFbConnection;
65
+ static VALUE rb_cFbCursor;
66
+ static VALUE rb_cFbSqlType;
67
+ /* static VALUE rb_cFbGlobal; */
68
+ static VALUE rb_eFbError;
69
+ static VALUE rb_sFbField;
70
+ static VALUE rb_sFbIndex;
71
+ static VALUE rb_sFbColumn;
72
+ static VALUE rb_cDate;
73
+
74
+ static ID id_matches;
75
+ static ID id_downcase_bang;
76
+ static VALUE re_lowercase;
77
+ static ID id_rstrip_bang;
78
+ static ID id_sub_bang;
79
+ static ID id_force_encoding;
80
+
81
+ /* static char isc_info_stmt[] = { isc_info_sql_stmt_type }; */
82
+ /* static char isc_info_buff[16]; */
83
+ static char isc_tpb_0[] = {
84
+ isc_tpb_version1, isc_tpb_write,
85
+ isc_tpb_concurrency, isc_tpb_nowait
86
+ };
87
+
88
+ /* structs */
89
+
90
+ /* DB handle and TR parameter block list structure */
91
+ typedef struct
92
+ {
93
+ isc_db_handle *dbb_ptr ;
94
+ long tpb_len ;
95
+ char *tpb_ptr ;
96
+ } ISC_TEB ; /* transaction existence block */
97
+
98
+ /* InterBase varchar structure */
99
+ typedef struct
100
+ {
101
+ short vary_length;
102
+ char vary_string[1];
103
+ } VARY;
104
+
105
+ struct FbConnection {
106
+ isc_db_handle db; /* DB handle */
107
+ isc_tr_handle transact; /* transaction handle */
108
+ VALUE cursor;
109
+ unsigned short dialect;
110
+ unsigned short db_dialect;
111
+ short downcase_names;
112
+ VALUE encoding;
113
+ int dropped;
114
+ ISC_STATUS isc_status[20];
115
+ /* struct FbConnection *next; */
116
+ };
117
+
118
+ /* static struct FbConnection *fb_connection_list; */
119
+
120
+ struct FbCursor {
121
+ int open;
122
+ int eof;
123
+ isc_tr_handle auto_transact;
124
+ isc_stmt_handle stmt;
125
+ XSQLDA *i_sqlda;
126
+ XSQLDA *o_sqlda;
127
+ char *i_buffer;
128
+ long i_buffer_size;
129
+ char *o_buffer;
130
+ long o_buffer_size;
131
+ VALUE fields_ary;
132
+ VALUE fields_hash;
133
+ VALUE connection;
134
+ };
135
+
136
+ typedef struct trans_opts
137
+ {
138
+ const char *option1;
139
+ const char *option2;
140
+ char optval;
141
+ short position;
142
+ struct trans_opts *sub_opts;
143
+ } trans_opts;
144
+
145
+ /* global utilities */
146
+
147
+ #define FB_ALIGN(n, b) ((n + b - 1) & ~(b - 1))
148
+ #define UPPER(c) (((c) >= 'a' && (c)<= 'z') ? (c) - 'a' + 'A' : (c))
149
+ #define FREE(p) if (p) { xfree(p); p = 0; }
150
+ #define SETNULL(p) if (p && strlen(p) == 0) { p = 0; }
151
+ // #define HERE(s) printf("%s\n", s)
152
+ #define HERE(s)
153
+
154
+ static long calculate_buffsize(XSQLDA *sqlda)
155
+ {
156
+ XSQLVAR *var;
157
+ long cols;
158
+ short dtp;
159
+ long offset = 0;
160
+ long alignment;
161
+ long length;
162
+ long count;
163
+
164
+ cols = sqlda->sqld;
165
+ var = sqlda->sqlvar;
166
+ for (count = 0; count < cols; var++,count++) {
167
+ length = alignment = var->sqllen;
168
+ dtp = var->sqltype & ~1;
169
+
170
+ if (dtp == SQL_TEXT) {
171
+ alignment = 1;
172
+ } else if (dtp == SQL_VARYING) {
173
+ length += sizeof(short);
174
+ alignment = sizeof(short);
175
+ }
176
+
177
+ offset = FB_ALIGN(offset, alignment);
178
+ offset += length;
179
+ offset = FB_ALIGN(offset, sizeof(short));
180
+ offset += sizeof(short);
181
+ }
182
+
183
+ return offset + sizeof(short);
184
+ }
185
+
186
+ #if (FB_API_VER >= 20)
187
+ static VALUE fb_error_msg(const ISC_STATUS *isc_status)
188
+ {
189
+ char msg[1024];
190
+ VALUE result = rb_str_new(NULL, 0);
191
+ while (fb_interpret(msg, 1024, &isc_status))
192
+ {
193
+ result = rb_str_cat(result, msg, strlen(msg));
194
+ result = rb_str_cat(result, "\n", strlen("\n"));
195
+ }
196
+ return result;
197
+ }
198
+ #else
199
+ static VALUE fb_error_msg(ISC_STATUS *isc_status)
200
+ {
201
+ char msg[1024];
202
+ VALUE result = rb_str_new(NULL, 0);
203
+ while (isc_interprete(msg, &isc_status))
204
+ {
205
+ result = rb_str_cat(result, msg, strlen(msg));
206
+ result = rb_str_cat(result, "\n", strlen("\n"));
207
+ }
208
+ return result;
209
+ }
210
+ #endif
211
+
212
+ struct time_object {
213
+ struct timeval tv;
214
+ struct tm tm;
215
+ int gmt;
216
+ int tm_got;
217
+ };
218
+
219
+ #define GetTimeval(obj, tobj) \
220
+ Data_Get_Struct(obj, struct time_object, tobj)
221
+
222
+ static VALUE fb_mktime(struct tm *tm, const char *which)
223
+ {
224
+ #if defined(_WIN32)
225
+ if (tm->tm_year + 1900 < 1970)
226
+ {
227
+ tm->tm_year = 70;
228
+ tm->tm_mon = 0;
229
+ tm->tm_mday = 1;
230
+ tm->tm_hour = 0;
231
+ tm->tm_min = 0;
232
+ tm->tm_sec = 0;
233
+ }
234
+ #endif
235
+ #if defined(_LP64) || defined(__LP64__) || defined(__arch64__)
236
+ // No need to floor time on 64-bit Unix.
237
+ #else
238
+ if (tm->tm_year + 1900 < 1902)
239
+ {
240
+ tm->tm_year = 2;
241
+ tm->tm_mon = 0;
242
+ tm->tm_mday = 1;
243
+ tm->tm_hour = 0;
244
+ tm->tm_min = 0;
245
+ tm->tm_sec = 0;
246
+ }
247
+ #endif
248
+ return rb_funcall(
249
+ rb_cTime, rb_intern(which), 6,
250
+ INT2FIX(tm->tm_year + 1900), INT2FIX(tm->tm_mon + 1), INT2FIX(tm->tm_mday),
251
+ INT2FIX(tm->tm_hour), INT2FIX(tm->tm_min), INT2FIX(tm->tm_sec));
252
+ }
253
+
254
+ static VALUE fb_mkdate(struct tm *tm)
255
+ {
256
+ return rb_funcall(
257
+ rb_cDate, rb_intern("civil"), 3,
258
+ INT2FIX(1900 + tm->tm_year), INT2FIX(tm->tm_mon + 1), INT2FIX(tm->tm_mday));
259
+ }
260
+
261
+ static int responds_like_date(VALUE obj)
262
+ {
263
+ return rb_respond_to(obj, rb_intern("year")) &&
264
+ rb_respond_to(obj, rb_intern("month")) &&
265
+ rb_respond_to(obj, rb_intern("day"));
266
+ }
267
+ static void tm_from_date(struct tm *tm, VALUE date)
268
+ {
269
+ VALUE year, month, day;
270
+
271
+ if (!responds_like_date(date)) {
272
+ VALUE s = rb_funcall(date, rb_intern("to_s"), 0);
273
+ date = rb_funcall(rb_cDate, rb_intern("parse"), 1, s);
274
+ }
275
+ year = rb_funcall(date, rb_intern("year"), 0);
276
+ month = rb_funcall(date, rb_intern("month"), 0);
277
+ day = rb_funcall(date, rb_intern("day"), 0);
278
+ memset(tm, 0, sizeof(struct tm));
279
+ tm->tm_year = FIX2INT(year) - 1900;
280
+ tm->tm_mon = FIX2INT(month) - 1;
281
+ tm->tm_mday = FIX2INT(day);
282
+ }
283
+
284
+ static void tm_from_timestamp(struct tm *tm, VALUE obj)
285
+ {
286
+ #ifdef TypedData_Get_Struct
287
+ VALUE year, month, day, hour, min, sec;
288
+ #else
289
+ struct time_object *tobj;
290
+ #endif
291
+
292
+ if (!rb_obj_is_kind_of(obj, rb_cTime))
293
+ {
294
+ if (rb_respond_to(obj, rb_intern("to_str")))
295
+ {
296
+ VALUE s = rb_funcall(obj, rb_intern("to_str"), 0);
297
+ obj = rb_funcall(rb_cTime, rb_intern("parse"), 1, s);
298
+ }
299
+ else
300
+ {
301
+ rb_raise(rb_eTypeError, "Expecting Time object or string.");
302
+ }
303
+ }
304
+
305
+ #ifdef TypedData_Get_Struct
306
+ year = rb_funcall(obj, rb_intern("year"), 0);
307
+ month = rb_funcall(obj, rb_intern("month"), 0);
308
+ day = rb_funcall(obj, rb_intern("day"), 0);
309
+ hour = rb_funcall(obj, rb_intern("hour"), 0);
310
+ min = rb_funcall(obj, rb_intern("min"), 0);
311
+ sec = rb_funcall(obj, rb_intern("sec"), 0);
312
+ memset(tm, 0, sizeof(struct tm));
313
+ tm->tm_year = FIX2INT(year) - 1900;
314
+ tm->tm_mon = FIX2INT(month) - 1;
315
+ tm->tm_mday = FIX2INT(day);
316
+ tm->tm_hour = FIX2INT(hour);
317
+ tm->tm_min = FIX2INT(min);
318
+ tm->tm_sec = FIX2INT(sec);
319
+ #else
320
+ GetTimeval(obj, tobj);
321
+ *tm = tobj->tm;
322
+ #endif
323
+ }
324
+
325
+ static VALUE object_to_fixnum(VALUE object)
326
+ {
327
+ if (TYPE(object) != T_FIXNUM && TYPE(object) != T_BIGNUM)
328
+ {
329
+ if (TYPE(object) == T_FLOAT || !strcmp(rb_class2name(CLASS_OF(object)), "BigDecimal"))
330
+ object = rb_funcall(object, rb_intern("round"), 0);
331
+ else if (TYPE(object) == T_STRING)
332
+ object = rb_funcall(rb_funcall(rb_mKernel, rb_intern("BigDecimal"), 1, object), rb_intern("round"), 0);
333
+ else if (!strcmp(rb_class2name(CLASS_OF(object)), "Time"))
334
+ rb_raise(rb_eTypeError, "Time value not allowed as Integer");
335
+ else if (rb_respond_to(object, rb_intern("to_i")))
336
+ object = rb_funcall(object, rb_intern("to_i"), 0);
337
+ else
338
+ rb_raise(rb_eTypeError, "Value doesn't respond to 'to_i' for conversion");
339
+ }
340
+ return (object);
341
+ }
342
+
343
+ static VALUE double_from_obj(VALUE obj)
344
+ {
345
+ if (TYPE(obj) == T_STRING)
346
+ {
347
+ obj = rb_funcall(obj, rb_intern("to_f"), 0);
348
+ }
349
+ return obj;
350
+ }
351
+
352
+ static VALUE fb_sql_type_from_code(int code, int subtype)
353
+ {
354
+ const char *sql_type = NULL;
355
+ switch(code) {
356
+ case SQL_TEXT:
357
+ case blr_text:
358
+ sql_type = "CHAR";
359
+ break;
360
+ case SQL_VARYING:
361
+ case blr_varying:
362
+ sql_type = "VARCHAR";
363
+ break;
364
+ case SQL_SHORT:
365
+ case blr_short:
366
+ switch (subtype) {
367
+ case 0: sql_type = "SMALLINT"; break;
368
+ case 1: sql_type = "NUMERIC"; break;
369
+ case 2: sql_type = "DECIMAL"; break;
370
+ }
371
+ break;
372
+ case SQL_LONG:
373
+ case blr_long:
374
+ switch (subtype) {
375
+ case 0: sql_type = "INTEGER"; break;
376
+ case 1: sql_type = "NUMERIC"; break;
377
+ case 2: sql_type = "DECIMAL"; break;
378
+ }
379
+ break;
380
+ case SQL_FLOAT:
381
+ case blr_float:
382
+ sql_type = "FLOAT";
383
+ break;
384
+ case SQL_DOUBLE:
385
+ case blr_double:
386
+ switch (subtype) {
387
+ case 0: sql_type = "DOUBLE PRECISION"; break;
388
+ case 1: sql_type = "NUMERIC"; break;
389
+ case 2: sql_type = "DECIMAL"; break;
390
+ }
391
+ break;
392
+ case SQL_D_FLOAT:
393
+ case blr_d_float:
394
+ sql_type = "DOUBLE PRECISION";
395
+ break;
396
+ case SQL_TIMESTAMP:
397
+ case blr_timestamp:
398
+ sql_type = "TIMESTAMP";
399
+ break;
400
+ case SQL_BLOB:
401
+ case blr_blob:
402
+ sql_type = "BLOB";
403
+ break;
404
+ case SQL_ARRAY:
405
+ sql_type = "ARRAY";
406
+ break;
407
+ #if (FB_API_VER >= 30)
408
+ case SQL_BOOLEAN:
409
+ case blr_boolean:
410
+ sql_type = "BOOLEAN";
411
+ break;
412
+ #endif
413
+ case SQL_QUAD:
414
+ case blr_quad:
415
+ sql_type = "DECIMAL";
416
+ break;
417
+ case SQL_TYPE_TIME:
418
+ case blr_sql_time:
419
+ sql_type = "TIME";
420
+ break;
421
+ case SQL_TYPE_DATE:
422
+ case blr_sql_date:
423
+ sql_type = "DATE";
424
+ break;
425
+ case SQL_INT64:
426
+ case blr_int64:
427
+ switch (subtype) {
428
+ case 0: sql_type = "BIGINT"; break;
429
+ case 1: sql_type = "NUMERIC"; break;
430
+ case 2: sql_type = "DECIMAL"; break;
431
+ }
432
+ break;
433
+ default:
434
+ printf("Unknown: %d, %d\n", code, subtype);
435
+ sql_type = "UNKNOWN";
436
+ break;
437
+ }
438
+ return rb_str_new2(sql_type);
439
+ }
440
+
441
+ /* call-seq:
442
+ * from_code(code, subtype) -> String
443
+ *
444
+ * Returns the SQL type, such as VARCHAR or INTEGER for a given type code and subtype.
445
+ */
446
+ static VALUE sql_type_from_code(VALUE self, VALUE code, VALUE subtype)
447
+ {
448
+ return fb_sql_type_from_code(NUM2INT(code), NUM2INT(subtype));
449
+ }
450
+
451
+ static void fb_error_check(ISC_STATUS *isc_status)
452
+ {
453
+ if (isc_status[0] == 1 && isc_status[1]) {
454
+ char buf[1024];
455
+ VALUE exc, msg, msg1, msg2;
456
+ short code = isc_sqlcode(isc_status);
457
+
458
+ isc_sql_interprete(code, buf, 1024);
459
+ msg1 = rb_str_new2(buf);
460
+ msg2 = fb_error_msg(isc_status);
461
+ msg = rb_str_cat(msg1, "\n", strlen("\n"));
462
+ msg = rb_str_concat(msg, msg2);
463
+
464
+ exc = rb_exc_new3(rb_eFbError, msg);
465
+ rb_iv_set(exc, "error_code", INT2FIX(code));
466
+ rb_exc_raise(exc);
467
+ }
468
+ }
469
+
470
+ static void fb_error_check_warn(ISC_STATUS *isc_status)
471
+ {
472
+ short code = isc_sqlcode(isc_status);
473
+ if (code != 0) {
474
+ char buf[1024];
475
+ isc_sql_interprete(code, buf, 1024);
476
+ rb_warning("%s(%d)", buf, code);
477
+ }
478
+ }
479
+
480
+ static XSQLDA* sqlda_alloc(long cols)
481
+ {
482
+ XSQLDA *sqlda;
483
+
484
+ sqlda = (XSQLDA*)xmalloc(XSQLDA_LENGTH(cols));
485
+ #ifdef SQLDA_CURRENT_VERSION
486
+ sqlda->version = SQLDA_CURRENT_VERSION;
487
+ #else
488
+ sqlda->version = SQLDA_VERSION1;
489
+ #endif
490
+ sqlda->sqln = cols;
491
+ sqlda->sqld = 0;
492
+ return sqlda;
493
+ }
494
+
495
+ static VALUE cursor_close _((VALUE));
496
+ static VALUE cursor_drop _((VALUE));
497
+ static VALUE cursor_execute _((int, VALUE*, VALUE));
498
+ static VALUE cursor_fetchall _((int, VALUE*, VALUE));
499
+
500
+ static void fb_cursor_mark();
501
+ static void fb_cursor_free();
502
+
503
+ /* connection utilities */
504
+ static void fb_connection_check(struct FbConnection *fb_connection)
505
+ {
506
+ if (fb_connection->db == 0) {
507
+ rb_raise(rb_eFbError, "closed db connection");
508
+ }
509
+ }
510
+
511
+ static void fb_connection_close_cursors(struct FbConnection *fb_connection)
512
+ {
513
+ int i;
514
+ long len = RARRAY_LEN(fb_connection->cursor);
515
+ for (i = 0; i < len; i++) {
516
+ cursor_close(RARRAY_PTR(fb_connection->cursor)[i]);
517
+ }
518
+ }
519
+
520
+ static void fb_connection_drop_cursors(struct FbConnection *fb_connection)
521
+ {
522
+ int i;
523
+ long len = RARRAY_LEN(fb_connection->cursor);
524
+ for (i = 0; i < len; i++) {
525
+ cursor_drop(RARRAY_PTR(fb_connection->cursor)[i]);
526
+ }
527
+ rb_ary_clear(fb_connection->cursor);
528
+ }
529
+
530
+ static void fb_connection_disconnect(struct FbConnection *fb_connection)
531
+ {
532
+ if (fb_connection->transact) {
533
+ isc_commit_transaction(fb_connection->isc_status, &fb_connection->transact);
534
+ fb_error_check(fb_connection->isc_status);
535
+ }
536
+ if (fb_connection->dropped) {
537
+ isc_drop_database(fb_connection->isc_status, &fb_connection->db);
538
+ } else {
539
+ isc_detach_database(fb_connection->isc_status, &fb_connection->db);
540
+ }
541
+ fb_error_check(fb_connection->isc_status);
542
+ }
543
+
544
+ static void fb_connection_disconnect_warn(struct FbConnection *fb_connection)
545
+ {
546
+ if (fb_connection->transact) {
547
+ isc_commit_transaction(fb_connection->isc_status, &fb_connection->transact);
548
+ fb_error_check_warn(fb_connection->isc_status);
549
+ }
550
+ isc_detach_database(fb_connection->isc_status, &fb_connection->db);
551
+ fb_error_check_warn(fb_connection->isc_status);
552
+ }
553
+
554
+ static void fb_connection_mark(struct FbConnection *fb_connection)
555
+ {
556
+ rb_gc_mark(fb_connection->cursor);
557
+ }
558
+
559
+ static void fb_connection_free(struct FbConnection *fb_connection)
560
+ {
561
+ if (fb_connection->db) {
562
+ fb_connection_disconnect_warn(fb_connection);
563
+ }
564
+ xfree(fb_connection);
565
+ }
566
+
567
+ /*
568
+ static struct FbConnection* fb_connection_check_retrieve(VALUE data)
569
+ {
570
+ if (TYPE(data) != T_DATA || RDATA(data)->dfree != (void *)fb_connection_free) {
571
+ rb_raise(rb_eTypeError,
572
+ "Wrong argument type %s (expected Fb::Connection)",
573
+ rb_class2name(CLASS_OF(data)));
574
+ }
575
+ return (struct FbConnection*)RDATA(data)->data;
576
+ }
577
+ */
578
+
579
+ static unsigned short fb_connection_db_SQL_Dialect(struct FbConnection *fb_connection)
580
+ {
581
+ long dialect;
582
+ long length;
583
+ char db_info_command = isc_info_db_sql_dialect;
584
+ char isc_info_buff[16];
585
+
586
+ /* Get the db SQL Dialect */
587
+ isc_database_info(fb_connection->isc_status, &fb_connection->db,
588
+ 1, &db_info_command,
589
+ sizeof(isc_info_buff), isc_info_buff);
590
+ fb_error_check(fb_connection->isc_status);
591
+
592
+ if (isc_info_buff[0] == isc_info_db_sql_dialect) {
593
+ length = isc_vax_integer(&isc_info_buff[1], 2);
594
+ dialect = isc_vax_integer(&isc_info_buff[3], (short)length);
595
+ } else {
596
+ dialect = 1;
597
+ }
598
+ return dialect;
599
+ }
600
+
601
+ static unsigned short fb_connection_dialect(struct FbConnection *fb_connection)
602
+ {
603
+ return fb_connection->dialect;
604
+ }
605
+
606
+ /*
607
+ static unsigned short fb_connection_db_dialect(struct FbConnection *fb_connection)
608
+ {
609
+ return fb_connection->db_dialect;
610
+ }
611
+ */
612
+
613
+ /* Transaction option list */
614
+
615
+ static trans_opts rcom_opt_S[] =
616
+ {
617
+ {"NO", "RECORD_VERSION", isc_tpb_no_rec_version, -1, 0},
618
+ {"RECORD_VERSION", 0, isc_tpb_rec_version, -1, 0},
619
+ {"*", 0, isc_tpb_no_rec_version, -1, 0},
620
+ {0, 0, 0, 0, 0}
621
+ };
622
+
623
+
624
+ static trans_opts read_opt_S[] =
625
+ {
626
+ {"WRITE", 0, isc_tpb_write, 1, 0},
627
+ {"ONLY", 0, isc_tpb_read, 1, 0},
628
+ {"COMMITTED", 0, isc_tpb_read_committed, 2, rcom_opt_S},
629
+ {0, 0, 0, 0, 0}
630
+ };
631
+
632
+
633
+ static trans_opts snap_opt_S[] =
634
+ {
635
+ {"TABLE", "STABILITY", isc_tpb_consistency, 2, 0},
636
+ {"*", 0, isc_tpb_concurrency, 2, 0},
637
+ {0, 0, 0, 0, 0}
638
+ };
639
+
640
+
641
+ static trans_opts isol_opt_S[] =
642
+ {
643
+ {"SNAPSHOT", 0, 0, 0, snap_opt_S},
644
+ {"READ", "COMMITTED", isc_tpb_read_committed, 2, rcom_opt_S},
645
+ {0, 0, 0, 0, 0}
646
+ };
647
+
648
+
649
+ static trans_opts trans_opt_S[] =
650
+ {
651
+ {"READ", 0, 0, 0, read_opt_S},
652
+ {"WAIT", 0, isc_tpb_wait, 3, 0},
653
+ {"NO", "WAIT", isc_tpb_nowait, 3, 0},
654
+ {"ISOLATION", "LEVEL", 0, 0, isol_opt_S},
655
+ {"SNAPSHOT", 0, 0, 0, snap_opt_S},
656
+ {"RESERVING", 0, -1, 0, 0},
657
+ {0, 0, 0, 0, 0}
658
+ };
659
+
660
+ /* Name1 Name2 Option value Position Sub-option */
661
+
662
+ #define RESV_TABLEEND "FOR"
663
+ #define RESV_SHARED "SHARED"
664
+ #define RESV_PROTECTD "PROTECTED"
665
+ #define RESV_READ "READ"
666
+ #define RESV_WRITE "WRITE"
667
+ #define RESV_CONTINUE ','
668
+
669
+ static char* trans_parseopts(VALUE opt, long *tpb_len)
670
+ {
671
+ char *s, *trans;
672
+ long used;
673
+ long size;
674
+ char *tpb;
675
+ trans_opts *curr_p;
676
+ trans_opts *target_p;
677
+ char *check1_p;
678
+ char *check2_p;
679
+ int count;
680
+ int next_c;
681
+ char check_f[4];
682
+ char *resv_p;
683
+ char *resend_p;
684
+ char *tblend_p = 0;
685
+ long tbl_len;
686
+ long res_first;
687
+ int res_count;
688
+ long ofs;
689
+ char sp_prm;
690
+ char rw_prm;
691
+ int cont_f;
692
+ const char *desc = 0;
693
+
694
+ /* Initialize */
695
+ s = StringValuePtr(opt);
696
+ trans = ALLOCA_N(char, strlen(s)+1);
697
+ strcpy(trans, s);
698
+ s = trans;
699
+ while (*s) {
700
+ *s = UPPER(*s);
701
+ s++;
702
+ }
703
+
704
+ used = 0;
705
+ size = 0;
706
+ tpb = NULL;
707
+ memset((void *)check_f, 0, sizeof(check_f));
708
+
709
+ /* Set the default transaction option */
710
+ tpb = (char*)xmalloc(TPBBUFF_ALLOC);
711
+ size = TPBBUFF_ALLOC;
712
+ memcpy((void*)tpb, (void*)isc_tpb_0, sizeof(isc_tpb_0));
713
+ used = sizeof(isc_tpb_0);
714
+
715
+ /* Analize the transaction option strings */
716
+ curr_p = trans_opt_S;
717
+ check1_p = strtok(trans, CMND_DELIMIT);
718
+ if (check1_p) {
719
+ check2_p = strtok(0, CMND_DELIMIT);
720
+ } else {
721
+ check2_p = 0;
722
+ }
723
+ while (curr_p) {
724
+ target_p = 0;
725
+ next_c = 0;
726
+ for (count = 0; curr_p[count].option1; count++) {
727
+ if (!strcmp(curr_p[count].option1, "*")) {
728
+ target_p = &curr_p[count];
729
+ break;
730
+ } else if (check1_p && !strcmp(check1_p, curr_p[count].option1)) {
731
+ if (!curr_p[count].option2) {
732
+ next_c = 1;
733
+ target_p = &curr_p[count];
734
+ break;
735
+ } else if (check2_p && !strcmp(check2_p, curr_p[count].option2)) {
736
+ next_c = 2;
737
+ target_p = &curr_p[count];
738
+ break;
739
+ }
740
+ }
741
+ }
742
+
743
+ if (!target_p) {
744
+ desc = "Illegal transaction option was specified";
745
+ goto error;
746
+ }
747
+
748
+ /* Set the transaction option */
749
+ if (target_p->optval > '\0') {
750
+ if (target_p->position > 0) {
751
+ if (check_f[target_p->position]) {
752
+ desc = "Duplicate transaction option was specified";
753
+ goto error;
754
+ }
755
+ tpb[target_p->position] = target_p->optval;
756
+ check_f[target_p->position] = 1;
757
+ } else {
758
+ if (used + 1 > size) {
759
+ tpb = (char *)realloc(tpb, size + TPBBUFF_ALLOC);
760
+ size += TPBBUFF_ALLOC;
761
+ }
762
+ tpb[used] = target_p->optval;
763
+ used++;
764
+ }
765
+ } else if (target_p->optval) { /* RESERVING ... FOR */
766
+ if (check_f[0]) {
767
+ desc = "Duplicate transaction option was specified";
768
+ goto error;
769
+ }
770
+ resv_p = check2_p;
771
+ if (!resv_p || !strcmp(resv_p, RESV_TABLEEND)) {
772
+ desc = "RESERVING needs table name list";
773
+ goto error;
774
+ }
775
+ while (resv_p) {
776
+ res_first = used;
777
+ res_count = 0;
778
+ resend_p = strtok(0, CMND_DELIMIT);
779
+ while (resend_p) {
780
+ if (!strcmp(resend_p, RESV_TABLEEND)) {
781
+ break;
782
+ }
783
+ resend_p = strtok(0, CMND_DELIMIT);
784
+ }
785
+
786
+ if (!resend_p) {
787
+ desc = "Illegal transaction option was specified";
788
+ goto error;
789
+ }
790
+
791
+ while (resv_p < resend_p) {
792
+ if (*resv_p == '\0' || (ofs = strspn(resv_p, LIST_DELIMIT)) < 0) {
793
+ resv_p++;
794
+ } else {
795
+ resv_p = &resv_p[ofs];
796
+ tblend_p = strpbrk(resv_p, LIST_DELIMIT);
797
+ if (tblend_p) {
798
+ tbl_len = tblend_p - resv_p;
799
+ } else {
800
+ tbl_len = strlen(resv_p);
801
+ }
802
+ if (tbl_len > META_NAME_MAX) {
803
+ desc = "Illegal table name was specified";
804
+ goto error;
805
+ }
806
+
807
+ if (tbl_len > 0) {
808
+ if (used + tbl_len + 3 > size) {
809
+ tpb = (char*)xrealloc(tpb, size+TPBBUFF_ALLOC);
810
+ size += TPBBUFF_ALLOC;
811
+ }
812
+ tpb[used+1] = (char)tbl_len;
813
+ memcpy((void *)&tpb[used+2],resv_p, tbl_len);
814
+ used += tbl_len + 3;
815
+ res_count++;
816
+ }
817
+ resv_p += tbl_len;
818
+ }
819
+ }
820
+
821
+ resv_p = strtok(0, CMND_DELIMIT);
822
+ if (resv_p && !strcmp(resv_p, RESV_SHARED)) {
823
+ sp_prm = isc_tpb_shared;
824
+ } else if (resv_p && !strcmp(resv_p, RESV_PROTECTD)) {
825
+ sp_prm = isc_tpb_protected;
826
+ } else {
827
+ desc = "RESERVING needs {SHARED|PROTECTED} {READ|WRITE}";
828
+ goto error;
829
+ }
830
+
831
+ cont_f = 0;
832
+ resv_p = strtok(0, CMND_DELIMIT);
833
+ if (resv_p) {
834
+ if (resv_p[strlen(resv_p)-1] == RESV_CONTINUE) {
835
+ cont_f = 1;
836
+ resv_p[strlen(resv_p)-1] = '\0';
837
+ } else {
838
+ tblend_p = strpbrk(resv_p, LIST_DELIMIT);
839
+ if (tblend_p) {
840
+ cont_f = 2;
841
+ *tblend_p = '\0';
842
+ }
843
+ }
844
+ }
845
+
846
+ if (resv_p && !strcmp(resv_p, RESV_READ)) {
847
+ rw_prm = isc_tpb_lock_read;
848
+ } else if (resv_p && !strcmp(resv_p, RESV_WRITE)) {
849
+ rw_prm = isc_tpb_lock_write;
850
+ } else {
851
+ desc = "RESERVING needs {SHARED|PROTECTED} {READ|WRITE}";
852
+ goto error;
853
+ }
854
+
855
+ ofs = res_first;
856
+ for (count = 0; count < res_count; count++) {
857
+ tpb[ofs++] = rw_prm;
858
+ ofs += tpb[ofs] + 1;
859
+ tpb[ofs++] = sp_prm;
860
+ }
861
+
862
+ if (cont_f == 1) {
863
+ resv_p = strtok(0, CMND_DELIMIT);
864
+ if (!resv_p) {
865
+ desc = "Unexpected end of command";
866
+ goto error;
867
+ }
868
+ }
869
+ if (cont_f == 2) {
870
+ resv_p = tblend_p + 1;
871
+ } else {
872
+ resv_p = strtok(0, CMND_DELIMIT);
873
+ if (resv_p) {
874
+ if ((int)strlen(resv_p) == 1 && resv_p[0] == RESV_CONTINUE) {
875
+ resv_p = strtok(0, CMND_DELIMIT);
876
+ if (!resv_p) {
877
+ desc = "Unexpected end of command";
878
+ goto error;
879
+ }
880
+ } else if (resv_p[0] == RESV_CONTINUE) {
881
+ resv_p++;
882
+ } else {
883
+ next_c = 1;
884
+ check2_p = resv_p;
885
+ resv_p = 0;
886
+ }
887
+ } else {
888
+ next_c = 0;
889
+ check1_p = check2_p = 0;
890
+ }
891
+ }
892
+ }
893
+
894
+ check_f[0] = 1;
895
+ }
896
+
897
+
898
+ /* Set the next check list */
899
+ curr_p = target_p->sub_opts;
900
+
901
+ for (count = 0; count < next_c; count++) {
902
+ check1_p = check2_p;
903
+ if (check2_p) {
904
+ check2_p = strtok(0, CMND_DELIMIT);
905
+ }
906
+ }
907
+
908
+ if (check1_p && !curr_p) {
909
+ curr_p = trans_opt_S;
910
+ }
911
+ }
912
+
913
+ /* Set the results */
914
+ *tpb_len = used;
915
+ return tpb;
916
+
917
+ error:
918
+ xfree(tpb);
919
+ rb_raise(rb_eFbError, "%s", desc);
920
+ }
921
+
922
+ static void fb_connection_transaction_start(struct FbConnection *fb_connection, VALUE opt)
923
+ {
924
+ char *tpb = 0;
925
+ long tpb_len;
926
+
927
+ if (fb_connection->transact) {
928
+ rb_raise(rb_eFbError, "A transaction has been already started");
929
+ }
930
+
931
+ if (!NIL_P(opt)) {
932
+ tpb = trans_parseopts(opt, &tpb_len);
933
+ } else {
934
+ tpb_len = 0;
935
+ tpb = NULL;
936
+ }
937
+
938
+ isc_start_transaction(fb_connection->isc_status, &fb_connection->transact, 1, &fb_connection->db, tpb_len, tpb);
939
+ xfree(tpb);
940
+ fb_error_check(fb_connection->isc_status);
941
+ }
942
+
943
+ static void fb_connection_commit(struct FbConnection *fb_connection)
944
+ {
945
+ if (fb_connection->transact) {
946
+ fb_connection_close_cursors(fb_connection);
947
+ isc_commit_transaction(fb_connection->isc_status, &fb_connection->transact);
948
+ fb_error_check(fb_connection->isc_status);
949
+ }
950
+ }
951
+
952
+ static void fb_connection_rollback(struct FbConnection *fb_connection)
953
+ {
954
+ if (fb_connection->transact) {
955
+ fb_connection_close_cursors(fb_connection);
956
+ isc_rollback_transaction(fb_connection->isc_status, &fb_connection->transact);
957
+ fb_error_check(fb_connection->isc_status);
958
+ }
959
+ }
960
+
961
+ /* call-seq:
962
+ * transaction(options) -> true
963
+ * transaction(options) { } -> block result
964
+ *
965
+ * Start a transaction for this connection.
966
+ */
967
+ static VALUE connection_transaction(int argc, VALUE *argv, VALUE self)
968
+ {
969
+ struct FbConnection *fb_connection;
970
+ VALUE opt = Qnil;
971
+
972
+ rb_scan_args(argc, argv, "01", &opt);
973
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
974
+
975
+ fb_connection_transaction_start(fb_connection, opt);
976
+
977
+ if (rb_block_given_p()) {
978
+ int state;
979
+ VALUE result = rb_protect(rb_yield, 0, &state);
980
+ if (state) {
981
+ fb_connection_rollback(fb_connection);
982
+ return rb_funcall(rb_mKernel, rb_intern("raise"), 0);
983
+ } else {
984
+ fb_connection_commit(fb_connection);
985
+ return result;
986
+ }
987
+ } else {
988
+ return Qtrue;
989
+ }
990
+ }
991
+
992
+ /* call-seq:
993
+ * transaction_started()? -> true or false
994
+ *
995
+ * Returns true if a transaction is currently active.
996
+ */
997
+ static VALUE connection_transaction_started(VALUE self)
998
+ {
999
+ struct FbConnection *fb_connection;
1000
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
1001
+
1002
+ return fb_connection->transact ? Qtrue : Qfalse;
1003
+ }
1004
+
1005
+ /* call-seq:
1006
+ * commit() -> nil
1007
+ *
1008
+ * Commit the current transaction.
1009
+ */
1010
+ static VALUE connection_commit(VALUE self)
1011
+ {
1012
+ struct FbConnection *fb_connection;
1013
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
1014
+
1015
+ fb_connection_commit(fb_connection);
1016
+ return Qnil;
1017
+ }
1018
+
1019
+ /* call-seq:
1020
+ * rollback() -> nil
1021
+ *
1022
+ * Rollback the current transaction.
1023
+ */
1024
+ static VALUE connection_rollback(VALUE self)
1025
+ {
1026
+ struct FbConnection *fb_connection;
1027
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
1028
+
1029
+ fb_connection_rollback(fb_connection);
1030
+ return Qnil;
1031
+ }
1032
+
1033
+ /*
1034
+ * call-seq:
1035
+ * open?() -> true or false
1036
+ *
1037
+ * Current connection status.
1038
+ */
1039
+
1040
+ static VALUE connection_is_open(VALUE self)
1041
+ {
1042
+ struct FbConnection *fb_connection;
1043
+
1044
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
1045
+ return (fb_connection->db == 0) ? Qfalse : Qtrue;
1046
+ }
1047
+
1048
+ /* call-seq:
1049
+ * to_s() -> String
1050
+ *
1051
+ * Return current database connection string and either (OPEN) or (CLOSED).
1052
+ */
1053
+ static VALUE connection_to_s(VALUE self)
1054
+ {
1055
+ VALUE is_open = connection_is_open(self);
1056
+ VALUE status = (is_open == Qtrue) ? rb_str_new2(" (OPEN)") : rb_str_new2(" (CLOSED)");
1057
+ VALUE database = rb_iv_get(self, "@database");
1058
+ VALUE s = rb_str_dup(database);
1059
+ return rb_str_concat(s, status);
1060
+ }
1061
+
1062
+ /* call-seq:
1063
+ * cursor() -> Cursor
1064
+ *
1065
+ * Creates a +Cursor+ for the +Connection+ and allocates a statement.
1066
+ * This function is no longer published.
1067
+ */
1068
+ static VALUE connection_cursor(VALUE self)
1069
+ {
1070
+ VALUE c;
1071
+ struct FbConnection *fb_connection;
1072
+ struct FbCursor *fb_cursor;
1073
+
1074
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
1075
+ fb_connection_check(fb_connection);
1076
+
1077
+ c = Data_Make_Struct(rb_cFbCursor, struct FbCursor, fb_cursor_mark, fb_cursor_free, fb_cursor);
1078
+ fb_cursor->connection = self;
1079
+ fb_cursor->fields_ary = Qnil;
1080
+ fb_cursor->fields_hash = Qnil;
1081
+ fb_cursor->open = Qfalse;
1082
+ fb_cursor->eof = Qfalse;
1083
+ fb_cursor->stmt = 0;
1084
+ fb_cursor->i_sqlda = sqlda_alloc(SQLDA_COLSINIT);
1085
+ fb_cursor->o_sqlda = sqlda_alloc(SQLDA_COLSINIT);
1086
+ fb_cursor->i_buffer = NULL;
1087
+ fb_cursor->i_buffer_size = 0;
1088
+ fb_cursor->o_buffer = NULL;
1089
+ fb_cursor->o_buffer_size = 0;
1090
+ isc_dsql_alloc_statement2(fb_connection->isc_status, &fb_connection->db, &fb_cursor->stmt);
1091
+ fb_error_check(fb_connection->isc_status);
1092
+
1093
+ return c;
1094
+ }
1095
+
1096
+ /* call-seq:
1097
+ * execute(sql, *args) -> Cursor or rows affected
1098
+ * execute(sql, *args) {|cursor| } -> block result
1099
+ *
1100
+ * Allocates a +Cursor+ and executes the +sql+ statement, matching up the
1101
+ * parameters in +args+ with the place holders in the statement, represented by ?'s.
1102
+ *
1103
+ * If the sql statement returns a result set and a block is provided, the cursor is
1104
+ * yielded to the block before being automatically closed.
1105
+ *
1106
+ * If the sql statement returns a result set and a block is not provided, a +Cursor+
1107
+ * object is returned.
1108
+ *
1109
+ * If the sql statement performs an INSERT, UPDATE or DELETE, the number of rows
1110
+ * affected is returned. Other statements, such as schema updates, return -1.
1111
+ *
1112
+ * +*args+ can be one or more tuples of parameters or an array of tuples (arrays),
1113
+ * in which case the statement will be executed once for each tuple of parameters.
1114
+ *
1115
+ * If no transaction is currently active, a transaction is automatically started
1116
+ * and is closed when the cursor is closed. Note that if additional statements
1117
+ * yielding cursors are started before the first cursor is closed, these cursors will
1118
+ * also be closed when the first one is closed and its transaction committed.
1119
+ */
1120
+ static VALUE connection_execute(int argc, VALUE *argv, VALUE self)
1121
+ {
1122
+ VALUE cursor = connection_cursor(self);
1123
+ VALUE val = cursor_execute(argc, argv, cursor);
1124
+
1125
+ if (NIL_P(val)) {
1126
+ if (rb_block_given_p()) {
1127
+ return rb_ensure(rb_yield,cursor,cursor_close,cursor);
1128
+ } else {
1129
+ HERE("connection_execute Y");
1130
+ return cursor;
1131
+ }
1132
+ } else {
1133
+ cursor_drop(cursor);
1134
+ }
1135
+ return val;
1136
+ }
1137
+
1138
+ /* call-seq:
1139
+ * query(:array, sql, *arg) -> Array of Arrays or nil
1140
+ * query(:hash, sql, *arg) -> Array of Hashes or nil
1141
+ * query(sql, *args) -> Array of Arrays or nil
1142
+ *
1143
+ * For queries returning a result set, an array is returned, containing
1144
+ * either a list of Arrays or Hashes, one for each row.
1145
+ *
1146
+ * If the sql statement performs an INSERT, UPDATE or DELETE, the number of rows
1147
+ * affected is returned. Other statements, such as schema updates, return -1.
1148
+ *
1149
+ * If no transaction is currently active, a transaction is automatically started
1150
+ * and committed. Otherwise, the statement executes within the context of the
1151
+ * current transaction.
1152
+ */
1153
+ static VALUE connection_query(int argc, VALUE *argv, VALUE self)
1154
+ {
1155
+ VALUE format;
1156
+ VALUE cursor;
1157
+ VALUE result;
1158
+
1159
+ if (argc >= 1 && TYPE(argv[0]) == T_SYMBOL) {
1160
+ format = argv[0];
1161
+ argc--; argv++;
1162
+ } else {
1163
+ format = ID2SYM(rb_intern("array"));
1164
+ }
1165
+ cursor = connection_cursor(self);
1166
+ result = cursor_execute(argc, argv, cursor);
1167
+ if (NIL_P(result)) {
1168
+ result = cursor_fetchall(1, &format, cursor);
1169
+ cursor_close(cursor);
1170
+ }
1171
+
1172
+ return result;
1173
+ }
1174
+
1175
+ /* call-seq:
1176
+ * close() -> nil
1177
+ *
1178
+ * Closes connection to database. All open cursors are dropped.
1179
+ */
1180
+ static VALUE connection_close(VALUE self)
1181
+ {
1182
+ struct FbConnection *fb_connection;
1183
+
1184
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
1185
+
1186
+ if (fb_connection->dropped) return Qnil;
1187
+
1188
+ fb_connection_check(fb_connection);
1189
+ fb_connection_disconnect(fb_connection);
1190
+ fb_connection_drop_cursors(fb_connection);
1191
+
1192
+ return Qnil;
1193
+ }
1194
+
1195
+ /* call-seq:
1196
+ * drop() -> nil
1197
+ *
1198
+ * Drops connected database. All open cursors are dropped.
1199
+ */
1200
+ static VALUE connection_drop(VALUE self)
1201
+ {
1202
+ struct FbConnection *fb_connection;
1203
+
1204
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
1205
+ fb_connection->dropped = 1;
1206
+ fb_connection_disconnect(fb_connection);
1207
+ fb_connection_drop_cursors(fb_connection);
1208
+
1209
+ return Qnil;
1210
+ }
1211
+
1212
+ /* call-seq:
1213
+ * dialect() -> int
1214
+ *
1215
+ * Returns dialect of connection.
1216
+ */
1217
+ static VALUE connection_dialect(VALUE self)
1218
+ {
1219
+ struct FbConnection *fb_connection;
1220
+
1221
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
1222
+ fb_connection_check(fb_connection);
1223
+
1224
+ return INT2FIX(fb_connection->dialect);
1225
+ }
1226
+
1227
+ /* call-seq:
1228
+ * db_dialect() -> int
1229
+ *
1230
+ * Returns database dialect.
1231
+ */
1232
+ static VALUE connection_db_dialect(VALUE self)
1233
+ {
1234
+ struct FbConnection *fb_connection;
1235
+
1236
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
1237
+ fb_connection_check(fb_connection);
1238
+
1239
+ return INT2FIX(fb_connection->db_dialect);
1240
+ }
1241
+
1242
+ static void fb_cursor_check(struct FbCursor *fb_cursor)
1243
+ {
1244
+ if (fb_cursor->stmt == 0) {
1245
+ rb_raise(rb_eFbError, "dropped db cursor");
1246
+ }
1247
+ if (!fb_cursor->open) {
1248
+ rb_raise(rb_eFbError, "closed db cursor");
1249
+ }
1250
+ }
1251
+
1252
+ static void fb_cursor_drop(struct FbCursor *fb_cursor)
1253
+ {
1254
+ ISC_STATUS isc_status[20];
1255
+ if (fb_cursor->open) {
1256
+ isc_dsql_free_statement(isc_status, &fb_cursor->stmt, DSQL_close);
1257
+ fb_error_check(isc_status);
1258
+ }
1259
+ isc_dsql_free_statement(isc_status, &fb_cursor->stmt, DSQL_drop);
1260
+ fb_error_check(isc_status);
1261
+ }
1262
+
1263
+ static void fb_cursor_drop_warn(struct FbCursor *fb_cursor)
1264
+ {
1265
+ ISC_STATUS isc_status[20];
1266
+ if (fb_cursor->open) {
1267
+ isc_dsql_free_statement(isc_status, &fb_cursor->stmt, DSQL_close);
1268
+ fb_error_check_warn(isc_status);
1269
+ }
1270
+ isc_dsql_free_statement(isc_status, &fb_cursor->stmt, DSQL_drop);
1271
+ fb_error_check_warn(isc_status);
1272
+ }
1273
+
1274
+ static void fb_cursor_mark(struct FbCursor *fb_cursor)
1275
+ {
1276
+ rb_gc_mark(fb_cursor->connection);
1277
+ rb_gc_mark(fb_cursor->fields_ary);
1278
+ rb_gc_mark(fb_cursor->fields_hash);
1279
+ }
1280
+
1281
+ static void fb_cursor_free(struct FbCursor *fb_cursor)
1282
+ {
1283
+ if (fb_cursor->stmt) {
1284
+ fb_cursor_drop_warn(fb_cursor);
1285
+ }
1286
+ xfree(fb_cursor->i_sqlda);
1287
+ xfree(fb_cursor->o_sqlda);
1288
+ xfree(fb_cursor->i_buffer);
1289
+ xfree(fb_cursor->o_buffer);
1290
+ xfree(fb_cursor);
1291
+ }
1292
+
1293
+ static VALUE sql_decimal_to_bigdecimal(long long sql_data, int scale)
1294
+ {
1295
+ unsigned long i;
1296
+ char bigdecimal_buffer[23];
1297
+ unsigned long bigdecimal_dot;
1298
+ sprintf(bigdecimal_buffer, "%022lld", sql_data);
1299
+ bigdecimal_dot = strlen(bigdecimal_buffer) + scale;
1300
+ for (i = strlen(bigdecimal_buffer); i > bigdecimal_dot; i--)
1301
+ bigdecimal_buffer[i] = bigdecimal_buffer[i-1];
1302
+ bigdecimal_buffer[bigdecimal_dot] = '.';
1303
+ return rb_funcall(rb_cObject, rb_intern("BigDecimal"), 1, rb_str_new2(bigdecimal_buffer));
1304
+ }
1305
+
1306
+ static VALUE object_to_unscaled_bigdecimal(VALUE object, int scale)
1307
+ {
1308
+ int i;
1309
+ long ratio = 1;
1310
+ for (i = 0; i > scale; i--)
1311
+ ratio *= 10;
1312
+ if (TYPE(object) == T_FLOAT)
1313
+ object = rb_funcall(object, rb_intern("to_s"), 0);
1314
+ object = rb_funcall(rb_cObject, rb_intern("BigDecimal"), 1, object);
1315
+ object = rb_funcall(object, rb_intern("*"), 1, LONG2NUM(ratio));
1316
+ return rb_funcall(object, rb_intern("round"), 0);
1317
+ }
1318
+
1319
+ static void fb_cursor_set_inputparams(struct FbCursor *fb_cursor, long argc, VALUE *argv)
1320
+ {
1321
+ struct FbConnection *fb_connection;
1322
+ long count;
1323
+ long offset;
1324
+ /* long type; */
1325
+ short dtp;
1326
+ VALUE obj;
1327
+ long lvalue;
1328
+ ISC_INT64 llvalue;
1329
+ long alignment;
1330
+ double dvalue;
1331
+ double dcheck;
1332
+ VARY *vary;
1333
+ XSQLVAR *var;
1334
+
1335
+ isc_blob_handle blob_handle;
1336
+ ISC_QUAD blob_id;
1337
+ /* static char blob_items[] = { isc_info_blob_max_segment }; */
1338
+ /* char blob_info[16]; */
1339
+ char *p;
1340
+ long length;
1341
+ /* struct time_object *tobj; */
1342
+ struct tm tms;
1343
+
1344
+ Data_Get_Struct(fb_cursor->connection, struct FbConnection, fb_connection);
1345
+
1346
+ /* Check the number of parameters */
1347
+ if (fb_cursor->i_sqlda->sqld != argc) {
1348
+ rb_raise(rb_eFbError, "statement requires %d items; %ld given", fb_cursor->i_sqlda->sqld, argc);
1349
+ }
1350
+
1351
+ /* Get the parameters */
1352
+ for (count = 0,offset = 0; count < argc; count++) {
1353
+ obj = argv[count];
1354
+
1355
+ /* type = TYPE(obj); */
1356
+
1357
+ /* Convert the data type for InterBase */
1358
+ var = &fb_cursor->i_sqlda->sqlvar[count];
1359
+ if (!NIL_P(obj)) {
1360
+ dtp = var->sqltype & ~1; /* Erase null flag */
1361
+ alignment = var->sqllen;
1362
+
1363
+ switch (dtp) {
1364
+ case SQL_TEXT :
1365
+ alignment = 1;
1366
+ offset = FB_ALIGN(offset, alignment);
1367
+ var->sqldata = (char *)(fb_cursor->i_buffer + offset);
1368
+ obj = rb_obj_as_string(obj);
1369
+ if (RSTRING_LEN(obj) > var->sqllen) {
1370
+ rb_raise(rb_eRangeError, "CHAR overflow: %ld bytes exceeds %d byte(s) allowed.",
1371
+ RSTRING_LEN(obj), var->sqllen);
1372
+ }
1373
+ memcpy(var->sqldata, RSTRING_PTR(obj), RSTRING_LEN(obj));
1374
+ var->sqllen = RSTRING_LEN(obj);
1375
+ offset += var->sqllen + 1;
1376
+ break;
1377
+
1378
+ case SQL_VARYING :
1379
+ alignment = sizeof(short);
1380
+ offset = FB_ALIGN(offset, alignment);
1381
+ var->sqldata = (char *)(fb_cursor->i_buffer + offset);
1382
+ vary = (VARY *)var->sqldata;
1383
+ obj = rb_obj_as_string(obj);
1384
+ if (RSTRING_LEN(obj) > var->sqllen) {
1385
+ rb_raise(rb_eRangeError, "VARCHAR overflow: %ld bytes exceeds %d byte(s) allowed.",
1386
+ RSTRING_LEN(obj), var->sqllen);
1387
+ }
1388
+ memcpy(vary->vary_string, RSTRING_PTR(obj), RSTRING_LEN(obj));
1389
+ vary->vary_length = RSTRING_LEN(obj);
1390
+ offset += vary->vary_length + sizeof(short);
1391
+ break;
1392
+
1393
+ case SQL_SHORT :
1394
+ offset = FB_ALIGN(offset, alignment);
1395
+ var->sqldata = (char *)(fb_cursor->i_buffer + offset);
1396
+ if (var->sqlscale < 0) {
1397
+ lvalue = NUM2LONG(object_to_unscaled_bigdecimal(obj, var->sqlscale));
1398
+ } else {
1399
+ lvalue = NUM2LONG(object_to_fixnum(obj));
1400
+ }
1401
+ if (lvalue < -32768 || lvalue > 32767) {
1402
+ rb_raise(rb_eRangeError, "short integer overflow");
1403
+ }
1404
+ *(short *)var->sqldata = lvalue;
1405
+ offset += alignment;
1406
+ break;
1407
+
1408
+ case SQL_LONG :
1409
+ offset = FB_ALIGN(offset, alignment);
1410
+ var->sqldata = (char *)(fb_cursor->i_buffer + offset);
1411
+ if (var->sqlscale < 0) {
1412
+ lvalue = NUM2LONG(object_to_unscaled_bigdecimal(obj, var->sqlscale));
1413
+ } else {
1414
+ lvalue = NUM2LONG(object_to_fixnum(obj));
1415
+ }
1416
+ if (lvalue < -2147483648LL || lvalue > 2147483647LL) {
1417
+ rb_raise(rb_eRangeError, "integer overflow");
1418
+ }
1419
+ *(ISC_LONG *)var->sqldata = (ISC_LONG)lvalue;
1420
+ offset += alignment;
1421
+ break;
1422
+
1423
+ case SQL_FLOAT :
1424
+ offset = FB_ALIGN(offset, alignment);
1425
+ var->sqldata = (char *)(fb_cursor->i_buffer + offset);
1426
+ obj = double_from_obj(obj);
1427
+ dvalue = NUM2DBL(obj);
1428
+ if (dvalue >= 0.0) {
1429
+ dcheck = dvalue;
1430
+ } else {
1431
+ dcheck = dvalue * -1;
1432
+ }
1433
+ if (dcheck != 0.0 && (dcheck < FLT_MIN || dcheck > FLT_MAX)) {
1434
+ rb_raise(rb_eRangeError, "float overflow");
1435
+ }
1436
+ *(float *)var->sqldata = (float)dvalue;
1437
+ offset += alignment;
1438
+ break;
1439
+
1440
+ case SQL_DOUBLE :
1441
+ offset = FB_ALIGN(offset, alignment);
1442
+ var->sqldata = (char *)(fb_cursor->i_buffer + offset);
1443
+ obj = double_from_obj(obj);
1444
+ dvalue = NUM2DBL(obj);
1445
+ *(double *)var->sqldata = dvalue;
1446
+ offset += alignment;
1447
+ break;
1448
+
1449
+ case SQL_INT64 :
1450
+ offset = FB_ALIGN(offset, alignment);
1451
+ var->sqldata = (char *)(fb_cursor->i_buffer + offset);
1452
+
1453
+ if (var->sqlscale < 0) {
1454
+ llvalue = NUM2LL(object_to_unscaled_bigdecimal(obj, var->sqlscale));
1455
+ } else {
1456
+ llvalue = NUM2LL(object_to_fixnum(obj));
1457
+ }
1458
+
1459
+ *(ISC_INT64 *)var->sqldata = llvalue;
1460
+ offset += alignment;
1461
+ break;
1462
+
1463
+ case SQL_BLOB :
1464
+ offset = FB_ALIGN(offset, alignment);
1465
+ var->sqldata = (char *)(fb_cursor->i_buffer + offset);
1466
+ obj = rb_obj_as_string(obj);
1467
+
1468
+ blob_handle = 0;
1469
+ isc_create_blob2(
1470
+ fb_connection->isc_status,&fb_connection->db,&fb_connection->transact,
1471
+ &blob_handle,&blob_id,0,NULL);
1472
+ fb_error_check(fb_connection->isc_status);
1473
+ length = RSTRING_LEN(obj);
1474
+ p = RSTRING_PTR(obj);
1475
+ while (length >= 4096) {
1476
+ isc_put_segment(fb_connection->isc_status,&blob_handle,4096,p);
1477
+ fb_error_check(fb_connection->isc_status);
1478
+ p += 4096;
1479
+ length -= 4096;
1480
+ }
1481
+ if (length) {
1482
+ isc_put_segment(fb_connection->isc_status,&blob_handle,length,p);
1483
+ fb_error_check(fb_connection->isc_status);
1484
+ }
1485
+ isc_close_blob(fb_connection->isc_status,&blob_handle);
1486
+ fb_error_check(fb_connection->isc_status);
1487
+
1488
+ *(ISC_QUAD *)var->sqldata = blob_id;
1489
+ offset += alignment;
1490
+ break;
1491
+
1492
+ case SQL_TIMESTAMP :
1493
+ offset = FB_ALIGN(offset, alignment);
1494
+ var->sqldata = (char *)(fb_cursor->i_buffer + offset);
1495
+ tm_from_timestamp(&tms, obj);
1496
+ isc_encode_timestamp(&tms, (ISC_TIMESTAMP *)var->sqldata);
1497
+ offset += alignment;
1498
+ break;
1499
+
1500
+ case SQL_TYPE_TIME :
1501
+ offset = FB_ALIGN(offset, alignment);
1502
+ var->sqldata = (char *)(fb_cursor->i_buffer + offset);
1503
+ tm_from_timestamp(&tms, obj);
1504
+ isc_encode_sql_time(&tms, (ISC_TIME *)var->sqldata);
1505
+ offset += alignment;
1506
+ break;
1507
+
1508
+ case SQL_TYPE_DATE :
1509
+ offset = FB_ALIGN(offset, alignment);
1510
+ var->sqldata = (char *)(fb_cursor->i_buffer + offset);
1511
+ tm_from_date(&tms, obj);
1512
+ isc_encode_sql_date(&tms, (ISC_DATE *)var->sqldata);
1513
+ offset += alignment;
1514
+ break;
1515
+
1516
+ #if 0
1517
+ case SQL_ARRAY :
1518
+ /* Not supported now
1519
+ offset = FB_ALIGN(offset, alignment);
1520
+ var->sqldata = (char *)(fb_cursor->i_buffer + offset);
1521
+ if (get_arrayvalue(self, type, obj, var))
1522
+ return(STATUS_ABNORMAL);
1523
+ offset += alignment;
1524
+ break;
1525
+ */
1526
+ rb_raise(rb_eArgError, "Arrays not supported");
1527
+ break;
1528
+ #endif
1529
+
1530
+ #if (FB_API_VER >= 30)
1531
+ case SQL_BOOLEAN:
1532
+ offset = FB_ALIGN(offset, alignment);
1533
+ var->sqldata = (char *)(fb_cursor->i_buffer + offset);
1534
+ *(bool *)var->sqldata = obj;
1535
+ offset += alignment;
1536
+ break;
1537
+ #endif
1538
+ default :
1539
+ rb_raise(rb_eFbError, "Specified table includes unsupported datatype (%d)", dtp);
1540
+ }
1541
+
1542
+ if (var->sqltype & 1) {
1543
+ offset = FB_ALIGN(offset, sizeof(short));
1544
+ var->sqlind = (short *)(fb_cursor->i_buffer + offset);
1545
+ *var->sqlind = 0;
1546
+ offset += sizeof(short);
1547
+ }
1548
+ } else if (var->sqltype & 1) {
1549
+ var->sqldata = 0;
1550
+ offset = FB_ALIGN(offset, sizeof(short));
1551
+ var->sqlind = (short *)(fb_cursor->i_buffer + offset);
1552
+ *var->sqlind = -1;
1553
+ offset += sizeof(short);
1554
+ } else {
1555
+ rb_raise(rb_eFbError, "specified column is not permitted to be null");
1556
+ }
1557
+ }
1558
+ }
1559
+
1560
+ static void fb_cursor_execute_withparams(struct FbCursor *fb_cursor, long argc, VALUE *argv)
1561
+ {
1562
+ struct FbConnection *fb_connection;
1563
+
1564
+ Data_Get_Struct(fb_cursor->connection, struct FbConnection, fb_connection);
1565
+ /* Check the first object type of the parameters */
1566
+ if (argc >= 1 && TYPE(argv[0]) == T_ARRAY) {
1567
+ int i;
1568
+ VALUE obj;
1569
+ VALUE ary = argv[0];
1570
+ if (RARRAY_LEN(ary) > 0 && TYPE(RARRAY_PTR(ary)[0]) == T_ARRAY) {
1571
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
1572
+ obj = rb_ary_entry(ary, i);
1573
+ fb_cursor_execute_withparams(fb_cursor, 1, &obj);
1574
+ }
1575
+ } else {
1576
+ for (i = 0; i < argc; i++) {
1577
+ obj = argv[i];
1578
+
1579
+ /* Set the input parameters */
1580
+ Check_Type(obj, T_ARRAY);
1581
+ fb_cursor_set_inputparams(fb_cursor, RARRAY_LEN(obj), RARRAY_PTR(obj));
1582
+
1583
+ /* Execute SQL statement */
1584
+ isc_dsql_execute2(fb_connection->isc_status, &fb_connection->transact, &fb_cursor->stmt, SQLDA_VERSION1, fb_cursor->i_sqlda, NULL);
1585
+ fb_error_check(fb_connection->isc_status);
1586
+ }
1587
+ }
1588
+ } else {
1589
+ /* Set the input parameters */
1590
+ fb_cursor_set_inputparams(fb_cursor, argc, argv);
1591
+
1592
+ /* Execute SQL statement */
1593
+ isc_dsql_execute2(fb_connection->isc_status, &fb_connection->transact, &fb_cursor->stmt, SQLDA_VERSION1, fb_cursor->i_sqlda, NULL);
1594
+ fb_error_check(fb_connection->isc_status);
1595
+ }
1596
+ }
1597
+
1598
+ static VALUE precision_from_sqlvar(XSQLVAR *sqlvar)
1599
+ {
1600
+ switch(sqlvar->sqltype & ~1) {
1601
+ case SQL_TEXT: return Qnil;
1602
+ case SQL_VARYING: return Qnil;
1603
+ case SQL_SHORT:
1604
+ switch (sqlvar->sqlsubtype) {
1605
+ case 0: return INT2FIX(0);
1606
+ case 1: return INT2FIX(4);
1607
+ case 2: return INT2FIX(4);
1608
+ }
1609
+ break;
1610
+ case SQL_LONG:
1611
+ switch (sqlvar->sqlsubtype) {
1612
+ case 0: return INT2FIX(0);
1613
+ case 1: return INT2FIX(9);
1614
+ case 2: return INT2FIX(9);
1615
+ }
1616
+ break;
1617
+ case SQL_FLOAT: return Qnil;
1618
+ case SQL_DOUBLE:
1619
+ case SQL_D_FLOAT:
1620
+ switch (sqlvar->sqlsubtype) {
1621
+ case 0: return Qnil;
1622
+ case 1: return INT2FIX(15);
1623
+ case 2: return INT2FIX(15);
1624
+ }
1625
+ break;
1626
+ case SQL_TIMESTAMP: return Qnil;
1627
+ case SQL_BLOB: return Qnil;
1628
+ case SQL_ARRAY: return Qnil;
1629
+ #if (FB_API_VER >= 30)
1630
+ case SQL_BOOLEAN: return Qnil;
1631
+ #endif
1632
+ case SQL_QUAD: return Qnil;
1633
+ case SQL_TYPE_TIME: return Qnil;
1634
+ case SQL_TYPE_DATE: return Qnil;
1635
+ case SQL_INT64:
1636
+ switch (sqlvar->sqlsubtype) {
1637
+ case 0: return INT2FIX(0);
1638
+ case 1: return INT2FIX(18);
1639
+ case 2: return INT2FIX(18);
1640
+ }
1641
+ break;
1642
+ }
1643
+ return Qnil;
1644
+ }
1645
+
1646
+ static int no_lowercase(VALUE value)
1647
+ {
1648
+ VALUE local_value = StringValue(value);
1649
+ int result = rb_funcall(re_lowercase, id_matches, 1, local_value) == Qnil;
1650
+ return result;
1651
+ }
1652
+
1653
+ static VALUE fb_cursor_fields_ary(XSQLDA *sqlda, short downcase_names)
1654
+ {
1655
+ long cols;
1656
+ long count;
1657
+ XSQLVAR *var;
1658
+ short dtp;
1659
+ VALUE ary;
1660
+
1661
+ cols = sqlda->sqld;
1662
+ if (cols == 0) {
1663
+ return Qnil;
1664
+ }
1665
+
1666
+ ary = rb_ary_new();
1667
+ for (count = 0; count < cols; count++) {
1668
+ VALUE field;
1669
+ VALUE name, type_code, sql_type, sql_subtype, display_size, internal_size, precision, scale, nullable;
1670
+
1671
+ var = &sqlda->sqlvar[count];
1672
+ dtp = var->sqltype & ~1;
1673
+
1674
+ if (var->aliasname_length) { /* aliasname always present? */
1675
+ name = rb_tainted_str_new(var->aliasname, var->aliasname_length);
1676
+ } else {
1677
+ name = rb_tainted_str_new(var->sqlname, var->sqlname_length);
1678
+ }
1679
+ if (downcase_names && no_lowercase(name)) {
1680
+ rb_funcall(name, id_downcase_bang, 0);
1681
+ }
1682
+ rb_str_freeze(name);
1683
+ type_code = INT2NUM((long)(var->sqltype & ~1));
1684
+ sql_type = fb_sql_type_from_code(dtp, var->sqlsubtype);
1685
+ sql_subtype = INT2FIX(var->sqlsubtype);
1686
+ display_size = INT2NUM((long)var->sqllen);
1687
+ if (dtp == SQL_VARYING) {
1688
+ internal_size = INT2NUM((long)var->sqllen + sizeof(short));
1689
+ } else {
1690
+ internal_size = INT2NUM((long)var->sqllen);
1691
+ }
1692
+ precision = precision_from_sqlvar(var);
1693
+ scale = INT2NUM((long)var->sqlscale);
1694
+ nullable = (var->sqltype & 1) ? Qtrue : Qfalse;
1695
+
1696
+ field = rb_struct_new(rb_sFbField, name, sql_type, sql_subtype, display_size, internal_size, precision, scale, nullable, type_code);
1697
+ rb_ary_push(ary, field);
1698
+ }
1699
+ rb_ary_freeze(ary);
1700
+ return ary;
1701
+ }
1702
+
1703
+ static VALUE fb_cursor_fields_hash(VALUE fields_ary)
1704
+ {
1705
+ int i;
1706
+ VALUE hash = rb_hash_new();
1707
+
1708
+ for (i = 0; i < RARRAY_LEN(fields_ary); i++) {
1709
+ VALUE field = rb_ary_entry(fields_ary, i);
1710
+ VALUE name = rb_struct_aref(field, LONG2NUM(0));
1711
+ rb_hash_aset(hash, name, field);
1712
+ }
1713
+
1714
+ return hash;
1715
+ }
1716
+
1717
+ static void fb_cursor_fetch_prep(struct FbCursor *fb_cursor)
1718
+ {
1719
+ struct FbConnection *fb_connection;
1720
+ long cols;
1721
+ long count;
1722
+ XSQLVAR *var;
1723
+ short dtp;
1724
+ long length;
1725
+ long alignment;
1726
+ long offset;
1727
+
1728
+ fb_cursor_check(fb_cursor);
1729
+
1730
+ Data_Get_Struct(fb_cursor->connection, struct FbConnection, fb_connection);
1731
+ fb_connection_check(fb_connection);
1732
+
1733
+ /* Check if open cursor */
1734
+ if (!fb_cursor->open) {
1735
+ rb_raise(rb_eFbError, "The cursor has not been opened. Use execute(query)");
1736
+ }
1737
+ /* Describe output SQLDA */
1738
+ isc_dsql_describe(fb_connection->isc_status, &fb_cursor->stmt, 1, fb_cursor->o_sqlda);
1739
+ fb_error_check(fb_connection->isc_status);
1740
+
1741
+ /* Set the output SQLDA */
1742
+ cols = fb_cursor->o_sqlda->sqld;
1743
+ for (var = fb_cursor->o_sqlda->sqlvar, offset = 0, count = 0; count < cols; var++, count++) {
1744
+ length = alignment = var->sqllen;
1745
+ dtp = var->sqltype & ~1;
1746
+
1747
+ if (dtp == SQL_TEXT) {
1748
+ alignment = 1;
1749
+ } else if (dtp == SQL_VARYING) {
1750
+ length += sizeof(short);
1751
+ alignment = sizeof(short);
1752
+ }
1753
+ offset = FB_ALIGN(offset, alignment);
1754
+ var->sqldata = (char*)(fb_cursor->o_buffer + offset);
1755
+ offset += length;
1756
+ offset = FB_ALIGN(offset, sizeof(short));
1757
+ var->sqlind = (short*)(fb_cursor->o_buffer + offset);
1758
+ offset += sizeof(short);
1759
+ }
1760
+ }
1761
+
1762
+ static VALUE fb_cursor_fetch(struct FbCursor *fb_cursor)
1763
+ {
1764
+ struct FbConnection *fb_connection;
1765
+ long cols;
1766
+ VALUE ary;
1767
+ long count;
1768
+ XSQLVAR *var;
1769
+ long dtp;
1770
+ VALUE val;
1771
+ VARY *vary;
1772
+ struct tm tms;
1773
+
1774
+ isc_blob_handle blob_handle;
1775
+ ISC_QUAD blob_id;
1776
+ unsigned short actual_seg_len;
1777
+ static char blob_items[] = {
1778
+ isc_info_blob_max_segment,
1779
+ isc_info_blob_num_segments,
1780
+ isc_info_blob_total_length
1781
+ };
1782
+ char blob_info[32];
1783
+ char *p, item;
1784
+ short length;
1785
+ unsigned short max_segment = 0;
1786
+ ISC_LONG num_segments = 0;
1787
+ ISC_LONG total_length = 0;
1788
+
1789
+ Data_Get_Struct(fb_cursor->connection, struct FbConnection, fb_connection);
1790
+ fb_connection_check(fb_connection);
1791
+
1792
+ if (fb_cursor->eof) {
1793
+ rb_raise(rb_eFbError, "Cursor is past end of data.");
1794
+ }
1795
+ /* Fetch one row */
1796
+ if (isc_dsql_fetch(fb_connection->isc_status, &fb_cursor->stmt, 1, fb_cursor->o_sqlda) == SQLCODE_NOMORE) {
1797
+ fb_cursor->eof = Qtrue;
1798
+ return Qnil;
1799
+ }
1800
+ fb_error_check(fb_connection->isc_status);
1801
+
1802
+ /* Create the result tuple object */
1803
+ cols = fb_cursor->o_sqlda->sqld;
1804
+ ary = rb_ary_new2(cols);
1805
+
1806
+ /* Create the result objects for each columns */
1807
+ for (count = 0; count < cols; count++) {
1808
+ var = &fb_cursor->o_sqlda->sqlvar[count];
1809
+ dtp = var->sqltype & ~1;
1810
+
1811
+ /* Check if column is null */
1812
+ if ((var->sqltype & 1) && (*var->sqlind < 0)) {
1813
+ val = Qnil;
1814
+ } else {
1815
+ /* Set the column value to the result tuple */
1816
+
1817
+ switch (dtp) {
1818
+ case SQL_TEXT:
1819
+ val = rb_tainted_str_new(var->sqldata, var->sqllen);
1820
+ #if HAVE_RUBY_ENCODING_H
1821
+ rb_funcall(val, id_force_encoding, 1, fb_connection->encoding);
1822
+ #endif
1823
+ break;
1824
+
1825
+ case SQL_VARYING:
1826
+ vary = (VARY*)var->sqldata;
1827
+ val = rb_tainted_str_new(vary->vary_string, vary->vary_length);
1828
+ #if HAVE_RUBY_ENCODING_H
1829
+ rb_funcall(val, id_force_encoding, 1, fb_connection->encoding);
1830
+ #endif
1831
+ break;
1832
+
1833
+ case SQL_SHORT:
1834
+ if (var->sqlscale < 0) {
1835
+ val = sql_decimal_to_bigdecimal((long long)*(ISC_SHORT*)var->sqldata, var->sqlscale);
1836
+ } else {
1837
+ val = INT2NUM((long)*(short*)var->sqldata);
1838
+ }
1839
+ break;
1840
+
1841
+ case SQL_LONG:
1842
+ if (var->sqlscale < 0) {
1843
+ val = sql_decimal_to_bigdecimal((long long)*(ISC_LONG*)var->sqldata, var->sqlscale);
1844
+ } else {
1845
+ val = INT2NUM(*(ISC_LONG*)var->sqldata);
1846
+ }
1847
+ break;
1848
+
1849
+ case SQL_FLOAT:
1850
+ val = rb_float_new((double)*(float*)var->sqldata);
1851
+ break;
1852
+
1853
+ case SQL_DOUBLE:
1854
+ val = rb_float_new(*(double*)var->sqldata);
1855
+ break;
1856
+
1857
+ case SQL_INT64:
1858
+ if (var->sqlscale < 0) {
1859
+ val = sql_decimal_to_bigdecimal(*(ISC_INT64*)var->sqldata, var->sqlscale);
1860
+ } else {
1861
+ val = LL2NUM(*(ISC_INT64*)var->sqldata);
1862
+ }
1863
+ break;
1864
+
1865
+ case SQL_TIMESTAMP:
1866
+ isc_decode_timestamp((ISC_TIMESTAMP *)var->sqldata, &tms);
1867
+ val = fb_mktime(&tms, "local");
1868
+ break;
1869
+
1870
+ case SQL_TYPE_TIME:
1871
+ isc_decode_sql_time((ISC_TIME *)var->sqldata, &tms);
1872
+ tms.tm_year = 100;
1873
+ tms.tm_mon = 0;
1874
+ tms.tm_mday = 1;
1875
+ val = fb_mktime(&tms, "utc");
1876
+ break;
1877
+
1878
+ case SQL_TYPE_DATE:
1879
+ isc_decode_sql_date((ISC_DATE *)var->sqldata, &tms);
1880
+ val = fb_mkdate(&tms);
1881
+ break;
1882
+
1883
+ case SQL_BLOB:
1884
+ blob_handle = 0;
1885
+ blob_id = *(ISC_QUAD *)var->sqldata;
1886
+ isc_open_blob2(fb_connection->isc_status, &fb_connection->db, &fb_connection->transact, &blob_handle, &blob_id, 0, NULL);
1887
+ fb_error_check(fb_connection->isc_status);
1888
+ isc_blob_info(
1889
+ fb_connection->isc_status, &blob_handle,
1890
+ sizeof(blob_items), blob_items,
1891
+ sizeof(blob_info), blob_info);
1892
+ fb_error_check(fb_connection->isc_status);
1893
+ for (p = blob_info; *p != isc_info_end; p += length) {
1894
+ item = *p++;
1895
+ length = (short) isc_vax_integer(p,2);
1896
+ p += 2;
1897
+ switch (item) {
1898
+ case isc_info_blob_max_segment:
1899
+ max_segment = isc_vax_integer(p,length);
1900
+ break;
1901
+ case isc_info_blob_num_segments:
1902
+ num_segments = isc_vax_integer(p,length);
1903
+ break;
1904
+ case isc_info_blob_total_length:
1905
+ total_length = isc_vax_integer(p,length);
1906
+ break;
1907
+ }
1908
+ }
1909
+ val = rb_tainted_str_new(NULL,total_length);
1910
+ for (p = RSTRING_PTR(val); num_segments > 0; num_segments--, p += actual_seg_len) {
1911
+ isc_get_segment(fb_connection->isc_status, &blob_handle, &actual_seg_len, max_segment, p);
1912
+ fb_error_check(fb_connection->isc_status);
1913
+ }
1914
+ #if HAVE_RUBY_ENCODING_H
1915
+ rb_funcall(val, id_force_encoding, 1, fb_connection->encoding);
1916
+ #endif
1917
+ isc_close_blob(fb_connection->isc_status, &blob_handle);
1918
+ fb_error_check(fb_connection->isc_status);
1919
+ break;
1920
+
1921
+ case SQL_ARRAY:
1922
+ rb_warn("ARRAY not supported (yet)");
1923
+ val = Qnil;
1924
+ break;
1925
+
1926
+ #if (FB_API_VER >= 30)
1927
+ case SQL_BOOLEAN:
1928
+ val = (*(bool*)var->sqldata) ? Qtrue : Qfalse;
1929
+ break;
1930
+ #endif
1931
+
1932
+ default:
1933
+ rb_raise(rb_eFbError, "Specified table includes unsupported datatype (%ld)", dtp);
1934
+ break;
1935
+ }
1936
+ }
1937
+ rb_ary_push(ary, val);
1938
+ }
1939
+
1940
+ return ary;
1941
+ }
1942
+
1943
+ static long cursor_rows_affected(struct FbCursor *fb_cursor, long statement_type)
1944
+ {
1945
+ long inserted = 0, selected = 0, updated = 0, deleted = 0;
1946
+ char request[] = { isc_info_sql_records };
1947
+ char response[64], *r;
1948
+ ISC_STATUS isc_status[20];
1949
+
1950
+ isc_dsql_sql_info(isc_status, &fb_cursor->stmt, sizeof(request), request, sizeof(response), response);
1951
+ fb_error_check(isc_status);
1952
+ if (response[0] != isc_info_sql_records) { return -1; }
1953
+
1954
+ r = response + 3; /* skip past first cluster */
1955
+ while (*r != isc_info_end) {
1956
+ char count_type = *r++;
1957
+ short len = isc_vax_integer(r, sizeof(short));
1958
+ r += sizeof(short);
1959
+ switch (count_type) {
1960
+ case isc_info_req_insert_count:
1961
+ inserted = isc_vax_integer(r, len);
1962
+ break;
1963
+ case isc_info_req_select_count:
1964
+ selected = isc_vax_integer(r, len);
1965
+ break;
1966
+ case isc_info_req_update_count:
1967
+ updated = isc_vax_integer(r, len);
1968
+ break;
1969
+ case isc_info_req_delete_count:
1970
+ deleted = isc_vax_integer(r, len);
1971
+ break;
1972
+ }
1973
+ r += len;
1974
+ }
1975
+ switch (statement_type) {
1976
+ case isc_info_sql_stmt_select: return selected;
1977
+ case isc_info_sql_stmt_insert: return inserted;
1978
+ case isc_info_sql_stmt_update: return updated;
1979
+ case isc_info_sql_stmt_delete: return deleted;
1980
+ default: return inserted + selected + updated + deleted;
1981
+ }
1982
+ }
1983
+
1984
+ /* call-seq:
1985
+ * execute2(sql, *args) -> nil or rows affected
1986
+ *
1987
+ * This function is not published.
1988
+ */
1989
+ static VALUE cursor_execute2(VALUE args)
1990
+ {
1991
+ struct FbCursor *fb_cursor;
1992
+ struct FbConnection *fb_connection;
1993
+ char *sql;
1994
+ VALUE rb_sql;
1995
+ long statement;
1996
+ long length;
1997
+ long in_params;
1998
+ long cols;
1999
+ long rows_affected;
2000
+ VALUE result = Qnil;
2001
+ char isc_info_buff[16];
2002
+ char isc_info_stmt[] = { isc_info_sql_stmt_type };
2003
+
2004
+ VALUE self = rb_ary_pop(args);
2005
+ Data_Get_Struct(self, struct FbCursor, fb_cursor);
2006
+ Data_Get_Struct(fb_cursor->connection, struct FbConnection, fb_connection);
2007
+
2008
+ rb_sql = rb_ary_shift(args);
2009
+ sql = StringValuePtr(rb_sql);
2010
+
2011
+ /* Prepare query */
2012
+ isc_dsql_prepare(fb_connection->isc_status, &fb_connection->transact, &fb_cursor->stmt, 0, sql, fb_connection_dialect(fb_connection), fb_cursor->o_sqlda);
2013
+ fb_error_check(fb_connection->isc_status);
2014
+
2015
+ /* Get the statement type */
2016
+ isc_dsql_sql_info(fb_connection->isc_status, &fb_cursor->stmt,
2017
+ sizeof(isc_info_stmt), isc_info_stmt,
2018
+ sizeof(isc_info_buff), isc_info_buff);
2019
+ fb_error_check(fb_connection->isc_status);
2020
+
2021
+ if (isc_info_buff[0] == isc_info_sql_stmt_type) {
2022
+ length = isc_vax_integer(&isc_info_buff[1], 2);
2023
+ statement = isc_vax_integer(&isc_info_buff[3], (short)length);
2024
+ } else {
2025
+ statement = 0;
2026
+ }
2027
+ /* Describe the parameters */
2028
+ isc_dsql_describe_bind(fb_connection->isc_status, &fb_cursor->stmt, 1, fb_cursor->i_sqlda);
2029
+ fb_error_check(fb_connection->isc_status);
2030
+
2031
+ isc_dsql_describe(fb_connection->isc_status, &fb_cursor->stmt, 1, fb_cursor->o_sqlda);
2032
+ fb_error_check(fb_connection->isc_status);
2033
+
2034
+ /* Get the number of parameters and reallocate the SQLDA */
2035
+ in_params = fb_cursor->i_sqlda->sqld;
2036
+ if (fb_cursor->i_sqlda->sqln < in_params) {
2037
+ xfree(fb_cursor->i_sqlda);
2038
+ fb_cursor->i_sqlda = sqlda_alloc(in_params);
2039
+ /* Describe again */
2040
+ isc_dsql_describe_bind(fb_connection->isc_status, &fb_cursor->stmt, 1, fb_cursor->i_sqlda);
2041
+ fb_error_check(fb_connection->isc_status);
2042
+ }
2043
+
2044
+ /* Get the size of parameters buffer and reallocate it */
2045
+ if (in_params) {
2046
+ length = calculate_buffsize(fb_cursor->i_sqlda);
2047
+ if (length > fb_cursor->i_buffer_size) {
2048
+ fb_cursor->i_buffer = xrealloc(fb_cursor->i_buffer, length);
2049
+ fb_cursor->i_buffer_size = length;
2050
+ }
2051
+ }
2052
+
2053
+ /* Execute the SQL statement if it is not query */
2054
+ if (!fb_cursor->o_sqlda->sqld) {
2055
+ if (statement == isc_info_sql_stmt_start_trans) {
2056
+ rb_raise(rb_eFbError, "use Fb::Connection#transaction()");
2057
+ } else if (statement == isc_info_sql_stmt_commit) {
2058
+ rb_raise(rb_eFbError, "use Fb::Connection#commit()");
2059
+ } else if (statement == isc_info_sql_stmt_rollback) {
2060
+ rb_raise(rb_eFbError, "use Fb::Connection#rollback()");
2061
+ } else if (in_params) {
2062
+ fb_cursor_execute_withparams(fb_cursor, RARRAY_LEN(args), RARRAY_PTR(args));
2063
+ } else {
2064
+ isc_dsql_execute2(fb_connection->isc_status, &fb_connection->transact, &fb_cursor->stmt, SQLDA_VERSION1, NULL, NULL);
2065
+ fb_error_check(fb_connection->isc_status);
2066
+ }
2067
+ rows_affected = cursor_rows_affected(fb_cursor, statement);
2068
+ result = INT2NUM(rows_affected);
2069
+ } else {
2070
+ /* Open cursor if the SQL statement is query */
2071
+ /* Get the number of columns and reallocate the SQLDA */
2072
+ cols = fb_cursor->o_sqlda->sqld;
2073
+ if (fb_cursor->o_sqlda->sqln < cols) {
2074
+ xfree(fb_cursor->o_sqlda);
2075
+ fb_cursor->o_sqlda = sqlda_alloc(cols);
2076
+ /* Describe again */
2077
+ isc_dsql_describe(fb_connection->isc_status, &fb_cursor->stmt, 1, fb_cursor->o_sqlda);
2078
+ fb_error_check(fb_connection->isc_status);
2079
+ }
2080
+
2081
+ if (in_params) {
2082
+ fb_cursor_set_inputparams(fb_cursor, RARRAY_LEN(args), RARRAY_PTR(args));
2083
+ }
2084
+
2085
+ /* Open cursor */
2086
+ isc_dsql_execute2(fb_connection->isc_status, &fb_connection->transact, &fb_cursor->stmt, SQLDA_VERSION1, in_params ? fb_cursor->i_sqlda : NULL, NULL);
2087
+ fb_error_check(fb_connection->isc_status);
2088
+ fb_cursor->open = Qtrue;
2089
+
2090
+ /* Get the size of results buffer and reallocate it */
2091
+ length = calculate_buffsize(fb_cursor->o_sqlda);
2092
+ if (length > fb_cursor->o_buffer_size) {
2093
+ fb_cursor->o_buffer = xrealloc(fb_cursor->o_buffer, length);
2094
+ fb_cursor->o_buffer_size = length;
2095
+ }
2096
+
2097
+ /* Set the description attributes */
2098
+ fb_cursor->fields_ary = fb_cursor_fields_ary(fb_cursor->o_sqlda, fb_connection->downcase_names);
2099
+ fb_cursor->fields_hash = fb_cursor_fields_hash(fb_cursor->fields_ary);
2100
+ }
2101
+ return result;
2102
+ }
2103
+
2104
+ /* call-seq:
2105
+ * execute(sql, *args) -> nil or rows affected
2106
+ *
2107
+ * This function is no longer published.
2108
+ */
2109
+ static VALUE cursor_execute(int argc, VALUE* argv, VALUE self)
2110
+ {
2111
+ struct FbCursor *fb_cursor;
2112
+ struct FbConnection *fb_connection;
2113
+ VALUE args;
2114
+
2115
+ if (argc < 1) {
2116
+ rb_raise(rb_eArgError, "At least 1 argument required.");
2117
+ }
2118
+
2119
+ args = rb_ary_new4(argc, argv);
2120
+ rb_ary_push(args, self);
2121
+
2122
+ Data_Get_Struct(self, struct FbCursor, fb_cursor);
2123
+ Data_Get_Struct(fb_cursor->connection, struct FbConnection, fb_connection);
2124
+ fb_connection_check(fb_connection);
2125
+
2126
+ if (fb_cursor->open) {
2127
+ isc_dsql_free_statement(fb_connection->isc_status, &fb_cursor->stmt, DSQL_close);
2128
+ fb_error_check(fb_connection->isc_status);
2129
+ fb_cursor->open = Qfalse;
2130
+ }
2131
+
2132
+ if (!fb_connection->transact) {
2133
+ VALUE result;
2134
+ int state;
2135
+
2136
+ fb_connection_transaction_start(fb_connection, Qnil);
2137
+ fb_cursor->auto_transact = fb_connection->transact;
2138
+
2139
+ result = rb_protect(cursor_execute2, args, &state);
2140
+ if (state) {
2141
+ fb_connection_rollback(fb_connection);
2142
+ return rb_funcall(rb_mKernel, rb_intern("raise"), 0);
2143
+ } else if (result != Qnil) {
2144
+ fb_connection_commit(fb_connection);
2145
+ return result;
2146
+ } else {
2147
+ return result;
2148
+ }
2149
+ } else {
2150
+ return cursor_execute2(args);
2151
+ }
2152
+ }
2153
+
2154
+ static VALUE fb_hash_from_ary(VALUE fields, VALUE row)
2155
+ {
2156
+ VALUE hash = rb_hash_new();
2157
+ int i;
2158
+ for (i = 0; i < RARRAY_LEN(fields); i++) {
2159
+ VALUE field = rb_ary_entry(fields, i);
2160
+ VALUE name = rb_struct_aref(field, LONG2NUM(0));
2161
+ VALUE v = rb_ary_entry(row, i);
2162
+ rb_hash_aset(hash, name, v);
2163
+ }
2164
+ return hash;
2165
+ }
2166
+
2167
+ static int hash_format(int argc, VALUE *argv)
2168
+ {
2169
+ if (argc == 0 || argv[0] == ID2SYM(rb_intern("array"))) {
2170
+ return 0;
2171
+ } else if (argv[0] == ID2SYM(rb_intern("hash"))) {
2172
+ return 1;
2173
+ } else {
2174
+ rb_raise(rb_eFbError, "Unknown format");
2175
+ }
2176
+ }
2177
+
2178
+ /* call-seq:
2179
+ * fetch() -> Array
2180
+ * fetch(:array) -> Array
2181
+ * fetch(:hash) -> Hash
2182
+ *
2183
+ * Reads and returns a single row from the open cursor in either an Array or a Hash,
2184
+ * where the column names or aliases from the query form the keys.
2185
+ * If the +downcase_names+ attribute of the associated connection evaluates to true,
2186
+ * the keys are lower case, except where the column name was mixed case to begin with.
2187
+ */
2188
+ static VALUE cursor_fetch(int argc, VALUE* argv, VALUE self)
2189
+ {
2190
+ VALUE ary;
2191
+ struct FbCursor *fb_cursor;
2192
+
2193
+ int hash_row = hash_format(argc, argv);
2194
+
2195
+ Data_Get_Struct(self, struct FbCursor, fb_cursor);
2196
+ fb_cursor_fetch_prep(fb_cursor);
2197
+
2198
+ ary = fb_cursor_fetch(fb_cursor);
2199
+ if (NIL_P(ary)) return Qnil;
2200
+ return hash_row ? fb_hash_from_ary(fb_cursor->fields_ary, ary) : ary;
2201
+ }
2202
+
2203
+ /* call-seq:
2204
+ * fetchall() -> Array of Arrays
2205
+ * fetchall(:array) -> Array of Arrays
2206
+ * fetchall(:hash) -> Array of Hashes
2207
+ *
2208
+ * Returns the remainder of the rows from the open cursor, with each row represented
2209
+ * by either an Array or a Hash, where the column names or aliases from the query form the keys.
2210
+ * If the +downcase_names+ attribute of the associated connection evaluates to true,
2211
+ * the keys are lower case, except where the column name was mixed case to begin with.
2212
+ */
2213
+ static VALUE cursor_fetchall(int argc, VALUE* argv, VALUE self)
2214
+ {
2215
+ VALUE ary, row;
2216
+ struct FbCursor *fb_cursor;
2217
+
2218
+ int hash_rows = hash_format(argc, argv);
2219
+
2220
+ Data_Get_Struct(self, struct FbCursor, fb_cursor);
2221
+ fb_cursor_fetch_prep(fb_cursor);
2222
+
2223
+ ary = rb_ary_new();
2224
+ for (;;) {
2225
+ row = fb_cursor_fetch(fb_cursor);
2226
+ if (NIL_P(row)) break;
2227
+ if (hash_rows) {
2228
+ rb_ary_push(ary, fb_hash_from_ary(fb_cursor->fields_ary, row));
2229
+ } else {
2230
+ rb_ary_push(ary, row);
2231
+ }
2232
+ }
2233
+
2234
+ return ary;
2235
+ }
2236
+
2237
+ /* call-seq:
2238
+ * each() {|Array| } -> nil
2239
+ * each(:array) {|Array| } -> nil
2240
+ * each(:hash) {|Hash| } -> nil
2241
+ *
2242
+ * Iterates the rows from the open cursor, passing each one to a block in either
2243
+ * an Array or a Hash, where the column names or aliases from the query form the keys.
2244
+ * If the +downcase_names+ attribute of the associated connection evaluates to true,
2245
+ * the keys are lower case, except where the column name was mixed case to begin with.
2246
+ */
2247
+ static VALUE cursor_each(int argc, VALUE* argv, VALUE self)
2248
+ {
2249
+ VALUE row;
2250
+ struct FbCursor *fb_cursor;
2251
+
2252
+ int hash_rows = hash_format(argc, argv);
2253
+
2254
+ Data_Get_Struct(self, struct FbCursor, fb_cursor);
2255
+ fb_cursor_fetch_prep(fb_cursor);
2256
+
2257
+ for (;;) {
2258
+ row = fb_cursor_fetch(fb_cursor);
2259
+ if (NIL_P(row)) break;
2260
+ if (hash_rows) {
2261
+ rb_yield(fb_hash_from_ary(fb_cursor->fields_ary, row));
2262
+ } else {
2263
+ rb_yield(row);
2264
+ }
2265
+ }
2266
+
2267
+ return Qnil;
2268
+ }
2269
+
2270
+ /* call-seq:
2271
+ * close(sql, *args) -> nil
2272
+ *
2273
+ * Closes the cursor. If a transaction was automatically started for this cursor, the transaction is commited.
2274
+ */
2275
+ static VALUE cursor_close(VALUE self)
2276
+ {
2277
+ struct FbCursor *fb_cursor;
2278
+ struct FbConnection *fb_connection;
2279
+
2280
+ Data_Get_Struct(self, struct FbCursor, fb_cursor);
2281
+ Data_Get_Struct(fb_cursor->connection, struct FbConnection, fb_connection);
2282
+ fb_cursor_check(fb_cursor);
2283
+
2284
+ /* Close the cursor */
2285
+ if (fb_cursor->stmt) {
2286
+ isc_dsql_free_statement(fb_connection->isc_status, &fb_cursor->stmt, DSQL_close);
2287
+ fb_error_check_warn(fb_connection->isc_status);
2288
+ isc_dsql_free_statement(fb_connection->isc_status, &fb_cursor->stmt, DSQL_drop);
2289
+ fb_error_check(fb_connection->isc_status);
2290
+ fb_cursor->open = Qfalse;
2291
+ if (fb_connection->transact == fb_cursor->auto_transact) {
2292
+ isc_commit_transaction(fb_connection->isc_status, &fb_connection->transact);
2293
+ fb_cursor->auto_transact = fb_connection->transact;
2294
+ fb_error_check(fb_connection->isc_status);
2295
+ }
2296
+ }
2297
+ fb_cursor->fields_ary = Qnil;
2298
+ fb_cursor->fields_hash = Qnil;
2299
+ return Qnil;
2300
+ }
2301
+
2302
+ /* call-seq:
2303
+ * drop() -> nil
2304
+ *
2305
+ * Drops the cursor.
2306
+ *
2307
+ * TODO: How is this different from close()?
2308
+ */
2309
+ static VALUE cursor_drop(VALUE self)
2310
+ {
2311
+ struct FbCursor *fb_cursor;
2312
+ struct FbConnection *fb_connection;
2313
+ int i;
2314
+
2315
+ Data_Get_Struct(self, struct FbCursor, fb_cursor);
2316
+ fb_cursor_drop(fb_cursor);
2317
+ fb_cursor->fields_ary = Qnil;
2318
+ fb_cursor->fields_hash = Qnil;
2319
+
2320
+ /* reset the reference from connection */
2321
+ Data_Get_Struct(fb_cursor->connection, struct FbConnection, fb_connection);
2322
+ for (i = 0; i < RARRAY_LEN(fb_connection->cursor); i++) {
2323
+ if (RARRAY_PTR(fb_connection->cursor)[i] == self) {
2324
+ RARRAY_PTR(fb_connection->cursor)[i] = Qnil;
2325
+ }
2326
+ }
2327
+
2328
+ return Qnil;
2329
+ }
2330
+
2331
+ /* call-seq:
2332
+ * fields() -> Array
2333
+ * fields(:array) -> Array
2334
+ * fields(:hash) -> Hash
2335
+ *
2336
+ * Return an array of Field Structs or a hash indexed by field name.
2337
+ * If the +downcase_names+ attribute of the associated connection evaluates to true,
2338
+ * the keys are lower case, except where the column name was mixed case to begin with.
2339
+ */
2340
+ static VALUE cursor_fields(int argc, VALUE* argv, VALUE self)
2341
+ {
2342
+ struct FbCursor *fb_cursor;
2343
+
2344
+ Data_Get_Struct(self, struct FbCursor, fb_cursor);
2345
+ if (argc == 0 || argv[0] == ID2SYM(rb_intern("array"))) {
2346
+ return fb_cursor->fields_ary;
2347
+ } else if (argv[0] == ID2SYM(rb_intern("hash"))) {
2348
+ return fb_cursor->fields_hash;
2349
+ } else {
2350
+ rb_raise(rb_eFbError, "Unknown format");
2351
+ }
2352
+ }
2353
+
2354
+ /* call-seq:
2355
+ * error_code -> int
2356
+ *
2357
+ * Returns the sqlcode associated with the error.
2358
+ */
2359
+ static VALUE error_error_code(VALUE error)
2360
+ {
2361
+ rb_p(error);
2362
+ return rb_iv_get(error, "error_code");
2363
+ }
2364
+
2365
+ static char* dbp_create(long *length)
2366
+ {
2367
+ char *dbp = ALLOC_N(char, 1);
2368
+ *dbp = isc_dpb_version1;
2369
+ *length = 1;
2370
+ return dbp;
2371
+ }
2372
+
2373
+ static char* dbp_add_string(char *dbp, char isc_dbp_code, char *s, long *length)
2374
+ {
2375
+ char *buf;
2376
+ long old_length = *length;
2377
+ long s_len = strlen(s);
2378
+ *length += 2 + s_len;
2379
+ REALLOC_N(dbp, char, *length);
2380
+ buf = dbp + old_length;
2381
+ *buf++ = isc_dbp_code;
2382
+ *buf++ = (char)s_len;
2383
+ memcpy(buf, s, s_len);
2384
+ return dbp;
2385
+ }
2386
+
2387
+ static char* connection_create_dbp(VALUE self, long *length)
2388
+ {
2389
+ char *dbp;
2390
+ VALUE username, password, charset, role;
2391
+
2392
+ username = rb_iv_get(self, "@username");
2393
+ Check_Type(username, T_STRING);
2394
+ password = rb_iv_get(self, "@password");
2395
+ Check_Type(password, T_STRING);
2396
+ role = rb_iv_get(self, "@role");
2397
+ charset = rb_iv_get(self, "@charset");
2398
+
2399
+ dbp = dbp_create(length);
2400
+ dbp = dbp_add_string(dbp, isc_dpb_user_name, StringValuePtr(username), length);
2401
+ dbp = dbp_add_string(dbp, isc_dpb_password, StringValuePtr(password), length);
2402
+ if (!NIL_P(charset)) {
2403
+ dbp = dbp_add_string(dbp, isc_dpb_lc_ctype, StringValuePtr(charset), length);
2404
+ }
2405
+ if (!NIL_P(role)) {
2406
+ dbp = dbp_add_string(dbp, isc_dpb_sql_role_name, StringValuePtr(role), length);
2407
+ }
2408
+ return dbp;
2409
+ }
2410
+
2411
+ static const char* CONNECTION_PARMS[] = {
2412
+ "@database",
2413
+ "@username",
2414
+ "@password",
2415
+ "@charset",
2416
+ "@role",
2417
+ "@downcase_names",
2418
+ "@encoding",
2419
+ (char *)0
2420
+ };
2421
+
2422
+ static VALUE connection_create(isc_db_handle handle, VALUE db)
2423
+ {
2424
+ unsigned short dialect;
2425
+ unsigned short db_dialect;
2426
+ VALUE downcase_names;
2427
+ const char *parm;
2428
+ int i;
2429
+ struct FbConnection *fb_connection;
2430
+ VALUE connection = Data_Make_Struct(rb_cFbConnection, struct FbConnection, fb_connection_mark, fb_connection_free, fb_connection);
2431
+ fb_connection->db = handle;
2432
+ fb_connection->transact = 0;
2433
+ fb_connection->cursor = rb_ary_new();
2434
+ dialect = SQL_DIALECT_CURRENT;
2435
+ db_dialect = fb_connection_db_SQL_Dialect(fb_connection);
2436
+
2437
+ if (db_dialect < dialect) {
2438
+ dialect = db_dialect;
2439
+ /* TODO: downgrade warning */
2440
+ }
2441
+
2442
+ fb_connection->dialect = dialect;
2443
+ fb_connection->db_dialect = db_dialect;
2444
+ downcase_names = rb_iv_get(db, "@downcase_names");
2445
+ fb_connection->downcase_names = RTEST(downcase_names);
2446
+ fb_connection->encoding = rb_iv_get(db, "@encoding");
2447
+
2448
+ for (i = 0; (parm = CONNECTION_PARMS[i]); i++) {
2449
+ rb_iv_set(connection, parm, rb_iv_get(db, parm));
2450
+ }
2451
+
2452
+ return connection;
2453
+ }
2454
+
2455
+ static VALUE connection_names(VALUE self, const char *sql)
2456
+ {
2457
+ VALUE row;
2458
+ VALUE query = rb_str_new2(sql);
2459
+ VALUE cursor = connection_execute(1, &query, self);
2460
+ VALUE names = rb_ary_new();
2461
+ struct FbConnection *fb_connection;
2462
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
2463
+
2464
+ while ((row = cursor_fetch(0, NULL, cursor)) != Qnil) {
2465
+ VALUE name = rb_ary_entry(row, 0);
2466
+ if (fb_connection->downcase_names && no_lowercase(name)) {
2467
+ rb_funcall(name, id_downcase_bang, 0);
2468
+ }
2469
+ rb_funcall(name, id_rstrip_bang, 0);
2470
+ rb_ary_push(names, name);
2471
+ }
2472
+
2473
+ cursor_close(cursor);
2474
+ return names;
2475
+ }
2476
+
2477
+ /* call-seq:
2478
+ * table_names() -> array
2479
+ *
2480
+ * Returns sorted array of table names in connected database.
2481
+ */
2482
+ static VALUE connection_table_names(VALUE self)
2483
+ {
2484
+ const char *sql = "SELECT RDB$RELATION_NAME FROM RDB$RELATIONS "
2485
+ "WHERE (RDB$SYSTEM_FLAG <> 1 OR RDB$SYSTEM_FLAG IS NULL) AND RDB$VIEW_BLR IS NULL "
2486
+ "ORDER BY RDB$RELATION_NAME";
2487
+ return connection_names(self, sql);
2488
+ }
2489
+
2490
+ /* call-seq:
2491
+ * generator_names() -> array
2492
+ *
2493
+ * Returns sorted array of generator names in connected database.
2494
+ */
2495
+ static VALUE connection_generator_names(VALUE self)
2496
+ {
2497
+ const char *sql = "SELECT RDB$GENERATOR_NAME FROM RDB$GENERATORS "
2498
+ "WHERE (RDB$SYSTEM_FLAG IS NULL OR RDB$SYSTEM_FLAG <> 1) "
2499
+ "ORDER BY RDB$GENERATOR_NAME";
2500
+ return connection_names(self, sql);
2501
+ }
2502
+
2503
+ /* call-seq:
2504
+ * view_names() -> array
2505
+ *
2506
+ * Returns sorted array of view names in connected database.
2507
+ */
2508
+ static VALUE connection_view_names(VALUE self)
2509
+ {
2510
+ const char *sql = "SELECT RDB$RELATION_NAME, RDB$OWNER_NAME, RDB$VIEW_SOURCE FROM RDB$RELATIONS "
2511
+ "WHERE (RDB$SYSTEM_FLAG <> 1 OR RDB$SYSTEM_FLAG IS NULL) AND NOT RDB$VIEW_BLR IS NULL AND RDB$FLAGS = 1 "
2512
+ "ORDER BY RDB$RELATION_ID";
2513
+ return connection_names(self, sql);
2514
+ }
2515
+
2516
+ /* call-seq:
2517
+ * role_names() -> array
2518
+ *
2519
+ * Returns sorted array of role names in connected database.
2520
+ */
2521
+ static VALUE connection_role_names(VALUE self)
2522
+ {
2523
+ const char *sql = "SELECT * FROM RDB$ROLES WHERE RDB$SYSTEM_FLAG = 0 ORDER BY RDB$ROLE_NAME";
2524
+ return connection_names(self, sql);
2525
+ }
2526
+
2527
+ /* call-seq:
2528
+ * procedure_names() -> array
2529
+ *
2530
+ * Returns sorted array of stored procedure names in connected database.
2531
+ */
2532
+ static VALUE connection_procedure_names(VALUE self)
2533
+ {
2534
+ const char *sql = "SELECT RDB$PROCEDURE_NAME FROM RDB$PROCEDURES "
2535
+ "ORDER BY RDB$PROCEDURE_NAME";
2536
+ return connection_names(self, sql);
2537
+ }
2538
+
2539
+ /* call-seq:
2540
+ * trigger_names() -> array
2541
+ *
2542
+ * Returns sorted array of trigger names in connected database.
2543
+ */
2544
+ static VALUE connection_trigger_names(VALUE self)
2545
+ {
2546
+ const char *sql = "SELECT RDB$TRIGGER_NAME FROM RDB$TRIGGERS "
2547
+ "ORDER BY RDB$TRIGGER_NAME";
2548
+ return connection_names(self, sql);
2549
+ }
2550
+
2551
+ /* call-seq:
2552
+ * columns(table_name) -> array
2553
+ *
2554
+ * Returns array of objects describing each column of table_name.
2555
+ */
2556
+
2557
+ static VALUE connection_columns(VALUE self, VALUE table_name)
2558
+ {
2559
+ int i;
2560
+ struct FbConnection *fb_connection;
2561
+ VALUE re_default = rb_reg_new("^\\s*DEFAULT\\s+", strlen("^\\s*DEFAULT\\s+"), IGNORECASE);
2562
+ VALUE re_rdb = rb_reg_new("^RDB\\$", strlen("^RDB\\$"), 0);
2563
+ VALUE empty = rb_str_new(NULL, 0);
2564
+ VALUE columns = rb_ary_new();
2565
+ const char *sql = "SELECT r.rdb$field_name NAME, r.rdb$field_source, f.rdb$field_type, f.rdb$field_sub_type, "
2566
+ "f.rdb$field_length, f.rdb$field_precision, f.rdb$field_scale SCALE, "
2567
+ "COALESCE(r.rdb$default_source, f.rdb$default_source), "
2568
+ "COALESCE(r.rdb$null_flag, f.rdb$null_flag) "
2569
+ "FROM rdb$relation_fields r "
2570
+ "JOIN rdb$fields f ON r.rdb$field_source = f.rdb$field_name "
2571
+ "WHERE UPPER(r.rdb$relation_name) = ? "
2572
+ "ORDER BY r.rdb$field_position";
2573
+ VALUE query = rb_str_new2(sql);
2574
+ VALUE upcase_table_name = rb_funcall(table_name, rb_intern("upcase"), 0);
2575
+ VALUE query_parms[] = { query, upcase_table_name };
2576
+ VALUE rs = connection_query(2, query_parms, self);
2577
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
2578
+ for (i = 0; i < RARRAY_LEN(rs); i++) {
2579
+ VALUE row = rb_ary_entry(rs, i);
2580
+ VALUE name = rb_ary_entry(row, 0);
2581
+ VALUE domain = rb_ary_entry(row, 1);
2582
+ VALUE sql_type = rb_ary_entry(row, 2);
2583
+ VALUE sql_subtype = rb_ary_entry(row, 3);
2584
+ VALUE length = rb_ary_entry(row, 4);
2585
+ VALUE precision = rb_ary_entry(row, 5);
2586
+ VALUE scale = rb_ary_entry(row, 6);
2587
+ VALUE dflt = rb_ary_entry(row, 7);
2588
+ VALUE not_null = rb_ary_entry(row, 8);
2589
+ VALUE nullable;
2590
+ VALUE column;
2591
+ rb_funcall(name, id_rstrip_bang, 0);
2592
+ rb_funcall(domain, id_rstrip_bang, 0);
2593
+ if (fb_connection->downcase_names && no_lowercase(name)) {
2594
+ rb_funcall(name, id_downcase_bang, 0);
2595
+ }
2596
+ if (rb_funcall(re_rdb, rb_intern("match"), 1, domain) != Qnil) {
2597
+ domain = Qnil;
2598
+ }
2599
+ if (sql_subtype == Qnil) {
2600
+ sql_subtype = INT2NUM(0);
2601
+ }
2602
+ sql_type = sql_type_from_code(self, sql_type, sql_subtype);
2603
+ if (dflt != Qnil) {
2604
+ rb_funcall(dflt, id_sub_bang, 2, re_default, empty);
2605
+ }
2606
+ nullable = RTEST(not_null) ? Qfalse : Qtrue;
2607
+ column = rb_struct_new(rb_sFbColumn, name, domain, sql_type, sql_subtype, length, precision, scale, dflt, nullable);
2608
+ rb_ary_push(columns, column);
2609
+ }
2610
+ rb_ary_freeze(columns);
2611
+ return columns;
2612
+ }
2613
+
2614
+ char *p(char *prompt, VALUE s)
2615
+ {
2616
+ char *sz;
2617
+ if (TYPE(s) != T_STRING) {
2618
+ s = rb_funcall(s, rb_intern("to_s"), 0);
2619
+ }
2620
+ sz = StringValuePtr(s);
2621
+ printf("%s: %s\n", prompt, sz);
2622
+ return sz;
2623
+ }
2624
+
2625
+ static VALUE connection_index_columns(VALUE self, VALUE index_name)
2626
+ {
2627
+ const char *sql_columns = "SELECT * "
2628
+ "FROM RDB$INDEX_SEGMENTS "
2629
+ "WHERE RDB$INDEX_SEGMENTS.RDB$INDEX_NAME = ? "
2630
+ "ORDER BY RDB$INDEX_SEGMENTS.RDB$FIELD_POSITION";
2631
+ VALUE query_columns = rb_str_new2(sql_columns);
2632
+ VALUE query_parms[] = { query_columns, index_name };
2633
+ VALUE result = connection_query(2, query_parms, self);
2634
+ VALUE columns = rb_ary_new();
2635
+ int i;
2636
+ struct FbConnection *fb_connection;
2637
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
2638
+
2639
+ for (i = 0; i < RARRAY_LEN(result); i++) {
2640
+ VALUE row = rb_ary_entry(result, i);
2641
+ VALUE name = rb_ary_entry(row, 1);
2642
+ rb_funcall(name, id_rstrip_bang, 0);
2643
+ if (fb_connection->downcase_names && no_lowercase(name)) {
2644
+ rb_funcall(name, id_downcase_bang, 0);
2645
+ }
2646
+ rb_ary_push(columns, name);
2647
+ }
2648
+ return columns;
2649
+ }
2650
+
2651
+ /* call-seq:
2652
+ * indexes() -> Hash
2653
+ *
2654
+ * Returns a hash of indexes, keyed by index name.
2655
+ */
2656
+ static VALUE connection_indexes(VALUE self)
2657
+ {
2658
+ const char *sql_indexes = "SELECT RDB$INDICES.RDB$RELATION_NAME, RDB$INDICES.RDB$INDEX_NAME, RDB$INDICES.RDB$UNIQUE_FLAG, RDB$INDICES.RDB$INDEX_TYPE "
2659
+ "FROM RDB$INDICES "
2660
+ " JOIN RDB$RELATIONS ON RDB$INDICES.RDB$RELATION_NAME = RDB$RELATIONS.RDB$RELATION_NAME "
2661
+ "WHERE (RDB$RELATIONS.RDB$SYSTEM_FLAG <> 1 OR RDB$RELATIONS.RDB$SYSTEM_FLAG IS NULL) ";
2662
+ VALUE query_indexes = rb_str_new2(sql_indexes);
2663
+ VALUE ary_indexes = connection_query(1, &query_indexes, self);
2664
+ VALUE indexes = rb_hash_new();
2665
+ int i;
2666
+ struct FbConnection *fb_connection;
2667
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
2668
+
2669
+ for (i = 0; i < RARRAY_LEN(ary_indexes); i++) {
2670
+ VALUE index_struct;
2671
+ VALUE row = rb_ary_entry(ary_indexes, i);
2672
+ VALUE table_name = rb_ary_entry(row, 0);
2673
+ VALUE index_name = rb_ary_entry(row, 1);
2674
+ VALUE unique = rb_ary_entry(row, 2);
2675
+ VALUE descending = rb_ary_entry(row, 3);
2676
+ VALUE columns = connection_index_columns(self, index_name);
2677
+
2678
+ rb_funcall(table_name, id_rstrip_bang, 0);
2679
+ rb_funcall(index_name, id_rstrip_bang, 0);
2680
+
2681
+ if (fb_connection->downcase_names) {
2682
+ if (no_lowercase(table_name)) {
2683
+ rb_funcall(table_name, id_downcase_bang, 0);
2684
+ }
2685
+ if (no_lowercase(index_name)) {
2686
+ rb_funcall(index_name, id_downcase_bang, 0);
2687
+ }
2688
+ }
2689
+
2690
+ rb_str_freeze(table_name);
2691
+ rb_str_freeze(index_name);
2692
+
2693
+ unique = (unique == INT2FIX(1)) ? Qtrue : Qfalse;
2694
+ descending = (descending == INT2FIX(1)) ? Qtrue : Qfalse;
2695
+
2696
+ index_struct = rb_struct_new(rb_sFbIndex, table_name, index_name, unique, descending, columns);
2697
+ rb_hash_aset(indexes, index_name, index_struct);
2698
+ }
2699
+ return indexes;
2700
+ }
2701
+
2702
+ /*
2703
+ static void define_attrs(VALUE klass, char **attrs)
2704
+ {
2705
+ char *parm;
2706
+ while ((parm = *attrs))
2707
+ {
2708
+ rb_define_attr(klass, parm+1, 1, 1);
2709
+ attrs++;
2710
+ }
2711
+ }
2712
+ */
2713
+
2714
+ static VALUE default_string(VALUE hash, const char *key, const char *def)
2715
+ {
2716
+ VALUE sym = ID2SYM(rb_intern(key));
2717
+ VALUE val = rb_hash_aref(hash, sym);
2718
+ return NIL_P(val) ? rb_str_new2(def) : StringValue(val);
2719
+ }
2720
+
2721
+ static VALUE default_int(VALUE hash, const char *key, int def)
2722
+ {
2723
+ VALUE sym = ID2SYM(rb_intern(key));
2724
+ VALUE val = rb_hash_aref(hash, sym);
2725
+ return NIL_P(val) ? INT2NUM(def) : val;
2726
+ }
2727
+
2728
+ static VALUE database_allocate_instance(VALUE klass)
2729
+ {
2730
+ NEWOBJ(obj, struct RObject);
2731
+ OBJSETUP((VALUE)obj, klass, T_OBJECT);
2732
+ return (VALUE)obj;
2733
+ }
2734
+
2735
+ static VALUE hash_from_connection_string(VALUE cs)
2736
+ {
2737
+ VALUE hash = rb_hash_new();
2738
+ VALUE re_SemiColon = rb_reg_regcomp(rb_str_new2("\\s*;\\s*"));
2739
+ VALUE re_Equal = rb_reg_regcomp(rb_str_new2("\\s*=\\s*"));
2740
+ ID id_split = rb_intern("split");
2741
+ VALUE pairs = rb_funcall(cs, id_split, 1, re_SemiColon);
2742
+ int i;
2743
+ for (i = 0; i < RARRAY_LEN(pairs); i++) {
2744
+ VALUE pair = rb_ary_entry(pairs, i);
2745
+ VALUE keyValue = rb_funcall(pair, id_split, 1, re_Equal);
2746
+ if (RARRAY_LEN(keyValue) == 2) {
2747
+ VALUE key = rb_ary_entry(keyValue, 0);
2748
+ VALUE val = rb_ary_entry(keyValue, 1);
2749
+ rb_hash_aset(hash, rb_str_intern(key), val);
2750
+ }
2751
+ }
2752
+ return hash;
2753
+ }
2754
+
2755
+ static void check_page_size(int page_size)
2756
+ {
2757
+ if (page_size != 1024 && page_size != 2048 && page_size != 4096 && page_size != 8192 && page_size != 16384) {
2758
+ rb_raise(rb_eFbError, "Invalid page size: %d", page_size);
2759
+ }
2760
+ }
2761
+
2762
+ /* call-seq:
2763
+ * Database.new(options) -> Database
2764
+ *
2765
+ * Initialize Database with Hash of values:
2766
+ * :database:: Full Firebird connection string, e.g. 'localhost:/var/fbdata/drivertest.fdb' (required)
2767
+ * :username:: database username (default: 'sysdba')
2768
+ * :password:: database password (default: 'masterkey')
2769
+ * :charset:: character set to be used with the connection (default: 'NONE')
2770
+ * :role:: database role to connect using (default: nil)
2771
+ * :downcase_names:: Column names are reported in lowercase, unless they were originally mixed case (default: nil).
2772
+ * :page_size:: page size to use when creating a database (default: 4096)
2773
+ */
2774
+ static VALUE database_initialize(int argc, VALUE *argv, VALUE self)
2775
+ {
2776
+ VALUE parms, database;
2777
+
2778
+ if (argc >= 1) {
2779
+ parms = argv[0];
2780
+ if (TYPE(parms) == T_STRING) {
2781
+ parms = hash_from_connection_string(parms);
2782
+ } else {
2783
+ Check_Type(parms, T_HASH);
2784
+ }
2785
+ database = rb_hash_aref(parms, ID2SYM(rb_intern("database")));
2786
+ if (NIL_P(database)) rb_raise(rb_eFbError, "Database must be specified.");
2787
+ rb_iv_set(self, "@database", database);
2788
+ rb_iv_set(self, "@username", default_string(parms, "username", "sysdba"));
2789
+ rb_iv_set(self, "@password", default_string(parms, "password", "masterkey"));
2790
+ rb_iv_set(self, "@charset", default_string(parms, "charset", "NONE"));
2791
+ rb_iv_set(self, "@role", rb_hash_aref(parms, ID2SYM(rb_intern("role"))));
2792
+ rb_iv_set(self, "@downcase_names", rb_hash_aref(parms, ID2SYM(rb_intern("downcase_names"))));
2793
+ rb_iv_set(self, "@encoding", default_string(parms, "encoding", "ASCII-8BIT"));
2794
+ rb_iv_set(self, "@page_size", default_int(parms, "page_size", 4096));
2795
+ }
2796
+ return self;
2797
+ }
2798
+
2799
+ /* call-seq:
2800
+ * create() -> Database
2801
+ * create() {|connection| } -> Database
2802
+ *
2803
+ * Create a database using the current database options.
2804
+ * If a block is provided, an open connection to the new database is passed to it
2805
+ * before being automatically closed.
2806
+ */
2807
+ static VALUE database_create(VALUE self)
2808
+ {
2809
+ ISC_STATUS isc_status[20];
2810
+ isc_db_handle handle = 0;
2811
+ isc_tr_handle local_transact = 0;
2812
+ VALUE parms, fmt, stmt;
2813
+ char *sql;
2814
+
2815
+ VALUE database = rb_iv_get(self, "@database");
2816
+ VALUE username = rb_iv_get(self, "@username");
2817
+ VALUE password = rb_iv_get(self, "@password");
2818
+ VALUE page_size = rb_iv_get(self, "@page_size");
2819
+ VALUE charset = rb_iv_get(self, "@charset");
2820
+
2821
+ check_page_size(NUM2INT(page_size));
2822
+
2823
+ parms = rb_ary_new3(5, database, username, password, page_size, charset);
2824
+
2825
+ fmt = rb_str_new2("CREATE DATABASE '%s' USER '%s' PASSWORD '%s' PAGE_SIZE = %d DEFAULT CHARACTER SET %s;");
2826
+ stmt = rb_funcall(fmt, rb_intern("%"), 1, parms);
2827
+ sql = StringValuePtr(stmt);
2828
+
2829
+ if (isc_dsql_execute_immediate(isc_status, &handle, &local_transact, 0, sql, 3, NULL) != 0) {
2830
+ fb_error_check(isc_status);
2831
+ }
2832
+ if (handle) {
2833
+ if (rb_block_given_p()) {
2834
+ VALUE connection = connection_create(handle, self);
2835
+ rb_ensure(rb_yield,connection,connection_close,connection);
2836
+ } else {
2837
+ isc_detach_database(isc_status, &handle);
2838
+ fb_error_check(isc_status);
2839
+ }
2840
+ }
2841
+
2842
+ return self;
2843
+ }
2844
+
2845
+ /* call-seq:
2846
+ * Database.create(options) -> Database
2847
+ * Database.create(options) {|connection| } -> Database
2848
+ *
2849
+ * Create a database using the specified options (see: Database.new for details of options Hash).
2850
+ * If a block is provided, an open connection to the new database is passed to it
2851
+ * before being automatically closed.
2852
+ */
2853
+ static VALUE database_s_create(int argc, VALUE *argv, VALUE klass)
2854
+ {
2855
+ VALUE obj = database_allocate_instance(klass);
2856
+ database_initialize(argc, argv, obj);
2857
+ return database_create(obj);
2858
+ }
2859
+
2860
+ /* call-seq:
2861
+ * connect() -> Connection
2862
+ * connect() {|connection| } -> nil
2863
+ *
2864
+ * Connect to the database specified by the current database options.
2865
+ *
2866
+ * If a block is provided, the open connection is passed to it before being
2867
+ * automatically closed.
2868
+ */
2869
+ static VALUE database_connect(VALUE self)
2870
+ {
2871
+ ISC_STATUS isc_status[20];
2872
+ char *dbp;
2873
+ long length;
2874
+ isc_db_handle handle = 0;
2875
+ VALUE database = rb_iv_get(self, "@database");
2876
+
2877
+ Check_Type(database, T_STRING);
2878
+ dbp = connection_create_dbp(self, &length);
2879
+ isc_attach_database(isc_status, 0, StringValuePtr(database), &handle, length, dbp);
2880
+ xfree(dbp);
2881
+ fb_error_check(isc_status);
2882
+ {
2883
+ VALUE connection = connection_create(handle, self);
2884
+ if (rb_block_given_p()) {
2885
+ return rb_ensure(rb_yield, connection, connection_close, connection);
2886
+ return Qnil;
2887
+ } else {
2888
+ return connection;
2889
+ }
2890
+ }
2891
+ }
2892
+
2893
+ /* call-seq:
2894
+ * Database.connect(options) -> Connection
2895
+ * Database.connect(options) {|connection| } -> nil
2896
+ *
2897
+ * Connect to a database using the options given (see: Database.new for details of options Hash).
2898
+ *
2899
+ * If a block is provided, the open connection is passed to it before being
2900
+ * automatically closed.
2901
+ */
2902
+ static VALUE database_s_connect(int argc, VALUE *argv, VALUE klass)
2903
+ {
2904
+ VALUE obj = database_allocate_instance(klass);
2905
+ database_initialize(argc, argv, obj);
2906
+ return database_connect(obj);
2907
+ }
2908
+
2909
+ /* call-seq:
2910
+ * drop() -> nil
2911
+ *
2912
+ * Drop the database specified by the current database options.
2913
+ */
2914
+ static VALUE database_drop(VALUE self)
2915
+ {
2916
+ struct FbConnection *fb_connection;
2917
+
2918
+ VALUE connection = database_connect(self);
2919
+ Data_Get_Struct(connection, struct FbConnection, fb_connection);
2920
+ isc_drop_database(fb_connection->isc_status, &fb_connection->db);
2921
+ fb_error_check(fb_connection->isc_status);
2922
+ /* fb_connection_remove(fb_connection); */
2923
+ return Qnil;
2924
+ }
2925
+
2926
+ /* call-seq:
2927
+ * Database.drop(options) -> nil
2928
+ *
2929
+ * Drop the database specified by the options given (see: Database.new for details of options Hash).
2930
+ */
2931
+ static VALUE database_s_drop(int argc, VALUE *argv, VALUE klass)
2932
+ {
2933
+ VALUE obj = database_allocate_instance(klass);
2934
+ database_initialize(argc, argv, obj);
2935
+ return database_drop(obj);
2936
+ }
2937
+
2938
+ void Init_fb_ext()
2939
+ {
2940
+ rb_funcall(rb_mKernel, rb_intern("require"), 1, rb_str_new2("bigdecimal"));
2941
+
2942
+ rb_mFb = rb_define_module("Fb");
2943
+
2944
+ rb_cFbDatabase = rb_define_class_under(rb_mFb, "Database", rb_cData);
2945
+ rb_define_alloc_func(rb_cFbDatabase, database_allocate_instance);
2946
+ rb_define_method(rb_cFbDatabase, "initialize", database_initialize, -1);
2947
+ rb_define_attr(rb_cFbDatabase, "database", 1, 1);
2948
+ rb_define_attr(rb_cFbDatabase, "username", 1, 1);
2949
+ rb_define_attr(rb_cFbDatabase, "password", 1, 1);
2950
+ rb_define_attr(rb_cFbDatabase, "charset", 1, 1);
2951
+ rb_define_attr(rb_cFbDatabase, "role", 1, 1);
2952
+ rb_define_attr(rb_cFbDatabase, "downcase_names", 1, 1);
2953
+ rb_define_attr(rb_cFbDatabase, "encoding", 1, 1);
2954
+ rb_define_attr(rb_cFbDatabase, "page_size", 1, 1);
2955
+ rb_define_method(rb_cFbDatabase, "create", database_create, 0);
2956
+ rb_define_singleton_method(rb_cFbDatabase, "create", database_s_create, -1);
2957
+ rb_define_method(rb_cFbDatabase, "connect", database_connect, 0);
2958
+ rb_define_singleton_method(rb_cFbDatabase, "connect", database_s_connect, -1);
2959
+ rb_define_method(rb_cFbDatabase, "drop", database_drop, 0);
2960
+ rb_define_singleton_method(rb_cFbDatabase, "drop", database_s_drop, -1);
2961
+
2962
+ rb_cFbConnection = rb_define_class_under(rb_mFb, "Connection", rb_cData);
2963
+ rb_define_attr(rb_cFbConnection, "database", 1, 1);
2964
+ rb_define_attr(rb_cFbConnection, "username", 1, 1);
2965
+ rb_define_attr(rb_cFbConnection, "password", 1, 1);
2966
+ rb_define_attr(rb_cFbConnection, "charset", 1, 1);
2967
+ rb_define_attr(rb_cFbConnection, "role", 1, 1);
2968
+ rb_define_attr(rb_cFbConnection, "downcase_names", 1, 1);
2969
+ rb_define_attr(rb_cFbConnection, "encoding", 1, 1);
2970
+ rb_define_method(rb_cFbConnection, "to_s", connection_to_s, 0);
2971
+ rb_define_method(rb_cFbConnection, "execute", connection_execute, -1);
2972
+ rb_define_method(rb_cFbConnection, "query", connection_query, -1);
2973
+ rb_define_method(rb_cFbConnection, "transaction", connection_transaction, -1);
2974
+ rb_define_method(rb_cFbConnection, "transaction_started", connection_transaction_started, 0);
2975
+ rb_define_method(rb_cFbConnection, "commit", connection_commit, 0);
2976
+ rb_define_method(rb_cFbConnection, "rollback", connection_rollback, 0);
2977
+ rb_define_method(rb_cFbConnection, "close", connection_close, 0);
2978
+ rb_define_method(rb_cFbConnection, "drop", connection_drop, 0);
2979
+ rb_define_method(rb_cFbConnection, "open?", connection_is_open, 0);
2980
+ rb_define_method(rb_cFbConnection, "dialect", connection_dialect, 0);
2981
+ rb_define_method(rb_cFbConnection, "db_dialect", connection_db_dialect, 0);
2982
+ rb_define_method(rb_cFbConnection, "table_names", connection_table_names, 0);
2983
+ rb_define_method(rb_cFbConnection, "generator_names", connection_generator_names, 0);
2984
+ rb_define_method(rb_cFbConnection, "view_names", connection_view_names, 0);
2985
+ rb_define_method(rb_cFbConnection, "role_names", connection_role_names, 0);
2986
+ rb_define_method(rb_cFbConnection, "procedure_names", connection_procedure_names, 0);
2987
+ rb_define_method(rb_cFbConnection, "trigger_names", connection_trigger_names, 0);
2988
+ rb_define_method(rb_cFbConnection, "indexes", connection_indexes, 0);
2989
+ rb_define_method(rb_cFbConnection, "columns", connection_columns, 1);
2990
+ /* rb_define_method(rb_cFbConnection, "cursor", connection_cursor, 0); */
2991
+
2992
+ rb_cFbCursor = rb_define_class_under(rb_mFb, "Cursor", rb_cData);
2993
+ /* rb_define_method(rb_cFbCursor, "execute", cursor_execute, -1); */
2994
+ rb_define_method(rb_cFbCursor, "fields", cursor_fields, -1);
2995
+ rb_define_method(rb_cFbCursor, "fetch", cursor_fetch, -1);
2996
+ rb_define_method(rb_cFbCursor, "fetchall", cursor_fetchall, -1);
2997
+ rb_define_method(rb_cFbCursor, "each", cursor_each, -1);
2998
+ rb_define_method(rb_cFbCursor, "close", cursor_close, 0);
2999
+ rb_define_method(rb_cFbCursor, "drop", cursor_drop, 0);
3000
+
3001
+ rb_cFbSqlType = rb_define_class_under(rb_mFb, "SqlType", rb_cData);
3002
+ rb_define_singleton_method(rb_cFbSqlType, "from_code", sql_type_from_code, 2);
3003
+
3004
+ rb_eFbError = rb_define_class_under(rb_mFb, "Error", rb_eStandardError);
3005
+ rb_define_method(rb_eFbError, "error_code", error_error_code, 0);
3006
+
3007
+ rb_sFbField = rb_struct_define("FbField", "name", "sql_type", "sql_subtype", "display_size", "internal_size", "precision", "scale", "nullable", "type_code", NULL);
3008
+ rb_sFbIndex = rb_struct_define("FbIndex", "table_name", "index_name", "unique", "descending", "columns", NULL);
3009
+ rb_sFbColumn = rb_struct_define("FbColumn", "name", "domain", "sql_type", "sql_subtype", "length", "precision", "scale", "default", "nullable", NULL);
3010
+
3011
+ rb_require("date");
3012
+ rb_require("time"); /* Needed as of Ruby 1.8.5 */
3013
+ rb_cDate = rb_const_get(rb_cObject, rb_intern("Date"));
3014
+
3015
+ id_matches = rb_intern("=~");
3016
+ id_downcase_bang = rb_intern("downcase!");
3017
+ re_lowercase = rb_reg_regcomp(rb_str_new2("[[:lower:]]"));
3018
+ rb_global_variable(&re_lowercase);
3019
+ id_rstrip_bang = rb_intern("rstrip!");
3020
+ id_sub_bang = rb_intern("sub!");
3021
+ id_force_encoding = rb_intern("force_encoding");
3022
+ }