Module: NusaDB::Protocol

Defined in:
lib/nusadb/protocol.rb

Overview

Low-level Nusa Wire Protocol codec (docs/wire-protocol.md, PROTOCOL_VERSION 1.1).

A frame is [type:u8][len:u32][payload]; len is the total including the 5-byte header. Big-endian throughout; strings are [len:u32][utf8 bytes], not null-terminated.

Constant Summary collapse

MAGIC =

"NUSA"

0x4E55_5341
MAJOR =
1
MINOR =

Request minor 1 to receive the typed RowDescription (per-column type tags). A 1.0 server ignores it and answers with the classic untyped form, which the driver still handles.

1
HEADER_LEN =
5
MAX_FRAME_LEN =
256 * 1024 * 1024
SCRAM_MECHANISM =
'SCRAM-SHA-256'
T_STARTUP =

Frontend type bytes.

'S'.ord
T_QUERY =
'Q'.ord
T_PARSE =
'P'.ord
T_BIND =
'B'.ord
T_DESCRIBE =
'D'.ord
T_EXECUTE =
'E'.ord
T_SYNC =
'Y'.ord
T_SASL_INITIAL =
'p'.ord
T_SASL_RESPONSE =
'r'.ord
T_TERMINATE =
'X'.ord
T_COPY_DATA =

COPY ... FROM STDIN data chunk (§12.1)

'd'.ord
T_COPY_DONE =

end of the client's COPY data stream

'c'.ord
T_COPY_FAIL =

abort the in-progress COPY: [message:Str]

'f'.ord
B_AUTH =

Backend type bytes.

'R'.ord
B_BACKEND_KEY =
'K'.ord
B_READY =
'Z'.ord
B_COMMAND_COMPLETE =
'C'.ord
B_ERROR =
'E'.ord
B_ROW_DESCRIPTION =
'T'.ord
B_ROW_DESCRIPTION_TYPED =

protocol 1.1 (typed columns)

'y'.ord
B_DATA_ROW =
'D'.ord
B_COPY_IN =

CopyInResponse (§12.1)

'G'.ord
B_COPY_OUT =

CopyOutResponse (§12.2)

'H'.ord
B_COPY_DATA =

a COPY data chunk

'd'.ord
B_COPY_DONE =

end of the COPY data stream

'c'.ord
B_NOTIFICATION =

async LISTEN/NOTIFY: [pid:u32][channel:str][payload:str]

'A'.ord
TYPE_TAGS =

Column type tags carried by RowDescriptionTyped (protocol 1.1, wire-protocol.md §9.2). Maps the 1-byte tag to a canonical type name; an unknown/0x00 tag is UNKNOWN (treated as text).

{
  0x00 => 'UNKNOWN', 0x01 => 'BOOL', 0x02 => 'INT', 0x03 => 'FLOAT', 0x04 => 'NUMERIC',
  0x05 => 'TEXT', 0x06 => 'BYTES', 0x07 => 'DATE', 0x08 => 'TIME', 0x09 => 'TIMETZ',
  0x0A => 'TIMESTAMP', 0x0B => 'TIMESTAMPTZ', 0x0C => 'INTERVAL', 0x0D => 'UUID',
  0x0E => 'JSON', 0x0F => 'ARRAY', 0x10 => 'VECTOR'
}.freeze
AUTH_OK =

Auth sub-codes.

0
AUTH_SASL =
10
AUTH_SASL_CONTINUE =
11
AUTH_SASL_FINAL =
12

Class Method Summary collapse

Class Method Details

.bind(portal, statement, values) ⇒ Object



115
116
117
# File 'lib/nusadb/protocol.rb', line 115

def bind(portal, statement, values)
  frame(T_BIND, str(portal) + str(statement) + fields(values) + u16(0))
end

.copy_data(data) ⇒ Object



143
144
145
# File 'lib/nusadb/protocol.rb', line 143

