cipherstash-pg 1.0.0.beta.1-arm64-darwin-22 → 1.0.0.beta.3-arm64-darwin-22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/.appveyor.yml +42 -0
  3. data/.gems +6 -0
  4. data/.gemtest +0 -0
  5. data/.github/workflows/binary-gems.yml +117 -0
  6. data/.github/workflows/source-gem.yml +137 -0
  7. data/.gitignore +19 -0
  8. data/.hgsigs +34 -0
  9. data/.hgtags +41 -0
  10. data/.irbrc +23 -0
  11. data/.pryrc +23 -0
  12. data/.tm_properties +21 -0
  13. data/.travis.yml +49 -0
  14. data/Gemfile +3 -3
  15. data/Gemfile.lock +45 -0
  16. data/{History.rdoc → History.md} +168 -153
  17. data/README.ja.md +266 -0
  18. data/README.md +272 -0
  19. data/Rakefile +65 -104
  20. data/Rakefile.cross +298 -0
  21. data/certs/larskanis-2023.pem +24 -0
  22. data/cipherstash-pg.gemspec +0 -0
  23. data/lib/2.7/pg_ext.bundle +0 -0
  24. data/lib/3.0/pg_ext.bundle +0 -0
  25. data/lib/3.1/pg_ext.bundle +0 -0
  26. data/lib/3.2/pg_ext.bundle +0 -0
  27. data/lib/cipherstash-pg/basic_type_map_based_on_result.rb +11 -0
  28. data/lib/cipherstash-pg/basic_type_map_for_queries.rb +113 -0
  29. data/lib/cipherstash-pg/basic_type_map_for_results.rb +30 -0
  30. data/lib/cipherstash-pg/basic_type_registry.rb +206 -0
  31. data/lib/cipherstash-pg/binary_decoder.rb +21 -0
  32. data/lib/cipherstash-pg/coder.rb +82 -0
  33. data/lib/cipherstash-pg/connection.rb +467 -0
  34. data/lib/cipherstash-pg/constants.rb +3 -0
  35. data/lib/cipherstash-pg/exceptions.rb +19 -0
  36. data/lib/cipherstash-pg/result.rb +22 -0
  37. data/lib/cipherstash-pg/text_decoder.rb +43 -0
  38. data/lib/cipherstash-pg/text_encoder.rb +67 -0
  39. data/lib/cipherstash-pg/tuple.rb +24 -0
  40. data/lib/cipherstash-pg/type_map_by_column.rb +11 -0
  41. data/lib/cipherstash-pg/version.rb +3 -0
  42. data/lib/cipherstash-pg.rb +56 -11
  43. data/lib/libpq.5.dylib +0 -0
  44. data/misc/openssl-pg-segfault.rb +15 -25
  45. data/misc/postgres/Rakefile +13 -20
  46. data/misc/postgres/lib/postgres.rb +10 -14
  47. data/misc/ruby-pg/Rakefile +13 -20
  48. data/misc/ruby-pg/lib/ruby/pg.rb +10 -14
  49. data/rakelib/task_extension.rb +17 -31
  50. data/sample/array_insert.rb +7 -20
  51. data/sample/async_api.rb +54 -96
  52. data/sample/async_copyto.rb +20 -35
  53. data/sample/async_mixed.rb +22 -50
  54. data/sample/check_conn.rb +8 -20
  55. data/sample/copydata.rb +18 -68
  56. data/sample/copyfrom.rb +26 -78
  57. data/sample/copyto.rb +10 -16
  58. data/sample/cursor.rb +9 -19
  59. data/sample/disk_usage_report.rb +89 -174
  60. data/sample/issue-119.rb +45 -93
  61. data/sample/losample.rb +48 -66
  62. data/sample/minimal-testcase.rb +6 -17
  63. data/sample/notify_wait.rb +21 -67
  64. data/sample/pg_statistics.rb +100 -281
  65. data/sample/replication_monitor.rb +119 -218
  66. data/sample/test_binary_values.rb +14 -30
  67. data/sample/wal_shipper.rb +199 -431
  68. data/sample/warehouse_partitions.rb +157 -307
  69. data/translation/.po4a-version +7 -0
  70. data/translation/po/all.pot +875 -0
  71. data/translation/po/ja.po +868 -0
  72. data/translation/po4a.cfg +9 -0
  73. metadata +50 -30
  74. data/README.ja.rdoc +0 -13
  75. data/README.rdoc +0 -233
  76. data/lib/pg/basic_type_map_based_on_result.rb +0 -47
  77. data/lib/pg/basic_type_map_for_queries.rb +0 -193
  78. data/lib/pg/basic_type_map_for_results.rb +0 -81
  79. data/lib/pg/basic_type_registry.rb +0 -301
  80. data/lib/pg/binary_decoder.rb +0 -23
  81. data/lib/pg/coder.rb +0 -104
  82. data/lib/pg/connection.rb +0 -878
  83. data/lib/pg/constants.rb +0 -12
  84. data/lib/pg/exceptions.rb +0 -18
  85. data/lib/pg/result.rb +0 -43
  86. data/lib/pg/text_decoder.rb +0 -46
  87. data/lib/pg/text_encoder.rb +0 -59
  88. data/lib/pg/tuple.rb +0 -30
  89. data/lib/pg/type_map_by_column.rb +0 -16
  90. data/lib/pg/version.rb +0 -4
  91. data/lib/pg.rb +0 -55
  92. data/vendor/database-extensions/install.sql +0 -317
  93. data/vendor/database-extensions/uninstall.sql +0 -20
