dbd-sqlanywhere 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +6 -0
- data/LICENSE +23 -0
- data/README +123 -0
- data/lib/dbd/SQLAnywhere.rb +175 -0
- data/lib/dbd/sqlanywhere/database.rb +253 -0
- data/lib/dbd/sqlanywhere/driver.rb +58 -0
- data/lib/dbd/sqlanywhere/statement.rb +209 -0
- metadata +75 -0
data/CHANGELOG
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
/*====================================================
|
2
|
+
*
|
3
|
+
* Copyright 2008 iAnywhere Solutions, Inc.
|
4
|
+
*
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
* you may not use this file except in compliance with the License.
|
7
|
+
* You may obtain a copy of the License at
|
8
|
+
*
|
9
|
+
*
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
*
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
*
|
16
|
+
* See the License for the specific language governing permissions and
|
17
|
+
* limitations under the License.
|
18
|
+
*
|
19
|
+
* While not a requirement of the license, if you do modify this file, we
|
20
|
+
* would appreciate hearing about it. Please email sqlany_interfaces@sybase.com
|
21
|
+
*
|
22
|
+
*
|
23
|
+
*====================================================*/
|
data/README
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
=SQL Anywhere DBD Driver for Ruby-DBI
|
2
|
+
|
3
|
+
This is a SQL Anywhere driver for Ruby DBI (http://ruby-dbi.rubyforge.org/). This driver requires the
|
4
|
+
native SQL Anywhere Ruby driver. To get the native driver, use:
|
5
|
+
|
6
|
+
gem install sqlanywhere
|
7
|
+
|
8
|
+
This driver is designed for use with DBI 0.4 and greater.
|
9
|
+
|
10
|
+
This driver is licensed under the Apache License, Version 2.
|
11
|
+
|
12
|
+
==Making a Connection
|
13
|
+
|
14
|
+
The following code is a sample database configuration object that connects
|
15
|
+
to a database called 'Test'.
|
16
|
+
|
17
|
+
require 'dbi'
|
18
|
+
|
19
|
+
DBI.connect('DBI:SQLAnywhere:Test') do | dbh |
|
20
|
+
if dbh.ping
|
21
|
+
print "Successfully Connected"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
The connection string takes the general form:
|
26
|
+
|
27
|
+
dbh = DBI.connect('DBI:SQLAnywhere:[DB_NAME]', [USER_NAME], [PASSWORD], [OPTIONS])
|
28
|
+
|
29
|
+
where,
|
30
|
+
[DB_NAME] is the name of the database you wish to connect to
|
31
|
+
[USER_NAME] is the username
|
32
|
+
[PASSWORD] is the correspoding password for the username
|
33
|
+
|
34
|
+
[OPTIONS] is a hash of additional options for the connection.
|
35
|
+
|
36
|
+
For example, to start with named connection called 'ruby_dbi' you can use:
|
37
|
+
|
38
|
+
dbh = DBI.connect('DBI:SQLAnywhere:Test', 'dba', 'sql', {:CON => "ruby_dbi"})
|
39
|
+
|
40
|
+
==Driver-Specific Features
|
41
|
+
|
42
|
+
At the time of this writing, there was no standard way to handle INOUT and OUT parameters
|
43
|
+
from stored procedures in DBI.
|
44
|
+
|
45
|
+
When binding to an OUT parameter, you must bind a hash that contains a key called
|
46
|
+
:name. This :name will be used to retrieve the OUT value after execution. If the
|
47
|
+
OUT column is of string or binary type, you must also pass a key called :length
|
48
|
+
with the expected maximum length of the OUT parameter.
|
49
|
+
|
50
|
+
In the case of an INOUT parameter, the :name and :length keys are the same as for
|
51
|
+
OUT parameters, but an additional :value parameter holds the value to be passed
|
52
|
+
into the stored procedure.
|
53
|
+
|
54
|
+
After execution, you can use the statement-specific function :bound_param
|
55
|
+
to retrieve the output value using the :name supplied at binding time.
|
56
|
+
|
57
|
+
===Example of using OUT and INOUT parameters
|
58
|
+
|
59
|
+
# The result that should be printed to console is:
|
60
|
+
# Complete string is PREFIX-some_string-SUFFIX and is 25 chars long
|
61
|
+
# Complete string is PREFIXPREFIX-some_string-SUFFIXSUFFIX and is 37 chars long
|
62
|
+
|
63
|
+
require 'dbi'
|
64
|
+
|
65
|
+
begin
|
66
|
+
DBI.connect("DBI:SQLAnywhere:test") do |dbh|
|
67
|
+
|
68
|
+
sql = <<SQL
|
69
|
+
IF EXISTS(select * from sysprocedure where proc_name = 'foo') THEN
|
70
|
+
DROP PROCEDURE foo;
|
71
|
+
END IF
|
72
|
+
SQL
|
73
|
+
|
74
|
+
dbh.do(sql);
|
75
|
+
|
76
|
+
sql = <<SQL
|
77
|
+
create procedure foo
|
78
|
+
( IN prefix char(10),
|
79
|
+
INOUT buffer varchar(256),
|
80
|
+
OUT str_len int,
|
81
|
+
IN suffix char(10)
|
82
|
+
)
|
83
|
+
begin
|
84
|
+
set buffer = prefix || buffer || suffix;
|
85
|
+
select length( buffer ) into str_len;
|
86
|
+
end
|
87
|
+
SQL
|
88
|
+
|
89
|
+
dbh.do(sql);
|
90
|
+
|
91
|
+
buffer = "-some_string-"
|
92
|
+
prefix = "PREFIX"
|
93
|
+
|
94
|
+
# preparing the statment
|
95
|
+
sth = dbh.prepare("call foo( ?, ?, ?, ? )")
|
96
|
+
|
97
|
+
# Binding buffer column as :buf, and str_len column as :len
|
98
|
+
# Note that we must supply a :value for :buf since it is an INOUT
|
99
|
+
# And we must supply a :length for :buf since it is a string column
|
100
|
+
sth.execute( prefix, {:name => :buf, :value => buffer, :length => 255}, {:name => :len}, "SUFFIX")
|
101
|
+
|
102
|
+
# Retrieve the OUT value from buffer
|
103
|
+
new_buffer = sth.func(:bound_param, :buf)
|
104
|
+
|
105
|
+
# Retrieve the OUT value from str_len
|
106
|
+
length = sth.func(:bound_param, :len)
|
107
|
+
puts "Complete string is #{new_buffer} and is #{length} chars long"
|
108
|
+
|
109
|
+
# Add the results back into the string, and re-execute
|
110
|
+
sth.execute("PREFIX", {:name => :buf, :value => new_buffer, :length => 255}, {:name => :len}, "SUFFIX")
|
111
|
+
new_buffer = sth.func(:bound_param, :buf)
|
112
|
+
length = sth.func(:bound_param, :len)
|
113
|
+
puts "Complete string is #{new_buffer} and is #{length} chars long"
|
114
|
+
end
|
115
|
+
|
116
|
+
rescue DBI::DatabaseError => e
|
117
|
+
puts "An error occurred"
|
118
|
+
puts "Error code: #{e.err}"
|
119
|
+
puts "Error message: #{e.errstr}"
|
120
|
+
puts "Error SQLSTATE: #{e.state}"
|
121
|
+
ensure
|
122
|
+
DBI.disconnect_all
|
123
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
#====================================================
|
2
|
+
#
|
3
|
+
# Copyright 2008 iAnywhere Solutions, Inc.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
#
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
# While not a requirement of the license, if you do modify this file, we
|
20
|
+
# would appreciate hearing about it. Please email sqlany_interfaces@sybase.com
|
21
|
+
#
|
22
|
+
#
|
23
|
+
#====================================================
|
24
|
+
|
25
|
+
begin
|
26
|
+
require 'rubygems'
|
27
|
+
gem 'sqlanywhere'
|
28
|
+
gem 'dbi'
|
29
|
+
rescue LoadError => e
|
30
|
+
end
|
31
|
+
|
32
|
+
require 'dbi'
|
33
|
+
require 'sqlanywhere'
|
34
|
+
require 'singleton'
|
35
|
+
|
36
|
+
module DBI
|
37
|
+
module DBD
|
38
|
+
module SQLAnywhere
|
39
|
+
|
40
|
+
VERSION = "0.1.0"
|
41
|
+
|
42
|
+
def self.driver_name
|
43
|
+
"SQLAnywhere"
|
44
|
+
end
|
45
|
+
|
46
|
+
class SA
|
47
|
+
include Singleton
|
48
|
+
attr_accessor :api
|
49
|
+
|
50
|
+
def initialize
|
51
|
+
unless defined? SQLAnywhere
|
52
|
+
require 'sqlanywhere'
|
53
|
+
end
|
54
|
+
|
55
|
+
@api = ::SQLAnywhere::SQLAnywhereInterface.new()
|
56
|
+
result = ::SQLAnywhere::API.sqlany_initialize_interface( @api )
|
57
|
+
if result == 0
|
58
|
+
raise LoadError, "Could not load SQLAnywhere DLL"
|
59
|
+
end
|
60
|
+
result = @api.sqlany_init()
|
61
|
+
if result == 0
|
62
|
+
raise LoadError, "Could not initialize SQLAnywhere DLL"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def free_api
|
67
|
+
::SQLAnywhere::API.sqlany_finalize_interface( @api );
|
68
|
+
@api = nil
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
DBI::TypeUtil.register_conversion(driver_name) do |obj|
|
74
|
+
case obj
|
75
|
+
when DBI::Binary # these need to be handled specially by the driver
|
76
|
+
obj.to_s
|
77
|
+
when ::NilClass
|
78
|
+
nil
|
79
|
+
when ::TrueClass
|
80
|
+
1
|
81
|
+
when ::FalseClass
|
82
|
+
0
|
83
|
+
when ::Time
|
84
|
+
obj.strftime("%H:%M:%S")
|
85
|
+
when ::Date
|
86
|
+
obj.strftime("%Y/%m/%d")
|
87
|
+
when ::DateTime, DBI::Timestamp
|
88
|
+
DateTime.parse(obj.to_s).strftime("%Y/%m/%d %H:%M:%S")
|
89
|
+
when ::String
|
90
|
+
obj
|
91
|
+
when ::BigDecimal
|
92
|
+
obj.to_s("F")
|
93
|
+
when ::Numeric
|
94
|
+
obj.to_s
|
95
|
+
else
|
96
|
+
obj
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
# This module provides funcationality that is used by all the DBD classes
|
102
|
+
module Utility
|
103
|
+
|
104
|
+
NO_DIRECTION = 0
|
105
|
+
INPUT_ONLY = 1
|
106
|
+
OUTPUT_ONLY = 2
|
107
|
+
INPUT_OUTPUT = 3
|
108
|
+
|
109
|
+
# do_bind takes the following arguments:
|
110
|
+
# * +prep_stmt+ : a handle the prepared Statement object
|
111
|
+
# * +param+ : the parameter to bound, obtained by sqlany_describe_bind_param
|
112
|
+
# * +bindvar+ : the actual value to bind the the parameter. Can be a +VALUE+, or a +HASH+.
|
113
|
+
# * +i+ : the parameter number to bind. Should be the same as used in sqlany_describe_bind_param
|
114
|
+
# * +bound+ : hash used to track INOUT, and OUT parameters
|
115
|
+
#
|
116
|
+
# +IN+ parameters will be bound once with +INPUT_ONLY+.
|
117
|
+
# +OUT+ parameters will be bound once with +OUTPUT_ONLY+.
|
118
|
+
# +INOUT+ parameters will be be bound twice, once as +INPUT_ONLY+, and once as +OUTPUT_ONLY+. +INOUT+ parameters
|
119
|
+
# will use *different* buffers to pass the input and output values to the DLL.
|
120
|
+
#
|
121
|
+
# If the parameter to be bound is +INPUT_ONLY+, +bindvar+ *must* be a regular value type such as
|
122
|
+
# Bignum, Fixnum, String, etc. This value will be bound to the input parameter
|
123
|
+
#
|
124
|
+
# If the parameter to be bound is +OUTPUT_ONLY+, +bindvar+ *must* be a hash with keys:
|
125
|
+
# ::name => This is the name that you will be used later to retrieve the output value
|
126
|
+
# ::length => If the output will be a string or binary, the expected length must be stated. If this length is exceeded
|
127
|
+
# a DatabaseError (truncation) will be raised.
|
128
|
+
#
|
129
|
+
# If the parameter to be bound is +INPUT_OUTPUT+, +bindvar+ *must* be a hash with keys:
|
130
|
+
# ::name => This is the name that you will be used later to retrieve the output value
|
131
|
+
# ::value => The value to bind to the input.
|
132
|
+
# ::length => If the output will be a string or binary, the expected length must be stated. If this length is exceeded
|
133
|
+
# a DatabaseError (truncation) will be raised.
|
134
|
+
#
|
135
|
+
def do_bind!(prep_stmt, param, bindvar, i, bound)
|
136
|
+
# Get the direction
|
137
|
+
orig_direction = param.get_direction;
|
138
|
+
|
139
|
+
# Bind INPUT
|
140
|
+
if orig_direction == INPUT_ONLY or orig_direction == INPUT_OUTPUT
|
141
|
+
param.set_direction(INPUT_ONLY)
|
142
|
+
# Obtain the value out of the hash if neccessary
|
143
|
+
if bindvar.class == Hash
|
144
|
+
raise DBI::ProgrammingError.new("Parameter hash must contain :value key") if !bindvar.has_key?(:value)
|
145
|
+
param.set_value(bindvar[:value])
|
146
|
+
else
|
147
|
+
param.set_value(bindvar)
|
148
|
+
end
|
149
|
+
raise error() if SA.instance.api.sqlany_bind_param(prep_stmt, i, param) == 0
|
150
|
+
end
|
151
|
+
|
152
|
+
# Bind OUTPUT
|
153
|
+
if orig_direction == OUTPUT_ONLY or orig_direction == INPUT_OUTPUT
|
154
|
+
param.set_direction(OUTPUT_ONLY)
|
155
|
+
# Add the +::name+ to the +bound+ hash so its output value can be retrieved later
|
156
|
+
raise DBI::ProgrammingError.new("Parameter hash must contain :name key") if !bindvar.has_key?(:name)
|
157
|
+
bound[bindvar[:name]] = i if !bound.nil?
|
158
|
+
# set the buffer length if appropriate
|
159
|
+
if bindvar.has_key?(:length)
|
160
|
+
param.set_buffer_size(bindvar[:length])
|
161
|
+
end
|
162
|
+
# +set_value+ sets up the receiveing buffer
|
163
|
+
param.set_value(nil)
|
164
|
+
raise error() if SA.instance.api.sqlany_bind_param(prep_stmt, i, param) == 0
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
end # module SQLAnywhere
|
170
|
+
end # module DBD
|
171
|
+
end # module DBI
|
172
|
+
|
173
|
+
require 'dbd/SQLAnywhere/driver'
|
174
|
+
require 'dbd/SQLAnywhere/database'
|
175
|
+
require 'dbd/SQLAnywhere/statement'
|
@@ -0,0 +1,253 @@
|
|
1
|
+
#====================================================
|
2
|
+
#
|
3
|
+
# Copyright 2008 iAnywhere Solutions, Inc.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
#
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
# While not a requirement of the license, if you do modify this file, we
|
20
|
+
# would appreciate hearing about it. Please email sqlany_interfaces@sybase.com
|
21
|
+
#
|
22
|
+
#
|
23
|
+
#====================================================
|
24
|
+
|
25
|
+
module DBI::DBD::SQLAnywhere
|
26
|
+
class Database < DBI::BaseDatabase
|
27
|
+
include Utility
|
28
|
+
|
29
|
+
COLUMN_SELECT_STRING = <<END_OF_STATEMENT
|
30
|
+
SELECT systabcol.column_name,
|
31
|
+
sysidxcol.sequence,
|
32
|
+
systabcol."nulls",
|
33
|
+
systabcol."default",
|
34
|
+
systabcol.scale,
|
35
|
+
systabcol.width,
|
36
|
+
sysdomain.type_id,
|
37
|
+
sysdomain.domain_name,
|
38
|
+
sysidx."unique"
|
39
|
+
FROM (systable join systabcol join sysdomain) left outer join (sysidxcol join sysidx)
|
40
|
+
WHERE table_name = ?
|
41
|
+
END_OF_STATEMENT
|
42
|
+
|
43
|
+
TABLE_SELECT_STRING = <<END_OF_STATEMENT
|
44
|
+
SELECT table_name
|
45
|
+
FROM sysuser, systab
|
46
|
+
WHERE user_name not in ('SYS', 'DBO', 'rs_systabgroup')
|
47
|
+
AND sysuser.user_id = systab.creator
|
48
|
+
END_OF_STATEMENT
|
49
|
+
|
50
|
+
SQLANY_to_DBI = {
|
51
|
+
5 => DBI::SQL_SMALLINT,
|
52
|
+
4 => DBI::SQL_INTEGER,
|
53
|
+
2 => DBI::SQL_NUMERIC,
|
54
|
+
7 => DBI::SQL_FLOAT,
|
55
|
+
8 => DBI::SQL_DOUBLE,
|
56
|
+
9 => DBI::SQL_DATE,
|
57
|
+
1 => DBI::SQL_CHAR,
|
58
|
+
12 => DBI::SQL_VARCHAR,
|
59
|
+
-1 => DBI::SQL_LONGVARCHAR,
|
60
|
+
-2 => DBI::SQL_BINARY,
|
61
|
+
-4 => DBI::SQL_LONGVARBINARY,
|
62
|
+
11 => DBI::SQL_TIMESTAMP,
|
63
|
+
10 => DBI::SQL_TIME,
|
64
|
+
-6 => DBI::SQL_TINYINT,
|
65
|
+
-5 => DBI::SQL_BIGINT,
|
66
|
+
-9 => DBI::SQL_INTEGER,
|
67
|
+
-10 => DBI::SQL_SMALLINT,
|
68
|
+
-11 => DBI::SQL_BIGINT,
|
69
|
+
-7 => DBI::SQL_BIT,
|
70
|
+
2 => DBI::SQL_DECIMAL,
|
71
|
+
-2 => DBI::SQL_VARBINARY,
|
72
|
+
-15 => DBI::SQL_BINARY,
|
73
|
+
-16 => DBI::SQL_VARBINARY,
|
74
|
+
-17 => DBI::SQL_LONGVARBINARY,
|
75
|
+
-18 => DBI::SQL_LONGVARCHAR,
|
76
|
+
-12 => DBI::SQL_CHAR,
|
77
|
+
-13 => DBI::SQL_VARCHAR,
|
78
|
+
-14 => DBI::SQL_LONGVARCHAR,
|
79
|
+
}
|
80
|
+
|
81
|
+
|
82
|
+
def disconnect
|
83
|
+
SA.instance.api.sqlany_rollback(@handle)
|
84
|
+
SA.instance.api.sqlany_disconnect(@handle)
|
85
|
+
SA.instance.api.sqlany_free_connection(@handle)
|
86
|
+
end
|
87
|
+
|
88
|
+
def prepare( statement )
|
89
|
+
stmt = SA.instance.api.sqlany_prepare(@handle, statement)
|
90
|
+
if stmt.nil?
|
91
|
+
raise error()
|
92
|
+
else
|
93
|
+
return Statement.new(stmt, @handle)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def ping
|
98
|
+
res = SA.instance.api.sqlany_execute_immediate(@handle, 'SELECT * FROM dummy')
|
99
|
+
raise error() if res == 0
|
100
|
+
return res
|
101
|
+
end
|
102
|
+
|
103
|
+
def commit
|
104
|
+
res = SA.instance.api.sqlany_commit(@handle)
|
105
|
+
raise error() if res == 0
|
106
|
+
return res
|
107
|
+
end
|
108
|
+
|
109
|
+
def rollback
|
110
|
+
res = SA.instance.api.sqlany_rollback(@handle)
|
111
|
+
raise error() if res == 0
|
112
|
+
return res
|
113
|
+
end
|
114
|
+
|
115
|
+
def quote(value)
|
116
|
+
value.gsub("'", "''")
|
117
|
+
end
|
118
|
+
|
119
|
+
def execute(sql, *bindvars)
|
120
|
+
bound = {}
|
121
|
+
prep_stmt = SA.instance.api.sqlany_prepare(@handle, sql);
|
122
|
+
raise error() if prep_stmt.nil?
|
123
|
+
num_params = SA.instance.api.sqlany_num_params(prep_stmt)
|
124
|
+
raise error("Wrong number of parameters. Supplied #{bindvars.length} but expecting #{num_params}") if (num_params != bindvars.length)
|
125
|
+
num_params.times do |i|
|
126
|
+
res, param = SA.instance.api.sqlany_describe_bind_param(prep_stmt, i)
|
127
|
+
raise error() if res == 0 or param.nil?
|
128
|
+
do_bind!(prep_stmt, param, bindvars[i], i, bound)
|
129
|
+
param.finish
|
130
|
+
end
|
131
|
+
|
132
|
+
res = SA.instance.api.sqlany_execute(prep_stmt)
|
133
|
+
raise error() if res == 0
|
134
|
+
return Statement.new(prep_stmt, @handle, bound)
|
135
|
+
end
|
136
|
+
|
137
|
+
def do(sql, *bindvars)
|
138
|
+
prep_stmt = SA.instance.api.sqlany_prepare(@handle, sql);
|
139
|
+
raise error() if prep_stmt.nil?
|
140
|
+
num_params = SA.instance.api.sqlany_num_params(prep_stmt)
|
141
|
+
raise error("Wrong number of parameters. Supplied #{bindvars.length} but expecting #{num_params}") if (num_params != bindvars.length)
|
142
|
+
num_params.times do |i|
|
143
|
+
res, param = SA.instance.api.sqlany_describe_bind_param(prep_stmt, i)
|
144
|
+
raise error() if res == 0 or param.nil?
|
145
|
+
do_bind!(prep_stmt, param, bindvars[i], i, nil)
|
146
|
+
param.finish
|
147
|
+
end
|
148
|
+
res = SA.instance.api.sqlany_execute(prep_stmt)
|
149
|
+
raise error() if res == 0
|
150
|
+
affected_rows = SA.instance.api.sqlany_affected_rows(prep_stmt)
|
151
|
+
return affected_rows
|
152
|
+
end
|
153
|
+
|
154
|
+
def tables
|
155
|
+
rs = SA.instance.api.sqlany_execute_direct(@handle, TABLE_SELECT_STRING)
|
156
|
+
if rs.nil?
|
157
|
+
return nil
|
158
|
+
else
|
159
|
+
tables = []
|
160
|
+
while (SA.instance.api.sqlany_fetch_next(rs) == 1)
|
161
|
+
res, cols = SA.instance.api.sqlany_get_column(rs, 0)
|
162
|
+
raise error() if res == 0 or cols.nil?
|
163
|
+
tables << cols
|
164
|
+
end
|
165
|
+
|
166
|
+
return tables
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def columns( table )
|
171
|
+
prep_stmt = SA.instance.api.sqlany_prepare(@handle, COLUMN_SELECT_STRING)
|
172
|
+
raise error() if prep_stmt.nil?
|
173
|
+
res, param = SA.instance.api.sqlany_describe_bind_param(prep_stmt, 0)
|
174
|
+
raise error() if res == 0 or param.nil?
|
175
|
+
param.set_value(table)
|
176
|
+
|
177
|
+
raise error() if SA.instance.api.sqlany_bind_param(prep_stmt, 0, param) == 0
|
178
|
+
|
179
|
+
res = SA.instance.api.sqlany_execute(prep_stmt)
|
180
|
+
|
181
|
+
raise error() if res == 0 or prep_stmt.nil?
|
182
|
+
columns = []
|
183
|
+
col_count = 0
|
184
|
+
while (SA.instance.api.sqlany_fetch_next(prep_stmt) == 1)
|
185
|
+
columns << {}
|
186
|
+
|
187
|
+
res, col_val = SA.instance.api.sqlany_get_column(prep_stmt, 0)
|
188
|
+
raise error() if res == 0
|
189
|
+
columns[col_count]['name'] = col_val
|
190
|
+
|
191
|
+
res, col_val = SA.instance.api.sqlany_get_column(prep_stmt, 1)
|
192
|
+
raise error() if res == 0
|
193
|
+
columns[col_count]['pkey'] = !col_val.nil?
|
194
|
+
|
195
|
+
res, col_val = SA.instance.api.sqlany_get_column(prep_stmt, 2)
|
196
|
+
raise error() if res == 0
|
197
|
+
if col_val == 'Y'
|
198
|
+
columns[col_count]['nullable'] = true
|
199
|
+
else
|
200
|
+
columns[col_count]['nullable'] = false
|
201
|
+
end
|
202
|
+
|
203
|
+
res, col_val = SA.instance.api.sqlany_get_column(prep_stmt, 3)
|
204
|
+
raise error() if res == 0
|
205
|
+
columns[col_count]['default'] = col_val
|
206
|
+
|
207
|
+
res, col_val = SA.instance.api.sqlany_get_column(prep_stmt, 4)
|
208
|
+
raise error() if res == 0
|
209
|
+
columns[col_count]['scale'] = col_val
|
210
|
+
|
211
|
+
res, col_val = SA.instance.api.sqlany_get_column(prep_stmt, 5)
|
212
|
+
raise error() if res == 0
|
213
|
+
columns[col_count]['precision'] = col_val
|
214
|
+
|
215
|
+
res, col_val = SA.instance.api.sqlany_get_column(prep_stmt, 6)
|
216
|
+
raise error() if res == 0
|
217
|
+
columns[col_count]['sql_type'] = SQLANY_to_DBI[col_val]
|
218
|
+
|
219
|
+
res, col_val = SA.instance.api.sqlany_get_column(prep_stmt, 7)
|
220
|
+
raise error() if res == 0
|
221
|
+
columns[col_count]['type_name'] = col_val.downcase
|
222
|
+
|
223
|
+
res, col_val = SA.instance.api.sqlany_get_column(prep_stmt, 8)
|
224
|
+
raise error() if res == 0
|
225
|
+
columns[col_count]['unique'] = (col_val == 1 or col_val == 2)
|
226
|
+
|
227
|
+
col_count += 1
|
228
|
+
end
|
229
|
+
param.finish
|
230
|
+
return columns
|
231
|
+
end
|
232
|
+
|
233
|
+
def [] (attr)
|
234
|
+
@attr[attr]
|
235
|
+
end
|
236
|
+
|
237
|
+
def []= (attr, val)
|
238
|
+
@attr[attr] = val
|
239
|
+
end
|
240
|
+
|
241
|
+
protected
|
242
|
+
def error(*custom_msg)
|
243
|
+
code, msg = SA.instance.api.sqlany_error(@handle)
|
244
|
+
state = SA.instance.api.sqlany_sqlstate(@handle)
|
245
|
+
SA.instance.api.sqlany_clear_error(@handle)
|
246
|
+
if !custom_msg.nil?
|
247
|
+
msg = "#{custom_msg}. #{msg}"
|
248
|
+
end
|
249
|
+
return DBI::DatabaseError.new(code, msg, state)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#====================================================
|
2
|
+
#
|
3
|
+
# Copyright 2008 iAnywhere Solutions, Inc.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
#
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
# While not a requirement of the license, if you do modify this file, we
|
20
|
+
# would appreciate hearing about it. Please email sqlany_interfaces@sybase.com
|
21
|
+
#
|
22
|
+
#
|
23
|
+
#====================================================
|
24
|
+
|
25
|
+
module DBI::DBD::SQLAnywhere
|
26
|
+
class Driver < DBI::BaseDriver
|
27
|
+
include Utility
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
super("0.4.0")
|
31
|
+
end
|
32
|
+
|
33
|
+
def connect( dbname, user, auth, attr )
|
34
|
+
conn = SA.instance.api.sqlany_new_connection()
|
35
|
+
conn_str = "uid=#{user};pwd=#{auth};";
|
36
|
+
|
37
|
+
if !dbname.nil?
|
38
|
+
conn_str += "eng=#{dbname};";
|
39
|
+
end
|
40
|
+
|
41
|
+
attr.keys.each do |option|
|
42
|
+
conn_str += "#{option}=#{attr[option]};"
|
43
|
+
end
|
44
|
+
|
45
|
+
SA.instance.api.sqlany_connect(conn, conn_str)
|
46
|
+
return Database.new(conn, attr)
|
47
|
+
end
|
48
|
+
|
49
|
+
def default_user
|
50
|
+
return ['dba', 'sql']
|
51
|
+
end
|
52
|
+
|
53
|
+
def disconnect_all
|
54
|
+
SA.instance.api.sqlany_fini()
|
55
|
+
SA.instance.free_api
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
#====================================================
|
2
|
+
#
|
3
|
+
# Copyright 2008 iAnywhere Solutions, Inc.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
#
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
# While not a requirement of the license, if you do modify this file, we
|
20
|
+
# would appreciate hearing about it. Please email sqlany_interfaces@sybase.com
|
21
|
+
#
|
22
|
+
#
|
23
|
+
#====================================================
|
24
|
+
|
25
|
+
module DBI::DBD::SQLAnywhere
|
26
|
+
class Statement < DBI::BaseStatement
|
27
|
+
include Utility
|
28
|
+
|
29
|
+
|
30
|
+
# Conversion table between SQL Anywhere E-SQL types and DBI SQL Types
|
31
|
+
SQLANY_NATIVE_TYPES = {
|
32
|
+
0 => DBI::SQL_LONGVARCHAR,
|
33
|
+
384 => DBI::SQL_DATE,
|
34
|
+
388 => DBI::SQL_TIME,
|
35
|
+
392 => DBI::SQL_TIMESTAMP,
|
36
|
+
448 => DBI::SQL_VARCHAR,
|
37
|
+
452 => DBI::SQL_CHAR,
|
38
|
+
456 => DBI::SQL_LONGVARCHAR,
|
39
|
+
460 => DBI::SQL_LONGVARCHAR,
|
40
|
+
480 => DBI::SQL_DOUBLE,
|
41
|
+
482 => DBI::SQL_FLOAT,
|
42
|
+
484 => DBI::SQL_DECIMAL,
|
43
|
+
496 => DBI::SQL_INTEGER,
|
44
|
+
500 => DBI::SQL_SMALLINT,
|
45
|
+
524 => DBI::SQL_BINARY,
|
46
|
+
528 => DBI::SQL_LONGVARBINARY,
|
47
|
+
604 => DBI::SQL_TINYINT,
|
48
|
+
608 => DBI::SQL_BIGINT,
|
49
|
+
612 => DBI::SQL_INTEGER,
|
50
|
+
616 => DBI::SQL_SMALLINT,
|
51
|
+
620 => DBI::SQL_BIGINT,
|
52
|
+
624 => DBI::SQL_BIT,
|
53
|
+
640 => DBI::SQL_LONGVARCHAR
|
54
|
+
}
|
55
|
+
|
56
|
+
def initialize(handle, conn, bound = {} )
|
57
|
+
@handle = handle
|
58
|
+
@conn = conn
|
59
|
+
@arr = []
|
60
|
+
@bound = bound
|
61
|
+
@offset = -1
|
62
|
+
end
|
63
|
+
|
64
|
+
def __bound_param(name)
|
65
|
+
if !@bound[name].nil?
|
66
|
+
res, param = SA.instance.api.sqlany_get_bind_param_info(@handle, @bound[name])
|
67
|
+
raise error() if res == 0
|
68
|
+
return param.get_output()
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Although SQL Anywhere allows multiple result sets,
|
73
|
+
# the @fetchable variable disallows there use
|
74
|
+
#
|
75
|
+
#def __next_resultset
|
76
|
+
# return SA.instance.api.sqlany_get_next_result(@handle)
|
77
|
+
#end
|
78
|
+
|
79
|
+
def bind_param(param, value, attribs)
|
80
|
+
param -= 1
|
81
|
+
res, param_description = SA.instance.api.sqlany_describe_bind_param(@handle, param)
|
82
|
+
raise error() if res == 0 or param_description.nil?
|
83
|
+
do_bind!(@handle, param_description, value, param, @bound)
|
84
|
+
param_description.finish
|
85
|
+
end
|
86
|
+
|
87
|
+
def execute()
|
88
|
+
res = SA.instance.api.sqlany_execute(@handle)
|
89
|
+
raise error() if res == 0
|
90
|
+
end
|
91
|
+
|
92
|
+
def fetch()
|
93
|
+
return fetch_scroll(DBI::SQL_FETCH_NEXT, 1)
|
94
|
+
end
|
95
|
+
|
96
|
+
def fetch_all()
|
97
|
+
rows = []
|
98
|
+
loop {
|
99
|
+
new_row = self.fetch_scroll(DBI::SQL_FETCH_NEXT, 1)
|
100
|
+
break if new_row.nil?
|
101
|
+
rows << new_row.clone
|
102
|
+
}
|
103
|
+
return rows
|
104
|
+
end
|
105
|
+
|
106
|
+
def fetch_scroll(direction, offset)
|
107
|
+
res = 0
|
108
|
+
new_offset = @offset
|
109
|
+
|
110
|
+
case direction
|
111
|
+
when DBI::SQL_FETCH_NEXT
|
112
|
+
res = SA.instance.api.sqlany_fetch_next(@handle)
|
113
|
+
new_offset += 1
|
114
|
+
when DBI::SQL_FETCH_PRIOR
|
115
|
+
res = SA.instance.api.sqlany_fetch_absolute(@handle, @offset)
|
116
|
+
new_offset -= 1
|
117
|
+
when DBI::SQL_FETCH_FIRST
|
118
|
+
res = SA.instance.api.sqlany_fetch_absolute(@handle, 1)
|
119
|
+
new_offset = 0
|
120
|
+
when DBI::SQL_FETCH_LAST
|
121
|
+
res = SA.instance.api.sqlany_fetch_absolute(@handle, -1)
|
122
|
+
new_offset = self.rows() - 1
|
123
|
+
when DBI::SQL_FETCH_ABSOLUTE
|
124
|
+
res = SA.instance.api.sqlany_fetch_absolute(@handle, offset)
|
125
|
+
if offset <= 0
|
126
|
+
new_offset = self.rows() + offset
|
127
|
+
else
|
128
|
+
new_offset = offset - 1
|
129
|
+
end
|
130
|
+
when DBI::SQL_FETCH_RELATIVE
|
131
|
+
res = SA.instance.api.sqlany_fetch_absolute(@handle, @offset + offset + 1)
|
132
|
+
new_offset += offset
|
133
|
+
end
|
134
|
+
|
135
|
+
if (res == 1)
|
136
|
+
retrieve_row_data()
|
137
|
+
@offset = new_offset
|
138
|
+
return @arr
|
139
|
+
else
|
140
|
+
return nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def column_info
|
145
|
+
columns = []
|
146
|
+
if !@handle.nil?
|
147
|
+
max_cols = SA.instance.api.sqlany_num_cols(@handle)
|
148
|
+
raise error() if max_cols == -1
|
149
|
+
max_cols.times do |cols|
|
150
|
+
columns << {}
|
151
|
+
res, holder, col_name, type, native_type, precision, scale, max_size, nullable = SA.instance.api.sqlany_get_column_info(@handle, cols)
|
152
|
+
raise error() if res == 0 or col_name.nil?
|
153
|
+
columns[cols]["name"] = col_name
|
154
|
+
sql_type = SQLANY_NATIVE_TYPES[native_type]
|
155
|
+
columns[cols]["sql_type"] = sql_type
|
156
|
+
columns[cols]["type_name"] = DBI::SQL_TYPE_NAMES[sql_type]
|
157
|
+
precision = max_size if precision == 0 and max_size != 0
|
158
|
+
columns[cols]["precision"] = precision
|
159
|
+
columns[cols]["scale"] = scale
|
160
|
+
columns[cols]["nullable"] = (nullable == 0)
|
161
|
+
|
162
|
+
columns[cols]["dbi_type"] = DBI::Type::Boolean if sql_type == DBI::SQL_BIT
|
163
|
+
end
|
164
|
+
end
|
165
|
+
return columns
|
166
|
+
end
|
167
|
+
|
168
|
+
def rows
|
169
|
+
if @handle.nil?
|
170
|
+
res = SA.instance.api.sqlany_affected_rows(@handle)
|
171
|
+
raise error() if res == -1
|
172
|
+
return res
|
173
|
+
else
|
174
|
+
0
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def finish
|
179
|
+
if !@handle.nil?
|
180
|
+
SA.instance.api.sqlany_free_stmt(@handle);
|
181
|
+
@handle = nil
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def cancel
|
186
|
+
end
|
187
|
+
|
188
|
+
protected
|
189
|
+
def error(*custom_msg)
|
190
|
+
code, msg = SA.instance.api.sqlany_error(@conn)
|
191
|
+
state = SA.instance.api.sqlany_sqlstate(@conn)
|
192
|
+
SA.instance.api.sqlany_clear_error(@conn)
|
193
|
+
if !custom_msg.nil?
|
194
|
+
msg = "#{custom_msg}. #{msg}"
|
195
|
+
end
|
196
|
+
return DBI::DatabaseError.new(code, msg, state)
|
197
|
+
end
|
198
|
+
|
199
|
+
def retrieve_row_data
|
200
|
+
max_cols = SA.instance.api.sqlany_num_cols(@handle)
|
201
|
+
raise error() if max_cols == -1
|
202
|
+
max_cols.times do |cols|
|
203
|
+
res, col_val = SA.instance.api.sqlany_get_column(@handle, cols)
|
204
|
+
raise error() if res == 0
|
205
|
+
@arr[cols] = col_val
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.4
|
3
|
+
specification_version: 1
|
4
|
+
name: dbd-sqlanywhere
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2008-11-03 00:00:00 -05:00
|
8
|
+
summary: Ruby DBI driver (DBD) for SQL Anywhere
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: eric.farrar@ianywhere.com
|
12
|
+
homepage: http://sqlanywhere.rubyforge.org
|
13
|
+
rubyforge_project: sqlanywhere
|
14
|
+
description: Ruby DBI driver (DBD) for SQL Anywhere
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.8.6
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Eric Farrar
|
31
|
+
files:
|
32
|
+
- lib/dbd/SQLAnywhere.rb
|
33
|
+
- lib/dbd/sqlanywhere/statement.rb
|
34
|
+
- lib/dbd/sqlanywhere/driver.rb
|
35
|
+
- lib/dbd/sqlanywhere/database.rb
|
36
|
+
- README
|
37
|
+
- CHANGELOG
|
38
|
+
- LICENSE
|
39
|
+
test_files: []
|
40
|
+
|
41
|
+
rdoc_options:
|
42
|
+
- --title
|
43
|
+
- SQL Anywhere DBD for Ruby-DBI
|
44
|
+
- --main
|
45
|
+
- README
|
46
|
+
- --line-numbers
|
47
|
+
extra_rdoc_files:
|
48
|
+
- README
|
49
|
+
- CHANGELOG
|
50
|
+
- LICENSE
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
requirements: []
|
56
|
+
|
57
|
+
dependencies:
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: sqlanywhere
|
60
|
+
version_requirement:
|
61
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: 0.1.0
|
66
|
+
version:
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: dbi
|
69
|
+
version_requirement:
|
70
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 0.4.0
|
75
|
+
version:
|