def copy_data(data)
  frame(T_COPY_DATA, data)
end

.copy_doneObject



147
148
149
# File 'lib/nusadb/protocol.rb', line 147

def copy_done
  frame(T_COPY_DONE, ''.b)
end

.copy_fail(message) ⇒ Object



151
152
153
# File 'lib/nusadb/protocol.rb', line 151

def copy_fail(message)
  frame(T_COPY_FAIL, str(message))
end

.describe_portal(name) ⇒ Object



119
120
121
# File 'lib/nusadb/protocol.rb', line 119

def describe_portal(name)
  frame(T_DESCRIBE, 'P'.b + str(name))
end

.execute(portal, max_rows = 0) ⇒ Object



123
124
125
# File 'lib/nusadb/protocol.rb', line 123

def execute(portal, max_rows = 0)
  frame(T_EXECUTE, str(portal) + u32(max_rows))
end

.fields(values) ⇒ Object

Encode a Fields list: [count:u16] then per field present byte + optional [len][bytes].



84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/nusadb/protocol.rb', line 84

def fields(values)
  out = u16(values.length)
  values.each do |v|
    if v.nil?
      out += "\x00".b
    else
      v = v.dup.force_encoding(Encoding::BINARY)
      out += "\x01".b + u32(v.bytesize) + v
    end
  end
  out
end

.frame(type, payload) ⇒ Object



97
98
99
100
# File 'lib/nusadb/protocol.rb', line 97

def frame(type, payload)
  total = payload.bytesize + HEADER_LEN
  (type.chr + u32(total) + payload).force_encoding(Encoding::BINARY)
end

.parse(name, sql) ⇒ Object



111
112
113
# File 'lib/nusadb/protocol.rb', line 111

def parse(name, sql)
  frame(T_PARSE, str(name) + str(sql) + u16(0))
end

.query(sql) ⇒ Object



107
108
109
# File 'lib/nusadb/protocol.rb', line 107

def query(sql)
  frame(T_QUERY, str(sql))
end

.sasl_initial(mechanism, data) ⇒ Object



135
136
137
# File 'lib/nusadb/protocol.rb', line 135

def sasl_initial(mechanism, data)
  frame(T_SASL_INITIAL, str(mechanism) + u32(data.bytesize) + data)
end

.sasl_response(data) ⇒ Object



139
140
141
# File 'lib/nusadb/protocol.rb', line 139

def sasl_response(data)
  frame(T_SASL_RESPONSE, data)
end

.startup(user, database) ⇒ Object



102
103
104
105
# File 'lib/nusadb/protocol.rb', line 102

def startup(user, database)
  payload = u32(MAGIC) + u16(MAJOR) + u16(MINOR) + str(user) + str(database)
  frame(T_STARTUP, payload)
end

.str(value) ⇒ Object



78
79
80
81
# File 'lib/nusadb/protocol.rb', line 78

def str(value)
  bytes = value.to_s.dup.force_encoding(Encoding::BINARY)
  u32(bytes.bytesize) + bytes
end

.syncObject



127
128
129
# File 'lib/nusadb/protocol.rb', line 127

def sync
  frame(T_SYNC, ''.b)
end

.terminateObject



131
132
133
# File 'lib/nusadb/protocol.rb', line 131

def terminate
  frame(T_TERMINATE, ''.b)
end

.type_name(tag) ⇒ Object

Canonical type name for a RowDescriptionTyped type tag (UNKNOWN if unrecognised).



58
59
60
# File 'lib/nusadb/protocol.rb', line 58

def self.type_name(tag)
  TYPE_TAGS.fetch(tag, 'UNKNOWN')
end

.u16(value) ⇒ Object



70
71
72
# File 'lib/nusadb/protocol.rb', line 70

def u16(value)
  [value].pack('n')
end

.u32(value) ⇒ Object



74
75
76
# File 'lib/nusadb/protocol.rb', line 74

def u32(value)
  [value].pack('N')
end