NOT NULL constraint failed while copying data to a new table - android

I migrate my database from SQLiteOpenHelper to Room.
I have a table that I want to change, lets call it "my_table".
Its simplified create statement:
CREATE TABLE `my_table`
(`_id` INTEGER PRIMARY KEY AUTOINCREMENT,
`title` TEXT
)
During an upgrade among other changes I add the new column type INTEGER NOT NULL (I'm adding Foreign Key aswell and doing other significant changes, that's the reason to create a new table instead of altering the existing one):
CREATE TABLE "new_table"
(`_id` INTEGER PRIMARY KEY AUTOINCREMENT,
`title` TEXT,
`type` INTEGER NOT NULL
)
Then I want to copy data from the my_table to the new_table and set type column's values.
SQL statement:
INSERT INTO new_table (title)
SELECT title FROM my_table;
UPDATE new_table SET type = 1;
DROP TABLE my_table;
ALTER TABLE new_table RENAME TO my_table;
Android migration:
public static final Migration MIGRATION_TEST = new Migration(1, 2) {
#Override
public void migrate(#NonNull SupportSQLiteDatabase database) {
// Create new table
database.execSQL("CREATE TABLE new_table (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `title` TEXT, `type` INTEGER NOT NULL)");
// Copy some data
database.execSQL("INSERT INTO new_table (title) SELECT title FROM old_table"); // constraint violation
// Insert default value into the measures column
database.execSQL("UPDATE new_table SET type = 1");
// Delete old table
database.execSQL("DROP TABLE old_table");
// Rename new table
database.execSQL("ALTER TABLE new_table RENAME TO my_table");
}
};
Obviously I get NOT NULL constraint failed: new_table.type error:
android.database.sqlite.SQLiteConstraintException: NOT NULL constraint failed: new_table.type (code 1299)
Error Code : 1299 (SQLITE_CONSTRAINT_NOTNULL)
Caused By : Abort due to constraint violation.
(NOT NULL constraint failed: new_table.type (code 1299))
I can avoid it by changing new table's create statement and setting default value for the type column.
CREATE TABLE "new_table"
(`_id` INTEGER PRIMARY KEY AUTOINCREMENT,
`title` TEXT,
`type` INTEGER NOT NULL DEFAULT 1
)
But I don't want to do this as Room doesn't suport default values out of the box and in order to avoid future mistakes when inserting new values into tables.
Are there any workarounds to avoid this error while inserting data to a new table?

I think the following may work :-
database.execSQL("INSERT INTO new_table (title,type) SELECT title, 1 FROM old_table");
That is, you are now saying to INSERT 2 columns as per the SELECT statement. The SELECT returns 2 values the title from the old_table and the literal value 1.
That is SELECT actual returns the result of expressions result-column which aren't limited to just columns. An Expression can be literal value, result of functions, results of operations and other expr
As per
The list of expressions between the SELECT and FROM keywords is known
as the result expression list.
SQL As Understood By SQLite - SELECT - 3. Generation of the set of result rows.
You then wouldn't need database.execSQL("UPDATE new_table SET type = 1").

Related

Sqlite insert trigger on virtual table with condition

I am trying to insert on the virtual table only when the condition met to insert, like when the type is "-1" then only insert.
But it was inserting all the record
DROP TRIGGER if exists test_ai;
CREATE TRIGGER test_ai AFTER INSERT ON test
WHEN new.type = -1
BEGIN INSERT INTO
test_fts(body) VALUES (new.body);
END;
/*Create table */
CREATE TABLE `test` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `body` TEXT, `caption` TEXT, `type` INTEGER );
/*Create virtual table */
CREATE VIRTUAL TABLE test_fts USING fts5(body, content=test, content_rowid=id);
/* Insert into test table*/
insert into test(body, caption, type) values("5", "",-6);
It just looks like they're there, it's basically a virtual "copy" of the content table. However, only the rows inserted with the trigger will be returned in an FTS query. Try
SELECT * from test_fts where body MATCH '5'
It will not return the sample row. The trigger insert builds the FTS structure for the desired row(s).

SQLite db update recreate table keep values

