skaidb 1.3.0 → 1.4.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: a452610f682221a59cfbbc48e42c9a37f1c0e880d2191037f1c9c7151a3034fa
4
- data.tar.gz: 1a73f7f552091bfaed9b4072dfd684b57daac637ff12d163a34bf1c278bef784
3
+ metadata.gz: f947092b55d66cc8d7c55153cbea09ebcdd0298f48fcb6c97ea0f3a5edd5ac01
4
+ data.tar.gz: 9b472d16803f161f2aa6d4554c19aede65f9a227b79d29c7f015204c04a7bcc7
5
5
  SHA512:
6
- metadata.gz: ce44a1e28e0add382aa9fde578eb09350e1b5306d22c3cf5d272faa5d606c8ec05be08b77967d014a33ceddba5fefd0ed449503c24d4997680c37af156ad2e5f
7
- data.tar.gz: 3d34175d862982988e593fb42c631bdbb9cd6f23682908ec40fc53a7d25fe3868105201dd50988108508783bfb8bf076065737864d71c6cc5337c8b6c371e6c3
6
+ metadata.gz: 68d82f58f409113c18f1536023042343ffbfa86c8ec6029a0f799cd8f88fd038f418ced9c1be77beca7826c7d5756e523de221c6bf798e7069db1e14be7aaf12
7
+ data.tar.gz: f95915e0918b17e31d2b9e63cfbcbe92ee717f07b49075bbd1da055cb49f60f592c346eb579b6c65a8dc855ddf669c41d467086dec90ccb59abc0861c784dacc
data/CHANGELOG.md CHANGED
@@ -4,6 +4,20 @@ All notable changes to the skaidb Ruby driver. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
5
5
  [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [1.4.0] - 2026-10-29
8
+
9
+ ### Fixed
10
+ - A streamed `CALL` whose procedure body `EMIT`s result sets returns them.
11
+ The server answers such a call with every set in one `ResultSets` frame
12
+ (tag 8) even to a stream request, and `conn.stream`, with or without
13
+ parameters, raised `QueryError` (`unexpected response tag 8 to stream
14
+ request`) and lost the sets. The frame is now decoded by the code `exec`
15
+ uses: the block receives the rows `exec`'s `Result` would yield (the
16
+ call's final result, the LAST set) and `stream` returns that same `:rows`
17
+ `Result`, every set in order under `result_sets`. Nothing is left in
18
+ flight, so the connection stays usable — the block may even run
19
+ statements on it.
20
+
7
21
  ## [1.3.0] - 2026-10-29
8
22
 
9
23
  ### Added
data/README.md CHANGED
@@ -55,13 +55,13 @@ Xcode command-line tools on macOS). `gem install skaidb` and vendoring reuse
55
55
  the `bigdecimal` already installed.
56
56
 
57
57
  Or vendor the single file: copy
58
- [`lib/skaidb.rb`](https://github.com/porcupin26/skaidb-ruby/blob/v1.3.0/lib/skaidb.rb)
58
+ [`lib/skaidb.rb`](https://github.com/porcupin26/skaidb-ruby/blob/v1.4.0/lib/skaidb.rb)
59
59
  into your project and `require_relative` it. It has no dependencies beyond
60
60
  Ruby's standard library.
61
61
 
62
62
  Every release is also attached as a `.gem` to its
63
63
  [GitHub release](https://github.com/porcupin26/skaidb-ruby/releases) for
64
- installs without registry access: `gem install ./skaidb-1.3.0.gem`.
64
+ installs without registry access: `gem install ./skaidb-1.4.0.gem`.
65
65
 
66
66
  ## Quick start
67
67
 
@@ -306,7 +306,10 @@ unordered or in storage-key order), and no scan budget applies to a streamed
306
306
  page (server ≥ 0.330). With a block `stream` returns a `Result` whose `kind`
307
307
  is `:stream` and whose `fields` are the column names (the rows went to the
308
308
  block); a non-row statement returns its `:mutation` / `:ddl` Result without
309
- yielding.
309
+ yielding. A `CALL` whose body `EMIT`s result sets is answered with all of
310
+ them in one frame even when streamed: the block receives the rows `exec`
311
+ would return (the last set), and `stream` returns that same `Result`, every
312
+ set under `result_sets`.
310
313
 
311
314
  **The abandon/drain rule.** The connection is busy for the whole stream:
312
315
  a statement from another thread waits for it. Leaving the block early —
@@ -458,7 +461,7 @@ frame after every handshake. It shows up as `client_name = 'ruby'` /
458
461
 
459
462
  ```sql
460
463
  SELECT client_name, client_version FROM drivers;
461
- -- ruby | 1.3.0
464
+ -- ruby | 1.4.0
462
465
  ```
463
466
 
464
467
  The server records the row asynchronously, so a `SELECT` immediately after
data/docs/api.md CHANGED
@@ -67,7 +67,10 @@ Raises `Skaidb::Error` before dialing for an unknown `auth_mechanism`, a
67
67
  `ArgumentError`. For a row stream the returned Result has
68
68
  `kind == :stream`, the column names in `fields` and no `rows` (they went to
69
69
  the block); a statement without rows returns its `:mutation` / `:ddl`
70
- Result without yielding. Without a block returns an Enumerator.
70
+ Result without yielding; a `CALL` whose body `EMIT`s result sets (answered
71
+ in one frame even when streamed) yields the rows `exec` would return (the
72
+ last set) and returns that same `:rows` Result, every set under
73
+ `result_sets`. Without a block returns an Enumerator.
71
74
  - `subscribe(stream_name, after: nil, poll: 0.5) { |event| … }` — yields a
72
75
  `CREATE STREAM` log's events forever, each a Hash with `"id"`, `"op"`,
73
76
  `"k"`, `"ts"`, `"doc"`. Polls `_stream_<name>` 500 rows at a time with a
data/docs/streaming.md CHANGED
@@ -28,6 +28,12 @@ end
28
28
  - A non-row statement (an `INSERT`, DDL, `USE`) answers with its ordinary
29
29
  result over the same opcode; `stream` returns that Result (`kind`
30
30
  `:mutation` with `cmd_tuples`, or `:ddl`) without yielding.
31
+ - A `CALL` whose procedure body `EMIT`s result sets answers with all of them
32
+ in one frame, as it does to `exec`: the block receives the rows `exec`
33
+ would return (the call's final result, the last set) and `stream` returns
34
+ that same `:rows` Result, every set in order under `result_sets`. Nothing
35
+ is left in flight, so the connection is free again before the first row
36
+ reaches the block.
31
37
  - A server without the streaming opcode raises `Skaidb::QueryError`
32
38
  (`server does not support streaming: …`).
33
39
 
data/lib/skaidb.rb CHANGED
@@ -35,7 +35,7 @@ require "bigdecimal"
35
35
  module Skaidb
36
36
  # The package version — the single source of truth. The gemspec reads it,
37
37
  # and it is what the driver reports to the server in the Hello frame.
38
- VERSION = "1.3.0"
38
+ VERSION = "1.4.0"
39
39
 
40
40
  # Base class for every error raised by this driver.
41
41
  class Error < StandardError; end
@@ -973,7 +973,12 @@ module Skaidb
973
973
  # +kind+ is +:stream+ and +fields+ holds the column names (known even when
974
974
  # no row arrived) while +rows+ stays empty — the rows went to the block;
975
975
  # a statement that produces no rows returns its ordinary +:mutation+ or
976
- # +:ddl+ Result without yielding.
976
+ # +:ddl+ Result without yielding. A CALL whose body EMITs result sets is
977
+ # answered with all of them in one frame, streamed or not: the block
978
+ # receives the rows exec's Result would yield (the call's final result,
979
+ # the LAST set) and the return value is that same +:rows+ Result, every
980
+ # set under +result_sets+ — with nothing left in flight, so the block may
981
+ # use the connection.
977
982
  def stream(sql, params = [], consistency: nil)
978
983
  return enum_for(:stream, sql, params, consistency: consistency) unless block_given?
979
984
  raise ConnectionError, "connection is closed" if @closed
@@ -991,6 +996,7 @@ module Skaidb
991
996
 
992
997
  live = false
993
998
  summary = nil
999
+ sets = nil
994
1000
  @mutex.synchronize do
995
1001
  started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
996
1002
  begin
@@ -1015,6 +1021,12 @@ module Skaidb
1015
1021
  return Result.new(fields: [], rows: [], cmd_tuples: r.u64, kind: :mutation) # not row-producing
1016
1022
  when 2
1017
1023
  return Result.new(fields: [], rows: [], cmd_tuples: 0, kind: :ddl)
1024
+ when 8
1025
+ # ResultSets: a CALL whose body EMITted is answered with every set
1026
+ # in ONE frame, streamed or not — the frame exec receives, decoded
1027
+ # by the same code. Nothing follows it, so the rows are yielded
1028
+ # below, with the connection free.
1029
+ sets = parse_result_sets(r)
1018
1030
  when 5
1019
1031
  # The server has already committed to a row stream, so from here
1020
1032
  # the socket carries frames this call owns — mark it BEFORE
@@ -1065,6 +1077,12 @@ module Skaidb
1065
1077
  raise QueryError, "unexpected response tag #{tag} to stream request"
1066
1078
  end
1067
1079
  end
1080
+ if sets
1081
+ # What exec's Result yields (the call's final result, the LAST set),
1082
+ # and that same Result back, every set under +result_sets+.
1083
+ sets.each { |row| yield row }
1084
+ return sets
1085
+ end
1068
1086
  summary
1069
1087
  end
1070
1088
 
@@ -1388,27 +1406,9 @@ module Skaidb
1388
1406
  tag = r.u8
1389
1407
  case tag
1390
1408
  when 0 # Rows
1391
- ncols = r.u32
1392
- columns = Array.new(ncols) { r.text }
1393
- nrows = r.u32
1394
- rows = Array.new(nrows) do
1395
- ncells = r.u32
1396
- Array.new(ncells) { Skaidb.decode_value(Reader.new(r.blob)) }
1397
- end
1398
- Result.new(fields: columns, rows: rows, cmd_tuples: 0)
1409
+ parse_rows(r)
1399
1410
  when 8 # ResultSets: a CALL whose body EMITted
1400
- sets = Array.new(r.u32) do
1401
- ncols = r.u32
1402
- columns = Array.new(ncols) { r.text }
1403
- nrows = r.u32
1404
- rows = Array.new(nrows) do
1405
- ncells = r.u32
1406
- Array.new(ncells) { Skaidb.decode_value(Reader.new(r.blob)) }
1407
- end
1408
- Result.new(fields: columns, rows: rows, cmd_tuples: 0)
1409
- end
1410
- last = sets.last || Result.new(fields: [], rows: [], cmd_tuples: 0)
1411
- Result.new(fields: last.fields, rows: last.rows, cmd_tuples: 0, result_sets: sets)
1411
+ parse_result_sets(r)
1412
1412
  when 1 # Mutation
1413
1413
  Result.new(fields: [], rows: [], cmd_tuples: r.u64, kind: :mutation)
1414
1414
  when 2 # Ddl
@@ -1419,6 +1419,29 @@ module Skaidb
1419
1419
  raise ConnectionError, "unknown response tag #{tag}"
1420
1420
  end
1421
1421
  end
1422
+
1423
+ # A Rows body (PROTOCOL.md §3.2), also one set of ResultSets; +r+ sits
1424
+ # just past the tag.
1425
+ def parse_rows(r)
1426
+ ncols = r.u32
1427
+ columns = Array.new(ncols) { r.text }
1428
+ nrows = r.u32
1429
+ rows = Array.new(nrows) do
1430
+ ncells = r.u32
1431
+ Array.new(ncells) { Skaidb.decode_value(Reader.new(r.blob)) }
1432
+ end
1433
+ Result.new(fields: columns, rows: rows, cmd_tuples: 0)
1434
+ end
1435
+
1436
+ # A ResultSets body (a CALL whose body EMITted), +r+ just past the tag:
1437
+ # the LAST set is the Result's own rows, every set is under
1438
+ # +result_sets+. The buffered path and a stream request both receive
1439
+ # this frame, and both decode it here.
1440
+ def parse_result_sets(r)
1441
+ sets = Array.new(r.u32) { parse_rows(r) }
1442
+ last = sets.last || Result.new(fields: [], rows: [], cmd_tuples: 0)
1443
+ Result.new(fields: last.fields, rows: last.rows, cmd_tuples: 0, result_sets: sets)
1444
+ end
1422
1445
  end
1423
1446
 
1424
1447
  # ---- module entry point --------------------------------------------------
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: skaidb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - skaidb contributors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-29 00:00:00.000000000 Z
11
+ date: 2026-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bigdecimal