Class: Cequel::Metal::DataSet

Inherits:
Object
  • Object
show all
Extended by:
Util::Forwardable
Includes:
Enumerable
Defined in:
lib/cequel/metal/data_set.rb

Overview

Encapsulates a data set, specified as a table and optionally various query elements.

Examples:

Data set representing entire contents of a table

data_set = database[:posts]

Data set limiting rows returned

data_set = database[:posts].limit(10)

Data set targeting only one partition

data_set = database[:posts].where(blog_subdomain: 'cassandra')

See Also:

Since:

  • 1.0.0

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Util::Forwardable

delegate

Constructor Details

#initialize(table_name, keyspace) ⇒ DataSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of DataSet.

Parameters:

  • table_name (Symbol)

    column family for this data set

  • keyspace (Keyspace)

    keyspace this data set’s table lives in

See Also:

Since:

  • 1.0.0



64
65
66
67
68
# File 'lib/cequel/metal/data_set.rb', line 64

def initialize(table_name, keyspace)
  @table_name, @keyspace = table_name, keyspace
  @select_columns, @ttl_columns, @writetime_columns, @row_specifications,
    @sort_order = [], [], [], [], {}
end

Instance Attribute Details

#allow_filteringObject

Since:

  • 1.0.0



53
54
55
# File 'lib/cequel/metal/data_set.rb', line 53

def allow_filtering
  @allow_filtering
end

#keyspaceKeyspace (readonly)

Returns keyspace that this data set’s table resides in.

Returns:

  • (Keyspace)

    keyspace that this data set’s table resides in

Since:

  • 1.0.0



27
28
29
# File 'lib/cequel/metal/data_set.rb', line 27

def keyspace
  @keyspace
end

#query_consistencySymbol

Returns what consistency level queries from this data set will use.

Returns:

  • (Symbol)

    what consistency level queries from this data set will use

Since:

  • 1.1.0



50
51
52
# File 'lib/cequel/metal/data_set.rb', line 50

def query_consistency
  @query_consistency
end

#query_page_sizeObject

Since:

  • 1.0.0



51
52
53
# File 'lib/cequel/metal/data_set.rb', line 51

def query_page_size
  @query_page_size
end

#query_paging_stateObject

Since:

  • 1.0.0



52
53
54
# File 'lib/cequel/metal/data_set.rb', line 52

def query_paging_state
  @query_paging_state
end

#row_limitInteger

Returns maximum number of rows to return, ‘nil` if no limit.

Returns:

  • (Integer)

    maximum number of rows to return, ‘nil` if no limit

Since:

  • 1.0.0



46
47
48
# File 'lib/cequel/metal/data_set.rb', line 46

def row_limit
  @row_limit
end

#row_specificationsArray<RowSpecification> (readonly)

Returns row specifications limiting the result rows returned by this data set.

Returns:

  • (Array<RowSpecification>)

    row specifications limiting the result rows returned by this data set

Since:

  • 1.0.0



42
43
44
# File 'lib/cequel/metal/data_set.rb', line 42

def row_specifications
  @row_specifications
end

#select_columnsArray<Symbol> (readonly)

Returns columns that this data set restricts result rows to; empty if none.

Returns:

  • (Array<Symbol>)

    columns that this data set restricts result rows to; empty if none

Since:

  • 1.0.0



33
34
35
# File 'lib/cequel/metal/data_set.rb', line 33

def select_columns
  @select_columns
end

#sort_orderHash<Symbol,Symbol> (readonly)

Returns map of column names to sort directions.

Returns:

  • (Hash<Symbol,Symbol>)

    map of column names to sort directions

Since:

  • 1.0.0



44
45
46
# File 'lib/cequel/metal/data_set.rb', line 44

def sort_order
  @sort_order
end

#table_nameSymbol (readonly)

Returns name of the table that this data set retrieves data from.

Returns:

  • (Symbol)

    name of the table that this data set retrieves data from

Since:

  • 1.0.0



30
31
32
# File 'lib/cequel/metal/data_set.rb', line 30

def table_name
  @table_name
end

#ttl_columnsArray<Symbol> (readonly)

Returns columns that this data set will select the TTLs of.

Returns:

  • (Array<Symbol>)

    columns that this data set will select the TTLs of

Since:

  • 1.0.0



