Skip to content

Commit ddfdb8c

Browse files
committed
Resultset metadata
- Feature to return resultset metadata on the low level API to help dynamic applications to analise column declared data types that can be "different" from storage datatypes, like datetime or booleans
1 parent dc5f89b commit ddfdb8c

8 files changed

Lines changed: 136 additions & 16 deletions

File tree

README.md

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,46 @@ Inspired/compatible with [react-native-sqlite-storage](https://github.com/andpor
3333

3434
```typescript
3535
interface QueryResult {
36-
status: 0 | 1; // 0 for correct execution
37-
message: string; // if status === 1, here you will find error description
38-
rows: any[];
36+
status?: 0 | 1; // 0 for correct execution
3937
insertId?: number;
38+
rowsAffected: number;
39+
message?: string;
40+
rows?: {
41+
/** Raw array with all dataset */
42+
_array: any[];
43+
/** The lengh of the dataset */
44+
length: number;
45+
/** A convenience function to acess the index based the row object
46+
* @param idx the row index
47+
* @returns the row structure identified by column names
48+
*/
49+
item: (idx: number) => any;
50+
};
51+
/**
52+
* Query metadata, avaliable only for select query results
53+
*/
54+
metadata?: ResultsetMetadata;
4055
}
4156

57+
/**
58+
* Column metadata
59+
* Describes some information about columns fetched by the query
60+
*/
61+
declare type ColumnMetadata = {
62+
/** The name used for this column for this resultset */
63+
columnName: string;
64+
/** The declared column type for this column, when fetched directly from a table or a View resulting from a table column. "UNKNOWN" for dynamic values, like function returned ones. */
65+
columnDeclaredType: string;
66+
/**
67+
* The index for this column for this resultset*/
68+
columnIndex: number;
69+
};
70+
71+
/**
72+
* Collection of columns that represents the resultset of a query
73+
*/
74+
declare type ResultsetMetadata = ColumnMetadata[];
75+
4276
interface BatchQueryResult {
4377
status?: 0 | 1;
4478
rowsAffected?: number;
@@ -116,6 +150,24 @@ if (!result.status) {
116150
}
117151
```
118152

153+
In some scenarios, dynamic applications may need to get some metadata information about the returned resultset.
154+
This can be done testing the returned data directly, but in some cases may not be enought, like when data is stored outside
155+
storage datatypes, like booleans or datetimes. When fetching data directly from tables or views linked to table columns, SQLite is able
156+
to identify the table declared types:
157+
158+
```typescript
159+
let result = sqlite.executeSql('myDatabase', 'SELECT int_column_1, bol_column_2 FROM sometable');
160+
if (!result.status) {
161+
// result.status undefined or 0 === sucess
162+
for (let i = 0; i < result.metadata.length; i++) {
163+
const column = result.metadata[i];
164+
console.log(`${column.columnName} - ${column.columnDeclaredType}`);
165+
// Output:
166+
// int_column_1 - INTEGER
167+
// bol_column_2 - BOOLEAN
168+
}
169+
}
170+
```
119171
Batch execution allows transactional execution of a set of commands
120172

121173
```typescript

cpp/JSIHelper.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ void jsiQueryArgumentsToSequelParam(jsi::Runtime &rt, jsi::Value const &params,
126126
}
127127
}
128128

129-
jsi::Value createSequelQueryExecutionResult(jsi::Runtime &rt, SQLiteOPResult status, vector<map<string, QuickValue>> *results)
129+
jsi::Value createSequelQueryExecutionResult(jsi::Runtime &rt, SQLiteOPResult status, vector<map<string, QuickValue>> *results, vector<QuickColumnMetadata> *metadata)
130130
{
131131
jsi::Object res = jsi::Object(rt);
132132
if (status.type == SQLiteOk)
@@ -183,7 +183,23 @@ jsi::Value createSequelQueryExecutionResult(jsi::Runtime &rt, SQLiteOPResult sta
183183
rows.setProperty(rt, "_array", move(array));
184184
res.setProperty(rt, "rows", move(rows));
185185
}
186-
rows.setProperty(rt, "status", jsi::Value(0));
186+
187+
if(metadata != NULL)
188+
{
189+
size_t column_count = metadata->size();
190+
auto column_array = jsi::Array(rt, column_count);
191+
for (int i = 0; i < column_count; i++) {
192+
auto column = metadata->at(i);
193+
jsi::Object column_object = jsi::Object(rt);
194+
column_object.setProperty(rt, "columnName", jsi::String::createFromUtf8(rt, column.colunmName.c_str()));
195+
column_object.setProperty(rt, "columnDeclaredType", jsi::String::createFromUtf8(rt, column.columnDeclaredType.c_str()));
196+
column_object.setProperty(rt, "columnIndex", jsi::Value(column.columnIndex));
197+
column_array.setValueAtIndex(rt, i, move(column_object));
198+
}
199+
res.setProperty(rt, "metadata", move(column_array));
200+
}
201+
202+
res.setProperty(rt, "status", jsi::Value(0));
187203
rows.setProperty(rt, "length", jsi::Value((int)rowCount));
188204
}
189205
else

cpp/JSIHelper.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,16 @@ struct SequelBatchOperationResult
8686
int commands;
8787
};
8888

89+
/**
90+
* Describe column information of a resultset
91+
*/
92+
struct QuickColumnMetadata
93+
{
94+
string colunmName;
95+
int columnIndex;
96+
string columnDeclaredType;
97+
};
98+
8999
/**
90100
* Fill the target vector with parsed parameters
91101
* */
@@ -99,6 +109,6 @@ QuickValue createIntegerQuickValue(double value);
99109
QuickValue createInt64QuickValue(long long value);
100110
QuickValue createDoubleQuickValue(double value);
101111
QuickValue createArrayBufferQuickValue(uint8_t *arrayBufferValue, size_t arrayBufferSize);
102-
jsi::Value createSequelQueryExecutionResult(jsi::Runtime &rt, SQLiteOPResult status, vector<map<string, QuickValue>> *results);
112+
jsi::Value createSequelQueryExecutionResult(jsi::Runtime &rt, SQLiteOPResult status, vector<map<string, QuickValue>> *results, vector<QuickColumnMetadata> *metadata);
103113

104114
#endif /* JSIHelper_h */

cpp/installer.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -182,10 +182,11 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
182182

183183
// Filling the results
184184
vector<map<string, QuickValue>> results;
185-
auto status = sqliteExecute(dbName, query, &params, &results);
185+
vector<QuickColumnMetadata> metadata;
186+
auto status = sqliteExecute(dbName, query, &params, &results, &metadata);
186187

187188
// Converting results into a JSI Response
188-
auto jsiResult = createSequelQueryExecutionResult(rt, status, &results);
189+
auto jsiResult = createSequelQueryExecutionResult(rt, status, &results, &metadata);
189190
return move(jsiResult);
190191
});
191192

@@ -407,12 +408,13 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
407408
{
408409
// Inside the new worker thread, we can now call sqlite operations
409410
vector<map<string, QuickValue>> results;
410-
auto status = sqliteExecute(dbName, query, params.get(), &results);
411-
invoker->invokeAsync([&rt, results = make_shared<vector<map<string, QuickValue>>>(results), status_copy = move(status), callback]
411+
vector<QuickColumnMetadata> metadata;
412+
auto status = sqliteExecute(dbName, query, params.get(), &results, &metadata);
413+
invoker->invokeAsync([&rt, results = make_shared<vector<map<string, QuickValue>>>(results), metadata = make_shared<vector<QuickColumnMetadata>>(metadata), status_copy = move(status), callback]
412414
{
413415
// Now, back into the JavaScript thread, we can translate the results
414416
// back to a JSI Object to pass on the callback
415-
auto jsiResult = createSequelQueryExecutionResult(rt, status_copy, results.get());
417+
auto jsiResult = createSequelQueryExecutionResult(rt, status_copy, results.get(), metadata.get());
416418
callback->asObject(rt).asFunction(rt).call(rt, move(jsiResult)); });
417419
}
418420
catch (std::exception &exc)

cpp/sqlbatchexecutor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ SequelBatchOperationResult executeBatch(std::string dbName, vector<QuickQueryArg
6060
for(int i = 0; i<commandCount; i++) {
6161
auto command = commands->at(i);
6262
// We do not provide a datastructure to receive query data because we don't need/want to handle this results in a batch execution
63-
auto result = sqliteExecute(dbName, command.sql, command.params.get(), NULL);
63+
auto result = sqliteExecute(dbName, command.sql, command.params.get(), NULL, NULL);
6464
if(result.type == SQLiteError)
6565
{
6666
return SequelBatchOperationResult {

cpp/sqliteBridge.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ void bindStatement(sqlite3_stmt *statement, vector<QuickValue> *values)
226226
}
227227
}
228228

229-
SQLiteOPResult sqliteExecute(string const dbName, string const &query, vector<QuickValue> *params, vector<map<string, QuickValue>> *results)
229+
SQLiteOPResult sqliteExecute(string const dbName, string const &query, vector<QuickValue> *params, vector<map<string, QuickValue>> *results, vector<QuickColumnMetadata> *metadata)
230230
{
231231
// Check if db connection is opened
232232
if (dbMap.count(dbName) == 0)
@@ -263,7 +263,7 @@ SQLiteOPResult sqliteExecute(string const dbName, string const &query, vector<Qu
263263
bool isFailed = false;
264264

265265
int result, i, count, column_type;
266-
string column_name;
266+
string column_name, column_declared_type;
267267
map<string, QuickValue> row;
268268

269269
while (isConsuming)
@@ -334,12 +334,29 @@ SQLiteOPResult sqliteExecute(string const dbName, string const &query, vector<Qu
334334
row[column_name] = createNullQuickValue();
335335
break;
336336
}
337-
338337
i++;
339338
}
340339
results->push_back(move(row));
341340
break;
342341
case SQLITE_DONE:
342+
if(metadata != NULL)
343+
{
344+
i = 0;
345+
count = sqlite3_column_count(statement);
346+
while (i < count)
347+
{
348+
column_name = sqlite3_column_name(statement, i);
349+
const char *tp = sqlite3_column_decltype(statement, i);
350+
column_declared_type = tp != NULL ? tp : "UNKNOWN";
351+
QuickColumnMetadata meta = {
352+
.colunmName = column_name,
353+
.columnIndex = i,
354+
.columnDeclaredType = column_declared_type,
355+
};
356+
metadata->push_back(meta);
357+
i++;
358+
}
359+
}
343360
isConsuming = false;
344361
break;
345362

cpp/sqliteBridge.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ SQLiteOPResult sqliteRemoveDb(string const dbName, string const docPath);
2121

2222
// SequelResult sequel_attach(string const &dbName);
2323

24-
SQLiteOPResult sqliteExecute(string const dbName, string const &query, vector<QuickValue> *values, vector<map<string, QuickValue>> *result);
24+
SQLiteOPResult sqliteExecute(string const dbName, string const &query, vector<QuickValue> *values, vector<map<string, QuickValue>> *result, vector<QuickColumnMetadata> *metadata);
2525

2626
SequelLiteralUpdateResult sqliteExecuteLiteral(string const dbName, string const &query);

src/index.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,31 @@ interface QueryResult {
4040
*/
4141
item: (idx: number) => any;
4242
};
43+
/**
44+
* Query metadata, avaliable only for select query results
45+
*/
46+
metadata?: ResultsetMetadata;
4347
}
4448

49+
/**
50+
* Column metadata
51+
* Describes some information about columns fetched by the query
52+
*/
53+
declare type ColumnMetadata = {
54+
/** The name used for this column for this resultset */
55+
columnName: string;
56+
/** The declared column type for this column, when fetched directly from a table or a View resulting from a table column. "UNKNOWN" for dynamic values, like function returned ones. */
57+
columnDeclaredType: string;
58+
/**
59+
* The index for this column for this resultset*/
60+
columnIndex: number;
61+
};
62+
63+
/**
64+
* Collection of columns that represents the resultset of a query
65+
*/
66+
declare type ResultsetMetadata = ColumnMetadata[];
67+
4568
/**
4669
* Allows the execution of bulk of sql commands
4770
* inside a transaction

0 commit comments

Comments
 (0)