Class: SqlServerDts::RawFile

Inherits:
Object
  • Object
show all
Defined in:
lib/sql_server_raw_file.rb

Overview

SqlServerDts::RawFile class

Defined Under Namespace

Classes: Error, FieldsNotExistsError, UnknownDataTypeError, UnsupportedRawVersionError

Constant Summary collapse

DEFAULT_RAW_FILE_ENCODING =

default encoding of raw file: ‘utf-16le’

'utf-16le'
DEFAULT_ENCODING =

default encoding of module environment: ‘utf-8’

'utf-8'
SUPPORTED_RAW_VERSIONS =

supported versions of Raw File:

00.90.00.00 => 9 (SQL Server: 9,10)
00.10.01.00 => 10 (SQL Server: 11,12)
%w(00.10.01.00 00.90.00.00)

Instance Method Summary collapse

Constructor Details

#initialize(io, init_pos = nil) ⇒ SqlServerDts::RawFile, [SqlServerDts::RawFile,IO]

initialization

Parameters:

  • io (String, IO)

    Path to raw file or io-stream

  • init_pos (Integer, nil) (defaults to: nil)

    Initial position in raw file or nil



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/sql_server_raw_file.rb', line 40

def initialize(io,init_pos=nil)
  if io.is_a?(IO)
    @raw_io = io
    @raw_io.binmode unless @raw_io.binmode?
    read_header
    @raw_io.seek(init_pos || @data_pos)
  else
    @raw_io = File.new(io,'rb')
    read_header
    @raw_io.seek(init_pos || @data_pos)
    if block_given?
      begin
        yield [self,@raw_io]
      ensure
        @raw_io.close
      end
    else
      self
    end
  end
end

Instance Method Details

#closeObject

close io-stream



197
198
199
# File 'lib/sql_server_raw_file.rb', line 197

def close
  @raw_io.close unless @raw_io.closed?
end

#eof?Boolean

check io-stream eof

Returns:

  • (Boolean)


202
203
204
# File 'lib/sql_server_raw_file.rb', line 202

def eof?
  @raw_io.eof?
end

#headerArray

get metadata (header) of raw file

Returns:

  • (Array)

    Array of hash of fields metadata



187
188
189
# File 'lib/sql_server_raw_file.rb', line 187

def header
  @fields_info
end

#posObject

get current position in inner io-stream object



207
208
209
# File 'lib/sql_server_raw_file.rb', line 207

def pos
  @raw_io.pos
end

#read_headerObject

read metadata (header) raw file and fill inner array of metadata

Raises:

  • (IOError)


63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/sql_server_raw_file.rb', line 63

def read_header
  raise IOError, 'Stream is closed' if @raw_io.closed?
  begin
    @raw_io.rewind
    @version = @raw_io.read(4).unpack('H2H2H2H2').join('.')
    raise UnsupportedRawVersionError, "Version #{@version} unsupported " unless SUPPORTED_RAW_VERSIONS.include?(@version)
    @fields_info = []
    fields_count = @raw_io.read(4).unpack('I')[0]
    raise FieldsNotExistsError, 'Fields not exists (fields_count=0)' if fields_count==0
    @nil_mask_len = fields_count/8+(fields_count-fields_count/8*8>0?1:0)
    fields_count.times {
      name_len = @raw_io.read(4).unpack('I')[0]
      field_name = @raw_io.read(name_len*2).encode(DEFAULT_ENCODING,DEFAULT_RAW_FILE_ENCODING,{ invalid: :replace, undef: :replace })
      (@version == '00.10.01.00') ?
          values_arr = @raw_io.read(40).unpack('I4iI5') :
          values_arr = @raw_io.read(32).unpack('I4iI3')
      field_info = {
        name: field_name,
        data_type: values_arr[0],
        max_length: values_arr[1],
        p2: values_arr[2],
        p3: values_arr[3],
        p4: values_arr[4],
        precision: values_arr[5],
        scale: values_arr[6],
        code_page: values_arr[7],
        p8: values_arr[8],
        p9: values_arr[9]
      }
      @fields_info << field_info
    }
  rescue
    raise
  else
    @data_pos = @raw_io.pos
  end
end

#read_lineHash

read line of raw data

Returns:

  • (Hash)

    Hash of converted raw data (one line)

Raises:

  • (IOError)


104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/sql_server_raw_file.rb', line 104

