Thread: Operator definitions
I've got a big problem: I had an operator defined as follows: CREATE OPERATOR ^ ( leftarg = bit1, rightarg = bit1, procedure = bit1xor ); and this was fine until 6.5.1, but in 6.5.2 I get ERROR: parser: parse error at or near "^" I've got the same problem with CREATE OPERATOR | ( leftarg = bit1, rightarg = bit1, procedure = bit1or, commutator = | ); but at least that didn't work under 6.5.1 either. Can anybody give me a hint how to fix this? I know nothing about the parser, or lex or yacc so I don't even know where to start. I need to fix this rather urgently , as I have tons of plpgsql functions that make use of the ^ operator. At the moment I get a power for all of these which leads to pretty disastrous consequences :-(. If anybody can give me any hint at all, I'll have a go at fixing it. Much appreciated! Adriaan
> > I've got a big problem: I had an operator defined as follows: > > CREATE OPERATOR ^ ( > leftarg = bit1, > rightarg = bit1, > procedure = bit1xor > ); > > and this was fine until 6.5.1, but in 6.5.2 I get > > ERROR: parser: parse error at or near "^" > > > I've got the same problem with > > CREATE OPERATOR | ( > leftarg = bit1, > rightarg = bit1, > procedure = bit1or, > commutator = | > ); > > but at least that didn't work under 6.5.1 either. Can anybody give me a > hint how to fix this? I know nothing about the parser, or lex or yacc so > I don't even know where to start. I need to fix this rather urgently , > as I have tons of plpgsql functions that make use of the ^ operator. At > the moment I get a power for all of these which leads to pretty > disastrous consequences :-(. > > If anybody can give me any hint at all, I'll have a go at fixing it. > Much appreciated! We do special things for ^ and | so it has proper precedence for select 2 ^ 1*2 select 'asdf' | 'asdf' | 'asdf' However, there were no changes I know of in 6.5.2 that would cause it to break. -- Bruce Momjian | http://www.op.net/~candle maillist@candle.pha.pa.us | (610) 853-3000+ If your life is a hard drive, | 830 Blythe Avenue + Christ can be your backup. | Drexel Hill, Pennsylvania19026
Adriaan Joubert <a.joubert@albourne.com> writes: > I've got a big problem: I had an operator defined as follows: > CREATE OPERATOR ^ ( > leftarg = bit1, > rightarg = bit1, > procedure = bit1xor > ); > and this was fine until 6.5.1, but in 6.5.2 I get > ERROR: parser: parse error at or near "^" It looks to me like '^' and '|' have been left out of the alternatives for MathOp in src/backend/parser/gram.y. It could be they were deliberately omitted, but I bet it's just an oversight. regards, tom lane
> It looks to me like '^' and '|' have been left out of the alternatives > for MathOp in src/backend/parser/gram.y. It could be they were > deliberately omitted, but I bet it's just an oversight. OK, here is a patch to allow both ^ and | as operators, both in operator definitions and expressions. It seems to work for me. Unfortunately the regression tests do not tell me an awful lot, as several of them fail on the Alpha anyway. As I don;t really know what I'm doing, I'd appreciate it if somebody else could check the patch out and let me know whether it is ok. Cheers, Adriaan*** src/backend/parser/gram.y Thu Sep 23 10:36:47 1999 --- src/backend/parser/gram.y.orig Tue Sep 14 09:07:35 1999 *************** *** 2021,2028 **** | '*' { $$ = "*"; } | '/' { $$ = "/"; } | '%' { $$ = "%"; } - | '^' { $$ = "^"; } - | '|' { $$ = "|"; } | '<' { $$ = "<"; } | '>' { $$ = ">"; } | '=' { $$ = "="; } --- 2021,2026 ---- *************** *** 3666,3673 **** { $$ = makeA_Expr(OP, "*", $1, $3); } | a_expr '^' a_expr { $$ = makeA_Expr(OP, "^", $1, $3); } - | a_expr '|' a_expr - { $$ = makeA_Expr(OP, "|", $1, $3); } | a_expr '<' a_expr { $$ = makeA_Expr(OP, "<", $1, $3); } | a_expr '>' a_expr --- 3664,3669 ----
> > It looks to me like '^' and '|' have been left out of the alternatives > > for MathOp in src/backend/parser/gram.y. It could be they were > > deliberately omitted, but I bet it's just an oversight. > OK, here is a patch to allow both ^ and | as operators, both in operator > definitions and expressions. It seems to work for me. Unfortunately the > regression tests do not tell me an awful lot, as several of them fail on > the Alpha anyway. As I don;t really know what I'm doing, I'd appreciate > it if somebody else could check the patch out and let me know whether it > is ok. It's fine as far as it goes, but istm that there are other cases missing from gram.y also :( Bruce, how did this stuff get into the stable release? Looks like we are going to need a v6.5.3 Real Soon Now. And packagers, we should plan on having a patch for v6.5.2. I'll try coming up with one in the next couple of days; I've tested on my own gram.y but it has too many other changes to be used as-is. - Thomas -- Thomas Lockhart lockhart@alumni.caltech.edu South Pasadena, California
Adriaan Joubert <a.joubert@albourne.com> writes: > OK, here is a patch to allow both ^ and | as operators, both in operator > definitions and expressions. It seems to work for me. Unfortunately the > regression tests do not tell me an awful lot, as several of them fail on > the Alpha anyway. As I don;t really know what I'm doing, I'd appreciate > it if somebody else could check the patch out and let me know whether it > is ok. If you search for, eg, '%', you will find there are several production lists that call out all the operators; your patch only caught one of them. This is a real pain in the neck to maintain, but AFAIK we couldn't collapse the productions into a single one using MathOp without losing operator precedence info :-( It might be helpful if gram.y had annotations like "# Here be MathOps" so that you could search for the darn things and make sure you had adjusted each and every production list whenever you added/deleted one. regards, tom lane
Tom Lane wrote: > > Adriaan Joubert <a.joubert@albourne.com> writes: > > OK, here is a patch to allow both ^ and | as operators, both in operator > > definitions and expressions. It seems to work for me. Unfortunately the > > regression tests do not tell me an awful lot, as several of them fail on > > the Alpha anyway. As I don;t really know what I'm doing, I'd appreciate > > it if somebody else could check the patch out and let me know whether it > > is ok. > > If you search for, eg, '%', you will find there are several production > lists that call out all the operators; your patch only caught one of them. > Well, as I said, I don't really understand what is going on in that file and I added the minimum to make my stuff work. Thomas said he was going to have a look at it, so I think I'll rely on him fixing it, before I break anything else ;-). I've already noticed that I will need | as an operator for the SQL bit types, so I may have to hack it a bit more. I just hate changing things blindly I don't really understand. Thanks for pointing it out though! Adriaan
> > > It looks to me like '^' and '|' have been left out of the alternatives > > > for MathOp in src/backend/parser/gram.y. It could be they were > > > deliberately omitted, but I bet it's just an oversight. > > OK, here is a patch to allow both ^ and | as operators, both in operator > > definitions and expressions. It seems to work for me. Unfortunately the > > regression tests do not tell me an awful lot, as several of them fail on > > the Alpha anyway. As I don;t really know what I'm doing, I'd appreciate > > it if somebody else could check the patch out and let me know whether it > > is ok. > > It's fine as far as it goes, but istm that there are other cases > missing from gram.y also :( > > Bruce, how did this stuff get into the stable release? Looks like we > are going to need a v6.5.3 Real Soon Now. And packagers, we should > plan on having a patch for v6.5.2. I'll try coming up with one in the > next couple of days; I've tested on my own gram.y but it has too many > other changes to be used as-is. I have no idea how this got in. Looking at the cvs logs, I don't see anything since 6.5 that would cause this to break in 6.5.2. Even looking at an actual diff against 6.5.1, I don't see anything. -- Bruce Momjian | http://www.op.net/~candle maillist@candle.pha.pa.us | (610) 853-3000+ If your life is a hard drive, | 830 Blythe Avenue + Christ can be your backup. | Drexel Hill, Pennsylvania19026
> Adriaan Joubert <a.joubert@albourne.com> writes: > > OK, here is a patch to allow both ^ and | as operators, both in operator > > definitions and expressions. It seems to work for me. Unfortunately the > > regression tests do not tell me an awful lot, as several of them fail on > > the Alpha anyway. As I don;t really know what I'm doing, I'd appreciate > > it if somebody else could check the patch out and let me know whether it > > is ok. > > If you search for, eg, '%', you will find there are several production > lists that call out all the operators; your patch only caught one of them. > > This is a real pain in the neck to maintain, but AFAIK we couldn't > collapse the productions into a single one using MathOp without losing > operator precedence info :-( > > It might be helpful if gram.y had annotations like "# Here be MathOps" > so that you could search for the darn things and make sure you had > adjusted each and every production list whenever you added/deleted one. OK, I have applied a patch to fix all the operator cases for ^ and |. This will be in 6.6. The issue is that we want to specify precedence for the common math operators, and I needed to be able to specify precedence for '|' so people could do SELECT 'A' | 'B' | 'C'. -- Bruce Momjian | http://www.op.net/~candle maillist@candle.pha.pa.us | (610) 853-3000 + If your life is a hard drive, | 830 Blythe Avenue + Christ can be your backup. | Drexel Hill, Pennsylvania 19026
> OK, I have applied a patch to fix all the operator cases for ^ and |. > This will be in 6.6. > The issue is that we want to specify precedence for the common math > operators, and I needed to be able to specify precedence for '|' so people > could do SELECT 'A' | 'B' | 'C'. I had already posted and applied a patch for the stable branch, since v6.5.2 was damaged wrt v6.5 functionality. The patch will also appear in RedHat's rpms for their RH6.1 release. I hadn't yet applied the patch to the main branch, but have it in my gram.y code where I'm working on join syntax. Can you compare your patch of the main branch with the very recent changes on the stable branch? Darn, back to cvs merge hell... - Thomas -- Thomas Lockhart lockhart@alumni.caltech.edu South Pasadena, California
> > OK, I have applied a patch to fix all the operator cases for ^ and |. > > This will be in 6.6. > > The issue is that we want to specify precedence for the common math > > operators, and I needed to be able to specify precedence for '|' so people > > could do SELECT 'A' | 'B' | 'C'. > > I had already posted and applied a patch for the stable branch, since > v6.5.2 was damaged wrt v6.5 functionality. The patch will also appear > in RedHat's rpms for their RH6.1 release. I hadn't yet applied the > patch to the main branch, but have it in my gram.y code where I'm > working on join syntax. > > Can you compare your patch of the main branch with the very recent > changes on the stable branch? > > Darn, back to cvs merge hell... > Man, there are tons of changes between the two. Here are the changes I made. I can easily back this out, and re-add after you are done. -- Bruce Momjian | http://www.op.net/~candle maillist@candle.pha.pa.us | (610) 853-3000 + If your life is a hard drive, | 830 Blythe Avenue + Christ can be your backup. | Drexel Hill, Pennsylvania 19026 Index: gram.y =================================================================== RCS file: /usr/local/cvsroot/pgsql/src/backend/parser/gram.y,v retrieving revision 2.100 retrieving revision 2.103 diff -c -r2.100 -r2.103 *** gram.y 1999/09/28 04:34:44 2.100 --- gram.y 1999/09/28 14:49:36 2.103 *************** *** 10,16 **** * * * IDENTIFICATION ! * $Header: /usr/local/cvsroot/pgsql/src/backend/parser/gram.y,v 2.100 1999/09/28 04:34:44 momjian Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT --- 10,16 ---- * * * IDENTIFICATION ! * $Header: /usr/local/cvsroot/pgsql/src/backend/parser/gram.y,v 2.103 1999/09/28 14:49:36 momjian Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT *************** *** 977,982 **** --- 977,984 ---- { $$ = nconc( $1, lcons( makeString( "*"), $3)); } | default_expr '^' default_expr { $$ = nconc( $1, lcons( makeString( "^"), $3)); } + | default_expr '|' default_expr + { $$ = nconc( $1, lcons( makeString( "|"), $3)); } | default_expr '=' default_expr { elog(ERROR,"boolean expressions not supported in DEFAULT"); } | default_expr '<' default_expr *************** *** 1127,1132 **** --- 1129,1136 ---- { $$ = nconc( $1, lcons( makeString( "*"), $3)); } | constraint_expr '^' constraint_expr { $$ = nconc( $1, lcons( makeString( "^"), $3)); } + | constraint_expr '|' constraint_expr + { $$ = nconc( $1, lcons( makeString( "|"), $3)); } | constraint_expr '=' constraint_expr { $$ = nconc( $1, lcons( makeString( "="), $3)); } | constraint_expr '<' constraint_expr *************** *** 2042,2047 **** --- 2046,2053 ---- | '*' { $$ = "*"; } | '/' { $$ = "/"; } | '%' { $$ = "%"; } + | '^' { $$ = "^"; } + | '|' { $$ = "|"; } | '<' { $$ = "<"; } | '>' { $$ = ">"; } | '=' { $$ = "="; } *************** *** 3638,3643 **** --- 3644,3651 ---- | '*' { $$ = "*"; } | '/' { $$ = "/"; } | '%' { $$ = "%"; } + | '^' { $$ = "^"; } + | '|' { $$ = "|"; } ; sub_type: ANY { $$ = ANY_SUBLINK; } *************** *** 3672,3693 **** { $$ = makeA_Expr(OP, "%", NULL, $2); } | '^' a_expr { $$ = makeA_Expr(OP, "^", NULL, $2); } | a_expr '%' { $$ = makeA_Expr(OP, "%", $1, NULL); } | a_expr '^' { $$ = makeA_Expr(OP, "^", $1, NULL); } | a_expr '+' a_expr { $$ = makeA_Expr(OP, "+", $1, $3); } | a_expr '-' a_expr { $$ = makeA_Expr(OP, "-", $1, $3); } | a_expr '/' a_expr { $$ = makeA_Expr(OP, "/", $1, $3); } | a_expr '%' a_expr { $$ = makeA_Expr(OP, "%", $1, $3); } - | a_expr '*' a_expr - { $$ = makeA_Expr(OP, "*", $1, $3); } | a_expr '^' a_expr { $$ = makeA_Expr(OP, "^", $1, $3); } | a_expr '<' a_expr { $$ = makeA_Expr(OP, "<", $1, $3); } | a_expr '>' a_expr --- 3680,3711 ---- { $$ = makeA_Expr(OP, "%", NULL, $2); } | '^' a_expr { $$ = makeA_Expr(OP, "^", NULL, $2); } + | '|' a_expr + { $$ = makeA_Expr(OP, "|", NULL, $2); } + | ':' a_expr + { $$ = makeA_Expr(OP, ":", NULL, $2); } + | ';' a_expr + { $$ = makeA_Expr(OP, ";", NULL, $2); } | a_expr '%' { $$ = makeA_Expr(OP, "%", $1, NULL); } | a_expr '^' { $$ = makeA_Expr(OP, "^", $1, NULL); } + | a_expr '|' + { $$ = makeA_Expr(OP, "|", $1, NULL); } | a_expr '+' a_expr { $$ = makeA_Expr(OP, "+", $1, $3); } | a_expr '-' a_expr { $$ = makeA_Expr(OP, "-", $1, $3); } + | a_expr '*' a_expr + { $$ = makeA_Expr(OP, "*", $1, $3); } | a_expr '/' a_expr { $$ = makeA_Expr(OP, "/", $1, $3); } | a_expr '%' a_expr { $$ = makeA_Expr(OP, "%", $1, $3); } | a_expr '^' a_expr { $$ = makeA_Expr(OP, "^", $1, $3); } + | a_expr '|' a_expr + { $$ = makeA_Expr(OP, "|", $1, $3); } | a_expr '<' a_expr { $$ = makeA_Expr(OP, "<", $1, $3); } | a_expr '>' a_expr *************** *** 3701,3712 **** | a_expr '=' a_expr { $$ = makeA_Expr(OP, "=", $1, $3); } - | ':' a_expr - { $$ = makeA_Expr(OP, ":", NULL, $2); } - | ';' a_expr - { $$ = makeA_Expr(OP, ";", NULL, $2); } - | '|' a_expr - { $$ = makeA_Expr(OP, "|", NULL, $2); } | a_expr TYPECAST Typename { $$ = (Node *)$1; --- 3719,3724 ---- *************** *** 4089,4094 **** --- 4101,4116 ---- n->subselect = $4; $$ = (Node *)n; } + | a_expr '*' '(' SubSelect ')' + { + SubLink *n = makeNode(SubLink); + n->lefthand = lcons($1, NULL); + n->oper = lcons("*",NIL); + n->useor = false; + n->subLinkType = EXPR_SUBLINK; + n->subselect = $4; + $$ = (Node *)n; + } | a_expr '/' '(' SubSelect ')' { SubLink *n = makeNode(SubLink); *************** *** 4109,4124 **** n->subselect = $4; $$ = (Node *)n; } ! | a_expr '*' '(' SubSelect ')' { SubLink *n = makeNode(SubLink); n->lefthand = lcons($1, NULL); ! n->oper = lcons("*",NIL); n->useor = false; n->subLinkType = EXPR_SUBLINK; n->subselect = $4; $$ = (Node *)n; } | a_expr '<' '(' SubSelect ')' { SubLink *n = makeNode(SubLink); --- 4131,4156 ---- n->subselect = $4; $$ = (Node *)n; } ! | a_expr '^' '(' SubSelect ')' { SubLink *n = makeNode(SubLink); n->lefthand = lcons($1, NULL); ! n->oper = lcons("^",NIL); n->useor = false; n->subLinkType = EXPR_SUBLINK; n->subselect = $4; $$ = (Node *)n; } + | a_expr '|' '(' SubSelect ')' + { + SubLink *n = makeNode(SubLink); + n->lefthand = lcons($1, NULL); + n->oper = lcons("|",NIL); + n->useor = false; + n->subLinkType = EXPR_SUBLINK; + n->subselect = $4; + $$ = (Node *)n; + } | a_expr '<' '(' SubSelect ')' { SubLink *n = makeNode(SubLink); *************** *** 4179,4184 **** --- 4211,4226 ---- n->subselect = $5; $$ = (Node *)n; } + | a_expr '*' ANY '(' SubSelect ')' + { + SubLink *n = makeNode(SubLink); + n->lefthand = lcons($1,NIL); + n->oper = lcons("*",NIL); + n->useor = false; + n->subLinkType = ANY_SUBLINK; + n->subselect = $5; + $$ = (Node *)n; + } | a_expr '/' ANY '(' SubSelect ')' { SubLink *n = makeNode(SubLink); *************** *** 4199,4209 **** n->subselect = $5; $$ = (Node *)n; } ! | a_expr '*' ANY '(' SubSelect ')' { SubLink *n = makeNode(SubLink); n->lefthand = lcons($1,NIL); ! n->oper = lcons("*",NIL); n->useor = false; n->subLinkType = ANY_SUBLINK; n->subselect = $5; --- 4241,4261 ---- n->subselect = $5; $$ = (Node *)n; } ! | a_expr '^' ANY '(' SubSelect ')' { SubLink *n = makeNode(SubLink); n->lefthand = lcons($1,NIL); ! n->oper = lcons("^",NIL); ! n->useor = false; ! n->subLinkType = ANY_SUBLINK; ! n->subselect = $5; ! $$ = (Node *)n; ! } ! | a_expr '|' ANY '(' SubSelect ')' ! { ! SubLink *n = makeNode(SubLink); ! n->lefthand = lcons($1,NIL); ! n->oper = lcons("|",NIL); n->useor = false; n->subLinkType = ANY_SUBLINK; n->subselect = $5; *************** *** 4269,4274 **** --- 4321,4336 ---- n->subselect = $5; $$ = (Node *)n; } + | a_expr '*' ALL '(' SubSelect ')' + { + SubLink *n = makeNode(SubLink); + n->lefthand = lcons($1, NULL); + n->oper = lcons("*",NIL); + n->useor = false; + n->subLinkType = ALL_SUBLINK; + n->subselect = $5; + $$ = (Node *)n; + } | a_expr '/' ALL '(' SubSelect ')' { SubLink *n = makeNode(SubLink); *************** *** 4289,4299 **** n->subselect = $5; $$ = (Node *)n; } ! | a_expr '*' ALL '(' SubSelect ')' { SubLink *n = makeNode(SubLink); n->lefthand = lcons($1, NULL); ! n->oper = lcons("*",NIL); n->useor = false; n->subLinkType = ALL_SUBLINK; n->subselect = $5; --- 4351,4371 ---- n->subselect = $5; $$ = (Node *)n; } ! | a_expr '^' ALL '(' SubSelect ')' { SubLink *n = makeNode(SubLink); n->lefthand = lcons($1, NULL); ! n->oper = lcons("^",NIL); ! n->useor = false; ! n->subLinkType = ALL_SUBLINK; ! n->subselect = $5; ! $$ = (Node *)n; ! } ! | a_expr '|' ALL '(' SubSelect ')' ! { ! SubLink *n = makeNode(SubLink); ! n->lefthand = lcons($1, NULL); ! n->oper = lcons("|",NIL); n->useor = false; n->subLinkType = ALL_SUBLINK; n->subselect = $5; *************** *** 4363,4390 **** { $$ = makeA_Expr(OP, "%", NULL, $2); } | '^' b_expr { $$ = makeA_Expr(OP, "^", NULL, $2); } | b_expr '%' { $$ = makeA_Expr(OP, "%", $1, NULL); } | b_expr '^' { $$ = makeA_Expr(OP, "^", $1, NULL); } | b_expr '+' b_expr { $$ = makeA_Expr(OP, "+", $1, $3); } | b_expr '-' b_expr { $$ = makeA_Expr(OP, "-", $1, $3); } | b_expr '/' b_expr { $$ = makeA_Expr(OP, "/", $1, $3); } | b_expr '%' b_expr { $$ = makeA_Expr(OP, "%", $1, $3); } - | b_expr '*' b_expr - { $$ = makeA_Expr(OP, "*", $1, $3); } | b_expr '^' b_expr { $$ = makeA_Expr(OP, "^", $1, $3); } ! | ':' b_expr ! { $$ = makeA_Expr(OP, ":", NULL, $2); } ! | ';' b_expr ! { $$ = makeA_Expr(OP, ";", NULL, $2); } ! | '|' b_expr ! { $$ = makeA_Expr(OP, "|", NULL, $2); } | b_expr TYPECAST Typename { $$ = (Node *)$1; --- 4435,4466 ---- { $$ = makeA_Expr(OP, "%", NULL, $2); } | '^' b_expr { $$ = makeA_Expr(OP, "^", NULL, $2); } + | '|' b_expr + { $$ = makeA_Expr(OP, "|", NULL, $2); } + | ':' b_expr + { $$ = makeA_Expr(OP, ":", NULL, $2); } + | ';' b_expr + { $$ = makeA_Expr(OP, ";", NULL, $2); } | b_expr '%' { $$ = makeA_Expr(OP, "%", $1, NULL); } | b_expr '^' { $$ = makeA_Expr(OP, "^", $1, NULL); } + | b_expr '|' + { $$ = makeA_Expr(OP, "|", $1, NULL); } | b_expr '+' b_expr { $$ = makeA_Expr(OP, "+", $1, $3); } | b_expr '-' b_expr { $$ = makeA_Expr(OP, "-", $1, $3); } + | b_expr '*' b_expr + { $$ = makeA_Expr(OP, "*", $1, $3); } | b_expr '/' b_expr { $$ = makeA_Expr(OP, "/", $1, $3); } | b_expr '%' b_expr { $$ = makeA_Expr(OP, "%", $1, $3); } | b_expr '^' b_expr { $$ = makeA_Expr(OP, "^", $1, $3); } ! | b_expr '|' b_expr ! { $$ = makeA_Expr(OP, "|", $1, $3); } | b_expr TYPECAST Typename { $$ = (Node *)$1;
Thomas Lockhart <lockhart@alumni.caltech.edu> writes: > Darn, back to cvs merge hell... Yes, Bruce seems to be catching up on his patch queue --- and applying a lot of old code that needs changes :-(. regards, tom lane
> Thomas Lockhart <lockhart@alumni.caltech.edu> writes: > > Darn, back to cvs merge hell... > > Yes, Bruce seems to be catching up on his patch queue --- and applying > a lot of old code that needs changes :-(. True. -- Bruce Momjian | http://www.op.net/~candle maillist@candle.pha.pa.us | (610) 853-3000 + If your life is a hard drive, | 830 Blythe Avenue + Christ can be your backup. | Drexel Hill, Pennsylvania 19026
> Thomas Lockhart <lockhart@alumni.caltech.edu> writes: > > Darn, back to cvs merge hell... > > Yes, Bruce seems to be catching up on his patch queue --- and applying > a lot of old code that needs changes :-(. Yes. Good news is that I am done. -- Bruce Momjian | http://www.op.net/~candle maillist@candle.pha.pa.us | (610) 853-3000 + If your life is a hard drive, | 830 Blythe Avenue + Christ can be your backup. | Drexel Hill, Pennsylvania 19026