sequel 3.43.0 → 3.44.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.
- data/CHANGELOG +32 -0
 - data/doc/association_basics.rdoc +10 -3
 - data/doc/release_notes/3.37.0.txt +1 -1
 - data/doc/release_notes/3.44.0.txt +152 -0
 - data/lib/sequel/adapters/ado.rb +0 -6
 - data/lib/sequel/adapters/db2.rb +0 -3
 - data/lib/sequel/adapters/dbi.rb +0 -6
 - data/lib/sequel/adapters/ibmdb.rb +0 -3
 - data/lib/sequel/adapters/jdbc.rb +8 -12
 - data/lib/sequel/adapters/jdbc/as400.rb +2 -18
 - data/lib/sequel/adapters/jdbc/derby.rb +10 -0
 - data/lib/sequel/adapters/jdbc/h2.rb +10 -0
 - data/lib/sequel/adapters/jdbc/hsqldb.rb +10 -0
 - data/lib/sequel/adapters/jdbc/sqlite.rb +5 -0
 - data/lib/sequel/adapters/jdbc/sqlserver.rb +2 -4
 - data/lib/sequel/adapters/mock.rb +5 -0
 - data/lib/sequel/adapters/mysql2.rb +4 -13
 - data/lib/sequel/adapters/odbc.rb +0 -5
 - data/lib/sequel/adapters/oracle.rb +3 -5
 - data/lib/sequel/adapters/postgres.rb +2 -1
 - data/lib/sequel/adapters/shared/access.rb +10 -0
 - data/lib/sequel/adapters/shared/cubrid.rb +9 -0
 - data/lib/sequel/adapters/shared/db2.rb +10 -0
 - data/lib/sequel/adapters/shared/mssql.rb +10 -0
 - data/lib/sequel/adapters/shared/mysql.rb +14 -0
 - data/lib/sequel/adapters/shared/oracle.rb +15 -0
 - data/lib/sequel/adapters/shared/postgres.rb +26 -2
 - data/lib/sequel/adapters/shared/sqlite.rb +15 -0
 - data/lib/sequel/adapters/tinytds.rb +5 -32
 - data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +2 -37
 - data/lib/sequel/core.rb +3 -3
 - data/lib/sequel/database/misc.rb +40 -4
 - data/lib/sequel/database/query.rb +1 -1
 - data/lib/sequel/database/schema_methods.rb +33 -12
 - data/lib/sequel/dataset/actions.rb +51 -2
 - data/lib/sequel/dataset/features.rb +0 -6
 - data/lib/sequel/dataset/sql.rb +1 -1
 - data/lib/sequel/exceptions.rb +22 -7
 - data/lib/sequel/extensions/columns_introspection.rb +30 -5
 - data/lib/sequel/extensions/pg_auto_parameterize.rb +9 -0
 - data/lib/sequel/model/associations.rb +50 -37
 - data/lib/sequel/model/base.rb +30 -1
 - data/lib/sequel/plugins/eager_each.rb +17 -21
 - data/lib/sequel/plugins/identity_map.rb +2 -1
 - data/lib/sequel/plugins/many_through_many.rb +1 -1
 - data/lib/sequel/plugins/single_table_inheritance.rb +2 -2
 - data/lib/sequel/plugins/tactical_eager_loading.rb +1 -1
 - data/lib/sequel/sql.rb +4 -2
 - data/lib/sequel/version.rb +1 -1
 - data/spec/adapters/postgres_spec.rb +32 -2
 - data/spec/adapters/sqlite_spec.rb +20 -0
 - data/spec/core/database_spec.rb +40 -0
 - data/spec/core/dataset_spec.rb +91 -4
 - data/spec/core/mock_adapter_spec.rb +2 -1
 - data/spec/core/schema_generator_spec.rb +4 -0
 - data/spec/core/schema_spec.rb +9 -3
 - data/spec/extensions/association_dependencies_spec.rb +3 -3
 - data/spec/extensions/columns_introspection_spec.rb +28 -2
 - data/spec/extensions/eager_each_spec.rb +0 -1
 - data/spec/extensions/identity_map_spec.rb +3 -2
 - data/spec/extensions/migration_spec.rb +6 -0
 - data/spec/extensions/prepared_statements_associations_spec.rb +2 -2
 - data/spec/extensions/rcte_tree_spec.rb +2 -2
 - data/spec/extensions/single_table_inheritance_spec.rb +3 -0
 - data/spec/extensions/tactical_eager_loading_spec.rb +1 -1
 - data/spec/extensions/validation_class_methods_spec.rb +8 -0
 - data/spec/integration/associations_test.rb +4 -4
 - data/spec/integration/database_test.rb +68 -20
 - data/spec/integration/dataset_test.rb +48 -0
 - data/spec/integration/schema_test.rb +25 -1
 - data/spec/model/associations_spec.rb +21 -8
 - data/spec/model/dataset_methods_spec.rb +58 -18
 - metadata +4 -2
 
| 
         @@ -30,6 +30,11 @@ module Sequel 
     | 
|
| 
       30 
30 
     | 
    
         | 
| 
       31 
31 
     | 
    
         
             
                    private
         
     | 
| 
       32 
32 
     | 
    
         | 
| 
      
 33 
     | 
    
         
            +
                    DATABASE_ERROR_REGEXPS = {/Abort due to constraint violation/ => ConstraintViolation}.freeze
         
     | 
| 
      
 34 
     | 
    
         
            +
                    def database_error_regexps
         
     | 
| 
      
 35 
     | 
    
         
            +
                      DATABASE_ERROR_REGEXPS
         
     | 
| 
      
 36 
     | 
    
         
            +
                    end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
       33 
38 
     | 
    
         
             
                    # Use last_insert_rowid() to get the last inserted id.
         
     | 
| 
       34 
39 
     | 
    
         
             
                    def last_insert_id(conn, opts={})
         
     | 
| 
       35 
40 
     | 
    
         
             
                      statement(conn) do |stmt|
         
     | 
| 
         @@ -18,7 +18,7 @@ module Sequel 
     | 
|
| 
       18 
18 
     | 
    
         
             
                    # than getObject() for this column avoids the problem.
         
     | 
| 
       19 
