ruby_volt 0.0.5

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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +3 -0
  3. data/LICENSE +20 -0
  4. data/README.md +282 -0
  5. data/lib/ruby_volt.rb +33 -0
  6. data/lib/ruby_volt/base.rb +105 -0
  7. data/lib/ruby_volt/connection.rb +150 -0
  8. data/lib/ruby_volt/data_type.rb +50 -0
  9. data/lib/ruby_volt/data_type/app_data_type.rb +24 -0
  10. data/lib/ruby_volt/data_type/app_data_type/procedure_call_status_code.rb +14 -0
  11. data/lib/ruby_volt/data_type/app_data_type/wire_type_info.rb +22 -0
  12. data/lib/ruby_volt/data_type/basic.rb +36 -0
  13. data/lib/ruby_volt/data_type/basic/fixed_size.rb +26 -0
  14. data/lib/ruby_volt/data_type/basic/fixed_size/decimal.rb +45 -0
  15. data/lib/ruby_volt/data_type/basic/fixed_size/float.rb +9 -0
  16. data/lib/ruby_volt/data_type/basic/fixed_size/geography_point.rb +28 -0
  17. data/lib/ruby_volt/data_type/basic/fixed_size/integer_type.rb +20 -0
  18. data/lib/ruby_volt/data_type/basic/fixed_size/integer_type/byte.rb +9 -0
  19. data/lib/ruby_volt/data_type/basic/fixed_size/integer_type/integer.rb +9 -0
  20. data/lib/ruby_volt/data_type/basic/fixed_size/integer_type/long.rb +9 -0
  21. data/lib/ruby_volt/data_type/basic/fixed_size/integer_type/short.rb +9 -0
  22. data/lib/ruby_volt/data_type/basic/fixed_size/integer_type/u_byte.rb +9 -0
  23. data/lib/ruby_volt/data_type/basic/fixed_size/integer_type/u_integer.rb +9 -0
  24. data/lib/ruby_volt/data_type/basic/fixed_size/integer_type/u_long.rb +9 -0
  25. data/lib/ruby_volt/data_type/basic/fixed_size/integer_type/u_short.rb +9 -0
  26. data/lib/ruby_volt/data_type/basic/fixed_size/null.rb +13 -0
  27. data/lib/ruby_volt/data_type/basic/fixed_size/timestamp.rb +24 -0
  28. data/lib/ruby_volt/data_type/basic/geography.rb +61 -0
  29. data/lib/ruby_volt/data_type/basic/string.rb +19 -0
  30. data/lib/ruby_volt/data_type/basic/varbinary.rb +34 -0
  31. data/lib/ruby_volt/data_type/complex.rb +11 -0
  32. data/lib/ruby_volt/data_type/complex/parameter.rb +20 -0
  33. data/lib/ruby_volt/data_type/complex/parameter_set.rb +19 -0
  34. data/lib/ruby_volt/data_type/complex/serializable_exception.rb +43 -0
  35. data/lib/ruby_volt/data_type/complex/volt_table.rb +96 -0
  36. data/lib/ruby_volt/data_type/compound.rb +8 -0
  37. data/lib/ruby_volt/data_type/compound/array.rb +42 -0
  38. data/lib/ruby_volt/data_type/extensions.rb +20 -0
  39. data/lib/ruby_volt/exceptions.rb +67 -0
  40. data/lib/ruby_volt/helper.rb +29 -0
  41. data/lib/ruby_volt/message.rb +29 -0
  42. data/lib/ruby_volt/message/invocation_request.rb +18 -0
  43. data/lib/ruby_volt/message/login_message.rb +17 -0
  44. data/lib/ruby_volt/meta.rb +6 -0
  45. data/lib/ruby_volt/meta/geography.rb +165 -0
  46. data/lib/ruby_volt/read_partial.rb +23 -0
  47. data/lib/ruby_volt/response.rb +24 -0
  48. data/lib/ruby_volt/response/invocation_response.rb +64 -0
  49. data/lib/ruby_volt/response/login_response.rb +27 -0
  50. data/lib/ruby_volt/version.rb +3 -0
  51. data/lib/ruby_volt/volt_table.rb +37 -0
  52. metadata +108 -0
