db-postgres 0.9.0 → 0.10.0
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 +4 -4
- checksums.yaml.gz.sig +3 -2
- data/lib/db/postgres/adapter.rb +8 -2
- data/lib/db/postgres/connection.rb +40 -10
- data/lib/db/postgres/error.rb +2 -1
- data/lib/db/postgres/native/connection.rb +34 -6
- data/lib/db/postgres/native/field.rb +6 -6
- data/lib/db/postgres/native/result.rb +27 -2
- data/lib/db/postgres/native/types.rb +65 -6
- data/lib/db/postgres/native.rb +6 -5
- data/lib/db/postgres/version.rb +4 -2
- data/lib/db/postgres.rb +5 -5
- data/license.md +1 -1
- data.tar.gz.sig +0 -0
- metadata +2 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 260820a8ff70bad753039f66377343b0740b0925cd7f8e54b4a6b5ccc6ff01e5
|
|
4
|
+
data.tar.gz: 6e65a9610fd6f07c3b026852e27f7b5d03aa0a08f2a04520c493d6d2f08a56ff
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 726edf7a8bc3bbfe27a4faa1c1450dfaadace53b90a198e92fcff8558f9ca484012fd2b0ea9272421f3ffaf4b2fcec768cf90cfcbf7abdfd0d3b72b08271e454
|
|
7
|
+
data.tar.gz: 4fc82ac8ffdb9908c13b2fea11b69c8a4f83a4a3c8d99eab75b92c4c043d901d025b6bcf115a7f4cf3d86bc8591a30055afc38869ceadbd7be8ed4f298505a6e
|
checksums.yaml.gz.sig
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
C+gY]�e�� 8/}����1 ���L̍�t��Y��5����r���ݴ��lO�=�`�R����?�wF�X.j�o]Q�1���!�-Y�5�-b�R3�.z,�a� ���0�b�Ȋ�:qv����/��փ;L���xa+r�=
|
|
2
|
+
����Q諥��������n��t����!�?t�F�1����ߚl���<�&C[Q��g!##m��6��+ڵ��悀8p�#@�ƥsŊw�tM)paMaR��
|
|
3
|
+
v�'R G����ԱKD�����[����(|�NV,OV=5�.C稦����� [c`qj%��Փk�"�fs��Pt���4d:�g3�/�XI�JbW��H���d��QMز��0V��sYj�K��ثɾ
|
data/lib/db/postgres/adapter.rb
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2018-
|
|
4
|
+
# Copyright, 2018-2026, by Samuel Williams.
|
|
5
5
|
|
|
6
|
-
require_relative
|
|
6
|
+
require_relative "connection"
|
|
7
7
|
|
|
8
8
|
module DB
|
|
9
9
|
module Postgres
|
|
10
|
+
# A database adapter for connecting to PostgreSQL servers.
|
|
10
11
|
class Adapter
|
|
12
|
+
# Initialize a new adapter with connection options.
|
|
13
|
+
# @parameter options [Hash] Connection options to be passed to the connection.
|
|
11
14
|
def initialize(**options)
|
|
12
15
|
@options = options
|
|
13
16
|
end
|
|
14
17
|
|
|
18
|
+
# @attribute [Hash] The connection options.
|
|
15
19
|
attr :options
|
|
16
20
|
|
|
21
|
+
# Create a new database connection.
|
|
22
|
+
# @returns [Connection] A new connection instance.
|
|
17
23
|
def call
|
|
18
24
|
Connection.new(**@options)
|
|
19
25
|
end
|
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2018-
|
|
4
|
+
# Copyright, 2018-2026, by Samuel Williams.
|
|
5
5
|
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
require_relative
|
|
6
|
+
require "async/pool/resource"
|
|
7
|
+
require "db/features"
|
|
8
|
+
require_relative "native/connection"
|
|
9
9
|
|
|
10
10
|
module DB
|
|
11
11
|
module Postgres
|
|
12
|
-
#
|
|
12
|
+
# A high-level database connection that implements the standardized connection interface.
|
|
13
|
+
# This class provides a bridge between the underlying native PostgreSQL interface and the DB gem's unified connection API.
|
|
13
14
|
class Connection < Async::Pool::Resource
|
|
15
|
+
# Initialize a new database connection.
|
|
16
|
+
# @parameter options [Hash] Connection options passed to the native connection.
|
|
14
17
|
def initialize(**options)
|
|
15
18
|
@native = Native::Connection.connect(**options)
|
|
16
19
|
|
|
17
20
|
super()
|
|
18
21
|
end
|
|
19
22
|
|
|
23
|
+
# Close the database connection and release resources.
|
|
20
24
|
def close
|
|
21
25
|
if @native
|
|
22
26
|
@native&.close
|
|
@@ -26,16 +30,26 @@ module DB
|
|
|
26
30
|
super
|
|
27
31
|
end
|
|
28
32
|
|
|
33
|
+
# Get the type mapping for database types.
|
|
34
|
+
# @returns [Hash] The type mapping configuration.
|
|
29
35
|
def types
|
|
30
36
|
@native.types
|
|
31
37
|
end
|
|
32
38
|
|
|
39
|
+
# Append an escaped string value to the buffer.
|
|
40
|
+
# @parameter value [String] The string value to escape and append.
|
|
41
|
+
# @parameter buffer [String] The buffer to append to.
|
|
42
|
+
# @returns [String] The buffer with the escaped string appended.
|
|
33
43
|
def append_string(value, buffer = String.new)
|
|
34
44
|
buffer << @native.escape_literal(value)
|
|
35
45
|
|
|
36
46
|
return buffer
|
|
37
47
|
end
|
|
38
48
|
|
|
49
|
+
# Append a literal value to the buffer with appropriate formatting.
|
|
50
|
+
# @parameter value [Object] The value to append (supports Time, Date, Numeric, Boolean, nil, and strings).
|
|
51
|
+
# @parameter buffer [String] The buffer to append to.
|
|
52
|
+
# @returns [String] The buffer with the formatted value appended.
|
|
39
53
|
def append_literal(value, buffer = String.new)
|
|
40
54
|
case value
|
|
41
55
|
when Time, DateTime, Date
|
|
@@ -43,11 +57,11 @@ module DB
|
|
|
43
57
|
when Numeric
|
|
44
58
|
buffer << value.to_s
|
|
45
59
|
when TrueClass
|
|
46
|
-
buffer <<
|
|
60
|
+
buffer << "TRUE"
|
|
47
61
|
when FalseClass
|
|
48
|
-
buffer <<
|
|
62
|
+
buffer << "FALSE"
|
|
49
63
|
when nil
|
|
50
|
-
buffer <<
|
|
64
|
+
buffer << "NULL"
|
|
51
65
|
else
|
|
52
66
|
append_string(value, buffer)
|
|
53
67
|
end
|
|
@@ -55,12 +69,16 @@ module DB
|
|
|
55
69
|
return buffer
|
|
56
70
|
end
|
|
57
71
|
|
|
72
|
+
# Append an escaped identifier to the buffer.
|
|
73
|
+
# @parameter value [String | Array(String)] The identifier or array of identifiers to escape.
|
|
74
|
+
# @parameter buffer [String] The buffer to append to.
|
|
75
|
+
# @returns [String] The buffer with the escaped identifier appended.
|
|
58
76
|
def append_identifier(value, buffer = String.new)
|
|
59
77
|
case value
|
|
60
78
|
when Array
|
|
61
79
|
first = true
|
|
62
80
|
value.each do |part|
|
|
63
|
-
buffer <<
|
|
81
|
+
buffer << "." unless first
|
|
64
82
|
first = false
|
|
65
83
|
|
|
66
84
|
buffer << @native.escape_identifier(part)
|
|
@@ -72,7 +90,12 @@ module DB
|
|
|
72
90
|
return buffer
|
|
73
91
|
end
|
|
74
92
|
|
|
75
|
-
|
|
93
|
+
# Generate a key column definition for table creation.
|
|
94
|
+
# @parameter name [String] The column name.
|
|
95
|
+
# @parameter primary [Boolean] Whether this is a primary key column.
|
|
96
|
+
# @parameter null [Boolean] Whether this column allows null values.
|
|
97
|
+
# @returns [String] The column definition string.
|
|
98
|
+
def key_column(name = "id", primary: true, null: false)
|
|
76
99
|
buffer = String.new
|
|
77
100
|
|
|
78
101
|
append_identifier(name, buffer)
|
|
@@ -92,16 +115,22 @@ module DB
|
|
|
92
115
|
return buffer
|
|
93
116
|
end
|
|
94
117
|
|
|
118
|
+
# Get the current connection status.
|
|
119
|
+
# @returns [Symbol] The status symbol from the server.
|
|
95
120
|
def status
|
|
96
121
|
@native.status
|
|
97
122
|
end
|
|
98
123
|
|
|
124
|
+
# Send a query to the database server.
|
|
125
|
+
# @parameter statement [String] The SQL statement to execute.
|
|
99
126
|
def send_query(statement)
|
|
100
127
|
@native.discard_results
|
|
101
128
|
|
|
102
129
|
@native.send_query(statement)
|
|
103
130
|
end
|
|
104
131
|
|
|
132
|
+
# Get the next result set from a multi-result query.
|
|
133
|
+
# @returns [Native::Result | Nil] The next result set, or `nil` if no more results.
|
|
105
134
|
def next_result
|
|
106
135
|
@native.next_result
|
|
107
136
|
end
|
|
@@ -117,6 +146,7 @@ module DB
|
|
|
117
146
|
)
|
|
118
147
|
|
|
119
148
|
# Database feature detection for migration and query building.
|
|
149
|
+
# @returns [DB::Features] The supported database features.
|
|
120
150
|
def features
|
|
121
151
|
FEATURES
|
|
122
152
|
end
|
data/lib/db/postgres/error.rb
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2020-
|
|
4
|
+
# Copyright, 2020-2026, by Samuel Williams.
|
|
5
5
|
|
|
6
6
|
module DB
|
|
7
7
|
module Postgres
|
|
8
|
+
# An error raised by the PostgreSQL adapter.
|
|
8
9
|
class Error < StandardError
|
|
9
10
|
end
|
|
10
11
|
end
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2018-
|
|
4
|
+
# Copyright, 2018-2026, by Samuel Williams.
|
|
5
5
|
|
|
6
|
-
require_relative
|
|
7
|
-
require_relative
|
|
8
|
-
require_relative
|
|
6
|
+
require_relative "result"
|
|
7
|
+
require_relative "field"
|
|
8
|
+
require_relative "../error"
|
|
9
9
|
|
|
10
10
|
module DB
|
|
11
11
|
module Postgres
|
|
12
12
|
module Native
|
|
13
|
+
# Helper class for managing FFI string arrays.
|
|
13
14
|
class Strings
|
|
15
|
+
# Initialize a string array for FFI.
|
|
16
|
+
# @parameter values [Array] The array of values to convert to FFI strings.
|
|
14
17
|
def initialize(values)
|
|
15
18
|
@array = FFI::MemoryPointer.new(:pointer, values.size + 1)
|
|
16
19
|
@pointers = values.map do |value|
|
|
@@ -19,6 +22,7 @@ module DB
|
|
|
19
22
|
@array.write_array_of_pointer(@pointers)
|
|
20
23
|
end
|
|
21
24
|
|
|
25
|
+
# @attribute [FFI::MemoryPointer] The FFI array pointer.
|
|
22
26
|
attr :array
|
|
23
27
|
end
|
|
24
28
|
|
|
@@ -46,7 +50,7 @@ module DB
|
|
|
46
50
|
# Non-blocking mode:
|
|
47
51
|
:started, # Waiting for connection to be made.
|
|
48
52
|
:made, # Connection OK; waiting to send.
|
|
49
|
-
:awaiting_response, #Waiting for a response from the postmaster.
|
|
53
|
+
:awaiting_response, # Waiting for a response from the postmaster.
|
|
50
54
|
:auth_ok, # Received authentication; waiting for backend startup.
|
|
51
55
|
:setenv, # Negotiating environment.
|
|
52
56
|
:ssl_startup, # Negotiating SSL.
|
|
@@ -81,7 +85,13 @@ module DB
|
|
|
81
85
|
ffi_attach_function :PQescapeLiteral, [:pointer, :string, :size_t], :pointer, as: :escape_literal
|
|
82
86
|
ffi_attach_function :PQescapeIdentifier, [:pointer, :string, :size_t], :pointer, as: :escape_identifier
|
|
83
87
|
|
|
88
|
+
# A native FFI connection to the PostgreSQL client library.
|
|
84
89
|
class Connection < FFI::Pointer
|
|
90
|
+
# Establish a connection to the PostgreSQL server.
|
|
91
|
+
# @parameter types [Hash] Type mapping configuration.
|
|
92
|
+
# @parameter options [Hash] Connection options (database, username, password, host, port, etc.).
|
|
93
|
+
# @returns [Connection] A new connected instance.
|
|
94
|
+
# @raises [Error] If the connection fails.
|
|
85
95
|
def self.connect(types: DEFAULT_TYPES, **options)
|
|
86
96
|
# Postgres expects "dbname" as the key name:
|
|
87
97
|
if database = options.delete(:database)
|
|
@@ -96,7 +106,7 @@ module DB
|
|
|
96
106
|
keys = Strings.new(options.keys)
|
|
97
107
|
values = Strings.new(options.values)
|
|
98
108
|
|
|
99
|
-
pointer = Native.connect_start_params(keys.array, values.array,
|
|
109
|
+
pointer = Native.connect_start_params(keys.array, values.array, 1)
|
|
100
110
|
Native.set_nonblocking(pointer, 1)
|
|
101
111
|
|
|
102
112
|
io = ::IO.new(Native.socket(pointer), "r+", autoclose: false)
|
|
@@ -121,6 +131,10 @@ module DB
|
|
|
121
131
|
return self.new(pointer, io, types)
|
|
122
132
|
end
|
|
123
133
|
|
|
134
|
+
# Initialize a native connection wrapper.
|
|
135
|
+
# @parameter address [FFI::Pointer] The pointer to the native connection.
|
|
136
|
+
# @parameter io [IO] The IO object for the socket.
|
|
137
|
+
# @parameter types [Hash] Type mapping configuration.
|
|
124
138
|
def initialize(address, io, types)
|
|
125
139
|
super(address)
|
|
126
140
|
|
|
@@ -128,6 +142,7 @@ module DB
|
|
|
128
142
|
@types = types
|
|
129
143
|
end
|
|
130
144
|
|
|
145
|
+
# @attribute [Hash] The type mapping configuration.
|
|
131
146
|
attr :types
|
|
132
147
|
|
|
133
148
|
# Return the status of the connection.
|
|
@@ -152,6 +167,9 @@ module DB
|
|
|
152
167
|
@io.close
|
|
153
168
|
end
|
|
154
169
|
|
|
170
|
+
# Escape a literal string value for safe inclusion in SQL queries.
|
|
171
|
+
# @parameter value [String] The value to escape.
|
|
172
|
+
# @returns [String] The escaped string with quotes.
|
|
155
173
|
def escape_literal(value)
|
|
156
174
|
value = value.to_s
|
|
157
175
|
|
|
@@ -164,6 +182,9 @@ module DB
|
|
|
164
182
|
return string
|
|
165
183
|
end
|
|
166
184
|
|
|
185
|
+
# Escape an identifier for safe inclusion in SQL queries.
|
|
186
|
+
# @parameter value [String] The identifier to escape.
|
|
187
|
+
# @returns [String] The escaped identifier with quotes.
|
|
167
188
|
def escape_identifier(value)
|
|
168
189
|
value = value.to_s
|
|
169
190
|
|
|
@@ -176,16 +197,23 @@ module DB
|
|
|
176
197
|
return string
|
|
177
198
|
end
|
|
178
199
|
|
|
200
|
+
# Enable single row mode for streaming large result sets.
|
|
179
201
|
def single_row_mode!
|
|
180
202
|
Native.set_single_row_mode(self)
|
|
181
203
|
end
|
|
182
204
|
|
|
205
|
+
# Send a query to the server for execution.
|
|
206
|
+
# @parameter statement [String] The SQL statement to execute.
|
|
183
207
|
def send_query(statement)
|
|
184
208
|
check! Native.send_query(self, statement)
|
|
185
209
|
|
|
186
210
|
flush
|
|
187
211
|
end
|
|
188
212
|
|
|
213
|
+
# Get the next result set from a multi-result query.
|
|
214
|
+
# @parameter types [Hash] Type mapping to use for this result.
|
|
215
|
+
# @returns [Result | Nil] The next result set, or `nil` if no more results.
|
|
216
|
+
# @raises [Error] If the query resulted in an error.
|
|
189
217
|
def next_result(types: @types)
|
|
190
218
|
if result = self.get_result
|
|
191
219
|
status = Native.result_status(result)
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2020-
|
|
4
|
+
# Copyright, 2020-2026, by Samuel Williams.
|
|
5
5
|
|
|
6
|
-
require_relative
|
|
6
|
+
require_relative "types"
|
|
7
7
|
|
|
8
8
|
module DB
|
|
9
9
|
module Postgres
|
|
10
10
|
module Native
|
|
11
11
|
DEFAULT_TYPES = {
|
|
12
12
|
# Pseudo types:
|
|
13
|
-
primary_key: Types::Integer.new(
|
|
14
|
-
foreign_key: Types::Integer.new(
|
|
13
|
+
primary_key: Types::Integer.new("BIGSERIAL PRIMARY KEY"),
|
|
14
|
+
foreign_key: Types::Integer.new("BIGINT"),
|
|
15
15
|
text: Types::Text.new("TEXT"),
|
|
16
16
|
string: Types::Text.new("VARCHAR(255)"),
|
|
17
17
|
|
|
@@ -46,8 +46,8 @@ module DB
|
|
|
46
46
|
|
|
47
47
|
114 => Types::JSON.new,
|
|
48
48
|
|
|
49
|
-
700 => Types::Float.new(
|
|
50
|
-
701 => Types::Float.new(
|
|
49
|
+
700 => Types::Float.new("float4"),
|
|
50
|
+
701 => Types::Float.new("float8"),
|
|
51
51
|
|
|
52
52
|
1082 => Types::Date.new,
|
|
53
53
|
1083 => Types::DateTime.new("TIME"),
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2018-
|
|
4
|
+
# Copyright, 2018-2026, by Samuel Williams.
|
|
5
5
|
|
|
6
|
-
require_relative
|
|
6
|
+
require_relative "../native"
|
|
7
7
|
|
|
8
8
|
module DB
|
|
9
9
|
module Postgres
|
|
@@ -38,7 +38,12 @@ module DB
|
|
|
38
38
|
ffi_attach_function :PQgetCopyData, [:pointer, :pointer, :int], :int, as: :get_copy_data
|
|
39
39
|
ffi_attach_function :PQfreemem, [:pointer], :void, as: :free_memory
|
|
40
40
|
|
|
41
|
+
# A result set from a database query with row iteration and type casting.
|
|
41
42
|
class Result < FFI::Pointer
|
|
43
|
+
# Initialize a new result set wrapper.
|
|
44
|
+
# @parameter connection [Connection] The connection that produced this result.
|
|
45
|
+
# @parameter types [Hash] Type mapping for field conversion.
|
|
46
|
+
# @parameter address [FFI::Pointer] The pointer to the native result.
|
|
42
47
|
def initialize(connection, types = {}, address)
|
|
43
48
|
super(address)
|
|
44
49
|
|
|
@@ -48,22 +53,33 @@ module DB
|
|
|
48
53
|
@casts = nil
|
|
49
54
|
end
|
|
50
55
|
|
|
56
|
+
# Get the number of fields in this result set.
|
|
57
|
+
# @returns [Integer] The field count.
|
|
51
58
|
def field_count
|
|
52
59
|
Native.field_count(self)
|
|
53
60
|
end
|
|
54
61
|
|
|
62
|
+
# Get the type converters for each field.
|
|
63
|
+
# @returns [Array] The array of type converter objects.
|
|
55
64
|
def field_types
|
|
56
65
|
field_count.times.collect{|i| @types[Native.field_type(self, i)]}
|
|
57
66
|
end
|
|
58
67
|
|
|
68
|
+
# Get the field names for this result set.
|
|
69
|
+
# @returns [Array(String)] The array of field names.
|
|
59
70
|
def field_names
|
|
60
71
|
field_count.times.collect{|i| Native.field_name(self, i)}
|
|
61
72
|
end
|
|
62
73
|
|
|
74
|
+
# Get the number of rows in this result set.
|
|
75
|
+
# @returns [Integer] The row count.
|
|
63
76
|
def row_count
|
|
64
77
|
Native.row_count(self)
|
|
65
78
|
end
|
|
66
79
|
|
|
80
|
+
# Cast row values to appropriate Ruby types.
|
|
81
|
+
# @parameter row [Array] The raw row data.
|
|
82
|
+
# @returns [Array] The row with values cast to proper types.
|
|
67
83
|
def cast!(row)
|
|
68
84
|
@casts ||= self.field_types
|
|
69
85
|
|
|
@@ -76,6 +92,9 @@ module DB
|
|
|
76
92
|
return row
|
|
77
93
|
end
|
|
78
94
|
|
|
95
|
+
# Iterate over each row in the result set.
|
|
96
|
+
# @yields {|row| ...} Each row as an array.
|
|
97
|
+
# @parameter row [Array] The current row data.
|
|
79
98
|
def each
|
|
80
99
|
row_count.times do |i|
|
|
81
100
|
yield cast!(get_row(i))
|
|
@@ -84,6 +103,10 @@ module DB
|
|
|
84
103
|
Native.clear(self)
|
|
85
104
|
end
|
|
86
105
|
|
|
106
|
+
# Map over each row in the result set.
|
|
107
|
+
# @yields {|row| ...} Each row as an array.
|
|
108
|
+
# @parameter row [Array] The current row data.
|
|
109
|
+
# @returns [Array] The mapped results.
|
|
87
110
|
def map(&block)
|
|
88
111
|
results = []
|
|
89
112
|
|
|
@@ -94,6 +117,8 @@ module DB
|
|
|
94
117
|
return results
|
|
95
118
|
end
|
|
96
119
|
|
|
120
|
+
# Convert the entire result set to an array.
|
|
121
|
+
# @returns [Array(Array)] All rows as arrays.
|
|
97
122
|
def to_a
|
|
98
123
|
rows = []
|
|
99
124
|
|
|
@@ -1,90 +1,137 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2020-
|
|
4
|
+
# Copyright, 2020-2026, by Samuel Williams.
|
|
5
5
|
|
|
6
|
-
require
|
|
7
|
-
require
|
|
6
|
+
require "json"
|
|
7
|
+
require "bigdecimal"
|
|
8
8
|
|
|
9
9
|
module DB
|
|
10
10
|
module Postgres
|
|
11
11
|
module Native
|
|
12
|
+
# Provides type converters for translating between PostgreSQL types and Ruby types.
|
|
12
13
|
module Types
|
|
14
|
+
# A text/string type converter.
|
|
13
15
|
class Text
|
|
16
|
+
# Initialize a text type converter.
|
|
17
|
+
# @parameter name [String] The SQL type name.
|
|
14
18
|
def initialize(name = "TEXT")
|
|
15
19
|
@name = name
|
|
16
20
|
end
|
|
17
21
|
|
|
22
|
+
# @attribute [String] The SQL type name.
|
|
18
23
|
attr :name
|
|
19
24
|
|
|
25
|
+
# Parse a string value from the database.
|
|
26
|
+
# @parameter string [String | Nil] The raw string value.
|
|
27
|
+
# @returns [String | Nil] The string value.
|
|
20
28
|
def parse(string)
|
|
21
29
|
string
|
|
22
30
|
end
|
|
23
31
|
end
|
|
24
32
|
|
|
33
|
+
# An integer type converter.
|
|
25
34
|
class Integer
|
|
35
|
+
# Initialize an integer type converter.
|
|
36
|
+
# @parameter name [String] The SQL type name.
|
|
26
37
|
def initialize(name = "INTEGER")
|
|
27
38
|
@name = name
|
|
28
39
|
end
|
|
29
40
|
|
|
41
|
+
# @attribute [String] The SQL type name.
|
|
30
42
|
attr :name
|
|
31
43
|
|
|
44
|
+
# Parse an integer value from the database.
|
|
45
|
+
# @parameter string [String | Nil] The raw string value.
|
|
46
|
+
# @returns [Integer | Nil] The parsed integer.
|
|
32
47
|
def parse(string)
|
|
33
48
|
Integer(string) if string
|
|
34
49
|
end
|
|
35
50
|
end
|
|
36
51
|
|
|
52
|
+
# A boolean type converter.
|
|
37
53
|
class Boolean
|
|
54
|
+
# Get the SQL type name for boolean.
|
|
55
|
+
# @returns [String] The type name.
|
|
38
56
|
def name
|
|
39
57
|
"BOOLEAN"
|
|
40
58
|
end
|
|
41
59
|
|
|
60
|
+
# Parse a boolean value from the database.
|
|
61
|
+
# @parameter string [String | Nil] The raw string value ('t' or 'f').
|
|
62
|
+
# @returns [Boolean] The parsed boolean value.
|
|
42
63
|
def parse(string)
|
|
43
|
-
string ==
|
|
64
|
+
string == "t"
|
|
44
65
|
end
|
|
45
66
|
end
|
|
46
67
|
|
|
68
|
+
# A decimal type converter.
|
|
47
69
|
class Decimal
|
|
70
|
+
# Get the SQL type name for decimal.
|
|
71
|
+
# @returns [String] The type name.
|
|
48
72
|
def name
|
|
49
73
|
"DECIMAL"
|
|
50
74
|
end
|
|
51
75
|
|
|
76
|
+
# Parse a decimal value from the database.
|
|
77
|
+
# @parameter string [String | Nil] The raw string value.
|
|
78
|
+
# @returns [BigDecimal | Nil] The parsed decimal.
|
|
52
79
|
def parse(string)
|
|
53
80
|
BigDecimal(string) if string
|
|
54
81
|
end
|
|
55
82
|
end
|
|
56
83
|
|
|
84
|
+
# A floating point type converter.
|
|
57
85
|
class Float
|
|
86
|
+
# Initialize a float type converter.
|
|
87
|
+
# @parameter name [String] The SQL type name.
|
|
58
88
|
def initialize(name = "FLOAT")
|
|
59
89
|
@name = name
|
|
60
90
|
end
|
|
61
91
|
|
|
92
|
+
# @attribute [String] The SQL type name.
|
|
62
93
|
attr :name
|
|
63
94
|
|
|
95
|
+
# Parse a float value from the database.
|
|
96
|
+
# @parameter string [String | Nil] The raw string value.
|
|
97
|
+
# @returns [Float | Nil] The parsed float.
|
|
64
98
|
def parse(string)
|
|
65
99
|
Float(string) if string
|
|
66
100
|
end
|
|
67
101
|
end
|
|
68
102
|
|
|
103
|
+
# A symbol/enum type converter.
|
|
69
104
|
class Symbol
|
|
105
|
+
# Get the SQL type name for enum.
|
|
106
|
+
# @returns [String] The type name.
|
|
70
107
|
def name
|
|
71
108
|
"ENUM"
|
|
72
109
|
end
|
|
73
110
|
|
|
111
|
+
# Parse a symbol value from the database.
|
|
112
|
+
# @parameter string [String | Nil] The raw string value.
|
|
113
|
+
# @returns [Symbol | Nil] The parsed symbol.
|
|
74
114
|
def parse(string)
|
|
75
115
|
string&.to_sym
|
|
76
116
|
end
|
|
77
117
|
end
|
|
78
118
|
|
|
119
|
+
# A datetime/timestamp type converter.
|
|
79
120
|
class DateTime
|
|
121
|
+
# Initialize a datetime type converter.
|
|
122
|
+
# @parameter name [String] The SQL type name.
|
|
80
123
|
def initialize(name = "TIMESTAMP")
|
|
81
124
|
@name = name
|
|
82
125
|
end
|
|
83
126
|
|
|
127
|
+
# @attribute [String] The SQL type name.
|
|
84
128
|
attr :name
|
|
85
129
|
|
|
130
|
+
# Parse a datetime value from the database.
|
|
131
|
+
# @parameter string [String | Nil] The raw string value or special values like 'infinity'.
|
|
132
|
+
# @returns [Time | String | Nil] The parsed datetime as a Time object, or special values as strings.
|
|
86
133
|
def parse(string)
|
|
87
|
-
if string ==
|
|
134
|
+
if string == "-infinity" || string == "infinity" || string.nil?
|
|
88
135
|
return string
|
|
89
136
|
end
|
|
90
137
|
|
|
@@ -94,7 +141,7 @@ module DB
|
|
|
94
141
|
parts[5] = Rational(parts[5])
|
|
95
142
|
|
|
96
143
|
if parts[6].nil?
|
|
97
|
-
parts[6] =
|
|
144
|
+
parts[6] = "+00"
|
|
98
145
|
end
|
|
99
146
|
|
|
100
147
|
return Time.new(*parts)
|
|
@@ -102,11 +149,17 @@ module DB
|
|
|
102
149
|
end
|
|
103
150
|
end
|
|
104
151
|
|
|
152
|
+
# A date type converter.
|
|
105
153
|
class Date
|
|
154
|
+
# Get the SQL type name for date.
|
|
155
|
+
# @returns [String] The type name.
|
|
106
156
|
def name
|
|
107
157
|
"DATE"
|
|
108
158
|
end
|
|
109
159
|
|
|
160
|
+
# Parse a date value from the database.
|
|
161
|
+
# @parameter string [String | Nil] The raw string value.
|
|
162
|
+
# @returns [Time | Nil] The parsed date as a UTC Time object.
|
|
110
163
|
def parse(string)
|
|
111
164
|
if string
|
|
112
165
|
parts = string.split(/[\-\s:]/)
|
|
@@ -116,11 +169,17 @@ module DB
|
|
|
116
169
|
end
|
|
117
170
|
end
|
|
118
171
|
|
|
172
|
+
# A JSON type converter.
|
|
119
173
|
class JSON
|
|
174
|
+
# Get the SQL type name for JSON.
|
|
175
|
+
# @returns [String] The type name.
|
|
120
176
|
def name
|
|
121
177
|
"JSON"
|
|
122
178
|
end
|
|
123
179
|
|
|
180
|
+
# Parse a JSON value from the database.
|
|
181
|
+
# @parameter string [String | Nil] The raw string value.
|
|
182
|
+
# @returns [Hash | Array | Nil] The parsed JSON with symbolized keys.
|
|
124
183
|
def parse(string)
|
|
125
184
|
::JSON.parse(string, symbolize_names: true) if string
|
|
126
185
|
end
|
data/lib/db/postgres/native.rb
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2018-
|
|
4
|
+
# Copyright, 2018-2026, by Samuel Williams.
|
|
5
5
|
|
|
6
|
-
require
|
|
7
|
-
require
|
|
6
|
+
require "ffi/native"
|
|
7
|
+
require "ffi/native/config_tool"
|
|
8
8
|
|
|
9
9
|
module DB
|
|
10
10
|
module Postgres
|
|
11
|
+
# Provides FFI bindings to the native PostgreSQL client library (libpq).
|
|
11
12
|
module Native
|
|
12
13
|
extend FFI::Native::Library
|
|
13
14
|
extend FFI::Native::Loader
|
|
14
15
|
extend FFI::Native::ConfigTool
|
|
15
16
|
|
|
16
|
-
ffi_load(
|
|
17
|
-
ffi_load_using_config_tool(%w{pg_config --libdir}, names: [
|
|
17
|
+
ffi_load("pq") ||
|
|
18
|
+
ffi_load_using_config_tool(%w{pg_config --libdir}, names: ["pq"]) ||
|
|
18
19
|
ffi_load_failure(<<~EOF)
|
|
19
20
|
Unable to load libpq!
|
|
20
21
|
|
data/lib/db/postgres/version.rb
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2018-
|
|
4
|
+
# Copyright, 2018-2026, by Samuel Williams.
|
|
5
5
|
|
|
6
|
+
# @namespace
|
|
6
7
|
module DB
|
|
8
|
+
# @namespace
|
|
7
9
|
module Postgres
|
|
8
|
-
VERSION = "0.
|
|
10
|
+
VERSION = "0.10.0"
|
|
9
11
|
end
|
|
10
12
|
end
|
data/lib/db/postgres.rb
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2018-
|
|
4
|
+
# Copyright, 2018-2026, by Samuel Williams.
|
|
5
5
|
|
|
6
|
-
require_relative
|
|
7
|
-
require_relative
|
|
6
|
+
require_relative "postgres/native"
|
|
7
|
+
require_relative "postgres/connection"
|
|
8
8
|
|
|
9
|
-
require_relative
|
|
9
|
+
require_relative "postgres/adapter"
|
|
10
10
|
|
|
11
|
-
require
|
|
11
|
+
require "db/adapters"
|
|
12
12
|
DB::Adapters.register(:postgres, DB::Postgres::Adapter)
|
data/license.md
CHANGED
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: db-postgres
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -122,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
122
122
|
- !ruby/object:Gem::Version
|
|
123
123
|
version: '0'
|
|
124
124
|
requirements: []
|
|
125
|
-
rubygems_version:
|
|
125
|
+
rubygems_version: 4.0.3
|
|
126
126
|
specification_version: 4
|
|
127
127
|
summary: Ruby FFI bindings for libpq C interface.
|
|
128
128
|
test_files: []
|
metadata.gz.sig
CHANGED
|
Binary file
|