Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package rqlite for openSUSE:Factory checked in at 2025-12-08 11:53:25 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rqlite (Old) and /work/SRC/openSUSE:Factory/.rqlite.new.1939 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rqlite" Mon Dec 8 11:53:25 2025 rev:34 rq:1321304 version:9.3.4 Changes: -------- --- /work/SRC/openSUSE:Factory/rqlite/rqlite.changes 2025-11-28 16:54:36.314512300 +0100 +++ /work/SRC/openSUSE:Factory/.rqlite.new.1939/rqlite.changes 2025-12-08 11:54:16.674301030 +0100 @@ -1,0 +2,7 @@ +Fri Dec 5 18:52:35 UTC 2025 - Andreas Stieger <[email protected]> + +- Update to version 9.3.4: + * Address issues flagged by static analysis + * Upgrade SQL parser + +------------------------------------------------------------------- Old: ---- rqlite-9.3.2.tar.xz New: ---- rqlite-9.3.4.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rqlite.spec ++++++ --- /var/tmp/diff_new_pack.kL8mNP/_old 2025-12-08 11:54:18.962396889 +0100 +++ /var/tmp/diff_new_pack.kL8mNP/_new 2025-12-08 11:54:18.982397727 +0100 @@ -16,7 +16,7 @@ # Name: rqlite -Version: 9.3.2 +Version: 9.3.4 Release: 0 Summary: Distributed relational database built on SQLite License: MIT ++++++ _service ++++++ --- /var/tmp/diff_new_pack.kL8mNP/_old 2025-12-08 11:54:19.306411302 +0100 +++ /var/tmp/diff_new_pack.kL8mNP/_new 2025-12-08 11:54:19.358413480 +0100 @@ -3,7 +3,7 @@ <param name="url">https://github.com/rqlite/rqlite.git</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v9.3.2</param> + <param name="revision">v9.3.4</param> <param name="versionformat">@PARENT_TAG@</param> <param name="changesgenerate">enable</param> <param name="versionrewrite-pattern">v(.*)</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.kL8mNP/_old 2025-12-08 11:54:19.602423703 +0100 +++ /var/tmp/diff_new_pack.kL8mNP/_new 2025-12-08 11:54:19.666426384 +0100 @@ -1,7 +1,7 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/rqlite/rqlite.git</param> - <param name="changesrevision">a78909820b8e2da6b47f07d291b363a10bd1f744</param> + <param name="changesrevision">ee3717411422ede142dfc644cff6156fb838ae6f</param> </service> </servicedata> (No newline at EOF) ++++++ rqlite-9.3.2.tar.xz -> rqlite-9.3.4.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/CHANGELOG.md new/rqlite-9.3.4/CHANGELOG.md --- old/rqlite-9.3.2/CHANGELOG.md 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/CHANGELOG.md 2025-12-04 16:07:22.000000000 +0100 @@ -1,3 +1,14 @@ +## v9.3.4 (December 4th 2025) +There are no changes in v9.3.4 relative to v9.3.3. Instead this releases modifies Docker image creation so that rqlite runs as a non-root user. + +### Implementation changes and bug fixes +- [PR #2400](https://github.com/rqlite/rqlite/pull/2400): Run `rqlited` as non-root user within Docker. Fixes issue [#2399](https://github.com/rqlite/rqlite/issues/2399) + +## v9.3.3 (December 3rd 2025) +### Implementation changes and bug fixes +- [PR #2396](https://github.com/rqlite/rqlite/pull/2396): Address issues flagged by static analysis. Thanks @The127 +- [PR #2397](https://github.com/rqlite/rqlite/pull/2397): Upgrade SQL parser. Fixes issue [#2394](https://github.com/rqlite/rqlite/issues/2394) + ## v9.3.2 (November 25th 2025) ### Implementation changes and bug fixes - [PR #2392](https://github.com/rqlite/rqlite/pull/2392): Bump golang.org/x/crypto from 0.44.0 to 0.45.0. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/Dockerfile new/rqlite-9.3.4/Dockerfile --- old/rqlite-9.3.2/Dockerfile 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/Dockerfile 2025-12-04 16:07:22.000000000 +0100 @@ -58,6 +58,11 @@ RUN apk add --no-cache icu-libs +# Create the user and group (Alpine syntax). +# Using 1000 is standard convention for non-root users. +RUN addgroup -g 1000 rqlite && \ + adduser -u 1000 -G rqlite -D rqlite + # Copy the init script and rqlite binaries. COPY --from=builder /app/docker-entrypoint.sh /bin COPY --from=builder /app/rqlited /bin @@ -66,9 +71,17 @@ # Bake in the extensions. COPY --from=builder /extensions /opt/extensions/ -RUN mkdir -p /rqlite/file +# Create the directory and fix ownership permissions. +# This ensures the 'rqlite' user can write to this directory. +RUN mkdir -p /rqlite/file && \ + chown -R rqlite:rqlite /rqlite/file + VOLUME /rqlite/file -EXPOSE 4001 4001 + +EXPOSE 4001 4002 + +# Switch to the non-root user. +USER rqlite ENTRYPOINT ["docker-entrypoint.sh"] CMD ["run"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/auto/restore/downloader.go new/rqlite-9.3.4/auto/restore/downloader.go --- old/rqlite-9.3.2/auto/restore/downloader.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/auto/restore/downloader.go 2025-12-04 16:07:22.000000000 +0100 @@ -106,7 +106,7 @@ if err == nil { stats.Add(numDownloadsOK, 1) if cw != nil { - stats.Add(numDownloadBytes, int64(cw.count)) + stats.Add(numDownloadBytes, cw.count) } } else { stats.Add(numDownloadsFail, 1) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/cluster/service.go new/rqlite-9.3.4/cluster/service.go --- old/rqlite-9.3.2/cluster/service.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/cluster/service.go 2025-12-04 16:07:22.000000000 +0100 @@ -106,7 +106,7 @@ // Backup writes a backup of the database to the writer. Backup(br *command.BackupRequest, dst io.Writer) error - // Loads an entire SQLite file into the database + // Load an entire SQLite file into the database Load(lr *command.LoadRequest) error } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/cluster/service_db_clstr_test.go new/rqlite-9.3.4/cluster/service_db_clstr_test.go --- old/rqlite-9.3.2/cluster/service_db_clstr_test.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/cluster/service_db_clstr_test.go 2025-12-04 16:07:22.000000000 +0100 @@ -501,7 +501,7 @@ dbF := mustNewMockDatabase() mgrF := mustNewMockManager() sF := New(tnF, dbF, mgrF, cred) - if sL == nil { + if sF == nil { t.Fatalf("failed to create cluster service for Follower") } mgrF.joinFn = func(jr *command.JoinRequest) error { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/cmd/rqlite/query.go new/rqlite-9.3.4/cmd/rqlite/query.go --- old/rqlite-9.3.2/cmd/rqlite/query.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/cmd/rqlite/query.go 2025-12-04 16:07:22.000000000 +0100 @@ -102,7 +102,8 @@ // If the error is HostChangedError, it should be propagated back to the caller to handle // accordingly (change prompt display), but we should still assume that the request succeeded on some // host and not treat it as an error. - innerErr, ok := err.(*cl.HostChangedError) + var innerErr *cl.HostChangedError + ok := errors.As(err, &innerErr) if !ok { return err } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/cmd/rqlited/main.go new/rqlite-9.3.4/cmd/rqlited/main.go --- old/rqlite-9.3.2/cmd/rqlited/main.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/cmd/rqlited/main.go 2025-12-04 16:07:22.000000000 +0100 @@ -15,10 +15,10 @@ "syscall" "time" - consul "github.com/rqlite/rqlite-disco-clients/consul" + "github.com/rqlite/rqlite-disco-clients/consul" "github.com/rqlite/rqlite-disco-clients/dns" "github.com/rqlite/rqlite-disco-clients/dnssrv" - etcd "github.com/rqlite/rqlite-disco-clients/etcd" + "github.com/rqlite/rqlite-disco-clients/etcd" "github.com/rqlite/rqlite/v9/auth" "github.com/rqlite/rqlite/v9/auto/backup" "github.com/rqlite/rqlite/v9/auto/restore" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/command/chunking/dechunker.go new/rqlite-9.3.4/command/chunking/dechunker.go --- old/rqlite-9.3.2/command/chunking/dechunker.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/command/chunking/dechunker.go 2025-12-04 16:07:22.000000000 +0100 @@ -124,7 +124,7 @@ } } -// Closes closes the DechunkerManager and all Dechunkers it manages. +// Close closes the DechunkerManager and all Dechunkers it manages. func (d *DechunkerManager) Close() { d.mu.Lock() defer d.mu.Unlock() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/command/sql/processor_test.go new/rqlite-9.3.4/command/sql/processor_test.go --- old/rqlite-9.3.2/command/sql/processor_test.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/command/sql/processor_test.go 2025-12-04 16:07:22.000000000 +0100 @@ -368,6 +368,7 @@ func Test_RETURNING_Some(t *testing.T) { for sql, b := range map[string]bool{ `INSERT INTO "names" VALUES (1, 'bob', '123-45-678') RETURNING *`: true, + `UPDATE t SET d = 'd1' WHERE d = '' RETURNING *`: true, `INSERT INTO "names" VALUES (1, 'bob', 'RETURNING')`: false, `INSERT INTO "names" VALUES (RANDOM(), 'bob', '123-45-678')`: false, `SELECT title FROM albums ORDER BY RANDOM()`: false, @@ -409,6 +410,25 @@ } } +func Test_RETURNING_KeywordAsIdent(t *testing.T) { + for sql, b := range map[string]bool{ + `UPDATE t SET desc = 'd1' WHERE desc = '' RETURNING *`: true, // SQLite keyword as column name + } { + + stmts := []*proto.Statement{ + { + Sql: sql, + }, + } + if err := Process(stmts, false, false); err != nil { + t.Fatalf("failed to not rewrite: %s", err) + } + if exp, got := b, stmts[0].ForceQuery; exp != got { + t.Fatalf(`expected %v for SQL "%s", but got %v`, exp, sql, got) + } + } +} + func Test_Both(t *testing.T) { stmt := &proto.Statement{ Sql: `INSERT INTO "names" VALUES (RANDOM(), 'bob', '123-45-678') RETURNING *`, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/db/swappable_db.go new/rqlite-9.3.4/db/swappable_db.go --- old/rqlite-9.3.2/db/swappable_db.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/db/swappable_db.go 2025-12-04 16:07:22.000000000 +0100 @@ -218,7 +218,7 @@ return s.db.RegisterPreUpdateHook(hook, tblRe, rowIDsOnly) } -// RegisterPreUpdateHook registers a commit hook on the underlying database. +// RegisterCommitHook registers a commit hook on the underlying database. func (s *SwappableDB) RegisterCommitHook(hook CommitHookCallback) error { s.dbMu.RLock() defer s.dbMu.RUnlock() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/go.mod new/rqlite-9.3.4/go.mod --- old/rqlite-9.3.2/go.mod 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/go.mod 2025-12-04 16:07:22.000000000 +0100 @@ -16,7 +16,7 @@ github.com/peterh/liner v1.2.2 github.com/rqlite/raft-boltdb/v2 v2.0.0-20230523104317-c08e70f4de48 github.com/rqlite/rqlite-disco-clients v0.0.0-20250205044118-8ada2b350099 - github.com/rqlite/sql v0.0.0-20251114131613-ef07423e7137 + github.com/rqlite/sql v0.0.0-20251204023435-65660522892e go.etcd.io/bbolt v1.4.3 golang.org/x/net v0.47.0 google.golang.org/protobuf v1.36.10 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/go.sum new/rqlite-9.3.4/go.sum --- old/rqlite-9.3.2/go.sum 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/go.sum 2025-12-04 16:07:22.000000000 +0100 @@ -239,8 +239,8 @@ github.com/rqlite/raft-boltdb/v2 v2.0.0-20230523104317-c08e70f4de48/go.mod h1:CRnsxgy5G8fAf5J+AM0yrsSdxXHKkIYOaq2sm+Q4DYc= github.com/rqlite/rqlite-disco-clients v0.0.0-20250205044118-8ada2b350099 h1:5cqkVLdl6sGJSY3kiF2dqaA3bD+8OS5FUdZqO0BxXLU= github.com/rqlite/rqlite-disco-clients v0.0.0-20250205044118-8ada2b350099/go.mod h1:6SVI8KegsW9Fyu2UQ+uvw0JI5CAILRYRyiQ/OFSJPrs= -github.com/rqlite/sql v0.0.0-20251114131613-ef07423e7137 h1:OG5MfYAA0yaWgllfPdOq9Xa2bo1vpurCNx1LWz7+Zh0= -github.com/rqlite/sql v0.0.0-20251114131613-ef07423e7137/go.mod h1:ib9zVtNgRKiGuoMyUqqL5aNpk+r+++YlyiVIkclVqPg= +github.com/rqlite/sql v0.0.0-20251204023435-65660522892e h1:ccOm5zC6YqJtBrMmtiNcLPjFyWzB+TDY+fDIlQNsIFw= +github.com/rqlite/sql v0.0.0-20251204023435-65660522892e/go.mod h1:ib9zVtNgRKiGuoMyUqqL5aNpk+r+++YlyiVIkclVqPg= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/http/query_params.go new/rqlite-9.3.4/http/query_params.go --- old/rqlite-9.3.2/http/query_params.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/http/query_params.go 2025-12-04 16:07:22.000000000 +0100 @@ -104,7 +104,7 @@ return qp.HasKey("blob_array") } -// NoRewrite returns true if the query parameters request no rewriting of queries. +// NoRewriteRandom returns true if the query parameters request no rewriting of queries. func (qp QueryParams) NoRewriteRandom() bool { return qp.HasKey("norwrandom") } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/http/request_parser_test.go new/rqlite-9.3.4/http/request_parser_test.go --- old/rqlite-9.3.2/http/request_parser_test.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/http/request_parser_test.go 2025-12-04 16:07:22.000000000 +0100 @@ -278,7 +278,7 @@ } func Test_SingleNamedParameterizedRequest(t *testing.T) { - if _, err := strconv.ParseInt(string("1.23"), 10, 64); err == nil { + if _, err := strconv.ParseInt("1.23", 10, 64); err == nil { // Just be sure that strconv.ParseInt fails on float, since // the internal implementation of ParseRequest relies on it. t.Fatal("strconv.ParseInt should fail on float") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/http/service_test.go new/rqlite-9.3.4/http/service_test.go --- old/rqlite-9.3.2/http/service_test.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/http/service_test.go 2025-12-04 16:07:22.000000000 +0100 @@ -1063,7 +1063,7 @@ t.Fatalf("failed to form redirect: %s", err.Error()) } - if exp, got := "http://foo:4001/db/query?x=y", rd; rd != got { + if exp, got := "http://foo:4001/db/query?x=y", rd; exp != got { t.Fatalf("incorrect redirect, exp: %s, got: %s", exp, got) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/internal/progress/progress.go new/rqlite-9.3.4/internal/progress/progress.go --- old/rqlite-9.3.2/internal/progress/progress.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/internal/progress/progress.go 2025-12-04 16:07:22.000000000 +0100 @@ -120,9 +120,9 @@ cm.loggerFn(cm.ctr.Count()) } -func (m *CountingMonitor) StopAndWait() { - m.once.Do(func() { - m.cancel() - <-m.doneCh +func (cm *CountingMonitor) StopAndWait() { + cm.once.Do(func() { + cm.cancel() + <-cm.doneCh }) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/internal/random/random.go new/rqlite-9.3.4/internal/random/random.go --- old/rqlite-9.3.2/internal/random/random.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/internal/random/random.go 2025-12-04 16:07:22.000000000 +0100 @@ -22,7 +22,7 @@ return nums } -// String returns a random string of n characters long. +// StringN returns a random string of n characters long. func StringN(n int) string { var output strings.Builder output.Grow(n) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/internal/rtls/certs_test.go new/rqlite-9.3.4/internal/rtls/certs_test.go --- old/rqlite-9.3.2/internal/rtls/certs_test.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/internal/rtls/certs_test.go 2025-12-04 16:07:22.000000000 +0100 @@ -24,7 +24,7 @@ } key, _ := pem.Decode(keyPEM) - if err != nil { + if key == nil { t.Fatal("failed to decode key") } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/snapshot/store.go new/rqlite-9.3.4/snapshot/store.go --- old/rqlite-9.3.2/snapshot/store.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/snapshot/store.go 2025-12-04 16:07:22.000000000 +0100 @@ -594,17 +594,19 @@ type snapMetaSlice []*raft.SnapshotMeta -// Implement the sort interface for []*fileSnapshotMeta. +// Len implements the sort interface for snapMetaSlice. func (s snapMetaSlice) Len() int { return len(s) } +// Less implements the sort interface for snapMetaSlice. func (s snapMetaSlice) Less(i, j int) bool { si := (*cmpSnapshotMeta)(s[i]) sj := (*cmpSnapshotMeta)(s[j]) return si.Less(sj) } +// Swap implements the sort interface for snapMetaSlice. func (s snapMetaSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/snapshot/upgrader.go new/rqlite-9.3.4/snapshot/upgrader.go --- old/rqlite-9.3.2/snapshot/upgrader.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/snapshot/upgrader.go 2025-12-04 16:07:22.000000000 +0100 @@ -18,7 +18,7 @@ v7StateFile = "state.bin" ) -// Upgrade writes a copy of the 7.x-format Snapshot directory at 'old' to a +// Upgrade7To8 writes a copy of the 7.x-format Snapshot directory at 'old' to a // 8.x-format new Snapshot directory at 'new'. If the upgrade is successful, // the 'old' directory is removed before the function returns. func Upgrade7To8(old, new string, logger *log.Logger) (retErr error) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/store/gzip/decompressor_test.go new/rqlite-9.3.4/store/gzip/decompressor_test.go --- old/rqlite-9.3.2/store/gzip/decompressor_test.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/store/gzip/decompressor_test.go 2025-12-04 16:07:22.000000000 +0100 @@ -14,7 +14,7 @@ testData := []byte("This is a test string, xxxxx -- xxxxxx -- test should compress") var buf bytes.Buffer gzw := gzip.NewWriter(&buf) - gzw.Write([]byte(testData)) + gzw.Write(testData) gzw.Close() // Decompress the data @@ -26,7 +26,7 @@ } // Verify the decompressed data matches original data - if !bytes.Equal(decompressedBuffer.Bytes(), []byte(testData)) { + if !bytes.Equal(decompressedBuffer.Bytes(), testData) { t.Fatalf("decompressed data does not match original") } @@ -84,7 +84,7 @@ t.Fatalf("failed to decompress: %v", err) } - if !bytes.Equal(dstBuf.Bytes(), []byte(testData)) { + if !bytes.Equal(dstBuf.Bytes(), testData) { t.Fatalf("decompressed data does not match original") } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/store/provider.go new/rqlite-9.3.4/store/provider.go --- old/rqlite-9.3.2/store/provider.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/store/provider.go 2025-12-04 16:07:22.000000000 +0100 @@ -38,7 +38,7 @@ return p.str.DBAppliedIndex(), nil } -// Provider writes the SQLite database to the given path. If path exists, +// Provide writes the SQLite database to the given path. If path exists, // it will be overwritten. func (p *Provider) Provide(w io.Writer) (retErr error) { stats.Add(numProviderProvides, 1) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/store/server_test.go new/rqlite-9.3.4/store/server_test.go --- old/rqlite-9.3.2/store/server_test.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/store/server_test.go 2025-12-04 16:07:22.000000000 +0100 @@ -121,7 +121,7 @@ } expectedOrder := []string{"1", "2", "3"} - sort.Sort(Servers(servers)) + sort.Sort(servers) for i, server := range servers { if server.ID != expectedOrder[i] { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/store/store.go new/rqlite-9.3.4/store/store.go --- old/rqlite-9.3.2/store/store.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/store/store.go 2025-12-04 16:07:22.000000000 +0100 @@ -30,7 +30,7 @@ "github.com/rqlite/rqlite/v9/command/proto" sql "github.com/rqlite/rqlite/v9/db" "github.com/rqlite/rqlite/v9/db/humanize" - wal "github.com/rqlite/rqlite/v9/db/wal" + "github.com/rqlite/rqlite/v9/db/wal" "github.com/rqlite/rqlite/v9/internal/progress" "github.com/rqlite/rqlite/v9/internal/random" "github.com/rqlite/rqlite/v9/internal/rsum" @@ -94,7 +94,7 @@ // ErrCDCEnabled is returned when CDC is already enabled. ErrCDCEnabled = errors.New("CDC already enabled") - // ErrInvalidVacuumFormat is returned when the requested backup format is not + // ErrInvalidVacuum is returned when the requested backup format is not // compatible with vacuum. ErrInvalidVacuum = errors.New("invalid vacuum") @@ -678,7 +678,7 @@ if err != nil { return fmt.Errorf("failed to retrieve log store indexes: %s", err) } - s.logger.Printf("raft log is %d bytes at open, indexes are: first %d, last %d", raftDBSize, fi, li) + s.logger.Println(raftLogInfoMessage(raftDBSize, fi, li)) if fi != 0 && li != 0 { err = s.boltStore.GetLog(fi, &raft.Log{}) if err != nil { @@ -996,7 +996,8 @@ if !s.open.Is() { return false } - return s.raft.Leader() != "" + leader, _ := s.raft.LeaderWithID() + return leader != "" } // IsVoter returns true if the current node is a voter in the cluster. If there @@ -1147,7 +1148,7 @@ followers := make([]*Server, 0, len(servers)-1) for i := range servers { - if servers[i].ID != raft.ServerID(id) && servers[i].Suffrage == raft.Voter { + if servers[i].ID != id && servers[i].Suffrage == raft.Voter { followers = append(followers, &Server{ ID: string(servers[i].ID), Addr: string(servers[i].Address), @@ -1770,7 +1771,7 @@ return ErrInvalidBackupFormat } -// Loads an entire SQLite file into the database, sending the request +// Load loads an entire SQLite file into the database, sending the request // through the Raft log. func (s *Store) Load(lr *proto.LoadRequest) error { if !s.open.Is() { @@ -2995,6 +2996,14 @@ return s.hcLogLevel() < hclog.Warn } +func raftLogInfoMessage(sz int64, fi, li uint64) string { + if sz == 0 { + return ("raft log is 0 bytes at open, no entries present") + } + hSz := humanize.Bytes(uint64(sz)) + return fmt.Sprintf("raft log is %s, first index is %d, last index is %d", hSz, fi, li) +} + // createDBOnDisk opens an on-disk database file at the configured path // for the store. The WAL-specific files are always removed as there is // no guarantee they are consistent with the main database file. If remove diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/store/store_cdc_test.go new/rqlite-9.3.4/store/store_cdc_test.go --- old/rqlite-9.3.2/store/store_cdc_test.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/store/store_cdc_test.go 2025-12-04 16:07:22.000000000 +0100 @@ -142,7 +142,6 @@ } if ev.NewRow.Values[1].GetS() != "fiona" { t.Fatalf("expected new row name value to be 'fiona', got %s", ev.NewRow.Values[1].GetS()) - break } case <-timeout: t.Fatalf("timeout waiting for CDC INSERT event for table 'foo'") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/store/store_test.go new/rqlite-9.3.4/store/store_test.go --- old/rqlite-9.3.2/store/store_test.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/store/store_test.go 2025-12-04 16:07:22.000000000 +0100 @@ -933,6 +933,65 @@ } } +func Test_SingleNodeExecuteQuery_RETURNING_KeywordAsIdent(t *testing.T) { + s, ln := mustNewStore(t) + defer ln.Close() + + if err := s.Open(); err != nil { + t.Fatalf("failed to open single-node store: %s", err.Error()) + } + if err := s.Bootstrap(NewServer(s.ID(), s.Addr(), true)); err != nil { + t.Fatalf("failed to bootstrap single-node store: %s", err.Error()) + } + defer s.Close(true) + _, err := s.WaitForLeader(10 * time.Second) + if err != nil { + t.Fatalf("Error waiting for leader: %s", err) + } + + er := executeRequestFromStrings([]string{ + `CREATE TABLE foo (id INTEGER NOT NULL PRIMARY KEY, desc TEXT)`, + `INSERT INTO foo(id, desc) VALUES(1, "fiona")`, + }, false, false) + _, _, err = s.Execute(er) + if err != nil { + t.Fatalf("failed to execute on single node: %s", err.Error()) + } + + er = executeRequestFromString(`INSERT INTO foo(id, desc) VALUES(2, "fiona") RETURNING desc`, false, false) + er.Request.Statements[0].ForceQuery = true + rows, _, err := s.Execute(er) + if err != nil { + t.Fatalf("failed to execute with RETURNING on single node: %s", err.Error()) + } + if exp, got := `[{"columns":["desc"],"types":["text"],"values":[["fiona"]]}]`, asJSON(rows); exp != got { + t.Fatalf("unexpected results for RETURNING query\nexp: %s\ngot: %s", exp, got) + } + + er = executeRequestFromString(`UPDATE foo SET desc = 'declan' where desc = 'fiona' RETURNING *`, false, false) + er.Request.Statements[0].ForceQuery = true + rows, _, err = s.Execute(er) + if err != nil { + t.Fatalf("failed to execute with RETURNING on single node: %s", err.Error()) + } + if exp, got := `[{"columns":["id","desc"],"types":["integer","text"],"values":[[1,"declan"],[2,"declan"]]}]`, asJSON(rows); exp != got { + t.Fatalf("unexpected results for RETURNING query\nexp: %s\ngot: %s", exp, got) + } + + qr := queryRequestFromString("SELECT * FROM foo", false, false) + qr.Level = proto.ConsistencyLevel_NONE + r, _, _, err := s.Query(qr) + if err != nil { + t.Fatalf("failed to query single node: %s", err.Error()) + } + if exp, got := `["id","desc"]`, asJSON(r[0].Columns); exp != got { + t.Fatalf("unexpected results for query\nexp: %s\ngot: %s", exp, got) + } + if exp, got := `[[1,"declan"],[2,"declan"]]`, asJSON(r[0].Values); exp != got { + t.Fatalf("unexpected results for query\nexp: %s\ngot: %s", exp, got) + } +} + // Test_SingleNodeExecuteQueryFail ensures database level errors are presented by the store. func Test_SingleNodeExecuteQueryFail(t *testing.T) { s, ln := mustNewStore(t) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/system_test/helpers.go new/rqlite-9.3.4/system_test/helpers.go --- old/rqlite-9.3.2/system_test/helpers.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/system_test/helpers.go 2025-12-04 16:07:22.000000000 +0100 @@ -111,7 +111,7 @@ return n.ExecuteMulti([]string{stmt}) } -// Execute executes a single statement against the node, requesting associative +// ExecuteAssoc executes a single statement against the node, requesting associative // output. func (n *Node) ExecuteAssoc(stmt string) (string, error) { return n.postExecuteAssoc(stmt, "text/plain") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rqlite-9.3.2/system_test/single_node_test.go new/rqlite-9.3.4/system_test/single_node_test.go --- old/rqlite-9.3.2/system_test/single_node_test.go 2025-11-26 00:08:42.000000000 +0100 +++ new/rqlite-9.3.4/system_test/single_node_test.go 2025-12-04 16:07:22.000000000 +0100 @@ -915,6 +915,33 @@ } } +func Test_SingleNode_RETURNING_KeywordAsIdent(t *testing.T) { + node := mustNewLeaderNode("leader1") + defer node.Deprovision() + + _, err := node.Execute(`CREATE TABLE foo (id integer not null primary key, desc text)`) + if err != nil { + t.Fatalf(`CREATE TABLE failed: %s`, err.Error()) + } + + // With RETURNING + res, err := node.Execute(`INSERT INTO foo(id, desc) VALUES(1, "declan") RETURNING *`) + if err != nil { + t.Fatalf(`write failed: %s`, err.Error()) + } + if got, exp := res, `{"results":[{"columns":["id","desc"],"types":["integer","text"],"values":[[1,"declan"]]}]}`; got != exp { + t.Fatalf("wrong execute results for RETURNING, exp %s, got %s", exp, got) + } + + res, err = node.Execute(`UPDATE foo SET desc = 'fiona' WHERE desc = 'declan' RETURNING *`) + if err != nil { + t.Fatalf(`write failed: %s`, err.Error()) + } + if got, exp := res, `{"results":[{"columns":["id","desc"],"types":["integer","text"],"values":[[1,"fiona"]]}]}`; got != exp { + t.Fatalf("wrong execute results for RETURNING, exp %s, got %s", exp, got) + } +} + func Test_SingleNodeQueued(t *testing.T) { node := mustNewLeaderNode("leader1") defer node.Deprovision() ++++++ vendor.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/rqlite/sql/parser.go new/vendor/github.com/rqlite/sql/parser.go --- old/vendor/github.com/rqlite/sql/parser.go 2025-11-26 00:08:42.000000000 +0100 +++ new/vendor/github.com/rqlite/sql/parser.go 2025-12-04 16:07:22.000000000 +0100 @@ -1829,8 +1829,8 @@ func (p *Parser) parseAssignment() (_ *Assignment, err error) { var assignment Assignment - // Parse either a single column (IDENT) or a column list (LP IDENT COMMA IDENT RP) - if isIdentToken(p.peek()) { + // Parse either a single column (IDENT or bare keyword) or a column list (LP IDENT COMMA IDENT RP) + if isIdentToken(p.peek()) || isBareToken(p.peek()) { col, _ := p.parseIdent("column name") assignment.Columns = []*Ident{col} } else if p.peek() == LP { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/rqlite/sql/token.go new/vendor/github.com/rqlite/sql/token.go --- old/vendor/github.com/rqlite/sql/token.go 2025-11-26 00:08:42.000000000 +0100 +++ new/vendor/github.com/rqlite/sql/token.go 2025-12-04 16:07:22.000000000 +0100 @@ -516,20 +516,25 @@ } // isExprIdentToken returns true if tok can be used as an identifier in an expression. -// It includes IDENT, QIDENT, and certain keywords. +// It includes IDENT, QIDENT, bare tokens (keywords that can be used as identifiers), +// and certain other keywords like ROWID. +// Note: Some bare tokens have special expression handling (CAST, CASE, RAISE, etc.) +// and should not be treated as identifiers in parseOperand. func isExprIdentToken(tok Token) bool { switch tok { case IDENT, QIDENT: return true - // List keywords that can be used as identifiers in expressions - case ROWID, CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP: + // ROWID is a special keyword that can be used as an identifier but is not a bare token + case ROWID: return true - // Core functions - case REPLACE, LIKE, GLOB, IF: - return true - // Add any other non-reserved keywords here - default: + // Exclude tokens that have special expression handling in parseOperand. + // These are in bareTokens but have precedence as expression keywords. + case CAST, CASE, RAISE, EXISTS, SELECT, NOT: return false + default: + // Bare tokens are keywords that can be used as unquoted identifiers + // (e.g., DESC, ASC, KEY, ACTION, REPLACE, LIKE, GLOB, IF, etc.) + return isBareToken(tok) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/modules.txt new/vendor/modules.txt --- old/vendor/modules.txt 2025-11-26 00:08:42.000000000 +0100 +++ new/vendor/modules.txt 2025-12-04 16:07:22.000000000 +0100 @@ -241,7 +241,7 @@ github.com/rqlite/rqlite-disco-clients/dnssrv github.com/rqlite/rqlite-disco-clients/etcd github.com/rqlite/rqlite-disco-clients/expand -# github.com/rqlite/sql v0.0.0-20251114131613-ef07423e7137 +# github.com/rqlite/sql v0.0.0-20251204023435-65660522892e ## explicit; go 1.17 github.com/rqlite/sql # go.etcd.io/bbolt v1.4.3
