postgres-pr-encoding 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/examples/client.rb +34 -0
- data/examples/og/test.rb +50 -0
- data/examples/server.rb +12 -0
- data/examples/test_connection.rb +18 -0
- data/lib/binary_reader.rb +120 -0
- data/lib/binary_writer.rb +100 -0
- data/lib/buffer.rb +97 -0
- data/lib/byteorder.rb +32 -0
- data/lib/pg.rb +4 -0
- data/lib/postgres-pr/connection.rb +177 -0
- data/lib/postgres-pr/message.rb +542 -0
- data/lib/postgres-pr/pg-compat.rb +174 -0
- data/lib/postgres-pr/postgres-compat.rb +170 -0
- data/lib/postgres-pr/typeconv/TC_conv.rb +18 -0
- data/lib/postgres-pr/typeconv/array.rb +46 -0
- data/lib/postgres-pr/typeconv/bytea.rb +28 -0
- data/lib/postgres-pr/typeconv/conv.rb +5 -0
- data/lib/postgres-pr/version.rb +3 -0
- data/lib/postgres.rb +8 -0
- data/test/TC_message.rb +103 -0
- metadata +63 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d0abce872430818917834a4090ee43ad100f65dd
|
4
|
+
data.tar.gz: 7121f1313924c2ac97160a3a6a0983812de2df77
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 982e80829402a1b479e8d2f668abfacc990d0a484cfa3b1abfb735c2bd90cb9eb004c6baccafa5853342dacac2e332cf3813346f87cc6a73138bdac9203e9b22
|
7
|
+
data.tar.gz: 3000ebc135b2c720111116b45492acd0d8052c578ae981b57620c52421ba1785b387dfaf717dc4729cb3a32f0a5e1a418e43c8e96d1d236f7ded8de5d7a04312
|
data/examples/client.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
$LOAD_PATH.unshift "../lib"
|
2
|
+
require 'postgres-pr/message'
|
3
|
+
require 'socket'
|
4
|
+
include PostgresPR
|
5
|
+
|
6
|
+
s = UNIXSocket.new(ARGV.shift || "/tmp/.s.PGSQL.5432")
|
7
|
+
|
8
|
+
msg = StartupMessage.new(196608, "user" => "mneumann", "database" => "mneumann")
|
9
|
+
s << msg.dump
|
10
|
+
|
11
|
+
Thread.start(s) { |s|
|
12
|
+
sleep 2
|
13
|
+
s << Query.new("drop table test").dump
|
14
|
+
s << Query.new("create table test (i int, v varchar(100))").dump
|
15
|
+
s << Parse.new("insert into test (i, v) values ($1, $2)", "blah").dump
|
16
|
+
s << Query.new("EXECUTE blah(1, 'hallo')").dump
|
17
|
+
|
18
|
+
while not (line = gets.chomp).empty?
|
19
|
+
s << Query.new(line).dump
|
20
|
+
end
|
21
|
+
exit
|
22
|
+
}
|
23
|
+
|
24
|
+
loop do
|
25
|
+
msg = Message.read(s)
|
26
|
+
p msg
|
27
|
+
|
28
|
+
case msg
|
29
|
+
when AuthentificationOk
|
30
|
+
p "OK"
|
31
|
+
when ErrorResponse
|
32
|
+
p "FAILED"
|
33
|
+
end
|
34
|
+
end
|
data/examples/og/test.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
$LOAD_PATH.unshift '../../lib'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'og'
|
4
|
+
require 'glue/logger'
|
5
|
+
|
6
|
+
$DBG = true
|
7
|
+
|
8
|
+
class User
|
9
|
+
end
|
10
|
+
|
11
|
+
class Comment
|
12
|
+
prop_accessor :body, String
|
13
|
+
belongs_to :user, User
|
14
|
+
end
|
15
|
+
|
16
|
+
class User
|
17
|
+
prop_accessor :name, String
|
18
|
+
has_many :comments, Comment
|
19
|
+
end
|
20
|
+
|
21
|
+
if __FILE__ == $0
|
22
|
+
config = {
|
23
|
+
:address => "localhost",
|
24
|
+
:database => "mneumann",
|
25
|
+
:backend => "psql",
|
26
|
+
:user => "mneumann",
|
27
|
+
:password => "",
|
28
|
+
:connection_count => 1
|
29
|
+
}
|
30
|
+
$log = Logger.new(STDERR)
|
31
|
+
$og = Og::Database.new(config)
|
32
|
+
|
33
|
+
$og.get_connection
|
34
|
+
|
35
|
+
u1 = User.new
|
36
|
+
u1.name = "Michael Neumann"
|
37
|
+
u1.save!
|
38
|
+
|
39
|
+
u2 = User.new
|
40
|
+
u2.name = "John User"
|
41
|
+
u2.save!
|
42
|
+
|
43
|
+
c1 = Comment.new
|
44
|
+
c1.body = "og test"
|
45
|
+
c1.user = u1
|
46
|
+
c1.save!
|
47
|
+
|
48
|
+
p User.all
|
49
|
+
p Comment.all
|
50
|
+
end
|
data/examples/server.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
$LOAD_PATH.unshift '../lib'
|
2
|
+
require 'postgres-pr/connection'
|
3
|
+
|
4
|
+
conn = PostgresPR::Connection.new('mneumann', 'mneumann', nil, 'unix:/var/run/postgresql/.s.PGSQL.5432')
|
5
|
+
p conn.query("DROP TABLE test") rescue nil
|
6
|
+
p conn.query("CREATE TABLE test (a VARCHAR(100))")
|
7
|
+
p conn.query("INSERT INTO test VALUES ('hallo')")
|
8
|
+
p conn.query("INSERT INTO test VALUES ('leute')")
|
9
|
+
conn.query("COMMIT")
|
10
|
+
|
11
|
+
conn.query("BEGIN")
|
12
|
+
10000.times do |i|
|
13
|
+
p i
|
14
|
+
conn.query("INSERT INTO test VALUES ('#{i}')")
|
15
|
+
end
|
16
|
+
conn.query("COMMIT")
|
17
|
+
|
18
|
+
p conn.query("SELECT * FROM test")
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'byteorder'
|
2
|
+
|
3
|
+
# This mixin solely depends on method read(n), which must be defined
|
4
|
+
# in the class/module where you mixin this module.
|
5
|
+
module BinaryReaderMixin
|
6
|
+
|
7
|
+
# == 8 bit
|
8
|
+
|
9
|
+
# no byteorder for 8 bit!
|
10
|
+
|
11
|
+
def read_word8
|
12
|
+
ru(1, 'C')
|
13
|
+
end
|
14
|
+
|
15
|
+
def read_int8
|
16
|
+
ru(1, 'c')
|
17
|
+
end
|
18
|
+
|
19
|
+
alias read_byte read_word8
|
20
|
+
|
21
|
+
# == 16 bit
|
22
|
+
|
23
|
+
# === Unsigned
|
24
|
+
|
25
|
+
def read_word16_native
|
26
|
+
ru(2, 'S')
|
27
|
+
end
|
28
|
+
|
29
|
+
def read_word16_little
|
30
|
+
ru(2, 'v')
|
31
|
+
end
|
32
|
+
|
33
|
+
def read_word16_big
|
34
|
+
ru(2, 'n')
|
35
|
+
end
|
36
|
+
|
37
|
+
# === Signed
|
38
|
+
|
39
|
+
def read_int16_native
|
40
|
+
ru(2, 's')
|
41
|
+
end
|
42
|
+
|
43
|
+
def read_int16_little
|
44
|
+
# swap bytes if native=big (but we want little)
|
45
|
+
ru_swap(2, 's', ByteOrder::Big)
|
46
|
+
end
|
47
|
+
|
48
|
+
def read_int16_big
|
49
|
+
# swap bytes if native=little (but we want big)
|
50
|
+
ru_swap(2, 's', ByteOrder::Little)
|
51
|
+
end
|
52
|
+
|
53
|
+
# == 32 bit
|
54
|
+
|
55
|
+
# === Unsigned
|
56
|
+
|
57
|
+
def read_word32_native
|
58
|
+
ru(4, 'L')
|
59
|
+
end
|
60
|
+
|
61
|
+
def read_word32_little
|
62
|
+
ru(4, 'V')
|
63
|
+
end
|
64
|
+
|
65
|
+
def read_word32_big
|
66
|
+
ru(4, 'N')
|
67
|
+
end
|
68
|
+
|
69
|
+
# === Signed
|
70
|
+
|
71
|
+
def read_int32_native
|
72
|
+
ru(4, 'l')
|
73
|
+
end
|
74
|
+
|
75
|
+
def read_int32_little
|
76
|
+
# swap bytes if native=big (but we want little)
|
77
|
+
ru_swap(4, 'l', ByteOrder::Big)
|
78
|
+
end
|
79
|
+
|
80
|
+
def read_int32_big
|
81
|
+
# swap bytes if native=little (but we want big)
|
82
|
+
ru_swap(4, 'l', ByteOrder::Little)
|
83
|
+
end
|
84
|
+
|
85
|
+
# == Aliases
|
86
|
+
|
87
|
+
alias read_uint8 read_word8
|
88
|
+
|
89
|
+
# add some short-cut functions
|
90
|
+
%w(word16 int16 word32 int32).each do |typ|
|
91
|
+
alias_method "read_#{typ}_network", "read_#{typ}_big"
|
92
|
+
end
|
93
|
+
|
94
|
+
{:word16 => :uint16, :word32 => :uint32}.each do |old, new|
|
95
|
+
['_native', '_little', '_big', '_network'].each do |bo|
|
96
|
+
alias_method "read_#{new}#{bo}", "read_#{old}#{bo}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# read exactly n characters, otherwise raise an exception.
|
101
|
+
def readn(n)
|
102
|
+
str = read(n)
|
103
|
+
raise "couldn't read #{n} characters" if str.nil? or str.size != n
|
104
|
+
str
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
# shortcut method for readn+unpack
|
110
|
+
def ru(size, template)
|
111
|
+
readn(size).unpack(template).first
|
112
|
+
end
|
113
|
+
|
114
|
+
# same as method +ru+, but swap bytes if native byteorder == _byteorder_
|
115
|
+
def ru_swap(size, template, byteorder)
|
116
|
+
str = readn(size)
|
117
|
+
str.reverse! if ByteOrder.byteorder == byteorder
|
118
|
+
str.unpack(template).first
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'byteorder'
|
2
|
+
|
3
|
+
module BinaryWriterMixin
|
4
|
+
|
5
|
+
# == 8 bit
|
6
|
+
|
7
|
+
# no byteorder for 8 bit!
|
8
|
+
|
9
|
+
def write_word8(val)
|
10
|
+
pw(val, 'C')
|
11
|
+
end
|
12
|
+
|
13
|
+
def write_int8(val)
|
14
|
+
pw(val, 'c')
|
15
|
+
end
|
16
|
+
|
17
|
+
alias write_byte write_word8
|
18
|
+
|
19
|
+
# == 16 bit
|
20
|
+
|
21
|
+
# === Unsigned
|
22
|
+
|
23
|
+
def write_word16_native(val)
|
24
|
+
pw(val, 'S')
|
25
|
+
end
|
26
|
+
|
27
|
+
def write_word16_little(val)
|
28
|
+
str = [val].pack('S')
|
29
|
+
str.reverse! if ByteOrder.network? # swap bytes as native=network (and we want little)
|
30
|
+
write(str)
|
31
|
+
end
|
32
|
+
|
33
|
+
def write_word16_network(val)
|
34
|
+
str = [val].pack('S')
|
35
|
+
str.reverse! if ByteOrder.little? # swap bytes as native=little (and we want network)
|
36
|
+
write(str)
|
37
|
+
end
|
38
|
+
|
39
|
+
# === Signed
|
40
|
+
|
41
|
+
def write_int16_native(val)
|
42
|
+
pw(val, 's')
|
43
|
+
end
|
44
|
+
|
45
|
+
def write_int16_little(val)
|
46
|
+
pw(val, 'v')
|
47
|
+
end
|
48
|
+
|
49
|
+
def write_int16_network(val)
|
50
|
+
pw(val, 'n')
|
51
|
+
end
|
52
|
+
|
53
|
+
# == 32 bit
|
54
|
+
|
55
|
+
# === Unsigned
|
56
|
+
|
57
|
+
def write_word32_native(val)
|
58
|
+
pw(val, 'L')
|
59
|
+
end
|
60
|
+
|
61
|
+
def write_word32_little(val)
|
62
|
+
str = [val].pack('L')
|
63
|
+
str.reverse! if ByteOrder.network? # swap bytes as native=network (and we want little)
|
64
|
+
write(str)
|
65
|
+
end
|
66
|
+
|
67
|
+
def write_word32_network(val)
|
68
|
+
str = [val].pack('L')
|
69
|
+
str.reverse! if ByteOrder.little? # swap bytes as native=little (and we want network)
|
70
|
+
write(str)
|
71
|
+
end
|
72
|
+
|
73
|
+
# === Signed
|
74
|
+
|
75
|
+
def write_int32_native(val)
|
76
|
+
pw(val, 'l')
|
77
|
+
end
|
78
|
+
|
79
|
+
def write_int32_little(val)
|
80
|
+
pw(val, 'V')
|
81
|
+
end
|
82
|
+
|
83
|
+
def write_int32_network(val)
|
84
|
+
pw(val, 'N')
|
85
|
+
end
|
86
|
+
|
87
|
+
# add some short-cut functions
|
88
|
+
%w(word16 int16 word32 int32).each do |typ|
|
89
|
+
alias_method "write_#{typ}_big", "write_#{typ}_network"
|
90
|
+
end
|
91
|
+
|
92
|
+
# == Other methods
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
# shortcut for pack and write
|
97
|
+
def pw(val, template)
|
98
|
+
write([val].pack(template))
|
99
|
+
end
|
100
|
+
end
|
data/lib/buffer.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'binary_writer'
|
2
|
+
require 'binary_reader'
|
3
|
+
|
4
|
+
# Fixed size buffer.
|
5
|
+
class Buffer
|
6
|
+
|
7
|
+
class Error < RuntimeError; end
|
8
|
+
class EOF < Error; end
|
9
|
+
|
10
|
+
def self.from_string(str)
|
11
|
+
new(str)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.of_size(size)
|
15
|
+
raise ArgumentError if size < 0
|
16
|
+
new('#' * size)
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(content)
|
20
|
+
@size = content.size
|
21
|
+
@content = content
|
22
|
+
@position = 0
|
23
|
+
end
|
24
|
+
|
25
|
+
def size
|
26
|
+
@size
|
27
|
+
end
|
28
|
+
|
29
|
+
def position
|
30
|
+
@position
|
31
|
+
end
|
32
|
+
|
33
|
+
def position=(new_pos)
|
34
|
+
raise ArgumentError if new_pos < 0 or new_pos > @size
|
35
|
+
@position = new_pos
|
36
|
+
end
|
37
|
+
|
38
|
+
def at_end?
|
39
|
+
@position == @size
|
40
|
+
end
|
41
|
+
|
42
|
+
def content
|
43
|
+
@content
|
44
|
+
end
|
45
|
+
|
46
|
+
def read(n)
|
47
|
+
raise EOF, 'cannot read beyond the end of buffer' if @position + n > @size
|
48
|
+
str = @content[@position, n]
|
49
|
+
@position += n
|
50
|
+
str
|
51
|
+
end
|
52
|
+
|
53
|
+
def write(str)
|
54
|
+
sz = str.size
|
55
|
+
raise EOF, 'cannot write beyond the end of buffer' if @position + sz > @size
|
56
|
+
@content[@position, sz] = str
|
57
|
+
@position += sz
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
def copy_from_stream(stream, n)
|
62
|
+
raise ArgumentError if n < 0
|
63
|
+
while n > 0
|
64
|
+
str = stream.read(n)
|
65
|
+
write(str)
|
66
|
+
n -= str.size
|
67
|
+
end
|
68
|
+
raise if n < 0
|
69
|
+
end
|
70
|
+
|
71
|
+
NUL = "\000"
|
72
|
+
|
73
|
+
def write_cstring(cstr)
|
74
|
+
raise ArgumentError, "Invalid Ruby/cstring" if cstr.include?(NUL)
|
75
|
+
write(cstr)
|
76
|
+
write(NUL)
|
77
|
+
end
|
78
|
+
|
79
|
+
# returns a Ruby string without the trailing NUL character
|
80
|
+
def read_cstring
|
81
|
+
nul_pos = @content.index(NUL, @position)
|
82
|
+
raise Error, "no cstring found!" unless nul_pos
|
83
|
+
|
84
|
+
sz = nul_pos - @position
|
85
|
+
str = @content[@position, sz]
|
86
|
+
@position += sz + 1
|
87
|
+
return str
|
88
|
+
end
|
89
|
+
|
90
|
+
# read till the end of the buffer
|
91
|
+
def read_rest
|
92
|
+
read(self.size-@position)
|
93
|
+
end
|
94
|
+
|
95
|
+
include BinaryWriterMixin
|
96
|
+
include BinaryReaderMixin
|
97
|
+
end
|