Nando wrote: Hello, I would like to rationalize the way metadata items load their state from the database; currently YTable has loadColumns() and checkAndLockColumns(), YProcedure has loadParameters() and checkLoadParameters(), I myself have just added something similar (loadProperties()) to YException, etc. I was thinking of bringing all this under the umbrella of YxMetadataItem, with the following semantics: // invalidates everything the item has read from the database; // this means that next time a property is requested it will be // read anew from the database. // Some classes might support selective invalidation of portions // of data (e.g. "columns", or "indexes"), and that's what // propertySetName is for. By default, invaliate() invalidates // everything and calls notify(). void YxMetadataItem::invalidate(std::string propertySetName = ""); The item would keep one of more internal flags to account for what's invalidated and reload it on request. For a sample of the benefits of the approach, this code excerpt from database.cpp: if (t == ntTable || t == ntView) result = ((YxMetadataItemWithColumns *)object)->loadColumns(); else if (t == ntProcedure) result = ((YProcedure *)object)->checkAndLoadParameters(); else if (t == ntException) ((YException *)object)->loadProperties(true); else object->notify(); (to which I have just contributed the third branch, BTW) would become: object->invalidate(); Comments? Remarks? Better ideas? Milan replied: | I would like to rationalize the way metadata items load their state | from the database; currently YTable has loadColumns() and | checkAndLockColumns(), YProcedure has loadParameters() and | checkLoadParameters(), I myself have just added something similar | (loadProperties()) to YException, etc. | | I was thinking of bringing all this under the umbrella of | YxMetadataItem, with the following semantics: | | // invalidates everything the item has read from the database; [snip] Very good. I have thought that something like this will soon be needed anyway. | // By default, invaliate() invalidates | // everything and calls notify(). We would have to be careful about this so we don't enter the endless loop. I guess we can use the locking mechanism that is already there. | For a sample of the benefits of the approach, this code excerpt from | database.cpp: | | if (t == ntTable || t == ntView) | result = ((YxMetadataItemWithColumns *)object)->loadColumns(); | else if (t == ntProcedure) | result = ((YProcedure *)object)->checkAndLoadParameters(); | else if (t == ntException) | ((YException *)object)->loadProperties(true); | else | object->notify(); | | (to which I have just contributed the third branch, BTW) would become: | | object->invalidate(); Well, not really since for tables/view/procedures you only have to invalidate columns/parameters (exactly what you wrote about: selective invalidation). Anyway, I fully agree what you wrote. We can have a parameter to invalidate() which will specify what is invalidated. Some objects would consider it, others ignore. One more remark: table->invalidate(columns) doesn't have to call notify() since reloading of columns will do that anyway, so a locking mechanism should really be used. Nando replied: M> | For a sample of the benefits of the approach, this code excerpt from M> | database.cpp: M> | M> | if (t == ntTable || t == ntView) M> | result = ((YxMetadataItemWithColumns *)object)->loadColumns(); M> | else if (t == ntProcedure) M> | result = ((YProcedure *)object)->checkAndLoadParameters(); M> | else if (t == ntException) M> | ((YException *)object)->loadProperties(true); M> | else | object->>notify(); M> | M> | (to which I have just contributed the third branch, BTW) would become: M> | | object->>invalidate(); M> Well, not really since for tables/view/procedures you only have to M> invalidate columns/parameters (exactly what you wrote about: selective M> invalidation). aren't columns/parameters all that those objects hold anyway? Huh no, tables have also constraints, for example. This needs a bit more thought... Premise: the code above comes from database.cpp, and it runs when an executed SQL (mostly DDL) statement is parsed by YDatabase to see if any objects need refreshing. The problem is that only the metadata object itself knows ultimately what should be invalidated as a result of executing a SQL statement, so the solution is to let all metadata objects in turn have a peek at the statement and let each of them do whatever is needed to. IOW instead of letting the YDatabase govern everything, we use a cooperative network to achieve the same. This can be implemented at different degrees depending on the level of efficiency (did I say that? ) we want to achieve: a) the YDatabase hands the SQL statement off to each metadata object it holds in turn. Each metadata collection hands it to the items it contains (look, here's another reason for having concrete metadata collections!). This could be wasteful as potentially many items would need to parse the statement again and again. I said "could" because we might well be talking about millisecond-scale delays anyway. b) the YDatabase pre-parses the statement, finds the object kind and name (as it already does), locates the relevant metadata object and hands it the statement. This is slightly less flexible but probably faster. c) Note: in many cases the SQL statement itself comes from a metadata object from the beginning. In other cases the user typed it freely in the SQL window. If we didn't have to support the latter scenario then a simpler variant of option b would be enough and the YDatabase wouldn't even be involved at all. But we do have to support it, right? M> One more remark: table->invalidate(columns) doesn't have to call M> notify() since reloading of columns will do that anyway, so a locking M> mechanism should really be used. In perspective I would even like to make notify() protected, so that each object deals with this stuff on its own. You just call a setXXX() member function and it sees by itself what's to invalidate/lock/notify, etc. Milan replied: | M> Well, not really since for tables/view/procedures you only have to | M> invalidate columns/parameters (exactly what you wrote about: selective | M> invalidation). | | aren't columns/parameters all that those objects hold anyway? Huh | no, tables have also constraints, for example Yep, and tables views might also get some links to triggers too. | a) the YDatabase hands the SQL statement off to each metadata object | it holds in turn. Each metadata collection hands it to the items it | contains (look, here's another reason for having concrete metadata | collections!). This could be wasteful as potentially many items would | need to parse the statement again and again. I said "could" because we | might well be talking about millisecond-scale delays anyway. Not needed IMHO. | b) the YDatabase pre-parses the statement, finds the object kind and | name (as it already does), locates the relevant metadata object and | hands it the statement. This is slightly less flexible but probably | faster. I think this will do. Also, note that DROP statements should be handles by YDatabase, so if it is going to parse the string anyway, we can use it for other things too. | c) Maybe the same as b) but send the parsed pieces too? Although I haven't got the idea how to do that cleanly. | Note: in many cases the SQL statement itself comes from a metadata | object from the beginning. In other cases the user typed it freely in | the SQL window. If we didn't have to support the latter scenario then | a simpler variant of option b would be enough and the YDatabase | wouldn't even be involved at all. But we do have to support it, right? It's a must. User could always type some ALTER (or some other) statement that we don't provide via GUI, and object should refresh. Also, this way we can centralize all DDL processing, and make logging stuff easy. It also let's various program parts just dump SQL into ExecuteSqlFrame and don't "think" about it. | M> One more remark: table->invalidate(columns) doesn't have to call | M> notify() since reloading of columns will do that anyway, so a locking | M> mechanism should really be used. | | In perspective I would even like to make notify() protected, so that | each object deals with this stuff on its own. You just call a setXXX() | member function and it sees by itself what's to | invalidate/lock/notify, etc. Maybe not a bad idea. I can't foresee how will it work in practice though. I guess we will have to test and see.