Class | Sequel::Postgres::PGArray::Parser |
In: |
lib/sequel/extensions/pg_array.rb
|
Parent: | Object |
PostgreSQL array parser that handles both text and numeric input. Because PostgreSQL arrays can contain objects that can be literalized in any number of ways, it is not possible to make a fully generic parser.
This parser is very simple and unoptimized, but should still be O(n) where n is the length of the input string.
pos | [R] |
Set the source for the input, and any converter callable to call with objects to be created. For nested parsers the source may contain text after the end current parse, which will be ignored.
# File lib/sequel/extensions/pg_array.rb, line 187 187: def initialize(source, converter=nil) 188: @source = source 189: @source_length = source.length 190: @converter = converter 191: @pos = -1 192: @entries = [] 193: @recorded = "" 194: @dimension = 0 195: end
Take the buffer of recorded characters and add it to the array of entries, and use a new buffer for recorded characters.
# File lib/sequel/extensions/pg_array.rb, line 216 216: def new_entry(include_empty=false) 217: if !@recorded.empty? || include_empty 218: entry = @recorded 219: if entry == NULL && !include_empty 220: entry = nil 221: elsif @converter 222: entry = @converter.call(entry) 223: end 224: @entries.push(entry) 225: @recorded = "" 226: end 227: end
Return 2 objects, whether the next character in the input was escaped with a backslash, and what the next character is.
# File lib/sequel/extensions/pg_array.rb, line 199 199: def next_char 200: @pos += 1 201: if (c = @source[@pos..@pos]) == BACKSLASH 202: @pos += 1 203: [true, @source[@pos..@pos]] 204: else 205: [false, c] 206: end 207: end
Parse the input character by character, returning an array of parsed (and potentially converted) objects.
# File lib/sequel/extensions/pg_array.rb, line 231 231: def parse(nested=false) 232: # quote sets whether we are inside of a quoted string. 233: quote = false 234: until @pos >= @source_length 235: escaped, char = next_char 236: if char == OPEN_BRACE && !quote 237: @dimension += 1 238: if (@dimension > 1) 239: # Multi-dimensional array encounter, use a subparser 240: # to parse the next level down. 241: subparser = self.class.new(@source[@pos..-1], @converter) 242: @entries.push(subparser.parse(true)) 243: @pos += subparser.pos - 1 244: end 245: elsif char == CLOSE_BRACE && !quote 246: @dimension -= 1 247: if (@dimension == 0) 248: new_entry 249: # Exit early if inside a subparser, since the 250: # text after parsing the current level should be 251: # ignored as it is handled by the parent parser. 252: return @entries if nested 253: end 254: elsif char == QUOTE && !escaped 255: # If already inside the quoted string, this is the 256: # ending quote, so add the entry. Otherwise, this 257: # is the opening quote, so set the quote flag. 258: new_entry(true) if quote 259: quote = !quote 260: elsif char == COMMA && !quote 261: # If not inside a string and a comma occurs, it indicates 262: # the end of the entry, so add the entry. 263: new_entry 264: else 265: # Add the character to the recorded character buffer. 266: record(char) 267: end 268: end 269: raise Sequel::Error, "array dimensions not balanced" unless @dimension == 0 270: @entries 271: end