ffi-sybase 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/TODO CHANGED
@@ -1,10 +1,10 @@
1
1
  soon:
2
2
 
3
- - Sybase::Client wraps Context/Connection
4
3
  - simple specs
5
-
4
+ - improve result handling
5
+
6
6
  maybe:
7
-
7
+
8
8
  - Async / EM
9
-
9
+
10
10
 
@@ -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} <db> <user> <pass>")
5
+ abort("USAGE: #{$PROGRAM_NAME} <db1,db2> <user> <pass>")
7
6
  end
8
7
 
9
- db, user, pass = *ARGV
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
- Sybase::Connection.new(ctx, :username => user, :password => pass) do |conn|
17
- conn.connect db
18
- pp Sybase::Command.new(conn, "sp_who").execute
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
@@ -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
@@ -18,3 +18,4 @@ require "sybase/lib"
18
18
  require "sybase/context"
19
19
  require "sybase/connection"
20
20
  require "sybase/command"
21
+ require "sybase/client"
@@ -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
@@ -37,8 +37,14 @@ module Sybase
37
37
  Lib.check Lib.ct_send(to_ptr), "ct_send failed"
38
38
  end
39
39
 
40
- def cancel
41
- Lib.ct_cancel(nil, to_ptr, CS_CANCEL_CURRENT)
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 :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))
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
- returned << Result.new(restype, nil, result_info(CS_ROW_COUNT), result_info(CS_TRANS_STATE))
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 << Result.new(restype, :columns => columns, :rows => rows)
84
+ returned << {:columns => columns, :rows => rows}
80
85
  else
81
- returned << Result.new(restype, nil, result_info(CS_ROW_COUNT), result_info(CS_TRANS_STATE))
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
@@ -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(:string) { |ptr| get_property(property, ptr) }.read_string
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, CS_UNUSED, nil), "ct_con_prop(CS_GET, #{property}) failed"
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
@@ -33,6 +33,7 @@ module Sybase
33
33
  CS_FAIL = 0
34
34
  CS_UNUSED = -99999
35
35
  CS_CANCEL_CURRENT = 6000
36
+ CS_CANCEL_ALL = 6001
36
37
  CS_FMT_UNUSED = 0
37
38
  CS_FMT_NULLTERM = 1
38
39
 
@@ -1,3 +1,3 @@
1
1
  module Sybase
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
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.1
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-03-19 00:00:00 +01:00
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/sp_who.rb
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.5.2
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: