@@ -502,6 +502,12 @@ transformIndirection(ParseState *pstate, A_Indirection *ind)
502502 return result ;
503503}
504504
505+ static bool
506+ type_is_subfieldable (Oid typeid )
507+ {
508+ return ISCOMPLEX (typeid ) || typeid == RECORDOID ;
509+ }
510+
505511/*
506512 * Transform a ColumnRef.
507513 *
@@ -510,18 +516,19 @@ transformIndirection(ParseState *pstate, A_Indirection *ind)
510516static Node *
511517transformColumnRef (ParseState * pstate , ColumnRef * cref )
512518{
513- Node * node = NULL ;
519+ Node * node = NULL , * node2 = NULL ;
514520 char * nspname = NULL ;
515521 char * relname = NULL ;
516522 char * colname = NULL ;
517- ParseNamespaceItem * nsitem ;
518- int levels_up ;
523+ ParseNamespaceItem * nsitem , * nsitem2 ;
524+ int levels_up , levels_up2 ;
525+ ColumnRef * indcref = NULL ;
526+ List * indirection = NULL ;
519527 enum
520528 {
521529 CRERR_NO_COLUMN ,
522530 CRERR_NO_RTE ,
523- CRERR_WRONG_DB ,
524- CRERR_TOO_MANY
531+ CRERR_AMBIGUOUS ,
525532 } crerr = CRERR_NO_COLUMN ;
526533 const char * err ;
527534
@@ -634,8 +641,12 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
634641 * if no luck, try to resolve as unqualified table name (A.*).
635642 * A.B A is an unqualified table name; B is either a
636643 * column or function name (trying column name first).
637- * A.B.C schema A, table B, col or func name C.
638- * A.B.C.D catalog A, schema B, table C, col or func D.
644+ * A.B.C schema A, table B, col or func name C; or
645+ * correlation A, column B, field C.
646+ * A.B.C.D catalog A, schema B, table C, col or func D; or
647+ * correlation A, column B, fields C, D.
648+ * A.B.C.D.E...
649+ * correlation A, column B, fields C, D, E, etc.
639650 * A.* A is an unqualified table name; means whole-row value.
640651 * A.B.* whole-row value of table B in schema A.
641652 * A.B.C.* whole-row value of table C in schema B in catalog A.
@@ -741,7 +752,45 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
741752 nsitem = refnameNamespaceItem (pstate , nspname , relname ,
742753 cref -> location ,
743754 & levels_up );
744- if (nsitem == NULL )
755+
756+ /*
757+ * Also look it up as a subfieldable column reference, but
758+ * then it can't end with a star
759+ */
760+ if (!IsA (field3 , A_Star ))
761+ nsitem2 = refnameNamespaceItem (pstate , NULL , strVal (field1 ),
762+ cref -> location ,
763+ & levels_up2 );
764+ else
765+ nsitem2 = NULL ;
766+
767+ /* must be an explicit alias */
768+ if (nsitem2 && nsitem2 -> p_rte -> alias == NULL )
769+ nsitem2 = NULL ;
770+
771+ if (nsitem2 )
772+ node2 = scanNSItemForColumn (pstate , nsitem2 , levels_up2 , strVal (field2 ), cref -> location );
773+
774+ /*
775+ * If we found a potential subfield reference, check that the
776+ * type is subfieldable, else forget it.
777+ */
778+ if (node2 )
779+ {
780+ if (type_is_subfieldable (castNode (Var , node2 )-> vartype ))
781+ {
782+ indcref = copyObject (cref );
783+ indcref -> fields = list_truncate (indcref -> fields , 2 );
784+ indirection = list_copy_tail (cref -> fields , 2 );
785+ }
786+ else
787+ {
788+ nsitem2 = NULL ;
789+ node2 = NULL ;
790+ }
791+ }
792+
793+ if (nsitem == NULL && nsitem2 == NULL )
745794 {
746795 crerr = CRERR_NO_RTE ;
747796 break ;
@@ -757,9 +806,12 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
757806
758807 colname = strVal (field3 );
759808
809+ if (nsitem )
810+ {
760811 /* Try to identify as a column of the nsitem */
761812 node = scanNSItemForColumn (pstate , nsitem , levels_up , colname ,
762813 cref -> location );
814+
763815 if (node == NULL )
764816 {
765817 /* Try it as a function call on the whole row */
@@ -773,6 +825,13 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
773825 false,
774826 cref -> location );
775827 }
828+ }
829+
830+ if (node != NULL && node2 != NULL )
831+ {
832+ crerr = CRERR_AMBIGUOUS ;
833+ break ;
834+ }
776835 break ;
777836 }
778837 case 4 :
@@ -787,20 +846,52 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
787846 nspname = strVal (field2 );
788847 relname = strVal (field3 );
789848
849+ /* Locate the referenced nsitem (only eligible if database name matches) */
850+ if (strcmp (catname , get_database_name (MyDatabaseId )) == 0 )
851+ nsitem = refnameNamespaceItem (pstate , nspname , relname ,
852+ cref -> location ,
853+ & levels_up );
854+ else
855+ nsitem = NULL ;
856+
857+ /*
858+ * Also look it up as a subfieldable column reference, but
859+ * then it can't end with a star
860+ */
861+ if (!IsA (field4 , A_Star ))
862+ nsitem2 = refnameNamespaceItem (pstate , NULL , strVal (field1 ),
863+ cref -> location ,
864+ & levels_up2 );
865+ else
866+ nsitem2 = NULL ;
867+
868+ /* must be an explicit alias */
869+ if (nsitem2 && nsitem2 -> p_rte -> alias == NULL )
870+ nsitem2 = NULL ;
871+
872+ if (nsitem2 )
873+ node2 = scanNSItemForColumn (pstate , nsitem2 , levels_up2 , strVal (field2 ), cref -> location );
874+
790875 /*
791- * We check the catalog name and then ignore it.
876+ * If we found a potential subfield reference, check that the
877+ * type is subfieldable, else forget it.
792878 */
793- if (strcmp ( catname , get_database_name ( MyDatabaseId )) != 0 )
879+ if (node2 )
794880 {
795- crerr = CRERR_WRONG_DB ;
796- break ;
881+ if (type_is_subfieldable (castNode (Var , node2 )-> vartype ))
882+ {
883+ indcref = copyObject (cref );
884+ indcref -> fields = list_truncate (indcref -> fields , 2 );
885+ indirection = list_copy_tail (cref -> fields , 2 );
886+ }
887+ else
888+ {
889+ nsitem2 = NULL ;
890+ node2 = NULL ;
891+ }
797892 }
798893
799- /* Locate the referenced nsitem */
800- nsitem = refnameNamespaceItem (pstate , nspname , relname ,
801- cref -> location ,
802- & levels_up );
803- if (nsitem == NULL )
894+ if (nsitem == NULL && nsitem2 == NULL )
804895 {
805896 crerr = CRERR_NO_RTE ;
806897 break ;
@@ -816,6 +907,8 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
816907
817908 colname = strVal (field4 );
818909
910+ if (nsitem )
911+ {
819912 /* Try to identify as a column of the nsitem */
820913 node = scanNSItemForColumn (pstate , nsitem , levels_up , colname ,
821914 cref -> location );
@@ -832,11 +925,85 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
832925 false,
833926 cref -> location );
834927 }
928+ }
929+
930+ if (node != NULL && node2 != NULL )
931+ {
932+ crerr = CRERR_AMBIGUOUS ;
933+ break ;
934+ }
835935 break ;
836936 }
837937 default :
838- crerr = CRERR_TOO_MANY ; /* too many dotted names */
839- break ;
938+ {
939+ Node * field1 = (Node * ) linitial (cref -> fields );
940+ Node * field2 = (Node * ) lsecond (cref -> fields );
941+ Node * fieldl = (Node * ) llast (cref -> fields );
942+
943+ /* XXX for error reporting below */
944+ relname = strVal (field1 );
945+
946+ /*
947+ * Look it up as a subfieldable column reference, but then it
948+ * can't end with a star
949+ */
950+ if (!IsA (fieldl , A_Star ))
951+ nsitem2 = refnameNamespaceItem (pstate , NULL , strVal (field1 ),
952+ cref -> location ,
953+ & levels_up2 );
954+ else
955+ nsitem2 = NULL ;
956+
957+ /* must be an explicit alias */
958+ if (nsitem2 && nsitem2 -> p_rte -> alias == NULL )
959+ nsitem2 = NULL ;
960+
961+ if (nsitem2 )
962+ node2 = scanNSItemForColumn (pstate , nsitem2 , levels_up2 , strVal (field2 ), cref -> location );
963+
964+ /*
965+ * If we found a potential subfield reference, check that the
966+ * type is subfieldable, else forget it.
967+ */
968+ if (node2 )
969+ {
970+ if (type_is_subfieldable (castNode (Var , node2 )-> vartype ))
971+ {
972+ indcref = copyObject (cref );
973+ indcref -> fields = list_truncate (indcref -> fields , 2 );
974+ indirection = list_copy_tail (cref -> fields , 2 );
975+ }
976+ else
977+ {
978+ nsitem2 = NULL ;
979+ node2 = NULL ;
980+ }
981+ }
982+
983+ if (nsitem2 == NULL )
984+ {
985+ crerr = CRERR_NO_RTE ;
986+ break ;
987+ }
988+ break ;
989+ }
990+ }
991+
992+ /*
993+ * If we decided it's a subfield reference, convert it to an indirection
994+ * (as if you had written "(A.B).C.D" instead of "A.B.C.D"). (Note that
995+ * the subfield references detected above always come from an identifier
996+ * chain of length >= 3, but the indirections we are building here have a
997+ * column reference of length 2, and so there won't be any endless
998+ * recursion.)
999+ */
1000+ if (node2 )
1001+ {
1002+ A_Indirection * a = makeNode (A_Indirection );
1003+
1004+ a -> arg = (Node * ) indcref ;
1005+ a -> indirection = indirection ;
1006+ node = transformIndirection (pstate , a );
8401007 }
8411008
8421009 /*
@@ -877,17 +1044,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
8771044 errorMissingRTE (pstate , makeRangeVar (nspname , relname ,
8781045 cref -> location ));
8791046 break ;
880- case CRERR_WRONG_DB :
881- ereport (ERROR ,
882- (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
883- errmsg ("cross-database references are not implemented: %s" ,
884- NameListToString (cref -> fields )),
885- parser_errposition (pstate , cref -> location )));
886- break ;
887- case CRERR_TOO_MANY :
1047+ case CRERR_AMBIGUOUS :
8881048 ereport (ERROR ,
8891049 (errcode (ERRCODE_SYNTAX_ERROR ),
890- errmsg ("improper qualified name (too many dotted names) : %s" ,
1050+ errmsg ("ambiguous identifier chain : %s" ,
8911051 NameListToString (cref -> fields )),
8921052 parser_errposition (pstate , cref -> location )));
8931053 break ;
0 commit comments