vhskit 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +6 -0
- data/README.md +16 -0
- data/Rakefile +10 -0
- data/cert +20 -0
- data/example +38 -0
- data/key +27 -0
- data/lib/vhskit.rb +22 -0
- data/lib/vhskit/handler.rb +255 -0
- data/lib/vhskit/oid.rb +85 -0
- data/lib/vhskit/server.rb +38 -0
- data/lib/vhskit/version.rb +7 -0
- data/vhskit.gemspec +23 -0
- metadata +86 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e430d0d0e31a503a95c60b807545dc1a3a94fc5e
|
4
|
+
data.tar.gz: 7a6a6ecc2b83794d50d64564fcb3bf29a3bc85df
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c9289b1c0329abe97406c8d8f63afcbf47cedb9494933b7d9c69a96979542ac7937bad712873ee0b52c08f627fafa51b85c5ed4bda1aad9034ee6d7bcf8f158c
|
7
|
+
data.tar.gz: 4021bc6ad57ccb61caec90c20b39706a7a2280b76927e9cccbdbfb991b5a5936ad51379be92967f44051d5344475cd9bfd0b7da81db48ae297c3a0a201e96124
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# VHSKit
|
2
|
+
|
3
|
+
VHSKit is a server that speaks like a real Postgres server.
|
4
|
+
|
5
|
+
See [example](example) for a sample.
|
6
|
+
|
7
|
+
`cert` (and its `key`) is a self-signed SSL certificate for 127.0.0.1, for your
|
8
|
+
convenience.
|
9
|
+
|
10
|
+
## license
|
11
|
+
|
12
|
+
MIT.
|
13
|
+
|
14
|
+
## copyright
|
15
|
+
|
16
|
+
© Amelia Cuss, 2013.
|
data/Rakefile
ADDED
data/cert
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDOzCCAiOgAwIBAgIJALKaTzk+J1xAMA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV
|
3
|
+
BAYTAkFVMREwDwYDVQQIDAhWaWN0b3JpYTESMBAGA1UEAwwJMTI3LjAuMC4xMB4X
|
4
|
+
DTEzMTAwMTAyNDU0NVoXDTEzMTAzMTAyNDU0NVowNDELMAkGA1UEBhMCQVUxETAP
|
5
|
+
BgNVBAgMCFZpY3RvcmlhMRIwEAYDVQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3
|
6
|
+
DQEBAQUAA4IBDwAwggEKAoIBAQDDBfIIsdVepCbi/sB+EP747kBS4YLLJBhPvV3W
|
7
|
+
zpBn45nSjikfEJmxGKE8oqVZSHfxMESWfdAJCwRQ+UhwiXIyiveZ8Dg0PsMDMLws
|
8
|
+
QljK0Qlmc8ZzBFKfqIT6wBVGks0u8RjE8KI0SA9QcKLSHBpmBktoaRQETnwYU222
|
9
|
+
oo3Z8cmYaIWmL/Zw0Hj3mGG3+nqFZEktk1xFo/COJ3gVGPJl7Gs9ESy60HWqEV1F
|
10
|
+
eTuGAak+5R3pmnyLrkV7UWKatWbWxGcIQ88G/BRsrR605OTH9HGZMj3ZtwDl60DF
|
11
|
+
/LDwRa9rA5X36L4Y/hUXXn/DAC5Qa2CAz2yQri8loLOXp0ExAgMBAAGjUDBOMB0G
|
12
|
+
A1UdDgQWBBQV2qgcTkj9NTGKpTBbK63D4IE62zAfBgNVHSMEGDAWgBQV2qgcTkj9
|
13
|
+
NTGKpTBbK63D4IE62zAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQCy
|
14
|
+
9WcFT6WUspF1t4SHNG2i6Xhbv9i8KeAhgwYakANmx10iJse4Ssn5+i9/Hx34cYlt
|
15
|
+
dLmhxF8pGBeQtuh5kBQtLVQHkdJYmfjKSf4Ny2daF2kRpDSbh8jhwmu1FxPOO/T2
|
16
|
+
86PyyBRRlEnfdTRrmz4UvHGVc8AWenJUcVHWqwiFwrOV6qE7sNwwXbXA9w16RnOg
|
17
|
+
M5jH+knVxqJT9ZNo2dmvM9b4jsmvtPDTElkWJ2D6ttPeHI0A8WtVU4Bf5mVlgq3O
|
18
|
+
n/n08+/Q1Z3hYUhibTfW1sKq9U3OKKo9zx6Whia3C3H+dbGkgO0J/jyiSc5Ns9/1
|
19
|
+
54qrouInAjWJe5Ki8tbb
|
20
|
+
-----END CERTIFICATE-----
|
data/example
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
$: << "./lib"
|
5
|
+
|
6
|
+
require 'vhskit'
|
7
|
+
|
8
|
+
VHSKit.run(port: 15432) do
|
9
|
+
=begin
|
10
|
+
on_auth do |user, database|
|
11
|
+
puts "auth got user #{user}, database #{database}"
|
12
|
+
# return the password for this user, or nil if none
|
13
|
+
"hello"
|
14
|
+
end
|
15
|
+
=end
|
16
|
+
|
17
|
+
on_query do |user, database, query|
|
18
|
+
puts "user #{user}, database #{database}, query #{query}"
|
19
|
+
|
20
|
+
if query =~ /^insert /i
|
21
|
+
{insert: 4}
|
22
|
+
elsif query =~ /^delete /i
|
23
|
+
{delete: 4}
|
24
|
+
elsif query =~ /^update /i
|
25
|
+
{update: 4}
|
26
|
+
elsif query =~ /^select /i
|
27
|
+
{select:
|
28
|
+
{columns: ["id", "int4",
|
29
|
+
"name", "text"],
|
30
|
+
rows: ["1", "Joanne",
|
31
|
+
"2", "Yorick"]}}
|
32
|
+
else
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# vim: set sw=2 et:
|
data/key
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIEpAIBAAKCAQEAwwXyCLHVXqQm4v7AfhD++O5AUuGCyyQYT71d1s6QZ+OZ0o4p
|
3
|
+
HxCZsRihPKKlWUh38TBEln3QCQsEUPlIcIlyMor3mfA4ND7DAzC8LEJYytEJZnPG
|
4
|
+
cwRSn6iE+sAVRpLNLvEYxPCiNEgPUHCi0hwaZgZLaGkUBE58GFNttqKN2fHJmGiF
|
5
|
+
pi/2cNB495hht/p6hWRJLZNcRaPwjid4FRjyZexrPREsutB1qhFdRXk7hgGpPuUd
|
6
|
+
6Zp8i65Fe1FimrVm1sRnCEPPBvwUbK0etOTkx/RxmTI92bcA5etAxfyw8EWvawOV
|
7
|
+
9+i+GP4VF15/wwAuUGtggM9skK4vJaCzl6dBMQIDAQABAoIBAH3knqNiOPzJNpke
|
8
|
+
HqDC0/VZx4g5Lzd3a4I+Gg+KdMm7sRas0nrNOomJ/runutlx4It/vybuvJa51+V1
|
9
|
+
pn+PGnrqHn/vBDklsrmigjaH9c2nN0w9vIOO7M9H99/uk01lgrKkzHqFZBAf3FLv
|
10
|
+
AwxjO99UfOU/v74v9Ae4A4D8WlmvjFKCGzQJh+NywECnonU6YJzH+CNo2j//6lv5
|
11
|
+
LCvMSNooCffDoEdkwakFGyglha8xf0SaiaUsB7YFx4Yp0e5RQeSRC7ubtqK3QjRJ
|
12
|
+
WeESWyQPJMbpHps0NfFei5dgmGlqIqFncbQwHrHtQNsagM1eFWF+GoTp9xxrU4I5
|
13
|
+
PGKxi+ECgYEA8ISAsrGMsa5eex9CDBIclefLt7NrFlFaE2JEkwa0zE/DqfbUQJ28
|
14
|
+
6NRcO5gLrIRA7uAnbstNVeTSNqiMkWbQhSAw9dhH4FiAEw0e/ZSsyZRFWFa6/W7T
|
15
|
+
ck6WI45Cn/QBavq5xkejMBFwGBIHtarzirw0sC4OP0FikeN+LU6fXyUCgYEAz5O9
|
16
|
+
VajcXiHU7gG6USozTMkD+HvPMLWlYR+eDlTPzpJ3+dRhySG6v8luYWN0X4yk0TsQ
|
17
|
+
hdbTv1iiJrB8qabMWVuMgXYJHDtnfIRWNzv0yr5HH2tcMZ1NWS+sXazNqUfdzxj1
|
18
|
+
KTxYRY+GOd57fwxgnMF90F0vENSo++asWp7fch0CgYEA1sEo/OUlv/z2pa7aVVbS
|
19
|
+
qVMIBiWwp4PKDua+Xh0t0AQkrB0VlgCCDc6X8Copukd4hxIkg3wJuHkQ7fb/VFDe
|
20
|
+
PQ/qR4lvXDUJXnlnw3o98dtvM5p1ahbLvBPJYUQD3ziLD4+B0zZh0mivkv5+Xcqf
|
21
|
+
nK/Bx9HHrNlf/u2G20OJb/UCgYAL5+5xLllNcOVUrXaxVxlQKyt2IivVIGYW9whK
|
22
|
+
zCSLNa15/+uH7M5YV8ZkAZ9YJ6oAckHVW2gTzpKmY3MTDAUmjvC1MD7/hoy+AJ0t
|
23
|
+
V9wHkPhlXfQQyHP6TJi3WsUFE6EuUsElF1f8zWmmghNVSzFzbEm6HM4pSflTUXzL
|
24
|
+
SdeJJQKBgQDcVB1n29fOX3icpADox3u9IMFNh0vPASa6MVbGhPq5NkDT7xHFRGVn
|
25
|
+
ECsS8H1MlLa4WG3UHsiUptk8K2hS71BqFlVUa2hbwY+NfthIFp2VDq33hAdLak2f
|
26
|
+
3DdPDrVgJNPAPtyBIOw4YN6/96w6u/GEDjAy27MgOOK8T2bXj0W4JQ==
|
27
|
+
-----END RSA PRIVATE KEY-----
|
data/lib/vhskit.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module VHSKit
|
4
|
+
require 'vhskit/server'
|
5
|
+
|
6
|
+
def self.run(opts, &block)
|
7
|
+
port = opts.delete(:port) || 5432
|
8
|
+
|
9
|
+
if opts.length > 0
|
10
|
+
raise ArgumentError.new("Unknown options: #{opts.keys.join ", "}")
|
11
|
+
end
|
12
|
+
|
13
|
+
conf = VHSKit::Configurator.new
|
14
|
+
if block
|
15
|
+
conf.instance_exec(&block)
|
16
|
+
end
|
17
|
+
|
18
|
+
VHSKit::Server.new(port, conf).run
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# vim: set sw=2 et:
|
@@ -0,0 +1,255 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# References:
|
4
|
+
# http://www.postgresql.org/docs/9.1/static/protocol-flow.html
|
5
|
+
# http://www.postgresql.org/docs/9.1/static/protocol-message-formats.html
|
6
|
+
# http://www.postgresql.org/docs/9.1/static/catalog-pg-type.html
|
7
|
+
# http://www.postgresql.org/docs/9.1/static/catalog-pg-attribute.html
|
8
|
+
# http://ruby-doc.org/core-2.0.0/Array.html#method-i-pack
|
9
|
+
# http://www.postgresql.org/docs/9.1/static/protocol-error-fields.html
|
10
|
+
# http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html
|
11
|
+
|
12
|
+
require 'openssl'
|
13
|
+
require 'vhskit/oid'
|
14
|
+
|
15
|
+
class VHSKit::Handler
|
16
|
+
def initialize(sock, conf)
|
17
|
+
@sock = sock
|
18
|
+
@conf = conf.fns
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
start
|
23
|
+
@sock.close rescue false
|
24
|
+
rescue Exception => e
|
25
|
+
STDERR.puts "error in VHSKit::Handler: #{e}"
|
26
|
+
STDERR.puts e.backtrace
|
27
|
+
@sock.close rescue false
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def start
|
33
|
+
startup = read_msg()
|
34
|
+
ver = startup.unpack('L>')[0]
|
35
|
+
|
36
|
+
if ver == 80877103 && startup.length == 4
|
37
|
+
@sock.write 'S' # accept the request
|
38
|
+
|
39
|
+
ctx = OpenSSL::SSL::SSLContext.new(:SSLv23_server)
|
40
|
+
ctx.cert = OpenSSL::X509::Certificate.new(File.read('cert'))
|
41
|
+
ctx.key = OpenSSL::PKey::RSA.new(File.read('key'))
|
42
|
+
ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
43
|
+
@sock = OpenSSL::SSL::SSLSocket.new(@sock.to_io, ctx)
|
44
|
+
@sock.accept
|
45
|
+
|
46
|
+
startup = read_msg()
|
47
|
+
ver = startup.unpack('L>')[0]
|
48
|
+
end
|
49
|
+
|
50
|
+
if ver != 196608
|
51
|
+
STDERR.puts "unknown version #{ver}; bye"
|
52
|
+
return
|
53
|
+
end
|
54
|
+
|
55
|
+
handle_opts(startup[4..-1])
|
56
|
+
end
|
57
|
+
|
58
|
+
def handle_opts(opts)
|
59
|
+
hash = {}
|
60
|
+
while opts.length > 1
|
61
|
+
key, val, opts = opts.unpack('Z*Z*a*')
|
62
|
+
hash[key] = val
|
63
|
+
end
|
64
|
+
|
65
|
+
if opts != "\0"
|
66
|
+
STDERR.puts "options terminated poorly; bye"
|
67
|
+
return
|
68
|
+
end
|
69
|
+
|
70
|
+
if !hash["user"]
|
71
|
+
STDERR.puts "no user specified; bye"
|
72
|
+
return
|
73
|
+
end
|
74
|
+
|
75
|
+
@user = hash["user"]
|
76
|
+
@database = hash["database"] || @user
|
77
|
+
|
78
|
+
if @conf[:on_auth]
|
79
|
+
pwd = @conf[:on_auth].call(@user, @database)
|
80
|
+
|
81
|
+
salt = Random::DEFAULT.bytes(4)
|
82
|
+
STDERR.puts salt.bytes.length
|
83
|
+
write_msg 'R', [5, salt].pack('L>a*') # AuthenticationMD5
|
84
|
+
tag = read_tag
|
85
|
+
|
86
|
+
if tag != 'p'
|
87
|
+
STDERR.puts "expected 'p', got #{tag}; bye"
|
88
|
+
return
|
89
|
+
end
|
90
|
+
|
91
|
+
enc = read_msg().unpack('Z*')[0]
|
92
|
+
|
93
|
+
if !pwd
|
94
|
+
comp = nil
|
95
|
+
else
|
96
|
+
comp = 'md5' + Digest::MD5.hexdigest(
|
97
|
+
Digest::MD5.hexdigest(pwd + @user) + salt)
|
98
|
+
end
|
99
|
+
|
100
|
+
if enc != comp
|
101
|
+
write_msg 'E', notice_error_body(
|
102
|
+
'S' => 'FATAL',
|
103
|
+
'C' => '28P01',
|
104
|
+
'M' => "password authentication failed for user \"#@user\"")
|
105
|
+
return
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
write_msg 'R', [0].pack('L>') # AuthenticationOk
|
110
|
+
|
111
|
+
mainloop
|
112
|
+
end
|
113
|
+
|
114
|
+
def mainloop
|
115
|
+
@abort = false
|
116
|
+
while !@abort
|
117
|
+
main
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def main
|
122
|
+
# ReadyForQuery
|
123
|
+
write_msg 'Z', (!@transaction ? 'I' : 'T') # 'E' for failed txn block
|
124
|
+
|
125
|
+
kind = @sock.read(1)
|
126
|
+
|
127
|
+
case kind
|
128
|
+
when 'Q'
|
129
|
+
query
|
130
|
+
when 'X'
|
131
|
+
puts "client terminated"
|
132
|
+
@abort = true
|
133
|
+
else
|
134
|
+
STDERR.puts "unknown kind #{kind}"
|
135
|
+
@abort = true
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def query
|
140
|
+
query = read_msg()
|
141
|
+
on_query = @conf[:on_query]
|
142
|
+
if !on_query
|
143
|
+
write_msg 'E', notice_error_body(
|
144
|
+
'S' => 'ERROR',
|
145
|
+
'C' => 'XX000',
|
146
|
+
'M' => 'Internal error: no query handler available')
|
147
|
+
return
|
148
|
+
end
|
149
|
+
|
150
|
+
each_query(query.strip, on_query)
|
151
|
+
end
|
152
|
+
|
153
|
+
def each_query(query, on_query)
|
154
|
+
result = on_query.call(@user, @database, query)
|
155
|
+
if !result || result.keys.length != 1
|
156
|
+
STDERR.puts "result.keys not unary for #{query.inspect}: #{result.inspect}"
|
157
|
+
write_msg 'E', notice_error_body(
|
158
|
+
'S' => 'ERROR',
|
159
|
+
'C' => 'XX000',
|
160
|
+
'M' => "Internal error: query handler didn't return sensible data")
|
161
|
+
return
|
162
|
+
end
|
163
|
+
|
164
|
+
key, value = result.to_a[0]
|
165
|
+
|
166
|
+
case key
|
167
|
+
when :insert
|
168
|
+
# CommandComplete
|
169
|
+
write_msg 'C', ["INSERT 0 #{value}"].pack('Z*')
|
170
|
+
when :delete
|
171
|
+
# CommandComplete
|
172
|
+
write_msg 'C', ["DELETE #{value}"].pack('Z*')
|
173
|
+
when :update
|
174
|
+
# CommandComplete
|
175
|
+
write_msg 'C', ["UPDATE #{value}"].pack('Z*')
|
176
|
+
when :select
|
177
|
+
rows = value[:columns].each_slice(2).map {|name, type|
|
178
|
+
[name, 0, 0, VHSKit::OID.by_name(type),
|
179
|
+
4, # XXX this is wrong
|
180
|
+
-1, 0]}
|
181
|
+
# RowDescription
|
182
|
+
write_msg 'T', row_descriptions(rows)
|
183
|
+
|
184
|
+
count = 0
|
185
|
+
value[:rows].each_slice(rows.length).each do |row|
|
186
|
+
# DataRow
|
187
|
+
write_msg 'D', data_row(row)
|
188
|
+
count += 1
|
189
|
+
end
|
190
|
+
|
191
|
+
# CommandComplete
|
192
|
+
write_msg 'C', ["SELECT #{count}"].pack('Z*')
|
193
|
+
# when :move
|
194
|
+
# when :fetch
|
195
|
+
# when :copy
|
196
|
+
else
|
197
|
+
raise StandardError.new("unknown key #{key.inspect}")
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def read_tag()
|
202
|
+
r = @sock.read(1)
|
203
|
+
if !r || r.length < 1
|
204
|
+
raise EOFError
|
205
|
+
end
|
206
|
+
r
|
207
|
+
end
|
208
|
+
|
209
|
+
def read_msg()
|
210
|
+
len = @sock.read(4).unpack('L>')[0]
|
211
|
+
r = @sock.read(len - 4)
|
212
|
+
if !r || r.length < len - 4
|
213
|
+
raise EOFError
|
214
|
+
end
|
215
|
+
r
|
216
|
+
end
|
217
|
+
|
218
|
+
def write_msg(tag, data)
|
219
|
+
@sock.write tag
|
220
|
+
@sock.write [data.length + 4].pack('L>')
|
221
|
+
@sock.write data
|
222
|
+
@sock.flush
|
223
|
+
end
|
224
|
+
|
225
|
+
def notice_error_body(pairs)
|
226
|
+
pairs.map do |k,v|
|
227
|
+
if k.length != 1
|
228
|
+
raise ArgumentError.new("notice_error_body got key #{k.inspect}")
|
229
|
+
end
|
230
|
+
|
231
|
+
[k, v].pack('aZ*')
|
232
|
+
end.join.force_encoding('US-ASCII') + "\00"
|
233
|
+
end
|
234
|
+
|
235
|
+
# rows should be an Array of Arrays of:
|
236
|
+
# - field name (String)
|
237
|
+
# - table OID or zero (Integer)
|
238
|
+
# - column OID or zero (Integer)
|
239
|
+
# - data type OID (Integer)
|
240
|
+
# - data type size (Integer)
|
241
|
+
# - type modifier (Integer)
|
242
|
+
# - format code (Integer: 0 text, 1 binary)
|
243
|
+
def row_descriptions(rows)
|
244
|
+
[rows.length,
|
245
|
+
rows.map {|e| e.pack('Z*L>S>L>S>L>S>')}.join].pack('S>a*')
|
246
|
+
end
|
247
|
+
|
248
|
+
# columns should be an Array of Strings
|
249
|
+
def data_row(columns)
|
250
|
+
[columns.length,
|
251
|
+
columns.map {|e| [e.length + 1, e].pack('L>Z*')}.join].pack('S>a*')
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
# vim: set sw=2 et:
|
data/lib/vhskit/oid.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# Source for OIDs:
|
4
|
+
# http://jdbc.postgresql.org/development/privateapi/constant-values.html#org.postgresql.core.Oid.INT4
|
5
|
+
|
6
|
+
module VHSKit::OID
|
7
|
+
BIT = 1560
|
8
|
+
BIT_ARRAY = 1561
|
9
|
+
BOOL = 16
|
10
|
+
BOOL_ARRAY = 1000
|
11
|
+
BPCHAR = 1042
|
12
|
+
BPCHAR_ARRAY = 1014
|
13
|
+
BYTEA = 17
|
14
|
+
BYTEA_ARRAY = 1001
|
15
|
+
CHAR = 18
|
16
|
+
CHAR_ARRAY = 1002
|
17
|
+
DATE = 1082
|
18
|
+
DATE_ARRAY = 1182
|
19
|
+
FLOAT4 = 700
|
20
|
+
FLOAT4_ARRAY = 1021
|
21
|
+
FLOAT8 = 701
|
22
|
+
FLOAT8_ARRAY = 1022
|
23
|
+
INT2 = 21
|
24
|
+
INT2_ARRAY = 1005
|
25
|
+
INT4 = 23
|
26
|
+
INT4_ARRAY = 1007
|
27
|
+
INT8 = 20
|
28
|
+
INT8_ARRAY = 1016
|
29
|
+
INTERVAL = 1186
|
30
|
+
INTERVAL_ARRAY = 1187
|
31
|
+
MONEY = 790
|
32
|
+
MONEY_ARRAY = 791
|
33
|
+
NAME = 19
|
34
|
+
NAME_ARRAY = 1003
|
35
|
+
NUMERIC = 1700
|
36
|
+
NUMERIC_ARRAY = 1231
|
37
|
+
OID = 26
|
38
|
+
OID_ARRAY = 1028
|
39
|
+
TEXT = 25
|
40
|
+
TEXT_ARRAY = 1009
|
41
|
+
TIME = 1083
|
42
|
+
TIME_ARRAY = 1183
|
43
|
+
TIMESTAMP = 1114
|
44
|
+
TIMESTAMP_ARRAY = 1115
|
45
|
+
TIMESTAMPTZ = 1184
|
46
|
+
TIMESTAMPTZ_ARRAY = 1185
|
47
|
+
TIMETZ = 1266
|
48
|
+
TIMETZ_ARRAY = 1270
|
49
|
+
UNSPECIFIED = 0
|
50
|
+
UUID = 2950
|
51
|
+
UUID_ARRAY = 2951
|
52
|
+
VARBIT = 1562
|
53
|
+
VARBIT_ARRAY = 1563
|
54
|
+
VARCHAR = 1043
|
55
|
+
VARCHAR_ARRAY = 1015
|
56
|
+
VOID = 2278
|
57
|
+
XML = 142
|
58
|
+
XML_ARRAY = 143
|
59
|
+
|
60
|
+
def self.by_name(name)
|
61
|
+
name = name.upcase
|
62
|
+
|
63
|
+
array = false
|
64
|
+
if name =~ /^(.+)\[\]$/
|
65
|
+
name = $1
|
66
|
+
array = true
|
67
|
+
end
|
68
|
+
|
69
|
+
if name =~ /^(.+) WITHOUT TIME ZONE$/
|
70
|
+
name = $1
|
71
|
+
end
|
72
|
+
|
73
|
+
if name =~ /^(.+) WITH TIME ZONE$/
|
74
|
+
name = "#{$1}TZ"
|
75
|
+
end
|
76
|
+
|
77
|
+
if array
|
78
|
+
name << "_ARRAY"
|
79
|
+
end
|
80
|
+
|
81
|
+
const_get name
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# vim: set sw=2 et:
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
require 'vhskit/handler'
|
5
|
+
|
6
|
+
class VHSKit::Server
|
7
|
+
def initialize(port, conf)
|
8
|
+
@serv = TCPServer.new port
|
9
|
+
@conf = conf
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
while true
|
14
|
+
sock = @serv.accept
|
15
|
+
Thread.start do
|
16
|
+
VHSKit::Handler.new(sock, @conf).run
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class VHSKit::Configurator
|
23
|
+
def initialize
|
24
|
+
@fns = {}
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :fns
|
28
|
+
|
29
|
+
FNS = [:on_auth, :on_query]
|
30
|
+
|
31
|
+
FNS.each do |fn|
|
32
|
+
define_method(fn) do |&block|
|
33
|
+
@fns[fn] = block
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# vim: set sw=2 et:
|
data/vhskit.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.expand_path('../lib/vhskit/version', __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.authors = ["Amelia Cuss"]
|
7
|
+
gem.email = ["amelia@kivikakk.ee"]
|
8
|
+
gem.description = %q{Just like a Postgres server, only fake}
|
9
|
+
gem.summary = %q{A Postgres server protocol implementation you can configure with a DSL.}
|
10
|
+
gem.homepage = "https://github.com/kivikakk/vhskit"
|
11
|
+
|
12
|
+
gem.add_development_dependency('rake')
|
13
|
+
gem.add_development_dependency('rspec')
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($\)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.name = "vhskit"
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
gem.version = VHSKit::VERSION
|
21
|
+
end
|
22
|
+
|
23
|
+
# vim: set sw=2 et:
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vhskit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Amelia Cuss
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-10-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Just like a Postgres server, only fake
|
42
|
+
email:
|
43
|
+
- amelia@kivikakk.ee
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- .gitignore
|
49
|
+
- .rspec
|
50
|
+
- .travis.yml
|
51
|
+
- Gemfile
|
52
|
+
- README.md
|
53
|
+
- Rakefile
|
54
|
+
- cert
|
55
|
+
- example
|
56
|
+
- key
|
57
|
+
- lib/vhskit.rb
|
58
|
+
- lib/vhskit/handler.rb
|
59
|
+
- lib/vhskit/oid.rb
|
60
|
+
- lib/vhskit/server.rb
|
61
|
+
- lib/vhskit/version.rb
|
62
|
+
- vhskit.gemspec
|
63
|
+
homepage: https://github.com/kivikakk/vhskit
|
64
|
+
licenses: []
|
65
|
+
metadata: {}
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options: []
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - '>='
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
requirements: []
|
81
|
+
rubyforge_project:
|
82
|
+
rubygems_version: 2.0.3
|
83
|
+
signing_key:
|
84
|
+
specification_version: 4
|
85
|
+
summary: A Postgres server protocol implementation you can configure with a DSL.
|
86
|
+
test_files: []
|