ffi-sybase 0.0.1
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/.gitignore +4 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README +25 -0
- data/Rakefile +2 -0
- data/TODO +10 -0
- data/examples/sp_who.rb +20 -0
- data/ffi-sybase.gemspec +23 -0
- data/lib/ffi-sybase.rb +20 -0
- data/lib/sybase/command.rb +209 -0
- data/lib/sybase/connection.rb +132 -0
- data/lib/sybase/constants.rb +165 -0
- data/lib/sybase/context.rb +71 -0
- data/lib/sybase/lib.rb +264 -0
- data/lib/sybase/structs/client_message.rb +33 -0
- data/lib/sybase/structs/column_data.rb +60 -0
- data/lib/sybase/structs/data_format.rb +63 -0
- data/lib/sybase/structs/message.rb +27 -0
- data/lib/sybase/structs/server_message.rb +38 -0
- data/lib/sybase/version.rb +3 -0
- metadata +86 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Jari Bakken
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
= ffi-sybase
|
2
|
+
|
3
|
+
Ruby/FFI bindings for Sybase's Open Client library.
|
4
|
+
|
5
|
+
= See also
|
6
|
+
|
7
|
+
* http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.infocenter.dc35570.1550/html/clcprgde/title.htm
|
8
|
+
|
9
|
+
= Dependencies
|
10
|
+
|
11
|
+
* ffi
|
12
|
+
|
13
|
+
== Note on Patches/Pull Requests
|
14
|
+
|
15
|
+
* Fork the project.
|
16
|
+
* Make your feature addition or bug fix.
|
17
|
+
* Add tests for it. This is important so I don't break it in a
|
18
|
+
future version unintentionally.
|
19
|
+
* Commit, do not mess with rakefile, version, or history.
|
20
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
21
|
+
* Send me a pull request. Bonus points for topic branches.
|
22
|
+
|
23
|
+
== Copyright
|
24
|
+
|
25
|
+
Copyright (c) 2011 Jari Bakken. See LICENSE for details.
|
data/Rakefile
ADDED
data/TODO
ADDED
data/examples/sp_who.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
2
|
+
require 'pp'
|
3
|
+
require 'ffi-sybase'
|
4
|
+
|
5
|
+
unless ARGV.size == 3
|
6
|
+
abort("USAGE: #{$PROGRAM_NAME} <db> <user> <pass>")
|
7
|
+
end
|
8
|
+
|
9
|
+
db, user, pass = *ARGV
|
10
|
+
|
11
|
+
Sybase::Context.new do |ctx|
|
12
|
+
ctx.callbacks.library { |message| puts "library : #{message}" }
|
13
|
+
ctx.callbacks.client { |message| puts "client : #{message}" }
|
14
|
+
ctx.callbacks.server { |message| puts "server : #{message}" }
|
15
|
+
|
16
|
+
Sybase::Connection.new(ctx, :username => user, :password => pass) do |conn|
|
17
|
+
conn.connect db
|
18
|
+
pp Sybase::Command.new(conn, "sp_who").execute
|
19
|
+
end
|
20
|
+
end
|
data/ffi-sybase.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "sybase/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "ffi-sybase"
|
7
|
+
s.version = Sybase::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Jari Bakken"]
|
10
|
+
s.email = ["jari.bakken@gmail.com"]
|
11
|
+
s.homepage = "http://github.com/jarib/ffi-sybase"
|
12
|
+
s.summary = %q{Ruby/FFI bindings for Sybase OCS}
|
13
|
+
s.description = %q{Ruby/FFI bindings for Sybase's Open Client library.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "ffi-sybase"
|
16
|
+
|
17
|
+
s.add_dependency "ffi", ">= 0.6.3"
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
end
|
data/lib/ffi-sybase.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
require 'ffi'
|
5
|
+
|
6
|
+
module Sybase
|
7
|
+
class Error < StandardError; end
|
8
|
+
end
|
9
|
+
|
10
|
+
require "sybase/version"
|
11
|
+
require "sybase/constants"
|
12
|
+
require "sybase/structs/message"
|
13
|
+
require "sybase/structs/client_message"
|
14
|
+
require "sybase/structs/server_message"
|
15
|
+
require "sybase/structs/column_data"
|
16
|
+
require "sybase/structs/data_format"
|
17
|
+
require "sybase/lib"
|
18
|
+
require "sybase/context"
|
19
|
+
require "sybase/connection"
|
20
|
+
require "sybase/command"
|
@@ -0,0 +1,209 @@
|
|
1
|
+
module Sybase
|
2
|
+
class Command
|
3
|
+
|
4
|
+
def initialize(connection, str)
|
5
|
+
FFI::MemoryPointer.new(:pointer) do |ptr|
|
6
|
+
Lib.check Lib.ct_cmd_alloc(connection, ptr), "ct_cmd_alloc"
|
7
|
+
@ptr = FFI::AutoPointer.new(ptr.read_pointer, Lib.method(:ct_cmd_drop))
|
8
|
+
end
|
9
|
+
|
10
|
+
@str = str.to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute
|
14
|
+
set_command
|
15
|
+
send
|
16
|
+
results
|
17
|
+
ensure
|
18
|
+
finish
|
19
|
+
end
|
20
|
+
|
21
|
+
def finish
|
22
|
+
to_ptr.free
|
23
|
+
@ptr = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_ptr
|
27
|
+
@ptr or raise "command #{self} already ran or was not initialized"
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def set_command
|
33
|
+
Lib.check Lib.ct_command(to_ptr, CS_LANG_CMD, @str, @str.bytesize, CS_UNUSED)
|
34
|
+
end
|
35
|
+
|
36
|
+
def send
|
37
|
+
Lib.check Lib.ct_send(to_ptr), "ct_send failed"
|
38
|
+
end
|
39
|
+
|
40
|
+
def cancel
|
41
|
+
Lib.ct_cancel(nil, to_ptr, CS_CANCEL_CURRENT)
|
42
|
+
end
|
43
|
+
|
44
|
+
COMMAND_RESULTS = {
|
45
|
+
CS_CMD_SUCCEED => :succeed,
|
46
|
+
CS_CMD_DONE => :done,
|
47
|
+
CS_CMD_FAIL => :fail,
|
48
|
+
CS_ROW_RESULT => :row,
|
49
|
+
CS_CURSOR_RESULT => :cursor,
|
50
|
+
CS_PARAM_RESULT => :param,
|
51
|
+
CS_COMPUTE_RESULT => :compute,
|
52
|
+
CS_STATUS_RESULT => :status
|
53
|
+
}
|
54
|
+
|
55
|
+
def results
|
56
|
+
intptr = FFI::MemoryPointer.new(:int)
|
57
|
+
|
58
|
+
state = :initial
|
59
|
+
|
60
|
+
returned = []
|
61
|
+
|
62
|
+
while successful? intptr
|
63
|
+
restype = intptr.read_int
|
64
|
+
restype = COMMAND_RESULTS[restype] || restype
|
65
|
+
|
66
|
+
case restype
|
67
|
+
when :succeed, # no row - e.g. insert/update
|
68
|
+
:done # results completely processed
|
69
|
+
returned << Result.new(restype, nil, result_info(CS_ROW_COUNT), result_info(CS_TRANS_STATE))
|
70
|
+
when :fail
|
71
|
+
returned << Result.new(restype, nil, result_info(CS_ROW_COUNT), result_info(CS_TRANS_STATE))
|
72
|
+
when :row,
|
73
|
+
:cursor,
|
74
|
+
:param,
|
75
|
+
:compute,
|
76
|
+
:status
|
77
|
+
|
78
|
+
columns, rows = fetch_data
|
79
|
+
returned << Result.new(restype, :columns => columns, :rows => rows)
|
80
|
+
else
|
81
|
+
returned << Result.new(restype, nil, result_info(CS_ROW_COUNT), result_info(CS_TRANS_STATE))
|
82
|
+
end
|
83
|
+
|
84
|
+
# check context timeout?
|
85
|
+
end
|
86
|
+
|
87
|
+
returned
|
88
|
+
end
|
89
|
+
|
90
|
+
class Result
|
91
|
+
def initialize(type, data, row_count = nil, transaction_state = nil)
|
92
|
+
@type = type
|
93
|
+
@data = data
|
94
|
+
@row_count = row_count
|
95
|
+
@transaction_state = transaction_state
|
96
|
+
end
|
97
|
+
end # Result
|
98
|
+
|
99
|
+
def successful?(intptr)
|
100
|
+
@return_code = Lib.ct_results(to_ptr, intptr)
|
101
|
+
@return_code == CS_SUCCEED
|
102
|
+
end
|
103
|
+
|
104
|
+
def fetch_data
|
105
|
+
num_cols = fetch_column_count
|
106
|
+
|
107
|
+
column_datas = Array.new(num_cols) { ColumnData.new }
|
108
|
+
|
109
|
+
num_cols.times do |i|
|
110
|
+
cd = column_datas[i]
|
111
|
+
df = cd.format
|
112
|
+
|
113
|
+
Lib.check Lib.ct_describe(to_ptr, i + 1, df)
|
114
|
+
type = df[:datatype]
|
115
|
+
|
116
|
+
case type
|
117
|
+
when CS_TINYINT_TYPE,
|
118
|
+
CS_SMALLINT_TYPE,
|
119
|
+
CS_INT_TYPE,
|
120
|
+
CS_BIT_TYPE,
|
121
|
+
CS_DECIMAL_TYPE,
|
122
|
+
CS_NUMERIC_TYPE
|
123
|
+
df[:maxlength] = FFI.type_size(:int)
|
124
|
+
df[:datatype] = CS_INT_TYPE
|
125
|
+
df[:format] = CS_FMT_UNUSED
|
126
|
+
|
127
|
+
cd.int_pointer!
|
128
|
+
when CS_REAL_TYPE, CS_FLOAT_TYPE
|
129
|
+
# not sure about this
|
130
|
+
df[:maxlength] = FFI.type_size(:double)
|
131
|
+
df[:datatype] = CS_FLOAT_TYPE
|
132
|
+
df[:format] = CS_FMT_UNUSED
|
133
|
+
|
134
|
+
cd.double_pointer!
|
135
|
+
else # treat as String
|
136
|
+
df[:maxlength] = Lib.display_length(df) + 1
|
137
|
+
|
138
|
+
if type == CS_IMAGE_TYPE
|
139
|
+
df[:format] = CS_FMT_UNUSED
|
140
|
+
else
|
141
|
+
df[:format] = CS_FMT_NULLTERM
|
142
|
+
df[:datatype] = CS_CHAR_TYPE
|
143
|
+
end
|
144
|
+
|
145
|
+
cd.char_pointer!(df[:maxlength])
|
146
|
+
end
|
147
|
+
|
148
|
+
bind i, df, cd
|
149
|
+
end
|
150
|
+
|
151
|
+
rows_read_ptr = FFI::MemoryPointer.new(:int)
|
152
|
+
row_count = 0
|
153
|
+
|
154
|
+
columns = column_datas.map { |cd| cd.format.name }
|
155
|
+
values = []
|
156
|
+
|
157
|
+
while (code = fetch_row(rows_read_ptr)) == CS_SUCCEED || code == CS_ROW_FAIL
|
158
|
+
# increment row count
|
159
|
+
row_count += rows_read_ptr.read_int
|
160
|
+
|
161
|
+
if code == CS_ROW_FAIL
|
162
|
+
raise Error, "error on row #{row_count}"
|
163
|
+
end
|
164
|
+
|
165
|
+
values << column_datas.map { |e| e.value }
|
166
|
+
end
|
167
|
+
|
168
|
+
# done processing rows, check final return code
|
169
|
+
case code
|
170
|
+
when CS_END_DATA
|
171
|
+
# all good
|
172
|
+
when CS_FAIL
|
173
|
+
raise Error, "ct_fetch() failed"
|
174
|
+
else
|
175
|
+
raise Error, "unexpected return code: #{code}"
|
176
|
+
end
|
177
|
+
|
178
|
+
[columns, values]
|
179
|
+
ensure
|
180
|
+
# ?
|
181
|
+
end
|
182
|
+
|
183
|
+
def fetch_row(rows_read_ptr)
|
184
|
+
Lib.ct_fetch(to_ptr, CS_UNUSED, CS_UNUSED, CS_UNUSED, rows_read_ptr)
|
185
|
+
end
|
186
|
+
|
187
|
+
def bind(index, data_format, column_data)
|
188
|
+
Lib.check Lib.ct_bind(to_ptr, index + 1, data_format, column_data.pointer, column_data.valuelen_pointer, column_data.indicator_pointer)
|
189
|
+
end
|
190
|
+
|
191
|
+
def fetch_column_count
|
192
|
+
num_cols = result_info(CS_NUMDATA)
|
193
|
+
|
194
|
+
if num_cols <= 0
|
195
|
+
cancel
|
196
|
+
raise Error, "bad column count (#{num_cols})"
|
197
|
+
end
|
198
|
+
|
199
|
+
num_cols
|
200
|
+
end
|
201
|
+
|
202
|
+
def result_info(operation)
|
203
|
+
int_ptr = FFI::MemoryPointer.new(:int)
|
204
|
+
Lib.check Lib.ct_res_info(to_ptr, operation, int_ptr, CS_UNUSED, nil), "ct_res_info failed"
|
205
|
+
num_cols = int_ptr.read_int
|
206
|
+
end
|
207
|
+
|
208
|
+
end # Command
|
209
|
+
end # Sybase
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module Sybase
|
2
|
+
class Connection
|
3
|
+
PROPERTIES = {
|
4
|
+
:username => [ CS_USERNAME, :string ],
|
5
|
+
:password => [ CS_PASSWORD, :string ],
|
6
|
+
:appname => [ CS_APPNAME, :string ],
|
7
|
+
:tds_version => [ CS_TDS_VERSION, :int ],
|
8
|
+
:hostname => [ CS_HOSTNAME, :string ]
|
9
|
+
}
|
10
|
+
|
11
|
+
|
12
|
+
def initialize(context, opts ={})
|
13
|
+
@context = context
|
14
|
+
|
15
|
+
FFI::MemoryPointer.new(:pointer) { |ptr|
|
16
|
+
Lib.check Lib.ct_con_alloc(context, ptr), "ct_con_alloc"
|
17
|
+
@ptr = FFI::AutoPointer.new(ptr.read_pointer, Lib.method(:ct_con_drop))
|
18
|
+
}
|
19
|
+
|
20
|
+
opts.each do |key, value|
|
21
|
+
self[key] = value
|
22
|
+
end
|
23
|
+
|
24
|
+
unless opts.has_key?(:hostname)
|
25
|
+
self[:hostname] = Socket.gethostname
|
26
|
+
end
|
27
|
+
|
28
|
+
if block_given?
|
29
|
+
begin
|
30
|
+
yield self
|
31
|
+
ensure
|
32
|
+
close
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def debug!
|
38
|
+
Lib.check Lib.ct_debug(@context, to_ptr, CS_SET_FLAG, CS_DBG_ALL, nil, CS_UNUSED)
|
39
|
+
end
|
40
|
+
|
41
|
+
def close
|
42
|
+
Lib.check Lib.ct_close(@ptr, CS_UNUSED), "ct_close"
|
43
|
+
end
|
44
|
+
|
45
|
+
def [](key)
|
46
|
+
property, type = property_type_for(key)
|
47
|
+
case type
|
48
|
+
when :string
|
49
|
+
get_string_property property
|
50
|
+
when :int
|
51
|
+
get_int_property property
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def []=(key, value)
|
56
|
+
property, type = property_type_for(key)
|
57
|
+
|
58
|
+
case type
|
59
|
+
when :string
|
60
|
+
set_string_property property, value
|
61
|
+
when :int
|
62
|
+
set_int_property property, value
|
63
|
+
else
|
64
|
+
raise Error, "invalid type: #{type.inspect}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def property_type_for(key)
|
69
|
+
PROPERTIES.fetch(key) { |key|
|
70
|
+
raise ArgumentError, "invalid option: #{key.inspect}, expected one of #{PROPERTIES.keys.inspect}"
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
def username=(user)
|
75
|
+
set_string CS_USERNAME, user
|
76
|
+
end
|
77
|
+
|
78
|
+
def username
|
79
|
+
get_string CS_USERNAME
|
80
|
+
end
|
81
|
+
|
82
|
+
def password=(password)
|
83
|
+
set_string CS_PASSWORD, password
|
84
|
+
end
|
85
|
+
|
86
|
+
def password
|
87
|
+
get_string CS_PASSWORD
|
88
|
+
end
|
89
|
+
|
90
|
+
def appname=(name)
|
91
|
+
set_string CS_APPNAME, name
|
92
|
+
end
|
93
|
+
|
94
|
+
def appname
|
95
|
+
get_string CS_APPNAME, name
|
96
|
+
end
|
97
|
+
|
98
|
+
def connect(server)
|
99
|
+
server = server.to_s
|
100
|
+
Lib.check Lib.ct_connect(@ptr, server, server.bytesize), "connect(#{server.inspect}) failed"
|
101
|
+
end
|
102
|
+
|
103
|
+
def to_ptr
|
104
|
+
@ptr
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def set_string_property(property, string)
|
110
|
+
Lib.check Lib.ct_con_props(@ptr, CS_SET, property, string.to_s, CS_NULLTERM, nil), "ct_con_prop(#{property} => #{string.inspect}) failed"
|
111
|
+
end
|
112
|
+
|
113
|
+
def get_string_property(property)
|
114
|
+
FFI::MemoryPointer.new(:string) { |ptr| get_property(property, ptr) }.read_string
|
115
|
+
end
|
116
|
+
|
117
|
+
def get_int_property(property)
|
118
|
+
FFI::MemoryPointer.new(:int) { |ptr| get_property(property, ptr) }.read_int
|
119
|
+
end
|
120
|
+
|
121
|
+
def set_int_property(property, int)
|
122
|
+
ptr = FFI::MemoryPointer.new(:int)
|
123
|
+
ptr.write_int(int)
|
124
|
+
|
125
|
+
Lib.check Lib.ct_con_props(@ptr, CS_SET, property, ptr, CS_UNUSED, nil), "ct_con_prop(#{property} => #{int.inspect}) failed"
|
126
|
+
end
|
127
|
+
|
128
|
+
def get_property(property, ptr)
|
129
|
+
Lib.check Lib.ct_con_props(@ptr, CS_GET, property, ptr, CS_UNUSED, nil), "ct_con_prop(CS_GET, #{property}) failed"
|
130
|
+
end
|
131
|
+
end # Connection
|
132
|
+
end # Sybase
|
@@ -0,0 +1,165 @@
|
|
1
|
+
module Sybase
|
2
|
+
DEFAULT_CTLIB_VERSION = 15001
|
3
|
+
|
4
|
+
MAX_CHAR_BUF = 1024
|
5
|
+
|
6
|
+
CS_CONV_ERR = -100
|
7
|
+
CS_EXTERNAL_ERR = -200
|
8
|
+
CS_INTERNAL_ERR = -300
|
9
|
+
|
10
|
+
CS_TDS_40 = 7360
|
11
|
+
CS_TDS_42 = 7361
|
12
|
+
CS_TDS_46 = 7362
|
13
|
+
CS_TDS_495 = 7363
|
14
|
+
CS_TDS_50 = 7364
|
15
|
+
|
16
|
+
CS_SET_FLAG = 1700
|
17
|
+
CS_CLEAR_FLAG = 1701
|
18
|
+
CS_DBG_ALL = 1
|
19
|
+
CS_DBG_ASYNC = 2
|
20
|
+
CS_DBG_ERROR = 4
|
21
|
+
|
22
|
+
CS_CLIENTMSG_CB = 3
|
23
|
+
CS_GET = 33
|
24
|
+
CS_MAX_CHAR = 256
|
25
|
+
CS_MAX_NAME = 255
|
26
|
+
CS_MAX_MSG = 1024
|
27
|
+
CS_MESSAGE_CB = 9119
|
28
|
+
CS_NULLTERM = -9
|
29
|
+
CS_SERVERMSG_CB = 2
|
30
|
+
CS_SET = 34
|
31
|
+
CS_SQLSTATE_SIZE = 8
|
32
|
+
CS_SUCCEED = 1
|
33
|
+
CS_FAIL = 0
|
34
|
+
CS_UNUSED = -99999
|
35
|
+
CS_CANCEL_CURRENT = 6000
|
36
|
+
CS_FMT_UNUSED = 0
|
37
|
+
CS_FMT_NULLTERM = 1
|
38
|
+
|
39
|
+
CS_SYNC_IO = 8111
|
40
|
+
CS_ASYNC_IO = 8112
|
41
|
+
CS_DEFER_IO = 8113
|
42
|
+
|
43
|
+
# server options
|
44
|
+
CS_OPT_CHARSET = 5010
|
45
|
+
CS_OPT_PARSEONLY = 5018
|
46
|
+
|
47
|
+
# ct_command types
|
48
|
+
CS_LANG_CMD = 148
|
49
|
+
CS_RPC_CMD = 149
|
50
|
+
CS_MSG_CMD = 150
|
51
|
+
CS_SEND_DATA_CMD = 152
|
52
|
+
|
53
|
+
# connection properties
|
54
|
+
CS_USERNAME = 9100
|
55
|
+
CS_PASSWORD = 9101
|
56
|
+
CS_APPNAME = 9102
|
57
|
+
CS_HOSTNAME = 9103
|
58
|
+
CS_LOGIN_STATUS = 9104
|
59
|
+
CS_TDS_VERSION = 9105
|
60
|
+
CS_CHARSETCNV = 9106
|
61
|
+
CS_PACKETSIZE = 9107
|
62
|
+
CS_USERDATA = 9108
|
63
|
+
CS_COMMBLOCK = 9109
|
64
|
+
CS_NETIO = 9110
|
65
|
+
CS_NOINTERRUPT = 9111
|
66
|
+
CS_TEXTLIMIT = 9112
|
67
|
+
CS_HIDDEN_KEYS = 9113
|
68
|
+
CS_VERSION = 9114
|
69
|
+
CS_IFILE = 9115
|
70
|
+
CS_LOGIN_TIMEOUT = 9116
|
71
|
+
CS_TIMEOUT = 9117
|
72
|
+
CS_MAX_CONNECT = 9118
|
73
|
+
CS_EXPOSE_FMTS = 9120
|
74
|
+
CS_EXTRA_INF = 9121
|
75
|
+
CS_TRANSACTION_NAME = 9122
|
76
|
+
CS_ANSI_BINDS = 9123
|
77
|
+
CS_BULK_LOGIN = 9124
|
78
|
+
CS_LOC_PROP = 9125
|
79
|
+
CS_CUR_STATUS = 9126
|
80
|
+
CS_CUR_ID = 9127
|
81
|
+
CS_CUR_NAME = 9128
|
82
|
+
CS_CUR_ROWCOUNT = 9129
|
83
|
+
CS_PARENT_HANDLE = 9130
|
84
|
+
CS_EED_CMD = 9131
|
85
|
+
CS_DIAG_TIMEOUT = 9132
|
86
|
+
CS_DISABLE_POLL = 9133
|
87
|
+
CS_NOTIF_CMD = 9134
|
88
|
+
CS_SEC_ENCRYPTION = 9135
|
89
|
+
CS_SEC_CHALLENGE = 9136
|
90
|
+
CS_SEC_NEGOTIATE = 9137
|
91
|
+
CS_MEM_POOL = 9138
|
92
|
+
CS_USER_ALLOC = 9139
|
93
|
+
CS_USER_FREE = 9140
|
94
|
+
CS_ENDPOINT = 9141
|
95
|
+
CS_NO_TRUNCATE = 9142
|
96
|
+
CS_CON_STATUS = 9143
|
97
|
+
CS_VER_STRING = 9144
|
98
|
+
CS_ASYNC_NOTIFS = 9145
|
99
|
+
CS_SERVERNAME = 9146
|
100
|
+
|
101
|
+
CS_SEND_BULK_CMD = 153
|
102
|
+
|
103
|
+
# ct_results
|
104
|
+
CS_ROW_RESULT = 4040
|
105
|
+
CS_CURSOR_RESULT = 4041
|
106
|
+
CS_PARAM_RESULT = 4042
|
107
|
+
CS_STATUS_RESULT = 4043
|
108
|
+
CS_MSG_RESULT = 4044
|
109
|
+
CS_COMPUTE_RESULT = 4045
|
110
|
+
CS_CMD_DONE = 4046
|
111
|
+
CS_CMD_SUCCEED = 4047
|
112
|
+
CS_CMD_FAIL = 4048
|
113
|
+
CS_ROWFMT_RESULT = 4049
|
114
|
+
CS_COMPUTEFMT_RESULT = 4050
|
115
|
+
CS_DESCRIBE_RESULT = 4051
|
116
|
+
CS_END_RESULTS = CS_EXTERNAL_ERR - 5
|
117
|
+
|
118
|
+
# ct_res_info
|
119
|
+
CS_ROW_COUNT = 800
|
120
|
+
CS_NUMDATA = 803
|
121
|
+
CS_MSGTYPE = 806
|
122
|
+
CS_TRANS_STATE = 808
|
123
|
+
|
124
|
+
# ct_fetch
|
125
|
+
CS_ROW_FAIL = CS_EXTERNAL_ERR - 3
|
126
|
+
CS_END_DATA = CS_EXTERNAL_ERR - 4
|
127
|
+
|
128
|
+
# data types
|
129
|
+
CS_ILLEGAL_TYPE = -1
|
130
|
+
CS_CHAR_TYPE = 0
|
131
|
+
CS_BINARY_TYPE = 1
|
132
|
+
CS_LONGCHAR_TYPE = 2
|
133
|
+
CS_LONGBINARY_TYPE = 3
|
134
|
+
CS_TEXT_TYPE = 4
|
135
|
+
CS_IMAGE_TYPE = 5
|
136
|
+
CS_TINYINT_TYPE = 6
|
137
|
+
CS_SMALLINT_TYPE = 7
|
138
|
+
CS_INT_TYPE = 8
|
139
|
+
CS_REAL_TYPE = 9
|
140
|
+
CS_FLOAT_TYPE = 10
|
141
|
+
CS_BIT_TYPE = 11
|
142
|
+
CS_DATETIME_TYPE = 12
|
143
|
+
CS_DATETIME4_TYPE = 13
|
144
|
+
CS_MONEY_TYPE = 14
|
145
|
+
CS_MONEY4_TYPE = 15
|
146
|
+
CS_NUMERIC_TYPE = 16
|
147
|
+
CS_DECIMAL_TYPE = 17
|
148
|
+
CS_VARCHAR_TYPE = 18
|
149
|
+
CS_VARBINARY_TYPE = 19
|
150
|
+
CS_LONG_TYPE = 20
|
151
|
+
CS_SENSITIVITY_TYPE = 21
|
152
|
+
CS_BOUNDARY_TYPE = 22
|
153
|
+
CS_VOID_TYPE = 23
|
154
|
+
CS_USHORT_TYPE = 24
|
155
|
+
CS_UNICHAR_TYPE = 25
|
156
|
+
CS_BLOB_TYPE = 26
|
157
|
+
CS_DATE_TYPE = 27
|
158
|
+
CS_TIME_TYPE = 28
|
159
|
+
CS_UNITEXT_TYPE = 29
|
160
|
+
CS_BIGINT_TYPE = 30
|
161
|
+
CS_USMALLINT_TYPE = 31
|
162
|
+
CS_UINT_TYPE = 32
|
163
|
+
CS_UBIGINT_TYPE = 33
|
164
|
+
CS_XML_TYPE = 34
|
165
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Sybase
|
2
|
+
class Context
|
3
|
+
def initialize(version = DEFAULT_CTLIB_VERSION)
|
4
|
+
@version = Integer(version)
|
5
|
+
|
6
|
+
FFI::MemoryPointer.new(:pointer) do |ptr|
|
7
|
+
Lib.check Lib.cs_ctx_alloc(@version, ptr), "cs_ctx_alloc failed"
|
8
|
+
@ptr = FFI::AutoPointer.new(ptr.read_pointer, Lib.method(:cs_ctx_drop))
|
9
|
+
end
|
10
|
+
|
11
|
+
Lib.check Lib.ct_init(@ptr, @version), "ct_init failed"
|
12
|
+
|
13
|
+
if block_given?
|
14
|
+
begin
|
15
|
+
yield self
|
16
|
+
ensure
|
17
|
+
exit
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def sync=(bool)
|
23
|
+
FFI::MemoryPointer.new(:int) do |ptr|
|
24
|
+
ptr.write_int(bool ? CS_SYNC_IO : CS_ASYNC_IO) # CS_DEFER_IO ?
|
25
|
+
Lib.check Lib.ct_config(@ptr, CS_SET, CS_NETIO, ptr, CS_UNUSED, nil)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def callbacks
|
30
|
+
@callbacks ||= Callbacks.new self
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_ptr
|
34
|
+
@ptr
|
35
|
+
end
|
36
|
+
|
37
|
+
def exit
|
38
|
+
Lib.check Lib.ct_exit(@ptr, CS_UNUSED), "ct_exit failed"
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
class Callbacks
|
44
|
+
def initialize(context)
|
45
|
+
@context = context
|
46
|
+
end
|
47
|
+
|
48
|
+
def library(&cb)
|
49
|
+
actual_callback = FFI::Function.new(:int, [:pointer, :pointer]) { |context, message|
|
50
|
+
cb.call ClientMessage.new(message)
|
51
|
+
CS_SUCCEED
|
52
|
+
}
|
53
|
+
Lib.check Lib.cs_config(@context, CS_SET, CS_MESSAGE_CB, actual_callback, CS_UNUSED, nil)
|
54
|
+
end
|
55
|
+
|
56
|
+
def client(&cb)
|
57
|
+
Lib.check Lib.ct_callback(@context, nil, CS_SET, CS_CLIENTMSG_CB, lambda { |context, connection, message|
|
58
|
+
cb.call ClientMessage.new(message)
|
59
|
+
CS_SUCCEED
|
60
|
+
})
|
61
|
+
end
|
62
|
+
|
63
|
+
def server(&cb)
|
64
|
+
Lib.check Lib.ct_callback(@context, nil, CS_SET, CS_SERVERMSG_CB, lambda { |context, connection, message|
|
65
|
+
cb.call ServerMessage.new(message)
|
66
|
+
CS_SUCCEED
|
67
|
+
})
|
68
|
+
end
|
69
|
+
end # Callbacks
|
70
|
+
end # Context
|
71
|
+
end # Sybase
|
data/lib/sybase/lib.rb
ADDED
@@ -0,0 +1,264 @@
|
|
1
|
+
module Sybase
|
2
|
+
module Lib
|
3
|
+
extend FFI::Library
|
4
|
+
|
5
|
+
suffix = RUBY_VERSION < '1.9' ? '' : '_r'
|
6
|
+
|
7
|
+
if FFI.type_size(:pointer) == 8
|
8
|
+
ffi_lib "sybct64#{suffix}"
|
9
|
+
else
|
10
|
+
ffi_lib "sybct#{suffix}"
|
11
|
+
end
|
12
|
+
|
13
|
+
# extern CS_RETCODE CS_PUBLIC cs_ctx_alloc PROTOTYPE((
|
14
|
+
# CS_INT version,
|
15
|
+
# CS_CONTEXT **outptr
|
16
|
+
# ));
|
17
|
+
|
18
|
+
attach_function :cs_ctx_alloc, [:int, :pointer], :int
|
19
|
+
|
20
|
+
# extern CS_RETCODE CS_PUBLIC cs_ctx_drop PROTOTYPE((
|
21
|
+
# CS_CONTEXT *context
|
22
|
+
# ));
|
23
|
+
|
24
|
+
attach_function :cs_ctx_drop, [:pointer], :int
|
25
|
+
|
26
|
+
# extern CS_RETCODE CS_PUBLIC ct_init PROTOTYPE((
|
27
|
+
# CS_CONTEXT *context,
|
28
|
+
# CS_INT version
|
29
|
+
# ));
|
30
|
+
|
31
|
+
attach_function :ct_init, [:pointer, :int], :int
|
32
|
+
|
33
|
+
# extern CS_RETCODE CS_PUBLIC ct_config PROTOTYPE((
|
34
|
+
# CS_CONTEXT *context,
|
35
|
+
# CS_INT action,
|
36
|
+
# CS_INT property,
|
37
|
+
# CS_VOID *buf,
|
38
|
+
# CS_INT buflen,
|
39
|
+
# CS_INT *outlen
|
40
|
+
# ));
|
41
|
+
|
42
|
+
attach_function :ct_config, [:pointer, :int, :int, :pointer, :int, :pointer], :int
|
43
|
+
|
44
|
+
# extern CS_RETCODE CS_PUBLIC cs_config PROTOTYPE((
|
45
|
+
# CS_CONTEXT *context,
|
46
|
+
# CS_INT action,
|
47
|
+
# CS_INT property,
|
48
|
+
# CS_VOID *buf,
|
49
|
+
# CS_INT buflen,
|
50
|
+
# CS_INT *outlen
|
51
|
+
# ));
|
52
|
+
|
53
|
+
attach_function :cs_config, [:pointer, :int, :int, :pointer, :int, :pointer], :int
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
callback :cs_clientmsg_cb, [:pointer, :pointer, :pointer], :int
|
58
|
+
|
59
|
+
# extern CS_RETCODE CS_PUBLIC ct_callback PROTOTYPE((
|
60
|
+
# CS_CONTEXT *context,
|
61
|
+
# CS_CONNECTION *connection,
|
62
|
+
# CS_INT action,
|
63
|
+
# CS_INT type,
|
64
|
+
# CS_VOID *func
|
65
|
+
# ));
|
66
|
+
|
67
|
+
attach_function :ct_callback, [:pointer, :pointer, :int, :int, :cs_clientmsg_cb], :int
|
68
|
+
|
69
|
+
# extern CS_RETCODE CS_PUBLIC ct_con_alloc PROTOTYPE((
|
70
|
+
# CS_CONTEXT *context,
|
71
|
+
# CS_CONNECTION **connection
|
72
|
+
# ));
|
73
|
+
|
74
|
+
attach_function :ct_con_alloc, [:pointer, :pointer], :int
|
75
|
+
|
76
|
+
# extern CS_RETCODE CS_PUBLIC ct_con_props PROTOTYPE((
|
77
|
+
# CS_CONNECTION *connection,
|
78
|
+
# CS_INT action,
|
79
|
+
# CS_INT property,
|
80
|
+
# CS_VOID *buf,
|
81
|
+
# CS_INT buflen,
|
82
|
+
# CS_INT *outlen
|
83
|
+
# ));
|
84
|
+
|
85
|
+
attach_function :ct_con_props, [:pointer, :int, :int, :pointer, :int, :pointer], :int
|
86
|
+
|
87
|
+
# extern CS_RETCODE CS_PUBLIC ct_connect PROTOTYPE((
|
88
|
+
# CS_CONNECTION *connection,
|
89
|
+
# CS_CHAR *server_name,
|
90
|
+
# CS_INT snamelen
|
91
|
+
# ));
|
92
|
+
|
93
|
+
attach_function :ct_connect, [:pointer, :pointer, :int], :int
|
94
|
+
|
95
|
+
# extern CS_RETCODE CS_PUBLIC ct_close PROTOTYPE((
|
96
|
+
# CS_CONNECTION *connection,
|
97
|
+
# CS_INT option
|
98
|
+
# ));
|
99
|
+
|
100
|
+
attach_function :ct_close, [:pointer, :int], :int
|
101
|
+
|
102
|
+
# extern CS_RETCODE CS_PUBLIC ct_cmd_alloc PROTOTYPE((
|
103
|
+
# CS_CONNECTION *connection,
|
104
|
+
# CS_COMMAND **cmdptr
|
105
|
+
# ));
|
106
|
+
|
107
|
+
attach_function :ct_cmd_alloc, [:pointer, :pointer], :int
|
108
|
+
|
109
|
+
# extern CS_RETCODE CS_PUBLIC ct_cmd_drop PROTOTYPE((
|
110
|
+
# CS_COMMAND *cmd
|
111
|
+
# ));
|
112
|
+
|
113
|
+
attach_function :ct_cmd_drop, [:pointer], :int
|
114
|
+
|
115
|
+
# extern CS_RETCODE CS_PUBLIC ct_cmd_props PROTOTYPE((
|
116
|
+
# CS_COMMAND *cmd,
|
117
|
+
# CS_INT action,
|
118
|
+
# CS_INT property,
|
119
|
+
# CS_VOID *buf,
|
120
|
+
# CS_INT buflen,
|
121
|
+
# CS_INT *outlen
|
122
|
+
# ));
|
123
|
+
|
124
|
+
attach_function :ct_cmd_props, [:pointer, :int, :int, :pointer, :int, :pointer], :int
|
125
|
+
|
126
|
+
# extern CS_RETCODE CS_PUBLIC ct_command PROTOTYPE((
|
127
|
+
# CS_COMMAND *cmd,
|
128
|
+
# CS_INT type,
|
129
|
+
# CS_CHAR *buf,
|
130
|
+
# CS_INT buflen,
|
131
|
+
# CS_INT option
|
132
|
+
# ));
|
133
|
+
|
134
|
+
attach_function :ct_command, [:pointer, :int, :string, :int, :int], :int
|
135
|
+
|
136
|
+
# extern CS_RETCODE CS_PUBLIC ct_send PROTOTYPE((
|
137
|
+
# CS_COMMAND *cmd
|
138
|
+
# ));
|
139
|
+
|
140
|
+
attach_function :ct_send, [:pointer], :int
|
141
|
+
|
142
|
+
# extern CS_RETCODE CS_PUBLIC ct_results PROTOTYPE((
|
143
|
+
# CS_COMMAND *cmd,
|
144
|
+
# CS_INT *result_type
|
145
|
+
# ));
|
146
|
+
|
147
|
+
attach_function :ct_results, [:pointer, :pointer], :int
|
148
|
+
|
149
|
+
# extern CS_RETCODE CS_PUBLIC ct_close PROTOTYPE((
|
150
|
+
# CS_CONNECTION *connection,
|
151
|
+
# CS_INT option
|
152
|
+
# ));
|
153
|
+
|
154
|
+
attach_function :close, [:pointer, :int], :int
|
155
|
+
|
156
|
+
# extern CS_RETCODE CS_PUBLIC ct_exit PROTOTYPE((
|
157
|
+
# CS_CONTEXT *context,
|
158
|
+
# CS_INT option
|
159
|
+
# ));
|
160
|
+
|
161
|
+
attach_function :ct_exit, [:pointer, :int], :int
|
162
|
+
|
163
|
+
# extern CS_RETCODE CS_PUBLIC ct_con_drop PROTOTYPE((
|
164
|
+
# CS_CONNECTION *connection
|
165
|
+
# ));
|
166
|
+
|
167
|
+
attach_function :ct_con_drop, [:pointer], :int
|
168
|
+
|
169
|
+
# extern CS_RETCODE CS_PUBLIC ct_cancel PROTOTYPE((
|
170
|
+
# CS_CONNECTION *connection,
|
171
|
+
# CS_COMMAND *cmd,
|
172
|
+
# CS_INT type
|
173
|
+
# ));
|
174
|
+
|
175
|
+
attach_function :ct_cancel, [:pointer, :pointer, :int], :int
|
176
|
+
|
177
|
+
# extern CS_RETCODE CS_PUBLIC ct_res_info PROTOTYPE((
|
178
|
+
# CS_COMMAND *cmd,
|
179
|
+
# CS_INT operation,
|
180
|
+
# CS_VOID *buf,
|
181
|
+
# CS_INT buflen,
|
182
|
+
# CS_INT *outlen
|
183
|
+
# ));
|
184
|
+
|
185
|
+
attach_function :ct_res_info, [:pointer, :int, :pointer, :int, :pointer], :int
|
186
|
+
|
187
|
+
|
188
|
+
# extern CS_RETCODE CS_PUBLIC ct_describe PROTOTYPE((
|
189
|
+
# CS_COMMAND *cmd,
|
190
|
+
# CS_INT item,
|
191
|
+
# CS_DATAFMT *datafmt
|
192
|
+
# ));
|
193
|
+
|
194
|
+
attach_function :ct_describe, [:pointer, :int, :pointer], :int
|
195
|
+
|
196
|
+
# extern CS_RETCODE CS_PUBLIC ct_bind PROTOTYPE((
|
197
|
+
# CS_COMMAND *cmd,
|
198
|
+
# CS_INT item,
|
199
|
+
# CS_DATAFMT *datafmt,
|
200
|
+
# CS_VOID *buf,
|
201
|
+
# CS_INT *outputlen,
|
202
|
+
# CS_SMALLINT *indicator
|
203
|
+
# ));
|
204
|
+
|
205
|
+
attach_function :ct_bind, [:pointer, :int, :pointer, :pointer, :pointer, :pointer], :int
|
206
|
+
|
207
|
+
# extern CS_RETCODE CS_PUBLIC ct_fetch PROTOTYPE((
|
208
|
+
# CS_COMMAND *cmd,
|
209
|
+
# CS_INT type,
|
210
|
+
# CS_INT offset,
|
211
|
+
# CS_INT option,
|
212
|
+
# CS_INT *count
|
213
|
+
# ));
|
214
|
+
|
215
|
+
attach_function :ct_fetch, [:pointer, :int, :int, :int, :pointer], :int
|
216
|
+
|
217
|
+
# extern CS_RETCODE CS_PUBLIC ct_debug PROTOTYPE((
|
218
|
+
# CS_CONTEXT *context,
|
219
|
+
# CS_CONNECTION *connection,
|
220
|
+
# CS_INT operation,
|
221
|
+
# CS_INT flag,
|
222
|
+
# CS_CHAR *filename,
|
223
|
+
# CS_INT fnamelen
|
224
|
+
# ));
|
225
|
+
|
226
|
+
attach_function :ct_debug, [:pointer, :pointer, :int, :int, :pointer, :int], :int
|
227
|
+
|
228
|
+
def self.check(code, msg = "error")
|
229
|
+
if code != CS_SUCCEED
|
230
|
+
raise Error, msg
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def self.display_length(data_format)
|
235
|
+
len = case data_format[:datatype]
|
236
|
+
when CS_CHAR_TYPE, CS_LONGCHAR_TYPE, CS_VARCHAR_TYPE, CS_TEXT_TYPE, CS_IMAGE_TYPE
|
237
|
+
[data_format[:maxlength], MAX_CHAR_BUF].min
|
238
|
+
when CS_UNICHAR_TYPE
|
239
|
+
[data_format[:maxlength] / 2, MAX_CHAR_BUF].min
|
240
|
+
when CS_BINARY_TYPE, CS_VARBINARY_TYPE
|
241
|
+
[(2 * data_format[:maxlength]) + 2, MAX_CHAR_BUF].min
|
242
|
+
when CS_BIT_TYPE, CS_TINYINT_TYPE
|
243
|
+
3
|
244
|
+
when CS_SMALLINT_TYPE
|
245
|
+
6
|
246
|
+
when CS_INT_TYPE
|
247
|
+
11
|
248
|
+
when CS_REAL_TYPE, CS_FLOAT_TYPE
|
249
|
+
20
|
250
|
+
when CS_MONEY_TYPE, CS_MONEY4_TYPE
|
251
|
+
24
|
252
|
+
when CS_DATETIME_TYPE, CS_DATETIME4_TYPE
|
253
|
+
30
|
254
|
+
when CS_NUMERIC_TYPE, CS_DECIMAL_TYPE
|
255
|
+
CS_MAX_PREC + 2
|
256
|
+
else
|
257
|
+
12
|
258
|
+
end
|
259
|
+
|
260
|
+
|
261
|
+
[data_format[:name].size + 1, len].max
|
262
|
+
end
|
263
|
+
end # Lib
|
264
|
+
end # Sybase
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Sybase
|
2
|
+
# typedef struct _cs_clientmsg
|
3
|
+
# {
|
4
|
+
# CS_INT severity;
|
5
|
+
# CS_MSGNUM msgnumber;
|
6
|
+
# CS_CHAR msgstring[CS_MAX_MSG];
|
7
|
+
# CS_INT msgstringlen;
|
8
|
+
# CS_INT osnumber;
|
9
|
+
# CS_CHAR osstring[CS_MAX_MSG];
|
10
|
+
# CS_INT osstringlen;
|
11
|
+
# CS_INT status;
|
12
|
+
# CS_BYTE sqlstate[CS_SQLSTATE_SIZE];
|
13
|
+
# CS_INT sqlstatelen;
|
14
|
+
# } CS_CLIENTMSG;
|
15
|
+
|
16
|
+
class ClientMessage < Message
|
17
|
+
layout :severity, :int,
|
18
|
+
:msgnumber, :uint,
|
19
|
+
:msgstring, [:char, CS_MAX_MSG],
|
20
|
+
:msgstringlen, :int,
|
21
|
+
:osnumber, :int,
|
22
|
+
:osstring, [:char, CS_MAX_MSG],
|
23
|
+
:osstringlen, :int,
|
24
|
+
:status, :int,
|
25
|
+
:sqlstate, [:uchar, CS_SQLSTATE_SIZE],
|
26
|
+
:sqlstatelen, :int
|
27
|
+
|
28
|
+
def text
|
29
|
+
self[:msgstring].to_s.chomp
|
30
|
+
end
|
31
|
+
|
32
|
+
end # ClientMessage
|
33
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Sybase
|
2
|
+
class ColumnData
|
3
|
+
attr_reader :pointer, :type, :format
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@pointer = nil
|
7
|
+
@type = nil
|
8
|
+
@format = DataFormat.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def inspect
|
12
|
+
"#<#{self.class}:0x#{(hash*2).to_s(16)} type=#{type.inspect} valuelen=#{valuelen} indicator=#{indicator}>"
|
13
|
+
end
|
14
|
+
|
15
|
+
def char_pointer!(size = 256)
|
16
|
+
@type = :char
|
17
|
+
@pointer = FFI::MemoryPointer.new(@type, size)
|
18
|
+
end
|
19
|
+
|
20
|
+
def int_pointer!
|
21
|
+
@type = :int
|
22
|
+
@pointer = FFI::MemoryPointer.new(@type)
|
23
|
+
end
|
24
|
+
|
25
|
+
def double_pointer!
|
26
|
+
@type = :double
|
27
|
+
@pointer = FFI::MemoryPointer.new(@type)
|
28
|
+
end
|
29
|
+
|
30
|
+
def valuelen_pointer
|
31
|
+
@valuelen_ptr ||= FFI::MemoryPointer.new(:int)
|
32
|
+
end
|
33
|
+
|
34
|
+
def indicator_pointer
|
35
|
+
@indicator_ptr ||= FFI::MemoryPointer.new(:int)
|
36
|
+
end
|
37
|
+
|
38
|
+
def valuelen
|
39
|
+
valuelen_pointer.read_int
|
40
|
+
end
|
41
|
+
|
42
|
+
def indicator
|
43
|
+
indicator_pointer.read_int
|
44
|
+
end
|
45
|
+
|
46
|
+
def value
|
47
|
+
case @type
|
48
|
+
when :int
|
49
|
+
@pointer.read_int
|
50
|
+
when :char
|
51
|
+
@pointer.get_bytes(0, valuelen - 1)
|
52
|
+
when :double
|
53
|
+
@pointer.read_double
|
54
|
+
else
|
55
|
+
raise Error, "uknown type #{type}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Sybase
|
2
|
+
|
3
|
+
# typedef struct _cs_datafmt
|
4
|
+
# {
|
5
|
+
# CS_CHAR name[CS_MAX_NAME]; // CS_MAX_CHAR if >= Sybase 15
|
6
|
+
# CS_INT namelen;
|
7
|
+
# CS_INT datatype;
|
8
|
+
# CS_INT format;
|
9
|
+
# CS_INT maxlength;
|
10
|
+
# CS_INT scale;
|
11
|
+
# CS_INT precision;
|
12
|
+
# CS_INT status;
|
13
|
+
# CS_INT count;
|
14
|
+
# CS_INT usertype;
|
15
|
+
# CS_LOCALE *locale;
|
16
|
+
# } CS_DATAFMT;
|
17
|
+
|
18
|
+
|
19
|
+
class DataFormat < FFI::Struct
|
20
|
+
attr_accessor :ruby_type
|
21
|
+
|
22
|
+
layout :name, [:char, CS_MAX_CHAR],
|
23
|
+
:namelen, :int,
|
24
|
+
:datatype, :int,
|
25
|
+
:format, :int,
|
26
|
+
:maxlength, :int,
|
27
|
+
:scale, :int,
|
28
|
+
:precision, :int,
|
29
|
+
:status, :int,
|
30
|
+
:count, :int,
|
31
|
+
:usertype, :int,
|
32
|
+
:locale, :pointer
|
33
|
+
|
34
|
+
INTS = [:namelen, :datatype, :format, :maxlength, :scale, :precision, :status, :count, :usertype]
|
35
|
+
|
36
|
+
def reset!
|
37
|
+
INTS.each { |key| self[key] = 0 }
|
38
|
+
end
|
39
|
+
|
40
|
+
def inspect
|
41
|
+
"#<%s name=%s namelen=%d datatype=%d format=%d maxlength=%d scale=%d precision=%d status=%d count=%d usertype=%d locale=%s address=%x>" % [
|
42
|
+
self.class.name,
|
43
|
+
name.inspect,
|
44
|
+
self[:namelen],
|
45
|
+
self[:datatype],
|
46
|
+
self[:format],
|
47
|
+
self[:maxlength],
|
48
|
+
self[:scale],
|
49
|
+
self[:precision],
|
50
|
+
self[:status],
|
51
|
+
self[:count],
|
52
|
+
self[:usertype],
|
53
|
+
self[:locale].inspect,
|
54
|
+
to_ptr.address
|
55
|
+
]
|
56
|
+
end
|
57
|
+
|
58
|
+
def name
|
59
|
+
self[:name].to_s
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Sybase
|
2
|
+
class Message < FFI::Struct
|
3
|
+
def severity
|
4
|
+
(self[:severity] >> 8) & 0xff
|
5
|
+
end
|
6
|
+
|
7
|
+
def number
|
8
|
+
self[:msgnumber] & 0xff
|
9
|
+
end
|
10
|
+
|
11
|
+
def origin
|
12
|
+
(self[:msgnumber]) >> 16 & 0xff
|
13
|
+
end
|
14
|
+
|
15
|
+
def layer
|
16
|
+
(self[:msgnumber] >> 24) & 0x44
|
17
|
+
end
|
18
|
+
|
19
|
+
def inspect
|
20
|
+
"#<%s text=%s severity=%d number=%d origin=%d layer=%d>" % [self.class.name, text.inspect, severity, number, origin, layer]
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
"%s (severity=%d number=%d origin=%d layer=%d)" % [text, severity, number, origin, layer]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Sybase
|
2
|
+
# typedef struct _cs_servermsg
|
3
|
+
# {
|
4
|
+
# CS_MSGNUM msgnumber;
|
5
|
+
# CS_INT state;
|
6
|
+
# CS_INT severity;
|
7
|
+
# CS_CHAR text[CS_MAX_MSG];
|
8
|
+
# CS_INT textlen;
|
9
|
+
# CS_CHAR svrname[CS_MAX_CHAR];
|
10
|
+
# CS_INT svrnlen;
|
11
|
+
# CS_CHAR proc[CS_MAX_CHAR];
|
12
|
+
# CS_INT proclen;
|
13
|
+
# CS_INT line;
|
14
|
+
# CS_INT status;
|
15
|
+
# CS_BYTE sqlstate[CS_SQLSTATE_SIZE];
|
16
|
+
# CS_INT sqlstatelen;
|
17
|
+
# } CS_SERVERMSG;
|
18
|
+
|
19
|
+
class ServerMessage < Message
|
20
|
+
layout :msgnumber, :uint,
|
21
|
+
:state, :int,
|
22
|
+
:severity, :int,
|
23
|
+
:text, [:char, CS_MAX_MSG],
|
24
|
+
:textlen, :int,
|
25
|
+
:svrname, [:char, CS_MAX_CHAR],
|
26
|
+
:svrnlen, :int,
|
27
|
+
:proc, [:char, CS_MAX_CHAR],
|
28
|
+
:proclen, :int,
|
29
|
+
:line, :int,
|
30
|
+
:status, :int,
|
31
|
+
:sqlstate, [:uchar, CS_SQLSTATE_SIZE],
|
32
|
+
:sqlstatelen, :int
|
33
|
+
|
34
|
+
def text
|
35
|
+
self[:text].to_s.chomp
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ffi-sybase
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jari Bakken
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-03-19 00:00:00 +01:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: ffi
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.6.3
|
25
|
+
type: :runtime
|
26
|
+
version_requirements: *id001
|
27
|
+
description: Ruby/FFI bindings for Sybase's Open Client library.
|
28
|
+
email:
|
29
|
+
- jari.bakken@gmail.com
|
30
|
+
executables: []
|
31
|
+
|
32
|
+
extensions: []
|
33
|
+
|
34
|
+
extra_rdoc_files: []
|
35
|
+
|
36
|
+
files:
|
37
|
+
- .gitignore
|
38
|
+
- Gemfile
|
39
|
+
- LICENSE
|
40
|
+
- README
|
41
|
+
- Rakefile
|
42
|
+
- TODO
|
43
|
+
- examples/sp_who.rb
|
44
|
+
- ffi-sybase.gemspec
|
45
|
+
- lib/ffi-sybase.rb
|
46
|
+
- lib/sybase/command.rb
|
47
|
+
- lib/sybase/connection.rb
|
48
|
+
- lib/sybase/constants.rb
|
49
|
+
- lib/sybase/context.rb
|
50
|
+
- lib/sybase/lib.rb
|
51
|
+
- lib/sybase/structs/client_message.rb
|
52
|
+
- lib/sybase/structs/column_data.rb
|
53
|
+
- lib/sybase/structs/data_format.rb
|
54
|
+
- lib/sybase/structs/message.rb
|
55
|
+
- lib/sybase/structs/server_message.rb
|
56
|
+
- lib/sybase/version.rb
|
57
|
+
has_rdoc: true
|
58
|
+
homepage: http://github.com/jarib/ffi-sybase
|
59
|
+
licenses: []
|
60
|
+
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: "0"
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: "0"
|
78
|
+
requirements: []
|
79
|
+
|
80
|
+
rubyforge_project: ffi-sybase
|
81
|
+
rubygems_version: 1.5.2
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: Ruby/FFI bindings for Sybase OCS
|
85
|
+
test_files: []
|
86
|
+
|