@capgo/capacitor-fast-sql 8.2.18 → 8.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Package.swift +2 -1
- package/README.md +22 -14
- package/android/build.gradle +3 -0
- package/android/src/main/java/app/capgo/capacitor/fastsql/BinaryFrame.java +152 -0
- package/android/src/main/java/app/capgo/capacitor/fastsql/CapgoCapacitorFastSqlPlugin.java +5 -1
- package/android/src/main/java/app/capgo/capacitor/fastsql/DatabaseConnection.java +2 -0
- package/android/src/main/java/app/capgo/capacitor/fastsql/EncryptedSQLDatabase.java +13 -2
- package/android/src/main/java/app/capgo/capacitor/fastsql/SQLDatabase.java +13 -2
- package/android/src/main/java/app/capgo/capacitor/fastsql/SQLHTTPServer.java +114 -57
- package/dist/esm/binary-frame.d.ts +6 -0
- package/dist/esm/binary-frame.d.ts.map +1 -0
- package/dist/esm/binary-frame.js +100 -0
- package/dist/esm/definitions.d.ts +17 -0
- package/dist/esm/definitions.d.ts.map +1 -1
- package/dist/esm/fast-sql.d.ts.map +1 -1
- package/dist/esm/fast-sql.js +13 -3
- package/dist/esm/native-sql-connection.d.ts +18 -7
- package/dist/esm/native-sql-connection.d.ts.map +1 -1
- package/dist/esm/native-sql-connection.js +145 -59
- package/dist/esm/web.d.ts.map +1 -1
- package/dist/esm/web.js +2 -0
- package/dist/plugin.cjs.js +260 -62
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +260 -62
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapgoCapacitorFastSqlPlugin/BinaryFrame.swift +95 -0
- package/ios/Sources/CapgoCapacitorFastSqlPlugin/CapgoCapacitorFastSqlPlugin.swift +7 -3
- package/ios/Sources/CapgoCapacitorFastSqlPlugin/SQLDatabase.swift +20 -3
- package/ios/Sources/CapgoCapacitorFastSqlPlugin/SQLHTTPServer.swift +59 -76
- package/ios/Tests/CapgoCapacitorFastSqlPluginTests/BinaryFrameTests.swift +65 -0
- package/ios/Tests/CapgoCapacitorFastSqlPluginTests/Fixtures/binary-v1.bin +0 -0
- package/package.json +3 -2
package/Package.swift
CHANGED
|
@@ -25,6 +25,7 @@ let package = Package(
|
|
|
25
25
|
.testTarget(
|
|
26
26
|
name: "CapgoCapacitorFastSqlPluginTests",
|
|
27
27
|
dependencies: ["CapgoCapacitorFastSqlPlugin"],
|
|
28
|
-
path: "ios/Tests/CapgoCapacitorFastSqlPluginTests"
|
|
28
|
+
path: "ios/Tests/CapgoCapacitorFastSqlPluginTests",
|
|
29
|
+
resources: [.copy("Fixtures")])
|
|
29
30
|
]
|
|
30
31
|
)
|
package/README.md
CHANGED
|
@@ -383,7 +383,7 @@ bypassing Capacitor's standard bridge for better performance with sync operation
|
|
|
383
383
|
### connect(...)
|
|
384
384
|
|
|
385
385
|
```typescript
|
|
386
|
-
connect(options: SQLConnectionOptions) => Promise<{ port: number; token: string; database: string; persistent?: boolean; }>
|
|
386
|
+
connect(options: SQLConnectionOptions) => Promise<{ port: number; token: string; database: string; persistent?: boolean; binaryProtocolVersion?: number; }>
|
|
387
387
|
```
|
|
388
388
|
|
|
389
389
|
Initialize the database connection and start the HTTP server.
|
|
@@ -392,7 +392,7 @@ Initialize the database connection and start the HTTP server.
|
|
|
392
392
|
| ------------- | --------------------------------------------------------------------- | -------------------- |
|
|
393
393
|
| **`options`** | <code><a href="#sqlconnectionoptions">SQLConnectionOptions</a></code> | - Connection options |
|
|
394
394
|
|
|
395
|
-
**Returns:** <code>Promise<{ port: number; token: string; database: string; persistent?: boolean; }></code>
|
|
395
|
+
**Returns:** <code>Promise<{ port: number; token: string; database: string; persistent?: boolean; binaryProtocolVersion?: number; }></code>
|
|
396
396
|
|
|
397
397
|
**Since:** 0.0.1
|
|
398
398
|
|
|
@@ -419,7 +419,7 @@ Close database connection and stop the HTTP server.
|
|
|
419
419
|
### getServerInfo(...)
|
|
420
420
|
|
|
421
421
|
```typescript
|
|
422
|
-
getServerInfo(options: { database: string; }) => Promise<{ port: number; token: string; persistent?: boolean; }>
|
|
422
|
+
getServerInfo(options: { database: string; }) => Promise<{ port: number; token: string; persistent?: boolean; binaryProtocolVersion?: number; }>
|
|
423
423
|
```
|
|
424
424
|
|
|
425
425
|
Get the HTTP server port and token for direct communication.
|
|
@@ -428,7 +428,7 @@ Get the HTTP server port and token for direct communication.
|
|
|
428
428
|
| ------------- | ---------------------------------- | --------------- |
|
|
429
429
|
| **`options`** | <code>{ database: string; }</code> | - Database name |
|
|
430
430
|
|
|
431
|
-
**Returns:** <code>Promise<{ port: number; token: string; persistent?: boolean; }></code>
|
|
431
|
+
**Returns:** <code>Promise<{ port: number; token: string; persistent?: boolean; binaryProtocolVersion?: number; }></code>
|
|
432
432
|
|
|
433
433
|
**Since:** 0.0.1
|
|
434
434
|
|
|
@@ -509,12 +509,12 @@ Rollback the current transaction.
|
|
|
509
509
|
### getPluginVersion()
|
|
510
510
|
|
|
511
511
|
```typescript
|
|
512
|
-
getPluginVersion() => Promise<{ version: string; }>
|
|
512
|
+
getPluginVersion() => Promise<{ version: string; binaryProtocolVersion?: number; }>
|
|
513
513
|
```
|
|
514
514
|
|
|
515
515
|
Get the native Capacitor plugin version.
|
|
516
516
|
|
|
517
|
-
**Returns:** <code>Promise<{ version: string; }></code>
|
|
517
|
+
**Returns:** <code>Promise<{ version: string; binaryProtocolVersion?: number; }></code>
|
|
518
518
|
|
|
519
519
|
**Since:** 0.0.1
|
|
520
520
|
|
|
@@ -548,14 +548,15 @@ or supply a custom Worker. This method is a no-op on iOS and Android.
|
|
|
548
548
|
|
|
549
549
|
Database connection options
|
|
550
550
|
|
|
551
|
-
| Prop | Type
|
|
552
|
-
| ------------------------ |
|
|
553
|
-
| **`
|
|
554
|
-
| **`
|
|
555
|
-
| **`
|
|
556
|
-
| **`
|
|
557
|
-
| **`
|
|
558
|
-
| **`
|
|
551
|
+
| Prop | Type | Description | Default | Since |
|
|
552
|
+
| ------------------------ | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------ | ------ |
|
|
553
|
+
| **`transport`** | <code><a href="#nativesqltransport">NativeSQLTransport</a></code> | Native HTTP data encoding. Binary v1 sends BLOBs as raw bytes without base64. Opt-in on iOS/Android only; requires a native plugin advertising binary v1. JSON remains the default. Binary is experimental; reconsider the default only in a future major after device performance and conformance testing. No failed write is retried on another transport. | <code>'http-json'</code> | |
|
|
554
|
+
| **`database`** | <code>string</code> | Database name (file will be created in app data directory) | | |
|
|
555
|
+
| **`encrypted`** | <code>boolean</code> | Enable encryption (iOS/Android only) | | |
|
|
556
|
+
| **`encryptionKey`** | <code>string</code> | Encryption key (required if encrypted is true) | | |
|
|
557
|
+
| **`readOnly`** | <code>boolean</code> | Open an existing native database read-only. Writes and missing-file opens fail. WAL mode changes are skipped for read-only connections. The web Worker transport rejects this option instead of silently opening writable. | | |
|
|
558
|
+
| **`walMode`** | <code>boolean</code> | Enable WAL (Write-Ahead Logging) journal mode for better concurrency and performance. Executes `PRAGMA journal_mode = WAL` immediately after opening the database. | | 8.0.49 |
|
|
559
|
+
| **`performancePresets`** | <code>boolean</code> | Apply recommended SQLite performance PRAGMAs after opening the database: `synchronous = NORMAL`, `busy_timeout = 5000`, `cache_size = -2000`, and `foreign_keys = ON`. | | 8.0.49 |
|
|
559
560
|
|
|
560
561
|
|
|
561
562
|
#### SQLResult
|
|
@@ -668,6 +669,13 @@ OPFS persistence requires Cross-Origin Isolation headers on your web server:
|
|
|
668
669
|
### Type Aliases
|
|
669
670
|
|
|
670
671
|
|
|
672
|
+
#### NativeSQLTransport
|
|
673
|
+
|
|
674
|
+
Native HTTP data encoding. JSON remains the compatibility default.
|
|
675
|
+
|
|
676
|
+
<code>'http-json' | 'http-binary'</code>
|
|
677
|
+
|
|
678
|
+
|
|
671
679
|
#### SQLValue
|
|
672
680
|
|
|
673
681
|
SQL value types supported by the plugin
|
package/android/build.gradle
CHANGED
|
@@ -41,6 +41,9 @@ android {
|
|
|
41
41
|
sourceCompatibility JavaVersion.VERSION_21
|
|
42
42
|
targetCompatibility JavaVersion.VERSION_21
|
|
43
43
|
}
|
|
44
|
+
sourceSets {
|
|
45
|
+
test.resources.srcDir "../ios/Tests/CapgoCapacitorFastSqlPluginTests/Fixtures"
|
|
46
|
+
}
|
|
44
47
|
testOptions {
|
|
45
48
|
unitTests.includeAndroidResources = true
|
|
46
49
|
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
package app.capgo.capacitor.fastsql;
|
|
2
|
+
|
|
3
|
+
import java.io.ByteArrayOutputStream;
|
|
4
|
+
import java.io.IOException;
|
|
5
|
+
import java.nio.ByteBuffer;
|
|
6
|
+
import java.nio.ByteOrder;
|
|
7
|
+
import java.nio.charset.CodingErrorAction;
|
|
8
|
+
import java.nio.charset.StandardCharsets;
|
|
9
|
+
import java.util.Iterator;
|
|
10
|
+
import org.json.JSONArray;
|
|
11
|
+
import org.json.JSONObject;
|
|
12
|
+
import org.json.JSONTokener;
|
|
13
|
+
|
|
14
|
+
/** Shared binary v1 envelope. Byte arrays never pass through base64 or JSON. */
|
|
15
|
+
final class BinaryFrame {
|
|
16
|
+
|
|
17
|
+
static final String CONTENT_TYPE = "application/vnd.capgo.fast-sql.v1+binary";
|
|
18
|
+
static final int MAX_BYTES = 64 * 1024 * 1024;
|
|
19
|
+
private static final int MAX_METADATA = 1024 * 1024;
|
|
20
|
+
private static final int HEADER = 12;
|
|
21
|
+
private static final byte[] MAGIC = { 70, 83, 81, 49 };
|
|
22
|
+
private int nodes;
|
|
23
|
+
private int blobs;
|
|
24
|
+
private long referencedBytes;
|
|
25
|
+
|
|
26
|
+
static Object decode(byte[] frame) throws Exception {
|
|
27
|
+
if (frame.length < HEADER || frame.length > MAX_BYTES) throw new IOException("Invalid binary frame length");
|
|
28
|
+
for (int i = 0; i < MAGIC.length; i++) if (frame[i] != MAGIC[i]) throw new IOException("Invalid binary frame version");
|
|
29
|
+
ByteBuffer header = ByteBuffer.wrap(frame).order(ByteOrder.LITTLE_ENDIAN);
|
|
30
|
+
int metadataLength = header.getInt(4);
|
|
31
|
+
int payloadLength = header.getInt(8);
|
|
32
|
+
if (
|
|
33
|
+
metadataLength <= 0 ||
|
|
34
|
+
metadataLength > MAX_METADATA ||
|
|
35
|
+
payloadLength < 0 ||
|
|
36
|
+
(long) HEADER + metadataLength + payloadLength != frame.length
|
|
37
|
+
) throw new IOException("Invalid binary frame lengths");
|
|
38
|
+
String metadata = StandardCharsets.UTF_8.newDecoder()
|
|
39
|
+
.onMalformedInput(CodingErrorAction.REPORT)
|
|
40
|
+
.onUnmappableCharacter(CodingErrorAction.REPORT)
|
|
41
|
+
.decode(ByteBuffer.wrap(frame, HEADER, metadataLength))
|
|
42
|
+
.toString();
|
|
43
|
+
checkNesting(metadata);
|
|
44
|
+
JSONTokener parser = new JSONTokener(metadata);
|
|
45
|
+
Object value = parser.nextValue();
|
|
46
|
+
if (parser.nextClean() != 0) throw new IOException("Trailing binary metadata");
|
|
47
|
+
return new BinaryFrame().decodeValue(value, frame, HEADER + metadataLength, payloadLength, 0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private static void checkNesting(String json) throws IOException {
|
|
51
|
+
boolean quoted = false;
|
|
52
|
+
boolean escaped = false;
|
|
53
|
+
int depth = 0;
|
|
54
|
+
for (int i = 0; i < json.length(); i++) {
|
|
55
|
+
char value = json.charAt(i);
|
|
56
|
+
if (quoted) {
|
|
57
|
+
if (escaped) escaped = false;
|
|
58
|
+
else if (value == 92) escaped = true;
|
|
59
|
+
else if (value == 34) quoted = false;
|
|
60
|
+
} else if (value == 34) quoted = true;
|
|
61
|
+
else if (value == '[' || value == '{') {
|
|
62
|
+
if (++depth > 33) throw new IOException("Binary metadata nesting exceeds limits");
|
|
63
|
+
} else if (value == ']' || value == '}') depth--;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private Object decodeValue(Object value, byte[] frame, int payloadStart, int payloadLength, int depth) throws Exception {
|
|
68
|
+
if (++nodes > 100000 || depth > 32) throw new IOException("Binary metadata exceeds limits");
|
|
69
|
+
if (value instanceof JSONArray) {
|
|
70
|
+
JSONArray input = (JSONArray) value;
|
|
71
|
+
JSONArray result = new JSONArray();
|
|
72
|
+
for (int i = 0; i < input.length(); i++) result.put(decodeValue(input.get(i), frame, payloadStart, payloadLength, depth + 1));
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
if (!(value instanceof JSONObject)) return value;
|
|
76
|
+
JSONObject input = (JSONObject) value;
|
|
77
|
+
if (input.has("$blob") && input.length() == 1) {
|
|
78
|
+
JSONArray reference = input.getJSONArray("$blob");
|
|
79
|
+
if (
|
|
80
|
+
reference.length() != 2 || !(reference.get(0) instanceof Number) || !(reference.get(1) instanceof Number)
|
|
81
|
+
) throw new IOException("Invalid binary attachment reference");
|
|
82
|
+
double offset = reference.getDouble(0);
|
|
83
|
+
double length = reference.getDouble(1);
|
|
84
|
+
if (
|
|
85
|
+
!Double.isFinite(offset) ||
|
|
86
|
+
!Double.isFinite(length) ||
|
|
87
|
+
offset < 0 ||
|
|
88
|
+
length < 0 ||
|
|
89
|
+
offset != Math.floor(offset) ||
|
|
90
|
+
length != Math.floor(length) ||
|
|
91
|
+
offset + length > payloadLength
|
|
92
|
+
) throw new IOException("Binary attachment outside frame");
|
|
93
|
+
referencedBytes += (long) length;
|
|
94
|
+
if (++blobs > 4096 || referencedBytes > MAX_BYTES) throw new IOException("Decoded attachments exceed limits");
|
|
95
|
+
return java.util.Arrays.copyOfRange(frame, payloadStart + (int) offset, payloadStart + (int) offset + (int) length);
|
|
96
|
+
}
|
|
97
|
+
JSONObject result = new JSONObject();
|
|
98
|
+
Iterator<String> keys = input.keys();
|
|
99
|
+
while (keys.hasNext()) {
|
|
100
|
+
String key = keys.next();
|
|
101
|
+
result.put(key, decodeValue(input.get(key), frame, payloadStart, payloadLength, depth + 1));
|
|
102
|
+
}
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
static byte[] encode(Object value) throws Exception {
|
|
107
|
+
ByteArrayOutputStream attachments = new ByteArrayOutputStream();
|
|
108
|
+
BinaryFrame codec = new BinaryFrame();
|
|
109
|
+
Object metadataValue = codec.encodeValue(value, attachments, 0);
|
|
110
|
+
String wrapped = new JSONArray().put(metadataValue).toString();
|
|
111
|
+
byte[] metadata = wrapped.substring(1, wrapped.length() - 1).getBytes(StandardCharsets.UTF_8);
|
|
112
|
+
long total = (long) HEADER + metadata.length + attachments.size();
|
|
113
|
+
if (metadata.length > MAX_METADATA || total > MAX_BYTES) throw new IOException("Binary frame exceeds limits");
|
|
114
|
+
ByteArrayOutputStream output = new ByteArrayOutputStream((int) total);
|
|
115
|
+
output.write(MAGIC);
|
|
116
|
+
output.write(ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putInt(metadata.length).putInt(attachments.size()).array());
|
|
117
|
+
output.write(metadata);
|
|
118
|
+
attachments.writeTo(output);
|
|
119
|
+
return output.toByteArray();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private Object encodeValue(Object value, ByteArrayOutputStream attachments, int depth) throws Exception {
|
|
123
|
+
if (++nodes > 100000 || depth > 32) throw new IOException("Binary metadata exceeds limits");
|
|
124
|
+
if (value instanceof byte[]) {
|
|
125
|
+
byte[] bytes = (byte[]) value;
|
|
126
|
+
if (++blobs > 4096 || (long) attachments.size() + bytes.length > MAX_BYTES - HEADER) throw new IOException(
|
|
127
|
+
"Binary attachments exceed limits"
|
|
128
|
+
);
|
|
129
|
+
JSONObject reference = new JSONObject();
|
|
130
|
+
reference.put("$blob", new JSONArray().put(attachments.size()).put(bytes.length));
|
|
131
|
+
attachments.write(bytes);
|
|
132
|
+
return reference;
|
|
133
|
+
}
|
|
134
|
+
if (value instanceof JSONArray) {
|
|
135
|
+
JSONArray input = (JSONArray) value;
|
|
136
|
+
JSONArray result = new JSONArray();
|
|
137
|
+
for (int i = 0; i < input.length(); i++) result.put(encodeValue(input.get(i), attachments, depth + 1));
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
if (value instanceof JSONObject) {
|
|
141
|
+
JSONObject input = (JSONObject) value;
|
|
142
|
+
JSONObject result = new JSONObject();
|
|
143
|
+
Iterator<String> keys = input.keys();
|
|
144
|
+
while (keys.hasNext()) {
|
|
145
|
+
String key = keys.next();
|
|
146
|
+
result.put(key, encodeValue(input.get(key), attachments, depth + 1));
|
|
147
|
+
}
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
return value;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -19,7 +19,7 @@ import java.util.Map;
|
|
|
19
19
|
@CapacitorPlugin(name = "CapgoCapacitorFastSql")
|
|
20
20
|
public class CapgoCapacitorFastSqlPlugin extends Plugin {
|
|
21
21
|
|
|
22
|
-
private final String pluginVersion = "8.
|
|
22
|
+
private final String pluginVersion = "8.3.1";
|
|
23
23
|
|
|
24
24
|
private Map<String, DatabaseConnection> databases = new HashMap<>();
|
|
25
25
|
private SQLHTTPServer server;
|
|
@@ -51,6 +51,7 @@ public class CapgoCapacitorFastSqlPlugin extends Plugin {
|
|
|
51
51
|
JSObject ret = new JSObject();
|
|
52
52
|
ret.put("port", server.getPort());
|
|
53
53
|
ret.put("token", server.getToken());
|
|
54
|
+
ret.put("binaryProtocolVersion", 1);
|
|
54
55
|
ret.put("database", database);
|
|
55
56
|
call.resolve(ret);
|
|
56
57
|
return;
|
|
@@ -89,6 +90,7 @@ public class CapgoCapacitorFastSqlPlugin extends Plugin {
|
|
|
89
90
|
JSObject ret = new JSObject();
|
|
90
91
|
ret.put("port", server.getPort());
|
|
91
92
|
ret.put("token", server.getToken());
|
|
93
|
+
ret.put("binaryProtocolVersion", 1);
|
|
92
94
|
ret.put("database", database);
|
|
93
95
|
call.resolve(ret);
|
|
94
96
|
} catch (Exception e) {
|
|
@@ -143,6 +145,7 @@ public class CapgoCapacitorFastSqlPlugin extends Plugin {
|
|
|
143
145
|
JSObject ret = new JSObject();
|
|
144
146
|
ret.put("port", server.getPort());
|
|
145
147
|
ret.put("token", server.getToken());
|
|
148
|
+
ret.put("binaryProtocolVersion", 1);
|
|
146
149
|
call.resolve(ret);
|
|
147
150
|
}
|
|
148
151
|
|
|
@@ -256,6 +259,7 @@ public class CapgoCapacitorFastSqlPlugin extends Plugin {
|
|
|
256
259
|
public void getPluginVersion(PluginCall call) {
|
|
257
260
|
JSObject ret = new JSObject();
|
|
258
261
|
ret.put("version", pluginVersion);
|
|
262
|
+
ret.put("binaryProtocolVersion", 1);
|
|
259
263
|
call.resolve(ret);
|
|
260
264
|
}
|
|
261
265
|
|
|
@@ -6,6 +6,8 @@ import com.getcapacitor.JSObject;
|
|
|
6
6
|
public interface DatabaseConnection {
|
|
7
7
|
JSObject execute(String statement, JSArray params) throws Exception;
|
|
8
8
|
|
|
9
|
+
JSObject executeBinary(String statement, JSArray params) throws Exception;
|
|
10
|
+
|
|
9
11
|
void beginTransaction() throws Exception;
|
|
10
12
|
|
|
11
13
|
void commitTransaction() throws Exception;
|
|
@@ -56,6 +56,14 @@ public class EncryptedSQLDatabase implements DatabaseConnection {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
public JSObject execute(String statement, JSArray params) throws Exception {
|
|
59
|
+
return execute(statement, params, false);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public JSObject executeBinary(String statement, JSArray params) throws Exception {
|
|
63
|
+
return execute(statement, params, true);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private JSObject execute(String statement, JSArray params, boolean binaryResults) throws Exception {
|
|
59
67
|
if (db == null || !db.isOpen()) {
|
|
60
68
|
throw new Exception("Database is not open");
|
|
61
69
|
}
|
|
@@ -83,7 +91,7 @@ public class EncryptedSQLDatabase implements DatabaseConnection {
|
|
|
83
91
|
JSObject row = new JSObject();
|
|
84
92
|
for (int i = 0; i < cursor.getColumnCount(); i++) {
|
|
85
93
|
String columnName = cursor.getColumnName(i);
|
|
86
|
-
Object value = getColumnValue(cursor, i);
|
|
94
|
+
Object value = getColumnValue(cursor, i, binaryResults);
|
|
87
95
|
row.put(columnName, value);
|
|
88
96
|
}
|
|
89
97
|
rows.put(row);
|
|
@@ -165,6 +173,8 @@ public class EncryptedSQLDatabase implements DatabaseConnection {
|
|
|
165
173
|
|
|
166
174
|
if (value == null || value == JSONObject.NULL) {
|
|
167
175
|
stmt.bindNull(index);
|
|
176
|
+
} else if (value instanceof byte[]) {
|
|
177
|
+
stmt.bindBlob(index, (byte[]) value);
|
|
168
178
|
} else if (value instanceof String) {
|
|
169
179
|
stmt.bindString(index, (String) value);
|
|
170
180
|
} else if (value instanceof Integer) {
|
|
@@ -192,7 +202,7 @@ public class EncryptedSQLDatabase implements DatabaseConnection {
|
|
|
192
202
|
}
|
|
193
203
|
}
|
|
194
204
|
|
|
195
|
-
private Object getColumnValue(Cursor cursor, int index) {
|
|
205
|
+
private Object getColumnValue(Cursor cursor, int index, boolean binaryResults) {
|
|
196
206
|
int type = cursor.getType(index);
|
|
197
207
|
switch (type) {
|
|
198
208
|
case Cursor.FIELD_TYPE_NULL:
|
|
@@ -205,6 +215,7 @@ public class EncryptedSQLDatabase implements DatabaseConnection {
|
|
|
205
215
|
return cursor.getString(index);
|
|
206
216
|
case Cursor.FIELD_TYPE_BLOB:
|
|
207
217
|
byte[] blob = cursor.getBlob(index);
|
|
218
|
+
if (binaryResults) return blob;
|
|
208
219
|
String base64 = Base64.encodeToString(blob, Base64.NO_WRAP);
|
|
209
220
|
JSObject blobObj = new JSObject();
|
|
210
221
|
blobObj.put("_type", "binary");
|
|
@@ -41,6 +41,14 @@ public class SQLDatabase implements DatabaseConnection {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
public JSObject execute(String statement, JSArray params) throws Exception {
|
|
44
|
+
return execute(statement, params, false);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public JSObject executeBinary(String statement, JSArray params) throws Exception {
|
|
48
|
+
return execute(statement, params, true);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private JSObject execute(String statement, JSArray params, boolean binaryResults) throws Exception {
|
|
44
52
|
if (db == null || !db.isOpen()) {
|
|
45
53
|
throw new Exception("Database is not open");
|
|
46
54
|
}
|
|
@@ -69,7 +77,7 @@ public class SQLDatabase implements DatabaseConnection {
|
|
|
69
77
|
JSObject row = new JSObject();
|
|
70
78
|
for (int i = 0; i < cursor.getColumnCount(); i++) {
|
|
71
79
|
String columnName = cursor.getColumnName(i);
|
|
72
|
-
Object value = getColumnValue(cursor, i);
|
|
80
|
+
Object value = getColumnValue(cursor, i, binaryResults);
|
|
73
81
|
row.put(columnName, value);
|
|
74
82
|
}
|
|
75
83
|
rows.put(row);
|
|
@@ -155,6 +163,8 @@ public class SQLDatabase implements DatabaseConnection {
|
|
|
155
163
|
|
|
156
164
|
if (value == null || value == JSONObject.NULL) {
|
|
157
165
|
stmt.bindNull(index);
|
|
166
|
+
} else if (value instanceof byte[]) {
|
|
167
|
+
stmt.bindBlob(index, (byte[]) value);
|
|
158
168
|
} else if (value instanceof String) {
|
|
159
169
|
stmt.bindString(index, (String) value);
|
|
160
170
|
} else if (value instanceof Integer) {
|
|
@@ -183,7 +193,7 @@ public class SQLDatabase implements DatabaseConnection {
|
|
|
183
193
|
}
|
|
184
194
|
}
|
|
185
195
|
|
|
186
|
-
private Object getColumnValue(Cursor cursor, int index) {
|
|
196
|
+
private Object getColumnValue(Cursor cursor, int index, boolean binaryResults) {
|
|
187
197
|
int type = cursor.getType(index);
|
|
188
198
|
switch (type) {
|
|
189
199
|
case Cursor.FIELD_TYPE_NULL:
|
|
@@ -196,6 +206,7 @@ public class SQLDatabase implements DatabaseConnection {
|
|
|
196
206
|
return cursor.getString(index);
|
|
197
207
|
case Cursor.FIELD_TYPE_BLOB:
|
|
198
208
|
byte[] blob = cursor.getBlob(index);
|
|
209
|
+
if (binaryResults) return blob;
|
|
199
210
|
String base64 = Base64.encodeToString(blob, Base64.NO_WRAP);
|
|
200
211
|
JSObject blobObj = new JSObject();
|
|
201
212
|
blobObj.put("_type", "binary");
|