I have a SQLite database with one table which I need to update in a new version of my database.
In the first version of the table I have a id and a txt value like this:
db.execSQL("CREATE TABLE table_name id INTEGER PRIMARY KEY AUTOINCREMENT, txt TEXT")
But in the next version of the database I wand to add a lastchanged value which must be a TIMESTAMP with default value current time in epoch which I do with (strftime('%s', 'now')).
But since I can't add a column with default current time, according to this error:
Cannot add a column with non-constant default (code 1): , while compiling: ALTER TABLE table_name ADD COLUMN edited_time TIMESTAMP DEFAULT (strftime('%s', 'now'))
I tought I could best recreate the data and copy it to the new database
// copy all data to single value something like a Cursor res maybe??
db.execSQL("DROP TABLE IF EXISTS table_name );
db.execSQL("CREATE TABLE table_name id INTEGER PRIMARY KEY AUTOINCREMENT, txt TEXT, edited_time TIMESTAMP DEFAULT (strftime('%s', 'now'))");
// now insert the values into the recreated database
But for this i need to copy all data into a new table that does not exist simultanious. Is it posible to do this without looping and having to save every value separate and then inserting them seperately again?
Yes. The general procedure is:
BEGIN;
ALTER TABLE table_name RENAME TO table_name_old;
CREATE TABLE table_name( ... );
INSERT INTO table_name(col1, ..., coln)
SELECT col1, ..., coln FROM table_name_old;
DROP table_name_old;
COMMIT;

SQLite alter column to remove constraint

I have a column with a NOT NULL constraint. I need to alter the database to remove the constraint. But when I try I get a duplicate column exception from android
db.execSQL("ALTER TABLE " + AnimalContract.DogEntry.TABLE_NAME
+ " ADD COLUMN " + AnimalContract.DogEntry.COLUMN_NAME+" TEXT DEFAULT NULL");
I also tried without DEFAULT
The constraint was UNIQUE NOT NULL
There's no direct way to ALTER COLUMN in SQLite.
I believe your only option is to:
Rename the table to a temporary name
Create a new table without the NOT NULL constraint
Copy the content of the old table to the new one
Remove the old table
This other Stackoverflow answer explains the process in details
Furthermore,
MODIFY COLUMN IN TABLE
You can not use the ALTER TABLE statement to modify a column in SQLite. Instead you will need to rename the table, create a new table, and copy the data into the new table.
Syntax
The syntax to MODIFY A COLUMN in a table in SQLite is:
PRAGMA foreign_keys=off;
BEGIN TRANSACTION;
ALTER TABLE table1 RENAME TO _table1_old;
CREATE TABLE table1 (
( column1 datatype [ NULL | NOT NULL ],
column2 datatype [ NULL | NOT NULL ],
...
);
INSERT INTO table1 (column1, column2)
SELECT column1, column2
FROM _table1_old;
COMMIT;
PRAGMA foreign_keys=on;
Example
Let's look at an example that shows how to modify a column in a SQLite table.
For example, if we had an employees table that had a column called last_name that was defined as a CHAR datatype:
CREATE TABLE employees
( employee_id INTEGER PRIMARY KEY AUTOINCREMENT,
last_name CHAR NOT NULL,
first_name VARCHAR,
hire_date DATE
);
And we wanted to change the datatype of the last_name field to VARCHAR, we could do the following:
PRAGMA foreign_keys=off;
BEGIN TRANSACTION;
ALTER TABLE employees RENAME TO _employees_old;
CREATE TABLE employees
( employee_id INTEGER PRIMARY KEY AUTOINCREMENT,
last_name VARCHAR NOT NULL,
first_name VARCHAR,
hire_date DATE
);
INSERT INTO employees (employee_id, last_name, first_name, hire_date)
SELECT employee_id, last_name, first_name, hire_date
FROM _employees_old;
COMMIT;
PRAGMA foreign_keys=on;
This example will rename our existing employees table to _employees_old.
Then it will create the new employees table with the last_name field defined as a VARCHAR datatype. Then it will insert all of the data from the _employees_old table into the employees table.

How to set a default value to a TEXT column in Sqlite?

I have a simple table. I'm trying to put a default value to a TEXT column. Here is the table query:
"CREATE TABLE book(_id INTEGER PRIMARY KEY AUTOINCREMENT, book_id TEXT NOT NULL DEFAULT '0', book_name TEXT NOT NULL);"
It creates the table but the problem occurs when i try to insert a data. I was only trying with giving a book name to book_name, as i expected that the book_id would have a default value 0 to the column. But it doesn't and it adds the null value, so the row doesn't get inserted. I have also tried with this query:
"CREATE TABLE book(_id INTEGER PRIMARY KEY AUTOINCREMENT, book_id TEXT NOT NULL DEFAULT \'0\', book_name TEXT NOT NULL);"
But the problem remains the same. I have searched the stack overflow, and got some answers but they are old and not working for me right now. So has something changed on how to set the default value to a TEXT column in sqlite. Thanks in advance :)
EDIT
Here is the insert statement:
database.insert("book", null, cv);
Here cv is the object of ContentValues which contains only the value for the column book_name.
You can specify a default value for the column when you create the table. (It doesn't appear as though you can add a default using an ALTER statement, so you'll have to recreate your table.)
CREATE TABLE your_table_name
(MainContactName TEXT NOT NULL DEFAULT '')
For Example,
CREATE TABLE book(_id INTEGER PRIMARY KEY AUTOINCREMENT,book TEXT DEFAULT "abc");
now see that , default value is set to "abc"
Check sqllite documentation.
To alter table,
sqlitedbInstance.execSQL("alter table myTable add column Address TEXT DEFAULT 'ABC' ");

Android SQLite auto increment

I currently have a table called User which has a id column which is created as
'INTEGER PRIMARY KEY'
Lets say I have created two users so the table has id 1 and 2
If I delete the second user and create a third the id is 2, I need this to be 3
So it seems Android is selecting the next available id, how can I change this to its more like a sequence number?
Regards
Make it INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL. Here's what the docs say:
If a column has the type INTEGER PRIMARY KEY AUTOINCREMENT then... the ROWID chosen
for the new row is at least one larger than the largest ROWID that has
ever before existed in that same table.
The behavior implemented by the AUTOINCREMENT keyword is subtly
different from the default behavior. With AUTOINCREMENT, rows with
automatically selected ROWIDs are guaranteed to have ROWIDs that have
never been used before by the same table in the same database. And the
automatically generated ROWIDs are guaranteed to be monotonically
increasing.
SQLite AUTOINCREMENT is a keyword used for auto incrementing a value of a field in the table. We can auto increment a field value by using AUTOINCREMENT keyword when creating a table with specific column name to auto incrementing it.
The keyword AUTOINCREMENT can be used with INTEGER field only.
Syntax:
The basic usage of AUTOINCREMENT keyword is as follows:
CREATE TABLE table_name(
column1 INTEGER AUTOINCREMENT,
column2 datatype,
column3 datatype,
.....
columnN datatype,
);
For Example See Below:
Consider COMPANY table to be created as follows:
sqlite> CREATE TABLE TB_COMPANY_INFO(
ID INTEGER PRIMARY KEY AUTOINCREMENT,
NAME TEXT NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR(50),
SALARY REAL
);
Now, insert following records into table TB_COMPANY_INFO:
INSERT INTO TB_COMPANY_INFO (NAME,AGE,ADDRESS,SALARY)
VALUES ( 'MANOJ KUMAR', 40, 'Meerut,UP,INDIA', 200000.00 );
Now Select the record
SELECT *FROM TB_COMPANY_INFO
ID NAME AGE ADDRESS SALARY
1 Manoj Kumar 40 Meerut,UP,INDIA 200000.00
If speaking for ANDROID, yes, above answers are correct, except naming of the id column.
database.execSQL("CREATE TABLE IF NOT EXISTS "
+ TableName
+ " ( rowid INTEGER PRIMARY KEY AUTOINCREMENT, Raqam VARCHAR, ChandBor INT(3));");
It looks like in Android it should be named as 'rowid'.
And with Cursor you need to instantiate it like:
Cursor cursorLcl = database.rawQuery("SELECT *," + TableName + ".rowid AS rowid" + " FROM " +
TableName, null);
Otherwise it didnt work for me. I don't know why it so.
Just remember for android when writing tot the database (ie. executing),
do
INSERT INTO TABLE_NAME (param1name, param2name) VALUES (param1,param2)
and there is no need to add a place holder for the auto increment. It will add it by itself when adding a record. If you do not declare the params that you will put in, you will get the error x amount of variables expected and you only gave x-1, and this is because you are not supposed to give any place holding value for the auto increment column

Categories

Resources