firebird 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.gitlab-ci.yml +27 -0
- data/.travis.yml +20 -0
- data/Gemfile +2 -0
- data/README.md +15 -0
- data/Rakefile +13 -0
- data/USAGE.txt +180 -0
- data/ext/fb/extconf.rb +93 -0
- data/ext/fb/fb_ext.c +3022 -0
- data/fb.gemspec +34 -0
- data/lib/fb.rb +23 -0
- data/lib/fb/version.rb +3 -0
- data/mkmf.cmd +1 -0
- data/test/ConnectionTestCases.rb +550 -0
- data/test/CursorTestCases.rb +294 -0
- data/test/DataTypesTestCases.rb +556 -0
- data/test/DatabaseTestCases.rb +183 -0
- data/test/EncodingTestCases.rb +39 -0
- data/test/NumericDataTypesTestCases.rb +588 -0
- data/test/TransactionTestCases.rb +270 -0
- data/test/test_helper.rb +91 -0
- metadata +108 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
data/.gitlab-ci.yml
ADDED
@@ -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
|
data/.travis.yml
ADDED
@@ -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
data/README.md
ADDED
@@ -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
|
data/Rakefile
ADDED
@@ -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
|
data/USAGE.txt
ADDED
@@ -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
|
data/ext/fb/extconf.rb
ADDED
@@ -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")
|
data/ext/fb/fb_ext.c
ADDED
@@ -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
|
+
}
|