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 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: