I propose the appended patch for the todo item:

* Add sleep() function, remove from regress.c

The patch also removes do_sleep() from the regression and substitues this
with the call to the new function.

I'd personally prefer to call the function pg_sleep(), but since it is
called sleep() on the TODO list and in previous discussions, I kept the
name. The internal function is called pg_sleep() however.

There was a discussion about this item in August of last year

http://archives.postgresql.org/pgsql-hackers/2005-08/msg00633.php

A few people liked the idea, Tom Lane pointed out that it's bad to have a
sleeping backend that can hold locks for a long time. Other developers
answered that this should just be documented since the user can do that
anyways very easily if he is allowed to create functions in one of the
traditional scripting languages or with busy waiting also in plpgsql.

Finally the item got added to the TODO list and nobody objected.

In the mysqlcompat project there is a sleep function that does busy waiting,
this function could profit from a sleep() function in the backend.

To the docs I introduced a new paragraph such that it is easy to find this
function and labeled the note about the locks with the "warning" tag.


Tom, can you live with that?

Other Comments?


Joachim

diff -cr cvs/pgsql/doc/src/sgml/func.sgml cvs.build/pgsql/doc/src/sgml/func.sgml
*** cvs/pgsql/doc/src/sgml/func.sgml    2005-12-28 02:29:58.000000000 +0100
--- cvs.build/pgsql/doc/src/sgml/func.sgml      2006-01-10 00:40:05.000000000 
+0100
***************
*** 6170,6175 ****
--- 6170,6223 ----
       </para>
      </tip>
    </sect2>
+ 
+   <sect2 id="functions-datetime-delay">
+    <title>Delaying the execution with <function>sleep</function></title>
+ 
+    <indexterm>
+     <primary>sleep</primary>
+    </indexterm>
+    <indexterm>
+     <primary>delay</primary>
+    </indexterm>
+ 
+    <para>
+     The following function is available to delay the execution of the
+     backend:
+ <synopsis>
+ sleep (<replaceable>seconds</replaceable>)
+ </synopsis>
+ 
+     <function>sleep(<replaceable>seconds</replaceable>)</function> makes the
+     current backend sleep until <replaceable>seconds</replaceable> seconds 
have
+     elapsed. Note that the argument <replaceable>seconds</replaceable> is 
given
+     in the <type>numeric</type> type. Thus you can also sleep for fractions of
+     seconds.
+ 
+ <programlisting>
+ SELECT sleep(0.5);
+ </programlisting>
+    </para>
+ 
+    <note>
+      <para>
+       The exact time that the process sleeps depends on the operating system.
+       On a busy system the backend process might get suspended for a longer
+       time than specified, especially if the priority of the backend process 
is
+       low compared to other processes.
+      </para>
+    </note>
+    <warning>
+      <para>
+       Make sure that your backend does not hold more locks than necessary
+       before calling
+       <function>sleep(<replaceable>seconds</replaceable>)</function>, since
+       this could slow down your whole system. Other backends might have to 
wait
+       for locks your sleeping backend still holds.
+      </para>
+    </warning>
+   </sect2>
+ 
   </sect1>
  
    
diff -cr cvs/pgsql/src/backend/utils/adt/misc.c 
cvs.build/pgsql/src/backend/utils/adt/misc.c
*** cvs/pgsql/src/backend/utils/adt/misc.c      2005-10-15 04:49:29.000000000 
+0200
--- cvs.build/pgsql/src/backend/utils/adt/misc.c        2006-01-10 
00:34:36.000000000 +0100
***************
*** 28,33 ****
--- 28,34 ----
  #include "storage/pmsignal.h"
  #include "storage/procarray.h"
  #include "utils/builtins.h"
+ #include "utils/numeric.h"
  
  #define atooid(x)  ((Oid) strtoul((x), NULL, 10))
  
***************
*** 259,261 ****
--- 260,315 ----
        FreeDir(fctx->dirdesc);
        SRF_RETURN_DONE(funcctx);
  }