@@ -0,0 +1,467 @@
1
+ require("cipherstash-pg") unless defined? CipherStashPG
2
+ require("uri")
3
+ require("io/wait")
4
+ require("socket")
5
+ class CipherStashPG::Connection
6
+ CONNECT_ARGUMENT_ORDER = ["host", "port", "options", "tty", "dbname", "user", "password"]
7
+
8
+ def self.quote_connstr(value)
9
+ return (("'" + value.to_s.gsub(/[\\']/) { |m| ("\\" + m) }) + "'")
10
+ end
11
+
12
+ def self.connect_hash_to_string(hash)
13
+ hash.map { |k, v| "#{k}=#{quote_connstr(v)}" }.join(" ")
14
+ end
15
+
16
+ def self.parse_connect_args(*args)
17
+ hash_arg = args.last.is_a?(Hash) ? (args.pop.transform_keys(&:to_sym)) : ({})
18
+ iopts = {}
19
+ if (args.length == 1) then
20
+ case args.first
21
+ when URI, /=/, /:\/\// then
22
+ conn_string = args.first.to_s
23
+ iopts = CipherStashPG::Connection.conninfo_parse(conn_string).each_with_object({}) do |h, o|
24
+ o[h[:keyword].to_sym] = h[:val] if h[:val]
25
+ end
26
+ else
27
+ iopts[CONNECT_ARGUMENT_ORDER.first.to_sym] = args.first
28
+ end
29
+ else
30
+ max = CONNECT_ARGUMENT_ORDER.length
31
+ if (args.length > max) then
32
+ raise(ArgumentError, ("Extra positional parameter %d: %p" % [(max + 1), args[max]]))
33
+ end
34
+ CONNECT_ARGUMENT_ORDER.zip(args) { |(k, v)| iopts[k.to_sym] = v if v }
35
+ iopts.delete(:tty)
36
+ end
37
+ iopts.merge!(hash_arg)
38
+ unless iopts[:fallback_application_name] then
39
+ iopts[:fallback_application_name] = $0.sub(/^(.{30}).{4,}(.{30})$/) { (($1 + "...") + $2) }
40
+ end
41
+ return connect_hash_to_string(iopts)
42
+ end
43
+
44
+ def inspect
45
+ str = self.to_s
46
+ str[-1, 0] = if finished? then
47
+ " finished"
48
+ else
49
+ stats = []
50
+ if (status != CONNECTION_OK) then
51
+ (stats << " status=#{CipherStashPG.constants.grep(/CONNECTION_/).find do |c|
52
+ (CipherStashPG.const_get(c) == status)
53
+ end}")
54
+ end
55
+ if (transaction_status != CipherStashPG::PQTRANS_IDLE) then
56
+ (stats << " transaction_status=#{CipherStashPG.constants.grep(/PQTRANS_/).find do |c|
57
+ (CipherStashPG.const_get(c) == transaction_status)
58
+ end}")
59
+ end
60
+ (stats << " nonblocking=#{isnonblocking}") if isnonblocking
61
+ if respond_to?(:pipeline_status) and (pipeline_status != CipherStashPG::PQ_PIPELINE_OFF) then
62
+ (stats << " pipeline_status=#{CipherStashPG.constants.grep(/PQ_PIPELINE_/).find do |c|
63
+ (CipherStashPG.const_get(c) == pipeline_status)
64
+ end}")
65
+ end
66
+ if (get_client_encoding != "UTF8") then
67
+ (stats << " client_encoding=#{get_client_encoding}")
68
+ end
69
+ unless type_map_for_results.is_a?(CipherStashPG::TypeMapAllStrings) then
70
+ (stats << " type_map_for_results=#{type_map_for_results.to_s}")
71
+ end
72
+ unless type_map_for_queries.is_a?(CipherStashPG::TypeMapAllStrings) then
73
+ (stats << " type_map_for_queries=#{type_map_for_queries.to_s}")
74
+ end
75
+ if encoder_for_put_copy_data then
76
+ (stats << " encoder_for_put_copy_data=#{encoder_for_put_copy_data.to_s}")
77
+ end
78
+ if decoder_for_get_copy_data then
79
+ (stats << " decoder_for_get_copy_data=#{decoder_for_get_copy_data.to_s}")
80
+ end
81
+ " host=#{host} port=#{port} user=#{user}#{stats.join}"
82
+ end
83
+ return str
84
+ end
85
+
86
+ def copy_data(sql, coder = nil)
87
+ if nonblocking? then
88
+ raise(CipherStashPG::NotInBlockingMode.new("copy_data can not be used in nonblocking mode", :connection => (self)))
89
+ end
90
+ res = exec(sql)
91
+ case res.result_status
92
+ when PGRES_COPY_IN then
93
+ begin
94
+ (if coder then
95
+ old_coder = self.encoder_for_put_copy_data
96
+ self.encoder_for_put_copy_data = coder
97
+ end
98
+ yield(res))
99
+ rescue Exception => err
100
+ errmsg = ("%s while copy data: %s" % [err.class.name, err.message])
101
+ begin
102
+ put_copy_end(errmsg)
103
+ rescue CipherStashPG::Error
104
+ # do nothing
105
+ end
106
+ discard_results
107
+ raise(err)
108
+ else
109
+ (begin
110
+ put_copy_end
111
+ rescue CipherStashPG::Error => err
112
+ raise(CipherStashPG::LostCopyState.new("#{err} (probably by executing another SQL query while running a COPY command)", :connection => (self)))
113
+ end
114
+ get_last_result)
115
+ ensure
116
+ self.encoder_for_put_copy_data = old_coder if coder
117
+ end
118
+ when PGRES_COPY_OUT then
119
+ begin
120
+ (if coder then
121
+ old_coder = self.decoder_for_get_copy_data
122
+ self.decoder_for_get_copy_data = coder
123
+ end
124
+ yield(res))
125
+ rescue Exception
126
+ cancel
127
+ discard_results
128
+ raise
129
+ else
130
+ (res = get_last_result
131
+ if res then
132
+ if (res.result_status != PGRES_COMMAND_OK) then
133
+ discard_results
134
+ raise(CipherStashPG::NotAllCopyDataRetrieved.new("Not all COPY data retrieved", :connection => (self)))
135
+ end
136
+ else
137
+ discard_results
138
+ raise(CipherStashPG::LostCopyState.new("Lost COPY state (probably by executing another SQL query while running a COPY command)", :connection => (self)))
139
+ end
140
+ res)
141
+ ensure
142
+ self.decoder_for_get_copy_data = old_coder if coder
143
+ end
144
+ else
145
+ raise(ArgumentError, "SQL command is no COPY statement: #{sql}")
146
+ end
147
+ end
148
+
149
+ class << self
150
+ define_method(:isthreadsafe, &CipherStashPG.method(:isthreadsafe))
151
+ end
152
+
153
+ def transaction
154
+ (rollback = false
155
+ exec("BEGIN")
156
+ yield(self))
157
+ rescue Exception
158
+ rollback = true
159
+ cancel if (transaction_status == CipherStashPG::PQTRANS_ACTIVE)
160
+ block
161
+ exec("ROLLBACK")
162
+ raise
163
+ ensure
164
+ exec("COMMIT") unless rollback
165
+ end
166
+
167
+ def conndefaults
168
+ return self.class.conndefaults
169
+ end
170
+
171
+ def self.conndefaults_hash
172
+ return self.conndefaults.each_with_object({}) do |info, hash|
173
+ hash[info[:keyword].to_sym] = info[:val]
174
+ end
175
+ end
176
+
177
+ def conndefaults_hash
178
+ return self.class.conndefaults_hash
179
+ end
180
+
181
+ def conninfo_hash
182
+ return self.conninfo.each_with_object({}) do |info, hash|
183
+ hash[info[:keyword].to_sym] = info[:val]
184
+ end
185
+ end
186
+
187
+ if self.instance_methods.find { |m| (m.to_sym == :ssl_attribute) } then
188
+ def ssl_attributes
189
+ ssl_attribute_names.each.with_object({}) { |n, h| h[n] = ssl_attribute(n) }
190
+ end
191
+ end
192
+
193
+ def get_result
194
+ block
195
+ sync_get_result
196
+ end
197
+
198
+ alias :async_get_result :get_result
199
+
200
+ def get_copy_data(async = false, decoder = nil)
201
+ if async then
202
+ return sync_get_copy_data(async, decoder)
203
+ else
204
+ while ((res = sync_get_copy_data(true, decoder)) == false) do
205
+ socket_io.wait_readable
206
+ consume_input
207
+ end
208
+ return res
209
+ end
210
+ end
211
+
212
+ alias :async_get_copy_data :get_copy_data
213
+
214
+ def setnonblocking(enabled)
215
+ singleton_class.async_send_api = (not enabled)
216
+ self.flush_data = (not enabled)
217
+ sync_setnonblocking(true)
218
+ end
219
+
220
+ alias :async_setnonblocking :setnonblocking
221
+
222
+ def isnonblocking
223
+ false
224
+ end
225
+
226
+ alias :async_isnonblocking :isnonblocking
227
+
228
+ alias :nonblocking? :isnonblocking
229
+
230
+ def put_copy_data(buffer, encoder = nil)
231
+ until res = sync_put_copy_data(buffer, encoder) do
232
+ res = flush
233
+ end
234
+ if (@calls_to_put_copy_data = (@calls_to_put_copy_data + 1) > 100) then
235
+ @calls_to_put_copy_data = 0
236
+ res = flush
237
+ end
238
+ res
239
+ end
240
+
241
+ alias :async_put_copy_data :put_copy_data
242
+
243
+ def put_copy_end(*args)
244
+ until sync_put_copy_end(*args) do
245
+ flush
246
+ end
247
+ @calls_to_put_copy_data = 0
248
+ flush
249
+ end
250
+
251
+ alias :async_put_copy_end :put_copy_end
252
+
253
+ if method_defined?(:sync_encrypt_password) then
254
+ def encrypt_password(password, username, algorithm = nil)
255
+ algorithm ||= exec("SHOW password_encryption").getvalue(0, 0)
256
+ sync_encrypt_password(password, username, algorithm)
257
+ end
258
+ (alias :async_encrypt_password :encrypt_password)
259
+ end
260
+
261
+ def reset
262
+ reset_start
263
+ async_connect_or_reset(:reset_poll)
264
+ self
265
+ end
266
+
267
+ alias :async_reset :reset
268
+
269
+ def cancel
270
+ (be_pid = backend_pid
271
+ be_key = backend_key
272
+ cancel_request = [16, 1234, 5678, be_pid, be_key].pack("NnnNN")
273
+ if Fiber.respond_to?(:scheduler) and (Fiber.scheduler and RUBY_PLATFORM =~ /mingw|mswin/) then
274
+ cl = Thread.new(socket_io.remote_address) { |ra| ra.connect }.value
275
+ begin
276
+ cl.write_nonblock(cancel_request)
277
+ rescue IO::WaitReadable, Errno::EINTR
278
+ cl.wait_writable
279
+ retry
280
+ end
281
+ begin
282
+ cl.read_nonblock(1)
283
+ rescue IO::WaitReadable, Errno::EINTR
284
+ cl.wait_readable
285
+ retry
286
+ rescue EOFError
287
+ # do nothing
288
+ end
289
+ else
290
+ if (RUBY_ENGINE == "truffleruby") then
291
+ begin
292
+ cl = socket_io.remote_address.connect
293
+ rescue NotImplementedError
294
+ cl2 = Socket.for_fd(socket_io.fileno)
295
+ cl2.autoclose = false
296
+ adr = cl2.remote_address
297
+ if adr.ip? then
298
+ cl = TCPSocket.new(adr.ip_address, adr.ip_port)
299
+ cl.autoclose = false
300
+ else
301
+ cl = UNIXSocket.new(adr.unix_path)
302
+ cl.autoclose = false
303
+ end
304
+ end
305
+ cl.write(cancel_request)
306
+ cl.read(1)
307
+ else
308
+ cl = socket_io.remote_address.connect
309
+ cl.write(cancel_request)
310
+ cl.read(1)
311
+ end
312
+ end
313
+ cl.close
314
+ nil)
315
+ rescue SystemCallError => err
316
+ err.to_s
317
+ end
318
+
319
+ alias :async_cancel :cancel
320
+
321
+ private(def async_connect_or_reset(poll_meth)
322
+ if timeo = conninfo_hash[:connect_timeout].to_i and (timeo > 0) then
323
+ timeo = [timeo, 2].max
324
+ host_count = (conninfo_hash[:host].to_s.count(",") + 1)
325
+ stop_time = ((timeo * host_count) + Process.clock_gettime(Process::CLOCK_MONOTONIC))
326
+ end
327
+ poll_status = CipherStashPG::PGRES_POLLING_WRITING
328
+ until ((poll_status == CipherStashPG::PGRES_POLLING_OK) or (poll_status == CipherStashPG::PGRES_POLLING_FAILED)) do
329
+ (if stop_time then
330
+ timeout = [timeo, (stop_time - Process.clock_gettime(Process::CLOCK_MONOTONIC))].min
331
+ end
332
+ event = if ((not timeout) or (timeout >= 0)) then
333
+ case poll_status
334
+ when CipherStashPG::PGRES_POLLING_READING then
335
+ if defined? IO::READABLE then
336
+ socket_io.wait((IO::READABLE | IO::PRIORITY), timeout)
337
+ else
338
+ IO.select([socket_io], nil, [socket_io], timeout)
339
+ end
340
+ when CipherStashPG::PGRES_POLLING_WRITING then
341
+ if defined? IO::WRITABLE then
342
+ socket_io.wait((IO::WRITABLE | IO::PRIORITY), timeout)
343
+ else
344
+ IO.select(nil, [socket_io], [socket_io], timeout)
345
+ end
346
+ end
347
+ end
348
+ unless event then
349
+ if self.class.send(:host_is_named_pipe?, host) then
350
+ connhost = "on socket \"#{host}\""
351
+ else
352
+ if respond_to?(:hostaddr) then
353
+ connhost = "at \"#{host}\" (#{hostaddr}), port #{port}"
354
+ else
355
+ connhost = "at \"#{host}\", port #{port}"
356
+ end
357
+ end
358
+ raise(CipherStashPG::ConnectionBad.new("connection to server #{connhost} failed: timeout expired", :connection => (self)))
359
+ end
360
+ poll_status = send(poll_meth))
361
+ end
362
+ unless (status == CipherStashPG::CONNECTION_OK) then
363
+ msg = error_message
364
+ finish
365
+ raise(CipherStashPG::ConnectionBad.new(msg, :connection => (self)))
366
+ end
367
+ sync_setnonblocking(true)
368
+ self.flush_data = true
369
+ set_default_encoding
370
+ end)
371
+
372
+ class << self
373
+ def new(*args)
374
+ conn = connect_to_hosts(*args)
375
+ if block_given? then
376
+ begin
377
+ return yield(conn)
378
+ ensure
379
+ conn.finish
380
+ end
381
+ end
382
+ conn
383
+ end
384
+ alias :async_connect :new
385
+ alias :connect :new
386
+ alias :open :new
387
+ alias :setdb :new
388
+ alias :setdblogin :new
389
+ private(def connect_to_hosts(*args)
390
+ option_string = parse_connect_args(*args)
391
+ iopts = CipherStashPG::Connection.conninfo_parse(option_string).each_with_object({}) do |h, o|
392
+ o[h[:keyword].to_sym] = h[:val] if h[:val]
393
+ end
394
+ iopts = CipherStashPG::Connection.conndefaults.each_with_object({}) do |h, o|
395
+ o[h[:keyword].to_sym] = h[:val] if h[:val]
396
+ end.merge(iopts)
397
+ unless iopts[:hostaddr] then
398
+ if iopts[:host] and ((not iopts[:host].empty?) and (CipherStashPG.library_version >= 100000)) then
399
+ ihosts = iopts[:host].split(",", -1)
400
+ iports = iopts[:port].split(",", -1)
401
+ iports = [nil] if (iports.size == 0)
402
+ iports = (iports * ihosts.size) if (iports.size == 1)
403
+ if (iports.size != ihosts.size) then
404
+ raise(CipherStashPG::ConnectionBad, "could not match #{iports.size} port numbers to #{ihosts.size} hosts")
405
+ end
406
+ dests = ihosts.each_with_index.flat_map do |mhost, idx|
407
+ if host_is_named_pipe?(mhost) then
408
+ hostaddrs = [nil]
409
+ else
410
+ if Fiber.respond_to?(:scheduler) and (Fiber.scheduler and (RUBY_VERSION < "3.1.")) then
411
+ hostaddrs = Thread.new do
412
+ Addrinfo.getaddrinfo(mhost, nil, nil, :STREAM).map(&:ip_address) rescue [""]
413
+ end.value
414
+ else
415
+ hostaddrs = Addrinfo.getaddrinfo(mhost, nil, nil, :STREAM).map(&:ip_address) rescue [""]
416
+ end
417
+ end
418
+ hostaddrs.map { |hostaddr| [hostaddr, mhost, iports[idx]] }
419
+ end
420
+ iopts.merge!(:hostaddr => dests.map { |d| d[0] }.join(","), :host => dests.map { |d| d[1] }.join(","), :port => dests.map { |d| d[2] }.join(","))
421
+ end
422
+ end
423
+ (conn = self.connect_start(iopts) or raise(CipherStashPG::Error, "Unable to create a new connection"))
424
+ if (conn.status == CipherStashPG::CONNECTION_BAD) then
425
+ raise(CipherStashPG::ConnectionBad, conn.error_message)
426
+ end
427
+ conn.send(:async_connect_or_reset, :connect_poll)
428
+ conn
429
+ end)
430
+ private(def host_is_named_pipe?(host_string)
431
+ (host_string.empty? or (host_string.start_with?("/") or (host_string.start_with?("@") or (RUBY_PLATFORM =~ /mingw|mswin/ and host_string =~ /\A([\/\\]|\w:[\/\\])/))))
432
+ end)
433
+ def ping(*args)
434
+ if Fiber.respond_to?(:scheduler) and Fiber.scheduler then
435
+ Thread.new { sync_ping(*args) }.value
436
+ else
437
+ sync_ping(*args)
438
+ end
439
+ end
440
+ alias :async_ping :ping
441
+ REDIRECT_CLASS_METHODS = { :new => ([:async_connect, :sync_connect]), :connect => ([:async_connect, :sync_connect]), :open => ([:async_connect, :sync_connect]), :setdb => ([:async_connect, :sync_connect]), :setdblogin => ([:async_connect, :sync_connect]), :ping => ([:async_ping, :sync_ping]) }
442
+ REDIRECT_SEND_METHODS = { :isnonblocking => ([:async_isnonblocking, :sync_isnonblocking]), :nonblocking? => ([:async_isnonblocking, :sync_isnonblocking]), :put_copy_data => ([:async_put_copy_data, :sync_put_copy_data]), :put_copy_end => ([:async_put_copy_end, :sync_put_copy_end]), :flush => ([:async_flush, :sync_flush]) }
443
+ REDIRECT_METHODS = { :exec => ([:async_exec, :sync_exec]), :query => ([:async_exec, :sync_exec]), :exec_params => ([:async_exec_params, :sync_exec_params]), :prepare => ([:async_prepare, :sync_prepare]), :exec_prepared => ([:async_exec_prepared, :sync_exec_prepared]), :describe_portal => ([:async_describe_portal, :sync_describe_portal]), :describe_prepared => ([:async_describe_prepared, :sync_describe_prepared]), :setnonblocking => ([:async_setnonblocking, :sync_setnonblocking]), :get_result => ([:async_get_result, :sync_get_result]), :get_last_result => ([:async_get_last_result, :sync_get_last_result]), :get_copy_data => ([:async_get_copy_data, :sync_get_copy_data]), :reset => ([:async_reset, :sync_reset]), :set_client_encoding => ([:async_set_client_encoding, :sync_set_client_encoding]), :client_encoding= => ([:async_set_client_encoding, :sync_set_client_encoding]), :cancel => ([:async_cancel, :sync_cancel]) }
444
+ if CipherStashPG::Connection.instance_methods.include?(:async_encrypt_password) then
445
+ REDIRECT_METHODS.merge!(:encrypt_password => ([:async_encrypt_password, :sync_encrypt_password]))
446
+ end
447
+ def async_send_api=(enable)
448
+ REDIRECT_SEND_METHODS.each do |ali, (async, sync)|
449
+ undef_method(ali) if method_defined?(ali)
450
+ alias_method(ali, (enable ? (async) : (sync)))
451
+ end
452
+ end
453
+ def async_api=(enable)
454
+ self.async_send_api = enable
455
+ REDIRECT_METHODS.each do |ali, (async, sync)|
456
+ remove_method(ali) if method_defined?(ali)
457
+ alias_method(ali, (enable ? (async) : (sync)))
458
+ end
459
+ REDIRECT_CLASS_METHODS.each do |ali, (async, sync)|
460
+ singleton_class.remove_method(ali) if method_defined?(ali)
461
+ singleton_class.alias_method(ali, (enable ? (async) : (sync)))
462
+ end
463
+ end
464
+ end
465
+
466
+ self.async_api = true
467
+ end
@@ -0,0 +1,3 @@
1
+ require("cipherstash-pg") unless defined? CipherStashPG
2
+ module CipherStashPG::Constants
3
+ end
@@ -0,0 +1,19 @@
1
+ require("cipherstash-pg") unless defined? CipherStashPG
2
+ module CipherStashPG
3
+ class Error < StandardError
4
+ def initialize(msg = nil, connection: nil, result: nil)
5
+ @connection = connection
6
+ @result = result
7
+ super(msg)
8
+ end
9
+ end
10
+
11
+ class NotAllCopyDataRetrieved < CipherStashPG::Error
12
+ end
13
+
14
+ class LostCopyState < CipherStashPG::Error
15
+ end
16
+
17
+ class NotInBlockingMode < CipherStashPG::Error
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ require("cipherstash-pg") unless defined? CipherStashPG
2
+ class CipherStashPG::Result
3
+ def map_types!(type_map)
4
+ self.type_map = type_map
5
+ return self
6
+ end
7
+
8
+ def field_names_as(type)
9
+ self.field_name_type = type
10
+ return self
11
+ end
12
+
13
+ def inspect
14
+ str = self.to_s
15
+ str[-1, 0] = if cleared? then
16
+ " cleared"
17
+ else
18
+ " status=#{res_status(result_status)} ntuples=#{ntuples} nfields=#{nfields} cmd_tuples=#{cmd_tuples}"
19
+ end
20
+ return str
21
+ end
22
+ end
@@ -0,0 +1,43 @@
1
+ require("date")
2
+ require("json")
3
+ module CipherStashPG
4
+ module TextDecoder
5
+ class Date < SimpleDecoder
6
+ def decode(string, tuple = nil, field = nil)
7
+ if string =~ /\A(\d{4})-(\d\d)-(\d\d)\z/ then
8
+ ::Date.new($1.to_i, $2.to_i, $3.to_i)
9
+ else
10
+ string
11
+ end
12
+ end
13
+ end
14
+
15
+ class JSON < SimpleDecoder
16
+ def decode(string, tuple = nil, field = nil)
17
+ ::JSON.parse(string, :quirks_mode => true)
18
+ end
19
+ end
20
+
21
+ class TimestampUtc < Timestamp
22
+ def initialize(params = {})
23
+ super(params.merge(:flags => (CipherStashPG::Coder::TIMESTAMP_DB_UTC | CipherStashPG::Coder::TIMESTAMP_APP_UTC)))
24
+ end
25
+ end
26
+
27
+ class TimestampUtcToLocal < Timestamp
28
+ def initialize(params = {})
29
+ super(params.merge(:flags => (CipherStashPG::Coder::TIMESTAMP_DB_UTC | CipherStashPG::Coder::TIMESTAMP_APP_LOCAL)))
30
+ end
31
+ end
32
+
33
+ class TimestampLocal < Timestamp
34
+ def initialize(params = {})
35
+ super(params.merge(:flags => (CipherStashPG::Coder::TIMESTAMP_DB_LOCAL | CipherStashPG::Coder::TIMESTAMP_APP_LOCAL)))
36
+ end
37
+ end
38
+
39
+ TimestampWithoutTimeZone = TimestampLocal
40
+
41
+ TimestampWithTimeZone = Timestamp
42
+ end
43
+ end
@@ -0,0 +1,67 @@
1
+ require("json")
2
+ require("ipaddr")
3
+ module CipherStashPG
4
+ module TextEncoder
5
+ class Date < SimpleEncoder
6
+ def encode(value)
7
+ value.respond_to?(:strftime) ? (value.strftime("%Y-%m-%d")) : (value)
8
+ end
9
+ end
10
+
11
+ class TimestampWithoutTimeZone < SimpleEncoder
12
+ def encode(value)
13
+ if value.respond_to?(:strftime) then
14
+ value.strftime("%Y-%m-%d %H:%M:%S.%N")
15
+ else
16
+ value
17
+ end
18
+ end
19
+ end
20
+
21
+ class TimestampUtc < SimpleEncoder
22
+ def encode(value)
23
+ if value.respond_to?(:utc) then
24
+ value.utc.strftime("%Y-%m-%d %H:%M:%S.%N")
25
+ else
26
+ value
27
+ end
28
+ end
29
+ end
30
+
31
+ class TimestampWithTimeZone < SimpleEncoder
32
+ def encode(value)
33
+ if value.respond_to?(:strftime) then
34
+ value.strftime("%Y-%m-%d %H:%M:%S.%N %:z")
35
+ else
36
+ value
37
+ end
38
+ end
39
+ end
40
+
41
+ class JSON < SimpleEncoder
42
+ def encode(value)
43
+ ::JSON.generate(value, :quirks_mode => true)
44
+ end
45
+ end
46
+
47
+ class Inet < SimpleEncoder
48
+ def encode(value)
49
+ case value
50
+ when IPAddr then
51
+ default_prefix = (value.family == Socket::AF_INET) ? (32) : (128)
52
+ s = value.to_s
53
+ if value.respond_to?(:prefix) then
54
+ prefix = value.prefix
55
+ else
56
+ range = value.to_range
57
+ prefix = (default_prefix - Math.log(((range.end.to_i - range.begin.to_i) + 1), 2).to_i)
58
+ end
59
+ ((s << "/") << prefix.to_s) if (prefix != default_prefix)
60
+ s
61
+ else
62
+ value
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,24 @@
1
+ require("cipherstash-pg") unless defined? CipherStashPG
2
+ class CipherStashPG::Tuple
3
+ def inspect
4
+ "#<#{self.class} #{self.map { |k, v| "#{k}: #{v.inspect}" }.join(", ")}>"
5
+ end
6
+
7
+ def has_key?(key)
8
+ field_map.has_key?(key)
9
+ end
10
+
11
+ alias :key? :has_key?
12
+
13
+ def keys
14
+ (field_names or field_map.keys.freeze)
15
+ end
16
+
17
+ def each_key(&block)
18
+ if fn = field_names then
19
+ fn.each(&block)
20
+ else
21
+ field_map.each_key(&block)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ require("cipherstash-pg") unless defined? CipherStashPG
2
+ class CipherStashPG::TypeMapByColumn
3
+ def oids
4
+ coders.map { |c| c.oid if c }
5
+ end
6
+
7
+ def inspect
8
+ type_strings = coders.map { |c| c ? (c.inspect_short) : ("nil") }
9
+ "#<#{self.class} #{type_strings.join(" ")}>"
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module CipherStashPG
2
+ VERSION = "1.0.0.beta.3"
3
+ end