How to create multiple tables in a database in sqflite? - database

Im building and app with flutter that uses SQLite database. I have created first table using this piece of code:
void _createDb(Database db, int newVersion) async {
await db.execute('''CREATE TABLE cards (id_card INTEGER PRIMARY KEY,
color TEXT, type TEXT, rarity TEXT, name TEXT UNIQUE, goldCost INTEGER,
manaCost INTEGER, armor INTEGER, attack INTEGER, health INTEGER, description TEXT)''');
}
Table gets created and I can access it without problems.
Unfortunately I cannot include more than 1 table that i just created. I tried adding another SQL CREATE TABLE clause in the same method, and repeating method db.execute with a different SQL clause just in the next line.
I'm mimicing code from this tutorial: https://www.youtube.com/watch?v=xke5_yGL0uk
How to add another table within the same database?

You can just combine multiple db.execute calls for exampple
await db.execute('''
create table $reminderTable (
$columnReminderId integer primary key autoincrement,
$columnReminderCarId integer not null,
$columnReminderName text not null,
$columnReminderNotifyMileage integer not null,
$columnReminderEndMileage integer not null
)''');
await db.execute('''
create table $carTable (
$columnCarId integer primary key autoincrement,
$columnCarTitle text not null
)''');

yes you can do it
void _createDb(Database db, int newVersion) async {
await db.execute('''
create table $carTable (
$columnCarId integer primary key autoincrement,
$columnCarTitle text not null
)''');
await db.execute('''
create table $userTable(
$userId integer primary key autoincrement,
$name text not null
)''');
}
but to speed up the process, let's assume we have 10 tables, you could use the batch this way
void _createDb(Database db, int newVersion) async {
Batch batch = db.batch();
batch.execute("Your query-> Create table if not exists");
batch.execute("Your query->Create table if not exists");
List<dynamic> res = await batch.commit();
//Insert your controls
}

You can use a .sql file that contains your DB script.
First,add the script file to assets.
Then, import the following packages:
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:flutter/services.dart' show rootBundle;
finally, use the following code
void _createDb() async
{
final database = openDatabase( join( await getDatabasesPath(), 'mydb.db'),
onCreate: (db, version) async
{
// call database script that is saved in a file in assets
String script = await rootBundle.loadString("assets\\db\\script.sql");
List<String> scripts = script.split(";");
scripts.forEach((v)
{
if(v.isNotEmpty )
{
print(v.trim());
db.execute(v.trim());
}
});
},
version: 1,
);
}

Change the name of the DB file. This will 'reset' your DB and creation will work.
e.g:
final dabasesPath = await getDatabasesPath();
final path = join(dabasesPath, "newName2.db");

In openDatabase(path, onCreate, version)
use one more optional parameter "onUpgrade" and define the dropping and again creating table scripts. and also upgrade (increase) the parameter version by one.
----- Code snippet ------
openDatabase(path, onCreate:_createDb, onUpgrade: onUpgrade,version:_DB_VERSION);
...
...
_onUpgrade( Database db, int oldVersion, int newVersion ) async {
Batch batch = db.batch();
// drop first
batch.execute("DROP TABLE IF EXISTS $_TABLE_3 ;");
batch.execute("DROP TABLE IF EXISTS $_TABLE_2 ;");
batch.execute("DROP TABLE IF EXISTS $_TABLE_1 ;");
// then create again
batch.execute("CREATE TABLE $TABLE_1 ...... ");
batch.execute("CREATE TABLE $TABLE_2 ...... ");
batch.execute("CREATE TABLE $TABLE_3 ...... ");
List<dynamic> result = await batch.commit();
}
Note: Each and every time when you will create or change the structure of some table(s), you will have to increase database version in openDatabase() method. so that the upgradation will be called, otherwise it will not be called.

Hard to say without seeing your openDatabase call and whether the database exists before or not. One of my guess is that you are still using the same database version. Once onCreate has been called it will never be called again. You should try to bump your database version and add the new table in on onUpgrade

you can use db.execute for creation of multiple tables under single database.
Future _createDB(Database db, int version) async {
await db.execute('CREATE TABLE users(userId INTEGER PRIMARY KEY, userName TEXT NOT NULL)');
await db.execute('CREATE TABLE tasks(taskId INTEGER PRIMARY KEY, userId INTEGER, task TEXT NOT NULL, status BOOL NOT NULL)');
}

Related

Unable to create another table in sqflite

I'm trying to create a new user table on existing database but sqflite keep showing the error of "no such table Exception" and it executes only one table . I tried changing database name also but nothing worked.
Here is my DBconnector Code:
class DBConnector {
Future<Database> setDatabase() async {
var directory = await getApplicationDocumentsDirectory();
var path = join(directory.path, 'sddssd.db');
var database =
await openDatabase(path, version:1, onCreate: _createDatabase);
return database;
}
Future<void> _createDatabase(Database database, int version) async {
String questionTableName = QuestionModel.KEY_QUESTION_TABLE_NAME;
String userTableName = UserModel.KEY_USER_TABLE_NAME;
String quesTableSql =
"CREATE TABLE IF NOT EXISTS $questionTableName (id INTEGER PRIMARY KEY,question VARCHAR(200),answer VARCHAR(200));";
String userTableSql =
"""CREATE TABLE IF NOT EXISTS $userTableName (uid INTEGER PRIMARY KEY,name TEXT,email TEXT,password TEXT);""";
await database.execute(quesTableSql);
await database.execute(userTableSql);
}
}
Error :
Exception has occurred.
SqfliteDatabaseException (DatabaseException(no such table: User (code 1 SQLITE_ERROR): , while compiling: INSERT INTO User (uid, name, email, password) VALUES (NULL, ?, ?, ?)) sql 'INSERT INTO User (uid, name, email, password) VALUES (NULL, ?, ?, ?)' args [f, e, s]})
String quesTableSql = """CREATE TABLE IF NOT EXISTS $userTableName
(
uid INTEGER PRIMARY KEY,
name TEXT,
email TEXT,
password TEXT
""");
I solved this , the thing is onCreate callback will get executed only one time when you load the application after that no changes will get updated unless u use migration .
So i created a seperate function for creating Table and got it working by making the instance of that class and call the create Table method.

MS-SQL server throws error when two foreign keys are set on same table using GORM

I am doing database migrations using GORM. So I define structs and run them through GORM's AutoMigrate function.
type Person struct {
ID string `gorm:"type:varchar(36);primary_key"`
}
err := db.Table("persons").AutoMigrate(&Person{}).Error
type Address struct {
ID string `gorm:"type:varchar(36);primary_key"`
PersonID string `gorm:"column:person_id;type:varchar(36);NOT NULL"`
}
err = db.AutoMigrate(&Address{}).Error
err = db.Model(&Address{}).AddForeignKey("person_id", "persons(id)", "NO ACTION", "CASCADE").Error
type Contact struct {
ID string `gorm:"type:varchar(36);primary_key"`
AddressID null.String `gorm:"column:address_id;type:varchar(36);NOT NULL"`
PersonID string `gorm:"column:person_id;type:varchar(36);NOT NULL"`
}
err = db.AutoMigrate(&Contact{}).Error
err = db.Model(&Contact{}).AddForeignKey("address_id", "addresses(id)", "NO ACTION", "CASCADE").Error
err = db.Model(&Contact{}).AddForeignKey("person_id", "persons(id)", "NO ACTION", "CASCADE").Error
In the above code, which ever is the second call to the AddForeignKey function on Contacts table is giving error :
mssql: Could not create constraint or index. See previous errors.
Even if I move person_id foreign key above address_id foreign key, then address_id foreign key fails.
I am running MS-SQL server using latest docker container setup(microsoft/mssql-server-linux:latest). Is this something regarding naming of constraint. If yes, then how can we set using GORM? Everything works fine with My-SQL.
It would be really helpful if I get a solution. I cannot run raw queries. Migrations have to be done using GORM only.
Thank You
Multiple foreignkeys can't have 'no action', 'cascade' on the same table. If u use no action, no action u can but u have to handle deletion and updating your self. What I usually do is turn on logmode and copy paste the log sql statement into SQL. This will give u a more clear error.

How to return id from insert into sqflite database in flutter