+ 
+ /*
+  * pg_sleep - delay for N seconds (N is numeric)
+  *
+  * This function gets exposed to the user as "sleep()"
+  *
+  * Note that pg_usleep() will abort when receiving a SIGHUP for example since
+  * it is implemented by means of select().
+  */
+ Datum
+ pg_sleep(PG_FUNCTION_ARGS)
+ {
+       Numeric         secs;
+       Numeric         usecs;
+       int64           to_sleep;
+ 
+       if (PG_ARGISNULL(0))
+               /* have to return NULL here to comply with the strictness 
property */
+               PG_RETURN_NULL();
+ 
+       secs = PG_GETARG_NUMERIC(0);
+ 
+       /* if we should sleep for example 2.5 seconds, we first calculate:
+        * 2.5 * 1,000,000 = 2,500,000
+        * Then we calculate the ceiling of this and transform it to an int64 
for
+        * easier calculations.
+        */
+       usecs = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
+                                                                               
Int64GetDatum(INT64CONST(1000000))));
+       usecs = DatumGetNumeric(DirectFunctionCall2(numeric_mul,
+                                                                               
                NumericGetDatum(secs),
+                                                                               
                NumericGetDatum(usecs)));
+       usecs = DatumGetNumeric(DirectFunctionCall1(numeric_ceil,
+                                                                               
                NumericGetDatum(usecs)));
+       to_sleep = DatumGetInt64(DirectFunctionCall1(numeric_int8,
+                                                                               
                NumericGetDatum(usecs)));
+ 
+       /* some sanity checks */
+       if (to_sleep < (int64) 0)
+               PG_RETURN_VOID();
+ 
+       if (to_sleep > (int64) LONG_MAX)
+       {
+               elog(WARNING, "Maximal delay for sleep() are %ld seconds on 
this machine. Will sleep for this time",
+                                         (LONG_MAX / 1000000L) - 1);
+               to_sleep = (int64) LONG_MAX;
+       }
+ 
+       pg_usleep((long) to_sleep);
+ 
+       PG_RETURN_VOID();
+ }
+ 
diff -cr cvs/pgsql/src/include/catalog/pg_proc.h 
cvs.build/pgsql/src/include/catalog/pg_proc.h
*** cvs/pgsql/src/include/catalog/pg_proc.h     2006-01-08 08:00:25.000000000 
+0100
--- cvs.build/pgsql/src/include/catalog/pg_proc.h       2006-01-10 
00:34:36.000000000 +0100
***************
*** 3025,3030 ****
--- 3025,3033 ----
  DESCR("Read text from a file");
  DATA(insert OID = 2625 ( pg_ls_dir                    PGNSP PGUID 12 f f t t 
v 1 25 "25" _null_ _null_ _null_ pg_ls_dir - _null_ ));
  DESCR("List all files in a directory");
+ DATA(insert OID = 2626 ( sleep                                PGNSP PGUID 12 
f f t f v 1 2278 "1700" _null_ _null_ _null_ pg_sleep - _null_ ));
+ DESCR("Sleeps for the specified time of seconds");
+ 
  
  
  /* Aggregates (moved here from pg_aggregate for 7.3) */
diff -cr cvs/pgsql/src/include/utils/builtins.h 
cvs.build/pgsql/src/include/utils/builtins.h
*** cvs/pgsql/src/include/utils/builtins.h      2006-01-08 08:00:26.000000000 
+0100
--- cvs.build/pgsql/src/include/utils/builtins.h        2006-01-10 
00:34:36.000000000 +0100
***************
*** 387,392 ****
--- 387,393 ----
  extern Datum pg_reload_conf(PG_FUNCTION_ARGS);
  extern Datum pg_tablespace_databases(PG_FUNCTION_ARGS);
  extern Datum pg_rotate_logfile(PG_FUNCTION_ARGS);
+ extern Datum pg_sleep(PG_FUNCTION_ARGS);
  
  /* not_in.c */
  extern Datum int4notin(PG_FUNCTION_ARGS);
diff -cr cvs/pgsql/src/test/regress/expected/stats.out 
cvs.build/pgsql/src/test/regress/expected/stats.out
*** cvs/pgsql/src/test/regress/expected/stats.out       2005-10-06 
04:29:22.000000000 +0200
--- cvs.build/pgsql/src/test/regress/expected/stats.out 2006-01-10 
00:34:36.000000000 +0100
***************
*** 36,44 ****
  (1 row)
  
  -- let stats collector catch up
