1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
| create or replace package sys.dbms_alert is
------------ -- OVERVIEW -- -- This package provides support for the asynchronous (as opposed to -- polling) notification of database events. By appropriate use of -- this package and database triggers, an application can cause itself -- to be notified whenever values of interest in the database are -- changed. -- -- For example, suppose a graphics tool is displaying a graph of some -- data from a database table. The graphics tool can, after reading and -- graphing the data, wait on a database alert ('dbms_alert.waitone') -- covering the data just read. The tool will automatically wake up when -- the data is changed by any other user. All that is required is that a -- trigger be placed on the database table which then performs a signal -- ('dbms_alert.signal') whenever the trigger is fired. -- -- Alerts are transaction based. This means that the waiting session -- does not get alerted until the transaction signalling the alert commits. -- -- There can be any number of concurrent signallers of a given alert, and -- there can be any number of concurrent waiters on a given alert. -- -- A waiting application will be blocked in the database and cannot do -- any other work. -- -- Most of the calls in the package, except for 'signal', do commits. --
----------- -- EXAMPLE -- -- Suppose the application wishes to graph average salaries, say by -- department, for all employees. So the application needs to know -- whenever 'emp' is changed. The application would look like this: -- -- dbms_alert.register('emp_table_alert'); -- readagain: -- <read the emp table and graph it> -- dbms_alert.waitone('emp_table_alert', :message, :status); -- if status = 0 then goto readagain; else <error condition> -- -- The 'emp' table would have a trigger similar to the following: -- -- create trigger emptrig after insert or update or delete on emp -- begin -- dbms_alert.signal('emp_table_alert', 'message_text'); -- end; -- -- When the application is no longer interested in the alert, it does -- dbms_alert.remove('emp_table_alert'); -- This is important since it reduces the amount of work required by -- the alert signaller. -- -- If a session exits (or dies) while there exist registered alerts, -- they will eventually be cleaned up by future users of this package. -- -- The above example guarantees that the application will always see -- the latest data, although it may not see every intermediate value.
-------------- -- VARIATIONS -- -- The application can register for multiple events and can then wait for -- any of them to occur using the 'waitany' call. -- -- An application can also supply an optional 'timeout' parameter to the -- 'waitone' or 'waitany' calls. A 'timeout' of 0 returns immediately -- if there is no pending alert. -- -- The signalling session can optionally pass a message which will be -- received by the waiting session. -- -- Alerts may be signalled more often than the corresponding application -- 'wait' calls. In such cases the older alerts are discaded. The -- application always gets the latest alert (based on transaction commit -- times). -- -- If the application does not require transaction based alerts, then the -- 'dbms_pipe' package may provide a useful alternative -- -- If the transaction is rolled back after the call to 'dbms_alert.signal', -- no alert will occur. -- -- It is possible to receive an alert, read the data, and find that no -- data has changed. This is because the data changed after the *prior* -- alert, but before the data was read for that *prior* alert.
-------------------------- -- IMPLEMENTATION DETAILS -- -- In most cases the implementation is event-driven, i.e., there are no -- polling loops. There are two cases where polling loops can occur: -- -- 1) Parallel mode. If your database is running parallel mode then -- a polling loop is required to check for alerts from another -- instance. The polling loop defaults to one second and is settable -- by the 'set_defaults' call. -- 2) Waitany call. If you use the 'waitany' call, and a signalling -- session does a signal but does not commit within one second of the -- signal, then a polling loop is required so that this uncommitted -- alert does not camouflage other alerts. The polling loop begins -- at a one second interval and exponentially backs off to 30 second -- intervals. -- -- This package uses the dbms_lock package (for synchronization between -- signallers and waiters) and the dbms_pipe package (for asynchronous -- event dispatching).
------------------------------------------------------- -- INTERACTION WITH MULTI-THREADED AND PARALLEL SERVER -- -- When running with the parallel server AND multi-threaded server, a -- multi-threaded (dispatcher) "shared server" will be bound to a -- session (and therefore not shareable) during the time a session has -- any alerts "registered", OR from the time a session "signals" an -- alert until the time the session commits. Therefore, applications -- which register for alerts should use "dedicated servers" rather than -- connecting through the dispatcher (to a "shared server") since -- registration typically lasts for a long time, and applications which -- cause "signals" should have relatively short transactions so as not -- to tie up "shared servers" for too long.
------------ -- SECURITY -- -- Security on this package may be controlled by granting execute on -- this package to just those users or roles that you trust. You may -- wish to write a cover package on top of this one which restricts -- the alertnames used. Execute privilege on this cover package can -- then be granted rather than on this package.
------------- -- RESOURCES -- -- This package uses one database pipe and two locks for each alert a -- session has registered.
--------------------- -- SPECIAL CONSTANTS -- maxwait constant integer := 86400000; -- 1000 days -- The maximum time to wait for an alert (essentially forever).
---------------------------- -- PROCEDURES AND FUNCTIONS -- procedure set_defaults(sensitivity in number); -- Set various defaults for this package. -- Input parameters: -- sensitivity -- In case a polling loop is required (see "Implementation Details" -- above), this is the time to sleep between polls. Deafult is 5 sec. -- procedure register(name in varchar2, cleanup in boolean default TRUE); -- Register interest in an alert. A session may register interest in -- an unlimited number of alerts. Alerts should be de-registered when -- the session no longer has any interest (see 'remove'). This call -- always performs a 'commit'. -- Input parameters: -- name -- The name of the alert in which this session is interested. -- WARNING: Alert names beginning with 'ORA$' are reserved for use for -- products provided by Oracle Corporation. Name must be 30 bytes -- or less. The name is case-insensitive. -- cleanup -- This specifies whether we should perform cleanup of any orphaned -- pipes that may exist and are used by the dbms_alert package. This -- cleanup is only performed on the first call to "register" for each -- package instantiation. The default for the parameter is TRUE. -- procedure remove(name in varchar2); -- Remove alert from registration list. Do this when the session is no -- longer interested in an alert. Removing an alert is important -- since it will reduce the amount of work done by signalers of the alert. -- If a session dies without removing the alert, that alert will -- eventually (but not immediately) be cleaned up. This call always -- performs a commit. -- Input parameters: -- name -- The name of the alert to be removed from registration list. The -- name is case-insensitive. -- procedure removeall; -- Remove all alerts for this session from registration list. Do this -- when the session is no longer interested in any alerts. Removing -- alerts is important since it will reduce the amount of work done -- by signalers of the alert. If a session dies without removing all -- of its alerts, the alerts will eventually (but not immediately) -- be cleaned up. This call always performs a commit. -- -- This procedure is called automatically upon first reference to this -- package during a session. Therefore no alerts from prior sessions -- which may have terminated abnormally can affect this session. procedure waitany(name out varchar2, message out varchar2, status out integer, timeout in number default maxwait); -- Wait for an alert to occur for any of the alerts for which this -- session is registered. Although probably unusual, the same session -- that waits for the alert may also first signal the alert. In this -- case remember to commit after the signal and prior to the wait. -- Otherwise a lock request exception (status 4) will occur. This -- call always performs a commit. -- Input parameters: -- timeout -- The maximum time to wait for an alert. If no alert occurs before -- timeout seconds, then this call will return with status of 1. -- Output parameters: -- name -- The name of the alert that occurred, in uppercase. -- message -- The message associated with the alert. This is the message -- provided by the 'signal' call. Note that if multiple signals -- on this alert occurred before the waitany call, then the message -- will correspond to the most recent signal call. Messages from -- prior signal calls will be discarded. -- status -- 0 - alert occurred -- 1 - timeout occurred -- Errors raised: -- -20000, ORU-10024: there are no alerts registered. -- Cause: You must register an alert before waiting. -- procedure waitone(name in varchar2, message out varchar2, status out integer, timeout in number default maxwait); -- Wait for specified alert to occur. If the alert was signalled since -- the register or last waitone/waitany, then this call will return -- immediately. The same session that waits for the alert may also -- first signal the alert. In this case remember to commit after the -- signal and prior to the wait. Otherwise a lock request exception -- (status 4) will occur. This call always performs a commit. -- Input parameters: -- name -- The name of the alert to wait for. The name is case-insensitive. -- timeout -- The maximum time to wait for this alert. If no alert occurs before -- timeout seconds, then this call will return with status of 1. -- If the named alert has not been registered then the this call -- will return after the timeout period expires. -- Output parameters: -- message -- The message associated with the alert. This is the message -- provided by the 'signal' call. Note that if multiple signals -- on this alert occurred before the waitone call, then the message -- will correspond to the most recent signal call. Messages from -- prior signal calls will be discarded. The message may be up to -- 1800 bytes. -- status -- 0 - alert occurred -- 1 - timeout occurred -- procedure signal(name in varchar2, message in varchar2); -- Signal an alert. -- Input parameters: -- name -- Name of the alert to signal. The effect of the signal call only -- occurs when the transaction in which it is made commits. If the -- transaction rolls back, then the effect of the signal call is as -- if it had never occurred. All sessions that have registered -- interest in this alert will be notified. If the interested sessions -- are currently waiting, they will be awakened. If the interested -- sessions are not currently waiting, then they will be notified the -- next time they do a wait call. Multiple sessions may concurrently -- perform signals on the same alert. However the first session -- will block concurrent sessions until the first session commits. -- Name must be 30 bytes or less. It is case-insensitive. This call -- does not perform a commit. -- message -- Message to associate with this alert. This will be passed to -- the waiting session. The waiting session may be able to avoid -- reading the database after the alert occurs by using the -- information in this message. The message must be 1800 bytes or less.
end;
|