Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 45 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,36 @@ Inspired/compatible with [react-native-sqlite-storage](https://github.com/andpor

```typescript
interface QueryResult {
status: 0 | 1; // 0 for correct execution
message: string; // if status === 1, here you will find error description
rows: any[];
status?: 0 | 1; // 0 for correct execution
insertId?: number;
rowsAffected: number;
message?: string;
rows?: {
/** Raw array with all dataset */
_array: any[];
/** The lengh of the dataset */
length: number;
};
/**
* Query metadata, avaliable only for select query results
*/
metadata?: ColumnMetadata[];
}

/**
* Column metadata
* Describes some information about columns fetched by the query
*/
declare type ColumnMetadata = {
/** The name used for this column for this resultset */
columnName: string;
/** 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. */
columnDeclaredType: string;
/**
* The index for this column for this resultset*/
columnIndex: number;
};

interface BatchQueryResult {
status?: 0 | 1;
rowsAffected?: number;
Expand Down Expand Up @@ -116,6 +140,24 @@ if (!result.status) {
}
```

In some scenarios, dynamic applications may need to get some metadata information about the returned resultset.
This can be done testing the returned data directly, but in some cases may not be enough, like when data is stored outside
storage datatypes, like booleans or datetimes. When fetching data directly from tables or views linked to table columns, SQLite is able
to identify the table declared types:

```typescript
let result = sqlite.executeSql('myDatabase', 'SELECT int_column_1, bol_column_2 FROM sometable');
if (!result.status) {
// result.status undefined or 0 === sucess
for (let i = 0; i < result.metadata.length; i++) {
const column = result.metadata[i];
console.log(`${column.columnName} - ${column.columnDeclaredType}`);
// Output:
// int_column_1 - INTEGER
// bol_column_2 - BOOLEAN
}
}
```
Batch execution allows transactional execution of a set of commands

```typescript
Expand Down
20 changes: 18 additions & 2 deletions cpp/JSIHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ void jsiQueryArgumentsToSequelParam(jsi::Runtime &rt, jsi::Value const &params,
}
}

jsi::Value createSequelQueryExecutionResult(jsi::Runtime &rt, SQLiteOPResult status, vector<map<string, QuickValue>> *results)
jsi::Value createSequelQueryExecutionResult(jsi::Runtime &rt, SQLiteOPResult status, vector<map<string, QuickValue>> *results, vector<QuickColumnMetadata> *metadata)
{
jsi::Object res = jsi::Object(rt);
if (status.type == SQLiteOk)
Expand Down Expand Up @@ -183,7 +183,23 @@ jsi::Value createSequelQueryExecutionResult(jsi::Runtime &rt, SQLiteOPResult sta
rows.setProperty(rt, "_array", move(array));
res.setProperty(rt, "rows", move(rows));
}
rows.setProperty(rt, "status", jsi::Value(0));

if(metadata != NULL)
{
size_t column_count = metadata->size();
auto column_array = jsi::Array(rt, column_count);
for (int i = 0; i < column_count; i++) {
auto column = metadata->at(i);
jsi::Object column_object = jsi::Object(rt);
column_object.setProperty(rt, "columnName", jsi::String::createFromUtf8(rt, column.colunmName.c_str()));
column_object.setProperty(rt, "columnDeclaredType", jsi::String::createFromUtf8(rt, column.columnDeclaredType.c_str()));
column_object.setProperty(rt, "columnIndex", jsi::Value(column.columnIndex));
column_array.setValueAtIndex(rt, i, move(column_object));
}
res.setProperty(rt, "metadata", move(column_array));
}

res.setProperty(rt, "status", jsi::Value(0));
rows.setProperty(rt, "length", jsi::Value((int)rowCount));
}
else
Expand Down
12 changes: 11 additions & 1 deletion cpp/JSIHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,16 @@ struct SequelBatchOperationResult
int commands;
};

/**
* Describe column information of a resultset
*/
struct QuickColumnMetadata
{
string colunmName;
int columnIndex;
string columnDeclaredType;
};

/**
* Fill the target vector with parsed parameters
* */
Expand All @@ -99,6 +109,6 @@ QuickValue createIntegerQuickValue(double value);
QuickValue createInt64QuickValue(long long value);
QuickValue createDoubleQuickValue(double value);
QuickValue createArrayBufferQuickValue(uint8_t *arrayBufferValue, size_t arrayBufferSize);
jsi::Value createSequelQueryExecutionResult(jsi::Runtime &rt, SQLiteOPResult status, vector<map<string, QuickValue>> *results);
jsi::Value createSequelQueryExecutionResult(jsi::Runtime &rt, SQLiteOPResult status, vector<map<string, QuickValue>> *results, vector<QuickColumnMetadata> *metadata);

#endif /* JSIHelper_h */
12 changes: 7 additions & 5 deletions cpp/installer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,11 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker

// Filling the results
vector<map<string, QuickValue>> results;
auto status = sqliteExecute(dbName, query, &params, &results);
vector<QuickColumnMetadata> metadata;
auto status = sqliteExecute(dbName, query, &params, &results, &metadata);

// Converting results into a JSI Response
auto jsiResult = createSequelQueryExecutionResult(rt, status, &results);
auto jsiResult = createSequelQueryExecutionResult(rt, status, &results, &metadata);
return move(jsiResult);
});

Expand Down Expand Up @@ -407,12 +408,13 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
{
// Inside the new worker thread, we can now call sqlite operations
vector<map<string, QuickValue>> results;
auto status = sqliteExecute(dbName, query, params.get(), &results);
invoker->invokeAsync([&rt, results = make_shared<vector<map<string, QuickValue>>>(results), status_copy = move(status), callback]
vector<QuickColumnMetadata> metadata;
auto status = sqliteExecute(dbName, query, params.get(), &results, &metadata);
invoker->invokeAsync([&rt, results = make_shared<vector<map<string, QuickValue>>>(results), metadata = make_shared<vector<QuickColumnMetadata>>(metadata), status_copy = move(status), callback]
{
// Now, back into the JavaScript thread, we can translate the results
// back to a JSI Object to pass on the callback
auto jsiResult = createSequelQueryExecutionResult(rt, status_copy, results.get());
auto jsiResult = createSequelQueryExecutionResult(rt, status_copy, results.get(), metadata.get());
callback->asObject(rt).asFunction(rt).call(rt, move(jsiResult)); });
}
catch (std::exception &exc)
Expand Down
2 changes: 1 addition & 1 deletion cpp/sqlbatchexecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ SequelBatchOperationResult executeBatch(std::string dbName, vector<QuickQueryArg
for(int i = 0; i<commandCount; i++) {
auto command = commands->at(i);
// We do not provide a datastructure to receive query data because we don't need/want to handle this results in a batch execution
auto result = sqliteExecute(dbName, command.sql, command.params.get(), NULL);
auto result = sqliteExecute(dbName, command.sql, command.params.get(), NULL, NULL);
if(result.type == SQLiteError)
{
return SequelBatchOperationResult {
Expand Down
23 changes: 20 additions & 3 deletions cpp/sqliteBridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ void bindStatement(sqlite3_stmt *statement, vector<QuickValue> *values)
}
}

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

int result, i, count, column_type;
string column_name;
string column_name, column_declared_type;
map<string, QuickValue> row;

while (isConsuming)
Expand Down Expand Up @@ -334,12 +334,29 @@ SQLiteOPResult sqliteExecute(string const dbName, string const &query, vector<Qu
row[column_name] = createNullQuickValue();
break;
}

i++;
}
results->push_back(move(row));
break;
case SQLITE_DONE:
if(metadata != NULL)
{
i = 0;
count = sqlite3_column_count(statement);
while (i < count)
{
column_name = sqlite3_column_name(statement, i);
const char *tp = sqlite3_column_decltype(statement, i);
column_declared_type = tp != NULL ? tp : "UNKNOWN";
QuickColumnMetadata meta = {
.colunmName = column_name,
.columnIndex = i,
.columnDeclaredType = column_declared_type,
};
metadata->push_back(meta);
i++;
}
}
isConsuming = false;
break;

Expand Down
2 changes: 1 addition & 1 deletion cpp/sqliteBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ SQLiteOPResult sqliteRemoveDb(string const dbName, string const docPath);

// SequelResult sequel_attach(string const &dbName);

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

SequelLiteralUpdateResult sqliteExecuteLiteral(string const dbName, string const &query);
18 changes: 18 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,26 @@ interface QueryResult {
*/
item: (idx: number) => any;
};
/**
* Query metadata, avaliable only for select query results
*/
metadata?: ColumnMetadata[];
}

/**
* Column metadata
* Describes some information about columns fetched by the query
*/
declare type ColumnMetadata = {
/** The name used for this column for this resultset */
columnName: string;
/** 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. */
columnDeclaredType: string;
/**
* The index for this column for this resultset*/
columnIndex: number;
};

/**
* Allows the execution of bulk of sql commands
* inside a transaction
Expand Down