36
37
38
# File 'lib/cequel/metal/data_set.rb', line 36

def ttl_columns
  @ttl_columns
end

#writetime_columnsArray<Symbol> (readonly)

Returns columns that this data set will select the writetimes of.

Returns:

  • (Array<Symbol>)

    columns that this data set will select the writetimes of

Since:

  • 1.0.0



39
40
41
# File 'lib/cequel/metal/data_set.rb', line 39

def writetime_columns
  @writetime_columns
end

Instance Method Details

#==(other) ⇒ Boolean

Returns:

  • (Boolean)

Since:

  • 1.0.0



673
674
675
# File 'lib/cequel/metal/data_set.rb', line 673

def ==(other)
  cql == other.cql
end

#allow_filtering!Object

See Also:

  • RecordSet#allow_filtering!

Since:

  • 1.0.0



582
583
584
585
586
# File 'lib/cequel/metal/data_set.rb', line 582

def allow_filtering!
  clone.tap do |data_set|
    data_set.allow_filtering = true
  end
end

#allow_filtering_cqlObject

Since:

  • 1.0.0



692
693
694
695
696
697
# File 'lib/cequel/metal/data_set.rb', line 692

def allow_filtering_cql
  if allow_filtering
    ' ALLOW FILTERING'
  else ''
  end
end

#consistency(consistency) ⇒ DataSet

Change the consistency for queries performed by this data set

Parameters:

  • consistency (Symbol)

    a consistency level

Returns:

  • (DataSet)

    new data set tuned to the given consistency

See Also:

Since:

  • 1.1.0



567
568
569
570
571
# File 'lib/cequel/metal/data_set.rb', line 567

def consistency(consistency)
  clone.tap do |data_set|
    data_set.query_consistency = consistency
  end
end

#countObject Also known as: length, size

Raises:

  • (DangerousQueryError)

    to prevent loading the entire record set to be counted

Since:

  • 1.0.0



644
645
646
# File 'lib/cequel/metal/data_set.rb', line 644

def count
  raise Cequel::Record::DangerousQueryError.new
end

#cqlStatement

Returns CQL ‘SELECT` statement encoding this data set’s scope.

Returns:

  • (Statement)

    CQL ‘SELECT` statement encoding this data set’s scope.

Since:

  • 1.0.0



653
654
655
656
657
658
659
660
661
# File 'lib/cequel/metal/data_set.rb', line 653

def cql
  statement = Statement.new
    .append(select_cql)
    .append(" FROM #{table_name}")
    .append(*row_specifications_cql)
    .append(sort_order_cql)
    .append(limit_cql)
    .append(allow_filtering_cql)
end

#decrement(deltas, options = {}) ⇒ void Also known as: decr

This method returns an undefined value.

Decrement one or more counter columns

Parameters:

  • deltas (Hash<Symbol,Integer>)

    map of counter column names to amount by which to decrement each column

See Also:

Since:

  • 0.5.0



175
176
177
# File 'lib/cequel/metal/data_set.rb', line 175

def decrement(deltas, options = {})
  incrementer { decrement(deltas) }.execute(options)
end

#delete(options = {}) ⇒ void #delete(*columns, options = {}) ⇒ void #delete(options = {}) { ... } ⇒ void

Note:

If enclosed in a Keyspace#batch block, this method will be executed as part of the batch.

This method returns an undefined value.

Overloads:

  • #delete(options = {}) ⇒ void

    Delete one or more rows from the table

    Examples:

    posts.where(blog_subdomain: 'cassandra', permalink: 'cequel').
      delete

    Parameters:

    • options (Options) (defaults to: {})

      options for persistence

  • #delete(*columns, options = {}) ⇒ void

    Delete data from given columns in the specified rows. This is equivalent to setting columns to ‘NULL` in an SQL database.

    Examples:

    posts.where(blog_subdomain: 'cassandra', permalink: 'cequel').
      delete(:body)

    Parameters:

    • columns (Symbol)

      columns to remove

    • options (Options) (defaults to: {})

      options for persistence

  • #delete(options = {}) { ... } ⇒ void

    Construct a ‘DELETE` statement with multiple operations (column deletions, collection element removals, etc.)

    Examples:

    posts.where(blog_subdomain: 'bigdata', permalink: 'cql').delete do
      delete_columns :body
      list_remove_at :categories, 2
    end

    Parameters:

    • options (Options) (defaults to: {})

      options for persistence

    Yields:

    • DSL context for construction delete statement

    See Also:

