Skip to content

Commit 90c95b7

Browse files
author
Oscar Franco
authored
Merge pull request #38 from EduFrazao/resultset_metadata
Resultset metadata
2 parents dc5f89b + 757e2c2 commit 90c95b7

File tree

8 files changed

+121
-16
lines changed

8 files changed

+121
-16
lines changed

README.md

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,36 @@ 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+
};
46+
/**
47+
* Query metadata, avaliable only for select query results
48+
*/
49+
metadata?: ColumnMetadata[];
4050
}
4151

52+
/**
53+
* Column metadata
54+
* Describes some information about columns fetched by the query
55+
*/
56+
declare type ColumnMetadata = {
57+
/** The name used for this column for this resultset */
58+
columnName: string;
59+
/** 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. */
60+
columnDeclaredType: string;
61+
/**
62+
* The index for this column for this resultset*/
63+
columnIndex: number;
64+
};
65+
4266
interface BatchQueryResult {
4367
status?: 0 | 1;
4468
rowsAffected?: number;
@@ -116,6 +140,24 @@ if (!result.status) {
116140
}
117141
```
118142

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

121163
```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: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,26 @@ interface QueryResult {
4040
*/
4141
item: (idx: number) => any;
4242
};
43+
/**
44+
* Query metadata, avaliable only for select query results
45+
*/
46+
metadata?: ColumnMetadata[];
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+
4563
/**
4664
* Allows the execution of bulk of sql commands
4765
* inside a transaction

0 commit comments

Comments
 (0)