Tuesday, 28 December 2010

SQL details

This is mainly a reply to anonymous from the last post's comments.

The database uses Android's built-in SQLite3. It's a local database - a tad
overkill at this point, but writing a dataprovider seems to be the easiest way to send potentially large arrays of data to the apps.

So what I'm doing, or going to be doing at any rate, is building up scene/narrative lists from XML and using them to drive the plot segments.

I think the SQL itself is all right. I have three tables:

                        mk_table(db, "create table narrative ("
                                + "     " + idstring + ", "
                                + "     ev_name varchar not null"
                                + ");"
                        );
                        count_narratives(db);

                        mk_table(db, "create table narrative_string ("
                                + "     " + idstring
                                + "     , ev_name varchar not null"
                                + "     , seq integer not null"
                                + "     , text_id integer not null"
                                + ");"
                        );

                        mk_table(db, "create table narrative_image ("
                                + "     " + idstring
                                + "     , ev_name varchar not null"
                                + "     , seq integer not null"
                                + "     , image_id integer not null"
                                + ");"
                        );


Where mk_table is defined thusly:

                private void mk_table(SQLiteDatabase db, String sql)
                {
                        Log.d(TAG, "creating table: " + sql);
                        db.execSQL(sql);
                }


The count_narratives function is just to sanity check things:

                private int count_narratives(SQLiteDatabase db)
                {
                        Cursor c;
                        String sql = "select count(*) n from " + TAB_NARRATIVE;

                        c = db.rawQuery(sql, null);
                        if(c == null) {
                                Log.d(TAG, "count_narratives: can't get cursor");
                                return -1;
                        }
                        Boolean bv = c.moveToFirst();
                        if(!bv) {
                                Log.d(TAG, "count_narratives: no rows for count(*)!");
                                return -1;
                        }
                        int n = c.getInt(0);
                        c.close();
                        Log.d(TAG, "count_narratives: "+n+" narratives added!");
                        return n;
                }


So if the table creates, that should always work. After I create the narrative table it tells me the table has zero rows (and therefore that it exists). So far so good.

I then populate the database:

                        Log.d(TAG, "starting intro narrative.");
                        int seq = 1;
                        db.beginTransaction();
                        insert_narrative(db, "intro", seq++, R.string.intro1, R.drawable.alley);
                        count_narratives(db);
                        insert_narrative(db, "intro", seq++, R.string.intro2);
                        count_narratives(db);
                        insert_narrative(db, "intro", seq++, R.string.intro3);
                        count_narratives(db);
                        insert_narrative(db, "intro", seq++, R.string.intro4);
                        count_narratives(db);
                        insert_narrative(db, "intro", seq++, R.string.intro5);
                        count_narratives(db);
                        insert_narrative(db, "intro", seq++, R.string.intro6);
                        insert_narrative(db, "intro", seq++, R.string.intro7);
                        insert_narrative(db, "intro", seq++, R.string.intro8);
                        insert_narrative(db, "intro", seq++, R.string.intro9, R.drawable.girl1);
                        insert_narrative(db, "intro", seq++, R.string.intro10);
                        insert_narrative(db, "intro", seq++, R.string.intro11);
                        insert_narrative(db, "intro", seq++, R.string.intro12);
                        db.endTransaction();
                        Log.d(TAG, "final narrative count.");
                        count_narratives(db);
                        Log.d(TAG, "final narrative count done.");
                        Log.d(TAG, "end of intro narrative.");


The count_narratives calls in here show the table continuing to exist, and with the expected number of rows. Again: so far, so good.

Then I try and retrieve the data:

                String sql = ""
                        + "select\n"
                        + "     e.ev_name,\n"
                        + "     s.seq,\n"
                        + "     s.text_id,\n"
                        + "     i.image_id\n"
                        + "from\n"
                        + "     "+TAB_NARRATIVE+" e\n"
                        + "     left outer join narrative_string s on\n"
                        + "             e.ev_name = s.ev_name\n"
                        + "     left outer join narrative_image i on\n"
                        + "             e.ev_name = i.ev_name\n"
                        + "where\n"
                        + "     e.ev_name = '" + narrative_name + "'\n"
                        + "order by\n"
                        + "     1, 2\n"
                ;


And for some reason the table no longer exists. So either A) I'm accidentally dropping the data after creation (possible, but I don't know where) or B) I'm accidentally opening a non-existent database for the query and SQLite is creating a new empty database, or else C) something very weird is happening.

I'm tending toward B) myself.

4 comments:

Anonymous said...

I presume that "TAB_NARRATIVE" == table "narrative" but what should be the value of narrative_name be?

Also could you post what should be the values of a few of the fields of the various tables because it seems I can't make sense of the code for populating the database.

P.S. I guess you know about that but just in case: http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html
I'm afraid my Android programing skills are nonexistent since I don't own one yet, but do you think you can extract the DB file from the device in order to verify that the tables are populated correctly?

P.S.S. Also is it a problem with my browser or is your blog's template using black text (for the already visited links) on black background?

Doc Clox said...

Yep, TAB_NARRATIVE is "narrative".

Sample data:

narrative
_ID ev_name
0 "intro"
1 "market1"

narrative_image
_ID ev_name seq image_id
0 "intro" 1 "alleyway.jpg"
1 "intro" 9 "girl1.jpg"


narrative_text
_ID ev_name seq text_id
0 "intro" 1 "Crossgate. A dirty little frontier town..."
1 "intro" 2 "My grandad was rich; a successful merchant..."

The _ID field is an android internal. It gets used to tie data into forms, I
belive. I'm not using that feature yet, but it seemed good practice.

The image and text_id columns take integers which refer to strings and images in the applications resource folder. Since the numbers aren't especially meaningful, I've replaced them here with the strings and filenames.

I have checked the database file, and the tables are not there. So either they're getting dropped by something, or I'm creating them in an in-memory db that needs something special to write it out.

Template problem should be fixed - it's been bugging me, too :)

Anonymous said...

If I use "intro" as a value for "narrative_name" in the SQL query for retrieving the data I get no result (0 rows) from the sample data you provided (the same happens even if I remove the "where e.ev_name = "intro" order by 1, 2" part):

select e.ev_name, s.seq, s.text_id, i.image_id from NARRATIVE e left outer join narrative_string s on e.ev_name = s.ev_name left outer join narrative_image i on e.ev_name = i.ev_name where e.ev_name = "intro" order by 1, 2;

My guess is that you have a problem in the DB population code or the data is not written after that - maybe you need to close the DB and then reopen it for that or maybe you are not specifying in which table should the data be written.

Doc Clox said...

Yeah, that's my guess too. I have to be doing something silly. Probably not helped by the fact I've been loaded with 'flu when trying to work this lot out.

I'll come back to the problem, I think, Right now I want to work out how to give orders to the girls without turning it into a nightmare of micromanagement.