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