! SELECT do_sleep(2);
!  do_sleep 
! ----------
   
  (1 row)
  
--- 36,44 ----
  (1 row)
  
  -- let stats collector catch up
! SELECT sleep(2.0);
!  sleep 
! -------
   
  (1 row)
  
diff -cr cvs/pgsql/src/test/regress/expected/time.out 
cvs.build/pgsql/src/test/regress/expected/time.out
*** cvs/pgsql/src/test/regress/expected/time.out        2003-11-01 
04:07:07.000000000 +0100
--- cvs.build/pgsql/src/test/regress/expected/time.out  2006-01-10 
00:34:36.000000000 +0100
***************
*** 71,73 ****
--- 71,100 ----
  SELECT f1 + time '00:01' AS "Illegal" FROM TIME_TBL;
  ERROR:  operator is not unique: time without time zone + time without time 
zone
  HINT:  Could not choose a best candidate operator. You may need to add 
explicit type casts.
+ -- Test sleep():
+ -- the problem with this function is that in theory the delay can be more than
+ -- 1 second if the system is busy and/or we run with a low priority... I think
+ -- that won't happen but to be sure just check if we have delayed for more 
than
+ -- 1 s after calling sleep(1.00)
+ CREATE OR REPLACE FUNCTION tmp_sleep_test() RETURNS int AS $$
+ DECLARE
+       starttime timestamptz;
+ BEGIN
+       starttime = timeofday()::timestamptz;
+       PERFORM sleep(0);
+       RAISE NOTICE '% seconds delayed (sleep(0))',
+               EXTRACT(SECONDS FROM date_trunc('second', 
timeofday()::timestamptz - starttime));
+       PERFORM sleep(1.00);
+       RAISE NOTICE 'seconds delayed (sleep(1.00)) more than 1: %',
+               EXTRACT(SECONDS FROM date_trunc('second', 
timeofday()::timestamptz - starttime)) >= 1;
+       RETURN 1;
+ END;
+ $$ LANGUAGE plpgsql;
+ SELECT tmp_sleep_test();
+ NOTICE:  0 seconds delayed (sleep(0))
+ NOTICE:  seconds delayed (sleep(1.00)) more than 1: t
+  tmp_sleep_test 
+ ----------------
+               1
+ (1 row)
+ 
diff -cr cvs/pgsql/src/test/regress/input/create_function_1.source 
cvs.build/pgsql/src/test/regress/input/create_function_1.source
*** cvs/pgsql/src/test/regress/input/create_function_1.source   2005-07-23 
16:18:56.000000000 +0200
--- cvs.build/pgsql/src/test/regress/input/create_function_1.source     
2006-01-10 00:34:36.000000000 +0100
***************
*** 52,62 ****
          AS '@abs_builddir@/[EMAIL PROTECTED]@'
          LANGUAGE 'C' STRICT;
  
- CREATE FUNCTION do_sleep (int4)
-         RETURNS void
-         AS '@abs_builddir@/[EMAIL PROTECTED]@'
-         LANGUAGE 'C' STRICT;
- 
  -- Things that shouldn't work:
  
  CREATE FUNCTION test1 (int) RETURNS int LANGUAGE sql
--- 52,57 ----
diff -cr cvs/pgsql/src/test/regress/output/create_function_1.source 
cvs.build/pgsql/src/test/regress/output/create_function_1.source
*** cvs/pgsql/src/test/regress/output/create_function_1.source  2005-07-23 
16:18:56.000000000 +0200
--- cvs.build/pgsql/src/test/regress/output/create_function_1.source    
2006-01-10 00:34:36.000000000 +0100
***************
*** 47,56 ****
          RETURNS int4
          AS '@abs_builddir@/[EMAIL PROTECTED]@'
          LANGUAGE 'C' STRICT;
- CREATE FUNCTION do_sleep (int4)
-         RETURNS void
-         AS '@abs_builddir@/[EMAIL PROTECTED]@'
-         LANGUAGE 'C' STRICT;
  -- Things that shouldn't work:
  CREATE FUNCTION test1 (int) RETURNS int LANGUAGE sql
      AS 'SELECT ''not an integer'';';