See Also:

Since:

  • 1.0.0



429
430
431
432
433
434
435
436
437
438
# File 'lib/cequel/metal/data_set.rb', line 429

def delete(*columns, &block)
  options = columns.extract_options!
  if block
    deleter(&block).execute(options)
  elsif columns.empty?
    deleter { delete_row }.execute(options)
  else
    deleter { delete_columns(*columns) }.execute(options)
  end
end

#eachEnumerator #each {|Hash| ... } ⇒ void

Enumerate over rows in this data set. Along with #each, all other Enumerable methods are implemented.

Overloads:

  • #eachEnumerator

    Returns enumerator for rows, if no block given.

    Returns:

    • (Enumerator)

      enumerator for rows, if no block given

  • #each {|Hash| ... } ⇒ void

    This method returns an undefined value.

    Yields:

    • (Hash)

      result rows

Returns:

  • (Enumerator, void)

Since:

  • 1.0.0



629
630
631
632
# File 'lib/cequel/metal/data_set.rb', line 629

def each
  return enum_for(:each) unless block_given?
  results.each { |row| yield Row.from_result_row(row) }
end

#firstHash

Returns the first row in this data set.

Returns:

  • (Hash)

    the first row in this data set

Since:

  • 1.0.0



637
638
639
640
# File 'lib/cequel/metal/data_set.rb', line 637

def first
  row = execute_cql(*limit(1).cql).first
  Row.from_result_row(row)
end

#increment(deltas, options = {}) ⇒ void Also known as: incr

Note:

This can only be used on counter tables

This method returns an undefined value.

Increment one or more counter columns

Examples:

post_analytics.
  where(blog_subdomain: 'cassandra', permalink: 'cequel').
  increment(pageviews: 10, tweets: 2)

Parameters:

  • deltas (Hash<Symbol,Integer>)

    map of counter column names to amount by which to increment each column

See Also:

Since:

  • 0.5.0



158
159
160
# File 'lib/cequel/metal/data_set.rb', line 158

def increment(deltas, options = {})
  incrementer { increment(deltas) }.execute(options)
end

#insert(data, options = {}) ⇒ void

Note:

