1
1
import 'dart:async';
2
2
import 'dart:js_interop';
3
3
import 'dart:js_interop_unsafe';
4
+ import 'dart:typed_data';
4
5
5
6
import 'package:sqlite3/common.dart';
6
7
import 'package:sqlite3/wasm.dart' as wasm_vfs;
8
+ // ignore: implementation_imports
9
+ import 'package:sqlite3/src/wasm/js_interop/core.dart';
7
10
import 'package:web/web.dart';
8
11
9
12
import 'types.dart';
@@ -69,6 +72,7 @@ class _UniqueFieldNames {
69
72
static const returnRows = 'r';
70
73
static const updateRowId = 'r';
71
74
static const rows = 'r'; // no clash, used on different message types
75
+ static const typeVector = 'v';
72
76
}
73
77
74
78
sealed class Message {
@@ -450,15 +454,25 @@ final class RunQuery extends Request {
450
454
});
451
455
452
456
factory RunQuery.deserialize(JSObject object) {
457
+ final rawParameters =
458
+ (object[_UniqueFieldNames.parameters] as JSArray).toDart;
459
+ final typeVector = switch (object[_UniqueFieldNames.typeVector]) {
460
+ final types? => (types as JSArrayBuffer).toDart.asUint8List(),
461
+ null => null,
462
+ };
463
+
464
+ final parameters = List<Object?>.filled(rawParameters.length, null);
465
+ for (var i = 0; i < parameters.length; i++) {
466
+ final typeCode =
467
+ typeVector != null ? TypeCode.of(typeVector[i]) : TypeCode.unknown;
468
+ parameters[i] = typeCode.decodeColumn(rawParameters[i]);
469
+ }
470
+
453
471
return RunQuery(
454
472
requestId: object.requestId,
455
473
databaseId: object.databaseId,
456
474
sql: (object[_UniqueFieldNames.sql] as JSString).toDart,
457
- parameters: [
458
- for (final raw
459
- in (object[_UniqueFieldNames.parameters] as JSArray).toDart)
460
- raw.dartify()
461
- ],
475
+ parameters: parameters,
462
476
returnRows: (object[_UniqueFieldNames.returnRows] as JSBoolean).toDart,
463
477
);
464
478
}
@@ -470,9 +484,25 @@ final class RunQuery extends Request {
470
484
void serialize(JSObject object, List<JSObject> transferred) {
471
485
super.serialize(object, transferred);
472
486
object[_UniqueFieldNames.sql] = sql.toJS;
473
- object[_UniqueFieldNames.parameters] =
474
- <JSAny?>[for (final parameter in parameters) parameter.jsify()].toJS;
475
487
object[_UniqueFieldNames.returnRows] = returnRows.toJS;
488
+
489
+ if (parameters.isNotEmpty) {
490
+ final jsParams = <JSAny?>[];
491
+ final typeCodes = Uint8List(parameters.length);
492
+ for (var i = 0; i < parameters.length; i++) {
493
+ final (code, jsParam) = TypeCode.encodeValue(parameters[i]);
494
+ typeCodes[i] = code.index;
495
+ jsParams.add(jsParam);
496
+ }
497
+
498
+ final jsTypes = typeCodes.buffer.toJS;
499
+ transferred.add(jsTypes);
500
+
501
+ object[_UniqueFieldNames.parameters] = jsParams.toJS;
502
+ object[_UniqueFieldNames.typeVector] = jsTypes;
503
+ } else {
504
+ object[_UniqueFieldNames.parameters] = JSArray();
505
+ }
476
506
}
477
507
}
478
508
@@ -557,6 +587,81 @@ final class EndpointResponse extends Response {
557
587
}
558
588
}
559
589
590
+ enum TypeCode {
591
+ unknown,
592
+ integer,
593
+ bigInt,
594
+ float,
595
+ text,
596
+ blob,
597
+ $null,
598
+ boolean;
599
+
600
+ static TypeCode of(int i) {
601
+ return i >= TypeCode.values.length ? TypeCode.unknown : TypeCode.values[i];
602
+ }
603
+
604
+ Object? decodeColumn(JSAny? column) {
605
+ const hasNativeInts = !identical(0, 0.0);
606
+
607
+ return switch (this) {
608
+ TypeCode.unknown => column.dartify(),
609
+ TypeCode.integer => (column as JSNumber).toDartInt,
610
+ TypeCode.bigInt => hasNativeInts
611
+ ? (column as JsBigInt).asDartInt
612
+ : (column as JsBigInt).asDartBigInt,
613
+ TypeCode.float => (column as JSNumber).toDartDouble,
614
+ TypeCode.text => (column as JSString).toDart,
615
+ TypeCode.blob => (column as JSUint8Array).toDart,
616
+ TypeCode.boolean => (column as JSBoolean).toDart,
617
+ TypeCode.$null => null,
618
+ };
619
+ }
620
+
621
+ static (TypeCode, JSAny?) encodeValue(Object? dart) {
622
+ // In previous clients/workers, values were encoded with dartify() and
623
+ // jsify() only. For backwards-compatibility, this value must be compatible
624
+ // with dartify() used on the other end.
625
+ // An exception are BigInts, which have not been sent correctly before this
626
+ // encoder.
627
+ // The reasons for adopting a custom format are: Being able to properly
628
+ // serialize BigInts, possible dartify/jsify incompatibilities between
629
+ // dart2js and dart2wasm and most importantly, being able to keep 1 and 1.0
630
+ // apart in dart2wasm when the worker is compiled with dart2js.
631
+ final JSAny? value;
632
+ final TypeCode code;
633
+
634
+ switch (dart) {
635
+ case null:
636
+ value = null;
637
+ code = TypeCode.$null;
638
+ case final int integer:
639
+ value = integer.toJS;
640
+ code = TypeCode.integer;
641
+ case final BigInt bi:
642
+ value = JsBigInt.fromBigInt(bi);
643
+ code = TypeCode.bigInt;
644
+ case final double d:
645
+ value = d.toJS;
646
+ code = TypeCode.float;
647
+ case final String s:
648
+ value = s.toJS;
649
+ code = TypeCode.text;
650
+ case final Uint8List blob:
651
+ value = blob.toJS;
652
+ code = TypeCode.blob;
653
+ case final bool boolean:
654
+ value = boolean.toJS;
655
+ code = TypeCode.boolean;
656
+ case final other:
657
+ value = other.jsify();
658
+ code = TypeCode.unknown;
659
+ }
660
+
661
+ return (code, value);
662
+ }
663
+ }
664
+
560
665
final class RowsResponse extends Response {
561
666
final ResultSet resultSet;
562
667
@@ -576,12 +681,20 @@ final class RowsResponse extends Response {
576
681
]
577
682
: null;
578
683
684
+ final typeVector = switch (object[_UniqueFieldNames.typeVector]) {
685
+ final types? => (types as JSArrayBuffer).toDart.asUint8List(),
686
+ null => null,
687
+ };
579
688
final rows = <List<Object?>>[];
689
+ var i = 0;
580
690
for (final row in (object[_UniqueFieldNames.rows] as JSArray).toDart) {
581
691
final dartRow = <Object?>[];
582
692
583
693
for (final column in (row as JSArray).toDart) {
584
- dartRow.add(column.dartify());
694
+ final typeCode =
695
+ typeVector != null ? TypeCode.of(typeVector[i]) : TypeCode.unknown;
696
+ dartRow.add(typeCode.decodeColumn(column));
697
+ i++;
585
698
}
586
699
587
700
rows.add(dartRow);
@@ -599,6 +712,29 @@ final class RowsResponse extends Response {
599
712
@override
600
713
void serialize(JSObject object, List<JSObject> transferred) {
601
714
super.serialize(object, transferred);
715
+ final jsRows = <JSArray>[];
716
+ final columns = resultSet.columnNames.length;
717
+ final typeVector = Uint8List(resultSet.length * columns);
718
+
719
+ for (var i = 0; i < resultSet.length; i++) {
720
+ final row = resultSet.rows[i];
721
+ assert(row.length == columns);
722
+ final jsRow = List<JSAny?>.filled(row.length, null);
723
+
724
+ for (var j = 0; j < columns; j++) {
725
+ final (code, value) = TypeCode.encodeValue(row[j]);
726
+
727
+ jsRow[j] = value;
728
+ typeVector[i * columns + j] = code.index;
729
+ }
730
+
731
+ jsRows.add(jsRow.toJS);
732
+ }
733
+
734
+ final jsTypes = typeVector.buffer.toJS;
735
+ object[_UniqueFieldNames.typeVector] = jsTypes;
736
+ transferred.add(jsTypes);
737
+
602
738
object[_UniqueFieldNames.rows] = <JSArray>[
603
739
for (final row in resultSet.rows)
604
740
<JSAny?>[
0 commit comments