• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

codenotary / immudb / 24841644892

23 Apr 2026 02:44PM UTC coverage: 85.279% (-4.0%) from 89.306%
24841644892

push

gh-ci

web-flow
feat: v1.11.0 PostgreSQL compatibility and SQL feature expansion (#2090)

* Add structured audit logging with immutable audit trail

Introduces a new --audit-log flag that records all gRPC operations as
structured JSON events in immudb's tamper-proof KV store. Events are
stored under the audit: key prefix in systemdb, queryable via Scan and
verifiable via VerifiableGet. An async buffered writer ensures minimal
latency impact. Configurable event filtering (all/write/admin) via
--audit-log-events flag.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add PostgreSQL ORM compatibility layer and verification functions

Extend the pgsql wire protocol with immudb verification functions
(immudb_state, immudb_verify_row, immudb_verify_tx, immudb_history,
immudb_tx) accessible via standard SQL SELECT statements.

Add pg_catalog resolvers (pg_attribute, pg_index, pg_constraint,
pg_type, pg_settings, pg_description) and information_schema
resolvers (tables, columns, schemata, key_column_usage) to support
ORM introspection from Django, SQLAlchemy, GORM, and ActiveRecord.

Add PostgreSQL compatibility functions: current_database,
current_schema, current_user, format_type, pg_encoding_to_char,
pg_get_expr, pg_get_constraintdef, obj_description, col_description,
has_table_privilege, has_schema_privilege, and others.

Add SHOW statement emulation for common ORM config queries and
schema-qualified name stripping for information_schema and public
schema references.

* Implement EXISTS and IN subquery support in SQL engine

Replace the previously stubbed ExistsBoolExp and InSubQueryExp
implementations with working non-correlated subquery execution.

EXISTS subqueries resolve the inner SELECT and check if any rows
are returned. IN subqueries resolve the inner SELECT, iterate the
result set, and compare each value against the outer expression.
Both support NOT variants (NOT EXISTS, NOT IN).

Correlated subqueries (referencing outer query columns) ar... (continued)

7254 of 10471 new or added lines in 124 files covered. (69.28%)

115 existing lines in 18 files now uncovered.

44599 of 52298 relevant lines covered (85.28%)

127676.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

66.36
/embedded/sql/set_op_row_reader.go
1
/*
2
Copyright 2026 Codenotary Inc. All rights reserved.
3

4
SPDX-License-Identifier: BUSL-1.1
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7

8
    https://mariadb.com/bsl11/
9

10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16

17
package sql
18

19
import (
20
        "context"
21
        "fmt"
22

23
        "github.com/codenotary/immudb/embedded/multierr"
24
)
25

26
type setOpType int
27

28
const (
29
        setOpExcept    setOpType = iota
30
        setOpIntersect
31
)
32

33
// setOpRowReader implements EXCEPT and INTERSECT set operations.
34
// It materializes the right side into memory and then filters left side rows.
35
type setOpRowReader struct {
36
        left  RowReader
37
        right RowReader
38
        cols  []ColDescriptor
39

40
        opType      setOpType
41
        rightLoaded bool
42
        rightRows   map[string]bool
43
}
44

45
func newSetOpRowReader(ctx context.Context, left, right RowReader, opType setOpType) (*setOpRowReader, error) {
14✔
46
        leftCols, err := left.Columns(ctx)
14✔
47
        if err != nil {
14✔
NEW
48
                return nil, err
×
NEW
49
        }
×
50

51
        rightCols, err := right.Columns(ctx)
14✔
52
        if err != nil {
14✔
NEW
53
                return nil, err
×
NEW
54
        }
×
55

56
        if len(leftCols) != len(rightCols) {
14✔
NEW
57
                return nil, fmt.Errorf("%w: each subquery must have same number of columns", ErrColumnMismatchInUnionStmt)
×
NEW
58
        }
×
59

60
        for c := 0; c < len(leftCols); c++ {
28✔
61
                if leftCols[c].Type != rightCols[c].Type {
14✔
NEW
62
                        return nil, fmt.Errorf("%w: expecting type '%v' for column '%s'", ErrColumnMismatchInUnionStmt, leftCols[c].Type, rightCols[c].Column)
×
NEW
63
                }
×
64
        }
65

66
        return &setOpRowReader{
14✔
67
                left:      left,
14✔
68
                right:     right,
14✔
69
                cols:      leftCols,
14✔
70
                opType:    opType,
14✔
71
                rightRows: make(map[string]bool),
14✔
72
        }, nil
14✔
73
}
74

75
func (sr *setOpRowReader) loadRight(ctx context.Context) error {
42✔
76
        if sr.rightLoaded {
74✔
77
                return nil
32✔
78
        }
32✔
79
        sr.rightLoaded = true
10✔
80

10✔
81
        for {
79✔
82
                row, err := sr.right.Read(ctx)
69✔
83
                if err == ErrNoMoreRows {
79✔
84
                        break
10✔
85
                }
86
                if err != nil {
59✔
NEW
87
                        return err
×
NEW
88
                }
×
89
                key := rowDigest(row)
59✔
90
                sr.rightRows[key] = true
59✔
91
        }
92

93
        // Close the right reader after materializing — it's no longer needed
94
        return sr.right.Close()
10✔
95
}
96

97
func rowDigest(row *Row) string {
121✔
98
        var b []byte
121✔
99
        for _, v := range row.ValuesByPosition {
242✔
100
                if v.IsNull() {
121✔
NEW
101
                        b = append(b, 0)
×
102
                } else {
121✔
103
                        b = append(b, 1)
121✔
104
                        b = append(b, []byte(fmt.Sprintf("%v", v.RawValue()))...)
121✔
105
                }
121✔
106
                b = append(b, '|')
121✔
107
        }
108
        return string(b)
121✔
109
}
110

111
func (sr *setOpRowReader) onClose(callback func()) {
14✔
112
        sr.left.onClose(callback)
14✔
113
}
14✔
114

NEW
115
func (sr *setOpRowReader) Tx() *SQLTx {
×
NEW
116
        return sr.left.Tx()
×
NEW
117
}
×
118

NEW
119
func (sr *setOpRowReader) TableAlias() string {
×
NEW
120
        return ""
×
NEW
121
}
×
122

NEW
123
func (sr *setOpRowReader) Parameters() map[string]interface{} {
×
NEW
124
        return sr.left.Parameters()
×
NEW
125
}
×
126

NEW
127
func (sr *setOpRowReader) OrderBy() []ColDescriptor {
×
NEW
128
        return nil
×
NEW
129
}
×
130

NEW
131
func (sr *setOpRowReader) ScanSpecs() *ScanSpecs {
×
NEW
132
        return nil
×
NEW
133
}
×
134

135
func (sr *setOpRowReader) Columns(ctx context.Context) ([]ColDescriptor, error) {
8✔
136
        return sr.cols, nil
8✔
137
}
8✔
138

NEW
139
func (sr *setOpRowReader) colsBySelector(ctx context.Context) (map[string]ColDescriptor, error) {
×
NEW
140
        return sr.left.colsBySelector(ctx)
×
NEW
141
}
×
142

NEW
143
func (sr *setOpRowReader) InferParameters(ctx context.Context, params map[string]SQLValueType) error {
×
NEW
144
        if err := sr.left.InferParameters(ctx, params); err != nil {
×
NEW
145
                return err
×
NEW
146
        }
×
NEW
147
        return sr.right.InferParameters(ctx, params)
×
148
}
149

150
func (sr *setOpRowReader) Read(ctx context.Context) (*Row, error) {
42✔
151
        if err := sr.loadRight(ctx); err != nil {
42✔
NEW
152
                return nil, err
×
NEW
153
        }
×
154

155
        for {
114✔
156
                row, err := sr.left.Read(ctx)
72✔
157
                if err != nil {
82✔
158
                        return nil, err
10✔
159
                }
10✔
160

161
                key := rowDigest(row)
62✔
162
                inRight := sr.rightRows[key]
62✔
163

62✔
164
                switch sr.opType {
62✔
165
                case setOpExcept:
32✔
166
                        if !inRight {
48✔
167
                                return row, nil
16✔
168
                        }
16✔
169
                case setOpIntersect:
30✔
170
                        if inRight {
46✔
171
                                return row, nil
16✔
172
                        }
16✔
173
                }
174
        }
175
}
176

177
func (sr *setOpRowReader) Close() error {
14✔
178
        merr := multierr.NewMultiErr()
14✔
179
        merr.Append(sr.left.Close())
14✔
180
        if !sr.rightLoaded {
18✔
181
                merr.Append(sr.right.Close())
4✔
182
        }
4✔
183
        return merr.Reduce()
14✔
184
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc