I've been bothered for awhile about a couple of inconsistencies in our
handling of user-defined type names: you can't schema-qualify a type
name that you use to prefix a literal constant, and you can't use
typmod qualifiers with user-defined types.  Shachar Shemesh's complaint
today about the latter problem got me thinking about it again, and a
few idle hours with bison led me to a possible solution.  A proof-of-
concept diff is attached.  (You can't do anything much with it, because
I haven't bothered to fill in some of the actions; the point is that
the modified grammar gets through bison with no errors.)

The principal idea that was needed to make this work is to make generic
type and function names be exactly the same set, so that you don't have
to decide which you are looking at until you see what comes later.
In terms of the present set of reserved and semi-reserved words, this
is no loss for function names and a net gain for type names.  However,
I have some lingering worry that hard-wiring this assumption might paint
us into a corner later.  Is anyone aware of dark corners of SQL99 or
SQL2003 where we might have a problem with such a restriction?

                        regards, tom lane

*** gram.y.orig Sun Jun 27 21:19:11 2004
--- gram.y      Sun Jun 27 22:51:26 2004
***************
*** 190,196 ****
  
  %type <str>           relation_name copy_file_name
                                database_name access_method_clause access_method 
attr_name
!                               index_name name function_name file_name
  
  %type <list>  func_name handler_name qual_Op qual_all_Op subquery_Op
                                opt_class opt_validator
--- 190,196 ----
  
  %type <str>           relation_name copy_file_name
                                database_name access_method_clause access_method 
attr_name
!                               index_name name file_name
  
  %type <list>  func_name handler_name qual_Op qual_all_Op subquery_Op
                                opt_class opt_validator
***************
*** 305,311 ****
  %type <str>           Sconst comment_text
  %type <str>           UserId opt_boolean ColId_or_Sconst
  %type <list>  var_list var_list_or_default
! %type <str>           ColId ColLabel var_name type_name param_name
  %type <node>  var_value zone_value
  
  %type <keyword> unreserved_keyword func_name_keyword
--- 305,311 ----
  %type <str>           Sconst comment_text
  %type <str>           UserId opt_boolean ColId_or_Sconst
  %type <list>  var_list var_list_or_default
! %type <str>           ColId ColLabel var_name type_function_name param_name
  %type <node>  var_value zone_value
  
  %type <keyword> unreserved_keyword func_name_keyword
***************
*** 3302,3308 ****
  /*
   * Ideally param_name should be ColId, but that causes too many conflicts.
   */
! param_name:   function_name
                ;
  
  func_return:
--- 3302,3308 ----
  /*
   * Ideally param_name should be ColId, but that causes too many conflicts.
   */
! param_name:   type_function_name
                ;
  
  func_return:
***************
*** 3318,3327 ****
  
  /*
   * We would like to make the second production here be ColId attrs etc,
!  * but that causes reduce/reduce conflicts.  type_name is next best choice.
   */
  func_type:    Typename                                                               
 { $$ = $1; }
!                       | type_name attrs '%' TYPE_P
                                {
                                        $$ = makeNode(TypeName);
                                        $$->names = lcons(makeString($1), $2);
--- 3318,3328 ----
  
  /*
   * We would like to make the second production here be ColId attrs etc,
!  * but that causes reduce/reduce conflicts.  type_function_name is next best
!  * choice.
   */
  func_type:    Typename                                                               
 { $$ = $1; }
!                       | type_function_name attrs '%' TYPE_P
                                {
                                        $$ = makeNode(TypeName);
                                        $$->names = lcons(makeString($1), $2);
***************
*** 5380,5393 ****
                                        {  $$ = NIL; }
                ;
  
- /*
-  * XXX ideally, the production for a qualified typename should be ColId attrs
-  * (there's no obvious reason why the first name should need to be restricted)
-  * and should be an alternative of GenericType (so that it can be used to
-  * specify a type for a literal in AExprConst).  However doing either causes
-  * reduce/reduce conflicts that I haven't been able to find a workaround
-  * for.  FIXME later.
-  */
  SimpleTypename:
                        GenericType                                                    
         { $$ = $1; }
                        | Numeric                                                      
         { $$ = $1; }
--- 5381,5386 ----
***************
*** 5418,5429 ****
                                        }
                                        $$->typmod = INTERVAL_TYPMOD($3, $5);
                                }
