• 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

80.0
/pkg/pgsql/server/rewriter_config.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 server
18

19
import (
20
        "strings"
21
        "sync/atomic"
22

23
        "github.com/codenotary/immudb/pkg/pgsql/server/rewrite"
24
        "github.com/codenotary/immudb/pkg/pgsql/server/rewrite/rules"
25
)
26

27
// The SQL rewriter mode selects between the legacy regex chain and
28
// the Part B AST-based rewriter. Kept as a package-level atomic so
29
// every caller of removePGCatalogReferences (tests, wire handlers,
30
// COPY subflow) reads a consistent value without having to thread
31
// the option through every signature.
32
//
33
// Values:
34
//   - "regex" (default): use the legacy pgTypeReplacements chain.
35
//   - "ast":             try the AST path first, fall back to regex
36
//                        on parse failure.
37
//
38
// Other values are treated as "regex". The feature flag is
39
// reversible at runtime — flipping it between requests is safe
40
// because the rewriter holds no per-session state.
41
var sqlRewriterModeValue atomic.Value
42

43
// DefaultSQLRewriterMode is the compiled-in default. Can be
44
// overridden via WithSQLRewriter(...) at server construction.
45
const DefaultSQLRewriterMode = "regex"
46

47
// SQLRewriter is a server option that selects the rewriter mode.
48
// Passing an unknown value is a no-op (default regex stays in
49
// effect); we don't panic so the server keeps starting if a config
50
// file carries a typo'd value.
51
func SQLRewriter(mode string) Option {
2✔
52
        return func(_ *pgsrv) {
4✔
53
                setSQLRewriterMode(mode)
2✔
54
        }
2✔
55
}
56

57
// setSQLRewriterMode writes the mode to the atomic cell. Exposed
58
// as lowercase so tests in the same package can flip it without
59
// constructing a full server.
60
func setSQLRewriterMode(mode string) {
20✔
61
        m := strings.ToLower(strings.TrimSpace(mode))
20✔
62
        switch m {
20✔
63
        case "ast", "regex":
19✔
64
                sqlRewriterModeValue.Store(m)
19✔
65
        default:
1✔
66
                sqlRewriterModeValue.Store(DefaultSQLRewriterMode)
1✔
67
        }
68
}
69

70
// sqlRewriterMode reads the current mode. Returns the compiled-in
71
// default if the atomic hasn't been populated yet (which is the
72
// case for any binary that hasn't constructed a server).
73
func sqlRewriterMode() string {
593✔
74
        v := sqlRewriterModeValue.Load()
593✔
75
        if v == nil {
593✔
NEW
76
                return DefaultSQLRewriterMode
×
NEW
77
        }
×
78
        s, _ := v.(string)
593✔
79
        if s == "" {
593✔
NEW
80
                return DefaultSQLRewriterMode
×
NEW
81
        }
×
82
        return s
593✔
83
}
84

85
// astRewriterSingleton is the pre-constructed pipeline used by the
86
// "ast" mode. Building it once at package init rather than per-call
87
// avoids O(n) rule-struct allocation on every query.
88
var astRewriterSingleton = rewrite.New().
89
        WithRule(rules.StripSchemaQualifier{}).
90
        WithRule(rules.StripPGCasts{}).
91
        WithRule(rules.StripCollate{}).
92
        WithRule(rules.NormalizeCountOne{}).
93
        WithRule(rules.StripTableStarPrefix{}).
94
        WithRule(rules.StripOnConflictColumns{}).
95
        WithRule(rules.StripCheckConstraints{}).
96
        WithRule(rules.StripForeignKeys{}).
97
        WithRule(rules.StripCreateIndexName{}).
98
        WithRule(rules.StripCreateViewColList{}).
99
        WithRule(rules.EnsureCreateTableIfNotExists{}).
100
        WithRule(rules.StripTransactionModes{}).
101
        WithRule(rules.CollapseStarCommaList{}).
102
        WithRule(rules.StripTrailingCommentOn{})
103

104
// astRewrite runs the B1 AST rewriter on a single SQL string.
105
// Returns (output, true) on success; (input, false) when the parser
106
// fails or when the output is empty (e.g. comment-only input). The
107
// caller uses the bool to decide whether to fall through to the
108
// regex chain.
109
//
110
// Why not merge the AST rules into the regex chain? Two reasons:
111
// (1) the AST path is a pure function of (rules, parser) and can
112
// be swapped out wholesale in B3 without touching callers; (2) the
113
// boolean return makes the A/B comparison possible in integration
114
// tests.
115
func astRewrite(sql string) (string, bool) {
28✔
116
        out, err := astRewriterSingleton.Rewrite(sql)
28✔
117
        if err != nil {
29✔
118
                return sql, false
1✔
119
        }
1✔
120
        if out == "" {
27✔
NEW
121
                return sql, false
×
NEW
122
        }
×
123
        return out, true
27✔
124
}
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