Skip to content

Commit 923d18d

Browse files
committed
Initial JSONB support for typed_sql.
1 parent 2f1ac14 commit 923d18d

16 files changed

+632
-2
lines changed

typed_sql/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 0.1.4
2+
* Initial support for `JSONB` columns with `JsonValue` wrapper.
3+
14
## 0.1.3
25
* Added `TransactionAbortedException.toString()` to render the `reason` when
36
printing an exception.

typed_sql/lib/src/adapter/adapter.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,17 @@ abstract base class RowReader {
320320
/// Throws [AssertionError], if the type is not [Uint8List].
321321
Uint8List? readUint8List();
322322

323+
/// Read [JsonValue] or `null`.
324+
///
325+
/// Throws [AssertionError], if the type is not [JsonValue].
326+
JsonValue? readJsonValue();
327+
323328
/// Return `true` and consume next column, if next column is `null`.
324329
bool tryReadNull();
325330
}
331+
332+
class JsonValue {
333+
final Object? value;
334+
335+
JsonValue(this.value);
336+
}

typed_sql/lib/src/adapter/postgres_adapter.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,19 @@ final class _PostgresRowReader extends RowReader {
439439
return value;
440440
}
441441

442+
@override
443+
JsonValue? readJsonValue() {
444+
final value = _row[_i++];
445+
if (value == null ||
446+
value is bool ||
447+
value is num ||
448+
value is List ||
449+
value is Map) {
450+
return JsonValue(value);
451+
}
452+
throw AssertionError('readJsonValue() expected a JSON type, got "$value"');
453+
}
454+
442455
@override
443456
bool tryReadNull() {
444457
if (_row[_i] == null) {
@@ -462,6 +475,7 @@ List<Object?> _paramsForPostgres(List<Object?> params) => params
462475
int i => i,
463476
double d => d,
464477
Uint8List u => TypedValue(Type.byteArray, u, isSqlNull: false),
478+
JsonValue v => TypedValue(Type.jsonb, v.value, isSqlNull: false),
465479
_ => throw UnsupportedError('Unsupported type: ${p.runtimeType}'),
466480
})
467481
.toList();

typed_sql/lib/src/adapter/sqlite_adapter.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,19 @@ final class _SqliteRowReader extends RowReader {
417417
return value;
418418
}
419419

420+
@override
421+
JsonValue? readJsonValue() {
422+
final value = jsonb.decode(_row.values[_i++] as Uint8List);
423+
if (value == null ||
424+
value is bool ||
425+
value is num ||
426+
value is List ||
427+
value is Map) {
428+
return JsonValue(value);
429+
}
430+
throw AssertionError('readJsonValue() expected a JSON type, got "$value"');
431+
}
432+
420433
@override
421434
bool tryReadNull() {
422435
if (_row.values[_i] == null) {
@@ -436,6 +449,7 @@ List<Object?> _paramsForSqlite(List<Object?> params) => params
436449
int i => i,
437450
double d => d,
438451
Uint8List u => u,
452+
JsonValue v => jsonb.encode(v.value),
439453
_ => throw UnsupportedError('Unsupported type: ${p.runtimeType}'),
440454
})
441455
.toList();

