Skip to content

Commit

Permalink
sqlparser support for jsonb
Browse files Browse the repository at this point in the history
  • Loading branch information
simolus3 committed Jan 17, 2024
1 parent 8adb3bd commit b6fbfdb
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 5 deletions.
3 changes: 2 additions & 1 deletion sqlparser/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
## 0.33.1
## 0.34.1

- Fix explicit `NULL` column constraints being dropped when converting nodes
to SQL.
- Add analysis errors for illegal unqualified references to `old` and `new` in
triggers.
- Analysis support for sqlite 3.45 and jsonb functions.

## 0.33.0

Expand Down
28 changes: 25 additions & 3 deletions sqlparser/lib/src/engine/module/json1.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@ class Json1Extension implements Extension {

@override
void register(SqlEngine engine) {
final supportsJsonb = engine.options.version >= SqliteVersion.v3_45;

engine
..registerFunctionHandler(const _Json1Functions())
..registerFunctionHandler(_Json1Functions(supportsJsonb))
..registerTableValuedFunctionHandler(const _JsonEachFunction())
..registerTableValuedFunctionHandler(const _JsonTreeFunction());
}
}

class _Json1Functions implements FunctionHandler {
const _Json1Functions();
final bool _supportBinaryJson;

const _Json1Functions(this._supportBinaryJson);

static const Set<String> _returnStrings = {
'json',
Expand All @@ -29,12 +33,27 @@ class _Json1Functions implements FunctionHandler {
'json_group_object',
};

static const Set<String> _returnBlobs = {
'jsonb',
'jsonb_array',
'jsonb_insert',
'jsonb_object',
'jsonb_patch',
'jsonb_remove',
'jsonb_replace',
'jsonb_set',
'jsonb_group_array',
'jsonb_group_object'
};

@override
Set<String> get functionNames => const {
Set<String> get functionNames => {
..._returnStrings,
if (_supportBinaryJson) ..._returnBlobs,
'json_type',
'json_valid',
'json_extract',
if (_supportBinaryJson) 'jsonb_extract',
'json_array_length',
};

Expand All @@ -51,6 +70,8 @@ class _Json1Functions implements FunctionHandler {

if (_returnStrings.contains(name)) {
return const ResolveResult(ResolvedType(type: BasicType.text));
} else if (_returnBlobs.contains(name)) {
return const ResolveResult(ResolvedType(type: BasicType.blob));
} else {
switch (name) {
case 'json_type':
Expand All @@ -59,6 +80,7 @@ class _Json1Functions implements FunctionHandler {
case 'json_valid':
return const ResolveResult(ResolvedType.bool());
case 'json_extract':
case 'jsonb_extract':
return const ResolveResult.unknown();
case 'json_array_length':
return const ResolveResult(ResolvedType(type: BasicType.int));
Expand Down
6 changes: 5 additions & 1 deletion sqlparser/lib/src/engine/options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ class SqliteVersion implements Comparable<SqliteVersion> {
/// can't provide analysis warnings when using recent sqlite3 features.
static const SqliteVersion minimum = SqliteVersion.v3(34);

/// Version `3.55.0` has added support for a binary JSON format and introduces
/// `jsonb` variants of JSON functions.
static const SqliteVersion v3_45 = SqliteVersion.v3(45);

/// Version `3.44.0` has added `ORDER BY` clauses as parameters for aggregate
/// functions and more SQL functions.
static const SqliteVersion v3_44 = SqliteVersion.v3(44);
Expand Down Expand Up @@ -122,7 +126,7 @@ class SqliteVersion implements Comparable<SqliteVersion> {
/// The highest sqlite version supported by this `sqlparser` package.
///
/// Newer features in `sqlite3` may not be recognized by this library.
static const SqliteVersion current = v3_44;
static const SqliteVersion current = v3_45;

/// The major version of sqlite.
///
Expand Down
28 changes: 28 additions & 0 deletions sqlparser/test/engine/module/json1_test.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import 'package:sqlparser/sqlparser.dart';
import 'package:test/test.dart';

import '../../analysis/errors/utils.dart';

void main() {
group('json with new type inference', () {
final engine = SqlEngine(EngineOptions(
enabledExtensions: const [Json1Extension()],
version: SqliteVersion.v3_45,
));
// add user (name, phone) table
final table = engine.schemaReader.read(
Expand All @@ -23,6 +26,7 @@ void main() {
}

const resolvedString = ResolveResult(ResolvedType(type: BasicType.text));
const resolvedBlob = ResolveResult(ResolvedType(type: BasicType.blob));

test('create json', () {
expect(findResult("json('{}')"), resolvedString);
Expand All @@ -38,6 +42,19 @@ void main() {
expect(findResult('json_group_object()'), resolvedString);
});

test('create binary json', () {
expect(findResult("jsonb('{}')"), resolvedBlob);
expect(findResult("jsonb_array('foo', 'bar')"), resolvedBlob);
expect(findResult("jsonb_insert('{}')"), resolvedBlob);
expect(findResult("jsonb_replace('{}')"), resolvedBlob);
expect(findResult("jsonb_set('{}')"), resolvedBlob);
expect(findResult('jsonb_object()'), resolvedBlob);
expect(findResult("jsonb_patch('{}', '{}')"), resolvedBlob);
expect(findResult("jsonb_remove('{}', '{}')"), resolvedBlob);
expect(findResult('jsonb_group_array()'), resolvedBlob);
expect(findResult('jsonb_group_object()'), resolvedBlob);
});

test('json_type', () {
expect(
findResult("json_type('foo', 'bar')"),
Expand Down Expand Up @@ -71,4 +88,15 @@ SELECT DISTINCT user.name
expect(result.errors, isEmpty);
});
});

test('does not allow jsonb functions before 3.45', () {
final engine = SqlEngine(EngineOptions(version: SqliteVersion.v3_44));
final result = engine.analyze('SELECT jsonb(?);');
expect(result.errors, [
analysisErrorWith(
lexeme: 'jsonb',
type: AnalysisErrorType.unknownFunction,
)
]);
});
}

0 comments on commit b6fbfdb

Please sign in to comment.