-                       | type_name attrs
-                               {
-                                       $$ = makeNode(TypeName);
-                                       $$->names = lcons(makeString($1), $2);
-                                       $$->typmod = -1;
-                               }
                ;
  
  /* We have a separate ConstTypename to allow defaulting fixed-length
--- 5411,5416 ----
***************
*** 5433,5453 ****
   * 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; }
                        | ConstBit                                                     
         { $$ = $1; }
                        | ConstCharacter                                               
 { $$ = $1; }
                        | ConstDatetime                                                
 { $$ = $1; }
                ;
  
  GenericType:
!                       type_name
!                               {
!                                       $$ = makeTypeName($1);
!                               }
                ;
  
  /* SQL92 numeric data types
--- 5420,5445 ----
   * 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).  Likewise, we have to handle
!  * the generic-type-name case in AExprConst to avoid premature
!  * reduce/reduce conflicts against function names.
   */
  ConstTypename:
!                       Numeric                                                        
         { $$ = $1; }
                        | ConstBit                                                     
         { $$ = $1; }
                        | ConstCharacter                                               
 { $$ = $1; }
                        | ConstDatetime                                                
 { $$ = $1; }
                ;
  
  GenericType:
!                       type_function_name
!                                       { ... }
!                       | type_function_name attrs
!                                       { ... }
!                       | type_function_name '(' Iconst ')'
!                                       { ... }
!                       | type_function_name attrs '(' Iconst ')'
!                                       { ... }
                ;
  
  /* SQL92 numeric data types
***************
*** 7385,7396 ****
  /*
   * The production for a qualified func_name has to exactly match the
   * production for a qualified columnref, because we cannot tell which we
!  * are parsing until we see what comes after it ('(' for a func_name,
   * anything else for a columnref).  Therefore we allow 'indirection' which
   * may contain subscripts, and reject that case in the C code.  (If we
   * ever implement SQL99-like methods, such syntax may actually become legal!)
   */
! func_name:    function_name
                                        { $$ = list_make1(makeString($1)); }
                        | relation_name indirection
                                        { $$ = check_func_name(lcons(makeString($1), 
$2)); }
--- 7377,7388 ----
  /*
   * The production for a qualified func_name has to exactly match the
   * production for a qualified columnref, because we cannot tell which we
!  * are parsing until we see what comes after it ('(' or Sconst for a func_name,
   * anything else for a columnref).  Therefore we allow 'indirection' which
   * may contain subscripts, and reject that case in the C code.  (If we
   * ever implement SQL99-like methods, such syntax may actually become legal!)
   */
! func_name:    type_function_name
                                        { $$ = list_make1(makeString($1)); }
                        | relation_name indirection
                                        { $$ = check_func_name(lcons(makeString($1), 
$2)); }
***************
*** 7448,7453 ****
--- 7440,7461 ----
                                        n->val.val.str = $2;
                                        $$ = (Node *)n;
                                }
+                       | func_name Sconst
+                               {
+                                       A_Const *n = makeNode(A_Const);
+                                       n->typename = ...;
+                                       n->val.type = T_String;
+                                       n->val.val.str = $2;
+                                       $$ = (Node *)n;
+                               }
+                       | func_name '(' expr_list ')' Sconst
+                               {
+                                       A_Const *n = makeNode(A_Const);
+                                       n->typename = ...;
+                                       n->val.type = T_String;
+                                       n->val.val.str = $5;
+                                       $$ = (Node *)n;
+                               }
                        | ConstInterval Sconst opt_interval
                                {
                                        A_Const *n = makeNode(A_Const);
***************
*** 7520,7534 ****
                        | col_name_keyword                                             
 { $$ = pstrdup($1); }
                ;
  
! /* Type identifier --- names that can be type names.
!  */
! type_name:    IDENT                                                                  
 { $$ = $1; }
!                       | unreserved_keyword                                    { $$ = 
pstrdup($1); }
!               ;
! 
! /* Function identifier --- names that can be function names.
   */
! function_name:
                        IDENT                                                          
         { $$ = $1; }
                        | unreserved_keyword                                    { $$ = 
pstrdup($1); }
                        | func_name_keyword                                            
 { $$ = pstrdup($1); }
--- 7528,7536 ----
                        | col_name_keyword                                             
 { $$ = pstrdup($1); }
                ;
  
! /* Type/function identifier --- names that can be type or function names.
   */
! type_function_name:
                        IDENT                                                          
         { $$ = $1; }
                        | unreserved_keyword                                    { $$ = 
pstrdup($1); }
                        | func_name_keyword                                            
 { $$ = pstrdup($1); }

---------------------------(end of broadcast)---------------------------
TIP 9: the planner will ignore your desire to choose an index scan if your
      joining column's datatypes do not match

Reply via email to