‘INSERT` statements will succeed even if a row at the specified primary key already exists. In this case, column values specified in the insert will overwrite the existing row.

Note:

If a enclosed in a Keyspace#batch block, this method will be executed as part of the batch.

This method returns an undefined value.

Insert a row into the column family.

Parameters:

  • data (Hash)

    column-value pairs

  • options (Options) (defaults to: {})

    options for persisting the row

See Also:

Since:

  • 1.0.0



86
87
88
# File 'lib/cequel/metal/data_set.rb', line 86

def insert(data, options = {})
  inserter { insert(data) }.execute(options)
end

#inspectString

Returns:

  • (String)

Since:

  • 1.0.0



666
667
668
# File 'lib/cequel/metal/data_set.rb', line 666

def inspect
  "#<#{self.class.name}: #{cql.inspect}>"
end

#last_page?Boolean

Returns whether no more pages are available

Returns:

  • (Boolean)

    Returns whether no more pages are available

See Also:

Since:

  • 1.0.0



610
611
612
# File 'lib/cequel/metal/data_set.rb', line 610

def last_page?
  results.last_page?
end

#limit(limit) ⇒ DataSet

Limit the number of rows returned by this data set

Parameters:

  • limit (Integer)

    maximum number of rows to return

Returns:

  • (DataSet)

    new data set scoped with given limit

Since:

  • 1.0.0



537
538
539
# File 'lib/cequel/metal/data_set.rb', line 537

def limit(limit)
  clone.tap { |data_set| data_set.row_limit = limit }
end

#list_append(column, elements, options = {}) ⇒ void

Note:

If a enclosed in a Keyspace#batch block, this method will be executed as part of the batch.

This method returns an undefined value.

Append element(s) to a list in the row(s) matched by this data set.

Examples:

posts.list_append(:categories, ['CQL', 'ORMs'])

Parameters:

  • column (Symbol)

    name of list column to append to

  • elements (Object, Array)

    one element or an array of elements to append

  • options (Options) (defaults to: {})

    options for persisting the column data

See Also:

Since:

  • 1.0.0



223
224
225
# File 'lib/cequel/metal/data_set.rb', line 223

def list_append(column, elements, options = {})
  updater { list_append(column, elements) }.execute(options)
end

#list_prepend(column, elements, options = {}) ⇒ void

Note:

A bug (CASSANDRA-8733) exists in Cassandra versions 0.3.0-2.0.12 and 2.1.0-2.1.2 which will make elements appear in REVERSE ORDER in the list.

Note:

If a enclosed in a Keyspace#batch block, this method will be executed as part of the batch.

This method returns an undefined value.

Prepend element(s) to a list in the row(s) matched by this data set.

Examples:

posts.list_prepend(:categories, ['CQL', 'ORMs'])

Parameters:

  • column (Symbol)

    name of list column to prepend to

  • elements (Object, Array)

    one element or an array of elements to prepend

  • options (Options) (defaults to: {})

    options for persisting the column data

See Also:

Since:

  • 1.0.0



200
201
202
# File 'lib/cequel/metal/data_set.rb', line 200

def list_prepend(column, elements, options = {})
  updater { list_prepend(column, elements) }.execute(options)
end

#list_remove(column, value, options = {}) ⇒ void

Note:

If enclosed in a Keyspace#batch block, this method will be executed as part of the batch.

This method returns an undefined value.

Remove all occurrences of a given value from a list column

Examples:

posts.list_remove(:categories, 'CQL3')

Parameters:

  • column (Symbol)

    name of list column

  • value (Object)

    value to remove

  • options (Options) (defaults to: {})

    options for persisting the data

See Also:

Since:

  • 1.0.0



267
268
269
# File 'lib/cequel/metal/data_set.rb', line 267

def list_remove(column, value, options = {})
  updater { list_remove(column, value) }.execute(options)
end

#list_remove_at(column, *positions, options = {}) ⇒ void

Note:

If enclosed in a Keyspace#batch block, this method will be executed as part of the batch.

This method returns an undefined value.

Remove the value from a given position or positions in a list column

Examples:

posts.list_remove_at(:categories, 2)

Parameters:

  • column (Symbol)

    name of list column

  • positions (Integer)

    position(s) in list to remove value from

  • options (Options) (defaults to: {})

    options for persisting the data

See Also:

Since:

  • 1.0.0

Since:

  • 1.0.0



290
291
292
293
294
295
# File 'lib/cequel/metal/data_set.rb', line 290

def list_remove_at(column, *positions)
  options = positions.extract_options!
  sorted_positions = positions.sort.reverse

  deleter { list_remove_at(column, *sorted_positions) }.execute(options)
end

#list_replace(column, index, value, options = {}) ⇒ void

Note:

if a enclosed in a Keyspace#batch block, this method will be executed as part of the batch.

This method returns an undefined value.

Replace a list element at a specified index with a new value

Examples:

posts.list_replace(:categories, 2, 'Object-Relational Mapper')

Parameters:

  • column (Symbol)

    name of list column

  • index (Integer)

    which element to replace

  • value (Object)

    new value at this index

  • options (Options) (defaults to: {})

    options for persisting the data

See Also:

Since:

  • 1.0.0



245
246
247
# File 'lib/cequel/metal/data_set.rb', line 245

def list_replace(column, index, value, options = {})
  updater { list_replace(column, index, value) }.execute(options)
end

#map_remove(column, *keys, options = {}) ⇒ void

Note:

If enclosed in a Keyspace#batch block, this method will be executed as part of the batch.

This method returns an undefined value.

Remove a given key from a map column

Examples:

posts.map_remove(:credits, 'editor')

Parameters:

  • column (Symbol)

    name of map column

  • keys (Object)

    map key to remove

  • options (Options) (defaults to: {})

    options for persisting the data

See Also:

Since:

  • 1.0.0

Since:

  • 1.0.0



315
316
317
318
# File 'lib/cequel/metal/data_set.rb', line 315

def map_remove(column, *keys)
  options = keys.extract_options!
  deleter { map_remove(column, *keys) }.execute(options)
end

#map_update(column, updates, options = {}) ⇒ void

Note:

If enclosed in a Keyspace#batch block, this method will be executed as part of the batch.

This method returns an undefined value.

Update one or more keys in a map column

Examples:

posts.map_update(:credits, 'editor' => 34)

Parameters:

  • column (Symbol)

    name of set column

  • updates (Hash)

    map of map keys to new values

  • options (Options) (defaults to: {})

    options for persisting the data

See Also:

Since:

  • 1.0.0



379
380
381
# File 'lib/cequel/metal/data_set.rb', line 379

def map_update(column, updates, options = {})
  updater { map_update(column, updates) }.execute(options)
end

#next_paging_stateString

Exposes current paging state for stateless pagination



601
602
603
# File 'lib/cequel/metal/data_set.rb', line 601

def next_paging_state
  results.paging_state
end

#order(pairs) ⇒ DataSet

Note:

The only valid ordering column is the first clustering column

Control how the result rows are sorted

Parameters:

  • pairs (Hash)

    Map of column name to sort direction

Returns:

  • (DataSet)

    new data set with the specified ordering

Since:

  • 1.0.0



550
551
552
553
554
# File 'lib/cequel/metal/data_set.rb', line 550

def order(pairs)
  clone.tap do |data_set|
    data_set.sort_order.merge!(pairs.symbolize_keys)
  end
end

#page_size(page_size) ⇒ Object

Since:

  • 1.0.0



573
574
575
576
577
# File 'lib/cequel/metal/data_set.rb', line 573

def page_size(page_size)
  clone.tap do |data_set|
    data_set.query_page_size = page_size
  end
end

#paging_state(paging_state) ⇒ Object

Since:

  • 1.0.0



588
589
590
591
592
# File 'lib/cequel/metal/data_set.rb', line 588

def paging_state(paging_state)
  clone.tap do |data_set|
    data_set.query_paging_state = paging_state
  end
end

#row_specifications_cqlObject

Since:

  • 1.0.0



678
679
680
681
682
683
684
685
686
687
688
689
# File 'lib/cequel/metal/data_set.rb', line 678

def row_specifications_cql
  if row_specifications.any?
    cql_fragments, bind_vars = [], []
    row_specifications.each do |spec|
      cql_with_vars = spec.cql
      cql_fragments << cql_with_vars.shift
      bind_vars.concat(cql_with_vars)
    end
    [" WHERE #{cql_fragments.join(' AND ')}", *bind_vars]
  else ['']
  end
end

#select(*columns) ⇒ DataSet

Select specified columns from this data set.

Parameters:

  • columns (Symbol)

    columns columns to select

Returns:

  • (DataSet)

    new data set scoped to specified columns

Since:

  • 1.0.0



446
447
448
449
450
# File 'lib/cequel/metal/data_set.rb', line 446

def select(*columns)
  clone.tap do |data_set|
    data_set.select_columns.concat(columns.flatten)
  end
end

#select!(*columns) ⇒ DataSet

Select specified columns from this data set, overriding chained scope.

Parameters:

  • columns (Symbol, Array)

    columns to select

Returns:

  • (DataSet)

    new data set scoped to specified columns

Since:

  • 1.0.0



487
488
489
490
491
# File 'lib/cequel/metal/data_set.rb', line 487

def select!(*columns)
  clone.tap do |data_set|
    data_set.select_columns.replace(columns.flatten)
  end
end

#select_ttl(*columns) ⇒ DataSet

Return the remaining TTL for the specified columns from this data set.

Parameters:

  • columns (Symbol)

    columns to select

Returns:

  • (DataSet)

    new data set scoped to specified columns

Since:

  • 1.0.0



460
461
462
463
464
# File 'lib/cequel/metal/data_set.rb', line 460

def select_ttl(*columns)
  clone.tap do |data_set|
    data_set.ttl_columns.concat(columns.flatten)
  end
end

#select_writetime(*columns) ⇒ DataSet Also known as: select_timestamp

Return the write time for the specified columns in the data set

Parameters:

  • columns (Symbol)

    columns to select

Returns:

  • (DataSet)

    new data set scoped to specified columns

Since:

  • 1.0.0



474
475
476
477
478
# File 'lib/cequel/metal/data_set.rb', line 474

def select_writetime(*columns)
  clone.tap do |data_set|
    data_set.writetime_columns.concat(columns.flatten)
  end
end

#set_add(column, values, options = {}) ⇒ void

Note:

If enclosed in a Keyspace#batch block, this method will be executed as part of the batch.

This method returns an undefined value.

Add one or more elements to a set column

Examples:

posts.set_add(:tags, 'cql3')

Parameters:

  • column (Symbol)

    name of set column

  • values (Object, Set)

    value or values to add

  • options (Options) (defaults to: {})

    options for persisting the data

See Also:

Since:

  • 1.0.0



337
338
339
# File 'lib/cequel/metal/data_set.rb', line 337

def set_add(column, values, options = {})
  updater { set_add(column, values) }.execute(options)
end

#set_remove(column, value, options = {}) ⇒ void

Note:

If enclosed in a Keyspace#batch block, this method will be executed as part of the batch.

This method returns an undefined value.

Remove an element from a set

Examples:

posts.set_remove(:tags, 'cql3')

Parameters:

  • column (Symbol)

    name of set column

  • value (Object)

    value to remove

  • options (Options) (defaults to: {})

    options for persisting the data

See Also:

Since:

  • 1.0.0



358
359
360
# File 'lib/cequel/metal/data_set.rb', line 358

def set_remove(column, value, options = {})
  updater { set_remove(column, value) }.execute(options)
end

#update(column_values, options = {}) ⇒ void #update(options = {}) { ... } ⇒ void

Note:

‘UPDATE` statements will succeed even if targeting a row that does not exist. In this case a new row will be created.