typed_sql/lib/src/codegen/build_code.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ Iterable<Spec> buildTable(ParsedTable table, ParsedSchema schema) sync* {
290290
'bool' => '$rowReader.readBool()',
291291
'DateTime' => '$rowReader.readDateTime()',
292292
'Uint8List' => '$rowReader.readUint8List()',
293+
'JsonValue' => '$rowReader.readJsonValue()',
293294
_ => throw UnsupportedError(
294295
'Unsupported type "${field.typeName}"',
295296
),
@@ -1155,6 +1156,7 @@ String backingExprType(String backingType) {
11551156
'bool' => 'ExposedForCodeGen.boolean',
11561157
'DateTime' => 'ExposedForCodeGen.dateTime',
11571158
'Uint8List' => 'ExposedForCodeGen.blob',
1159+
'JsonValue' => 'ExposedForCodeGen.jsonValue',
11581160
_ => throw UnsupportedError(
11591161
'Unsupported backingType: "$backingType"',
11601162
),

typed_sql/lib/src/codegen/parse_library.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,12 @@ String? _tryGetColumnType(DartType t) {
509509
return 'DateTime';
510510
} else if (uint8ListTypeChecker.isExactlyType(t)) {
511511
return 'Uint8List';
512+
} else if (t.toString().contains('JsonValue') &&
513+
(t.element3
514+
?.getExtendedDisplayName2()
515+
.contains('/typed_sql/lib/src/adapter/adapter.dart') ??
516+
false)) {
517+
return 'JsonValue';
512518
}
513519
return null;
514520
}

typed_sql/lib/src/dialect/postgres_dialect.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import 'dart:typed_data';
1616

1717
import 'package:collection/collection.dart';
1818

19+
import '../adapter/adapter.dart' show JsonValue;
1920
import 'dialect.dart';
2021

2122
SqlDialect postgresDialect() => _PostgresDialect();
@@ -580,6 +581,7 @@ extension on ColumnType {
580581
ColumnType<int> _ => 'BIGINT',
581582
ColumnType<double> _ => 'DOUBLE PRECISION',
582583
ColumnType<String> _ => 'TEXT',
584+
ColumnType<JsonValue> _ => 'JSONB',
583585
ColumnType<Null> _ => throw UnsupportedError(
584586
'Null type cannot be used as column type',
585587
),

typed_sql/lib/src/dialect/sqlite_dialect.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import 'dart:typed_data' show Uint8List;
1616

1717
import 'package:collection/collection.dart';
1818

19+
import '../adapter/adapter.dart' show JsonValue;
1920
import 'dialect.dart';
2021

2122
SqlDialect sqliteDialect() => _Sqlite();
@@ -550,6 +551,7 @@ extension on ColumnType {
550551
ColumnType<int> _ => 'INTEGER',
551552
ColumnType<double> _ => 'REAL',
552553
ColumnType<String> _ => 'TEXT',
554+
ColumnType<JsonValue> _ => 'JSONB',
553555
ColumnType<Null> _ => throw UnsupportedError(
554556
'Null type cannot be used as column type',
555557
),

typed_sql/lib/src/typed_sql.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,4 +232,5 @@ final class ExposedForCodeGen {
232232
static const ColumnType<double> real = ColumnType.real;
233233
static const ColumnType<String> text = ColumnType.text;
234234
static const ColumnType<Null> nullType = ColumnType.nullType;
235+
static const ColumnType<JsonValue> jsonValue = ColumnType.jsonValue;
235236
}

typed_sql/lib/src/typed_sql.expr.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ sealed class ColumnType<T extends Object?> extends FieldType<T> {
3737
static const ColumnType<double> real = _RealExprType._();
3838
static const ColumnType<String> text = _TextExprType._();
3939
static const ColumnType<Null> nullType = _NullExprType._();
40+
static const ColumnType<JsonValue> jsonValue = _JsonValueExprType._();
4041
}
4142

4243
final class _BlobExprType extends ColumnType<Uint8List> {
@@ -81,6 +82,13 @@ final class _TextExprType extends ColumnType<String> {
8182
String? _read(RowReader r) => r.readString();
8283
}
8384

85+
final class _JsonValueExprType extends ColumnType<JsonValue> {
86+
const _JsonValueExprType._() : super._();
87+
88+
@override
89+
JsonValue? _read(RowReader r) => r.readJsonValue();
90+
}
91+
8492
final class _NullExprType extends ColumnType<Null> {
8593
const _NullExprType._() : super._();
8694

@@ -485,6 +493,8 @@ final class Literal<T> extends SingleValueExpr<T> {
485493
return Literal._(value, ColumnType.blob as _ExprType<T>);
486494
case DateTime _:
487495
return Literal._(value, ColumnType.dateTime as _ExprType<T>);
496+
case JsonValue _:
497+
return Literal._(value, ColumnType.jsonValue as _ExprType<T>);
488498

489499
case CustomDataType _:
490500
throw ArgumentError.value(

typed_sql/lib/typed_sql.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
/// See topic on _schema defintion_ for how to get started.
2323
library;
2424

25-
export 'src/adapter/adapter.dart' show DatabaseAdapter, RowReader;
25+
export 'src/adapter/adapter.dart' show DatabaseAdapter, JsonValue, RowReader;
2626
export 'src/dialect/dialect.dart' show SqlDialect;
2727
export 'src/exceptions.dart' hide throwTransactionAbortedException;
2828
export 'src/typed_sql.dart'

typed_sql/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: typed_sql
2-
version: 0.1.3
2+
version: 0.1.4
33
description: Package for doing SQL with some type safety.
44
homepage: https://github.com/google/dart-neats/tree/master/typed_sql
55
repository: https://github.com/google/dart-neats.git

0 commit comments

Comments
 (0)