Saturday, October 24, 2009

Wizz Jones: The legendary me - and musings on music samplers

I wrote some time ago about my musical adventures in 1971, a year which in retrospect was very important for the formation of my musical tastes. In that entry, there was a reference to local (to Bristol) folk artist Dave Evans; Dave recorded for a local record company which has since apparently become legendary, "The Village Thing". They had an office close to my school, and during lunch-hours I often used to go to that office.

The first release that I purchased from TVT was a four track mini-single called "The Great White Dap" (a dap is the primordial ancestor of what is now called a trainer, i.e. a soft shoe which was worn in sporty events). This featured one song each from the first VT releases: Ian A. Anderson, The Pigsty Hill Light Orchestra (somewhat similar to The Temperance Seven or The Bonzos), a Welsh duo called 'The Sun Also Rises' and guitarist Wizz Jones.

Jones' contribution was called "See how the time is flying", which I learnt to play in short order. This was always an intriguing song harmonically, as its verse starts in Dm and ends in Em. Instead of trying to make a harmonically pleasing transaction between these keys, Jones simply starts the next verse with an abrupt change to Dm.

Later on, TVT released a sampler lp with twelve tracks, including Jones' "Beggarman". Again, I enjoyed this song, but apparently not enough in order to buy its parent album, "The legendary me". Obviously my money at the time had more important claimants.

Fast forward maybe thirty five years; money is no longer a problem and anyway my record buying days are virtually over. I began a search for those Village Thing records of my youth, and quickly found Dave Evans' initial record which I wrote about in that blog entry, "The words inbetween". Lately I have found as downloads Hunt and Turner's "Magic Landscape" and Ian A. Anderson's "A vulture is not a bird you can trust". I have yet to find its predecessor, "Royal York Crescent", named after the street where Anderson lived at the time, and where he recorded some of Evans' songs. I used to visit him there.

- digression
I think that Jim Hunter, my English teacher at school, lived there as well, and he told me that another resident at the time was novelist Angela Carter, whose early book "Several perceptions" had become essential reading for me, and even the basis for an exercise in turning prose into poetry. I realised that the district in which Carter's book was set was indeed the arty Clifton district of Bristol, where the above crescent is situated. Hunter wrote a biography of poet Gerard Manley Hopkins, which led to the school graffito "Hunter puts the man into Gerard Manley Hopkins".
-end of digression

For several years, I have been on the trail of a cd release of Wizz Jones' "The legendary me", and eventually found it a few weeks ago. I've been listening to it on and off the past week, and reluctantly have come to the conclusion that the two songs which I knew in 1971 were the highlights of the disk, and that had I heard some of the other songs, I wouldn't have bothered tracking down this disk. In certain aspects, Jones might have been a role model for my music - clean acoustic guitar picking, roughish vocals - had my harmonic vocabulary not been irrevocably twisted by Sandy Denny's "North Star Grassman" in the autumn of 1971.

In the early 1970s, the sampler was a legitimate way of allowing impecunious people the chance to hear a wide variety of artists signed to a label and so inform them of the quality of those artists. The Island samplers ("You can all join in", "Nice enough to eat", "Bumpers" and "El pea") immediately come to mind, but there were also successful samplers from CBS ("The rock machine turns you on"), Harvest, Vertigo and others. Versions of these samplers can now be found in the many cd compilations possibly saturating the market (for example, "Refugees: A Charisma Records Anthology 1969-1978 [Box set]".

I have often mused on the selection criteria for those samplers: did the compilers pick the best track on an album for inclusion in the sampler? Did they pick the most accessible song? Did they pick the most representative song? In terms of Wizz Jones, they (presumably the afore mentioned Anderson) definitely picked the best songs, but when I consider "Nice enough to eat", which sufficed as many a young gentleman's introduction to 'underground' music (and to the Island label), then I'm not so sure.

Certainly, Fairport Convention's track on the sampler, "Cajun woman", was neither the best, most accessible nor most representative track from its parent album, "Unhalfbricking". In my opinion, it was the worst track on this ultimate record, not too accessible and certainly not representative (it was more an experiment in applying a Cajun arrangement to what would shortly be called a modern song in the trad idiom).

Another criterion seemed to be that compilers would pick the opening song from an album, presuming that each album's producer knew what he was doing when sequencing. Thus we have "Time has told me", which opened Nick Drake's "Five leaves left", followed by King Crimson's "21st century schizoid man", which similarly held the pole position on their "Court of the crimson king" debut album. On the other hand, neither Jethro Tull's nor Blodwyn Pig's contribution were the opening track.

Another problem which I have with listening to familiar songs from forty years ago along with unfamiliar songs is that the familiar will always sound better. I hope that I am free of this bias regarding "The legendary me", and can explain lucidly why the two songs which have become part of my musical fabric are better than anything else on this album. Incidentally, the cd release has three bonus tracks added at the end; as I didn't know the original, I can't complain that these songs stick out like a sore thumb, but even so.... The first is a recording of Leonard Cohen's "Sisters of mercy"; whilst adding nothing to the original, Jones inexplicably alters the song's chord sequence, making listening to it a jarring experience for someone versed in the original.

Thursday, October 22, 2009

Dollhouse

One of our satellite tv stations showed the first episode of Fox's new series, "Dollhouse". Intrigued by the promos which have been screened daily for the past few weeks, I decided to record the programme (as it's broadcast at an inconvenient hour for me).

Yesterday morning, I watched the first ten minutes or so, completely unable to fathom what was happening. In fact, I spent most of that time trying to figure out why the leading actress (Eliza Dushko) looked so familiar*, and in doing so, completely missed the fact that an actress that I had admired only the day before (Olivia Williams) was appearing.

During the day, I looked up the IMDB reference and began to understand what the show might be telling me. In the evening, I watched the programme from beginning to end. I needn't have bothered. After the very confusing first ten minutes (consisting of three or four scenes which appear to have absolutely nothing in common aside from the aforementioned Ms Dushko), the programme then turned into a high action, low meaning adventure story which did not interest me in the slightest. Once again, I am reminded why I don't like watching American tv series (aside from The West Wing and certain cartoon families).

On the basis of this opening salvo, and considering the programme's broadcast hours, I can't see myself watching it again.

* Yes, Ms Dushko was a cheerleader in "Bring it on". She does look vaguely like singer Natalie Imbruglia.

Tuesday, October 20, 2009

Lucky break

I saw the film "Lucky Break" the other night and thoroughly enjoyed it. I don't know why most of the IMDB reviewers dissed it. True, I doubt very much that prison life is as depicted in the film; I would be more afraid of my fellow prisoners and less of the prison guards (there was a moment of such fear when Jimmy Hands had his head covered in a plastic bin liner and taken to see the new block 'king').

Any film with James Nesbitt, Bill Nighy, Timothy Spall and especially Olivia Williams is going to be a pleasurable experience. Nighy and Spall join forces again after their appearance in "Still crazy".

Side note: Nesbitt's sidekick, Rudy, is played by Lennie James. I was surprised to see that Lennie appeared in 'Cold feet' as Rachel's first husband (Kris "with a K"), whereas Nesbitt was Rachel's second husband (Adam). So, in one instance, actors Nesbitt and James were antagonists whereas in another instance, they were best mates.

