I changed the implementation of the domain model of DBConan Zeta. In the previous entry I mentioned the domain object (Schema, Table, Column, etc.) will make use of QExplicitlySharedDataPointer. Well..., I decided this weekend to use QSharedDataPointer instead. More on that later.
Second, on the trick to avoid that "field has incomplete type". In the previous entry I said "use pointer". Now I decided to get rid of pointers altogether. It looks so un-QT-ish :) to me. I might be wrong here..., but the * signs hurt my eyes :).
Oh no, I'm slowly becoming a QT zealot. It reminds me of my early days learning Java 12 years ago....Source code here: http://www.box.net/shared/8zt5p6u9vz
Ok, on the jump from QExplicitlySharedDataPointer to QSharedDataPointer. Why? At first I thought QExplicitySharedDataPointer is the way to go, because -- the way I understand it -- it practically gives the effect of "pass-by-reference", which is the default mode of Java / C#.
For those who are not familiar with QT: QSharedDataPointer means implicit sharing..., while QExplicitlySharedDataPointer means explicit sharing.
In practical terms, implicit sharing means: when you modify an object, from within a function (that gets passed in the object), the caller of the function -- the one that passed the object into the function -- will not see the change you made on that object. Surprise (!). The reason is, at the point a non-const function is invoked on that object, the sharing is automatically broken; a deep copy is made, which means that object now has its own set of data, so that any changes made by that function would not ripple (inadvertently) to other part(s) of the program.
Hmm..., actually I'm not very happy with the way I explain it :). So, please head over to the authoritative source (in QT ref.) instead.
Ok, sounds like implicit sharing is not-intuitive (at least for Java / C# programmers). In Java / C#, by default, we expect the change we made in an object to ripple through the program (and we do some tricks when we want otherwise).
Here, with implicit sharing, it's exactly the opposite. So, why not use explicit sharing instead? Well, in a few words: I found it hard to achieve consistency with explicit sharing, partly because my domain objects have member fields of types that use implicit sharing (e.g.: QString and QList). Mixing implicit sharing and explicit sharing, in my opinion, is not a good thing. It will only lead to confusion.
Ok, let's suppose the Schema object is explicitly shared. Suppose you pass that Schema to a function (xxxFunc). Inside the function you obtain the list of Tables that belong to the schema. Then, you append another instance of Table to that list. You would expect that change to be seen from the part where you invoke the xxxFunc. Chance is: that's not the case. Because QList is implicitly shared.
So..., I thought "I don't want to mess with that. Let's stick with one approach only, implicit sharing". The (only) problem, or I'd rather say "consequence", with that decision is...: it requires a shift in my way of thinking (in the rest of the program). I will have to keep in mind (everytime I pass an object to a function): it's a pass-by-value, it's a pass-by-value.
That may not be as bad as it sounds. I think it has something to do with immutability. I remember Joshua Bloch in his book (Effective Java) mentions "favor immutability". Maybe this pass-by-value will force us to lean ourselves more toward that principle. I don't know..., we'll see. I'll report back on that later.
In the meantime you can download the C++ source code. Compare it with dbconan_schema.py below (complete python source: here). You see, there are many checks -- that are available in the python version -- currently not implemented in the C++ port. I'll get to that later. Ciao!
UPDATE: I also decided to change the target database from Oracle to Postgre. The reason: I don't have time to (re)compile QT on my Ubuntu Linux in order to have the QT's Oracle SQLDriver. On the other hand, Postgre SQLDriver comes out of the box (when we install QT SDK from the repository).dbconan_schema.py
In the Python+Oracle version the program makes heavy use of Oracle Data Dictionary (that's how it learns the structure of the database).
The equivalent of it in Postgre is "information schema". Here are a few tables in the information_schema that I think will be useful for the program:
select * from information_schema.tables where table_schema = 'public' -- public is the default schema name
select * from information_schema.columns where table_name = 'table_a'
select * from information_schema.constraint_column_usage
select * from information_schema.key_column_usage
select * from information_schema.referential_constraints
select * from information_schema.constraint_table_usage
select * from information_schema.table_constraint