jeremyevans-postgres-pr 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- 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/postgres-pr/connection.rb +174 -0
- data/lib/postgres-pr/message.rb +544 -0
- data/lib/postgres-pr/postgres-compat.rb +140 -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 +26 -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 +72 -0
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
|
data/lib/byteorder.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module ByteOrder
|
2
|
+
Native = :Native
|
3
|
+
BigEndian = Big = Network = :BigEndian
|
4
|
+
LittleEndian = Little = :LittleEndian
|
5
|
+
|
6
|
+
# examines the byte order of the underlying machine
|
7
|
+
def byte_order
|
8
|
+
if [0x12345678].pack("L") == "\x12\x34\x56\x78"
|
9
|
+
BigEndian
|
10
|
+
else
|
11
|
+
LittleEndian
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
alias byteorder byte_order
|
16
|
+
|
17
|
+
def little_endian?
|
18
|
+
byte_order == LittleEndian
|
19
|
+
end
|
20
|
+
|
21
|
+
def big_endian?
|
22
|
+
byte_order == BigEndian
|
23
|
+
end
|
24
|
+
|
25
|
+
alias little? little_endian?
|
26
|
+
alias big? big_endian?
|
27
|
+
alias network? big_endian?
|
28
|
+
|
29
|
+
module_function :byte_order, :byteorder
|
30
|
+
module_function :little_endian?, :little?
|
31
|
+
module_function :big_endian?, :big?, :network?
|
32
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Michael Neumann
|
3
|
+
# Copyright:: (c) 2005 by Michael Neumann
|
4
|
+
# License:: Same as Ruby's or BSD
|
5
|
+
#
|
6
|
+
|
7
|
+
require 'postgres-pr/message'
|
8
|
+
require 'postgres-pr/version'
|
9
|
+
require 'uri'
|
10
|
+
require 'socket'
|
11
|
+
|
12
|
+
module PostgresPR
|
13
|
+
|
14
|
+
PROTO_VERSION = 3 << 16 #196608
|
15
|
+
|
16
|
+
class Connection
|
17
|
+
|
18
|
+
# A block which is called with the NoticeResponse object as parameter.
|
19
|
+
attr_accessor :notice_processor
|
20
|
+
|
21
|
+
#
|
22
|
+
# Returns one of the following statuses:
|
23
|
+
#
|
24
|
+
# PQTRANS_IDLE = 0 (connection idle)
|
25
|
+
# PQTRANS_INTRANS = 2 (idle, within transaction block)
|
26
|
+
# PQTRANS_INERROR = 3 (idle, within failed transaction)
|
27
|
+
# PQTRANS_UNKNOWN = 4 (cannot determine status)
|
28
|
+
#
|
29
|
+
# Not yet implemented is:
|
30
|
+
#
|
31
|
+
# PQTRANS_ACTIVE = 1 (command in progress)
|
32
|
+
#
|
33
|
+
def transaction_status
|
34
|
+
case @transaction_status
|
35
|
+
when ?I
|
36
|
+
0
|
37
|
+
when ?T
|
38
|
+
2
|
39
|
+
when ?E
|
40
|
+
3
|
41
|
+
else
|
42
|
+
4
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize(database, user, password=nil, uri = nil)
|
47
|
+
uri ||= DEFAULT_URI
|
48
|
+
|
49
|
+
@transaction_status = nil
|
50
|
+
@params = {}
|
51
|
+
establish_connection(uri)
|
52
|
+
|
53
|
+
@conn << StartupMessage.new(PROTO_VERSION, 'user' => user, 'database' => database).dump
|
54
|
+
|
55
|
+
loop do
|
56
|
+
msg = Message.read(@conn)
|
57
|
+
|
58
|
+
case msg
|
59
|
+
when AuthentificationClearTextPassword
|
60
|
+
raise PGError, "no password specified" if password.nil?
|
61
|
+
@conn << PasswordMessage.new(password).dump
|
62
|
+
|
63
|
+
when AuthentificationCryptPassword
|
64
|
+
raise PGError, "no password specified" if password.nil?
|
65
|
+
@conn << PasswordMessage.new(password.crypt(msg.salt)).dump
|
66
|
+
|
67
|
+
when AuthentificationMD5Password
|
68
|
+
raise PGError, "no password specified" if password.nil?
|
69
|
+
require 'digest/md5'
|
70
|
+
|
71
|
+
m = Digest::MD5.hexdigest(password + user)
|
72
|
+
m = Digest::MD5.hexdigest(m + msg.salt)
|
73
|
+
m = 'md5' + m
|
74
|
+
@conn << PasswordMessage.new(m).dump
|
75
|
+
|
76
|
+
when AuthentificationKerberosV4, AuthentificationKerberosV5, AuthentificationSCMCredential
|
77
|
+
raise PGError, "unsupported authentification"
|
78
|
+
|
79
|
+
when AuthentificationOk
|
80
|
+
when ErrorResponse
|
81
|
+
raise PGError, msg.field_values.join("\t")
|
82
|
+
when NoticeResponse
|
83
|
+
@notice_processor.call(msg) if @notice_processor
|
84
|
+
when ParameterStatus
|
85
|
+
@params[msg.key] = msg.value
|
86
|
+
when BackendKeyData
|
87
|
+
# TODO
|
88
|
+
#p msg
|
89
|
+
when ReadyForQuery
|
90
|
+
@transaction_status = msg.backend_transaction_status_indicator
|
91
|
+
break
|
92
|
+
else
|
93
|
+
raise PGError, "unhandled message type"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def close
|
99
|
+
raise(PGError, "connection already closed") if @conn.nil?
|
100
|
+
@conn.shutdown
|
101
|
+
@conn = nil
|
102
|
+
end
|
103
|
+
|
104
|
+
class Result
|
105
|
+
attr_accessor :rows, :fields, :cmd_tag
|
106
|
+
def initialize(rows=[], fields=[])
|
107
|
+
@rows, @fields = rows, fields
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def query(sql)
|
112
|
+
@conn << Query.dump(sql)
|
113
|
+
|
114
|
+
result = Result.new
|
115
|
+
errors = []
|
116
|
+
|
117
|
+
loop do
|
118
|
+
msg = Message.read(@conn)
|
119
|
+
case msg
|
120
|
+
when DataRow
|
121
|
+
result.rows << msg.columns
|
122
|
+
when CommandComplete
|
123
|
+
result.cmd_tag = msg.cmd_tag
|
124
|
+
when ReadyForQuery
|
125
|
+
@transaction_status = msg.backend_transaction_status_indicator
|
126
|
+
break
|
127
|
+
when RowDescription
|
128
|
+
result.fields = msg.fields
|
129
|
+
when CopyInResponse
|
130
|
+
when CopyOutResponse
|
131
|
+
when EmptyQueryResponse
|
132
|
+
when ErrorResponse
|
133
|
+
# TODO
|
134
|
+
errors << msg
|
135
|
+
when NoticeResponse
|
136
|
+
@notice_processor.call(msg) if @notice_processor
|
137
|
+
else
|
138
|
+
# TODO
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
raise(PGError, errors.map{|e| e.field_values.join("\t") }.join("\n")) unless errors.empty?
|
143
|
+
|
144
|
+
result
|
145
|
+
end
|
146
|
+
|
147
|
+
DEFAULT_PORT = 5432
|
148
|
+
DEFAULT_HOST = 'localhost'
|
149
|
+
DEFAULT_PATH = '/tmp'
|
150
|
+
DEFAULT_URI =
|
151
|
+
if RUBY_PLATFORM.include?('win')
|
152
|
+
'tcp://' + DEFAULT_HOST + ':' + DEFAULT_PORT.to_s
|
153
|
+
else
|
154
|
+
'unix:' + File.join(DEFAULT_PATH, '.s.PGSQL.' + DEFAULT_PORT.to_s)
|
155
|
+
end
|
156
|
+
|
157
|
+
private
|
158
|
+
|
159
|
+
# tcp://localhost:5432
|
160
|
+
# unix:/tmp/.s.PGSQL.5432
|
161
|
+
def establish_connection(uri)
|
162
|
+
u = URI.parse(uri)
|
163
|
+
case u.scheme
|
164
|
+
when 'tcp'
|
165
|
+
@conn = TCPSocket.new(u.host || DEFAULT_HOST, u.port || DEFAULT_PORT)
|
166
|
+
when 'unix'
|
167
|
+
@conn = UNIXSocket.new(u.path)
|
168
|
+
else
|
169
|
+
raise PGError, 'unrecognized uri scheme format (must be tcp or unix)'
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
end # module PostgresPR
|