Data not being saved to core data - iphone

I am using Xcode 5 compiling exclusively for iOS 7.
I am reading data from a UTF8 txt file to populate a core data entity. After reading the data, I log it to the console. The data is there. I populate the entity and log the entity to the console. I save it. No error. No crash.
I see 3 files on the device:
MyDatabase.sqlite
MyDatabase.sqlite-shm
MyDatabase.sqlite-wal
When the app starts and MyDatabase.sqlite is created empty it has 40 kb. At this point the shm file is 32kb and the wal file is zero.
After I write the data to the database, the wal files grows to 1,7 Mb but the other two files keep their initial sizes or in other words, the data is not being saved to the database. I have confirmed that by inspecting the sqlite file with an external database viewer.
This is the code I am using:
// NSArray *arrayOfYears = ...
// this contains an array of numbers read from the CSV file
// at this point the array contains numbers in text format
// NSArray *arrayOfBrands = ...
// this is an array of brands
for (int i=0; i<[arrayOfBrands count]; i++) {
Cars *car = [NSEntityDescription insertNewObjectForEntityForName:#"Cars" inManagedObjectContext:context];
car.brand = [arrayOfBrands objectAtIndex:i];
car.year = [NSNumber numberWithInt:[[arrayOfYears objectAtIndex:i] integerValue]];
NSError *error = nil;
if (![context save:&error]) {
// Handle the error.
NSLog(#"error = %#", error);
}
}
This produces no error or crash. It goes like it was saving. I have increased mySql debug level to 3 and this is what I see on console...
when the table is created empty
CoreData: annotation: Connecting to sqlite database file at "/var/mobile/Applications/BB22334C4-550A-4C44-B17A-3F02062EC687/Documents/MyDatabase.sqlite"
CoreData: annotation: creating schema.
CoreData: sql: pragma page_size=4096
CoreData: sql: pragma auto_vacuum=2
CoreData: sql: BEGIN EXCLUSIVE
CoreData: sql: SELECT TBL_NAME FROM SQLITE_MASTER WHERE TBL_NAME = 'Z_METADATA'
CoreData: sql: CREATE TABLE ZCARS ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZYEAR INTEGER, ZBRAND VARCHAR )
CoreData: annotation: Creating primary key table.
CoreData: sql: CREATE TABLE Z_PRIMARYKEY (Z_ENT INTEGER PRIMARY KEY, Z_NAME VARCHAR, Z_SUPER INTEGER, Z_MAX INTEGER)
CoreData: sql: INSERT INTO Z_PRIMARYKEY(Z_ENT, Z_NAME, Z_SUPER, Z_MAX) VALUES(1, 'Cars', 0, 0)
CoreData: sql: CREATE TABLE Z_METADATA (Z_VERSION INTEGER PRIMARY KEY, Z_UUID VARCHAR(255), Z_PLIST BLOB)
CoreData: sql: SELECT TBL_NAME FROM SQLITE_MASTER WHERE TBL_NAME = 'Z_METADATA'
CoreData: sql: DELETE FROM Z_METADATA WHERE Z_VERSION = ?
CoreData: details: SQLite bind[0] = 1
CoreData: sql: INSERT INTO Z_METADATA (Z_VERSION, Z_UUID, Z_PLIST) VALUES (?, ?, ?)
CoreData: details: SQLite bind[0] = 1
CoreData: details: SQLite bind[1] = "C6E2268B-6792-4298-B292-5025E5BDE31A"
CoreData: details: SQLite bind[2] = <NSData len=455>
CoreData: annotation: Saving new meta data; version = 1 ; UUID = C6E2268B-6792-4298-B292-5025E5BDE31A
CoreData: sql: COMMIT
CoreData: sql: pragma journal_mode=wal
CoreData: sql: pragma journal_mode=wal
CoreData: sql: pragma cache_size=200
CoreData: sql: SELECT Z_VERSION, Z_UUID, Z_PLIST FROM Z_METADATA
after saving one brand/year
CoreData: sql: BEGIN EXCLUSIVE
CoreData: sql: SELECT Z_MAX FROM Z_PRIMARYKEY WHERE Z_ENT = ?
CoreData: annotation: getting max pk for entityID = 4
CoreData: sql: UPDATE Z_PRIMARYKEY SET Z_MAX = ? WHERE Z_ENT = ? AND Z_MAX = ?
CoreData: annotation: updating max pk for entityID = 4 with old = 0 and new = 1
CoreData: sql: COMMIT
CoreData: sql: BEGIN EXCLUSIVE
CoreData: sql: INSERT INTO ZTABULEIRO(Z_PK, Z_ENT, Z_OPT, ZYEAR, ZBRAND) VALUES(?, ?, ?, ?, ?)
CoreData: details: SQLite bind[0] = (int64)1
CoreData: details: SQLite bind[1] = (int64)4
CoreData: details: SQLite bind[2] = (int64)1
CoreData: details: SQLite bind[3] = "FORD"
CoreData: details: SQLite bind[4] = (int64)1989
CoreData: sql: COMMIT
CoreData: annotation: Changing objectID 0x14e71730 <x-coredata:///Cars/t38FA86EE-4124-4FD7-A8C0-8CE7BBAC73782> to 0x14e73ef0 <x-coredata://C6E3368B-6792-4298-B292-5025E5BDE31A/Cars/p1>
CoreData: sql: pragma page_count
CoreData: annotation: sql execution time: 0.0014s
CoreData: sql: pragma freelist_count
CoreData: annotation: sql execution time: 0.0016s
one strange thing I see here is the year variable like int64 when I have defined it like integer 16. Why it is using integer 64 is beyond me...
The data is not being saved at all. The data is there. If I log the arrays of data to console I see the data. If I log the entities populated before saving I see the data.
I am testing this on a device running iOS 7. Thanks.
What am I missing? How do I debug that?

The data is being saved to the database if the write-ahead-log (wal) is increasing in size, See documentation here http://www.sqlite.org/draft/wal.html. What are you using to open the database file? You must use a tool that works with WAL journal mode. Rather than looking at the db files directly, try creating a new context and fetching your saved entities.

Related

Make insert in view work with EF6 and SQLite

I created a entity based on a sqlite view, and I want to insert data in it using EF6. I altered the .edmx file to make EF see the view as a table and define a primary key on the view.
The problem is that when my program tries to insert data into the view, (which of course has a INSTEAD OF INSERT trigger that insert the data on 2 underlying tables of the view), I get the exception:
Store update, insert, or delete statement affected an unexpected
number of rows (0). Entities may have been modified or deleted since
entities were loaded. Handling optimistic concurrency exceptions.
The underlying query called from EF, is that:
Opened connection at 12/07/2019 16:58:18 +02:00
Started transaction at 12/07/2019 16:58:18 +02:00
INSERT INTO [testcasesView]([name], [deviceversion_id], [user_id], [timestamp], [comment], [static_result], [description], [precondition], [action], [expected_result], [reviewed], [automated])
VALUES (#p0, #p1, #p2, #p3, NULL, #p4, #p5, #p6, #p7, #p8, #p9, #p10);
SELECT [id]
FROM [testcasesView]
WHERE last_rows_affected() > 0 AND [id] = last_insert_rowid()
;
-- #p0: 'asd' (Type = String)
-- #p1: '67' (Type = Int64)
-- #p2: '20' (Type = Int64)
-- #p3: '1562943498' (Type = Int64)
-- #p4: 'as' (Type = String)
-- #p5: 'asd' (Type = String)
-- #p6: 'asd' (Type = String)
-- #p7: 'asd' (Type = String)
-- #p8: 'asd' (Type = String)
-- #p9: '0' (Type = Int64)
-- #p10: '0' (Type = Int64)
-- Executing at 12/07/2019 16:58:18 +02:00
-- Completed in 1 ms with result: SQLiteDataReader
Closed connection at 12/07/2019 16:58:18 +02:00
Disposed transaction at 12/07/2019 16:58:18 +02:00
I understood why but I don't know if I can solve this. The problem is that since the real insert happens inside the trigger (I don't post the code because it's two simple insert), the query
SELECT [id]
FROM [testcasesView]
WHERE last_rows_affected() > 0 AND [id] = last_insert_rowid()
Returns no contents, then EF thinks that no rows was affected.
Is there a statement or code that I can add to my trigger that makes EF see that the insert happened?

sqlalchemy to create temporary table

I created a temporary table with sqlalchemy (with an underlying postgres database) that is going to be joined with a database table. However, in some cases when a value is empty '' then postgres throws the error:
failed to find conversion function from unknown to text
SqlAlchemy assembles everything to the following context
[SQL: 'WITH temp_table AS \n(SELECT %(param_1)s AS id, %(param_2)s AS email, %(param_3)s AS phone)\n SELECT campaigns_contact.id, campaigns_contact.email, campaigns_contact.phone \nFROM campaigns_contact JOIN temp_table ON temp_table.id = campaigns_contact.id AND temp_table.email = campaigns_contact.email AND temp_table.phone = campaigns_contact.phone'] [parameters: {'param_1': 83, 'param_2': '', 'param_3': '+1234567890'}]
I assemble the temporary table as follows
stmts = []
for row in import_data:
row_values = [literal(row[value]).label(value) for value in values]
stmts.append(select(row_values))
subquery = union_all(*stmts)
subquery = subquery.cte(name="temp_table")
The problem seems to be the part here
...%(param_2)s AS email...
which after replacing the param_2 results in
...'' AS email...
which will cause the error mentioned above.
One way to solve the issue is to perform a cast
...''::text AS email...
However, I don't know how to perform ::text cast with sqlalchemy!?

updating multiple rows of 2 columns with different values (db2 sql)

I have a table in which I need to change the values of a couple of columns in multiple rows.
The table with values to be changed is like:
The code I have tried containing updated values, with no success, is:
UPDATE <table_name>
SET (IDENTIFIER_1, IDENTIFIER_2)
VALUES (1635, 1755),
(2024, 2199),
(1868, 1692),
(3577, 4825)
WHERE ID
IN ('1',
'23',
'54',
'21');
To be honest, I am not sure if this is even supported in db2 SQL. The error is:
[Error Code: -104, SQL State: 42601] DB2 SQL Error: SQLCODE=-104, SQLSTATE=42601, SQLERRMC=update *
I should also advise that I am a db2 newbie.
You can always use Merge
MERGE INTO TABLE1
USING (
VALUES (1,1635, 1755),
(23,2024, 2199),
(54,1868, 1692) ) dummytable(ID_T, INF1,INF2)
on table1.id_table = dummytable.id_t
when matched
then UPDATE set TABLE1.IDENTIFIER_1 = dummytable.INF1
, TABLE1.IDENTIFIER_2 = dummytable.INF2
else ignore

IF EXISTS not recognized in Derby

DROP TABLE IF EXISTS Pose ;
results in the error
Error code -1, SQL state 42X01: Syntax error: Encountered "EXISTS" at line 1, column 15.
I'm running this from inside NetBeans 7.3 using the default Derby sample db.
Derby does not currently support IF EXISTS
Are you trying to create a table? If yes, this is what you should do:
public void createTables() throws SQLException {
Statement statement = getConnection().createStatement();
System.out.println("Checking database for table");
DatabaseMetaData databaseMetadata = getConnection().getMetaData();
ResultSet resultSet = databaseMetadata.getTables(null, null, "PATIENT", null);
if (resultSet.next()) {
System.out.println("TABLE ALREADY EXISTS");
} else {
//language=MySQL
statement.execute("CREATE TABLE Patient (" +
"CardNumber CHAR(10) NOT NULL PRIMARY KEY, " +
" FirstName CHAR(50)," +
" MiddleName CHAR(50)," +
" LastName CHAR(50) )");
}
}
Remember to use all caps for the table name you pass into databaseMetadata.getTables(...)
The MySQL 6.0 syntax for declaring a table is this:
CREATE TABLE [IF NOT EXISTS] tableName ...
and the MySQL syntax for removing a table is this:
DROP TABLE [IF EXISTS] tableName ...
These clauses are MySQL extensions which are not part of the ANSI/ISO SQL Standard. This functionality may be peculiar to MySQL: I can't find anything similar documented for Derby, Postgres, Oracle, or DB2.
The best alternative I can find is to query the system tables to see if the table exists.
select count(*) from sys.systables where tablename = 'YOUR_TABLE_NAME'"
I had a similar issue dropping stored procedures. They can be queried using this statement.
select count(*) from sys.sysaliases where alias = 'YOUR_STORED_PROCEDURE_NAME'
If someone is looking to drop and create a table in an sql file that is Run with Spring test framework, Check https://stackoverflow.com/a/47459214/3584693 for an answer that ensures that no exception is thrown when drop table is invoked when said table doesn't exist.

Core Data: Is it possible to use custom function in group by

Is it possible to use custom function (strftime) in group by part when crafting NSFetchRequest in objective-c? The sql statements is perfectly valid in sqlite:
select date, count(*) from note group by strftime('%Y%m%d', date)
However, after a day of digging and trying various ways and solutions I came to the conclusion it's impossible to do it with core data in single sql fetch. I'm using NSFetchRequest with setPropertiesToGroupBy (iOS 5.0 only).
UPDATE. There's an example for getting such behavior with core data provided by apple. However, I have enabled "-com.apple.CoreData.SQLDebug 1" for showing SQL performed by core data. It appears, core data is not performing my desired SQL. It just selects them all with a first sql query, and then loops through the groups performing multiple "SELEC .. WHERE ID IN ...." SQL queries. An excerpt from console:
2012-07-05 10:34:57.244 DateSectionTitles[1139:fb03] CoreData: sql: SELECT 0, t0.Z_PK FROM ZEVENT t0 ORDER BY t0.ZTIMESTAMP
2012-07-05 10:34:57.245 DateSectionTitles[1139:fb03] CoreData: annotation: sql connection fetch time: 0.0010s
2012-07-05 10:34:57.246 DateSectionTitles[1139:fb03] CoreData: annotation: total fetch execution time: 0.0015s for 52 rows.
2012-07-05 10:34:57.247 DateSectionTitles[1139:fb03] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZTIMESTAMP, t0.ZTITLE FROM ZEVENT t0 WHERE t0.Z_PK IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY t0.ZTIMESTAMP LIMIT 20
2012-07-05 10:34:57.248 DateSectionTitles[1139:fb03] CoreData: annotation: sql connection fetch time: 0.0012s
2012-07-05 10:34:57.249 DateSectionTitles[1139:fb03] CoreData: annotation: total fetch execution time: 0.0022s for 20 rows.
2012-07-05 10:34:57.250 DateSectionTitles[1139:fb03] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZTIMESTAMP, t0.ZTITLE FROM ZEVENT t0 WHERE t0.Z_PK IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY t0.ZTIMESTAMP LIMIT 20
2012-07-05 10:34:57.281 DateSectionTitles[1139:fb03] CoreData: annotation: sql connection fetch time: 0.0303s
2012-07-05 10:34:57.281 DateSectionTitles[1139:fb03] CoreData: annotation: total fetch execution time: 0.0310s for 20 rows.
Yes you can. Assemble your NSFetchedResultsController similar to:
NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:someManagedObjectContext
sectionNameKeyPath:#"yearOnly" // <==
cacheName:#"cacheAsneeded"];
Then inside your NSManagedObject class add a method to be used with sectionNameKeyPath
- (int)yearOnly { //
NSCalendar *cal = [NSCalendar currentCalendar];
return [[cal components:NSYearCalendarUnit fromDate:self.date] year];
}
The request will return the results grouped by the year so that each encountered year will become a section.
If you need a grouping by month too, you might want to change the calendar components appropriately.
For the count(*) add an NSExpression to your fetchRequest:
NSExpression * many = [NSExpression expressionForFunction:#"count:" arguments:[NSArray arrayWithObject:[NSExpression expressionForKeyPath:#"someAttribute"]]];