--- 47,52 ----
diff -cr cvs/pgsql/src/test/regress/regress.c 
cvs.build/pgsql/src/test/regress/regress.c
*** cvs/pgsql/src/test/regress/regress.c        2005-10-15 04:49:51.000000000 
+0200
--- cvs.build/pgsql/src/test/regress/regress.c  2006-01-10 00:34:36.000000000 
+0100
***************
*** 26,32 ****
  extern int    oldstyle_length(int n, text *t);
  extern Datum int44in(PG_FUNCTION_ARGS);
  extern Datum int44out(PG_FUNCTION_ARGS);
- extern Datum do_sleep(PG_FUNCTION_ARGS);
  
  
  /*
--- 26,31 ----
***************
*** 736,752 ****
        PG_RETURN_CSTRING(result);
  }
  
- /*
-  * do_sleep - delay for N seconds
-  */
- PG_FUNCTION_INFO_V1(do_sleep);
- 
- Datum
- do_sleep(PG_FUNCTION_ARGS)
- {
-       int32           secs = PG_GETARG_INT32(0);
- 
-       pg_usleep(secs * 1000000L);
- 
-       PG_RETURN_VOID();
- }
--- 735,737 ----
diff -cr cvs/pgsql/src/test/regress/sql/stats.sql 
cvs.build/pgsql/src/test/regress/sql/stats.sql
*** cvs/pgsql/src/test/regress/sql/stats.sql    2005-10-06 04:29:23.000000000 
+0200
--- cvs.build/pgsql/src/test/regress/sql/stats.sql      2006-01-10 
00:34:36.000000000 +0100
***************
*** 26,32 ****
  SELECT count(*) FROM tenk2 WHERE unique1 = 1;
  
  -- let stats collector catch up
! SELECT do_sleep(2);
  
  -- check effects
  SELECT st.seq_scan >= pr.seq_scan + 1,
--- 26,32 ----
  SELECT count(*) FROM tenk2 WHERE unique1 = 1;
  
  -- let stats collector catch up
! SELECT sleep(2.0);
  
  -- check effects
  SELECT st.seq_scan >= pr.seq_scan + 1,
diff -cr cvs/pgsql/src/test/regress/sql/time.sql 
cvs.build/pgsql/src/test/regress/sql/time.sql
*** cvs/pgsql/src/test/regress/sql/time.sql     2003-01-31 02:08:08.000000000 
+0100
--- cvs.build/pgsql/src/test/regress/sql/time.sql       2006-01-10 
00:34:36.000000000 +0100
***************
*** 34,36 ****
--- 34,62 ----
  -- where we do mixed-type arithmetic. - thomas 2000-12-02
  
  SELECT f1 + time '00:01' AS "Illegal" FROM TIME_TBL;
+ 
+ 
+ 
+ -- Test sleep():
+ -- the problem with this function is that in theory the delay can be more than
+ -- 1 second if the system is busy and/or we run with a low priority... I think
+ -- that won't happen but to be sure just check if we have delayed for more 
than
+ -- 1 s after calling sleep(1.00)
+ 
+ CREATE OR REPLACE FUNCTION tmp_sleep_test() RETURNS int AS $$
+ DECLARE
+       starttime timestamptz;
+ BEGIN
+       starttime = timeofday()::timestamptz;
+       PERFORM sleep(0);
+       RAISE NOTICE '% seconds delayed (sleep(0))',
+               EXTRACT(SECONDS FROM date_trunc('second', 
timeofday()::timestamptz - starttime));
+       PERFORM sleep(1.00);
+       RAISE NOTICE 'seconds delayed (sleep(1.00)) more than 1: %',
+               EXTRACT(SECONDS FROM date_trunc('second', 
timeofday()::timestamptz - starttime)) >= 1;
+       RETURN 1;
+ END;
+ $$ LANGUAGE plpgsql;
+ 
+ SELECT tmp_sleep_test();
+ 
---------------------------(end of broadcast)---------------------------
TIP 2: Don't 'kill -9' the postmaster

Reply via email to