sqlanywhere-ffi 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,34 @@
1
+ =CHANGE LOG
2
+
3
+ =====0.1.5 -- 2010/01/25
4
+ - Updated library to use DBCAPI 2.0
5
+ - Changed test suite to correctly test BIT and TINYINT types
6
+ - Added support for Ruby 1.9.1
7
+
8
+ =====0.1.4 -- 2009/03/25
9
+ - Changed Rakefile to automatically create lib directory
10
+ - Fixed bug that tried to build native extensions on binary distsributions
11
+
12
+ =====0.1.3 -- 2008/12/31
13
+ - Removed 'hint file' in lib directory
14
+ - Added extensions to gemspec to force a local compile
15
+
16
+ =====0.1.2 -- 2008/12/18
17
+ - Fixed bug when trying to read input value from OUTPUT parameter
18
+ - Added error checking to C2RB
19
+ - Fixed Rake to handle .bundle on OSX
20
+
21
+ =====0.1.1 -- 2008/11/06
22
+ - Created a source gem rake task
23
+ - Created a 'hint' file if the source gem is used directly
24
+ - Changed file permissions on archives
25
+ - Changed archives to be specific to platform (.zip on windows, .tar.gz
26
+ otherwise)
27
+ - Changed default rake task to only build library, not gem
28
+ - Added a gem rake task
29
+
30
+ =====0.1.0 -- 2008/10/15
31
+ - Initial Release
32
+ - Wraps DBCAPI functionality
33
+ - Includes a simple unit test suite
34
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'ffi'
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ /*====================================================
2
+ *
3
+ * Copyright 2012 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.md ADDED
@@ -0,0 +1,102 @@
1
+ SQL Anywhere Ruby Driver
2
+ ------------------------
3
+ This is a native SQL Anywhere driver for Ruby. This library wraps the
4
+ functionality provided by the SQL Anywhere DBCAPI library. This driver
5
+ is intended to be a base-level library to be used by interface libraries
6
+ such as Ruby-DBI and ActiveRecord.
7
+
8
+ This driver can be used with SQL Anywhere 10 and later versions.
9
+
10
+ This driver is licensed under the Apache License, Version 2.
11
+
12
+ The official code repository is located on GitHub. The repository can be cloned with:
13
+
14
+ git clone git://github.com/sqlanywhere/sqlanywhere.git
15
+
16
+ ==Running Unit Tests==
17
+
18
+ 1. Change to the the <tt>test</tt> directory
19
+
20
+ cd test
21
+
22
+ 2. Create a testing database:
23
+
24
+ dbinit test
25
+
26
+ 3. Start the testing database:
27
+
28
+ dbeng12 test.db
29
+
30
+ 4. Create the test schema:
31
+
32
+ dbisql -c "eng=test;uid=dba;pwd=sql" test.sql
33
+
34
+ 5. Run the unit tests:
35
+
36
+ ruby sqlanywhere_test.rb
37
+
38
+ <b>If the tests fail to run, make sure you have set up the SQL Anywhere environment variables correctly.</b> For more information,
39
+ review the online documentation here [http://dcx.sybase.com/index.html#1200/en/dbadmin/da-envvar.html].
40
+
41
+ Sample
42
+ ------
43
+
44
+ This script makes a connection, prints <tt>Successful Ruby Connection</tt> to the SQL
45
+ Anywhere console, then disconnects.
46
+
47
+ # load the SQLAnywhere gem
48
+
49
+ begin
50
+
51
+ require 'rubygems'
52
+
53
+ gem 'sqlanywhere'
54
+
55
+ unless defined? SQLAnywhere
56
+
57
+ require 'sqlanywhere'
58
+
59
+ end
60
+
61
+ end
62
+
63
+ # create an interface
64
+
65
+ api = SQLAnywhere::SQLAnywhereInterface.new()
66
+
67
+ # initialize the interface (loads the DLL/SO)
68
+
69
+ SQLAnywhere::API.sqlany_initialize_interface( api )
70
+
71
+ # initialize our api object
72
+
73
+ api.sqlany_init()
74
+
75
+ # create a connection
76
+
77
+ conn = api.sqlany_new_connection()
78
+
79
+ # establish a connection
80
+
81
+ api.sqlany_connect(conn, "uid=dba;pwd=sql")
82
+
83
+ # execute a query without a result set
84
+
85
+ api.sqlany_execute_immediate(conn, "MESSAGE 'Successful Ruby Connection'")
86
+
87
+ # disconnect from the database
88
+
89
+ api.sqlany_disconnect(conn)
90
+
91
+ # free the connection resources
92
+
93
+ api.sqlany_free_connection(conn)
94
+
95
+ # free resources the api object uses
96
+
97
+ api.sqlany_fini()
98
+
99
+ # close the interface
100
+
101
+ SQLAnywhere::API.sqlany_finalize_interface( api )
102
+
data/lib/api.rb ADDED
@@ -0,0 +1,8 @@
1
+ module SQLAnywhere::API
2
+
3
+ def self.sqlany_initialize_interface(api, path=nil)
4
+ end
5
+
6
+ def self.sqlany_finalize_interface(api)
7
+ end
8
+ end
data/lib/bind_param.rb ADDED
@@ -0,0 +1,92 @@
1
+ require 'ffi'
2
+
3
+ class SQLAnywhere::BindParam < FFI::Struct
4
+
5
+ layout(
6
+ :direction, SQLAnywhere::DataDirection,
7
+ :value, SQLAnywhere::DataValue,
8
+ :name, :string,
9
+ )
10
+
11
+ def get_name
12
+ self[:name]
13
+ end
14
+
15
+ def get_direction
16
+ self[:direction]
17
+ end
18
+
19
+ def set_direction d
20
+ self[:direction] = d
21
+ end
22
+
23
+ def inspect
24
+ "<#{self.class} direction: #{self[:direction]}, name: #{self[:name]}, value: #{self[:value].inspect}>"
25
+ end
26
+
27
+ def set_value(value)
28
+
29
+ self[:value][:is_null] = FFI::MemoryPointer.new(SQLAnywhere::Bool, 1, true)
30
+
31
+ if self[:direction] == :input
32
+
33
+ case value
34
+ when String
35
+ self[:value][:length] = FFI::MemoryPointer.new(SQLAnywhere::SIZE_T, 1, false)
36
+ length = value.bytesize
37
+ SQLAnywhere.write_size_t(self[:value][:length], length)
38
+ self[:value][:buffer] = FFI::MemoryPointer.new(:char, length + 1, false)
39
+
40
+ self[:value][:buffer].put_string(0, value)
41
+ self[:value][:type] = :string
42
+ when Fixnum
43
+ self[:value][:buffer] = FFI::MemoryPointer.new(:long, 1, false)
44
+ self[:value][:type] = :val64
45
+ byte_array = [value].pack('q')
46
+ byte_array.each_byte.each_with_index do |b, offset|
47
+ self[:value][:buffer].put_char(offset, b)
48
+ end
49
+ when Bignum
50
+ self[:value][:buffer] = FFI::MemoryPointer.new(:long_long, 1, false)
51
+ self[:value][:type] = :val64
52
+ byte_array = [value].pack('Q')
53
+ byte_array.each_byte.each_with_index do |b, offset|
54
+ self[:value][:buffer].put_char(offset, b)
55
+ end
56
+ when Float
57
+ self[:value][:buffer] = FFI::MemoryPointer.new(:double, 1, false)
58
+ self[:value][:buffer].write_double(value)
59
+ self[:value][:type] = :double
60
+ when nil
61
+ self[:value][:is_null].write_int(1)
62
+ self[:value][:buffer] = FFI::MemoryPointer.new(:int, 1, false)
63
+ self[:value][:type] = :val32
64
+ else
65
+ raise "Cannot convert type (#{value.class}). Must be STRING, FIXNUM, BIGNUM, FLOAT, or NIL"
66
+ end
67
+
68
+ else
69
+ self[:value][:buffer] = FFI::MemoryPointer.new(:char,
70
+ case self[:value][:type]
71
+ when :string
72
+ self[:value][:buffer_size]
73
+ when :double
74
+ FFI::Type::FLOAT.size # Is this right? it's what the old code does
75
+ when :val64, :uval64
76
+ FFI::Type::LONG_LONG.size
77
+ when :val32, :uval32
78
+ FFI::Type::INT.size
79
+ when :val16, :uval16
80
+ FFI::Type::SHORT.size
81
+ when :val8, :uval8
82
+ FFI::Type::CHAR.size
83
+ else
84
+ raise "Type unknown (#{self[:value][:type]})"
85
+ end
86
+ )
87
+
88
+ end
89
+ nil
90
+ end
91
+
92
+ end
@@ -0,0 +1,12 @@
1
+ require 'ffi'
2
+
3
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-a-sqlany-bind-param-info-str.html
4
+ class SQLAnywhere::BindParamInfo < FFI::Struct
5
+
6
+ layout(
7
+ :name, :string,
8
+ :direction, SQLAnywhere::DataDirection,
9
+ :input_value, SQLAnywhere::DataValue,
10
+ :output_value, SQLAnywhere::DataValue,
11
+ )
12
+ end
data/lib/bool.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'ffi'
2
+
3
+ class SQLAnywhere::Bool < FFI::Struct
4
+
5
+ layout(
6
+ :value, :int32,
7
+ )
8
+
9
+ def read
10
+ self[:value]
11
+ end
12
+
13
+ end
@@ -0,0 +1,18 @@
1
+ require 'ffi'
2
+
3
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-a-sqlany-column-info-str.html
4
+ class SQLAnywhere::ColumnInfo < FFI::Struct
5
+
6
+ layout(
7
+ # The name of the column (null-terminated).
8
+ # The string can be referenced as long as the result set object is not freed.
9
+ :name, :string,
10
+ :type, SQLAnywhere::DataType,
11
+ :native_type, SQLAnywhere::NativeType,
12
+ :precision, :ushort,
13
+ :scale, :ushort,
14
+ :max_size, SQLAnywhere::SIZE_T,
15
+ :nullable, SQLAnywhere::Bool,
16
+ )
17
+
18
+ end
data/lib/data_info.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'ffi'
2
+
3
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-a-sqlany-data-info-str.html
4
+ class SQLAnywhere::DataInfo < FFI::Struct
5
+
6
+ layout(
7
+ :type, SQLAnywhere::DataType,
8
+ :is_null, SQLAnywhere::Bool,
9
+ :data_size, SQLAnywhere::SIZE_T,
10
+ )
11
+
12
+ end
data/lib/data_value.rb ADDED
@@ -0,0 +1,60 @@
1
+ require 'ffi'
2
+
3
+ #
4
+ class SQLAnywhere::DataValue < FFI::Struct
5
+
6
+ layout(
7
+ :buffer, :pointer, # deliberately not a string
8
+ :buffer_size, SQLAnywhere::SIZE_T,
9
+ :length, :pointer,
10
+ :type, SQLAnywhere::DataType,
11
+ :is_null, :pointer,
12
+ )
13
+
14
+ def inspect
15
+ "<#{self.class} value: #{self.value}, buffer_size: #{self[:buffer_size]}, is_null: #{self.is_null?}, length: #{self.length}, type: #{self[:type]}, buffer: #{self[:buffer]}, length_pointer: #{self[:length]}>"
16
+ end
17
+
18
+
19
+ def is_null?
20
+ return nil if self[:is_null].null?
21
+ self[:is_null].read_uint == 1
22
+ end
23
+
24
+ def length
25
+ return nil if self[:length].null?
26
+ self[:length].read_uint
27
+ end
28
+
29
+ def value
30
+ return nil if is_null?
31
+
32
+ case self[:type]
33
+ #when :invalid_type
34
+ when :binary, :string
35
+ self[:buffer].get_string(0, length)
36
+ when :double
37
+ self[:buffer].read_double
38
+ when :val64 # 64 bit integer
39
+ self[:buffer].read_long_long
40
+ when :unsigned_val64 # 64 bit unsigned integer
41
+ self[:buffer].read_ulong_long
42
+ when :val32
43
+ self[:buffer].read_int
44
+ when :unsigned_val32
45
+ self[:buffer].read_uint
46
+ when :val16
47
+ self[:buffer].read_short
48
+ when :unsigned_val16
49
+ self[:buffer].read_ushort
50
+ when :val8
51
+ self[:buffer].read_char
52
+ when :unsigned_val8
53
+ self[:buffer].read_uchar
54
+ else
55
+ raise "Type not yet implemented #{self[:type]}"
56
+ end
57
+
58
+ end
59
+
60
+ end
@@ -0,0 +1,113 @@
1
+ require 'ffi'
2
+
3
+ class SQLAnywhere::SQLAnywhereInterface
4
+
5
+ extend FFI::Library
6
+ ffi_lib 'dbcapi'
7
+
8
+ def sqlany_init(app_name = "RUBY", api_version = SQLAnywhere::API_VERSION_2, version_available = FFI::MemoryPointer.new(:int, 1, false))
9
+ sqlany_init_ app_name, api_version, version_available
10
+ end
11
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-init-met.html
12
+ attach_function :sqlany_init_, :sqlany_init, [:string, :uint, :pointer], :int
13
+
14
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-new-connection-met.html
15
+ attach_function :sqlany_new_connection, [], :pointer
16
+
17
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-connect-met.html
18
+ attach_function :sqlany_connect, [:pointer, :string], :int
19
+
20
+ def sqlany_error(connection)
21
+ size = 255
22
+ buffer = FFI::MemoryPointer.new(:char, size, false)
23
+ code = sqlany_error_ connection, buffer, size
24
+ return code, buffer.read_string
25
+ end
26
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-error-met.html
27
+ attach_function :sqlany_error_, :sqlany_error, [:pointer, :pointer, SQLAnywhere::SIZE_T], :int
28
+
29
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-execute-direct-met.html
30
+ attach_function :sqlany_execute_direct, [:pointer, :string], :pointer
31
+
32
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-fetch-next-met.html
33
+ attach_function :sqlany_fetch_next, [:pointer], :int
34
+
35
+ def sqlany_get_column(statement, column_number)
36
+ buffer = SQLAnywhere::DataValue.new
37
+ result = sqlany_get_column_(statement, column_number, buffer)
38
+ return [0, nil] if result == 0
39
+ [result, buffer.value]
40
+ end
41
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-get-column-met.html
42
+ attach_function :sqlany_get_column_, :sqlany_get_column, [:pointer, :uint32, :pointer], :int
43
+
44
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-free-stmt-met.html
45
+ attach_function :sqlany_free_stmt, [:pointer], :void
46
+
47
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-execute-immediate-met.html
48
+ attach_function :sqlany_execute_immediate, [:pointer, :string], :int
49
+
50
+ def sqlany_disconnect(connection)
51
+ sqlany_disconnect_(connection)
52
+ return nil
53
+ end
54
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-disconnect-met.html
55
+ attach_function :sqlany_disconnect_, :sqlany_disconnect, [:pointer], :int
56
+
57
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-free-connection-met.html
58
+ attach_function :sqlany_free_connection, [:pointer], :void
59
+
60
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-fini-met.html
61
+ attach_function :sqlany_fini, [], :void
62
+
63
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-prepare-met.html
64
+ attach_function :sqlany_prepare, [:pointer, :string], :pointer
65
+
66
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-num-cols-met.html
67
+ attach_function :sqlany_num_cols, [:pointer], :int
68
+
69
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-commit-met.html
70
+ attach_function :sqlany_commit, [:pointer], :int
71
+
72
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-execute-met.html
73
+ attach_function :sqlany_execute, [:pointer], :int
74
+
75
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-rollback-met.html
76
+ attach_function :sqlany_rollback, [:pointer], :int
77
+
78
+ def sqlany_describe_bind_param(statement, index)
79
+ bind_parameter = SQLAnywhere::BindParam.new
80
+ result = sqlany_describe_bind_param_(statement, index, bind_parameter)
81
+ [result, bind_parameter]
82
+ end
83
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-describe-bind-param-met.html
84
+ attach_function :sqlany_describe_bind_param_, :sqlany_describe_bind_param, [:pointer, :uint32, :pointer], :int
85
+
86
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-bind-param-met.html
87
+ attach_function :sqlany_bind_param, [:pointer, :uint, :pointer], :int
88
+
89
+ def sqlany_get_column_info(statement, column_number)
90
+
91
+ info = SQLAnywhere::ColumnInfo.new
92
+
93
+ result = sqlany_get_column_info_(statement, column_number, info)
94
+ [result, column_number, info[:name], info[:type], info[:native_type], info[:precision], info[:scale], info[:max_size], info[:nullable].read]
95
+ end
96
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-get-column-info-met.html
97
+ attach_function :sqlany_get_column_info_, :sqlany_get_column_info, [:pointer, :uint, :pointer], :int
98
+
99
+ def sqlany_sqlstate(connection)
100
+ size = 255
101
+ buffer = FFI::MemoryPointer.new(:char, size, false)
102
+ used = sqlany_sqlstate_(connection, buffer, size)
103
+ buffer.read_string(used)
104
+ end
105
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-sqlstate-met.html
106
+ attach_function :sqlany_sqlstate_, :sqlany_sqlstate, [:pointer, :pointer, SQLAnywhere::SIZE_T], SQLAnywhere::SIZE_T
107
+
108
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-clear-error-met.html
109
+ attach_function :sqlany_clear_error, [:pointer], :void
110
+
111
+ # http://dcx.sybase.com/1200/en/dbprogramming/programming-sacpp-sacapi-h-fil-sqlany-num-params-met.html
112
+ attach_function :sqlany_num_params, [:pointer], :int
113
+ end