ffi-sybase 0.0.1 → 0.0.2
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/TODO +4 -4
- data/examples/{sp_who.rb → low_level.rb} +10 -7
- data/examples/simple.rb +13 -0
- data/lib/ffi-sybase.rb +1 -0
- data/lib/sybase/client.rb +116 -0
- data/lib/sybase/command.rb +17 -21
- data/lib/sybase/connection.rb +12 -38
- data/lib/sybase/constants.rb +1 -0
- data/lib/sybase/version.rb +1 -1
- metadata +7 -6
data/TODO
CHANGED
@@ -1,20 +1,23 @@
|
|
1
1
|
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
2
|
-
require 'pp'
|
3
2
|
require 'ffi-sybase'
|
4
|
-
|
3
|
+
require "pp"
|
5
4
|
unless ARGV.size == 3
|
6
|
-
abort("USAGE: #{$PROGRAM_NAME} <
|
5
|
+
abort("USAGE: #{$PROGRAM_NAME} <db1,db2> <user> <pass>")
|
7
6
|
end
|
8
7
|
|
9
|
-
|
8
|
+
dbs, user, pass = *ARGV
|
10
9
|
|
11
10
|
Sybase::Context.new do |ctx|
|
12
11
|
ctx.callbacks.library { |message| puts "library : #{message}" }
|
13
12
|
ctx.callbacks.client { |message| puts "client : #{message}" }
|
14
13
|
ctx.callbacks.server { |message| puts "server : #{message}" }
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
connections = []
|
16
|
+
dbs.split(',').each do |db|
|
17
|
+
connections << Sybase::Connection.new(ctx, :username => user, :password => pass).connect(db)
|
18
|
+
end
|
19
|
+
|
20
|
+
connections.each do |conn|
|
21
|
+
p Sybase::Command.new(conn, "sp_who").execute.size
|
19
22
|
end
|
20
23
|
end
|
data/examples/simple.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
2
|
+
require 'ffi-sybase'
|
3
|
+
require "pp"
|
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::Client.new(db, :username => user, :password => pass) do |client|
|
12
|
+
pp client.execute "sp_who"
|
13
|
+
end
|
data/lib/ffi-sybase.rb
CHANGED
@@ -0,0 +1,116 @@
|
|
1
|
+
module Sybase
|
2
|
+
class Client
|
3
|
+
|
4
|
+
attr_reader :context, :connection, :messages
|
5
|
+
|
6
|
+
#
|
7
|
+
# Create a new client for the given database name
|
8
|
+
#
|
9
|
+
|
10
|
+
def initialize(db, opts = {})
|
11
|
+
@context = Context.new
|
12
|
+
|
13
|
+
@messages = Messages.new # TODO: listener?
|
14
|
+
@context.callbacks.library { |msg| @messages.on_message :library, msg }
|
15
|
+
@context.callbacks.client { |msg| @messages.on_message :client, msg }
|
16
|
+
@context.callbacks.server { |msg| @messages.on_message :server, msg }
|
17
|
+
|
18
|
+
|
19
|
+
@connection = Connection.new(@context,
|
20
|
+
:username => opts.fetch(:username) { raise ArgumentError, "no :username given" },
|
21
|
+
:password => opts.fetch(:password) { raise ArgumentError, "no :password given" },
|
22
|
+
:appname => opts.fetch(:appname) { "#{self.class} #{RUBY_DESCRIPTION}" },
|
23
|
+
:hostname => opts.fetch(:hostname) { Socket.gethostname }
|
24
|
+
)
|
25
|
+
|
26
|
+
@connection.connect db.to_s
|
27
|
+
|
28
|
+
if block_given?
|
29
|
+
begin
|
30
|
+
yield self
|
31
|
+
ensure
|
32
|
+
close
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def execute(sql)
|
38
|
+
$stderr.puts "executing: #{sql.inspect}" if $DEBUG
|
39
|
+
build_result { |res| res.affected, res.data = Command.new(@connection, sql).execute }
|
40
|
+
end
|
41
|
+
|
42
|
+
def close
|
43
|
+
@connection.close if @connection
|
44
|
+
@context.exit
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def build_result
|
50
|
+
res = ResultBuilder.new
|
51
|
+
@messages.reset!
|
52
|
+
|
53
|
+
res.start
|
54
|
+
begin
|
55
|
+
yield res
|
56
|
+
ensure
|
57
|
+
res.stop
|
58
|
+
res.messages = @messages.messages
|
59
|
+
end
|
60
|
+
|
61
|
+
res
|
62
|
+
end
|
63
|
+
|
64
|
+
class ResultBuilder
|
65
|
+
attr_accessor :messages, :affected, :data
|
66
|
+
|
67
|
+
def start
|
68
|
+
@started_at = Time.now
|
69
|
+
end
|
70
|
+
|
71
|
+
def stop
|
72
|
+
@elapsed = Time.now - @started_at
|
73
|
+
end
|
74
|
+
|
75
|
+
def <<(res)
|
76
|
+
(@results ||= []) << res
|
77
|
+
end
|
78
|
+
|
79
|
+
def result_set
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class Messages
|
85
|
+
def initialize
|
86
|
+
# TODO: order is important
|
87
|
+
@messages = Hash.new { |hash, key| hash[key] = [] }
|
88
|
+
end
|
89
|
+
|
90
|
+
def any?
|
91
|
+
@messages.any?
|
92
|
+
end
|
93
|
+
|
94
|
+
def on_message(type, msg)
|
95
|
+
@messages[type] << msg.to_s
|
96
|
+
end
|
97
|
+
|
98
|
+
def messages_for(type)
|
99
|
+
@messages[type]
|
100
|
+
end
|
101
|
+
|
102
|
+
def messages
|
103
|
+
@messages.values.flatten
|
104
|
+
end
|
105
|
+
|
106
|
+
def reset!
|
107
|
+
@messages.clear
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_s
|
111
|
+
messages.join("\n")
|
112
|
+
end
|
113
|
+
|
114
|
+
end # Messages
|
115
|
+
end # Client
|
116
|
+
end # Sybase
|
data/lib/sybase/command.rb
CHANGED
@@ -37,8 +37,14 @@ module Sybase
|
|
37
37
|
Lib.check Lib.ct_send(to_ptr), "ct_send failed"
|
38
38
|
end
|
39
39
|
|
40
|
-
|
41
|
-
|
40
|
+
CANCELS = {
|
41
|
+
:current => CS_CANCEL_CURRENT,
|
42
|
+
:all => CS_CANCEL_CURRENT
|
43
|
+
}
|
44
|
+
|
45
|
+
def cancel(type = :current)
|
46
|
+
code = CANCELS.fetch(type) { raise ArgumentError, "unknown type: #{type.inspect}, expected #{CANCELS.keys.inspect}" }
|
47
|
+
Lib.ct_cancel(nil, to_ptr, code)
|
42
48
|
end
|
43
49
|
|
44
50
|
COMMAND_RESULTS = {
|
@@ -54,9 +60,7 @@ module Sybase
|
|
54
60
|
|
55
61
|
def results
|
56
62
|
intptr = FFI::MemoryPointer.new(:int)
|
57
|
-
|
58
|
-
state = :initial
|
59
|
-
|
63
|
+
rows_affected = -1
|
60
64
|
returned = []
|
61
65
|
|
62
66
|
while successful? intptr
|
@@ -64,11 +68,12 @@ module Sybase
|
|
64
68
|
restype = COMMAND_RESULTS[restype] || restype
|
65
69
|
|
66
70
|
case restype
|
67
|
-
when :
|
68
|
-
|
69
|
-
|
71
|
+
when :done # results completely processed
|
72
|
+
rows_affected = result_info(CS_ROW_COUNT)
|
73
|
+
when :succeed
|
74
|
+
# no row - e.g. insert/update
|
70
75
|
when :fail
|
71
|
-
|
76
|
+
raise Error, "ct_results failed (#{restype.inspect})"
|
72
77
|
when :row,
|
73
78
|
:cursor,
|
74
79
|
:param,
|
@@ -76,26 +81,17 @@ module Sybase
|
|
76
81
|
:status
|
77
82
|
|
78
83
|
columns, rows = fetch_data
|
79
|
-
returned <<
|
84
|
+
returned << {:columns => columns, :rows => rows}
|
80
85
|
else
|
81
|
-
|
86
|
+
raise Error, "unknown result (#{restype.inspect})"
|
82
87
|
end
|
83
88
|
|
84
89
|
# check context timeout?
|
85
90
|
end
|
86
91
|
|
87
|
-
returned
|
92
|
+
[rows_affected, returned]
|
88
93
|
end
|
89
94
|
|
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
95
|
def successful?(intptr)
|
100
96
|
@return_code = Lib.ct_results(to_ptr, intptr)
|
101
97
|
@return_code == CS_SUCCEED
|
data/lib/sybase/connection.rb
CHANGED
@@ -21,10 +21,6 @@ module Sybase
|
|
21
21
|
self[key] = value
|
22
22
|
end
|
23
23
|
|
24
|
-
unless opts.has_key?(:hostname)
|
25
|
-
self[:hostname] = Socket.gethostname
|
26
|
-
end
|
27
|
-
|
28
24
|
if block_given?
|
29
25
|
begin
|
30
26
|
yield self
|
@@ -65,39 +61,11 @@ module Sybase
|
|
65
61
|
end
|
66
62
|
end
|
67
63
|
|
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
64
|
def connect(server)
|
99
65
|
server = server.to_s
|
100
66
|
Lib.check Lib.ct_connect(@ptr, server, server.bytesize), "connect(#{server.inspect}) failed"
|
67
|
+
|
68
|
+
self
|
101
69
|
end
|
102
70
|
|
103
71
|
def to_ptr
|
@@ -106,16 +74,22 @@ module Sybase
|
|
106
74
|
|
107
75
|
private
|
108
76
|
|
77
|
+
def property_type_for(key)
|
78
|
+
PROPERTIES.fetch(key) {
|
79
|
+
raise ArgumentError, "invalid option: #{key.inspect}, expected one of #{PROPERTIES.keys.inspect}"
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
109
83
|
def set_string_property(property, string)
|
110
84
|
Lib.check Lib.ct_con_props(@ptr, CS_SET, property, string.to_s, CS_NULLTERM, nil), "ct_con_prop(#{property} => #{string.inspect}) failed"
|
111
85
|
end
|
112
86
|
|
113
87
|
def get_string_property(property)
|
114
|
-
FFI::MemoryPointer.new(:
|
88
|
+
FFI::MemoryPointer.new(:char, CS_MAX_CHAR) { |ptr| get_property(property, ptr, CS_MAX_CHAR) }.get_bytes(0, CS_MAX_CHAR)
|
115
89
|
end
|
116
90
|
|
117
91
|
def get_int_property(property)
|
118
|
-
FFI::MemoryPointer.new(:int) { |ptr| get_property(property, ptr) }.read_int
|
92
|
+
FFI::MemoryPointer.new(:int) { |ptr| get_property(property, ptr, ptr.size) }.read_int
|
119
93
|
end
|
120
94
|
|
121
95
|
def set_int_property(property, int)
|
@@ -125,8 +99,8 @@ module Sybase
|
|
125
99
|
Lib.check Lib.ct_con_props(@ptr, CS_SET, property, ptr, CS_UNUSED, nil), "ct_con_prop(#{property} => #{int.inspect}) failed"
|
126
100
|
end
|
127
101
|
|
128
|
-
def get_property(property, ptr)
|
129
|
-
Lib.check Lib.ct_con_props(@ptr, CS_GET, property, ptr,
|
102
|
+
def get_property(property, ptr, length)
|
103
|
+
Lib.check Lib.ct_con_props(@ptr, CS_GET, property, ptr, length, nil), "ct_con_prop(CS_GET, #{property}) failed"
|
130
104
|
end
|
131
105
|
end # Connection
|
132
106
|
end # Sybase
|
data/lib/sybase/constants.rb
CHANGED
data/lib/sybase/version.rb
CHANGED
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: ffi-sybase
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Jari Bakken
|
@@ -10,8 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
14
|
-
default_executable:
|
13
|
+
date: 2011-04-11 00:00:00 Z
|
15
14
|
dependencies:
|
16
15
|
- !ruby/object:Gem::Dependency
|
17
16
|
name: ffi
|
@@ -40,9 +39,11 @@ files:
|
|
40
39
|
- README
|
41
40
|
- Rakefile
|
42
41
|
- TODO
|
43
|
-
- examples/
|
42
|
+
- examples/low_level.rb
|
43
|
+
- examples/simple.rb
|
44
44
|
- ffi-sybase.gemspec
|
45
45
|
- lib/ffi-sybase.rb
|
46
|
+
- lib/sybase/client.rb
|
46
47
|
- lib/sybase/command.rb
|
47
48
|
- lib/sybase/connection.rb
|
48
49
|
- lib/sybase/constants.rb
|
@@ -54,7 +55,6 @@ files:
|
|
54
55
|
- lib/sybase/structs/message.rb
|
55
56
|
- lib/sybase/structs/server_message.rb
|
56
57
|
- lib/sybase/version.rb
|
57
|
-
has_rdoc: true
|
58
58
|
homepage: http://github.com/jarib/ffi-sybase
|
59
59
|
licenses: []
|
60
60
|
|
@@ -78,9 +78,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
78
|
requirements: []
|
79
79
|
|
80
80
|
rubyforge_project: ffi-sybase
|
81
|
-
rubygems_version: 1.
|
81
|
+
rubygems_version: 1.7.2
|
82
82
|
signing_key:
|
83
83
|
specification_version: 3
|
84
84
|
summary: Ruby/FFI bindings for Sybase OCS
|
85
85
|
test_files: []
|
86
86
|
|
87
|
+
has_rdoc:
|