I have 2 databases. One for folders and one for notes.
The folder database contains a title and json String of a list of id's
The id's are supposed to represent the second database of notes.
Basically folders needs to contain a list of notes and this is the best way I found so far. My problem is that when I create a new note and insert it into the database, I'm not sure how to get the id of the new note back.
If I set the id when I create the note then all notes will have the same id.
If I don't set the id then calling note.id returns null.
Based on my create function the Note's id should set itself (id INTEGER PRIMARY KEY AUTOINCREMENT)
My idea is that when creating the new Note I would somehow return the int id so that I could easily insert it into the folder. Here's what I have so far.
void addNewNote(Note note) async {
var db_connection = await db;
String query =
'INSERT INTO Note(title, content, date_created, date_last_edited) VALUES(\'${note.title}\', \'${note.content}\', \'${note.date_created}\', \'${note.date_last_edited}\')';
await db_connection.transaction((transaction) async {
return await transaction.rawInsert(query);
});
}
The inserted id is also returned from the db.insert() helper method, as the signature
Future<int> insert(String table, Map<String, dynamic> values,
{String nullColumnHack, ConflictAlgorithm conflictAlgorithm})
describes. Example:
final db = await getDb();
final insertedId = await db.insert(
todosTableName,
todo.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
print('insertedId: $insertedId');
You should be able to get the id from the rawInsert like so:
final noteId = await transaction.rawInsert(query);
according to the docs: https://pub.dev/packages/sqflite#raw-sql-queries
Instead of storing a string list of ids on the folders table you can also try store folder_id on the notes table as a foreign key:
https://sqlite.org/foreignkeys.html

IdentityServer4 Sample with ASP Identity with real SQL Server

I have been struggling to get the final SAMPLE (ASP.Net, EF Core, SQL) to work against a real SQL Server. Every sample I can find does not use real SQL they always opt for in-memory data store
I changed the connection string
"Data Source=.;Initial Catalog=IS4;Integrated Security=True;"
and ran
dotnet ef database update -c ApplicationDbContext
This created me a SQL database with 25 tables.
I tweaked Startup.cs to change
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
and b.UseSqlite to b.UseSqlServer
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = b =>
b.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
})
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = b =>
b.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
// options.TokenCleanupInterval = 15;
});
I ran the server with "/seed" on the command line but the Seed functionality doesn't work
First it complains CLIENT can't have a NULL ID when it calls SaveChanges(). If I change the code to add the ID
if (!context.Clients.Any())
{
Console.WriteLine("Clients being populated");
int i = 1;
foreach (var client in Config.GetClients().ToList())
{
var x = client.ToEntity();
x.Id = i++;
context.Clients.Add(x);
}
context.SaveChanges();
}
else
{
Console.WriteLine("Clients already populated");
}
I then get
"Cannot insert the value NULL into column 'Id', table 'IS4.dbo.ClientGrantTypes".
When I watch the video's it says it can be migrated from SQLite to full SQL simply by changing the connection string which is obviously not true, given all the other changes I have done, so I must be doing (or missing) something else.
Any thoughts?
Could it be that all the tables with an "Id INT" column should all be IDENTITY columns and they are not!
I checked the migrations code and it has
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ApiResources",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Description = table.Column<string>(maxLength: 1000, nullable: true),
DisplayName = table.Column<string>(maxLength: 200, nullable: true),
I am guessing
.Annotation("Sqlite:Autoincrement", true),
doesn't work with full SQL and therefore all the tables need identity properties setting.
Interestingly if you run the other template to add the AdminUI
dotnet new is4admin
It seems to add a couple of SQL scripts
CREATE TABLE "Clients" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_Clients" PRIMARY KEY AUTOINCREMENT,
"AbsoluteRefreshTokenLifetime" INTEGER NOT NULL,
"AccessTokenLifetime" INTEGER NOT NULL,
which does make them identity columns.
I was faced with this issue today and did a couple of searches online and stumbled upon this https://entityframeworkcore.com/knowledge-base/46587067/ef-core---do-sqlserver-migrations-apply-to-sqlite-
The link pointed out to switch the annotation portion in the migration class UP method after
Id = table.Column(nullable: false)
from
.Annotation("Sqlite:Autoincrement", true);
to
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn)
And you will need to import
using Microsoft.EntityFrameworkCore.Metadata;
Then you build, and the migration will be successful.
To resolve this particular issue I used SSMS.
right click on table
select script to drop and create
add IDENTITY after the NOT NULL
Execute
However you are correct, it is using sqlite annotations in the sql file and in the migrations.
To fully resolve this issue, you need to create an implementation of all 3 necessary database contexts: identity, persisted grant, and configuration.
That requires an implementation of design time factories for each of those contexts as well.
Then you can run add-migration in the package manager console for each of those contexts, and then run update database, or run the application with the migrate function when seeding.
So to recap:
Create implementations for the 3 db contexts
Create Design time factory implementations for those db contexts
Add the migrations
Update the database with those migrations

How do I query SQLite Database in Android?

I successfully created the Database and inserted a row however I cannot Query it for some reason. My Droid crashes everytime.
// Create a new row of values to insert.
ContentValues newValues = new ContentValues();
// Assign values for each row.
newValues.put("value", "kunjan");
// Insert the row into your table
myDatabase.insert(DATABASE_TABLE, null, newValues);
String[] result_columns = new String[] { "value" };
// I GET AN EXCEPTION HERE
Cursor allRows = myDatabase.query(true, DATABASE_TABLE, result_columns, null, null,
null, null, null, null);
if (allRows.moveToFirst()) {
String value = allRows.getString(0);
TextView foo = (TextView) findViewById(R.id.TextView01);
foo.setText(value);
}
allRows.close();
myDatabase.close();
I get this exception
no such column: value: , while compiling: SELECT DISTINCT value FROM mainTable
I think you are creating table with only one column(value) and you are trying to read the column-1 instead of column-0 from the cursor. If that still not helps, please add try-catch block and capture the logs
String value = allRows.getString(0);
Some of the problems with the original post
Creating a table that has already been created. I wrote "if not exists" in the "create table" instruction.
Not handling Exceptions properly. I got a lot of exceptions in the creation of the table, quering it etc. I sorrounded all these operations with try-catch
Using hard coded values for Column names - This made it harder to verify if I was querying the column that I had created.

Resources