Class: Sequel::Dataset

Inherits:
Object
  • Object
show all
Defined in:
lib/sequel_opal_runtime_patches.rb,
lib/sequel_opal_async_dataset_patches.rb

Direct Known Subclasses

Sequel::D1::Dataset

Instance Method Summary collapse

Instance Method Details

#__homura_orig__insert_values_sqlObject



133
# File 'lib/sequel_opal_runtime_patches.rb', line 133

alias_method :__homura_orig__insert_values_sql, :_insert_values_sql

#__homura_orig_literal_appendObject

upstream literal_append Symbol branch:

l = String.new
literal_symbol_append(l, v)
db.literal_symbol_set(v, l)

homura: use Buffer for the append accumulator; cache the rendered String form on the db so subsequent calls hit the cache without re-creating a Buffer.



88
# File 'lib/sequel_opal_runtime_patches.rb', line 88

alias_method :__homura_orig_literal_append, :literal_append

#__homura_orig_update_sql_values_hashObject



127
# File 'lib/sequel_opal_runtime_patches.rb', line 127

alias_method :__homura_orig_update_sql_values_hash, :update_sql_values_hash

#_all(block) ⇒ Object

upstream: a = []; yield a; post_load(a); a.each(&block) if block; a homura: yield(a) returns a Promise when the caller block crosses the D1 adapter’s async boundary (Dataset#all’s block is async-compiled). Await so ‘a` is populated before post_load.



51
52
53
54
55
56
57
# File 'lib/sequel_opal_async_dataset_patches.rb', line 51

def _all(block)
  a = []
  yield(a).__await__
  post_load(a)
  a.each(&block) if block
  a
end

#_insert_values_sql(sql, values) ⇒ Object



134
135
136
137
138
139
# File 'lib/sequel_opal_runtime_patches.rb', line 134

def _insert_values_sql(sql, values)
  if values.is_a?(Array)
    values = values.map { |v| homura_sql_value(v) }
  end
  __homura_orig__insert_values_sql(sql, values)
end

#eachObject

upstream each:

if rp = row_proc; fetch_rows(select_sql){|r| yield rp.call(r)}
else              fetch_rows(select_sql){|r| yield r}
end; self

homura: fetch_rows is async (D1 adapter awaits the Promise chain). If we leave each sync, the block passed to fetch_rows never runs before each returns. Await fetch_rows to let the inner block fire first.



38
39
40
41
42
43
44
45
# File 'lib/sequel_opal_async_dataset_patches.rb', line 38

def each
  if rp = row_proc
    fetch_rows(select_sql){|r| yield rp.call(r)}.__await__
  else
    fetch_rows(select_sql){|r| yield r}.__await__
  end
  self
end

#get_first_row(*conds, &block) ⇒ Object

Sqlite3-ruby / homura-runtime D1Database compatibility shim. Mirrors the ‘db.get_first_row(sql, [params])` shape exposed by `Cloudflare::D1Database` so code that mixes the two layers (or follows that documentation style) works on `Sequel::Dataset` too.

db[:users].where(id: 1).get_first_row    # → Hash or nil
db[:users].get_first_row('id = ?', 1)    # accepts the same
                                           # params as #where.

Implemented as a thin wrapper over ‘Dataset#first`, which already crosses the async boundary correctly via the patches in this file.



132
133
134
# File 'lib/sequel_opal_async_dataset_patches.rb', line 132

def get_first_row(*conds, &block)
  conds.empty? && block.nil? ? first : first(*conds, &block)
end

#literal_append(sql, v) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/sequel_opal_runtime_patches.rb', line 89

def literal_append(sql, v)
  # IMPORTANT: in Opal, `Symbol` is the same constant as `String`
  # (`Symbol = String`), so `String#<` subclasses such as
  # `Sequel::LiteralString` and `Sequel::SQL::Blob` ALSO satisfy
  # `is_a?(Symbol)`. Upstream Sequel's `case v when Symbol` therefore
  # captures these subclasses and routes them through
  # `literal_symbol_append`, which interprets the value as a column
  # identifier (`Sequel.lit('1 - done')` becomes `` `1 - done` ``).
  # This was the root cause of homura issue #31:
  #
  #   db[:todos].where(id: 1).update(done: Sequel.lit('1 - done'))
  #   # SQLite error: no such column: 1 - done
  #
  # We branch on the Sequel SQL types FIRST, before Symbol, so they
  # take the literal path that upstream Ruby would hit naturally
  # (where `Symbol` and `String` are distinct classes).
  if v.is_a?(::HomuraSqlStringLiteral)
    v.sql_literal_append(self, sql)
  elsif v.is_a?(::Sequel::LiteralString)
    literal_literal_string_append(sql, v)
  elsif defined?(::Sequel::SQL::Blob) && v.is_a?(::Sequel::SQL::Blob)
    literal_blob_append(sql, v)
  elsif v.is_a?(Symbol)
    if skip_symbol_cache?
      literal_symbol_append(sql, v)
    else
      unless l = db.literal_symbol(v)
        l = sql_string_origin
        literal_symbol_append(l, v)
        db.literal_symbol_set(v, l.to_s)
      end
      sql << l
    end
  else
    __homura_orig_literal_append(sql, v)
  end
end

#single_valueObject

upstream: single_value_ds.each{|r| r.each{|_, v| return v}}; nil homura: capture first value using sentinel flag. Relies on each awaiting the D1 Promise chain.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/sequel_opal_async_dataset_patches.rb', line 91

def single_value
  value = nil
  found = false
  # each is async (patched in this file); must await.
  (single_value_ds.each do |r|
    next if found
    r.each do |_, v|
      next if found
      value = v
      found = true
    end
  end).__await__
  found ? value : nil
end

#sql_string_originObject



66
67
68
# File 'lib/sequel_opal_runtime_patches.rb', line 66

def sql_string_origin
  ::HomuraSqlBuffer.new
end

#update_sql_values_hash(sql, values) ⇒ Object



128
129
130
131
# File 'lib/sequel_opal_runtime_patches.rb', line 128

def update_sql_values_hash(sql, values)
  values = values.each_with_object({}) { |(k, v), acc| acc[k] = homura_sql_value(v) }
  __homura_orig_update_sql_values_hash(sql, values)
end

#with_sql_each(sql) ⇒ Object

upstream: with_sql_each(sql){|r| yield r}; self (sync, but fetch_rows returns a Promise under the D1 adapter, so the block body doesn’t run before the caller’s sync flow resumes — the Promise is dropped). homura: reopen with ‘.__await__` so the async fetch_rows resolves before this method returns. Required for #first / #with_sql_first / #with_sql_single_value which expect the block to have fired.



65
66
67
68
69
70
71
72
# File 'lib/sequel_opal_async_dataset_patches.rb', line 65

def with_sql_each(sql)
  if rp = row_proc
    _with_sql_dataset.fetch_rows(sql){|r| yield rp.call(r)}.__await__
  else
    _with_sql_dataset.fetch_rows(sql){|r| yield r}.__await__
  end
  self
end

#with_sql_first(sql) ⇒ Object

upstream: with_sql_each(sql){|r| return r}; nil homura: capture first row without ‘return`; callers always clone with limit(1) so the no-break form is equivalent. Needs with_sql_each to actually await its fetch_rows (see above).



78
79
80
81
82
83
84
85
86
# File 'lib/sequel_opal_async_dataset_patches.rb', line 78

def with_sql_first(sql)
  result = nil
  # with_sql_each is async now (awaits fetch_rows internally); we must
  # await here so `result` is populated before returning.
  (with_sql_each(sql) do |r|
    result = r if result.nil?
  end).__await__
  result
end

#with_sql_single_value(sql) ⇒ Object

upstream: if r = with_sql_first(sql); r.each{|_, v| return v}; end homura: same capture-first-value pattern via sentinel.



108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/sequel_opal_async_dataset_patches.rb', line 108

def with_sql_single_value(sql)
  if r = with_sql_first(sql)
    value = nil
    captured = false
    r.each do |_, v|
      next if captured
      value = v
      captured = true
    end
    value
  end
end