A smaller but similar coincidence happened in the BBC series 'Survivors' about which I was going to write but decided not to; in my opinion, this series did not live up to its potential and was very scattered at the end. Anyway, lead actress was Julie Graham, and one of her tribe was played by the ubiquitous Paterson Joseph, who seems to be cast in a huge number of television shows (he even appeared in one or two episodes of 'Cold feet'). Julie played the eponymous Mary in 'William and Mary', and Paterson played Reuben, the father of her children.

Thursday, October 15, 2009

Another day not wasted

Today I was considering how to improve the general look of one class of my programs. These have a dbgrid and several buttons, and whilst they are functional, they are somewhat old fashioned. As a picture is worth a thousand words, here is a screen shot of the main form of one of the programs (I should point out that these programs display personal data, which in the interests of privacy I am not going to show here, so I'm showing the form as it appears at design time. Imagine that the grid is populated).

The various components which appear in the grid are non-visible.

The first improvement was to take all the buttons and place them on a gray panel

This is certainly better, but the real problem is the grid, which is "so Win 3.1". What can be done? Dispose of the grid! I decided to display the data in a listview control, which is where the fun begins. I need the listview to be displayed from right to left, which requires a magic code. Once I had done that, I discovered that the header of the listview considers itself to be autonomous; this resolutely stayed as left to right as the lines displayed right to left, making an ugly sight. More searching of the internet revealed how to get a pointer to the header of the listview, which was then fed the magic code. Success!

Of course, I couldn't leave things as they were; I had to figure out how to change the sort order data in the listview whenever a header was clicked. This turned out to be slightly easier than I thought. I also added code to allow editing (via a subform) of a row, and to display any change that might be made in the data. This required an extra query component to get the new data for the chosen record.

Here is the new screen (note that the header is still left to right):


And here is (some of) the code:

type
 TForm1 = class(TForm)
   lv: TListView;
   Panel1: TPanel;
   EditBtn: TBitBtn;
   BitBtn2: TBitBtn;
   SQLConnection1: TSQLConnection;
   SQLQuery1: TSQLQuery;
   qPerson: TSQLQuery;
   procedure FormCreate(Sender: TObject);
   procedure BitBtn2Click(Sender: TObject);
   procedure EditBtnClick(Sender: TObject);
   procedure FormActivate(Sender: TObject);
   procedure lvColumnClick(Sender: TObject; Column: TListColumn);
 private
   LastSortedColumn: integer;
 public
end;


var
 Form1: TForm1;

implementation
{$R *.dfm}
uses unit2;

Function SortByColumn (Item1, Item2: TListItem; Data: integer): integer; stdcall;
begin
 case data of
   0: Result:= AnsiCompareText (Item1.Caption, Item2.Caption);
   1, 2: Result:= AnsiCompareText(Item1.SubItems[0], Item2.SubItems[0]);
   3: if strtodate (Item1.SubItems[2]) < strtodate (Item2.SubItems[2])
       then result:= 1
       else result:= -1
 end;
end;

procedure TForm1.FormCreate(Sender: TObject);
const
 LVM_FIRST = $1000; // ListView messages
 LVM_GETHEADER = LVM_FIRST + 31;

var
 ListItem: TListItem;
 header: thandle;
 i: integer;

begin
 header:= SendMessage (lv.Handle, LVM_GETHEADER, 0, 0);
 SetWindowLong (header, GWL_EXSTYLE, GetWindowLong (header, GWL_EXSTYLE) or
                                WS_EX_LAYOUTRTL or WS_EX_NOINHERITLAYOUT);
 SetWindowLong (lv.Handle, GWL_EXSTYLE, GetWindowLong (lv.Handle, GWL_EXSTYLE) or
                                WS_EX_LAYOUTRTL or WS_EX_NOINHERITLAYOUT);
  lv.invalidate; // get the list view to display right to left
  lv.items.beginupdate; // reduce flicker

  with sqlquery1 do
   begin
    open;
    while not eof do
    begin
     ListItem:= lv.Items.Add;
     ListItem.Caption:= fieldbyname ('zehut').asstring;
     for i:= 1 to 4 do ListItem.SubItems.Add (fields[i].asstring);
     next
     end;
    close
   end;

 lv.Items.endupdate;
 qPerson.Prepared:= true;
 LastSortedColumn:= -1;
end;

procedure TForm1.lvColumnClick(Sender: TObject; Column: TListColumn);
begin
 LastSortedColumn:= Column.Index;
 TListView(Sender).CustomSort(@SortByColumn, Column.Index);
end;

procedure TForm1.FormActivate(Sender: TObject);
begin
 lv.setfocus
end;

procedure TForm1.BitBtn2Click(Sender: TObject);
begin
 close
end;

procedure TForm1.EditBtnClick(Sender: TObject);
var
 flag: boolean;
 id: longint;
 i: integer;

begin
 if lv.selected = nil then exit;
 id:= strtoint (lv.selected.SubItems[3]);
 with TGetDetails.Create (nil) do
   try
    flag:= execute (id);
   finally
    free
   end;

 if flag then
   begin
    with qPerson do
     begin
      params[0].asinteger:= id;
      open;
      lv.selected.Caption:= fieldbyname ('zehut').AsString;
      for i:= 1 to 3 do lv.selected.subitems[i - 1]:= fields[i].asstring;
      close
     end
   end;
 lv.setfocus
end;

end.

Sunday, October 11, 2009

This tickled my fancy

My dog sheds hair all the time, and someone suggested improving her food by adding a teaspoon of olive oil each day. I'll check the dietary values of her food this evening.

Whilst idling googling dogs and olive oil, I came across this pearl of wisdom:

Q: If you give olive oil to your dog, will it make my dog's nose any wetter?


To which the reply was "No, your dog's nose won't get wetter if I give my dog olive oil".

Someone has a problem with pronouns.

Friday, October 09, 2009

Firebird triggers and autoincrements

I often say that a day in which nothing new is learnt is a day wasted. Today will not be wasted.

One of the good things about the Borland Database Engine (BDE) is that it has an 'autoincrement' type; I would use a field of this type to provide primary keys for almost all database tables.

Firebird (FB) per se does not have an autoincrement type, which at first was a setback when I first started developing for this database manager. I use an oldish version of "EMS SQL Manager 2005 for InterBase & Firebird Lite" to create the databases, and this allows one to define fields as autoincrements. Under the hood, this program is creating two entities connected to the autoincrement field: a generator and a trigger. The generator starts with a seed value and whenever it is called, it increments the seed and returns the new seed; thus every call to the generator is guaranteed to get a value which does not exist in the database. Since I have been porting applications to FB, I have been calling the generator manually and getting the new key value.

Until now I have ignored the trigger which is also defined. Today I was defining a new database and decided to look at the trigger:
CREATE TRIGGER BI_PEOPLE_ID FOR PEOPLE
ACTIVE BEFORE INSERT
POSITION 0
AS
BEGIN
IF (NEW.ID IS NULL) THEN
NEW.ID = GEN_ID(PEOPLE_ID_GEN, 1);
END;

Turned into English, whenever a program is about to enter (insert) new data into the 'people' table, if the value of the 'id' field is null then the trigger will automatically call the generator and obtain the new value. This means that I don't have to call the generator manually which means that I can simplify my program code.

Almost all the programs I write for my occupational psychologist read in a data file which has been prepared by an 'exam' program run by an examinee; these data files' contents can be divided into two parts, where the first part is the examinee's personal data and the second part is the examinee's results.

The first part will go into the 'people' table, and I will need the id number of the inserted tuple in order to use it as a foreign key during the insertion of the 'results' tuple. Thus it's more efficient to call the generator manually for the 'people' table before inserting the data as I can save the value for later. But as the 'results' table has an autoincrement field as its primary key, I don't need to call the generator manually for this as I don't need the resulting value for anything other than as a primary key for the current tuple.

To give an explicit example, if the 'results' table has the structure
id: autoincrement (primary key)
pid: longint (foreign key, points to person whose results these are)
subject: longint
score: smallint

Then instead of writing queries like this
insert into results
values (:p1, :p2, :p3, :p4)

I can now write this query
insert into results (pid, subject, score)
values (:p2, :p3, :p4)

and have the pre-insert trigger automatically generate the value for the primary key. If the 'insert' statement does not have a list of named fields then it automatically inserts data starting with the first field in the table.

Actually, I know that in the above situation, I could probably do away with the 'id' field, and have the primary key based on the fields 'pid' and 'subject', in which case using a trigger is beside the point.

Unfortunately, it seems that I will be able to use the trigger method (as opposed to calling the generator manually) infrequently, and then only for tables whose values are entered programmatically. Many tables are populated by data entry screens, where the database table is buffered by a client dataset, and in such cases it is impossible to get the trigger to fire, as a 'field requires value' message appears.

What is important about today's efforts is that I am thinking how to use the dbExpress/Firebird combination in the most efficient manner. This is all still relatively new material to me, whereas the BDE code has had over a decade to settle and improve.

Thursday, October 08, 2009

The girl who played with fire


In view of the favourable, but mixed, reaction that I had to Stieg Larsson's first book, "The girl with the dragon tattoo" (GDT), I decided to purchase and read its followup, "The girl who played with fire" (GPF). In retrospect, I wish that I hadn't bothered. The good parts of GFT far outweighed its flaws, but there's no way that I can say that about GPF. This book is in sore need of an editor, and unfortunately it's far too late for that.

Again, I am torn between holding back the spoilers for those who have not read the book, and giving specific examples of poor editing. When I write "editing", I don't mean copy editing, which is a technical function, but rather the equivalent of what a record producer does: gives the raw material a direction, chops out the irrelevant and sequences the raw material in the best order possible.

The first two hundred pages (approximately) are almost totally irrelevant to the rest of the book. The material about Salander would be better served by placing it in a collection of novellas and removing it from this book; it feels like a warm-up exercise and has no part in GPF. I am reminded of the late Roger Zelazny who prior to beginning a novel would write short stories about his main characters in order to get a feel for them. These pages give one the (post-reading) feeling that Larsson was making the whole thing up as he went along, and inserted events (or "hooks", as the musician or computer scientist might call them) as they occurred to him. If later events revolve around prior knowledge which is given by these hooks, then the reader feels satisfied, but if the hooks are left unresolved, then the feeling is awkward. I found myself reading the opening pages wondering where the events described would lead, and in the end, they led absolutely nowhere.

I find myself comparing the structure of (the rest of) the book to that of a police procedural. Such books generally begin with a murder, and then focus on the detective's attempts to solve the crime. Had GPF began with the murders and then focused on the detective team's attempts, then it would have been on more solid ground (and perhaps more predictable). Larsson makes the book extremely complicated by having three (or even four) different teams trying to solve the murders and it makes for exceedingly complicated reading, whilst dissipating the thrust of the story. Where was Salander all the time her liberally enhanced sordid past was being splashed over all the newspapers in Sweden?

I sincerely hope that Swedish policemen are more professional than some of those portrayed in this book, as their attitude towards someone who is supposed to be a witness and possibly a suspect is remarkably tabloid (and some of the policemen's attitude towards one character who wasn't even a witness was deplorable).

After a while, the police procedural theme disappears and the book becomes more of a crime thriller, at which point any points which the book might have accrued slip away with remarkable speed. There are still unresolved hooks spread around (for example, those connected with Erika Berger) which dilute the story's impact.

In order to check the book's literary style, I opened the book a few times at random. The first thing that I noticed was that most of the pages were written in direct conversation between characters; the second was that the non-literal descriptions were sorely lacking in adjectives. This gives the prose a very straight-forward and brisk attitude but it doesn't make reading it a pleasure.

On the basis of GPF, I can't see myself reading the third part of the "Millenium trilogy", "The girl who kicked the hornets' nest". I would probably see any film made from these stories but I won't be investing any more money or time in these books.

Wednesday, October 07, 2009

Pressure cooker

Whilst reading the cookery column of Friday's newspaper the other day, I resolved to buy a pressure cooker. After a little research on the Internet, I went down to the local shopping mall and found myself a 9 litre cooker for 230 shekels (about $55). At first I thought there must be something wrong with the pot as the lid didn't seem to fit, but one of the sales staff showed me how to close the lid hermetically and thus keep all the steam inside.

I don't have a recipe book yet for this type of cooking, and I was warned not to try and experiment. I found a recipe for "Italian inspired chuck roast with herbed red wine gravy" which seemed like a good place to start. I marinaded overnight chunks of goulash beef in a mixture of olive oil, wine and vegetable soup, then drained and dried the chunks. In the pressure cooker I heated oil and then browned the meat; after removing it, I fried a chopped onion, then added two cups of water, the meat, and chopped sweet potato and pumpkin. I then closed the lid of the pressure cooker as instructed and waited for steam to emerge; when it did, I closed a valve on the cooker's lid, reduced the heat and waited 30 minutes. After that time, I removed the pot from the heat, waited about five minutes, then opened the valve, allowing all the steam to escape. When the cooker showed signs of returning to normal, I removed the lid and decanted the contents into a bowl.

Somehow the amount of liquid had doubled during cooking, leaving me with a large amount of broth. The meat was tender and good. My wife had been very doubtful about my purchasing the cooker, but when she tasted the meal - and heard that the cooking time was only 30 minutes - she strongly endorsed the purchase. She has made goulash with this meat before, and has normally boiled the meat for anywhere between two and four hours.

Guess what we're having for dinner on Friday night....

Monday, October 05, 2009

Old computer in new case

I think that I haven't blogged about the problems that I've been having with my home computer. Looking back, I discovered that I bought the computer two years ago, and it's served me very well over that time.

About a month ago, the computer suddenly started turning itself off after about an hour, which made it very difficult to do any serious work with it. After checking, it seemed very likely that the fan in the power supply was no longer working (it had been making very suspicious noises previously and then was surprisingly quiet) and the computer was 'clever' enough to turn itself off before it overheated. I sent the computer to two different places in the hope of getting it repaired, but nothing was done, apart from cleaning out all the dog hairs which had accumulated inside.

After these 'repairs', the computer was still turning itself off after an hour. I spent one Saturday programming, with a very close eye on the clock: whenever an hour would go past, I would save everything, turn the computer off and then do something else whilst the computer cooled down. Obviously it wasn't possible to continue in this manner, and I transferred my development environment to my mobile computer. Whilst developing (or porting) on the mobile has definite advantages (like being able to sit on the balcony in the early morning and work amongst the bird song), I don't like working with the small keyboard, and the screen is poorly lit. I also don't have Office installed on the computer (my company tries to save on licenses), making debugging any automisation code impossible.

I am on holiday all week, so I decided that this would be the time to settle the computer's fate. I opened up the case yesterday and realised that I can't do anything with the computer's innards, as the (very neatly engineered) contents were developed specially for this model. I took the computer to a nearby repair shop, and this time the repairman suggested that he transfer the motherboard, disks and assorted card to a new case (which would have a more standard power supply).

This he did, for the princely sum of 350 shekels (about 90 dollars), and now I have a computer which seems to work properly. It's been on for five consecutive hours, I've done a virus scan, several compilations and downloaded email; the computer has performed perfectly, and is totally quiet to boot.

It seems that the power problems are passed (and past).

Friday, September 25, 2009

Defining my laptop computer's wireless IP settings

My company gave me a laptop computer a few months ago, primarily to help me when I visit the company's various sites, which are spread out across the country. All of these sites have a wireless network, which requires the computer's wireless network card to be defined as DHCP. No problem.

The problems begin when I get home and I want to use the mobile computer; the kibbutz network requires fixed IP addresses. So far, I've been accessing the network card's settings and changing them manually. This is getting to be a drag, and today I learnt how to build two batch files which will set the values automatically. These files don't execute any quicker than setting the values manually, but they're much easier.

First of all, here are the commands necessary to set the wireless card to use DHCP:
netsh interface ip set address name="Wireless Network Connection" source=dhcp
netsh interface ip set dns name="Wireless Network Connection" source=dhcp

I created a batch file called 'dhcp.bat' and added those two lines to the file.

Setting the fixed ip addresses was a bit more complicated, because there are more values to list, and because I need two dns addresses (by experiment, it seems that the secondary dns server would seem to be more important than the primary, but this may change).
netsh interface ip set address name="Wireless Network Connection" static 10.0.90.82 255.0.0.0 10.0.0.1 1
netsh interface ip set dns name="Wireless Network Connection" source=static addr=212.117.128.3 register=primary
netsh interface ip add dns name="Wireless Network Connection" addr=212.117.129.6 index=2

Note that the first line (set address) has three different addresses: the first is the address of the wireless card, the second is the mask and the third is the address of the gateway. Following these addresses is a parameter "1". It seems that the command processor is prepared to read the values without the need to specify what they are. Alternatively, this line could be written in full as
Netsh interface ip set address name=”Local Area Connection” source=static addr=10.0.90.82 mask=255.0.0.0 gateway=10.0.0.1 gwmetric=1

As I never use a network cable to connect the laptop to the kibbutz network, I can leave the settings of the standard network card as dhcp.




Saturday, September 19, 2009

The Girl with the dragon tattoo (two)

I've had more thoughts about this book, no doubt precipitated by the review of the Hebrew translation which appeared in yesterday's newspaper (the translation has curiously lost its definite articles, so it is now "Girl with a dragon tattoo") as well as cogitation about some of the Amazon reviews. Such reviews are torn between the desire to tell what the book is about (so that people will know whether the subject interests them) and the need to review (criticise and praise) the writing. Here I have no such problem: I don't need to tell the story and I can give as many spoilers as I want.

One person wrote about how the characters don't develop (surely, one of the tenets of fiction is that at least one character has an epiphany which changes how she feels about life and coincidentally helps her provide the story with a resolution). Another reviewer noted in response that in real life, people don't really change. Whilst that is mainly true, I know that I am trying to change, or at least deepen my understanding of what is happening around me in the hope that I will be able to impress myself more on the environment, or have the environment impress itself less on me (current book "Predictably Irrational" by Dan Arieli, which I was given as a New Year's gift yesterday). Going back to the matter at hand, Lisbeth Salander, the eponymous girl with the dragon tattoo, does change, although that doesn't seem to affect the story's resolution.

The real problem with the book is that it's really two stories in one. Now, I know that a good book has a plot and a sub-plot, but this one has two plots, neither of which is dominant to the other, and this is what caused the feeling of anti-climax at the end of one of those plots. The book could either have been about the search for Harriet Vanger, or it could have been about the shady financier Wennerstrom. The background material (whilst overly done) fits into both stories equally well. The only links between the two stories (apart from the characters) is Henrik Vanger's desire to bring Wennerstrom down, and his feeble promise about the material necessary to do so.

True, protagonist Mikael Blomkvist was lured into the Vanger plot by being promised the hot material on Wennerstrom, and his actions in the Vanger plot lead to him linking up with Salander, but otherwise the two plots have nothing to do with each other.

Ian Rankin, in his Rebus novels, usually has three or even four plots runnning in the book, but they run in parallel and are solved simultaneously by the end of the book. This has often given me a deux ex machina feeling about some of the stories; whilst I know that life - and especially police work - is not so simple, in that plots run into each other with no apparent beginning or end, and can run side by side (but not necessarily in tandem) for years, I am aware of literary constraints which demand that most (if not all) loose ends are tied by the end of the book. I often wondered what would happen if Rankin (or Peter Robinson, or any other detective novelist) would introduce a thread which does not get solved by the end of the book.

Steig Larsson has written a book with two concurrent but non-parallel plots; whilst both plots have conclusions, they are not simultaneous. So this book answers in part my query about how such a book would feel. And having read this book, I now know the answer: due to literary conventions and the escapist need to have a complete ending, I prefer Rankin with his multiple and parallel plots which finish simultaneously.

Thursday, September 17, 2009

The Girl with the Dragon Tattoo

I, too, have read this book. Amazon has been pushing it at me for some time, so eventually I decided to order it.

I am in two minds as to how to rate the book. The first two hundred or so pages were quite heavy going, with too much exposition and a frequently irrelevant depth of detail: for example, the description of Salander's employer, or the passage describing the destruction of her first laptop and her subsequent purchase. Characters were introduced who had no connection with the story and could easily have been edited out. Whilst I understand the necessity to describe what happened between Salander and her second guardian, in order to improve her characterisation, this section was too vivid, too long and too brutal (mirroring to a certain extent something which takes place towards the end of the book).

But after the two protagonists meet, sparks begin to fly and the pace hots up considerably. Once the major problem has been solved, the last hundred pages or so seem like an anti-climax, although actually this part is much more rewarding and could have been made into a novel by itself.

Reviewers on Amazon have mentioned unlikely coincidences, but almost all fiction depends on such devices. The skill of the author is measured in how believable he can make those coincidences, and I think that the late Steig Larsson managed quite well in that respect.

Sunday, September 13, 2009

Using dbExpress - conclusions

I've just discovered that if one googles "Porting BDE", the first few entries reference this blog! It transpires that someone has been following my jottings and posting them on the Firebird site. At the moment, I've converted about 20 forms, and the conclusions resulting from these conversions are becoming clear. I don't expect to post any more on the subject, unless I run into a staggering 'gotcha'.

When porting the contents of the database itself, the TSqlQuery should be used with the following statement: insert into values (:p1, :p2, :p3, etc). In the delphi code, one writes as follows:
with SqlQuery1 do
 begin
  close;
  parambyname ('p1').asinteger:= ...
  parambyname ('p2').asstring:= ...
  parambyname ('p3').asdate:= ...
  execsql;
 end;
A date parameter has to be passed AsDate, not AsDateTime. Apparently this is a bug in dbExpress.

In applications, I have discerned three different cases in which previously I would have used a BDE tquery component. These are:
  1. For statements which update or delete from a table and for statements which return one or more values which will not be displayed via a data-aware component, a TSqlQuery component can be used which simply replaces the TQuery on almost a one-for-one basis.
  2. For DBLookupComboBoxes, one can use the SimpleDataSet component, not forgetting to set the 'connection' property to the global connection. This component is considered to be deprecated and not to be used, although I have found that for this simple use (and also for a dbgrid from which values are chosen), it's easier to use this than the 'holy trinity'.
  3. Whenever data-aware components are used, the 'trinity' must serve them. The trinity consists of: a SQLDataSet (which is linked to the global sql connection), a DataSetProvider and a ClientDataSet.
I have also developed a new template for data editing forms. Once I would write as follows:
function TEditDiag.Execute (n: longint): boolean;
begin
 with qEditDiag do
  begin
   close;
   if n = -1 then
    begin
     open;
     append;
     caption:= 'Adding'
    end
  else
    begin
     params[0].asinteger:= n;
     open;
     caption:= 'Editing';
    end;
   edit;
   if showmodal = mrOK then
    begin
     result:= true;
     post
    end
   else
    begin
     cancel;
     result:= false
    end;
   end
 end;
The above code takes advantage of the fact that the BDE has autoincrement fields, and so one doesn't have to explicitly create a new key. FB does not have such fields, and so the use of a generator is required. The above code is now replaced with the following:
function TEditDiag.Execute (n: longint): boolean;
var
 gen: longint;

begin
 sdsEditDiag.commandtext:= 'select * from minidiag where id = ' + inttostr (n);
 with qEditDiag do // clientdataset
  begin
   open;
   if n = -1 then
    begin
     caption:= 'Adding';
     insert
    end
   else caption:= 'Editing';

   edit;
   if showmodal = mrOK then
    begin
     result:= true;
     if n = -1 then
      begin
       with qGetID do // get new id number from generator
        begin
         open;
         gen:= fieldbyname ('gen').asinteger;
         close
        end;

       FieldByName ('id').asinteger:= gen
      end;
     post;
     applyupdates (0)
    end
   else
    begin
     cancel;
     result:= false
    end;
   end
 end;

Friday, September 11, 2009

Sandy Denny in "Ma'ariv"

I wrote two weeks ago about an article in the Israeli newspaper "Ma'ariv" about Nick Drake. In today's arts supplement, it was the turn of Sandy Denny. Whilst it's true that there's no such thing as bad publicity, this article was so inaccurate that I became very annoyed. Instead of writing about the classic Denny songs, her participation in Fairport's groundbreaking "Liege and Lief" and her twice winning the "Melody Maker female singer of the year award" (1970/1), we got to read about her drinking and smoking (twice), her husband who ran off with her infant baby and a little about her "fall down the stairs". Naturally "The battle of Evermore", the first (and only?) Led Zeppelin track to be recorded with someone outside of LZ was mentioned.

The final nail in the coffin? The "fact" that Sandy's best song was "Listen to the song of the sea". Excuse me? I've never heard of such a song. To be fair, it might be a problem with copyediting and punctuation marks, in which the sentence becomes "Listen to the song 'The Sea'". Whilst I have no problem in considering this Fotheringay song as one of her best, I wouldn't consider it to be the best.

I fired off an angry and frustrated letter, but I don't expect to receive any reply. I certainly didn't get one after the Nick Drake article. I even offered to copy edit any future articles about Witchseason artists, figuring I probably know more about them than anyone else in Israel.

Tuesday, September 08, 2009

Richard Thompson - You?Me?Us?

I had a long (for me) car journey yesterday, two and a quarter hours in the morning and two and a quarter hours back in the afternoon. For accompaniment, I brought with me the above Richard Thompson disc as well as an mp3 disc. Unfortunately, the car cd player wouldn't play the mp3 disc, so I listened to RT all the way.

Although this is nominally a double cd, both sides are quite short (about 37 minutes) and theoretically could have been put together on one disc. RT didn't do that, because the two discs are in different styles: the first, subtitled "voltage enhanced", features the electric guitar, whereas the second, "nude", features the acoustic. The contrast wouldn't provide for a good sequencing flow, although the closing track on the first disc, the lovely "The ghost of you walks", serves as a good bridge between the two styles.

I remember writing in 1996, which is when the disc was released, that the 'nude' disc was the album I had been waiting 15 years for, as it features several on top form RT songs. I wasn't so enamoured though with the electric disc and have played it infrequently over the years. Yesterday was a good chance to listen to it again.

What impressed me the most was that it's almost as if Richard was trying out different styles of songwriting. The 'nude' songs are conventional in their structure (although again, there are a few songs which sound like songwriting exercises, especially "Train don't leave"), but most of the 'voltage enhanced" songs have no counterpart in the RT canon. After thinking about it, I was able to narrow the differences down to a few stylistic changes in the music:
  1. Many of the songs have repeated lines: for example, the chorus of "Dark hand" has the line "There's a dark hand" repeated six times before the closing "over my heart" completes the sentence. "Business on you" again has several triplet repetitions of "I'm going to do the business on you" before the payoff line of "I've got all the magic I need". The repeated lyrics are set to the same melodic lines, so it's as if the song's progress gets stuck in a loop. Whilst "other" music features this quite often (think of the 12 bar blues as an early example, or the "three times and you're out" petite reprise on many Beatles songs), it's a rarity in RT's songs.
  2. The first few songs don't have conventional harmony, especially "She steers by lightning". Following this is "Dark hand", which too begins with a dynamic bass line over the same chord.
  3. There are songs which hold the I chord for a very long time - the verse of "Business on you" is a good example of this, as is "Am I wasting my love on you?".
  4. There is one explicit example of a slow burner 12/8 - "Bank vault in heaven".
The musical side of Richard Thompson's songs have always been off the beaten track. Whilst some may not have sophisticated harmonic progressions (I'm thinking of the lovely "Withered and died"), they have always featured chord progressions, and normally display a harmonic rate of change at about one chord per bar. There has always been a mix of 4/4 and 3/4 songs, but extremely few in 12/8.

The result of these experiments is that the songs are much more commercial (for want of a better word), more familiar to the ear of the common listener (who isn't aware of the 'guitar as bagpipes' sound of RT). The electric guitar and mandolin duet in "Business on you" probably sounds fresh, exciting and exotic to those ears, whereas to the cognoscenti it sounds like a rehash (albeit a brilliant one) of "When I get to the border".

Interestingly, the two songs which feature on both discs, "Razor dance" and "Hide it away", feature none of the characteristics which I have noted. "Razor dance" is quintessential RT; the acoustic version sounds like a demo for the electric version. "Hide it away" is slightly off the beaten RT track, although its roots can be found in a few songs on the preceding "Mirror Blue" album. Again, I think that the electric version is better than the acoustic, although this time they are arranged differently: the electric version sounds like a Burt Bacharach smoocher (is that electric piano or electric guitar providing the chordal accompaniment?), whereas the acoustic version is rougher and features a double bass solo.

The lyrics are interesting and inventive throughout both discs and have no relation to the musical experiments to which they are set. As a result, the lyrics of "Business on you" or "Am I wasting my love?" are far better than their music, a rare occurrence in an RT song, where music and lyrics are normally at a high and equal level.

Saturday, September 05, 2009

More porting issues

Saturday morning, 7:30 am. I'm sitting on the small balcony outside our house writing this on a mobile computer. It's very quiet; the only sounds I hear are the birds twittering in the tree and the occasional child. The dog and I have already been out for our walk. I'm using the mobile because my main computer is "in hospital": it's been conking out after about 30 minutes of use. Obviously the fan in the power supply is causing problems. Actually, according to the repairman, the fan is fine; the insides of the computer are full of dog hairs....

I am in the process of porting my flagship program from using the Borland Development Engine (BDE) to Firebird; I want to write about some of the "gotchas" which I've come across in the past few days, things which one doesn't read about in the various texts. Actually, the issues have nothing to do with Firebird and everything to do with the dbExpress components which I am using to connect to the Firebird server.

As any Delphi programming book which covers the topic will tell you, the dbExpress components are uni-directional; this makes them simpler and less demanding of system resources, but it also makes them problematic for someone used to the BDE query component. Here are two problems which I've come across.

1. Recordcount
The 'recordcount' function of a query gives the number of records which have been returned by the query, as its name might suggest. I use this function in two different ways: most times, I set a progress bar's length to the number of records returned so that the user can have feedback via the progress bar how the computation is progressing. Very important when there are 3000 records to be processed.... Less frequently, I use the function to see whether the query even returned a record set, in order to do some more computation.

The dbExpress TSqlComponent has a 'recordcount' function, but unfortunately most of the time it doesn't work. The documentation states that there is no function when the query is called with parameters or when it is a multi-table query (there is also a third case but I don't remember what it is). This means that the recordcount function is fairly useless, as it is extremely rare that I will program a function which uses only one table and has no parameters (it turns out that I do have one such query in my program, 'select key from table'). So how does one solve the problem?

When I initially want to see whether the query returned a record set before processing, I now use the 'eof' function; if 'eof' is true after opening the query, then there is no record set. Simple.

As for getting the number of records returned, the only solution which I've found is to define another query, the same as the original but simpler, which returns 'count (*)' instead of actual fields. After opening the query, the value of the aggregate is passed to the progress bar.

2. Sorting a record set displayed in a dbgrid by clicking on column titles
Delphi's dbgrid component exposes a method which is called when a column title is double clicked. I use this method in order to sort and display the record set, according to which column is clicked. The first gotcha which came when porting this was that the column array is now zero based instead of one based; this is strange as it has nothing to do with porting to dbExpress. It only became clear when clicking on one column caused the grid to sort by a different column.

In BDE programs, the query component connects to the database on one side and to a datasource component on the other side. Sorting the data required closing the query, changing the SQL statement to order by a different field and then reopening. Fairly simple. But when using dbExpress components, life is more difficult: the database is connected to a TSqlConnection which is connected to a TSqlDataset which is connected to a TDatasetProvider which is connected to a TClientDataset which is connected to the TDatasource which is connected to the dbgrid. The SQL statement which chooses the data is in the TSqlDataset, which is then buffered by the TClientDataset. It occurred to me that it would more efficient to acquire the data once from the database, and then sort it in memory via the TClientDataset ('cds').

In order to do this, I predefined several indices in the cds, each corresponding to a column to be displayed; some of these indices contain two fields, and some are defined as descending indices.

Now, when a column title is clicked, I simply change the index used in the cds, and hey presto! the grid is displayed, sorted by the new index. This is very fast as there is no need to reaccess the database.

Here is the code for the event (qPeople is the cds):

procedure TMainForm.DBGrid1TitleClick(Column: TColumn);
begin
 try
  dbgrid1.columns[prevcol].title.font.color:= clNavy
  except
 end;
with dbgrid1.datasource.dataset do
 begin
  column.Title.Font.color:= clRed;
  prevcol:= column.Index
 end;

with qPeople do
 try
  disablecontrols;
  close;
  case prevcol of
   0: indexname:= 'idx_date';
   1: indexname:= 'idx_zehut';
   2, 3: indexname:= 'idx_name';
   4: indexname:= 'idx_company';
   5: indexname:= 'idx_matara';
  end;
  open
 finally
  enablecontrols
 end;
end;

Sunday, August 30, 2009

The world of Henry Orient

Sometime around 1970/1, the BBC ran a series of Peter Sellers' films. Amongst those shown was a delightful number called 'The world of Henry Orient', in which Sellers plays a classical pianist who is better at being a lothario than playing the piano. As far as I was concerned, the focus of the film wasn't Sellers, but rather two girls on the edge of adolescence. I only remember all this because a few months later I wrote a poem which hints at this film. I tried looking for the poem yesterday - not because it was any good, but because it would give me a date - but couldn't find the folder in which it was stored.  [see end of post]

Fast forward nearly 40 years. The MGM movie channel showed TWOHO a few weeks ago, and I eagerly recorded it onto DVD. When watching it, I was instantly charmed by the film's youth, but discovered to my dismay that the physical media on which I had recorded the film couldn't keep up with the pace, and so it stuttered. Too often. So much so that in the end, I couldn't watch the film. It seems that the first 20 or so DVDs in the pack were ok, but then the quality went down, as a few other films which I recorded suffered the same fate. I have since bought better quality DVDs (or at least, more expensive ones) and am saving the cheaper ones for digital copies only.

TWOHO was screened again yesterday, and this time I made sure that I recorded it onto a good disc. One more technical gripe before I write about the film: the television set displayed the film with a strange aspect ratio, which ignored the bottom 10% of the picture, and sometimes caused faces to appear distorted. Fortunately, the DVD recorded the film with a better aspect ratio, showing everything properly.

As many have commented before me (check out the comments at IMDB), this is a lovely teenage movie made just before things were about to change. Instead of adding my own bumbling commentary, here is one of the comments from IMDB:
The sixties became The Sixties around the time of this film, 1964. There was a time, believe it or not, when kids played grown-up, instead of the other way around, as is the case today. Two cute girls are venturing from childhood to youth, in a benign Manhattan. They have a crush on a pianist-lothario who happens to be Peter Sellers. You can imagine the complications - and the hilarity.

What makes this film so appealing, is the way it portrays sexual awakening and same-sex bonding as a completely unsordid and sweet experience. Yes, there is pathos, when the two discover how adults have turned their world into Henry Orient's world.

Although the cast is sterling all around, Tom Bosley is a standout as father to one of the girls, who helps put things to rights.

If the Kennedy assassination and Vietnam are cultural watersheds, then this film is a wonderful cinematic artifact; it gives the lie to the condescending put-downs of the era by the current generation.

Another comment talks about "the magical, honest bond of best-friendship between girls". Whatever. I would like to be gender blind, and say that the film demonstrates the magical and honest bond of best friendships, something which I was encountering when I first saw the film. To me, that is more important than the plot involving Sellers, and especially more important than the film's ending (not the final scene, but the ten minutes which precede it).

In fact, the film could almost have been split in half: the first forty minutes, and the rest of the film. This is not to say the that the second half is any less good than the first, but rather the first part shows a specific world and emphasizes the values of friendship, whereas the second part develops a story.

I am not too sure whether the accompanying score is subtle or blatant; certainly whilst watching this film for the fourth or fifth time, I was paying more attention to the score than one normally does. During the Central Park scene at the beginning, note how whenever the girls are on screen, a 'youthful' theme is being played, but whenever the camera cuts to Sellers, the score consists of abstract piano chords, in keeping with his character.

The young actress playing Val - Tippy Walker - unfortunately did not stay in the movie business. Most of the time she looks to me like a young Diane Keaton; if one takes the Diane of 'Sleeper' and tries to imagine what she would have looked like ten years younger, then Val is the result.

[Edit from 22 July 2010: The poem referred to above was written on 16 Nov 1971, which means that the film was screened a few months before hand]

Saturday, August 29, 2009

Nick Drake in "Ma'ariv"

The weekend culture supplement of the Israeli newspaper "Ma'ariv" has been running for several months a feature in which one of the editors presents a long forgotten record which is considered to be a classic. For example, a few weeks ago, the record chosen was Love's "Forever changes".

This week it was the turn of Nick Drake's "Five leaves left". Whilst it is true that there is no such thing as bad publicity, there were a few "facts" in the article which left me scratching my head. For example, did you know that Fairport Convention provided the accompaniment for the record? Did you know that Elton John was the second (recording) engineer? Did you know that the meaning of the record's title is unknown?

Maybe these "facts" upset my equilibrium so much that I was unable to concentrate on the rest of the article, but I'm fairly sure that there wasn't much of a case made which would make anyone want to listen to this record. Which is a shame.

I sent an email last night to the paper, and it will be interesting to read the response (if there is one).

For the record, although Ashley Hutchings, then bassist of Fairport, "discovered" Nick Drake and brought him to the attention of Joe Boyd, the only Fairporter present on the recording is Richard Thompson, and he played guitar on one track only. To be fair, Dave Pegg and Dave Mattacks appear on most of the tracks on Nick's second album, "Bryter Layter".

Elton John worked as a session pianist before he became famous. The only connection with Nick that I can think of was that he was recruited by Joe Boyd to sing on a privately issued record (500 copies) which was intended to publicise songs published by Witchseason Music (Boyd's publishing company) in the hope that other artists would cover them. This record was mythological, until a few years ago when someone laid their hands on a copy and digitised it. Since then, it has been distributed here and there, and someone was kind enough to send me a copy a few years ago (I have to be vague here, as I don't recall who it was).

I spent half an hour looking for the disk, and was unable to find it, until I found an archived and compressed version, along with the Brad Pitt narrated "Poor Boy" audio documentary. After decompressing the songs, I listened and then remembered why I don't have an audio disk: EJ murders the songs! Or at least, he renders them unlistenable to anyone who knows the originals. His versions are blues infected rock, and whilst an unaware audience might find them interesting, to me he removes any subtlety which there might be in the songs (and there is subtlety a plenty!) and emphasises the wrong things.

Friday, August 28, 2009

Firebird date fields, continued

Following on from my previous post, I discovered that the code which I posted did not work.


What did work was a query with parameters:
update person set
dob = :a1, examdate = :a2
where id = :an

Here is the corresponding program code
with qDates do
 begin
  close;
  parambyname ('a1').asdate:= dob;
  parambyname ('a2').asdate:= examdate;
  parambyname ('ap').asinteger:= pid;
  execsql;
 end;

It's nice to know that this blog is being read by someone (see comment attached to previous post), and it's even better to know that we reached the same conclusion.

At the moment, the code which enters a new person into the database is split between two queries: one is an insert query, passing string literals, and the other (shown above) is an update query passing parameters. I now wonder whether it is possible to use only one query, which will insert using parameters. This will make the code run quicker and will also be clearer. Early days.

The port of the simple program from BDE to FB was completed last night with the above code. It's interesting to draw conclusions from the port:
  1. One unit reads a text file (which is the output from the exam program), and inserts data into the 'people' and 'results' table. This is done using the TSQLQuery component with 'insert' and 'update' statements (should only be 'inserts' - I'll work on this tonight).
  2. One unit allows the 'people' data to be edited - this is done at the moment with the trinity of TSQLDataSet, TDataSetProvider and TClientDataSet. Now that I think of it, one needs the 'trinity' only when a bi-directional dataset is needed, and in this case I am displaying only one record, which means that I may be able to use the TSQLQuery alone, in the same way that I used TQuery in BDE programs.
  3. One unit displays all the people who have taken the exam - as this uses a dbgrid, the trinity is used.
  4. Two units output data (to Word) - TSQLQuery.
So, apart from correcting the input unit (a simple matter), I should investigate whether the editing unit can be simplified.

One final 'gotcha': in order to use the program during development, the TSQLConnection has the appropriate database string and is automatically opened. This has to be changed after primary development has been finished, as the program must be deployed without a database string, meaning that the connection must be initially closed. I can't see a way of doing this automatically; I will have to remember to change the connection parameters before deploying the program.

Thursday, August 27, 2009

Firebird date fields

In continuation of my previous post about problems of inserting/update date fields in FB databases, I did a little poking around and eventually - by trial and error - found the correct syntax:
update data
set datefield = '01/01/2008'
where id = 1;

It's going to be an interesting exercise producing code like that from Delphi, as the single quotation mark is the string delimiter, and it's always fun trying to create strings which include the quote. It'll have to be something like this:
with sqlquery.sql do
begin
add ('update data');
add ('set datefield = ''' + query1.fieldbyname ('datefield').asstring + ''' where id = 1';
end;

Let us not forget that the date is being passed from a query which is accessing the original BDE database.

Climbing the learning curve

I've spent the last week getting Firebird to work in my occupation psychologist's office. On my development computer, this is very easy, as the FB server runs on the same computer as the development system, but in the office, the FB server runs on a computer server, and the programs accessing this server run on different computers.

Eventually, after a lot of huffing and puffing, I got FB running in the configuration that I want: server program on server computer, client programs on client computers. In the process of doing this, I wasn't able to utilise the 'aliases.conf' file of FB successfully; this is a system in which each client program uses an alias to access the actual database data. The alias allows one to move the database file from location to location without having to redefine within the client program the physical location of the database file. This is fine in theory, but I couldn't get it to work in practice (the problem lies in the fact that at home, the file is on the same computer as the client, whereas in the office, it isn't).

In the end, I solved the problem by ignoring aliases, and storing the database location in the client computer's registry. I had originally intended to do this in the client program, but after a few minutes cogitation, I realised that this would not be a good idea, as the same code would have to be duplicated in every client program. Accessing the location from the registry is not the problem, but rather saving the data in the registry (which would involve a 'find file' dialog).

With this in mind, I sat down yesterday to write a small program which displays the data saved in the registry, along with the possibility of adding and editing this data (I'm not going to bother with deleting values at the moment, even though this would be simple). The program is akin to many other simple database programs that I have written, although with two very important differences:
  1. the data is stored in the registry, not in a database, meaning that the access methods are completely different
  2. the data is displayed in a list view and not in a dbgrid
I have never used a list view before, but was able to find simple code which explained what I needed to do.

I have also been porting a program from using the BDE to Firebird; to warm myself up, I took a simple program (actually two programs) which has a small database. As I wrote before, the screens which display data are fairly easy to convert. Adding data is more difficult, and this time I went a stage further than the first program which I ported: I learnt how to use a generator in order to provide an auto-incrementing value. In the BDE, this happens automatically, but in FB one has to do this manually.

The major problem in porting an application would seem to be converting the data from one database format to another. Whilst there is no problem in exporting and importing integer and string values, there is a minor problem with boolean values (BDE has them, FB doesn't), and a major problem with dates. This is the next problem to be cracked. Fortunately, the database which I am porting only has about 45 records (two dates per record), so I added the dates manually, but the major program suite which I will port in the near future has over 3,000 records (not a huge amount for mankind, but large enough) with two dates per record, and there is no way that I am going to add those dates manually.

So this week, I've learnt the following things
  • how to install FB on a network
  • how to access FB from client computers on that network
  • how to use the list view component
  • how to use an FB generator for auto-incremented integers
  • how to insert/update a date into an FB table [to do]
That's enough for one week.

Fortunately (and purposely) my MBA load is very light at the moment, so I don't have to devote more than five hours on a Friday morning to this on-going project. The accountancy course is very simple - for me, as I spent years of my life working on the material which is being presented - but apparently is difficult for people when it's the first time that they come across it.

Tuesday, August 11, 2009

Moving office

The powers that be decided that our factory should be moved to a new building. I think that the decision was based on two factors:
  1. Not being satisfied with our current arrangements (where the raw materials warehouse is 4km from the production areas!)
  2. A reduction in the volume of orders means that the current leased floor space is too large
So we're moving to a brand new building, about 500 metres away. As it's new, everything has to be installed - primarily electricity, but also sprinklers, telephone wires, network cables, plumbing - and an office has to be built. Due to the scarcity of space, the office consists of two separate floors.

Each cubicle looks at the moment to be very small. I have been given to understand that my table will fit in the space allotted me, but I don't know what else will fit. Maybe it's time to start a diet, so that I will be able to slide between the wall and the desk in order to get to my chair.

The server room has also been reduced in size; I gave an estimate of the minimum amount of space that I need, and that's the size that I've been given. What I didn't know is that one wall of this 'room' - the outside wall - is mostly taken up with a window! This is bad on several accounts:
  1. Anyone can break in through the window (so bars are supposed to be fitted)
  2. Too much light comes in
  3. Too much heat comes in
  4. Valuable wall space is lost - there is equipment which could have been fixed on the wall in that area.
Today we spent a few hours packing up. It's interesting to note how many cartons have been left unpacked since our previous move, 20 months ago. I have a real problem with throwing things away, but this time I was able to part with ten (or more) old printer cables, an external modem, uncounted electricity cables, old ink cartridges for printers that no longer exist, a few L120 cassettes, ad infinitum. I could also part with documentation relating to the upkeep of my old Alpha server which was decommissioned several years ago.

Sunday, August 02, 2009

Speed IV

Honestly, Ma, I tried as hard as possible using threads, but there was always some problem. So I dropped using threads and tried to improve my code algorithmically.

The problematic module is basically composed of four sections: an initialisation section, and three different queries. Most of the time was being spent executing the second query, so I concentrated my efforts here. The first optimisation was to note that data from one table was being read several times in several different queries; although it goes against the grain, it proved quicker to extract that data with a simple query, store the data in an array in the program code, and then simplify the other queries which would look up the missing value in the array. This speeded up the execution, but still not enough.

Then I went down to the metal. There was a query which basically was returning the same data again and again which would then be sent to an expensive operation. I had implemented a check before the expensive operation several months ago to make sure that the same data wasn't being sent, but now it was clear that the check was occuring too late. I was checking after the query had returned a dataset instead of checking before accessing data. Once I had repositioned and recoded the check, the program ran much faster.

So basically I've spent two weeks running around, learning about threads and emitting HTML code, when in fact I could have saved all that effort by concentrating on improving one query.

I read somewhere in this blog that SQL joins slow down a program and should be avoided at all costs. This sounds like shooting oneself in the foot, because if there are no joins, then all the power of SQL is lost. Well, not quite. Although it's messy, it makes no sense to extract the same data continually from the server; better to extract it once, save in in RAM and use lookup statements in code.

Thursday, July 30, 2009

Speed III

Carrying on from yesterday: I converted the specific module to emit HTML stored in a file. After the report is finished, Word opens the HTML file. The conversion was surprisingly easy; the main thing which I was worried about was creating the main table in the report which has a slightly irregular structure. The 'colspan' command helped here, and in fact, it turned out to be much easier to create a table in HTML than it is in Word!

After completing everything and debugging so that the program worked, I checked its performance; it's maybe only 10% faster than Word and definitely not a huge improvement. So the plan is as follows:
  1. Turn the new code into a thread
  2. In the main program, when the user presses on the button which executes this report, the button is deactivated.
  3. When the thread finishes, it has to notify the main program, and will set a flag saying that there is an HTML file waiting to be displayed.
  4. Check whether it is safe to run Word, and if so, do so and read the file. At the same time, clear the 'pending' flag and enable the button.
This reminds me of programming TSRs in assembly language 20 years ago: is it safe to pop-up? Set flags to show that the pop-up is waiting.

When is it safe to run Word? I am going to take a very conservative view, and say that it is unsafe any time a module in the program is run. This will be very easy to implement, as every module is activated by pressing a button on the main screen; I simply add 'wordsafe:= false' in the button's handler before the module is displayed, and 'wordsafe:= true' after the module closes. At the same time, a check should be made to see whether there is a file pending, and if so, display it.

Wednesday, July 29, 2009

Speed, speed, speed/2

It occurred to me yesterday afternoon that a better strategy would be to emit HTML code, and then read that file into Word. This way, I get the advantage of using a standard template, and I also don't have to worry about pagination. I probably won't have to use a thread, either.

I wrote a short program yesterday evening which emits some HTML code in Hebrew, including building a table, and then imported it into Word. Apart from the fact that the table looks different from a table built in Word, everything was ok (or to use a good word, copacetic).

All I need now is the time to convert my code to emit HTML.

Tuesday, July 28, 2009

Speed, speed, speed

As is apparent from my previous blogs about Word Automation, one of the applications which I wrote/develop/support uses WA to write a complicated report. Unfortunately, as the complication of the data rises, so the report takes longer (this isn't because of the WA), so much so that running this report on the client's computer was taking several minutes. I have tried to speed up the report as much as possible, by using techniques such as precalculating values and moving expensive operations (such as SQL selects) out of loops, but there is a limit to how much one can optimise.

Jon Bentley devotes a chapter in his seminal book "Programming Pearls" to optimising, and points out that the kind of optimisation which I was performing can only save a certain amount of time. In order to get big savings in time, one has to change the algorithm. Whilst I can't see how the algorithm can be changed (it's a question of get these data and print them, then get other data and print them), I started thinking of other ways of printing. This led me to consider the option of using a separate thread to print the document. I have never used threads before, but I found an article which demystified them.

I spent most of Thursday evening playing with this thread code. Fortunately, it explains how to make thread-safe calls to the Borland Database Engine (BDE), so I didn't have any problems there, although the lack of a main window meant that I had to write out the sql code manually. Calling Word from the thread worked, and it seemed that I had found a solution to the speed problem. In another life, I cynically call this 'good management' - let someone else worry about the problem. Here, instead of my program worrying about printing the document, a separate thread could do the work, and free my program to the user so that more examinees could be inputted.

I tested the new program extensively, once I had got all the obvious bugs removed, and was dismayed to find that the program would fail frequently but unpredictably. I came to the sad conclusion that Word was not thread-safe, and that all my attempts were for naught.

Today I was reading another of Joel Spolsky's blogs, this time on Office formats. A comment at the end hinted at a new solution:
Use a simpler format for writing files. If you merely have to produce Office documents programmatically, there’s almost always a better format than the Office binary formats that you can use which Word and Excel will open happily, without missing a beat.

In my application, I don't write a Word file, but instead use Word as an automation server. What if I used another program as an automation server? What if I outputted a file with the correct format as HTML, loaded that file in a browser and then printed it? Fortunately straight away I found a snippet of code which shows how to operate IE as a server, and only a few minutes later I had written a test program which worked.

I only have two problems left:
1. Closing IE after printing seems to cause the file not to print; somehow I must add a delay before closing IE.
2. I have to convert my program's output to HTML. Whilst this is going to be extremely tedious, it's not going to be too difficult. Whilst I know how to write code in HTML which produces a table, I don't yet know how to shadow one or more columns.

I'll give this solution an hour or two's work and see how I get on.