Revert 29854ee8d1 due to buildfarm failures
authorAlexander Korotkov <akorotkov@postgresql.org>
Tue, 15 Jun 2021 18:43:17 +0000 (21:43 +0300)
committerAlexander Korotkov <akorotkov@postgresql.org>
Tue, 15 Jun 2021 18:44:40 +0000 (21:44 +0300)
Reported-by: Tom Lane
Discussion: https://postgr.es/m/CAPpHfdvcnw3x7jdV3r52p4%3D5S4WUxBCzcQKB3JukQHoicv1LSQ%40mail.gmail.com

doc/src/sgml/func.sgml
doc/src/sgml/rangetypes.sgml
src/backend/commands/typecmds.c
src/backend/utils/adt/multirangetypes.c
src/include/catalog/catversion.h
src/include/catalog/pg_cast.dat
src/include/catalog/pg_proc.dat
src/test/regress/expected/multirangetypes.out
src/test/regress/expected/opr_sanity.out
src/test/regress/sql/multirangetypes.sql
src/test/regress/sql/opr_sanity.sql

index 91b7674cbb30be86c71439f0ca5ca2d0678ab0d2..6388385edc56f5dea81fe33d5146657216ea910c 100644 (file)
@@ -19181,29 +19181,6 @@ SELECT NULLIF(value, '(none)') ...
         <returnvalue>{[1,2)}</returnvalue>
        </para></entry>
       </row>
-
-      <row>
-       <entry role="func_table_entry"><para role="func_signature">
-        <indexterm>
-         <primary>unnest</primary>
-         <secondary>for multirange</secondary>
-        </indexterm>
-        <function>unnest</function> ( <type>anymultirange</type> )
-        <returnvalue>setof anyrange</returnvalue>
-       </para>
-       <para>
-        Expands a multirange into a set of ranges.
-        The ranges are read out in storage order (ascending).
-       </para>
-       <para>
-        <literal>unnest('{[1,2), [3,4)}'::int4multirange)</literal>
-        <returnvalue></returnvalue>
-<programlisting>
- [1,2)
- [3,4)
-</programlisting>
-       </para></entry>
-      </row>
      </tbody>
     </tgroup>
    </table>
index 9ec5510e23a8cba147e4d0016797d0e339e588fa..92ea0e83dab7b4294e58c5e637ede51394db0710 100644 (file)
@@ -266,18 +266,6 @@ SELECT '[4,4)'::int4range;
 SELECT '{}'::int4multirange;
 SELECT '{[3,7)}'::int4multirange;
 SELECT '{[3,7), [8,9)}'::int4multirange;
-</programlisting>
-  </para>
-
-  <para>
-   A multirange can be cast to an array of ranges of the same type.
-  </para>
-
-  <para>
-  Examples:
-<programlisting>
-SELECT '{[3,7), [8,9)}'::int4multirange::int4range[];
-SELECT '{[1.0,14.0), [20.0,25.0)}'::nummultirange::numrange[];
 </programlisting>
   </para>
 
index 9ea0ce17e1223eeccf497ce96d495dda64ee89ed..58ec65c6afc10460cd17bfc89dd7f96b27b4cf7f 100644 (file)
@@ -114,11 +114,7 @@ static void makeRangeConstructors(const char *name, Oid namespace,
                                  Oid rangeOid, Oid subtype);
 static void makeMultirangeConstructors(const char *name, Oid namespace,
                                       Oid multirangeOid, Oid rangeOid,
-                                      Oid rangeArrayOid,
-                                      Oid *oneArgContructorOid);
-static void makeMultirangeCasts(const char *name, Oid namespace,
-                               Oid multirangeOid, Oid rangeOid,
-                               Oid rangeArrayOid, Oid singleArgContructorOid);
+                                      Oid rangeArrayOid, Oid *castFuncOid);
 static Oid findTypeInputFunction(List *procname, Oid typeOid);
 static Oid findTypeOutputFunction(List *procname, Oid typeOid);
 static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -1369,7 +1365,7 @@ DefineRange(CreateRangeStmt *stmt)
    ListCell   *lc;
    ObjectAddress address;
    ObjectAddress mltrngaddress PG_USED_FOR_ASSERTS_ONLY;