@@ -0,0 +1,9 @@
1
+ module RubyVolt
2
+ class DataType
3
+ class Integer < IntegerType
4
+ DIRECTIVE = 'l>'
5
+ LENGTH = 4
6
+ NULL_INDICATOR = -2147483648 # SQL NULL indicator for object type serializations
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module RubyVolt
2
+ class DataType
3
+ class Long < IntegerType
4
+ DIRECTIVE = 'q>'
5
+ LENGTH = 8
6
+ NULL_INDICATOR = -9223372036854775808 # SQL NULL indicator for object type serializations
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module RubyVolt
2
+ class DataType
3
+ class Short < IntegerType
4
+ DIRECTIVE = 's>'
5
+ LENGTH = 2
6
+ NULL_INDICATOR = -32768 # SQL NULL indicator for object type serializations
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module RubyVolt
2
+ class DataType
3
+ class UByte < IntegerType # Unsigned
4
+ DIRECTIVE = 'C'
5
+ LENGTH = 1
6
+ NULL_INDICATOR = 0 # SQL NULL indicator for object type serializations
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module RubyVolt
2
+ class DataType
3
+ class UInteger < IntegerType # Unsigned
4
+ DIRECTIVE = 'L>'
5
+ LENGTH = 4
6
+ NULL_INDICATOR = 0 # SQL NULL indicator for object type serializations
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module RubyVolt
2
+ class DataType
3
+ class ULong < IntegerType # Unsigned
4
+ DIRECTIVE = 'Q>'
5
+ LENGTH = 8
6
+ NULL_INDICATOR = 0 # SQL NULL indicator for object type serializations
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module RubyVolt
2
+ class DataType
3
+ class UShort < IntegerType # Unsigned
4
+ DIRECTIVE = 'S>'
5
+ LENGTH = 2
6
+ NULL_INDICATOR = 0 # SQL NULL indicator for object type serializations
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module RubyVolt
2
+ class DataType
3
+ class Null < Byte
4
+
5
+ class << self
6
+ def pack(*)
7
+ super(Byte::NULL_INDICATOR)
8
+ end
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ module RubyVolt
2
+ class DataType
3
+ class Timestamp < Long
4
+
5
+ class << self
6
+ def pack(val)
7
+ # All dates are represented on the wire as Long values. This signed number represents the number of microseconds before or after Jan. 1 1970 00:00:00 GMT, the Unix epoch. Note that the units are microseconds, not milliseconds.
8
+ val = case val
9
+ when ::Integer then val
10
+ when ::Time then val.to_i*1000000 + val.usec # Microseconds
11
+ end
12
+ super(val)
13
+ end
14
+
15
+ def unpack(bytes)
16
+ if unpacked = super(bytes)
17
+ Time.at(unpacked/1000000.to_f) # Microseconds
18
+ end
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,61 @@
1
+ module RubyVolt
2
+ class DataType
3
+ class Geography < Basic
4
+ DIRECTIVE = 'a'
5
+ NULL_INDICATOR = -1 # SQL NULL indicator for object type serializations
6
+
7
+ class << self
8
+ def pack(polygon)
9
+ if polygon.nil?
10
+ # Like strings, GeographyValue begins with a 4-byte integer storing the number of bytes of raw data, followed by the raw data itself. The NULL GeographyValue has a length of -1 followed by 0 (zero) bytes of data. Unlike strings, there are no zero byte GeographyValue data values.
11
+ Integer.pack(self::NULL_INDICATOR)
12
+ else
13
+ raw_data =
14
+ Byte.pack(0) + # The first byte, byte0, is an encoding version, which tells whether certain fields need to be initialized by the Execution Engine. This is initially zero (0) and should be maintained on read.
15
+ Byte.pack(1) + # The next byte, byte 1, is internal. It should be initially 1, and should be maintained on read.
16
+ Byte.pack(polygon.has_holes? ? 1 : 0) + # The next byte, byte 2, is 1 if the polygon has holes and 0 if it does not.
17
+ Integer.pack(polygon.rings.size) + # The next four bytes, bytes 3, 4, 5 and 6, comprise a 32 bit integer which gives the number of rings. Call this value `NRINGS`
18
+ polygon.transform_rings.map do |ring| # NRINGS ring representations
19
+ Byte.pack(0) + # The first byte of a ring tells if the ring is initialized. It is initially zero (0) and should be maintained on read.
20
+ Integer.pack(ring.points.size) + # The next 4 bytes are a 32-bit integer containing the number of vertices in the ring. Call this number `NVERTS`.
21
+ ring.points.map do |point| #The next `NVERTS*3*8` bytes are `NVERTS` triples of double precision floating point numbers, in the order `X`, `Y` and `Z`.
22
+ xyz = point.to_XYZPoint
23
+ xyz.map {|double| Float.pack(double)}
24
+ end.join +
25
+ ([0]*38).pack('c38') # The next 38 bytes contain a bounding box and some internal fields. They should all be initially zero (0) and should be maintained on read.
26
+ end.join +
27
+ ([0]*33).pack('c33') # The next 33 bytes, after all the vertices, should be initially zero (0) and should be maintained on read.
28
+ raw_data.prepend(Integer.pack(raw_data.size))
29
+ end
30
+ end
31
+
32
+ def unpack(bytes)
33
+ if (length = Integer.unpack(bytes)) && (length != self::NULL_INDICATOR)
34
+ Byte.unpack(bytes) # The first byte, byte 0, is an encoding version, which tells whether certain fields need to be initialized by the Execution Engine. This is initially zero (0) and should be maintained on read.
35
+ Byte.unpack(bytes) # The next byte, byte 1, is internal. It should be initially 1, and should be maintained on read
36
+ has_holes = Byte.unpack(bytes) # The next byte, byte 2, is 1 if the polygon has holes and 0 if it does not.
37
+ rings_size = Integer.unpack(bytes)
38
+ polygon = Meta::Geography::Polygon.new
39
+ rings_size.times do
40
+ ring = Meta::Geography::Ring.new
41
+ Byte.unpack(bytes) # Ring is initialized. It is initially zero (0) and should be maintained on read.
42
+ verticles_size = Integer.unpack(bytes)
43
+ verticles_size.times do
44
+ x = Float.unpack(bytes)
45
+ y = Float.unpack(bytes)
46
+ z = Float.unpack(bytes)
47
+ point = Meta::Geography::Point.from_XYZPoint(x, y, z)
48
+ ring.add_point(point)
49
+ end
50
+ bytes.read(38).unpack1('c38') # Blob of zeros. 38 bytes contain a bounding box and some internal fields. They should all be initially zero (0) and should be maintained on read.
51
+ polygon.add_ring(ring)
52
+ end
53
+ bytes.read(33).unpack1('c33') # Blob of zeros. The next 33 bytes, after all the vertices, should be initially zero (0) and should be maintained on read.
54
+ polygon.transform_rings! if has_holes == 1
55
+ polygon
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,19 @@
1
+ module RubyVolt
2
+ class DataType
3
+ class String < Varbinary
4
+
5
+ class << self
6
+ def unpack(bytes)
7
+ if unpacked = super(bytes)
8
+ unpacked.force_encoding("utf-8")
9
+ end
10
+ end
11
+
12
+ def convert_input(val)
13
+ val.to_s.encode("utf-8")
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,34 @@
1
+ module RubyVolt
2
+ class DataType
3
+ class Varbinary < Basic
4
+ DIRECTIVE = 'a'
5
+ NULL_INDICATOR = -1 # SQL NULL indicator for object type serializations
6
+
7
+ class << self
8
+ def pack(val)
9
+ if val.nil?
10
+ Integer.pack(self::NULL_INDICATOR)
11
+ else
12
+ val = convert_input(val)
13
+ Integer.pack(val.bytesize) + val
14
+ end
15
+ end
16
+
17
+ def unpack(bytes)
18
+ length = Integer.unpack(bytes)
19
+ case length
20
+ when self::NULL_INDICATOR then nil
21
+ when 0 then ::String.new
22
+ else
23
+ bytes.read(length).unpack1("#{self::DIRECTIVE}#{length}")
24
+ end
25
+ end
26
+
27
+ def convert_input(val)
28
+ val.to_s.encode("ascii-8bit")
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,11 @@
1
+ module RubyVolt
2
+ class DataType
3
+ class Complex < self
4
+ end
5
+ end
6
+ end
7
+
8
+ require 'ruby_volt/data_type/complex/volt_table'
9
+ require 'ruby_volt/data_type/complex/serializable_exception'
10
+ require 'ruby_volt/data_type/complex/parameter_set'
11
+ require 'ruby_volt/data_type/complex/parameter'
@@ -0,0 +1,20 @@
1
+ module RubyVolt
2
+ class DataType
3
+ class Parameter < Complex
4
+
5
+ class << self
6
+ def pack(val)
7
+ dataType = voltDataType(val)
8
+ WireTypeInfo.pack(dataType) + dataType.pack(val)
9
+ end
10
+
11
+ def unpack(bytes)
12
+ if dataType = WireTypeInfo.unpack(bytes)
13
+ dataType.unpack(bytes)
14
+ end
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ module RubyVolt
2
+ class DataType
3
+ class ParameterSet < Complex
4
+
5
+ class << self
6
+ def pack(*vals)
7
+ params_count = vals.size
8
+ Short.pack(params_count) + vals.map {|val| Parameter.pack(val)}.join
9
+ end
10
+
11
+ def unpack(bytes)
12
+ params_count = Short.unpack(bytes)
13
+ params_count.times.map {Parameter.unpack(bytes)}
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,43 @@
1
+ module RubyVolt
2
+ class DataType
3
+ class SerializableException < Basic
4
+ extend Extensions::CodeValuesHash
5
+ hash_codes [1, EEException],
6
+ [2, SQLException],
7
+ [3, ConstraintFailureException]
8
+
9
+ DIRECTIVE = 'a'
10
+ NULL_INDICATOR = 0 # The ordinal will not be present if the exception's length is 0.
11
+
12
+ class << self
13
+ def pack(val, body = nil)
14
+ if val.nil?
15
+ Integer.pack(self::NULL_INDICATOR)
16
+ else
17
+ ordinal = val.is_a?(::Integer) ? val : code_values[val]
18
+ if body
19
+ Integer.pack(1 + body.bytesize) + Byte.pack(ordinal) + body # Exception ordinal (1 byte) + opaque length
20
+ else
21
+ Integer.pack(1) + Byte.pack(ordinal) # Exception ordinal (1 byte)
22
+ end
23
+ end
24
+ end
25
+
26
+ def unpack(bytes)
27
+ if (length = Integer.unpack(bytes)) && length > 0
28
+ ordinal = Byte.unpack(bytes)
29
+ body_size = length - 1
30
+ if body_size == 0 # Ordinal only, no body
31
+ [codes[ordinal], nil]
32
+ else
33
+ [codes[ordinal], bytes.read(body_size).unpack1("#{self::DIRECTIVE}#{body_size}")]
34
+ end
35
+ else
36
+ [nil, nil]
37
+ end
38
+ end
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,96 @@
1
+ module RubyVolt
2
+ class DataType
3
+ class VoltTable < Complex
4
+
5
+ class << self
6
+ def pack(columns, rows)
7
+ # The "Table Metadata Length" stores the length in bytes of the contents of the table from byte 8 (the end of the metadata length field) all the way to the end of the "Column Names" sequence. NOTE: It does not include the row count value. See below for an example.
8
+ status_code = ProcedureCallStatusCode.pack(0) # OK Status code
9
+ columns_sequence = pack_columns(columns) # Columns data
10
+ rows_sequence = pack_rows(columns, rows) # Rows data
11
+ table_metadata_length = status_code.bytesize + columns_sequence.bytesize # Table Metadata Length
12
+ total_table_length = table_metadata_length + rows_sequence.bytesize # Total Table Length
13
+ [Integer.pack(total_table_length),
14
+ Integer.pack(table_metadata_length),
15
+ status_code,
16
+ columns_sequence,
17
+ rows_sequence].join
18
+ end
19
+
20
+ def unpack(bytes, &block)
21
+ table_info = unpack_table_info(bytes)
22
+ columns = unpack_columns(bytes)
23
+ rows = unpack_rows(columns, bytes, &block)
24
+ [*table_info, columns, rows]
25
+ end
26
+
27
+ def unpack_table_info(bytes)
28
+ [Integer.unpack(bytes), # total_table_length
29
+ Integer.unpack(bytes), # table_metadata_length
30
+ ProcedureCallStatusCode.unpack(bytes)] # status_code
31
+ end
32
+
33
+ def pack_columns(columns = []) # [["column", DataType]] ex.: [["city", :String], ["population", :Long]]
34
+ # The size of the "Column Types" and "Column Names" sequences is expected to equal the value stored in "Column Count".
35
+ # Column names are limited to the ASCII character set. Strings in row values are still UTF-8 encoded.
36
+ # Values with 4-byte (integer) length fields are signed and are limited to a max of 1 megabyte.
37
+ col_types, col_names = [], []
38
+ columns.each do |col|
39
+ col_name, dataType = *col
40
+ col_name = "modified_tuples" if col_name == ""
41
+ dataType = classifyDataType(dataType) unless dataType < DataType
42
+ col_types << WireTypeInfo.pack(dataType)
43
+ col_names << Varbinary.pack(col_name) # ASCII-8bit
44
+ end
45
+ Short.pack(columns.size) + col_types.join + col_names.join
46
+ end
47
+
48
+ def unpack_columns(bytes)
49
+ columns_count = Short.unpack(bytes)
50
+ columns = columns_count.times.map do
51
+ WireTypeInfo.unpack(bytes)
52
+ end
53
+ columns.map do |dataType|
54
+ col_name = Varbinary.unpack(bytes) # ASCII-8bit
55
+ col_name = "modified_tuples" if col_name == ""
56
+ [col_name, dataType]
57
+ end
58
+ end
59
+
60
+ def pack_rows(columns, rows = []) # [[val1, val2, ...]]
61
+ # Each row is preceded by a 4 byte integer that holds the length of the row not including the length. For example, if a row is a single 4-byte integer column, the value of this length prefix will be 4. Row size is artificially restricted to 2 megabytes.
62
+ # The body of the row is packed array of values. The value at index i is is of type specified by the column type field for index i.
63
+ Integer.pack(rows.size) + # Row count
64
+ rows.map do |row_values|
65
+ rlength = 0
66
+ row_values.map.with_index do |val, index|
67
+ dataType = columns[index][1]
68
+ packed = dataType.pack(val)
69
+ rlength += packed.bytesize
70
+ packed
71
+ end.join.prepend(Integer.pack(rlength))
72
+ end.join
73
+ end
74
+
75
+ def unpack_rows(columns, bytes, &block) # Block for parsing on fly
76
+ rows_count = Integer.unpack(bytes)
77
+ rows = []
78
+ rows_count.times do
79
+ Integer.unpack(bytes) # Bytesize of row length
80
+ row = columns.map do |c|
81
+ dataType = c[1]
82
+ dataType.unpack(bytes)
83
+ end
84
+ if block_given?
85
+ yield(row)
86
+ else
87
+ rows << row
88
+ end
89
+ end
90
+ rows
91
+ end
92
+ end
93
+
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,8 @@
1
+ module RubyVolt
2
+ class DataType
3
+ class Compound < self
4
+ end
5
+ end
6
+ end
7
+
8
+ require 'ruby_volt/data_type/compound/array'
@@ -0,0 +1,42 @@
1
+ module RubyVolt
2
+ class DataType
3
+ class Array < Compound
4
+ # Arrays are represented as Byte value indicating the wire type of the elements and a 2 byte Short value indicating the number of elements in the array, followed by the specified number of elements. The length preceding value for the TINYINT (byte) type is length preceded by a 4 byte integer instead of a 2 byte short. This important exception allows large quantities of binary or string data to be passed as a byte array, and allows the serialization of and array of TINYINTs (bytes) to match the serialization of VARBINARY. Each array is serialized according to its type (Strings as Strings, VoltTables as VoltTables, Integers as Integers). Arrays are only present as parameters in parameter sets. Note that it is possible to pass an array of arrays of bytes if they are serialized as an array of VARBINARY types.
5
+
6
+ class << self
7
+ def pack(val = []) # First element is a DataType indicator (DataType itself, Integer, String/Symbol)
8
+ array = val[1..-1]
9
+ dataType = classifyDataType(val[0])
10
+ unless dataType # No indicator recognized
11
+ array = val
12
+ dataType = autodetect_dataType(array)
13
+ end
14
+ countDataType = (dataType <= Byte) ? Integer : Short # The length preceding value for the TINYINT (byte) type is length preceded by a 4 byte integer instead of a 2 byte short.
15
+ WireTypeInfo.pack(dataType) + countDataType.pack(array.size) + array.map {|e| dataType.pack(e)}.join
16
+ end
17
+
18
+ def unpack(bytes)
19
+ if dataType = WireTypeInfo.unpack(bytes)
20
+ array = [dataType]
21
+ countDataType = (dataType <= Byte) ? Integer : Short
22
+ array_size = countDataType.unpack(bytes)
23
+ array_size.times do
24
+ array << dataType.unpack(bytes)
25
+ end
26
+ array
27
+ end
28
+ end
29
+
30
+ def autodetect_dataType(array)
31
+ if array[0].is_a?(::Integer)
32
+ max_int = array.select {|e| e.is_a?(::Integer)}.max
33
+ voltDataType(max_int)
34
+ else
35
+ voltDataType(array[0])
36
+ end
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+ end