sqlanywhere-ffi 1.0.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,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