-   Oid         singleArgContructorOid;
+   Oid         castFuncOid;
 
    /* Convert list of names to a name and namespace */
    typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1721,12 +1717,10 @@ DefineRange(CreateRangeStmt *stmt)
    makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
    makeMultirangeConstructors(multirangeTypeName, typeNamespace,
                               multirangeOid, typoid, rangeArrayOid,
-                              &singleArgContructorOid);
+                              &castFuncOid);
 
-   /* Create casts for this multirange type */
-   makeMultirangeCasts(multirangeTypeName, typeNamespace,
-                       multirangeOid, typoid, rangeArrayOid,
-                       singleArgContructorOid);
+   /* Create cast from the range type to its multirange type */
+   CastCreate(typoid, multirangeOid, castFuncOid, 'e', 'f', DEPENDENCY_INTERNAL);
 
    pfree(multirangeTypeName);
    pfree(multirangeArrayName);
@@ -1814,13 +1808,13 @@ makeRangeConstructors(const char *name, Oid namespace,
  * If we had an anyrangearray polymorphic type we could use it here,
  * but since each type has its own constructor name there's no need.
  *
- * Sets oneArgContructorOid to the oid of the new constructor that can be used
+ * Sets castFuncOid to the oid of the new constructor that can be used
  * to cast from a range to a multirange.
  */
 static void
 makeMultirangeConstructors(const char *name, Oid namespace,
                           Oid multirangeOid, Oid rangeOid, Oid rangeArrayOid,
-                          Oid *oneArgContructorOid)
+                          Oid *castFuncOid)
 {
    ObjectAddress myself,
                referenced;
@@ -1910,7 +1904,7 @@ makeMultirangeConstructors(const char *name, Oid namespace,
    /* ditto */
    recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
    pfree(argtypes);
-   *oneArgContructorOid = myself.objectId;
+   *castFuncOid = myself.objectId;
 
    /* n-arg constructor - vararg */
    argtypes = buildoidvector(&rangeArrayOid, 1);
@@ -1955,76 +1949,6 @@ makeMultirangeConstructors(const char *name, Oid namespace,
    pfree(parameterModes);
 }
 
-/*
- * Create casts for the multirange type.  The first cast makes multirange from
- * range, and it's based on the single-argument constructor.  The second cast
- * makes an array of ranges from multirange.
- */
-static void
-makeMultirangeCasts(const char *name, Oid namespace,
-                   Oid multirangeOid, Oid rangeOid, Oid rangeArrayOid,
-                   Oid singleArgContructorOid)
-{
-   ObjectAddress myself,
-               referenced;
-   oidvector  *argtypes;
-
-   /*
-    * Create cast from range to multirange using the existing single-argument
-    * constructor procedure.
-    */
-   CastCreate(rangeOid, multirangeOid, singleArgContructorOid, 'e', 'f',
-              DEPENDENCY_INTERNAL);
-
-   referenced.classId = TypeRelationId;
-   referenced.objectId = multirangeOid;
-   referenced.objectSubId = 0;
-
-   /* multirange_to_array() function */
-   argtypes = buildoidvector(&multirangeOid, 1);
-   myself = ProcedureCreate("multirange_to_array", /* name */
-                            namespace,
-                            false, /* replace */
-                            false, /* returns set */
-                            rangeArrayOid, /* return type */
-                            BOOTSTRAP_SUPERUSERID, /* proowner */
-                            INTERNALlanguageId,    /* language */
-                            F_FMGR_INTERNAL_VALIDATOR,
-                            "multirange_to_array", /* prosrc */
-                            NULL,  /* probin */
-                            NULL,  /* prosqlbody */
-                            PROKIND_FUNCTION,
-                            false, /* security_definer */
-                            false, /* leakproof */
-                            true,  /* isStrict */
-                            PROVOLATILE_IMMUTABLE, /* volatility */
-                            PROPARALLEL_SAFE,  /* parallel safety */
-                            argtypes,  /* parameterTypes */
-                            PointerGetDatum(NULL), /* allParameterTypes */
-                            PointerGetDatum(NULL), /* parameterModes */
-                            PointerGetDatum(NULL), /* parameterNames */
-                            NIL,   /* parameterDefaults */
-                            PointerGetDatum(NULL), /* trftypes */
-                            PointerGetDatum(NULL), /* proconfig */
-                            InvalidOid,    /* prosupport */
-                            1.0,   /* procost */
-                            0.0);  /* prorows */
-
-   /*
-    * Make the multirange_to_array() function internally-dependent on the
-    * multirange type so that they go away silently when the type is dropped.
-    */
-   recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
-   pfree(argtypes);
-
-   /*
-    * Create cast from multirange to the array of ranges using
-    * multirange_to_array() function.
-    */
-   CastCreate(multirangeOid, rangeArrayOid, myself.objectId, 'e', 'f',
-              DEPENDENCY_INTERNAL);
-}
-
 /*
  * Find suitable I/O and other support functions for a type.
  *
index fbbda4af36d673f5cda0a7bd10e95ef81bf04cb9..fbcc27d0726c7190dfb46d9a03f6774aad7bf9eb 100644 (file)
@@ -34,7 +34,6 @@
 
 #include "access/tupmacs.h"
 #include "common/hashfn.h"
-#include "funcapi.h"
 #include "lib/stringinfo.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
@@ -1069,39 +1068,6 @@ multirange_constructor0(PG_FUNCTION_ARGS)
    PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
 }
 
-/*
- * Cast multirange to an array of ranges.
- */
-Datum
-multirange_to_array(PG_FUNCTION_ARGS)
-{
-   ArrayBuildState *astate = NULL;
-   MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
-   TypeCacheEntry *typcache;
-   int         i;
-
-   typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
-
-   astate = initArrayResult(typcache->rngtype->type_id,
-                            CurrentMemoryContext,
-                            false);
-
-   for (i = 0; i < mr->rangeCount; i++)
-   {
-       RangeType  *r;
-
-       r = multirange_get_range(typcache->rngtype, mr, i);
-       astate = accumArrayResult(astate,
-                                 RangeTypePGetDatum(r),
-                                 false,
-                                 typcache->rngtype->type_id,
-                                 CurrentMemoryContext);
-   }
-
-   PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
-}
-
-
 
 /* multirange, multirange -> multirange type functions */
 
@@ -2679,78 +2645,6 @@ range_merge_from_multirange(PG_FUNCTION_ARGS)
    PG_RETURN_RANGE_P(result);
 }
 
-/* Turn multirange into a set of ranges */
-Datum
-multirange_unnest(PG_FUNCTION_ARGS)
-{
-   typedef struct
-   {
-       MultirangeType *mr;
-       TypeCacheEntry *typcache;
-       int         index;
-   } multirange_unnest_fctx;
-
-   FuncCallContext *funcctx;
-   multirange_unnest_fctx *fctx;
-   MemoryContext oldcontext;
-
-   /* stuff done only on the first call of the function */
-   if (SRF_IS_FIRSTCALL())
-   {
-       MultirangeType *mr;
-
-       /* create a function context for cross-call persistence */
-       funcctx = SRF_FIRSTCALL_INIT();
-
-       /*
-        * switch to memory context appropriate for multiple function calls
-        */
-       oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
-
-       /*
-        * Get the multirange value and detoast if needed.  We can't do this
-        * earlier because if we have to detoast, we want the detoasted copy
-        * to be in multi_call_memory_ctx, so it will go away when we're done
-        * and not before.  (If no detoast happens, we assume the originally
-        * passed multirange will stick around till then.)
-        */
-       mr = PG_GETARG_MULTIRANGE_P(0);
-
-       /* allocate memory for user context */
-       fctx = (multirange_unnest_fctx *) palloc(sizeof(multirange_unnest_fctx));
-
-       /* initialize state */
-       fctx->mr = mr;
-       fctx->index = 0;
-       fctx->typcache = lookup_type_cache(MultirangeTypeGetOid(mr),
-                                          TYPECACHE_MULTIRANGE_INFO);
-
-       funcctx->user_fctx = fctx;
-       MemoryContextSwitchTo(oldcontext);
-   }
-
-   /* stuff done on every call of the function */
-   funcctx = SRF_PERCALL_SETUP();
-   fctx = funcctx->user_fctx;
-
-   if (fctx->index < fctx->mr->rangeCount)
-   {
-       RangeType  *range;
-
-       range = multirange_get_range(fctx->typcache->rngtype,
-                                    fctx->mr,
-                                    fctx->index);
-       fctx->index++;
-
-       SRF_RETURN_NEXT(funcctx, RangeTypePGetDatum(range));
-   }
-   else
-   {
-       /* do when there is no more left */
-       SRF_RETURN_DONE(funcctx);
-   }
-}
-
 /* Hash support */
 
 /* hash a multirange value */
index 7f483936dbcc76121b035b5e455bfde5ff90499d..1b23c7c253bcb11daf9bf2586995b5e89e5f8cca 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 202106152
+#define CATALOG_VERSION_NO 202106151
 
 #endif
index d0b37edb60571f94fe30bb68a832dcd1864e0a43..67f73cb6fb2b624a4931ea4df1642e1fdb5a9db6 100644 (file)
 { castsource => 'tstzrange', casttarget => 'tstzmultirange',
   castfunc => 'tstzmultirange(tstzrange)', castcontext => 'e',
   castmethod => 'f' },
-
-# multirange to array
-{ castsource => 'int4multirange', casttarget => '_int4range',
-  castfunc => 'multirange_to_array(int4multirange)', castcontext => 'e',
-  castmethod => 'f' }
-{ castsource => 'int8multirange', casttarget => '_int8range',
-  castfunc => 'multirange_to_array(int8multirange)', castcontext => 'e',
-  castmethod => 'f' }
-{ castsource => 'nummultirange', casttarget => '_numrange',
-  castfunc => 'multirange_to_array(nummultirange)', castcontext => 'e',
-  castmethod => 'f' }
-{ castsource => 'datemultirange', casttarget => '_daterange',
-  castfunc => 'multirange_to_array(datemultirange)', castcontext => 'e',
-  castmethod => 'f' }
-{ castsource => 'tsmultirange', casttarget => '_tsrange',
-  castfunc => 'multirange_to_array(tsmultirange)', castcontext => 'e',
-  castmethod => 'f' }
-{ castsource => 'tstzmultirange', casttarget => '_tstzrange',
-  castfunc => 'multirange_to_array(tstzmultirange)', castcontext => 'e',
-  castmethod => 'f' }
 ]
index 5af721ee3df2bb27bf0624546c5b0b28a5ab8b07..fde251fa4f3e1445686d74dd596950c90294a539 100644 (file)
   proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
   prorettype => 'anymultirange', proargtypes => 'anymultirange',
   prosrc => 'aggregate_dummy' },
-{ oid => '1293', descr => 'expand multirange to set of ranges',
-  proname => 'unnest', prorows => '100',
-  proretset => 't', prorettype => 'anyrange', proargtypes => 'anymultirange',
-  prosrc => 'multirange_unnest' },
-
-{ oid => '4544', descr => 'convert multirange to array of ranges',
-  proname => 'multirange_to_array', prorettype => '_int4range',
-  proargtypes => 'int4multirange', prosrc => 'multirange_to_array' },
-{ oid => '4545', descr => 'convert multirange to array of ranges',
-  proname => 'multirange_to_array', prorettype => '_int8range',
-  proargtypes => 'int8multirange', prosrc => 'multirange_to_array' },
-{ oid => '4546', descr => 'convert multirange to array of ranges',
-  proname => 'multirange_to_array', prorettype => '_numrange',
-  proargtypes => 'nummultirange', prosrc => 'multirange_to_array' },
-{ oid => '4547', descr => 'convert multirange to array of ranges',
-  proname => 'multirange_to_array', prorettype => '_daterange',
-  proargtypes => 'datemultirange', prosrc => 'multirange_to_array' },
-{ oid => '4548', descr => 'convert multirange to array of ranges',
-  proname => 'multirange_to_array', prorettype => '_tsrange',
-  proargtypes => 'tsmultirange', prosrc => 'multirange_to_array' },
-{ oid => '4549', descr => 'convert multirange to array of ranges',
-  proname => 'multirange_to_array', prorettype => '_tstzrange',
-  proargtypes => 'tstzmultirange', prosrc => 'multirange_to_array' },
 
 # date, time, timestamp constructors
 { oid => '3846', descr => 'construct date',
index 7ca2d3f30a97de92448d658de36bb8a21c3b30da..98ac592127b568328adb72b226eeca83667f37d3 100644 (file)
@@ -322,18 +322,6 @@ select int4range(null, null)::int4multirange;
  {(,)}
 (1 row)
 
-select 'empty'::int4range::int4multirange::int4range[];
- int4range 
------------
- {}
-(1 row)
-
-select int4multirange(int4range('5', '6'), int4range('1', '2'))::int4range[];
-  int4multirange   
--------------------
- {"[1,2)","[5,6)"}
-(1 row)
-
 select 'empty'::textrange::textmultirange;
  textmultirange 
 ----------------
@@ -358,35 +346,6 @@ select textrange(null, null)::textmultirange;
  {(,)}
 (1 row)
 
-select textmultirange(textrange('a', 'b'), textrange('d', 'e'))::textrange[];
-  textmultirange   
--------------------
- {"[a,b)","[d,e)"}
-(1 row)
-
-select 'empty'::textrange::textmultirange::textrange[];
- textrange 
------------
- {}
-(1 row)
-
---
--- test unnest(multirange) function
---
-select unnest(int4multirange(int4range('5', '6'), int4range('1', '2')));
- unnest 
---------
- [1,2)
- [5,6)
-(2 rows)
-
-select unnest(textmultirange(textrange('a', 'b'), textrange('d', 'e')));
- unnest 
---------
- [a,b)
- [d,e)
-(2 rows)
-
 --
 -- create some test data and test the operators
 --
@@ -2769,25 +2728,6 @@ LINE 1: select multirange_of_text(textrange2('a','Z'));
 HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 select multirange_of_text(textrange1('a','Z')) @> 'b'::text;
 ERROR:  range lower bound must be less than or equal to range upper bound
-select multirange_of_text(textrange1('a','b'), textrange1('d','e'))::textrange1[];
- multirange_of_text 
---------------------
- {"[a,b)","[d,e)"}
-(1 row)
-
-select multirange_of_text()::textrange1[];
- multirange_of_text 
---------------------
- {}
-(1 row)
-
-select unnest(multirange_of_text(textrange1('a','b'), textrange1('d','e')));
- unnest 
---------
- [a,b)
- [d,e)
-(2 rows)
-
 select _textrange1(textrange2('a','z')) @> 'b'::text;
  ?column? 
 ----------
index fbcd19340930b70809cf031af72def729c8c0601..562b586d8e0482150882f868bd8f56306b37a631 100644 (file)
@@ -151,8 +151,6 @@ WHERE p1.oid != p2.oid AND
     p2.prosrc NOT LIKE E'range\\_constructor_' AND
     p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
     p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
-    p1.prosrc != 'multirange_to_array' AND
-    p2.prosrc != 'multirange_to_array' AND
     (p1.prorettype < p2.prorettype)
 ORDER BY 1, 2;
          prorettype          |        prorettype        
@@ -173,8 +171,6 @@ WHERE p1.oid != p2.oid AND
     p2.prosrc NOT LIKE E'range\\_constructor_' AND
     p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
     p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
-    p1.prosrc != 'multirange_to_array' AND
-    p2.prosrc != 'multirange_to_array' AND
     (p1.proargtypes[0] < p2.proargtypes[0])
 ORDER BY 1, 2;
          proargtypes         |       proargtypes        
@@ -197,8 +193,6 @@ WHERE p1.oid != p2.oid AND
     p2.prosrc NOT LIKE E'range\\_constructor_' AND
     p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
     p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
-    p1.prosrc != 'multirange_to_array' AND
-    p2.prosrc != 'multirange_to_array' AND
     (p1.proargtypes[1] < p2.proargtypes[1])
 ORDER BY 1, 2;
          proargtypes         |       proargtypes        
index d636e8ca344fa1651e81ba0b622220f43fcbaca4..3cbebedcd4a11732cd51079433ec6a6f24bfc9b7 100644 (file)
@@ -72,20 +72,10 @@ select 'empty'::int4range::int4multirange;
 select int4range(1, 3)::int4multirange;
 select int4range(1, null)::int4multirange;
 select int4range(null, null)::int4multirange;
-select 'empty'::int4range::int4multirange::int4range[];
-select int4multirange(int4range('5', '6'), int4range('1', '2'))::int4range[];
 select 'empty'::textrange::textmultirange;
 select textrange('a', 'c')::textmultirange;
 select textrange('a', null)::textmultirange;
 select textrange(null, null)::textmultirange;
-select textmultirange(textrange('a', 'b'), textrange('d', 'e'))::textrange[];
-select 'empty'::textrange::textmultirange::textrange[];
-
---
--- test unnest(multirange) function
---
-select unnest(int4multirange(int4range('5', '6'), int4range('1', '2')));
-select unnest(textmultirange(textrange('a', 'b'), textrange('d', 'e')));
 
 --
 -- create some test data and test the operators
@@ -631,9 +621,6 @@ create type textrange2 as range(subtype=text, multirange_type_name=_textrange1,
 
 select multirange_of_text(textrange2('a','Z'));  -- should fail
 select multirange_of_text(textrange1('a','Z')) @> 'b'::text;
-select multirange_of_text(textrange1('a','b'), textrange1('d','e'))::textrange1[];
-select multirange_of_text()::textrange1[];
-select unnest(multirange_of_text(textrange1('a','b'), textrange1('d','e')));
 select _textrange1(textrange2('a','z')) @> 'b'::text;
 
 drop type textrange1;
index 19497625f9aa424549519eb32e4e4ea35b4d60ae..5a9c4796923d65af2d66fb7b5ea9530fa385f895 100644 (file)
@@ -127,8 +127,6 @@ WHERE p1.oid != p2.oid AND
     p2.prosrc NOT LIKE E'range\\_constructor_' AND
     p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
     p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
-    p1.prosrc != 'multirange_to_array' AND
-    p2.prosrc != 'multirange_to_array' AND
     (p1.prorettype < p2.prorettype)
 ORDER BY 1, 2;
 
@@ -142,8 +140,6 @@ WHERE p1.oid != p2.oid AND
     p2.prosrc NOT LIKE E'range\\_constructor_' AND
     p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
     p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
-    p1.prosrc != 'multirange_to_array' AND
-    p2.prosrc != 'multirange_to_array' AND
     (p1.proargtypes[0] < p2.proargtypes[0])
 ORDER BY 1, 2;
 
@@ -157,8 +153,6 @@ WHERE p1.oid != p2.oid AND
     p2.prosrc NOT LIKE E'range\\_constructor_' AND
     p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
     p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
-    p1.prosrc != 'multirange_to_array' AND
-    p2.prosrc != 'multirange_to_array' AND
     (p1.proargtypes[1] < p2.proargtypes[1])
 ORDER BY 1, 2;