sequel 5.9.0 → 5.10.0

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.
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