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 ADDED
@@ -0,0 +1,6 @@
1
+ =CHANGE LOG
2
+
3
+ =====0.1.0 -- 2008/10/29
4
+ - Initial Release
5
+ - Wraps DBCAPI funcationality
6
+ - Tested against DBI 0.4
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: