@capgo/capacitor-fast-sql 7.0.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/CapgoCapacitorFastSql.podspec +18 -0
- package/Package.swift +30 -0
- package/README.md +340 -0
- package/android/build.gradle +60 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/app/capgo/capacitor/fastsql/CapgoCapacitorFastSqlPlugin.java +220 -0
- package/android/src/main/java/app/capgo/capacitor/fastsql/SQLDatabase.java +231 -0
- package/android/src/main/java/app/capgo/capacitor/fastsql/SQLHTTPServer.java +229 -0
- package/dist/esm/definitions.d.ts +225 -0
- package/dist/esm/definitions.d.ts.map +1 -0
- package/dist/esm/definitions.js +10 -0
- package/dist/esm/helpers.d.ts +7 -0
- package/dist/esm/helpers.d.ts.map +1 -0
- package/dist/esm/helpers.js +6 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/native-sql.d.ts +42 -0
- package/dist/esm/native-sql.d.ts.map +1 -0
- package/dist/esm/native-sql.js +69 -0
- package/dist/esm/plugin.d.ts +3 -0
- package/dist/esm/plugin.d.ts.map +1 -0
- package/dist/esm/plugin.js +4 -0
- package/dist/esm/sql-connection.d.ts +94 -0
- package/dist/esm/sql-connection.d.ts.map +1 -0
- package/dist/esm/sql-connection.js +246 -0
- package/dist/esm/web.d.ts +67 -0
- package/dist/esm/web.d.ts.map +1 -0
- package/dist/esm/web.js +215 -0
- package/dist/plugin.cjs.js +557 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +560 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/CapgoCapacitorFastSqlPlugin/CapgoCapacitorFastSqlPlugin.swift +202 -0
- package/ios/Sources/CapgoCapacitorFastSqlPlugin/SQLDatabase.swift +215 -0
- package/ios/Sources/CapgoCapacitorFastSqlPlugin/SQLHTTPServer.swift +311 -0
- package/package.json +90 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
package app.capgo.capacitor.fastsql;
|
|
2
|
+
|
|
3
|
+
import android.database.Cursor;
|
|
4
|
+
import android.database.sqlite.SQLiteDatabase;
|
|
5
|
+
import android.database.sqlite.SQLiteStatement;
|
|
6
|
+
import android.util.Base64;
|
|
7
|
+
|
|
8
|
+
import com.getcapacitor.JSArray;
|
|
9
|
+
import com.getcapacitor.JSObject;
|
|
10
|
+
|
|
11
|
+
import org.json.JSONArray;
|
|
12
|
+
import org.json.JSONException;
|
|
13
|
+
import org.json.JSONObject;
|
|
14
|
+
|
|
15
|
+
import java.util.ArrayList;
|
|
16
|
+
import java.util.List;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* SQLite database wrapper for Android
|
|
20
|
+
*/
|
|
21
|
+
public class SQLDatabase {
|
|
22
|
+
private SQLiteDatabase db;
|
|
23
|
+
private boolean inTransaction = false;
|
|
24
|
+
|
|
25
|
+
public SQLDatabase(String path) {
|
|
26
|
+
this.db = SQLiteDatabase.openOrCreateDatabase(path, null);
|
|
27
|
+
// Enable foreign keys
|
|
28
|
+
db.execSQL("PRAGMA foreign_keys = ON");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public void close() {
|
|
32
|
+
if (db != null && db.isOpen()) {
|
|
33
|
+
db.close();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public JSObject execute(String statement, JSArray params) throws Exception {
|
|
38
|
+
if (db == null || !db.isOpen()) {
|
|
39
|
+
throw new Exception("Database is not open");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check if this is a query (SELECT) or a modification (INSERT/UPDATE/DELETE)
|
|
43
|
+
String trimmedStatement = statement.trim().toUpperCase();
|
|
44
|
+
boolean isQuery = trimmedStatement.startsWith("SELECT") ||
|
|
45
|
+
trimmedStatement.startsWith("PRAGMA") ||
|
|
46
|
+
trimmedStatement.startsWith("EXPLAIN");
|
|
47
|
+
|
|
48
|
+
if (isQuery) {
|
|
49
|
+
return executeQuery(statement, params);
|
|
50
|
+
} else {
|
|
51
|
+
return executeUpdate(statement, params);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private JSObject executeQuery(String statement, JSArray params) throws Exception {
|
|
56
|
+
Cursor cursor = null;
|
|
57
|
+
try {
|
|
58
|
+
// Prepare statement with parameters
|
|
59
|
+
String[] bindArgs = convertParamsToStringArray(params);
|
|
60
|
+
cursor = db.rawQuery(statement, bindArgs);
|
|
61
|
+
|
|
62
|
+
// Build result
|
|
63
|
+
JSArray rows = new JSArray();
|
|
64
|
+
while (cursor.moveToNext()) {
|
|
65
|
+
JSObject row = new JSObject();
|
|
66
|
+
for (int i = 0; i < cursor.getColumnCount(); i++) {
|
|
67
|
+
String columnName = cursor.getColumnName(i);
|
|
68
|
+
Object value = getColumnValue(cursor, i);
|
|
69
|
+
row.put(columnName, value);
|
|
70
|
+
}
|
|
71
|
+
rows.put(row);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
JSObject result = new JSObject();
|
|
75
|
+
result.put("rows", rows);
|
|
76
|
+
result.put("rowsAffected", 0);
|
|
77
|
+
return result;
|
|
78
|
+
} finally {
|
|
79
|
+
if (cursor != null) {
|
|
80
|
+
cursor.close();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private JSObject executeUpdate(String statement, JSArray params) throws Exception {
|
|
86
|
+
SQLiteStatement stmt = null;
|
|
87
|
+
try {
|
|
88
|
+
stmt = db.compileStatement(statement);
|
|
89
|
+
|
|
90
|
+
// Bind parameters
|
|
91
|
+
bindParams(stmt, params);
|
|
92
|
+
|
|
93
|
+
// Execute
|
|
94
|
+
long result;
|
|
95
|
+
if (statement.trim().toUpperCase().startsWith("INSERT")) {
|
|
96
|
+
result = stmt.executeInsert();
|
|
97
|
+
} else {
|
|
98
|
+
stmt.execute();
|
|
99
|
+
result = -1;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Get affected rows
|
|
103
|
+
int changes = (int) db.compileStatement("SELECT changes()").simpleQueryForLong();
|
|
104
|
+
|
|
105
|
+
JSObject ret = new JSObject();
|
|
106
|
+
ret.put("rows", new JSArray());
|
|
107
|
+
ret.put("rowsAffected", changes);
|
|
108
|
+
if (result > 0) {
|
|
109
|
+
ret.put("insertId", result);
|
|
110
|
+
}
|
|
111
|
+
return ret;
|
|
112
|
+
} finally {
|
|
113
|
+
if (stmt != null) {
|
|
114
|
+
stmt.close();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public void beginTransaction() throws Exception {
|
|
120
|
+
if (inTransaction) {
|
|
121
|
+
throw new Exception("Transaction already active");
|
|
122
|
+
}
|
|
123
|
+
db.beginTransaction();
|
|
124
|
+
inTransaction = true;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
public void commitTransaction() throws Exception {
|
|
128
|
+
if (!inTransaction) {
|
|
129
|
+
throw new Exception("No transaction active");
|
|
130
|
+
}
|
|
131
|
+
db.setTransactionSuccessful();
|
|
132
|
+
db.endTransaction();
|
|
133
|
+
inTransaction = false;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
public void rollbackTransaction() throws Exception {
|
|
137
|
+
if (!inTransaction) {
|
|
138
|
+
throw new Exception("No transaction active");
|
|
139
|
+
}
|
|
140
|
+
db.endTransaction();
|
|
141
|
+
inTransaction = false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private String[] convertParamsToStringArray(JSArray params) throws JSONException {
|
|
145
|
+
if (params == null || params.length() == 0) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
List<String> args = new ArrayList<>();
|
|
150
|
+
for (int i = 0; i < params.length(); i++) {
|
|
151
|
+
Object value = params.get(i);
|
|
152
|
+
if (value == null || value == JSONObject.NULL) {
|
|
153
|
+
args.add(null);
|
|
154
|
+
} else if (value instanceof JSONObject) {
|
|
155
|
+
JSONObject obj = (JSONObject) value;
|
|
156
|
+
if (obj.has("_type") && "binary".equals(obj.getString("_type"))) {
|
|
157
|
+
// For queries, we can't bind binary data as string
|
|
158
|
+
// This is a limitation - for binary data, use executeUpdate
|
|
159
|
+
args.add(obj.getString("_data"));
|
|
160
|
+
} else {
|
|
161
|
+
args.add(value.toString());
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
args.add(value.toString());
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return args.toArray(new String[0]);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private void bindParams(SQLiteStatement stmt, JSArray params) throws Exception {
|
|
171
|
+
if (params == null || params.length() == 0) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
for (int i = 0; i < params.length(); i++) {
|
|
176
|
+
Object value = params.get(i);
|
|
177
|
+
int index = i + 1; // SQLite parameters are 1-indexed
|
|
178
|
+
|
|
179
|
+
if (value == null || value == JSONObject.NULL) {
|
|
180
|
+
stmt.bindNull(index);
|
|
181
|
+
} else if (value instanceof String) {
|
|
182
|
+
stmt.bindString(index, (String) value);
|
|
183
|
+
} else if (value instanceof Integer) {
|
|
184
|
+
stmt.bindLong(index, ((Integer) value).longValue());
|
|
185
|
+
} else if (value instanceof Long) {
|
|
186
|
+
stmt.bindLong(index, (Long) value);
|
|
187
|
+
} else if (value instanceof Double) {
|
|
188
|
+
stmt.bindDouble(index, (Double) value);
|
|
189
|
+
} else if (value instanceof Float) {
|
|
190
|
+
stmt.bindDouble(index, ((Float) value).doubleValue());
|
|
191
|
+
} else if (value instanceof Boolean) {
|
|
192
|
+
stmt.bindLong(index, ((Boolean) value) ? 1 : 0);
|
|
193
|
+
} else if (value instanceof JSONObject) {
|
|
194
|
+
JSONObject obj = (JSONObject) value;
|
|
195
|
+
if (obj.has("_type") && "binary".equals(obj.getString("_type"))) {
|
|
196
|
+
// Handle binary data
|
|
197
|
+
String base64 = obj.getString("_data");
|
|
198
|
+
byte[] bytes = Base64.decode(base64, Base64.DEFAULT);
|
|
199
|
+
stmt.bindBlob(index, bytes);
|
|
200
|
+
} else {
|
|
201
|
+
stmt.bindString(index, value.toString());
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
stmt.bindString(index, value.toString());
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
private Object getColumnValue(Cursor cursor, int index) {
|
|
210
|
+
int type = cursor.getType(index);
|
|
211
|
+
switch (type) {
|
|
212
|
+
case Cursor.FIELD_TYPE_NULL:
|
|
213
|
+
return JSONObject.NULL;
|
|
214
|
+
case Cursor.FIELD_TYPE_INTEGER:
|
|
215
|
+
return cursor.getLong(index);
|
|
216
|
+
case Cursor.FIELD_TYPE_FLOAT:
|
|
217
|
+
return cursor.getDouble(index);
|
|
218
|
+
case Cursor.FIELD_TYPE_STRING:
|
|
219
|
+
return cursor.getString(index);
|
|
220
|
+
case Cursor.FIELD_TYPE_BLOB:
|
|
221
|
+
byte[] blob = cursor.getBlob(index);
|
|
222
|
+
String base64 = Base64.encodeToString(blob, Base64.NO_WRAP);
|
|
223
|
+
JSObject blobObj = new JSObject();
|
|
224
|
+
blobObj.put("_type", "binary");
|
|
225
|
+
blobObj.put("_data", base64);
|
|
226
|
+
return blobObj;
|
|
227
|
+
default:
|
|
228
|
+
return JSONObject.NULL;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
package app.capgo.capacitor.fastsql;
|
|
2
|
+
|
|
3
|
+
import com.getcapacitor.JSArray;
|
|
4
|
+
import com.getcapacitor.JSObject;
|
|
5
|
+
import com.google.gson.Gson;
|
|
6
|
+
import com.google.gson.JsonArray;
|
|
7
|
+
import com.google.gson.JsonElement;
|
|
8
|
+
import com.google.gson.JsonObject;
|
|
9
|
+
import com.google.gson.JsonParser;
|
|
10
|
+
|
|
11
|
+
import org.json.JSONArray;
|
|
12
|
+
import org.json.JSONObject;
|
|
13
|
+
|
|
14
|
+
import java.io.IOException;
|
|
15
|
+
import java.security.SecureRandom;
|
|
16
|
+
import java.util.Map;
|
|
17
|
+
|
|
18
|
+
import fi.iki.elonen.NanoHTTPD; // Note: org.nanohttpd:nanohttpd:2.3.1 still uses fi.iki.elonen package
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* HTTP server for efficient SQL operations
|
|
22
|
+
*
|
|
23
|
+
* This server handles direct HTTP requests from JavaScript, bypassing Capacitor's
|
|
24
|
+
* bridge for better performance with large datasets and sync operations.
|
|
25
|
+
*/
|
|
26
|
+
public class SQLHTTPServer extends NanoHTTPD {
|
|
27
|
+
private final String token;
|
|
28
|
+
private final Map<String, SQLDatabase> databases;
|
|
29
|
+
private final Gson gson = new Gson();
|
|
30
|
+
|
|
31
|
+
public SQLHTTPServer(Map<String, SQLDatabase> databases) throws IOException {
|
|
32
|
+
super(findAvailablePort());
|
|
33
|
+
this.databases = databases;
|
|
34
|
+
this.token = generateToken();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public String getToken() {
|
|
38
|
+
return token;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public int getPort() {
|
|
42
|
+
return getListeningPort();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@Override
|
|
46
|
+
public Response serve(IHTTPSession session) {
|
|
47
|
+
// Check authentication
|
|
48
|
+
String authHeader = session.getHeaders().get("authorization");
|
|
49
|
+
if (authHeader == null || !authHeader.equals("Bearer " + token)) {
|
|
50
|
+
return newFixedLengthResponse(Response.Status.UNAUTHORIZED, "text/plain", "Unauthorized");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Get database name
|
|
54
|
+
String database = session.getHeaders().get("x-database");
|
|
55
|
+
if (database == null) {
|
|
56
|
+
return newFixedLengthResponse(Response.Status.BAD_REQUEST, "text/plain", "Database header required");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Check if database exists
|
|
60
|
+
SQLDatabase db = databases.get(database);
|
|
61
|
+
if (db == null) {
|
|
62
|
+
return newFixedLengthResponse(Response.Status.NOT_FOUND, "text/plain", "Database not found");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
String uri = session.getUri();
|
|
66
|
+
Method method = session.getMethod();
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
if (method == Method.POST && uri.equals("/execute")) {
|
|
70
|
+
return handleExecute(session, db);
|
|
71
|
+
} else if (method == Method.POST && uri.equals("/batch")) {
|
|
72
|
+
return handleBatch(session, db);
|
|
73
|
+
} else if (method == Method.POST && uri.equals("/transaction/begin")) {
|
|
74
|
+
return handleBeginTransaction(db);
|
|
75
|
+
} else if (method == Method.POST && uri.equals("/transaction/commit")) {
|
|
76
|
+
return handleCommitTransaction(db);
|
|
77
|
+
} else if (method == Method.POST && uri.equals("/transaction/rollback")) {
|
|
78
|
+
return handleRollbackTransaction(db);
|
|
79
|
+
} else {
|
|
80
|
+
return newFixedLengthResponse(Response.Status.NOT_FOUND, "text/plain", "Endpoint not found");
|
|
81
|
+
}
|
|
82
|
+
} catch (Exception e) {
|
|
83
|
+
return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, "text/plain",
|
|
84
|
+
"Error: " + e.getMessage());
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private Response handleExecute(IHTTPSession session, SQLDatabase db) throws Exception {
|
|
89
|
+
// Read request body
|
|
90
|
+
String body = readRequestBody(session);
|
|
91
|
+
JsonObject request = JsonParser.parseString(body).getAsJsonObject();
|
|
92
|
+
|
|
93
|
+
String statement = request.get("statement").getAsString();
|
|
94
|
+
JsonArray paramsJson = request.has("params") ? request.getAsJsonArray("params") : new JsonArray();
|
|
95
|
+
|
|
96
|
+
// Convert to JSArray
|
|
97
|
+
JSArray params = new JSArray();
|
|
98
|
+
for (JsonElement param : paramsJson) {
|
|
99
|
+
if (param.isJsonNull()) {
|
|
100
|
+
params.put(JSONObject.NULL);
|
|
101
|
+
} else if (param.isJsonPrimitive()) {
|
|
102
|
+
if (param.getAsJsonPrimitive().isNumber()) {
|
|
103
|
+
try {
|
|
104
|
+
params.put(param.getAsLong());
|
|
105
|
+
} catch (NumberFormatException e) {
|
|
106
|
+
params.put(param.getAsDouble());
|
|
107
|
+
}
|
|
108
|
+
} else if (param.getAsJsonPrimitive().isBoolean()) {
|
|
109
|
+
params.put(param.getAsBoolean());
|
|
110
|
+
} else {
|
|
111
|
+
params.put(param.getAsString());
|
|
112
|
+
}
|
|
113
|
+
} else if (param.isJsonObject()) {
|
|
114
|
+
JsonObject obj = param.getAsJsonObject();
|
|
115
|
+
JSONObject jsonObj = new JSONObject();
|
|
116
|
+
for (String key : obj.keySet()) {
|
|
117
|
+
jsonObj.put(key, obj.get(key).getAsString());
|
|
118
|
+
}
|
|
119
|
+
params.put(jsonObj);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Execute query
|
|
124
|
+
JSObject result = db.execute(statement, params);
|
|
125
|
+
|
|
126
|
+
// Convert result to JSON
|
|
127
|
+
String resultJson = result.toString();
|
|
128
|
+
return newFixedLengthResponse(Response.Status.OK, "application/json", resultJson);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private Response handleBatch(IHTTPSession session, SQLDatabase db) throws Exception {
|
|
132
|
+
// Read request body
|
|
133
|
+
String body = readRequestBody(session);
|
|
134
|
+
JsonObject request = JsonParser.parseString(body).getAsJsonObject();
|
|
135
|
+
JsonArray operations = request.getAsJsonArray("operations");
|
|
136
|
+
|
|
137
|
+
JSONArray results = new JSONArray();
|
|
138
|
+
for (JsonElement opElement : operations) {
|
|
139
|
+
JsonObject operation = opElement.getAsJsonObject();
|
|
140
|
+
String statement = operation.get("statement").getAsString();
|
|
141
|
+
JsonArray paramsJson = operation.has("params") ? operation.getAsJsonArray("params") : new JsonArray();
|
|
142
|
+
|
|
143
|
+
// Convert to JSArray
|
|
144
|
+
JSArray params = new JSArray();
|
|
145
|
+
for (JsonElement param : paramsJson) {
|
|
146
|
+
if (param.isJsonNull()) {
|
|
147
|
+
params.put(JSONObject.NULL);
|
|
148
|
+
} else if (param.isJsonPrimitive()) {
|
|
149
|
+
if (param.getAsJsonPrimitive().isNumber()) {
|
|
150
|
+
try {
|
|
151
|
+
params.put(param.getAsLong());
|
|
152
|
+
} catch (NumberFormatException e) {
|
|
153
|
+
params.put(param.getAsDouble());
|
|
154
|
+
}
|
|
155
|
+
} else if (param.getAsJsonPrimitive().isBoolean()) {
|
|
156
|
+
params.put(param.getAsBoolean());
|
|
157
|
+
} else {
|
|
158
|
+
params.put(param.getAsString());
|
|
159
|
+
}
|
|
160
|
+
} else if (param.isJsonObject()) {
|
|
161
|
+
JsonObject obj = param.getAsJsonObject();
|
|
162
|
+
JSONObject jsonObj = new JSONObject();
|
|
163
|
+
for (String key : obj.keySet()) {
|
|
164
|
+
jsonObj.put(key, obj.get(key).getAsString());
|
|
165
|
+
}
|
|
166
|
+
params.put(jsonObj);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
JSObject result = db.execute(statement, params);
|
|
171
|
+
results.put(new JSONObject(result.toString()));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return newFixedLengthResponse(Response.Status.OK, "application/json", results.toString());
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private Response handleBeginTransaction(SQLDatabase db) throws Exception {
|
|
178
|
+
db.beginTransaction();
|
|
179
|
+
return newFixedLengthResponse(Response.Status.OK, "application/json", "{}");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private Response handleCommitTransaction(SQLDatabase db) throws Exception {
|
|
183
|
+
db.commitTransaction();
|
|
184
|
+
return newFixedLengthResponse(Response.Status.OK, "application/json", "{}");
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private Response handleRollbackTransaction(SQLDatabase db) throws Exception {
|
|
188
|
+
db.rollbackTransaction();
|
|
189
|
+
return newFixedLengthResponse(Response.Status.OK, "application/json", "{}");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private String readRequestBody(IHTTPSession session) throws IOException {
|
|
193
|
+
int contentLength = Integer.parseInt(session.getHeaders().get("content-length"));
|
|
194
|
+
byte[] buffer = new byte[contentLength];
|
|
195
|
+
session.getInputStream().read(buffer, 0, contentLength);
|
|
196
|
+
return new String(buffer);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private static String generateToken() {
|
|
200
|
+
SecureRandom random = new SecureRandom();
|
|
201
|
+
byte[] bytes = new byte[32];
|
|
202
|
+
random.nextBytes(bytes);
|
|
203
|
+
StringBuilder sb = new StringBuilder();
|
|
204
|
+
for (byte b : bytes) {
|
|
205
|
+
sb.append(String.format("%02x", b));
|
|
206
|
+
}
|
|
207
|
+
return sb.toString();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private static int findAvailablePort() {
|
|
211
|
+
// Try ports in the range 9000-9100
|
|
212
|
+
for (int port = 9000; port < 9100; port++) {
|
|
213
|
+
try {
|
|
214
|
+
NanoHTTPD testServer = new NanoHTTPD(port) {
|
|
215
|
+
@Override
|
|
216
|
+
public Response serve(IHTTPSession session) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
testServer.start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
|
|
221
|
+
testServer.stop();
|
|
222
|
+
return port;
|
|
223
|
+
} catch (IOException e) {
|
|
224
|
+
// Port not available, try next one
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return 9000; // Fallback
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQL value types supported by the plugin
|
|
3
|
+
*/
|
|
4
|
+
export type SQLValue = string | number | boolean | null | Uint8Array;
|
|
5
|
+
/**
|
|
6
|
+
* SQL row result - values indexed by column name
|
|
7
|
+
*/
|
|
8
|
+
export interface SQLRow {
|
|
9
|
+
[column: string]: SQLValue;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Result of a SQL query execution
|
|
13
|
+
*/
|
|
14
|
+
export interface SQLResult {
|
|
15
|
+
/**
|
|
16
|
+
* Rows returned by the query (for SELECT statements)
|
|
17
|
+
*/
|
|
18
|
+
rows: SQLRow[];
|
|
19
|
+
/**
|
|
20
|
+
* Number of rows affected by the query (for INSERT/UPDATE/DELETE)
|
|
21
|
+
*/
|
|
22
|
+
rowsAffected: number;
|
|
23
|
+
/**
|
|
24
|
+
* ID of the last inserted row (for INSERT statements with auto-increment)
|
|
25
|
+
*/
|
|
26
|
+
insertId?: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Batch SQL operation
|
|
30
|
+
*/
|
|
31
|
+
export interface SQLBatchOperation {
|
|
32
|
+
/**
|
|
33
|
+
* SQL statement to execute
|
|
34
|
+
*/
|
|
35
|
+
statement: string;
|
|
36
|
+
/**
|
|
37
|
+
* Parameters to bind to the statement
|
|
38
|
+
*/
|
|
39
|
+
params?: SQLValue[];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Database connection options
|
|
43
|
+
*/
|
|
44
|
+
export interface SQLConnectionOptions {
|
|
45
|
+
/**
|
|
46
|
+
* Database name (file will be created in app data directory)
|
|
47
|
+
*/
|
|
48
|
+
database: string;
|
|
49
|
+
/**
|
|
50
|
+
* Enable encryption (iOS/Android only)
|
|
51
|
+
*/
|
|
52
|
+
encrypted?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Encryption key (required if encrypted is true)
|
|
55
|
+
*/
|
|
56
|
+
encryptionKey?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Read-only mode
|
|
59
|
+
*/
|
|
60
|
+
readOnly?: boolean;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Transaction isolation levels
|
|
64
|
+
*/
|
|
65
|
+
export declare enum IsolationLevel {
|
|
66
|
+
ReadUncommitted = "READ UNCOMMITTED",
|
|
67
|
+
ReadCommitted = "READ COMMITTED",
|
|
68
|
+
RepeatableRead = "REPEATABLE READ",
|
|
69
|
+
Serializable = "SERIALIZABLE"
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Native SQL Plugin for high-performance SQLite database access.
|
|
73
|
+
*
|
|
74
|
+
* This plugin uses a custom HTTP-based protocol for efficient data transfer,
|
|
75
|
+
* bypassing Capacitor's standard bridge for better performance with sync operations.
|
|
76
|
+
*
|
|
77
|
+
* @since 0.0.1
|
|
78
|
+
*/
|
|
79
|
+
export interface CapgoCapacitorNativeSqlPlugin {
|
|
80
|
+
/**
|
|
81
|
+
* Initialize the database connection and start the HTTP server.
|
|
82
|
+
*
|
|
83
|
+
* @param options - Connection options
|
|
84
|
+
* @returns Connection information including server port and auth token
|
|
85
|
+
* @throws Error if connection fails
|
|
86
|
+
* @since 0.0.1
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* const conn = await CapgoCapacitorNativeSql.connect({ database: 'myapp' });
|
|
90
|
+
* console.log('Connected on port:', conn.port);
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
connect(options: SQLConnectionOptions): Promise<{
|
|
94
|
+
port: number;
|
|
95
|
+
token: string;
|
|
96
|
+
database: string;
|
|
97
|
+
}>;
|
|
98
|
+
/**
|
|
99
|
+
* Close database connection and stop the HTTP server.
|
|
100
|
+
*
|
|
101
|
+
* @param options - Database name to close
|
|
102
|
+
* @returns Promise that resolves when disconnected
|
|
103
|
+
* @throws Error if database is not connected or disconnect fails
|
|
104
|
+
* @since 0.0.1
|
|
105
|
+
* @example
|
|
106
|
+
* ```typescript
|
|
107
|
+
* await CapgoCapacitorNativeSql.disconnect({ database: 'myapp' });
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
disconnect(options: {
|
|
111
|
+
database: string;
|
|
112
|
+
}): Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* Get the HTTP server port and token for direct communication.
|
|
115
|
+
*
|
|
116
|
+
* @param options - Database name
|
|
117
|
+
* @returns Server port and auth token
|
|
118
|
+
* @throws Error if database is not connected
|
|
119
|
+
* @since 0.0.1
|
|
120
|
+
* @example
|
|
121
|
+
* ```typescript
|
|
122
|
+
* const info = await CapgoCapacitorNativeSql.getServerInfo({ database: 'myapp' });
|
|
123
|
+
* console.log('Server port:', info.port);
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
getServerInfo(options: {
|
|
127
|
+
database: string;
|
|
128
|
+
}): Promise<{
|
|
129
|
+
port: number;
|
|
130
|
+
token: string;
|
|
131
|
+
}>;
|
|
132
|
+
/**
|
|
133
|
+
* Execute a SQL query via Capacitor bridge (for simple queries).
|
|
134
|
+
* For better performance with large datasets, use the HTTP protocol directly via SQLConnection class.
|
|
135
|
+
*
|
|
136
|
+
* @param options - Query parameters
|
|
137
|
+
* @returns Query results
|
|
138
|
+
* @throws Error if execution fails
|
|
139
|
+
* @since 0.0.1
|
|
140
|
+
* @example
|
|
141
|
+
* ```typescript
|
|
142
|
+
* const result = await CapgoCapacitorNativeSql.execute({
|
|
143
|
+
* database: 'myapp',
|
|
144
|
+
* statement: 'SELECT * FROM users WHERE age > ?',
|
|
145
|
+
* params: [18]
|
|
146
|
+
* });
|
|
147
|
+
* console.log('Rows:', result.rows);
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
execute(options: {
|
|
151
|
+
database: string;
|
|
152
|
+
statement: string;
|
|
153
|
+
params?: SQLValue[];
|
|
154
|
+
}): Promise<SQLResult>;
|
|
155
|
+
/**
|
|
156
|
+
* Begin a database transaction.
|
|
157
|
+
*
|
|
158
|
+
* @param options - Transaction options
|
|
159
|
+
* @returns Promise that resolves when transaction begins
|
|
160
|
+
* @throws Error if transaction fails to start
|
|
161
|
+
* @since 0.0.1
|
|
162
|
+
* @example
|
|
163
|
+
* ```typescript
|
|
164
|
+
* await CapgoCapacitorNativeSql.beginTransaction({ database: 'myapp' });
|
|
165
|
+
* // Execute multiple operations
|
|
166
|
+
* await CapgoCapacitorNativeSql.commitTransaction({ database: 'myapp' });
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
beginTransaction(options: {
|
|
170
|
+
database: string;
|
|
171
|
+
isolationLevel?: IsolationLevel;
|
|
172
|
+
}): Promise<void>;
|
|
173
|
+
/**
|
|
174
|
+
* Commit the current transaction.
|
|
175
|
+
*
|
|
176
|
+
* @param options - Database name
|
|
177
|
+
* @returns Promise that resolves when transaction is committed
|
|
178
|
+
* @throws Error if no transaction is active or commit fails
|
|
179
|
+
* @since 0.0.1
|
|
180
|
+
* @example
|
|
181
|
+
* ```typescript
|
|
182
|
+
* await CapgoCapacitorNativeSql.commitTransaction({ database: 'myapp' });
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
commitTransaction(options: {
|
|
186
|
+
database: string;
|
|
187
|
+
}): Promise<void>;
|
|
188
|
+
/**
|
|
189
|
+
* Rollback the current transaction.
|
|
190
|
+
*
|
|
191
|
+
* @param options - Database name
|
|
192
|
+
* @returns Promise that resolves when transaction is rolled back
|
|
193
|
+
* @throws Error if no transaction is active or rollback fails
|
|
194
|
+
* @since 0.0.1
|
|
195
|
+
* @example
|
|
196
|
+
* ```typescript
|
|
197
|
+
* try {
|
|
198
|
+
* await CapgoCapacitorNativeSql.beginTransaction({ database: 'myapp' });
|
|
199
|
+
* // Operations...
|
|
200
|
+
* await CapgoCapacitorNativeSql.commitTransaction({ database: 'myapp' });
|
|
201
|
+
* } catch (error) {
|
|
202
|
+
* await CapgoCapacitorNativeSql.rollbackTransaction({ database: 'myapp' });
|
|
203
|
+
* }
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
rollbackTransaction(options: {
|
|
207
|
+
database: string;
|
|
208
|
+
}): Promise<void>;
|
|
209
|
+
/**
|
|
210
|
+
* Get the native Capacitor plugin version.
|
|
211
|
+
*
|
|
212
|
+
* @returns Promise that resolves with the plugin version
|
|
213
|
+
* @throws Error if getting the version fails
|
|
214
|
+
* @since 0.0.1
|
|
215
|
+
* @example
|
|
216
|
+
* ```typescript
|
|
217
|
+
* const { version } = await CapgoCapacitorNativeSql.getPluginVersion();
|
|
218
|
+
* console.log('Plugin version:', version);
|
|
219
|
+
* ```
|
|
220
|
+
*/
|
|
221
|
+
getPluginVersion(): Promise<{
|
|
222
|
+
version: string;
|
|
223
|
+
}>;
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=definitions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,UAAU,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB;;OAEG;IACH,IAAI,EAAE,MAAM,EAAE,CAAC;IAEf;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,oBAAY,cAAc;IACxB,eAAe,qBAAqB;IACpC,aAAa,mBAAmB;IAChC,cAAc,oBAAoB;IAClC,YAAY,iBAAiB;CAC9B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,6BAA6B;IAC5C;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC;QAC9C,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IAEH;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzD;;;;;;;;;;;;OAYG;IACH,aAAa,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QACpD,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;IAEH;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,OAAO,EAAE;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;KACrB,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAEvB;;;;;;;;;;;;;OAaG;IACH,gBAAgB,CAAC,OAAO,EAAE;QACxB,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,CAAC,EAAE,cAAc,CAAC;KACjC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElB;;;;;;;;;;;OAWG;IACH,iBAAiB,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhE;;;;;;;;;;;;;;;;;OAiBG;IACH,mBAAmB,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElE;;;;;;;;;;;OAWG;IACH,gBAAgB,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transaction isolation levels
|
|
3
|
+
*/
|
|
4
|
+
export var IsolationLevel;
|
|
5
|
+
(function (IsolationLevel) {
|
|
6
|
+
IsolationLevel["ReadUncommitted"] = "READ UNCOMMITTED";
|
|
7
|
+
IsolationLevel["ReadCommitted"] = "READ COMMITTED";
|
|
8
|
+
IsolationLevel["RepeatableRead"] = "REPEATABLE READ";
|
|
9
|
+
IsolationLevel["Serializable"] = "SERIALIZABLE";
|
|
10
|
+
})(IsolationLevel || (IsolationLevel = {}));
|