From eb121ba2cfe1dba9463301f612743df9b63e35ce Mon Sep 17 00:00:00 2001 From: "Thomas G. Lockhart" Date: Sun, 4 Aug 2002 06:51:23 +0000 Subject: [PATCH] Implement IS OF and IS NOT OF type predicate. Can now do queries of the form: SELECT value IS OF (integer, float8); Define macros for handling typmod manipulation for date/time types. Should be more robust than all of that brute-force inline code. Rename macros for masking and typmod manipulation to put TIMESTAMP_ or INTERVAL_ in front of the macro name, to reduce the possibility of name space collisions. Allow bit string constants without fully-specified length declaration. Try implementing CREATE TABLE/OF as a mapping to inheritance. May be appropriate, or may be replace later with something more exactly like one might expect from databases without the feature. --- src/backend/parser/gram.y | 263 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 200 insertions(+), 63 deletions(-) diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 76731f97d7..c794dd7d80 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.353 2002/08/04 04:31:44 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.354 2002/08/04 06:51:23 thomas Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -70,7 +70,6 @@ static bool QueryIsRule = FALSE; static Oid *param_type_info; static int pfunc_num_args; - /* * If you need access to certain yacc-generated variables and find that * they're static by default, uncomment the next line. (this is not a @@ -97,8 +96,6 @@ static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg); static Node *doNegate(Node *n); static void doNegateFloat(Value *v); -#define MASK(b) (1 << (b)) - %} @@ -265,7 +262,7 @@ static void doNegateFloat(Value *v); %type def_arg, columnElem, where_clause, insert_column_item, a_expr, b_expr, c_expr, r_expr, AexprConst, in_expr, having_clause, func_table -%type row, row_descriptor, row_list, in_expr_nodes +%type row, row_descriptor, row_list, in_expr_nodes, type_list %type case_expr, case_arg, when_clause, case_default %type when_clause_list %type sub_type @@ -282,8 +279,11 @@ static void doNegateFloat(Value *v); %type target_el, insert_target_el, update_target_el %type Typename, SimpleTypename, ConstTypename, - GenericType, Numeric, opt_float, Character, - ConstDatetime, ConstInterval, Bit + GenericType, Numeric, opt_float, + Character, ConstCharacter, + CharacterWithLength, CharacterWithoutLength, + ConstDatetime, ConstInterval, + Bit, ConstBit, BitWithLength, BitWithoutLength %type character %type extract_arg %type opt_charset, opt_collate @@ -404,7 +404,7 @@ static void doNegateFloat(Value *v); %token UNIONJOIN /* Special keywords, not in the query language - see the "lex" file */ -%token IDENT, FCONST, SCONST, BITCONST, Op +%token IDENT, FCONST, SCONST, NCONST, BCONST, XCONST, Op %token ICONST, PARAM /* these are not real. they are here so that they get generated as #define's*/ @@ -943,12 +943,13 @@ zone_value: | ConstInterval Sconst opt_interval { A_Const *n = (A_Const *) makeStringConst($2, $1); - if ($3 != -1) + if ($3 != INTERVAL_FULL_RANGE) { - if (($3 & ~(MASK(HOUR) | MASK(MINUTE))) != 0) + if (($3 & ~(INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE))) != 0) elog(ERROR, - "Time zone interval must be HOUR or HOUR TO MINUTE"); - n->typename->typmod = ((($3 & 0x7FFF) << 16) | 0xFFFF); + "Time zone interval" + " must be HOUR or HOUR TO MINUTE"); + n->typename->typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, $3); } $$ = (Node *)n; } @@ -959,17 +960,14 @@ zone_value: elog(ERROR, "INTERVAL(%d) precision must be between %d and %d", $3, 0, MAX_INTERVAL_PRECISION); - if ($6 != -1) - { - if (($6 & ~(MASK(HOUR) | MASK(MINUTE))) != 0) - elog(ERROR, - "Time zone interval must be HOUR or HOUR TO MINUTE"); - n->typename->typmod = ((($6 & 0x7FFF) << 16) | $3); - } - else - { - n->typename->typmod = ((0x7FFF << 16) | $3); - } + + if (($6 != INTERVAL_FULL_RANGE) + && (($6 & ~(INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE))) != 0)) + elog(ERROR, + "Time zone interval" + " must be HOUR or HOUR TO MINUTE"); + + n->typename->typmod = INTERVAL_TYPMOD($3, $6); $$ = (Node *)n; } @@ -1388,6 +1386,21 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->hasoids = $9; $$ = (Node *)n; } + | CREATE OptTemp TABLE qualified_name OF qualified_name + '(' OptTableElementList ')' OptWithOids + { + /* SQL99 CREATE TABLE OF (cols) seems to be satisfied + * by our inheritance capabilities. Let's try it... + */ + CreateStmt *n = makeNode(CreateStmt); + $4->istemp = $2; + n->relation = $4; + n->tableElts = $8; + n->inhRelations = makeList1($6); + n->constraints = NIL; + n->hasoids = $10; + $$ = (Node *)n; + } ; /* @@ -2100,7 +2113,8 @@ TriggerFuncArg: } | FCONST { $$ = makeString($1); } | Sconst { $$ = makeString($1); } - | BITCONST { $$ = makeString($1); } + | BCONST { $$ = makeString($1); } + | XCONST { $$ = makeString($1); } | ColId { $$ = makeString($1); } ; @@ -4651,6 +4665,13 @@ relation_expr: $$->inhOpt = INH_NO; $$->alias = NULL; } + | ONLY '(' qualified_name ')' + { + /* no inheritance, SQL99-style syntax */ + $$ = $3; + $$->inhOpt = INH_NO; + $$->alias = NULL; + } ; @@ -4722,12 +4743,16 @@ opt_array_bounds: * for. FIXME later. */ SimpleTypename: - ConstTypename { $$ = $1; } + GenericType { $$ = $1; } + | Numeric { $$ = $1; } + | Bit { $$ = $1; } + | Character { $$ = $1; } + | ConstDatetime { $$ = $1; } | ConstInterval opt_interval { $$ = $1; - if ($2 != -1) - $$->typmod = ((($2 & 0x7FFF) << 16) | 0xFFFF); + if ($2 != INTERVAL_FULL_RANGE) + $$->typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, $2); } | ConstInterval '(' Iconst ')' opt_interval { @@ -4736,7 +4761,7 @@ SimpleTypename: elog(ERROR, "INTERVAL(%d) precision must be between %d and %d", $3, 0, MAX_INTERVAL_PRECISION); - $$->typmod = ((($5 & 0x7FFF) << 16) | $3); + $$->typmod = INTERVAL_TYPMOD($3, $5); } | type_name attrs { @@ -4746,12 +4771,21 @@ SimpleTypename: } ; +/* We have a separate ConstTypename to allow defaulting fixed-length + * types such as CHAR() and BIT() to an unspecified length. + * SQL9x requires that these default to a length of one, but this + * makes no sense for constructs like CHAR 'hi' and BIT '0101', + * where there is an obvious better choice to make. + * Note that ConstInterval is not included here since it must + * be pushed up higher in the rules to accomodate the postfix + * options (e.g. INTERVAL '1' YEAR). + */ ConstTypename: - GenericType { $$ = $1;} - | Numeric { $$ = $1;} - | Bit { $$ = $1;} - | Character { $$ = $1;} - | ConstDatetime { $$ = $1;} + GenericType { $$ = $1; } + | Numeric { $$ = $1; } + | ConstBit { $$ = $1; } + | ConstCharacter { $$ = $1; } + | ConstDatetime { $$ = $1; } ; GenericType: @@ -4899,7 +4933,29 @@ opt_decimal: * SQL92 bit-field data types * The following implements BIT() and BIT VARYING(). */ -Bit: BIT opt_varying '(' Iconst ')' +Bit: BitWithLength + { + $$ = $1; + } + | BitWithoutLength + { + $$ = $1; + } + ; + +ConstBit: BitWithLength + { + $$ = $1; + } + | BitWithoutLength + { + $$->typmod = -1; + $$ = $1; + } + ; + +BitWithLength: + BIT opt_varying '(' Iconst ')' { char *typname; @@ -4913,7 +4969,10 @@ Bit: BIT opt_varying '(' Iconst ')' typname, (MaxAttrSize * BITS_PER_BYTE)); $$->typmod = $4; } - | BIT opt_varying + ; + +BitWithoutLength: + BIT opt_varying { /* bit defaults to bit(1), varbit to no limit */ if ($2) @@ -4934,7 +4993,34 @@ Bit: BIT opt_varying '(' Iconst ')' * SQL92 character data types * The following implements CHAR() and VARCHAR(). */ -Character: character '(' Iconst ')' opt_charset +Character: CharacterWithLength + { + $$ = $1; + } + | CharacterWithoutLength + { + $$ = $1; + } + ; + +ConstCharacter: CharacterWithLength + { + $$ = $1; + } + | CharacterWithoutLength + { + /* Length was not specified so allow to be unrestricted. + * This handles problems with fixed-length (bpchar) strings + * which in column definitions must default to a length + * of one, but should not be constrained if the length + * was not specified. + */ + $1->typmod = -1; + $$ = $1; + } + ; + +CharacterWithLength: character '(' Iconst ')' opt_charset { if (($5 != NULL) && (strcmp($5, "sql_text") != 0)) { @@ -4963,7 +5049,9 @@ Character: character '(' Iconst ')' opt_charset */ $$->typmod = VARHDRSZ + $3; } - | character opt_charset + ; + +CharacterWithoutLength: character opt_charset { if (($2 != NULL) && (strcmp($2, "sql_text") != 0)) { @@ -5090,27 +5178,30 @@ opt_timezone: ; opt_interval: - YEAR_P { $$ = MASK(YEAR); } - | MONTH_P { $$ = MASK(MONTH); } - | DAY_P { $$ = MASK(DAY); } - | HOUR_P { $$ = MASK(HOUR); } - | MINUTE_P { $$ = MASK(MINUTE); } - | SECOND_P { $$ = MASK(SECOND); } + YEAR_P { $$ = INTERVAL_MASK(YEAR); } + | MONTH_P { $$ = INTERVAL_MASK(MONTH); } + | DAY_P { $$ = INTERVAL_MASK(DAY); } + | HOUR_P { $$ = INTERVAL_MASK(HOUR); } + | MINUTE_P { $$ = INTERVAL_MASK(MINUTE); } + | SECOND_P { $$ = INTERVAL_MASK(SECOND); } | YEAR_P TO MONTH_P - { $$ = MASK(YEAR) | MASK(MONTH); } + { $$ = INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH); } | DAY_P TO HOUR_P - { $$ = MASK(DAY) | MASK(HOUR); } + { $$ = INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR); } | DAY_P TO MINUTE_P - { $$ = MASK(DAY) | MASK(HOUR) | MASK(MINUTE); } + { $$ = INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) + | INTERVAL_MASK(MINUTE); } | DAY_P TO SECOND_P - { $$ = MASK(DAY) | MASK(HOUR) | MASK(MINUTE) | MASK(SECOND); } + { $$ = INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) + | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND); } | HOUR_P TO MINUTE_P - { $$ = MASK(HOUR) | MASK(MINUTE); } + { $$ = INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE); } | HOUR_P TO SECOND_P - { $$ = MASK(HOUR) | MASK(MINUTE) | MASK(SECOND); } + { $$ = INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) + | INTERVAL_MASK(SECOND); } | MINUTE_P TO SECOND_P - { $$ = MASK(MINUTE) | MASK(SECOND); } - | /*EMPTY*/ { $$ = -1; } + { $$ = INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND); } + | /*EMPTY*/ { $$ = INTERVAL_FULL_RANGE; } ; @@ -5510,6 +5601,14 @@ a_expr: c_expr { $$ = $1; } } | a_expr IS DISTINCT FROM a_expr %prec DISTINCT { $$ = (Node *) makeSimpleA_Expr(DISTINCT, "=", $1, $5); } + | a_expr IS OF '(' type_list ')' + { + $$ = (Node *) makeSimpleA_Expr(OF, "=", $1, (Node *) $5); + } + | a_expr IS NOT OF '(' type_list ')' + { + $$ = (Node *) makeSimpleA_Expr(OF, "!=", $1, (Node *) $6); + } | a_expr BETWEEN b_expr AND b_expr %prec BETWEEN { $$ = (Node *) makeA_Expr(AND, NIL, @@ -5588,6 +5687,19 @@ a_expr: c_expr { $$ = $1; } n->subselect = $4; $$ = (Node *)n; } + | UNIQUE select_with_parens %prec Op + { + /* Not sure how to get rid of the parentheses + * but there are lots of shift/reduce errors without them. + * + * Should be able to implement this by plopping the entire + * select into a node, then transforming the target expressions + * from whatever they are into count(*), and testing the + * entire result equal to one. + * But, will probably implement a separate node in the executor. + */ + elog(ERROR, "UNIQUE predicate is not yet implemented"); + } | r_expr { $$ = $1; } ; @@ -5643,6 +5755,14 @@ b_expr: c_expr { $$ = (Node *) makeA_Expr(OP, $2, $1, NULL); } | b_expr IS DISTINCT FROM b_expr %prec Op { $$ = (Node *) makeSimpleA_Expr(DISTINCT, "=", $1, $5); } + | b_expr IS OF '(' type_list ')' + { + $$ = (Node *) makeSimpleA_Expr(OF, "=", $1, (Node *) $5); + } + | b_expr IS NOT OF '(' type_list ')' + { + $$ = (Node *) makeSimpleA_Expr(OF, "!=", $1, (Node *) $6); + } ; /* @@ -6139,6 +6259,16 @@ extract_list: | /*EMPTY*/ { $$ = NIL; } ; +type_list: type_list ',' Typename + { + $$ = lappend($1, $3); + } + | Typename + { + $$ = makeList1($1); + } + ; + /* Allow delimited string SCONST in extract_arg as an SQL extension. * - thomas 2001-04-12 */ @@ -6186,6 +6316,7 @@ position_list: * SQL9x defines a specific syntax for arguments to SUBSTRING(): * o substring(text from int for int) * o substring(text from int) get entire string from starting point "int" + * o substring(text from pattern) get entire string matching pattern * o substring(text for int) get first "int" characters of string * We also want to implement generic substring functions which accept * the usual generic list of arguments. So we will accept both styles @@ -6511,8 +6642,8 @@ func_name: function_name ; -/* Constants - * Include TRUE/FALSE for SQL3 support. - thomas 1997-10-24 +/* + * Constants */ AexprConst: Iconst { @@ -6535,19 +6666,25 @@ AexprConst: Iconst n->val.val.str = $1; $$ = (Node *)n; } - | BITCONST + | BCONST { A_Const *n = makeNode(A_Const); n->val.type = T_BitString; n->val.val.str = $1; $$ = (Node *)n; } - /* This rule formerly used Typename, - * but that causes reduce conflicts with subscripted column names. - * Now, separate into ConstTypename and ConstInterval, - * to allow implementing the SQL92 syntax for INTERVAL literals. - * - thomas 2000-06-24 - */ + | XCONST + { + /* This is a bit constant per SQL99: + * Without Feature F511, "BIT data type", + * a shall not be a + * or a . + */ + A_Const *n = makeNode(A_Const); + n->val.type = T_BitString; + n->val.val.str = $1; + $$ = (Node *)n; + } | ConstTypename Sconst { A_Const *n = makeNode(A_Const); @@ -6563,8 +6700,8 @@ AexprConst: Iconst n->val.type = T_String; n->val.val.str = $2; /* precision is not specified, but fields may be... */ - if ($3 != -1) - n->typename->typmod = ((($3 & 0x7FFF) << 16) | 0xFFFF); + if ($3 != INTERVAL_FULL_RANGE) + n->typename->typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, $3); $$ = (Node *)n; } | ConstInterval '(' Iconst ')' Sconst opt_interval @@ -6578,7 +6715,7 @@ AexprConst: Iconst elog(ERROR, "INTERVAL(%d) precision must be between %d and %d", $3, 0, MAX_INTERVAL_PRECISION); - n->typename->typmod = ((($6 & 0x7FFF) << 16) | $3); + n->typename->typmod = INTERVAL_TYPMOD($3, $6); $$ = (Node *)n; } | PARAM opt_indirection -- 2.11.0