sequel 5.9.0 → 5.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 49e114f3c425c0cdcd798fdcb170ac2112fada51077235f8e53b3d9a3940954a
4
- data.tar.gz: db61fd6ab89b98c48fd2461f500db1f35afd2624808721733a8efbdc4c14a9ba
3
+ metadata.gz: 629d77185f762e5399741ae9b1caf03c8cd2bbe5f72fd9487900dc0c4bfc2e7a
4
+ data.tar.gz: ee46b9c3180a67eb356b1f2ae7b274cc293687b333bf216b76a3e55def081c57
5
5
  SHA512:
6
- metadata.gz: ff2793a7d47c0fe5ff7669ef248cd2c66bd1761f36a9ca0d2742adb8ee59be88a12264df0155864e7ac34f2795ff8f1a633d4e97fc87bcd9bb7976e93b67615b
7
- data.tar.gz: a5996916449312e4a83ecc1e4bdaf2a3789c98321fa622389d675b3b6caf5cc84ce9e413198c2d875d0963ac07b7f3b59f2e80cd50b7eb9f45110b1f87263489
6
+ metadata.gz: 2b5a5c9ed14dcd9bc6c98286bb4c22637052bb2f152f352d5002b096e2d361e2ac6ec15dcd44881e3e96e37b6acd8896161ad94cac4b674d55a6f54a9d43a948
7
+ data.tar.gz: 847891aeebf175eca649d58461c38d3127311594bb3906bb73429726230b57dd1555ffbd760e3d5f7b30bb9907d5c6d461af1ac0d3b8e166b8dcbf61787fea97
data/CHANGELOG CHANGED
@@ -1,3 +1,37 @@
1
+ === 5.10.0 (2018-07-01)
2
+
3
+ * Use input type casts when using the postgres adapter with pg 0.18+ to reduce string allocations for some primitive types used as prepared statement arguments (jeremyevans)
4
+
5
+ * Assume local time if database timezone not specified when handling BC timestamps on JRuby 9.2.0.0 in the pg_extended_date_support extension (jeremyevans)
6
+
7
+ * Fix parsing of timetz types in the jdbc/postgresql adapter (jeremyevans)
8
+
9
+ * Make SQLTime.parse respect SQLTime.date and Sequel.application_timezone (jeremyevans)
10
+
11
+ * Add :top as an option in the list plugin (celsworth) (#1526)
12
+
13
+ * Fix Model#{ancestors,descendants,self_and_siblings} in the tree plugin when custom parent/children association names are used (jeremyevans) (#1525)
14
+
15
+ * Treat read-only mode error as disconnect error on mysql and mysql2 adapters, for better behavior on AWS Aurora cluster (jeremyevans)
16
+
17
+ * Don't use cached placeholder literalizers for in Dataset#{first,where_all,where_each,where_single_value} if argument is empty array or hash (jeremyevans)
18
+
19
+ * Support :tablespace option when adding tables, indexes, and materialized views on PostgreSQL (jeremyevans)
20
+
21
+ * Support :include option for indexes on PostgreSQL 11+ (jeremyevans)
22
+
23
+ * Allow the use of IN/NOT IN operators with set returning functions for Sequel::Model datasets (jeremyevans)
24
+
25
+ * Make many_to_pg_array associations in the pg_array_associations plugin work on PostgreSQL 11 (jeremyevans)
26
+
27
+ * Only load strscan library in pg_array extension if it is needed (jeremyevans)
28
+
29
+ * Don't remove related many_to_one associations from cache when setting column value to existing value for model instances that have not been persisted (jeremyevans) (#1521)
30
+
31
+ * Support ruby 2.6+ endless ranges in the pg_range extension (jeremyevans)
32
+
33
+ * Support ruby 2.6+ endless ranges in filters, using just a >= operator for them (jeremyevans)
34
+
1
35
  === 5.9.0 (2018-06-01)
2
36
 
3
37
  * Support generated columns on MySQL 5.7+ and MariaDB 5.2+ (wjordan, jeremyevans) (#1517)
@@ -0,0 +1,84 @@
1
+ = New Features
2
+
3
+ * Ruby 2.6+ endless ranges are now supported as condition specifier
4
+ values, using a >= operator for them:
5
+
6
+ DB[:t].where(c: 1...)
7
+ # SELECT * FROM t WHERE (c >= 1)
8
+
9
+ * Ruby 2.6+ endless ranges are now supported in the pg_range
10
+ extension:
11
+
12
+ DB[:t].where(id: 1).update(r: 1...)
13
+ # UPDATE t SET r = '[1,)' WHERE (id = 1)
14
+
15
+ * The :include option when creating indexes is now supported on
16
+ PostgreSQL 11, specifying additional columns to include in the index
17
+ without indexing them. This is useful to allow index only scans in
18
+ additional cases.
19
+
20
+ * The :tablespace option is now supported when creating tables,
21
+ indexes, and materialized views on PostgreSQL.
22
+
23
+ * The list plugin now supports a :top option, which can be used to
24
+ specify the top of the list. The default value for the top of the
25
+ list is 1, but using this option you can make the top of the list be
26
+ 0.
27
+
28
+ = Other Improvements
29
+
30
+ * In the pg_array_associations plugin, filtering by associations for
31
+ many_to_pg_array associations now works correctly on PostgreSQL 11.
32
+ Previously it did not work on PostgreSQL 11 due to new restrictions
33
+ on using set returning functions in the the SELECT list.
34
+
35
+ * When setting the value of a column to the same value the column
36
+ already has, for a new model object that has not yet been persisted,
37
+ where the column is used as the foreign key for at least one
38
+ many_to_one association, do not clear any related associations from
39
+ the associations cache.
40
+
41
+ * In the pg_array extension, if there are separate conversion procs for
42
+ timetz and time types, the conversion proc for the timetz[] type now
43
+ correctly uses the conversion proc for the timetz type to convert
44
+ scalar values, instead of the conversion proc for the time type.
45
+
46
+ * Empty arrays and hashes are now correctly handled in
47
+ Dataset#{first,where_all,where_each,where_single_value} when a
48
+ cached placeholder literalizer is used.
49
+
50
+ * In the tree plugin, Model#{ancestors,descendants,self_and_siblings}
51
+ now work correctly when custom parent/children association names
52
+ are used.
53
+
54
+ * The inner loop of the postgres adapter row fetching code is now
55
+ 2-3% faster.
56
+
57
+ * When using the postgres adapter with pg-0.18+, set a
58
+ type_map_for_queries for the connection to allow it to handle input
59
+ type casts for Integer, Float, TrueClass, and FalseClass values
60
+ without allocating strings.
61
+
62
+ * SQLTime.parse (and therefore Sequel.string_to_time) now respects the
63
+ SQLTime.date and Sequel.application_timezone settings.
64
+
65
+ * The jdbc/postgresql adapter now correctly parses timetz types.
66
+
67
+ * On JRuby 9.2.0.0, when handling BC timestamps without timezones in
68
+ the pg_extended_date_support extension, assume local time and not
69
+ UTC time if the database timezone is not specified and
70
+ Sequel.datetime_class is Time.
71
+
72
+ * Errors indicating that a MySQL database is in read-only mode are
73
+ now treated as disconnect errors in the mysql and mysql2 adapters,
74
+ for better behavior in failover scenarios.
75
+
76
+ * Sequel::Model datasets now support the use of IN/NOT IN operators
77
+ where the second argument for the operator (the right hand side) is
78
+ a set returning function. Previously, the Sequel::Model code
79
+ assumed the right hand side of an IN/NOT IN operator was a datasets
80
+ or array, since those are the only values where Sequel will
81
+ automatically create such an operator.
82
+
83
+ * Sequel no longer loads the strscan library in the pg_array extension
84
+ if it is not necessary because the parser from sequel_pg is used.
@@ -214,7 +214,7 @@ module Sequel
214
214
  STRING_TYPE = Java::JavaSQL::Types::VARCHAR
215
215
  ARRAY_TYPE = Java::JavaSQL::Types::ARRAY
216
216
  ARRAY_METHOD = Postgres.method(:RubyPGArray)
217
- PG_SPECIFIC_TYPES = [ARRAY_TYPE, Java::JavaSQL::Types::OTHER, Java::JavaSQL::Types::STRUCT].freeze
217
+ PG_SPECIFIC_TYPES = [ARRAY_TYPE, Java::JavaSQL::Types::OTHER, Java::JavaSQL::Types::STRUCT, Java::JavaSQL::Types::TIME_WITH_TIMEZONE, Java::JavaSQL::Types::TIME].freeze
218
218
  HSTORE_METHOD = Postgres.method(:RubyPGHstore)
219
219
 
220
220
  def type_convertor(map, meta, type, i)
@@ -15,6 +15,12 @@ begin
15
15
  end
16
16
 
17
17
  Sequel::Postgres::USES_PG = true
18
+ if defined?(PG::TypeMapByClass)
19
+ type_map = Sequel::Postgres::PG_QUERY_TYPE_MAP = PG::TypeMapByClass.new
20
+ type_map[Integer] = PG::TextEncoder::Integer.new
21
+ type_map[FalseClass] = type_map[TrueClass] = PG::TextEncoder::Boolean.new
22
+ type_map[Float] = PG::TextEncoder::Float.new
23
+ end
18
24
  rescue LoadError => e
19
25
  begin
20
26
  require 'postgres-pr/postgres-compat'
@@ -211,6 +217,9 @@ module Sequel
211
217
  end
212
218
 
213
219
  conn.instance_variable_set(:@db, self)
220
+ if USES_PG && conn.respond_to?(:type_map_for_queries=) && defined?(PG_QUERY_TYPE_MAP)
221
+ conn.type_map_for_queries = PG_QUERY_TYPE_MAP
222
+ end
214
223
 
215
224
  if encoding = opts[:encoding] || opts[:charset]
216
225
  if conn.respond_to?(:set_client_encoding)
@@ -737,9 +746,9 @@ module Sequel
737
746
  cols = []
738
747
  procs = db.conversion_procs
739
748
  res.nfields.times do |fieldnum|
740
- cols << [fieldnum, procs[res.ftype(fieldnum)], output_identifier(res.fname(fieldnum))]
749
+ cols << [procs[res.ftype(fieldnum)], output_identifier(res.fname(fieldnum))]
741
750
  end
742
- self.columns = cols.map{|c| c[2]}
751
+ self.columns = cols.map{|c| c[1]}
743
752
  cols
744
753
  end
745
754
 
@@ -756,13 +765,20 @@ module Sequel
756
765
  # For each row in the result set, yield a hash with column name symbol
757
766
  # keys and typecasted values.
758
767
  def yield_hash_rows(res, cols)
759
- res.ntuples.times do |recnum|
768
+ ntuples = res.ntuples
769
+ recnum = 0
770
+ while recnum < ntuples
771
+ fieldnum = 0
772
+ nfields = cols.length
760
773
  converted_rec = {}
761
- cols.each do |fieldnum, type_proc, fieldsym|
774
+ while fieldnum < nfields
775
+ type_proc, fieldsym = cols[fieldnum]
762
776
  value = res.getvalue(recnum, fieldnum)
763
777
  converted_rec[fieldsym] = (value && type_proc) ? type_proc.call(value) : value
778
+ fieldnum += 1
764
779
  end
765
780
  yield converted_rec
781
+ recnum += 1
766
782
  end
767
783
  end
768
784
  end
@@ -1042,6 +1042,10 @@ module Sequel
1042
1042
  sql += " ON COMMIT #{ON_COMMIT[on_commit]}"
1043
1043
  end
1044
1044
 
1045
+ if tablespace = options[:tablespace]
1046
+ sql += " TABLESPACE #{quote_identifier(tablespace)}"
1047
+ end
1048
+
1045
1049
  if server = options[:foreign]
1046
1050
  sql += " SERVER #{quote_identifier(server)}"
1047
1051
  if foreign_opts = options[:options]
@@ -1077,7 +1081,13 @@ module Sequel
1077
1081
 
1078
1082
  # DDL fragment for initial part of CREATE VIEW statement
1079
1083
  def create_view_prefix_sql(name, options)
1080
- create_view_sql_append_columns("CREATE #{'OR REPLACE 'if options[:replace]}#{'TEMPORARY 'if options[:temp]}#{'RECURSIVE ' if options[:recursive]}#{'MATERIALIZED ' if options[:materialized]}VIEW #{quote_schema_table(name)}", options[:columns] || options[:recursive])
1084
+ sql = create_view_sql_append_columns("CREATE #{'OR REPLACE 'if options[:replace]}#{'TEMPORARY 'if options[:temp]}#{'RECURSIVE ' if options[:recursive]}#{'MATERIALIZED ' if options[:materialized]}VIEW #{quote_schema_table(name)}", options[:columns] || options[:recursive])
1085
+
1086
+ if tablespace = options[:tablespace]
1087
+ sql += " TABLESPACE #{quote_identifier(tablespace)}"
1088
+ end
1089
+
1090
+ sql
1081
1091
  end
1082
1092
 
1083
1093
  # SQL for dropping a function from the database.
@@ -1147,7 +1157,7 @@ module Sequel
1147
1157
  when :spatial
1148
1158
  index_type = :gist
1149
1159
  end
1150
- "CREATE #{unique}INDEX#{' CONCURRENTLY' if index[:concurrently]}#{if_not_exists} #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{filter}"
1160
+ "CREATE #{unique}INDEX#{' CONCURRENTLY' if index[:concurrently]}#{if_not_exists} #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{" INCLUDE #{literal(Array(index[:include]))}" if index[:include]}#{" TABLESPACE #{quote_identifier(index[:tablespace])}" if index[:tablespace]}#{filter}"
1151
1161
  end
1152
1162
 
1153
1163
  # Setup datastructures shared by all postgres adapters.
@@ -17,6 +17,7 @@ module Sequel
17
17
  MySQL client is not connected
18
18
  This connection is still waiting for a result, try again once you have the result
19
19
  closed MySQL connection
20
+ The MySQL server is running with the --read-only option so it cannot execute this statement
20
21
  END
21
22
  # Error messages for mysql and mysql2 that indicate the current connection should be disconnected
22
23
  MYSQL_DATABASE_DISCONNECT_ERRORS = /\A#{Regexp.union(disconnect_errors)}/
@@ -222,6 +222,9 @@ module Sequel
222
222
  # operations on the table while the index is being
223
223
  # built.
224
224
  # :opclass :: Use a specific operator class in the index.
225
+ # :include :: Include additional column values in the index, without
226
+ # actually indexing on those values (PostgreSQL 11+).
227
+ # :tablespace :: Specify tablespace for index.
225
228
  #
226
229
  # Microsoft SQL Server specific options:
227
230
  #
@@ -181,6 +181,7 @@ module Sequel
181
181
  # where keys are option names and values are option values. Note
182
182
  # that option names are unquoted, so you should not use untrusted
183
183
  # keys.
184
+ # :tablespace :: The tablespace to use for the table.
184
185
  #
185
186
  # See <tt>Schema::CreateTableGenerator</tt> and the {"Schema Modification" guide}[rdoc-ref:doc/schema_modification.rdoc].
186
187
  def create_table(name, options=OPTS, &block)
@@ -281,6 +282,7 @@ module Sequel
281
282
  # in a subquery, if you are providing a Dataset as the source
282
283
  # argument, if should probably call the union method with the
283
284
  # all: true and from_self: false options.
285
+ # :tablespace :: The tablespace to use for materialized views.
284
286
  def create_view(name, source, options = OPTS)
285
287
  execute_ddl(create_view_sql(name, source, options))
286
288
  remove_cached_schema(name)
@@ -231,10 +231,11 @@ module Sequel
231
231
 
232
232
  return res
233
233
  end
234
+ where_args = args
234
235
  args = arg
235
236
  end
236
237
 
237
- if loader = cached_placeholder_literalizer(:_first_cond_loader) do |pl|
238
+ if loader = cached_where_placeholder_literalizer(where_args||args, block, :_first_cond_loader) do |pl|
238
239
  _single_record_ds.where(pl.arg)
239
240
  end
240
241
 
@@ -875,7 +876,7 @@ module Sequel
875
876
  # DB[:table].where_all(id: [1,2,3])
876
877
  # # SELECT * FROM table WHERE (id IN (1, 2, 3))
877
878
  def where_all(cond, &block)
878
- if loader = _where_loader
879
+ if loader = _where_loader([cond], nil)
879
880
  loader.all(filter_expr(cond), &block)
880
881
  else
881
882
  where(cond).all(&block)
@@ -889,7 +890,7 @@ module Sequel
889
890
  # DB[:table].where_each(id: [1,2,3]){|row| p row}
890
891
  # # SELECT * FROM table WHERE (id IN (1, 2, 3))
891
892
  def where_each(cond, &block)
892
- if loader = _where_loader
893
+ if loader = _where_loader([cond], nil)
893
894
  loader.each(filter_expr(cond), &block)
894
895
  else
895
896
  where(cond).each(&block)
@@ -904,7 +905,7 @@ module Sequel
904
905
  # DB[:table].select(:name).where_single_value(id: 1)
905
906
  # # SELECT name FROM table WHERE (id = 1) LIMIT 1
906
907
  def where_single_value(cond)
907
- if loader = cached_placeholder_literalizer(:_where_single_value_loader) do |pl|
908
+ if loader = cached_where_placeholder_literalizer([cond], nil, :_where_single_value_loader) do |pl|
908
909
  single_value_ds.where(pl.arg)
909
910
  end
910
911
 
@@ -1039,8 +1040,8 @@ module Sequel
1039
1040
  end
1040
1041
 
1041
1042
  # Loader used for where_all and where_each.
1042
- def _where_loader
1043
- cached_placeholder_literalizer(:_where_loader) do |pl|
1043
+ def _where_loader(where_args, where_block)
1044
+ cached_where_placeholder_literalizer(where_args, where_block, :_where_loader) do |pl|
1044
1045
  where(pl.arg)
1045
1046
  end
1046
1047
  end
@@ -309,6 +309,20 @@ module Sequel
309
309
  loader
310
310
  end
311
311
 
312
+ # Return a cached placeholder literalizer for the key, unless where_block is
313
+ # nil and where_args is an empty array or hash. This is designed to guard
314
+ # against placeholder literalizer use when passing arguments to where
315
+ # in the uncached case and filter_expr if a cached placeholder literalizer
316
+ # is used.
317
+ def cached_where_placeholder_literalizer(where_args, where_block, key, &block)
318
+ where_args = where_args[0] if where_args.length == 1
319
+ unless where_block
320
+ return if where_args == OPTS || where_args == EMPTY_ARRAY
321
+ end
322
+
323
+ cached_placeholder_literalizer(key, &block)
324
+ end
325
+
312
326
  # Set the columns for the current dataset.
313
327
  def columns=(v)
314
328
  cache_set(:_columns, v)
@@ -65,12 +65,12 @@
65
65
  # If you want an easy way to call PostgreSQL array functions and
66
66
  # operators, look into the pg_array_ops extension.
67
67
  #
68
- # This extension requires the strscan and delegate libraries.
68
+ # This extension requires the delegate library, and the strscan library
69
+ # sequel_pg has not been loaded.
69
70
  #
70
71
  # Related module: Sequel::Postgres::PGArray
71
72
 
72
73
  require 'delegate'
73
- require 'strscan'
74
74
 
75
75
  module Sequel
76
76
  module Postgres
@@ -99,7 +99,7 @@ module Sequel
99
99
  register_array_type('bytea', :oid=>1001, :scalar_oid=>17, :type_symbol=>:blob)
100
100
  register_array_type('date', :oid=>1182, :scalar_oid=>1082)
101
101
  register_array_type('time without time zone', :oid=>1183, :scalar_oid=>1083, :type_symbol=>:time)
102
- register_array_type('time with time zone', :oid=>1270, :scalar_oid=>1083, :type_symbol=>:time_timezone, :scalar_typecast=>:time)
102
+ register_array_type('time with time zone', :oid=>1270, :scalar_oid=>1266, :type_symbol=>:time_timezone, :scalar_typecast=>:time)
103
103
 
104
104
  register_array_type('smallint', :oid=>1005, :scalar_oid=>21, :scalar_typecast=>:integer)
105
105
  register_array_type('oid', :oid=>1028, :scalar_oid=>26, :scalar_typecast=>:integer)
@@ -300,93 +300,97 @@ module Sequel
300
300
  end
301
301
  end
302
302
 
303
- # PostgreSQL array parser that handles PostgreSQL array output format.
304
- # Note that does not handle all forms out input that PostgreSQL will
305
- # accept, and it will not raise an error for all forms of invalid input.
306
- class Parser < StringScanner
307
- # Set the source for the input, and any converter callable
308
- # to call with objects to be created. For nested parsers
309
- # the source may contain text after the end current parse,
310
- # which will be ignored.
311
- def initialize(source, converter=nil)
312
- super(source)
313
- @converter = converter
314
- @stack = [[]]
315
- @encoding = string.encoding
316
- @recorded = String.new.force_encoding(@encoding)
317
- end
303
+ unless Sequel::Postgres.respond_to?(:parse_pg_array)
304
+ require 'strscan'
305
+
306
+ # PostgreSQL array parser that handles PostgreSQL array output format.
307
+ # Note that does not handle all forms out input that PostgreSQL will
308
+ # accept, and it will not raise an error for all forms of invalid input.
309
+ class Parser < StringScanner
310
+ # Set the source for the input, and any converter callable
311
+ # to call with objects to be created. For nested parsers
312
+ # the source may contain text after the end current parse,
313
+ # which will be ignored.
314
+ def initialize(source, converter=nil)
315
+ super(source)
316
+ @converter = converter
317
+ @stack = [[]]
318
+ @encoding = string.encoding
319
+ @recorded = String.new.force_encoding(@encoding)
320
+ end
318
321
 
319
- # Take the buffer of recorded characters and add it to the array
320
- # of entries, and use a new buffer for recorded characters.
321
- def new_entry(include_empty=false)
322
- if !@recorded.empty? || include_empty
323
- entry = @recorded
324
- if entry == 'NULL' && !include_empty
325
- entry = nil
326
- elsif @converter
327
- entry = @converter.call(entry)
322
+ # Take the buffer of recorded characters and add it to the array
323
+ # of entries, and use a new buffer for recorded characters.
324
+ def new_entry(include_empty=false)
325
+ if !@recorded.empty? || include_empty
326
+ entry = @recorded
327
+ if entry == 'NULL' && !include_empty
328
+ entry = nil
329
+ elsif @converter
330
+ entry = @converter.call(entry)
331
+ end
332
+ @stack.last.push(entry)
333
+ @recorded = String.new.force_encoding(@encoding)
328
334
  end
329
- @stack.last.push(entry)
330
- @recorded = String.new.force_encoding(@encoding)
331
335
  end
332
- end
333
336
 
334
- # Parse the input character by character, returning an array
335
- # of parsed (and potentially converted) objects.
336
- def parse
337
- raise Sequel::Error, "invalid array, empty string" if eos?
338
- raise Sequel::Error, "invalid array, doesn't start with {" unless scan(/((\[\d+:\d+\])+=)?\{/)
339
-
340
- while !eos?
341
- char = scan(/[{}",]|[^{}",]+/)
342
- if char == ','
343
- # Comma outside quoted string indicates end of current entry
344
- new_entry
345
- elsif char == '"'
346
- raise Sequel::Error, "invalid array, opening quote with existing recorded data" unless @recorded.empty?
347
- while true
348
- char = scan(/["\\]|[^"\\]+/)
349
- if char == '\\'
350
- @recorded << getch
351
- elsif char == '"'
352
- n = peek(1)
353
- raise Sequel::Error, "invalid array, closing quote not followed by comma or closing brace" unless n == ',' || n == '}'
354
- break
337
+ # Parse the input character by character, returning an array
338
+ # of parsed (and potentially converted) objects.
339
+ def parse
340
+ raise Sequel::Error, "invalid array, empty string" if eos?
341
+ raise Sequel::Error, "invalid array, doesn't start with {" unless scan(/((\[\d+:\d+\])+=)?\{/)
342
+
343
+ while !eos?
344
+ char = scan(/[{}",]|[^{}",]+/)
345
+ if char == ','
346
+ # Comma outside quoted string indicates end of current entry
347
+ new_entry
348
+ elsif char == '"'
349
+ raise Sequel::Error, "invalid array, opening quote with existing recorded data" unless @recorded.empty?
350
+ while true
351
+ char = scan(/["\\]|[^"\\]+/)
352
+ if char == '\\'
353
+ @recorded << getch
354
+ elsif char == '"'
355
+ n = peek(1)
356
+ raise Sequel::Error, "invalid array, closing quote not followed by comma or closing brace" unless n == ',' || n == '}'
357
+ break
358
+ else
359
+ @recorded << char
360
+ end
361
+ end
362
+ new_entry(true)
363
+ elsif char == '{'
364
+ raise Sequel::Error, "invalid array, opening brace with existing recorded data" unless @recorded.empty?
365
+
366
+ # Start of new array, add it to the stack
367
+ new = []
368
+ @stack.last << new
369
+ @stack << new
370
+ elsif char == '}'
371
+ # End of current array, add current entry to the current array
372
+ new_entry
373
+
374
+ if @stack.length == 1
375
+ raise Sequel::Error, "array parsing finished without parsing entire string" unless eos?
376
+
377
+ # Top level of array, parsing should be over.
378
+ # Pop current array off stack and return it as result
379
+ return @stack.pop
355
380
  else
356
- @recorded << char
381
+ # Nested array, pop current array off stack
382
+ @stack.pop
357
383
  end
358
- end
359
- new_entry(true)
360
- elsif char == '{'
361
- raise Sequel::Error, "invalid array, opening brace with existing recorded data" unless @recorded.empty?
362
-
363
- # Start of new array, add it to the stack
364
- new = []
365
- @stack.last << new
366
- @stack << new
367
- elsif char == '}'
368
- # End of current array, add current entry to the current array
369
- new_entry
370
-
371
- if @stack.length == 1
372
- raise Sequel::Error, "array parsing finished without parsing entire string" unless eos?
373
-
374
- # Top level of array, parsing should be over.
375
- # Pop current array off stack and return it as result
376
- return @stack.pop
377
384
  else
378
- # Nested array, pop current array off stack
379
- @stack.pop
385
+ # Add the character to the recorded character buffer.
386
+ @recorded << char
380
387
  end
381
- else
382
- # Add the character to the recorded character buffer.
383
- @recorded << char
384
388
  end
385
- end
386
389
 
387
- raise Sequel::Error, "array parsing finished with array unclosed"
390
+ raise Sequel::Error, "array parsing finished with array unclosed"
391
+ end
388
392
  end
389
- end unless Sequel::Postgres.respond_to?(:parse_pg_array)
393
+ end
390
394
 
391
395
  # Callable object that takes the input string and parses it using Parser.
392
396
  class Creator