vhskit 0.0.1
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.
- 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: []
|