Create bd in android - flutter - database

I was trying to create a local DB but I'm having some problems, this is my code
Future<Future<Database>> createTable() async {
WidgetsFlutterBinding.ensureInitialized();
final database = openDatabase(
join(await getDatabasesPath(), 'doggie_database.db'),
onCreate: (db, version) {
return db.execute(
'CREATE TABLE dogs(id INTEGER PRIMARY KEY, ref TEXT, descricao TEXT, name TEXT, price DOUBLE, quantity DOUBLE, image TEXT, cor TEXT, tamanho TEXT, priceWithoutTax DOUBLE)',
);
},
version: 1,
);
return database;
}
Future<void> insertDog(Dog dog) async {
var db = await createTable();
await db.insert(
'dogs',
dog.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
that is, I'm creating the DB when I call the insertDog. At first I tried to create the DB in main but then I couldn't call the "database" . So I decided this way, now I can't continue because "await db.insert" is giving error.
erro:"The method 'insert' isn't defined for the type 'Future'.
Try correcting the name to the name of an existing method, or defining a method named 'insert'."
I don't know if there is a solution to this problem or if anyone has another suggestion.
thanks

Related

On knex migration:up, table.foreign is not showing up on the actual table on the terminal

I have setup knex migration to create two tables
export async function up(knex: Knex): Promise<void> {
const table1 = await knex.schema.hasTable('users');
if (!table1) {
await knex.schema.createTable('users', (table) => {
table.increments("id").primary();
table.string("username");
table.string("email");
table.string("password");
table.string("role");
table.timestamp("created_at");
table.timestamp("updated_at");
table.timestamp("logged_at");
});
}
const table2 = await knex.schema.hasTable('usersloc');
if (!table2) {
await knex.schema.createTable('usersloc', (table) => {
table.increments("locid");
table.string("lat");
table.string("lng")
table.foreign("userid");
});
}
}
However, when I did a select * from usersloc;, userid in usersloc is nowhere to be found.
My intention is to have it referred to the id at the "users" table.
Any idea what happened?
Solution found - looks like the UUID needs to have a .unique() reference on both tables. My guess is that even though UUID may take billions of years to have 1% chance of collision, it is still not a unique ID, thus a .unique() reference is needed.

Best way to reference PostgreSQL query in PERN app

Summary: PERN app needs to download XLSX that represents the same query that was run to populate a data grid on the client side. For example a user has defined a series of product family prefixes and they're sent to the server endpoint:
const getItemsByFamily = async (like) => {
const items = await fetch('/item/like/' + like);
const result = await items.json();
return result;
};
Where the endpoint uses a call to the associated database function:
app.get('/item/like/:like', async (req, res) => {
const { like } = req.params;
const { rows } = await dbo.getByItemPrefix(like);
res.json(rows);
});
And, the function runs the actual query against the PG Db:
getByItemPrefix: async (like) => await pool.query(`select * from myschema.items where ${like}`);
All is well... But, now stakeholders on this project have requested the ability to download a representative XLSX for the same data set. My idea is to use PG's COPY command to write a unique XLSX on the server and link to it somehow.
I start with a proof of concept, hard-coded address, just to simply download a sample xlsx:
// TODO: Add some sort of reference to represent data-grid query.
const DownloadXLSX = ({/* query or query ref */}) => {
return (
<a href='http://localhost:1234/download?fileName=sample.xlsx' target='_blank' rel='noopener noreferrer' download>
<Button
variant='contained'
size='small'
>
Download
</Button>
</a>
);
};
But, now I'm on SO because I am stuck in an architecture question. What is possible?
Is it time to audit every query and reference it somehow that way? Currently this product has no auditing, but perhaps it is time to spend time to do that? Possibly make an audit entry and send the id down with the results, passing the audit.id down do the download button for its query param?
Or, is there some more simple way to track a data grid's representative query on the client?
I like to do whatever is optimal and I'm hoping to find opinions or some insight. Thanks in advance for any help in getting over this architecture hurdle (missing having a team!).
I thought I should follow up here.
To access the actual text of a query within a PG stored function that could be used with a trigger you can use the function current_query().
However, my whole idea of using triggers went down the drain when I found this in the PG docs:
SELECT does not modify any rows so you cannot create SELECT triggers. Rules and views are more appropriate in such cases.
While the suggested approach might work in some cases it really would not for me as the results of some queries are huge and I really needed a reference.
Instead I wrap pool.query with a function that just takes the passed-in SQL and stores it in a simple, but sufficient for my purposes, "audit" table.
CREATE TABLE myschema.audit
(
id serial,
user_id varchar(50) not null,
full_query text not null,
x_time timestamp not null default now(),
CONSTRAINT audit_pkey PRIMARY KEY (id)
)
TABLESPACE pg_default;
ALTER TABLE myschema.audit
OWNER to postgres;
pool.queryWithAudit = async query => {
const { rows } = await pool.query(SQL`insert into iol.trac_audit (user_id, full_query) values ($1, $2) returning id`, ['dev-trac', query]);
const auditID = rows[0].id;
const queryResult = await pool.query(query);
queryResult.auditID = auditID;
return queryResult;
};
And, finally, the retun from the server changed just a little bit to include the auditID, and the download component had to change (to CSV for now, instead of XLSX):
const DownloadXLSX = ({ queryId, style = {} }) => {
return (
<a style={{ textDecoration: 'none', ...style }} href={`http://localhost:1234/download?queryId=${queryId}`} target='_blank' rel='noopener noreferrer' download>
<Button
endIcon={<GetAppIcon />}
size='small'
>
Download
</Button>
</a>
);
};
Along with corresponding route for lookup of query, then execution:
app.get('/download', async (req, res) => {
const { queryId } = req.query;
// Now grab query from audit and run that.
const fileName = await dbo.makeXLSX(queryId);
res.download(fileName, fileName.split('/').pop(), (err) => {
console.log(err ? 'Error attempting to download: ' + err.message : 'No server-side error in attempt to download.');
if (!err) fs.unlink(fileName, (unlinkErr) => console.log(unlinkErr ? 'Temp folder cleanup error: ' + unlinkErr.message : `File at: [${fileName}] removed from system temp folder.`));
});
});
dbo.js updates:
makeXLSX: async auditId => {
const { rows } = await pool.query(`select full_query from myschema.audit where id = ${auditId}`);
const query = rows[0].full_query;
const fileName = path.join('C:/tmp/', Date.now().toString() + '.csv');
const copyCommand = `COPY (${query}) TO '${fileName}' CSV HEADER;`;
await pool.query(copyCommand);
return fileName;
}
Anyway, this works for me. Maybe somebody will submit improvements? I just include the button if there is a queryId, which I null-out with the associated useState setter function:
const [queryId, setAuditIdRef] = useState(null);
{queryId ? <DownloadXLSX queryId={queryId} /> : null}
Good day.

Initialize a store the first time is created in sembast

I'm developing a mobile application in Flutter, and I would like to manage the settings of my application (light theme or dark theme, ...).
I'm using sembast to store the settings of my application. I would like to initialize the store with some initial values the first time is created. How can I do that?
This is the my database helper class:
class AppDatabase {
// Name of the database.
static final String _dbName = 'mydb.db';
// Singleton instance.
static final AppDatabase _singleton = AppDatabase._();
// Singleton getter.
static AppDatabase get instance => _singleton;
// Transforms synchronous code into asynchronous code.
Completer<Database> _dbOpenCompleter;
// Private constructor.
AppDatabase._();
// Database object getter.
Future<Database> get database async {
// If completer is null, AppDatabaseClass is newly instantiated, so database is not yet opened.
if (_dbOpenCompleter == null) {
_dbOpenCompleter = Completer();
_openDatabase();
}
return _dbOpenCompleter.future;
}
Future<void> _openDatabase() async {
// Get a platform-specific directory where persistent app data can be stored.
final appDocumentDir = await getApplicationDocumentsDirectory();
// Path with the form: /platform-specific-directory/demo.db
final dbPath = join(appDocumentDir.path, _dbName);
final database = await databaseFactoryIo.openDatabase(dbPath);
// Any code awaiting the Completer's future will now start executing.
_dbOpenCompleter.complete(database);
}
}
Then I use the following repository to perform CRUD operations on my Settings:
class SettingsDatabaseRepository implements SettingsRepository {
// The name of the store.
static const String SETTINGS_STORE_NAME = 'settings';
// This store acts like a persistent map, values of which are Settings objects
// converted to Map.
final _settingsStore = intMapStoreFactory.store(SETTINGS_STORE_NAME);
// Private getter to shorten the amount of code needed to get the singleton
// instance of an opened database.
Future<Database> get _db async => await AppDatabase.instance.database;
#override
Future<void> insert(Settings settings) async {
await _settingsStore.add(await _db, settings.toMap());
}
#override
Future<void> update(Settings settings) async {
final finder = Finder(filter: Filter.byKey(settings.settingsId));
await _settingsStore.update(
await _db,
settings.toMap(),
finder: finder,
);
}
#override
Future<Settings> getSettings() async {
final recordSnapshots = await _settingsStore.find(await _db);
final settingsList = recordSnapshots.map((snapshot) {
final settings = Settings.fromMap(snapshot.value);
settings.copyWith(settingsId: snapshot.key);
return settings;
}).toList();
if (settingsList.isEmpty)
return null;
else
return settingsList.first;
}
}
I would like to initialize the store with some initial values the first time is created.
You cannot perform action when a store is created (since a store is not really created, it just holds records), however you can perform action when the database is created.
Sembast supports a database versioning system similar to sqlite, although here there is not much schema to modify. You can use this system to perform action when the database is created (or when you decide later in a new version to update it).
// Our shop store sample data
var store = intMapStoreFactory.store('shop');
var db = await factory.openDatabase(path, version: 1,
onVersionChanged: (db, oldVersion, newVersion) async {
// If the db does not exist, create some data
if (oldVersion == 0) {
await store.add(db, {'name': 'Lamp', 'price': 10});
await store.add(db, {'name': 'Chair', 'price': 15});
}
});
See more info

Discord SDK: lobbyManager.ConnectLobby() returns NotFound

Join lobby:
public void JoinLobby(String lobbyIdSecret)
{
String[] parsedItems = lobbyIdSecret.Split(':');
lobbyId = Int64.Parse(parsedItems[0]);
lobbySecret = parsedItems[1];
lobbyManager.ConnectLobby(lobbyId, lobbySecret, (Discord.Result result, ref Discord.Lobby lobby) =>
{
Debug.Log(String.Format("Result: {0}", result));
});
}
I have created a lobby and now I'm trying to connect another user to it. However, as a result I'm getting NotFound. I know the ID and secret are correct. Any ideas on what could be wrong?
Create lobby:
public void CreateLobby(LobbyCreatedCB cb)
{
// Create the transaction
var txn = lobbyManager.GetLobbyCreateTransaction();
// Set lobby information
txn.SetCapacity(6);
txn.SetType(Discord.LobbyType.Public);
txn.SetMetadata("a", "123");
// Create it!
lobbyManager.CreateLobby(txn, (Discord.Result result, ref Discord.Lobby lobby) =>
{
lobbyId = lobby.Id;
lobbySecret = lobby.Secret;
Debug.Log(String.Format("lobby {0} created with secret {1}", lobbyId, lobby.Secret));
Debug.Log(String.Format("lobby has {0} user connected", lobbyManager.MemberCount(lobbyId)));
// We want to update the capacity of the lobby
// So we get a new transaction for the lobby
var newTxn = lobbyManager.GetLobbyUpdateTransaction(lobby.Id);
newTxn.SetCapacity(5);
lobbyManager.UpdateLobby(lobby.Id, newTxn, (updatedResult) =>
{
Debug.Log(String.Format("lobby {0} updated", updatedResult));
});
UpdateActivity(discord, lobby);
cb(String.Format("{0}:{1}", lobbyId, lobbySecret));
});
}
Edit: Changed join lobby snippet to be exact code that I'm using. Added create lobby function.
Not sure if you might've just written the parameter types backwards, but in the SDK, id is an Int64 and secret is a string. If your compiler isn't yelling enough to stop before even running the code, it may be that the SDK isn't sure what to do with a string for an ID and returning NotFound. Could you post your snippet for creating the lobby as well?

Flutter - Sending SQL data to a ListView

Using the flutter package sqflite I've managed to save data from a news source to a database, such as title, description and more.
Please take the time to look at this application created database file that follows my structure.
This is how I create the database, and how I save data to it from an online source.
var title = articles[index].title;
var description = articles[index].description;
var url = articles[index].url;
var urlToImage = articles[index].urlToImage;
var publishedAt = articles[index].publishedAt;
var databasesPath = await getDatabasesPath();
String path = join(databasesPath, 'saved_articles.db');
Database database = await openDatabase(path, version: 1,
onCreate: (Database db, int version) async {
// When creating the db, create the table
await db.execute(
'CREATE TABLE Article (id INTEGER PRIMARY KEY, title TEXT, description TEXT, url TEXT, urltoimage TEXT, publishedat TEXT)');
});
print(database);
print(databasesPath);
// Insert some records in a transaction
await database.transaction((txn) async {
int id1 = await txn.rawInsert(
'INSERT INTO Article(title, description, url, urltoimage, publishedat) VALUES("$title", "$description", "$url", "$urlToImage", "$publishedAt")'
);
debugPrint('inserted1: $id1');
});
I'm looking for a method in which to send this data straight to an ordered ListView. The end goal for this being a favourites page which saves articles to the user's device for offline viewing.
This is how I'm retrieving specific data for converting into the bottom title list.
var databasesPath = await getDatabasesPath();
String path = join(databasesPath, 'saved_articles.db');
Database database = await openDatabase(path, version: 1,
onCreate: (Database db, int version) async {
// When creating the db, create the table
await db.execute(
'CREATE TABLE Article (id INTEGER PRIMARY KEY, title TEXT, description TEXT, url TEXT, urltoimage TEXT, publishedat TEXT)');
});
List<Map> title = await database.rawQuery('SELECT title FROM Article');
List<Map> description = await database.rawQuery('SELECT description FROM Article');
List<Map> url = await database.rawQuery('SELECT url FROM Article');
This is what I'm using to send specific queries to list, but I'm not sure where to go from here in terms of creating a viable structure for sending this data to the desired ListView
List<Map> title = await database.rawQuery('SELECT title FROM Article');
Thanks
I'm not entirely sure I understand your question or issue, so please follow up if I'm missing something.
I'm looking for a method in which to send this data straight to an ordered ListView.
From this, I understand your question as you are just wondering what to do next in flutter?
If so, I would recommend taking a look at: https://pub.dartlang.org/packages/jaguar_serializer
Then here's what I would do
Create an Article model, following jaguar's readme OR just make a manual function to convert a map to an instance of Article.
import 'package:jaguar_serializer/jaguar_serializer.dart';
part 'article.jser.dart';
class Article {
string title;
string description;
string url;
etc...
}
etc..
Run the cli tool
Create a query to get a List of all articles, such as (ordering in sql is faster)
'SELECT title, description, url FROM Article ORDER BY title'
Map that to a list of articles (again take a look at jaguar readme for creatont a JsonRepo.
List<Article> articles = jsonRepo.decodeList<Complete>(response.body);
Then you can just pass in your articles to the ListView in flutter.
ListView.builder(
padding: EdgeInsets.all(8.0),
itemExtent: 20.0,
itemBuilder: (BuildContext context, int index) {
return Text('entry ${articles[index].title}');
},
)

Resources