def read_line
  raise IOError, 'Stream is closed' if @raw_io.closed?
  begin
    nil_mask = @raw_io.read(@nil_mask_len).unpack('b*')[0].each_char.to_a
    data_row = []
    @fields_info.each_with_index { |field_info, index|
      field_value =
        if nil_mask[index] == '1'
          nil
        else
          case field_info[:data_type]
            when 2 # smallint

              @raw_io.read(get_len(field_info)).unpack('s')[0]
            when 3 # int, NULL w/o cast

              @raw_io.read(get_len(field_info)).unpack('i')[0]
            when 4 # real (float(24))

              @raw_io.read(get_len(field_info)).unpack('f')[0]
            when 5 # float

              @raw_io.read(get_len(field_info)).unpack('d')[0]
            when 6 # money (decimal(19,4)), smallmoney ((decimal(10,4)))

              values_arr = @raw_io.read(get_len(field_info)).unpack('q')
              "#{values_arr[0].to_s.insert((values_arr[0].to_s.length-4), '.')}"
            when 11 # bit

              (@raw_io.read(get_len(field_info)).unpack('B')[0] == '1') ? true : false
            when 17 # tinyint

              @raw_io.read(get_len(field_info)).unpack('C')[0]
            when 20 # bigint

              @raw_io.read(get_len(field_info)).unpack('q')[0]
            when 72 # uniqueidentifier

              uid_arr = @raw_io.read(get_len(field_info)).unpack('H2'*get_len(field_info))
              uid_arr[0..3].reverse.join+'-'+uid_arr[4..5].reverse.join+'-'+uid_arr[6..7].reverse.join+'-'+uid_arr[8..9].join+'-'+uid_arr[10..-1].join
            when 128 # varbinary, binary, timestamp

              data_len = get_len
              @raw_io.read(data_len).unpack('H2'*data_len).join
            when 129 # varchar, char

              data_len = get_len
              @raw_io.read(data_len).encode({invalid: :replace, undef: :replace})
            when 130 # nvarchar, nchar

              @raw_io.read(get_len*2).encode(DEFAULT_ENCODING, DEFAULT_RAW_FILE_ENCODING, {invalid: :replace, undef: :replace})
            when 131 # numeric, decimal

              # precision, scale, sign, value

              values_arr = @raw_io.read(get_len(field_info)).unpack('C3Q')
              "#{('-' if values_arr[2] == 0)}#{values_arr[3].to_s.insert((values_arr[3].to_s.length-values_arr[1]), '.')}"
            when 133 # date

              values_arr = @raw_io.read(get_len(field_info)).unpack('S3')
              Date.new(values_arr[0], values_arr[1], values_arr[2])
            when 135 # datetime, smalldatetime

              values_arr = @raw_io.read(get_len(field_info)).unpack('S6I')
              Time.local(values_arr[0], values_arr[1], values_arr[2], values_arr[3], values_arr[4], "#{values_arr[5]}.#{values_arr[6]}".to_f).strftime('%F %T.%L')
            when 145 # time

              values_arr = @raw_io.read(10).unpack('S3I')
              Time.local(Time.now.year, Time.now.month, Time.now.day, values_arr[0], values_arr[1], "#{values_arr[2]}.#{values_arr[3]}".to_f).strftime("%T.%#{field_info[:scale]}N")
            when 146 # datetimeoffset

              values_arr = @raw_io.read(get_len(field_info)).unpack('S6Is2')
              Time.new(values_arr[0], values_arr[1], values_arr[2], values_arr[3], values_arr[4], "#{values_arr[5]}.#{values_arr[6]}".to_f,format('%+03d:%02d',values_arr[7],values_arr[8].abs)).strftime("%F %T.%#{field_info[:scale]}N%:z")
            when 304 # datetime2

              values_arr = @raw_io.read(get_len(field_info)).unpack('S6I')
              Time.local(values_arr[0], values_arr[1], values_arr[2], values_arr[3], values_arr[4], "#{values_arr[5]}.#{values_arr[6]}".to_f).strftime("%F %T.%#{field_info[:scale]}N")
            else
              raise UnknownDataTypeError, "Unknown data type #{field_info[:data_type]}"
          end
        end
      data_row << [ field_info[:name], field_value ]
    }
  rescue
    raise
  else
    data_row.to_h
  end
end

#versionObject

get version of raw file



192
193
194
# File 'lib/sql_server_raw_file.rb', line 192

def version
  @version
end