19 
     | 
    
         
             
                    # Reference: http://social.msdn.microsoft.com/Forums/en/sqldataaccess/thread/20df12f3-d1bf-4526-9daa-239a83a8e435
         
     | 
| 
       20 
20 
     | 
    
         
             
                    module MetadataDatasetMethods
         
     | 
| 
       21 
     | 
    
         
            -
                      def process_result_set_convert(cols, result 
     | 
| 
      
 21 
     | 
    
         
            +
                      def process_result_set_convert(cols, result)
         
     | 
| 
       22 
22 
     | 
    
         
             
                        while result.next
         
     | 
| 
       23 
23 
     | 
    
         
             
                          row = {}
         
     | 
| 
       24 
24 
     | 
    
         
             
                          cols.each do |n, i, p|
         
     | 
| 
         @@ -40,18 +40,16 @@ module Sequel 
     | 
|
| 
       40 
40 
     | 
    
         
             
                              v
         
     | 
| 
       41 
41 
     | 
    
         
             
                            end
         
     | 
| 
       42 
42 
     | 
    
         
             
                          end
         
     | 
| 
       43 
     | 
    
         
            -
                          row.delete(rn) if rn
         
     | 
| 
       44 
43 
     | 
    
         
             
                          yield row
         
     | 
| 
       45 
44 
     | 
    
         
             
                        end
         
     | 
| 
       46 
45 
     | 
    
         
             
                      end
         
     | 
| 
       47 
46 
     | 
    
         | 
| 
       48 
     | 
    
         
            -
                      def process_result_set_no_convert(cols, result 
     | 
| 
      
 47 
     | 
    
         
            +
                      def process_result_set_no_convert(cols, result)
         
     | 
| 
       49 
48 
     | 
    
         
             
                        while result.next
         
     | 
| 
       50 
49 
     | 
    
         
             
                          row = {}
         
     | 
| 
       51 
50 
     | 
    
         
             
                          cols.each do |n, i|
         
     | 
| 
       52 
51 
     | 
    
         
             
                            row[n] = (n == :is_autoincrement ? result.getString(i) : result.getObject(i))
         
     | 
| 
       53 
52 
     | 
    
         
             
                          end
         
     | 
| 
       54 
     | 
    
         
            -
                          row.delete(rn) if rn
         
     | 
| 
       55 
53 
     | 
    
         
             
                          yield row
         
     | 
| 
       56 
54 
     | 
    
         
             
                        end
         
     | 
| 
       57 
55 
     | 
    
         
             
                      end
         
     | 
    
        data/lib/sequel/adapters/mock.rb
    CHANGED
    
    
| 
         @@ -138,21 +138,12 @@ module Sequel 
     | 
|
| 
       138 
138 
     | 
    
         
             
                  # Yield all rows matching this dataset.
         
     | 
| 
       139 
139 
     | 
    
         
             
                  def fetch_rows(sql)
         
     | 
| 
       140 
140 
     | 
    
         
             
                    execute(sql) do |r|
         
     | 
| 
       141 
     | 
    
         
            -
                      if identifier_output_method
         
     | 
| 
       142 
     | 
    
         
            -
                         
     | 
| 
       143 
     | 
    
         
            -
                        @columns = cols2 = cols.map{|c| output_identifier(c.to_s)}
         
     | 
| 
       144 
     | 
    
         
            -
                        cs = cols.zip(cols2)
         
     | 
| 
       145 
     | 
    
         
            -
                        r.each(:cast_booleans=>convert_tinyint_to_bool?) do |row|
         
     | 
| 
       146 
     | 
    
         
            -
                          h = {}
         
     | 
| 
       147 
     | 
    
         
            -
                          cs.each do |a, b|
         
     | 
| 
       148 
     | 
    
         
            -
                            h[b] = row[a]
         
     | 
| 
       149 
     | 
    
         
            -
                          end
         
     | 
| 
       150 
     | 
    
         
            -
                          yield h
         
     | 
| 
       151 
     | 
    
         
            -
                        end
         
     | 
| 
      
 141 
     | 
    
         
            +
                      @columns = if identifier_output_method
         
     | 
| 
      
 142 
     | 
    
         
            +
                        r.fields.map!{|c| output_identifier(c.to_s)}
         
     | 
| 
       152 
143 
     | 
    
         
             
                      else
         
     | 
| 
       153 
     | 
    
         
            -
                         
     | 
| 
       154 
     | 
    
         
            -
                        r.each(:cast_booleans=>convert_tinyint_to_bool?){|h| yield h}
         
     | 
| 
      
 144 
     | 
    
         
            +
                        r.fields
         
     | 
| 
       155 
145 
     | 
    
         
             
                      end
         
     | 
| 
      
 146 
     | 
    
         
            +
                      r.each(:cast_booleans=>convert_tinyint_to_bool?){|h| yield h}
         
     | 
| 
       156 
147 
     | 
    
         
             
                    end
         
     | 
| 
       157 
148 
     | 
    
         
             
                    self
         
     | 
| 
       158 
149 
     | 
    
         
             
                  end
         
     | 
    
        data/lib/sequel/adapters/odbc.rb
    CHANGED
    
    | 
         @@ -105,16 +105,11 @@ module Sequel 
     | 
|
| 
       105 
105 
     | 
    
         
             
                      i = -1
         
     | 
| 
       106 
106 
     | 
    
         
             
                      cols = s.columns(true).map{|c| [output_identifier(c.name), i+=1]}
         
     | 
| 
       107 
107 
     | 
    
         
             
                      columns = cols.map{|c| c.at(0)}
         
     | 
| 
       108 
     | 
    
         
            -
                      if opts[:offset] && offset_returns_row_number_column?
         
     | 
| 
       109 
     | 
    
         
            -
                        rn = row_number_column
         
     | 
| 
       110 
     | 
    
         
            -
                        columns.delete(rn)
         
     | 
| 
       111 
     | 
    
         
            -
                      end
         
     | 
| 
       112 
108 
     | 
    
         
             
                      @columns = columns
         
     | 
| 
       113 
109 
     | 
    
         
             
                      if rows = s.fetch_all
         
     | 
| 
       114 
110 
     | 
    
         
             
                        rows.each do |row|
         
     | 
| 
       115 
111 
     | 
    
         
             
                          hash = {}
         
     | 
| 
       116 
112 
     | 
    
         
             
                          cols.each{|n,i| hash[n] = convert_odbc_value(row[i])}
         
     | 
| 
       117 
     | 
    
         
            -
                          hash.delete(rn) if rn
         
     | 
| 
       118 
113 
     | 
    
         
             
                          yield hash
         
     | 
| 
       119 
114 
     | 
    
         
             
                        end
         
     | 
| 
       120 
115 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -37,7 +37,9 @@ module Sequel 
     | 
|
| 
       37 
37 
     | 
    
         
             
                      dbname = opts[:host]
         
     | 
| 
       38 
38 
     | 
    
         
             
                    end
         
     | 
| 
       39 
39 
     | 
    
         
             
                    conn = OCI8.new(opts[:user], opts[:password], dbname, opts[:privilege])
         
     | 
| 
       40 
     | 
    
         
            -
                     
     | 
| 
      
 40 
     | 
    
         
            +
                    if prefetch_rows = opts.fetch(:prefetch_rows, 100)
         
     | 
| 
      
 41 
     | 
    
         
            +
                      conn.prefetch_rows = typecast_value_integer(prefetch_rows)
         
     | 
| 
      
 42 
     | 
    
         
            +
                    end
         
     | 
| 
       41 
43 
     | 
    
         
             
                    conn.autocommit = true
         
     | 
| 
       42 
44 
     | 
    
         
             
                    conn.non_blocking = true
         
     | 
| 
       43 
45 
     | 
    
         | 
| 
         @@ -383,18 +385,14 @@ module Sequel 
     | 
|
| 
       383 
385 
     | 
    
         | 
| 
       384 
386 
     | 
    
         
             
                  def fetch_rows(sql)
         
     | 
| 
       385 
387 
     | 
    
         
             
                    execute(sql) do |cursor|
         
     | 
| 
       386 
     | 
    
         
            -
                      offset = @opts[:offset]
         
     | 
| 
       387 
     | 
    
         
            -
                      rn = row_number_column
         
     | 
| 
       388 
388 
     | 
    
         
             
                      cps = db.conversion_procs
         
     | 
| 
       389 
389 
     | 
    
         
             
                      cols = columns = cursor.get_col_names.map{|c| output_identifier(c)}
         
     | 
| 
       390 
390 
     | 
    
         
             
                      metadata = cursor.column_metadata
         
     | 
| 
       391 
391 
     | 
    
         
             
                      cm = cols.zip(metadata).map{|c, m| [c, cps[m.data_type]]}
         
     | 
| 
       392 
     | 
    
         
            -
                      columns = cols.reject{|x| x == rn} if offset
         
     | 
| 
       393 
392 
     | 
    
         
             
                      @columns = columns
         
     | 
| 
       394 
393 
     | 
    
         
             
                      while r = cursor.fetch
         
     | 
| 
       395 
394 
     | 
    
         
             
                        row = {}
         
     | 
| 
       396 
395 
     | 
    
         
             
                        r.zip(cm).each{|v, (c, cp)| row[c] = ((v && cp) ? cp.call(v) : v)}
         
     | 
| 
       397 
     | 
    
         
            -
                        row.delete(rn) if offset
         
     | 
| 
       398 
396 
     | 
    
         
             
                        yield row
         
     | 
| 
       399 
397 
     | 
    
         
             
                      end
         
     | 
| 
       400 
398 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -289,7 +289,8 @@ module Sequel 
     | 
|
| 
       289 
289 
     | 
    
         | 
| 
       290 
290 
     | 
    
         
             
                    # +copy_into+ uses PostgreSQL's +COPY FROM STDIN+ SQL statement to do very fast inserts 
         
     | 
| 
       291 
291 
     | 
    
         
             
                    # into a table using input preformatting in either CSV or PostgreSQL text format.
         
     | 
| 
       292 
     | 
    
         
            -
                    # This method is only supported if pg is the underlying ruby driver. 
     | 
| 
      
 292 
     | 
    
         
            +
                    # This method is only supported if pg 0.14.0+ is the underlying ruby driver.
         
     | 
| 
      
 293 
     | 
    
         
            +
                    # This method should only be called if you want
         
     | 
| 
       293 
294 
     | 
    
         
             
                    # results returned to the client.  If you are using +COPY FROM+
         
     | 
| 
       294 
295 
     | 
    
         
             
                    # with a filename, you should just use +run+ instead of this method.
         
     | 
| 
       295 
296 
     | 
    
         
             
                    #
         
     | 
| 
         @@ -43,6 +43,16 @@ module Sequel 
     | 
|
| 
       43 
43 
     | 
    
         
             
                    run(ds.into(name).sql)
         
     | 
| 
       44 
44 
     | 
    
         
             
                  end
         
     | 
| 
       45 
45 
     | 
    
         | 
| 
      
 46 
     | 
    
         
            +
                  DATABASE_ERROR_REGEXPS = {
         
     | 
| 
      
 47 
     | 
    
         
            +
                    /The changes you requested to the table were not successful because they would create duplicate values in the index, primary key, or relationship/ => UniqueConstraintViolation,
         
     | 
| 
      
 48 
     | 
    
         
            +
                    /You cannot add or change a record because a related record is required|The record cannot be deleted or changed because table/ => ForeignKeyConstraintViolation,
         
     | 
| 
      
 49 
     | 
    
         
            +
                    /One or more values are prohibited by the validation rule/ => CheckConstraintViolation,
         
     | 
| 
      
 50 
     | 
    
         
            +
                    /You must enter a value in the .+ field|cannot contain a Null value because the Required property for this field is set to True/ => NotNullConstraintViolation,
         
     | 
| 
      
 51 
     | 
    
         
            +
                  }.freeze
         
     | 
| 
      
 52 
     | 
    
         
            +
                  def database_error_regexps
         
     | 
| 
      
 53 
     | 
    
         
            +
                    DATABASE_ERROR_REGEXPS
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
       46 
56 
     | 
    
         
             
                  # The SQL to drop an index for the table.
         
     | 
| 
       47 
57 
     | 
    
         
             
                  def drop_index_sql(table, op)
         
     | 
| 
       48 
58 
     | 
    
         
             
                    "DROP INDEX #{quote_identifier(op[:name] || default_index_name(table, op[:columns]))} ON #{quote_schema_table(table)}"
         
     | 
| 
         @@ -126,6 +126,15 @@ module Sequel 
     | 
|
| 
       126 
126 
     | 
    
         
             
                    :query
         
     | 
| 
       127 
127 
     | 
    
         
             
                  end
         
     | 
| 
       128 
128 
     | 
    
         | 
| 
      
 129 
     | 
    
         
            +
                  DATABASE_ERROR_REGEXPS = {
         
     | 
| 
      
 130 
     | 
    
         
            +
                    /Operation would have caused one or more unique constraint violations/ => UniqueConstraintViolation,
         
     | 
| 
      
 131 
     | 
    
         
            +
                    /The constraint of the foreign key .+ is invalid|Update\/Delete operations are restricted by the foreign key/ => ForeignKeyConstraintViolation,
         
     | 
| 
      
 132 
     | 
    
         
            +
                    /cannot be made NULL/ => NotNullConstraintViolation,
         
     | 
| 
      
 133 
     | 
    
         
            +
                  }.freeze
         
     | 
| 
      
 134 
     | 
    
         
            +
                  def database_error_regexps
         
     | 
| 
      
 135 
     | 
    
         
            +
                    DATABASE_ERROR_REGEXPS
         
     | 
| 
      
 136 
     | 
    
         
            +
                  end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
       129 
138 
     | 
    
         
             
                  # CUBRID is case insensitive, so don't modify identifiers
         
     | 
| 
       130 
139 
     | 
    
         
             
                  def identifier_input_method_default
         
     | 
| 
       131 
140 
     | 
    
         
             
                    nil
         
     | 
| 
         @@ -154,6 +154,16 @@ module Sequel 
     | 
|
| 
       154 
154 
     | 
    
         
             
                    end
         
     | 
| 
       155 
155 
     | 
    
         
             
                  end
         
     | 
| 
       156 
156 
     | 
    
         | 
| 
      
 157 
     | 
    
         
            +
                  DATABASE_ERROR_REGEXPS = {
         
     | 
| 
      
 158 
     | 
    
         
            +
                    /DB2 SQL Error: SQLCODE=-803, SQLSTATE=23505|One or more values in the INSERT statement, UPDATE statement, or foreign key update caused by a DELETE statement are not valid because the primary key, unique constraint or unique index/ => UniqueConstraintViolation,
         
     | 
| 
      
 159 
     | 
    
         
            +
                    /DB2 SQL Error: (SQLCODE=-530, SQLSTATE=23503|SQLCODE=-532, SQLSTATE=23504)|The insert or update value of the FOREIGN KEY .+ is not equal to any value of the parent key of the parent table|A parent row cannot be deleted because the relationship .+ restricts the deletion/ => ForeignKeyConstraintViolation,
         
     | 
| 
      
 160 
     | 
    
         
            +
                    /DB2 SQL Error: SQLCODE=-545, SQLSTATE=23513|The requested operation is not allowed because a row does not satisfy the check constraint/ => CheckConstraintViolation,
         
     | 
| 
      
 161 
     | 
    
         
            +
                    /DB2 SQL Error: SQLCODE=-407, SQLSTATE=23502|Assignment of a NULL value to a NOT NULL column/ => NotNullConstraintViolation,
         
     | 
| 
      
 162 
     | 
    
         
            +
                  }.freeze
         
     | 
| 
      
 163 
     | 
    
         
            +
                  def database_error_regexps
         
     | 
| 
      
 164 
     | 
    
         
            +
                    DATABASE_ERROR_REGEXPS
         
     | 
| 
      
 165 
     | 
    
         
            +
                  end
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
       157 
167 
     | 
    
         
             
                  # DB2 has issues with quoted identifiers, so
         
     | 
| 
       158 
168 
     | 
    
         
             
                  # turn off database quoting by default.
         
     | 
| 
       159 
169 
     | 
    
         
             
                  def quote_identifiers_default
         
     | 
| 
         @@ -238,6 +238,16 @@ module Sequel 
     | 
|
| 
       238 
238 
     | 
    
         
             
                    run(ds.into(name).sql)
         
     | 
| 
       239 
239 
     | 
    
         
             
                  end
         
     | 
| 
       240 
240 
     | 
    
         | 
| 
      
 241 
     | 
    
         
            +
                  DATABASE_ERROR_REGEXPS = {
         
     | 
| 
      
 242 
     | 
    
         
            +
                    /Violation of UNIQUE KEY constraint/ => UniqueConstraintViolation,
         
     | 
| 
      
 243 
     | 
    
         
            +
                    /conflicted with the (FOREIGN KEY.*|REFERENCE) constraint/ => ForeignKeyConstraintViolation,
         
     | 
| 
      
 244 
     | 
    
         
            +
                    /conflicted with the CHECK constraint/ => CheckConstraintViolation,
         
     | 
| 
      
 245 
     | 
    
         
            +
                    /column does not allow nulls/ => NotNullConstraintViolation,
         
     | 
| 
      
 246 
     | 
    
         
            +
                  }.freeze
         
     | 
| 
      
 247 
     | 
    
         
            +
                  def database_error_regexps
         
     | 
| 
      
 248 
     | 
    
         
            +
                    DATABASE_ERROR_REGEXPS
         
     | 
| 
      
 249 
     | 
    
         
            +
                  end
         
     | 
| 
      
 250 
     | 
    
         
            +
             
     | 
| 
       241 
251 
     | 
    
         
             
                  # The name of the constraint for setting the default value on the table and column.
         
     | 
| 
       242 
252 
     | 
    
         
             
                  # The SQL used to select default constraints utilizes MSSQL catalog views which were introduced in 2005.
         
     | 
| 
       243 
253 
     | 
    
         
             
                  # This method intentionally does not support MSSQL 2000.
         
     | 
| 
         @@ -350,6 +350,15 @@ module Sequel 
     | 
|
| 
       350 
350 
     | 
    
         
             
                    "#{super}#{" ENGINE=#{engine}" if engine}#{" DEFAULT CHARSET=#{charset}" if charset}#{" DEFAULT COLLATE=#{collate}" if collate}"
         
     | 
| 
       351 
351 
     | 
    
         
             
                  end
         
     | 
| 
       352 
352 
     | 
    
         | 
| 
      
 353 
     | 
    
         
            +
                  DATABASE_ERROR_REGEXPS = {
         
     | 
| 
      
 354 
     | 
    
         
            +
                    /Duplicate entry .+ for key/ => UniqueConstraintViolation,
         
     | 
| 
      
 355 
     | 
    
         
            +
                    /foreign key constraint fails/ => ForeignKeyConstraintViolation,
         
     | 
| 
      
 356 
     | 
    
         
            +
                    /cannot be null/ => NotNullConstraintViolation,
         
     | 
| 
      
 357 
     | 
    
         
            +
                  }.freeze
         
     | 
| 
      
 358 
     | 
    
         
            +
                  def database_error_regexps
         
     | 
| 
      
 359 
     | 
    
         
            +
                    DATABASE_ERROR_REGEXPS
         
     | 
| 
      
 360 
     | 
    
         
            +
                  end
         
     | 
| 
      
 361 
     | 
    
         
            +
             
     | 
| 
       353 
362 
     | 
    
         
             
                  # Backbone of the tables and views support using SHOW FULL TABLES.
         
     | 
| 
       354 
363 
     | 
    
         
             
                  def full_tables(type, opts)
         
     | 
| 
       355 
364 
     | 
    
         
             
                    m = output_identifier_meth
         
     | 
| 
         @@ -430,6 +439,11 @@ module Sequel 
     | 
|
| 
       430 
439 
     | 
    
         
             
                    true
         
     | 
| 
       431 
440 
     | 
    
         
             
                  end
         
     | 
| 
       432 
441 
     | 
    
         | 
| 
      
 442 
     | 
    
         
            +
                  # MySQL supports CREATE OR REPLACE VIEW.
         
     | 
| 
      
 443 
     | 
    
         
            +
                  def supports_create_or_replace_view?
         
     | 
| 
      
 444 
     | 
    
         
            +
                    true
         
     | 
| 
      
 445 
     | 
    
         
            +
                  end
         
     | 
| 
      
 446 
     | 
    
         
            +
             
     | 
| 
       433 
447 
     | 
    
         
             
                  # Respect the :size option if given to produce
         
     | 
| 
       434 
448 
     | 
    
         
             
                  # tinyblob, mediumblob, and longblob if :tiny,
         
     | 
| 
       435 
449 
     | 
    
         
             
                  # :medium, or :long is given.
         
     | 
| 
         @@ -136,6 +136,16 @@ module Sequel 
     | 
|
| 
       136 
136 
     | 
    
         
             
                    sql
         
     | 
| 
       137 
137 
     | 
    
         
             
                  end
         
     | 
| 
       138 
138 
     | 
    
         | 
| 
      
 139 
     | 
    
         
            +
                  DATABASE_ERROR_REGEXPS = {
         
     | 
| 
      
 140 
     | 
    
         
            +
                    /unique constraint .+ violated/ => UniqueConstraintViolation,
         
     | 
| 
      
 141 
     | 
    
         
            +
                    /integrity constraint .+ violated/ => ForeignKeyConstraintViolation,
         
     | 
| 
      
 142 
     | 
    
         
            +
                    /check constraint .+ violated/ => CheckConstraintViolation,
         
     | 
| 
      
 143 
     | 
    
         
            +
                    /cannot insert NULL into|cannot update .+ to NULL/ => NotNullConstraintViolation,
         
     | 
| 
      
 144 
     | 
    
         
            +
                  }.freeze
         
     | 
| 
      
 145 
     | 
    
         
            +
                  def database_error_regexps
         
     | 
| 
      
 146 
     | 
    
         
            +
                    DATABASE_ERROR_REGEXPS
         
     | 
| 
      
 147 
     | 
    
         
            +
                  end
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
       139 
149 
     | 
    
         
             
                  def default_sequence_name(table, column)
         
     | 
| 
       140 
150 
     | 
    
         
             
                    "seq_#{table}_#{column}"
         
     | 
| 
       141 
151 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -160,6 +170,11 @@ module Sequel 
     | 
|
| 
       160 
170 
     | 
    
         
             
                    end
         
     | 
| 
       161 
171 
     | 
    
         
             
                  end
         
     | 
| 
       162 
172 
     | 
    
         | 
| 
      
 173 
     | 
    
         
            +
                  # Oracle supports CREATE OR REPLACE VIEW.
         
     | 
| 
      
 174 
     | 
    
         
            +
                  def supports_create_or_replace_view?
         
     | 
| 
      
 175 
     | 
    
         
            +
                    true
         
     | 
| 
      
 176 
     | 
    
         
            +
                  end
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
       163 
178 
     | 
    
         
             
                  # Oracle's integer/:number type handles larger values than
         
     | 
| 
       164 
179 
     | 
    
         
             
                  # most other databases's bigint types, so it should be
         
     | 
| 
       165 
180 
     | 
    
         
             
                  # safe to use for Bignum.
         
     | 
| 
         @@ -53,14 +53,14 @@ module Sequel 
     | 
|
| 
       53 
53 
     | 
    
         
             
                end
         
     | 
| 
       54 
54 
     | 
    
         | 
| 
       55 
55 
     | 
    
         
             
                class CreateTableGenerator < Sequel::Schema::Generator
         
     | 
| 
       56 
     | 
    
         
            -
                  # Add an exclusion constraint when creating the table.  
     | 
| 
      
 56 
     | 
    
         
            +
                  # Add an exclusion constraint when creating the table. Elements should be
         
     | 
| 
       57 
57 
     | 
    
         
             
                  # an array of 2 element arrays, with the first element being the column or
         
     | 
| 
       58 
58 
     | 
    
         
             
                  # expression the exclusion constraint is applied to, and the second element
         
     | 
| 
       59 
59 
     | 
    
         
             
                  # being the operator to use for the column/expression to check for exclusion.
         
     | 
| 
       60 
60 
     | 
    
         
             
                  #
         
     | 
| 
       61 
61 
     | 
    
         
             
                  # Example:
         
     | 
| 
       62 
62 
     | 
    
         
             
                  #
         
     | 
| 
       63 
     | 
    
         
            -
                  #    
     | 
| 
      
 63 
     | 
    
         
            +
                  #   exclude([[:col1, '&&'], [:col2, '=']])
         
     | 
| 
       64 
64 
     | 
    
         
             
                  #   # EXCLUDE USING gist (col1 WITH &&, col2 WITH =)
         
     | 
| 
       65 
65 
     | 
    
         
             
                  #
         
     | 
| 
       66 
66 
     | 
    
         
             
                  # Options supported:
         
     | 
| 
         @@ -89,6 +89,9 @@ module Sequel 
     | 
|
| 
       89 
89 
     | 
    
         
             
                  end
         
     | 
| 
       90 
90 
     | 
    
         
             
                end
         
     | 
| 
       91 
91 
     | 
    
         | 
| 
      
 92 
     | 
    
         
            +
                # Error raised when Sequel determines a PostgreSQL exclusion constraint has been violated.
         
     | 
| 
      
 93 
     | 
    
         
            +
                class ExclusionConstraintViolation < Sequel::ConstraintViolation; end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
       92 
95 
     | 
    
         
             
                # Methods shared by Database instances that connect to PostgreSQL.
         
     | 
| 
       93 
96 
     | 
    
         
             
                module DatabaseMethods
         
     | 
| 
       94 
97 
     | 
    
         
             
                  EXCLUDE_SCHEMAS = /pg_*|information_schema/i
         
     | 
| 
         @@ -627,6 +630,17 @@ module Sequel 
     | 
|
| 
       627 
630 
     | 
    
         
             
                    end
         
     | 
| 
       628 
631 
     | 
    
         
             
                  end
         
     | 
| 
       629 
632 
     | 
    
         | 
| 
      
 633 
     | 
    
         
            +
                  DATABASE_ERROR_REGEXPS = {
         
     | 
| 
      
 634 
     | 
    
         
            +
                    /duplicate key value violates unique constraint/ => UniqueConstraintViolation,
         
     | 
| 
      
 635 
     | 
    
         
            +
                    /violates foreign key constraint/ => ForeignKeyConstraintViolation,
         
     | 
| 
      
 636 
     | 
    
         
            +
                    /violates check constraint/ => CheckConstraintViolation,
         
     | 
| 
      
 637 
     | 
    
         
            +
                    /violates not-null constraint/ => NotNullConstraintViolation,
         
     | 
| 
      
 638 
     | 
    
         
            +
                    /conflicting key value violates exclusion constraint/ => ExclusionConstraintViolation,
         
     | 
| 
      
 639 
     | 
    
         
            +
                  }.freeze
         
     | 
| 
      
 640 
     | 
    
         
            +
                  def database_error_regexps
         
     | 
| 
      
 641 
     | 
    
         
            +
                    DATABASE_ERROR_REGEXPS
         
     | 
| 
      
 642 
     | 
    
         
            +
                  end
         
     | 
| 
      
 643 
     | 
    
         
            +
             
     | 
| 
       630 
644 
     | 
    
         
             
                  # SQL for doing fast table insert from stdin.
         
     | 
| 
       631 
645 
     | 
    
         
             
                  def copy_into_sql(table, opts)
         
     | 
| 
       632 
646 
     | 
    
         
             
                    sql = "COPY #{literal(table)}"
         
     | 
| 
         @@ -717,6 +731,11 @@ module Sequel 
     | 
|
| 
       717 
731 
     | 
    
         
             
                    "CREATE TRIGGER #{name} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}#{' FOR EACH ROW' if opts[:each_row]} EXECUTE PROCEDURE #{function}(#{Array(opts[:args]).map{|a| literal(a)}.join(', ')})"
         
     | 
| 
       718 
732 
     | 
    
         
             
                  end
         
     | 
| 
       719 
733 
     | 
    
         | 
| 
      
 734 
     | 
    
         
            +
                  # DDL fragment for initial part of CREATE VIEW statement
         
     | 
| 
      
 735 
     | 
    
         
            +
                  def create_view_prefix_sql(name, options)
         
     | 
| 
      
 736 
     | 
    
         
            +
                    "CREATE #{'OR REPLACE 'if options[:replace]}#{'TEMPORARY 'if options[:temp]}VIEW #{quote_schema_table(name)}"
         
     | 
| 
      
 737 
     | 
    
         
            +
                  end
         
     | 
| 
      
 738 
     | 
    
         
            +
             
     | 
| 
       720 
739 
     | 
    
         
             
                  # The errors that the main adapters can raise, depends on the adapter being used
         
     | 
| 
       721 
740 
     | 
    
         
             
                  def database_error_classes
         
     | 
| 
       722 
741 
     | 
    
         
             
                    CONVERTED_EXCEPTIONS
         
     | 
| 
         @@ -919,6 +938,11 @@ module Sequel 
     | 
|
| 
       919 
938 
     | 
    
         
             
                    true
         
     | 
| 
       920 
939 
     | 
    
         
             
                  end
         
     | 
| 
       921 
940 
     | 
    
         | 
| 
      
 941 
     | 
    
         
            +
                  # PostgreSQL supports CREATE OR REPLACE VIEW.
         
     | 
| 
      
 942 
     | 
    
         
            +
                  def supports_create_or_replace_view?
         
     | 
| 
      
 943 
     | 
    
         
            +
                    true
         
     | 
| 
      
 944 
     | 
    
         
            +
                  end
         
     | 
| 
      
 945 
     | 
    
         
            +
             
     | 
| 
       922 
946 
     | 
    
         
             
                  # Handle bigserial type if :serial option is present
         
     | 
| 
       923 
947 
     | 
    
         
             
                  def type_literal_generic_bignum(column)
         
     | 
| 
       924 
948 
     | 
    
         
             
                    column[:serial] ? :bigserial : super
         
     | 
| 
         @@ -312,6 +312,21 @@ module Sequel 
     | 
|
| 
       312 
312 
     | 
    
         
             
                    ps
         
     | 
| 
       313 
313 
     | 
    
         
             
                  end
         
     | 
| 
       314 
314 
     | 
    
         | 
| 
      
 315 
     | 
    
         
            +
                  # SQLite support creating temporary views.
         
     | 
| 
      
 316 
     | 
    
         
            +
                  def create_view_prefix_sql(name, options)
         
     | 
| 
      
 317 
     | 
    
         
            +
                    "CREATE #{'TEMPORARY 'if options[:temp]}VIEW #{quote_schema_table(name)}"
         
     | 
| 
      
 318 
     | 
    
         
            +
                  end
         
     | 
| 
      
 319 
     | 
    
         
            +
             
     | 
| 
      
 320 
     | 
    
         
            +
                  DATABASE_ERROR_REGEXPS = {
         
     | 
| 
      
 321 
     | 
    
         
            +
                    /is not unique\z/ => UniqueConstraintViolation,
         
     | 
| 
      
 322 
     | 
    
         
            +
                    /foreign key constraint failed\z/ => ForeignKeyConstraintViolation,
         
     | 
| 
      
 323 
     | 
    
         
            +
                    /\A(SQLITE ERROR 19 \(CONSTRAINT\) : )?constraint failed\z/ => ConstraintViolation,
         
     | 
| 
      
 324 
     | 
    
         
            +
                    /may not be NULL\z/ => NotNullConstraintViolation,
         
     | 
| 
      
 325 
     | 
    
         
            +
                  }.freeze
         
     | 
| 
      
 326 
     | 
    
         
            +
                  def database_error_regexps
         
     | 
| 
      
 327 
     | 
    
         
            +
                    DATABASE_ERROR_REGEXPS
         
     | 
| 
      
 328 
     | 
    
         
            +
                  end
         
     | 
| 
      
 329 
     | 
    
         
            +
             
     | 
| 
       315 
330 
     | 
    
         
             
                  # The array of column schema hashes for the current columns in the table
         
     | 
| 
       316 
331 
     | 
    
         
             
                  def defined_columns_for(table)
         
     | 
| 
       317 
332 
     | 
    
         
             
                    cols = parse_pragma(table, {})
         
     | 
| 
         @@ -18,6 +18,7 @@ module Sequel 
     | 
|
| 
       18 
18 
     | 
    
         
             
                    opts = server_opts(server)
         
     | 
| 
       19 
19 
     | 
    
         
             
                    opts[:username] = opts[:user]
         
     | 
| 
       20 
20 
     | 
    
         
             
                    c = TinyTds::Client.new(opts)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    c.query_options.merge!(:cache_rows=>false)
         
     | 
| 
       21 
22 
     | 
    
         | 
| 
       22 
23 
     | 
    
         
             
                    if (ts = opts[:textsize])
         
     | 
| 
       23 
24 
     | 
    
         
             
                      sql = "SET TEXTSIZE #{typecast_value_integer(ts)}"
         
     | 
| 
         @@ -215,40 +216,12 @@ module Sequel 
     | 
|
| 
       215 
216 
     | 
    
         
             
                  # various cases.
         
     | 
| 
       216 
217 
     | 
    
         
             
                  def fetch_rows(sql)
         
     | 
| 
       217 
218 
     | 
    
         
             
                    execute(sql) do |result|
         
     | 
| 
       218 
     | 
    
         
            -
                       
     | 
| 
       219 
     | 
    
         
            -
                       
     | 
| 
       220 
     | 
    
         
            -
             
     | 
| 
       221 
     | 
    
         
            -
                      columns = cols = result.fields.map{|c| output_identifier(c)}
         
     | 
| 
       222 
     | 
    
         
            -
                      if offset
         
     | 
| 
       223 
     | 
    
         
            -
                        rn = row_number_column
         
     | 
| 
       224 
     | 
    
         
            -
                        columns = columns.dup
         
     | 
| 
       225 
     | 
    
         
            -
                        columns.delete(rn)
         
     | 
| 
       226 
     | 
    
         
            -
                      end
         
     | 
| 
       227 
     | 
    
         
            -
                      @columns = columns
         
     | 
| 
       228 
     | 
    
         
            -
                      #if identifier_output_method
         
     | 
| 
       229 
     | 
    
         
            -
                        each_opts[:as] = :array
         
     | 
| 
       230 
     | 
    
         
            -
                        result.each(each_opts) do |r|
         
     | 
| 
       231 
     | 
    
         
            -
                          h = {}
         
     | 
| 
       232 
     | 
    
         
            -
                          cols.zip(r).each{|k, v| h[k] = v}
         
     | 
| 
       233 
     | 
    
         
            -
                          h.delete(rn) if rn
         
     | 
| 
       234 
     | 
    
         
            -
                          yield h
         
     | 
| 
       235 
     | 
    
         
            -
                        end
         
     | 
| 
       236 
     | 
    
         
            -
            =begin
         
     | 
| 
       237 
     | 
    
         
            -
                    # Temporarily disable this optimization, as tiny_tds uses string keys
         
     | 
| 
       238 
     | 
    
         
            -
                    # if result.fields is called before result.each(:symbolize_keys=>true).
         
     | 
| 
       239 
     | 
    
         
            -
                    # See https://github.com/rails-sqlserver/tiny_tds/issues/57
         
     | 
| 
      
 219 
     | 
    
         
            +
                      @columns = result.fields.map!{|c| output_identifier(c)}
         
     | 
| 
      
 220 
     | 
    
         
            +
                      if db.timezone == :utc
         
     | 
| 
      
 221 
     | 
    
         
            +
                        result.each(:timezone=>:utc){|r| yield r}
         
     | 
| 
       240 
222 
     | 
    
         
             
                      else
         
     | 
| 
       241 
     | 
    
         
            -
                         
     | 
| 
       242 
     | 
    
         
            -
                        if offset
         
     | 
| 
       243 
     | 
    
         
            -
                          result.each(each_opts) do |r|
         
     | 
| 
       244 
     | 
    
         
            -
                            r.delete(rn) if rn
         
     | 
| 
       245 
     | 
    
         
            -
                            yield r
         
     | 
| 
       246 
     | 
    
         
            -
                          end
         
     | 
| 
       247 
     | 
    
         
            -
                        else
         
     | 
| 
       248 
     | 
    
         
            -
                          result.each(each_opts, &Proc.new)
         
     | 
| 
       249 
     | 
    
         
            -
                        end
         
     | 
| 
      
 223 
     | 
    
         
            +
                        result.each{|r| yield r}
         
     | 
| 
       250 
224 
     | 
    
         
             
                      end
         
     | 
| 
       251 
     | 
    
         
            -
            =end
         
     | 
| 
       252 
225 
     | 
    
         
             
                    end
         
     | 
| 
       253 
226 
     | 
    
         
             
                    self
         
     | 
| 
       254 
227 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -1,36 +1,5 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Sequel
         
     | 
| 
       2 
2 
     | 
    
         
             
              module EmulateOffsetWithRowNumber
         
     | 
| 
       3 
     | 
    
         
            -
                # When a subselect that uses :offset is used in IN or NOT IN,
         
     | 
| 
       4 
     | 
    
         
            -
                # use a nested subselect that only includes the first column
         
     | 
| 
       5 
     | 
    
         
            -
                # instead of the ROW_NUMBER column added by the emulated offset support.
         
     | 
| 
       6 
     | 
    
         
            -
                def complex_expression_sql_append(sql, op, args)
         
     | 
| 
       7 
     | 
    
         
            -
                  case op
         
     | 
| 
       8 
     | 
    
         
            -
                  when :IN, :"NOT IN"
         
     | 
| 
       9 
     | 
    
         
            -
                    ds = args.at(1)
         
     | 
| 
       10 
     | 
    
         
            -
                    if ds.is_a?(Sequel::Dataset) && ds.opts[:offset]
         
     | 
| 
       11 
     | 
    
         
            -
                      c = ds.opts[:select].first
         
     | 
| 
       12 
     | 
    
         
            -
                      case c
         
     | 
| 
       13 
     | 
    
         
            -
                      when Symbol
         
     | 
| 
       14 
     | 
    
         
            -
                        t, cl, a = split_symbol(c)
         
     | 
| 
       15 
     | 
    
         
            -
                        if a
         
     | 
| 
       16 
     | 
    
         
            -
                          c = SQL::Identifier.new(a)
         
     | 
| 
       17 
     | 
    
         
            -
                        elsif t
         
     | 
| 
       18 
     | 
    
         
            -
                          c = SQL::Identifier.new(cl)
         
     | 
| 
       19 
     | 
    
         
            -
                        end
         
     | 
| 
       20 
     | 
    
         
            -
                      when SQL::AliasedExpression
         
     | 
| 
       21 
     | 
    
         
            -
                        c = SQL::Identifier.new(c.aliaz)
         
     | 
| 
       22 
     | 
    
         
            -
                      when SQL::QualifiedIdentifier
         
     | 
| 
       23 
     | 
    
         
            -
                        c = SQL::Identifier.new(c.column)
         
     | 
| 
       24 
     | 
    
         
            -
                      end
         
     | 
| 
       25 
     | 
    
         
            -
                      super(sql, op, [args.at(0), ds.from_self.select(c)])
         
     | 
| 
       26 
     | 
    
         
            -
                    else
         
     | 
| 
       27 
     | 
    
         
            -
                      super
         
     | 
| 
       28 
     | 
    
         
            -
                    end
         
     | 
| 
       29 
     | 
    
         
            -
                  else
         
     | 
| 
       30 
     | 
    
         
            -
                    super
         
     | 
| 
       31 
     | 
    
         
            -
                  end
         
     | 
| 
       32 
     | 
    
         
            -
                end
         
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
3 
     | 
    
         
             
                # Emulate OFFSET support with the ROW_NUMBER window function
         
     | 
| 
       35 
4 
     | 
    
         
             
                # 
         
     | 
| 
       36 
5 
     | 
    
         
             
                # The implementation is ugly, cloning the current dataset and modifying
         
     | 
| 
         @@ -47,6 +16,7 @@ module Sequel 
     | 
|
| 
       47 
16 
     | 
    
         
             
                    raise(Error, "#{db.database_type} requires an order be provided if using an offset")
         
     | 
| 
       48 
17 
     | 
    
         
             
                  end
         
     | 
| 
       49 
18 
     | 
    
         | 
| 
      
 19 
     | 
    
         
            +
                  columns = clone(:append_sql=>'').columns
         
     | 
| 
       50 
20 
     | 
    
         
             
                  dsa1 = dataset_alias(1)
         
     | 
| 
       51 
21 
     | 
    
         
             
                  rn = row_number_column
         
     | 
| 
       52 
22 
     | 
    
         
             
                  sql = @opts[:append_sql] || ''
         
     | 
| 
         @@ -54,6 +24,7 @@ module Sequel 
     | 
|
| 
       54 
24 
     | 
    
         
             
                    unordered.
         
     | 
| 
       55 
25 
     | 
    
         
             
                    select_append{ROW_NUMBER(:over, :order=>order){}.as(rn)}.
         
     | 
| 
       56 
26 
     | 
    
         
             
                    from_self(:alias=>dsa1).
         
     | 
| 
      
 27 
     | 
    
         
            +
                    select(*columns).
         
     | 
| 
       57 
28 
     | 
    
         
             
                    limit(@opts[:limit]).
         
     | 
| 
       58 
29 
     | 
    
         
             
                    where(SQL::Identifier.new(rn) > o).
         
     | 
| 
       59 
30 
     | 
    
         
             
                    order(rn))
         
     | 
| 
         @@ -67,11 +38,5 @@ module Sequel 
     | 
|
| 
       67 
38 
     | 
    
         
             
                def default_offset_order
         
     | 
| 
       68 
39 
     | 
    
         
             
                  clone(:append_sql=>'').columns
         
     | 
| 
       69 
40 
     | 
    
         
             
                end
         
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
                # This emulation adds an extra row number column that should be
         
     | 
| 
       72 
     | 
    
         
            -
                # eliminated.
         
     | 
| 
       73 
     | 
    
         
            -
                def offset_returns_row_number_column?
         
     | 
| 
       74 
     | 
    
         
            -
                  true
         
     | 
| 
       75 
     | 
    
         
            -
                end
         
     | 
| 
       76 
41 
     | 
    
         
             
              end
         
     | 
| 
       77 
42 
     | 
    
         
             
            end
         
     |