Note:

This statement will fail unless one or more rows are fully specified by primary key using ‘where`

Note:

If a enclosed in a Keyspace#batch block, this method will be executed as part of the batch.

This method returns an undefined value.

Upsert data into one or more rows

Overloads:

  • #update(column_values, options = {}) ⇒ void

    Update the rows specified in the data set with new values

    Examples:

    posts.where(blog_subdomain: 'cassandra', permalink: 'cequel').
      update(title: 'Announcing Cequel 1.0')

    Parameters:

    • column_values (Hash)

      map of column names to new values

    • options (Options) (defaults to: {})

      options for persisting the column data

  • #update(options = {}) { ... } ⇒ void

    Construct an update statement consisting of multiple operations

    Examples:

    posts.where(blog_subdomain: 'bigdata', permalink: 'cql').update do
      set(title: 'Announcing Cequel 1.0')
      list_append(categories: 'ORMs')
    end

    Parameters:

    • options (Options) (defaults to: {})

      options for persisting the data

    Yields:

    • DSL context for adding write operations

    See Also:

    Since:

    • 1.0.0

See Also:

Since:

  • 1.0.0



131
132
133
134
135
136
137
138
# File 'lib/cequel/metal/data_set.rb', line 131

def update(*args, &block)
  if block
    updater(&block).execute(args.extract_options!)
  else
    data = args.shift
    updater { set(data) }.execute(args.extract_options!)
  end
end

#where(column_values) ⇒ DataSet #where(cql, *bind_vars) ⇒ DataSet

Filter this data set with a row specification

Overloads:

  • #where(column_values) ⇒ DataSet

    Examples:

    database[:posts].where(title: 'Hey')

    Parameters:

    • column_values (Hash)

      Map of column name to values to match

  • #where(cql, *bind_vars) ⇒ DataSet

    Examples:

    DB[:posts].where('title = ?', 'Hey')

    Parameters:

    • cql (String)

      CQL fragment representing ‘WHERE` statement

    • bind_vars (Object)

      Bind variables for the CQL fragment

Returns:

  • (DataSet)

    New data set scoped to the row specification

Since:

  • 1.0.0



511
512
513
514
515
516
# File 'lib/cequel/metal/data_set.rb', line 511

def where(row_specification, *bind_vars)
  clone.tap do |data_set|
    data_set.row_specifications
      .concat(build_row_specifications(row_specification, bind_vars))
  end
end

#where!(row_specification, *bind_vars) ⇒ DataSet

Replace existing row specifications

Returns:

  • (DataSet)

    New data set with only row specifications given

See Also:

Since:

  • 1.0.0



524
525
526
527
528
529
# File 'lib/cequel/metal/data_set.rb', line 524

def where!(row_specification, *bind_vars)
  clone.tap do |data_set|
    data_set.row_specifications
      .replace(build_row_specifications(row_specification, bind_vars))
  end
end