Subject | [info/tip] dynamically created generators (inside stored procedures). |
---|---|
Author | Jochem Maas |
Post date | 2006-02-23T15:22:38Z |
hi firebirders!
<loose_thought>
how I wished I had the time, energy and know-how to put my
firebird realted php project into the public domain (in such
a way that people could actually make a decent stab at
evaluating it :-(
</loose_thought>
in the mean time.... today I have spent most of my time trying to
figure out how I can dynamically create GENERATORS (a process
that is triggered on a successful insert of a certain table of my
db). why would I want to do this? well I have a table that stores
'prizedraw' [marketing] actions that the client can add to (& edit)
via a CMS - according to local law the [3] winning prizedraw numbers
must be determined beforehand and the numbers deposited with a
sollicitor - the upshot being that entrants must be handed out prizedraw
numbers from a predefined range (in fact a start number is enough) and to avoid
any kind of race condition when handing out prizedraw numbers from
the predefined range I chose to use firebird GENERATORs (why re-invent the wheel
right).
so I had to create a trigger that would create a GENERATOR for
me when a new 'prizedraw' was inserted into the DB.... easier said than done!
the major help came from Ivan Presnosil via a page he wrote:
http://www.volny.cz/iprenosil/interbase/ip_ib_code_generator.htm#_code_creategen
I don't know Ivan personally but I get the impression that he has more
english languange documentation articles and tips about firebird under his name
than everybody else on the planet put together - thank you Ivan!
BUT I wasn't there yet - for 2 reasons:
1. the GEN_ID() function does not recognize variables as the first argument, which
means the following are symantically identical (i.e. you can only use hardcoded
names for the GENERATOR (at least when:
r = GEN_ID(MY_GEN, 1);
r = GEN_ID('MY_GEN', 1);
2. Once I had figured out how to get aroung the first problem I kept getting
type mismatchs for the return value of GEN_ID() and the variable I was stuffing
it into.
The solution to the first problem was to create (i.e. using concat'ing) a string
of SQL in my stored procedure and execute it with the 'EXECUTE STATEMENT ... INTO'
The solution to the second problem was to make the variable [that will store
the return value of GEN_ID()] of type BIG_INT (as opposed to INTEGER which what I had
it as).
In the end I had a stored proc whose body looks something like:
BEGIN
IF (NEW.start_number IS NOT NULL) THEN
BEGIN
-- create a generator name.
genname = 'GEN_PRIZEDRAW_' || NEW.webcode;
genstart = 0;
-- determine if a generator all ready exists by the given name
IF (NOT EXISTS(SELECT * FROM RDB$GENERATORS WHERE RDB$GENERATOR_NAME = :genname)) THEN
BEGIN
-- create the generator
INSERT INTO RDB$GENERATORS (RDB$GENERATOR_NAME) VALUES (UPPER(:genname));
-- init the generator to the correct value.
vsql = 'SELECT GEN_ID(' || genname || ', ' || NEW.start_number || ' - GEN_ID(' || genname || ',0)) FROM
RDB$DATABASE';
EXECUTE STATEMENT :vsql INTO :genstart;
-- link the generator to the prizedraw [row]
UPDATE lc_prizedraw l SET l.generator_name=:genname WHERE l.lc_prizedraw_id=NEW.lc_prizedraw_id;
END
END
END
<loose_thought>
how I wished I had the time, energy and know-how to put my
firebird realted php project into the public domain (in such
a way that people could actually make a decent stab at
evaluating it :-(
</loose_thought>
in the mean time.... today I have spent most of my time trying to
figure out how I can dynamically create GENERATORS (a process
that is triggered on a successful insert of a certain table of my
db). why would I want to do this? well I have a table that stores
'prizedraw' [marketing] actions that the client can add to (& edit)
via a CMS - according to local law the [3] winning prizedraw numbers
must be determined beforehand and the numbers deposited with a
sollicitor - the upshot being that entrants must be handed out prizedraw
numbers from a predefined range (in fact a start number is enough) and to avoid
any kind of race condition when handing out prizedraw numbers from
the predefined range I chose to use firebird GENERATORs (why re-invent the wheel
right).
so I had to create a trigger that would create a GENERATOR for
me when a new 'prizedraw' was inserted into the DB.... easier said than done!
the major help came from Ivan Presnosil via a page he wrote:
http://www.volny.cz/iprenosil/interbase/ip_ib_code_generator.htm#_code_creategen
I don't know Ivan personally but I get the impression that he has more
english languange documentation articles and tips about firebird under his name
than everybody else on the planet put together - thank you Ivan!
BUT I wasn't there yet - for 2 reasons:
1. the GEN_ID() function does not recognize variables as the first argument, which
means the following are symantically identical (i.e. you can only use hardcoded
names for the GENERATOR (at least when:
r = GEN_ID(MY_GEN, 1);
r = GEN_ID('MY_GEN', 1);
2. Once I had figured out how to get aroung the first problem I kept getting
type mismatchs for the return value of GEN_ID() and the variable I was stuffing
it into.
The solution to the first problem was to create (i.e. using concat'ing) a string
of SQL in my stored procedure and execute it with the 'EXECUTE STATEMENT ... INTO'
The solution to the second problem was to make the variable [that will store
the return value of GEN_ID()] of type BIG_INT (as opposed to INTEGER which what I had
it as).
In the end I had a stored proc whose body looks something like:
BEGIN
IF (NEW.start_number IS NOT NULL) THEN
BEGIN
-- create a generator name.
genname = 'GEN_PRIZEDRAW_' || NEW.webcode;
genstart = 0;
-- determine if a generator all ready exists by the given name
IF (NOT EXISTS(SELECT * FROM RDB$GENERATORS WHERE RDB$GENERATOR_NAME = :genname)) THEN
BEGIN
-- create the generator
INSERT INTO RDB$GENERATORS (RDB$GENERATOR_NAME) VALUES (UPPER(:genname));
-- init the generator to the correct value.
vsql = 'SELECT GEN_ID(' || genname || ', ' || NEW.start_number || ' - GEN_ID(' || genname || ',0)) FROM
RDB$DATABASE';
EXECUTE STATEMENT :vsql INTO :genstart;
-- link the generator to the prizedraw [row]
UPDATE lc_prizedraw l SET l.generator_name=:genname WHERE l.lc_prizedraw_id=NEW.lc_prizedraw_id;
END
END
END