pgtclId.c
上传用户:blenddy
上传日期:2007-01-07
资源大小:6495k
文件大小:19k
源码类别:

数据库系统

开发平台:

Unix_Linux

  1. /*-------------------------------------------------------------------------
  2.  *
  3.  * pgtclId.c
  4.  *
  5.  * Contains Tcl "channel" interface routines, plus useful routines
  6.  * to convert between strings and pointers.  These are needed because
  7.  * everything in Tcl is a string, but in C, pointers to data structures
  8.  * are needed.
  9.  *
  10.  * ASSUMPTION:  sizeof(long) >= sizeof(void*)
  11.  *
  12.  * Copyright (c) 1994, Regents of the University of California
  13.  *
  14.  * IDENTIFICATION
  15.  *   $Header: /usr/local/cvsroot/pgsql/src/interfaces/libpgtcl/pgtclId.c,v 1.20 1999/05/25 22:43:46 momjian Exp $
  16.  *
  17.  *-------------------------------------------------------------------------
  18.  */
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <errno.h>
  22. #include "postgres.h"
  23. #include "pgtclCmds.h"
  24. #include "pgtclId.h"
  25. static int
  26. PgEndCopy(Pg_ConnectionId *connid, int *errorCodePtr)
  27. {
  28. connid->res_copyStatus = RES_COPY_NONE;
  29. if (PQendcopy(connid->conn))
  30. {
  31. PQclear(connid->results[connid->res_copy]);
  32. connid->results[connid->res_copy] =
  33. PQmakeEmptyPGresult(connid->conn, PGRES_BAD_RESPONSE);
  34. connid->res_copy = -1;
  35. *errorCodePtr = EIO;
  36. return -1;
  37. }
  38. else
  39. {
  40. PQclear(connid->results[connid->res_copy]);
  41. connid->results[connid->res_copy] =
  42. PQmakeEmptyPGresult(connid->conn, PGRES_COMMAND_OK);
  43. connid->res_copy = -1;
  44. return 0;
  45. }
  46. }
  47. /*
  48.  * Called when reading data (via gets) for a copy <rel> to stdout.
  49.  */
  50. int
  51. PgInputProc(DRIVER_INPUT_PROTO)
  52. {
  53. Pg_ConnectionId *connid;
  54. PGconn    *conn;
  55. int avail;
  56. connid = (Pg_ConnectionId *) cData;
  57. conn = connid->conn;
  58. if (connid->res_copy < 0 ||
  59.  PQresultStatus(connid->results[connid->res_copy]) != PGRES_COPY_OUT)
  60. {
  61. *errorCodePtr = EBUSY;
  62. return -1;
  63. }
  64. /*
  65.  * Read any newly arrived data into libpq's buffer, thereby clearing
  66.  * the socket's read-ready condition.
  67.  */
  68. if (!PQconsumeInput(conn))
  69. {
  70. *errorCodePtr = EIO;
  71. return -1;
  72. }
  73. /* Move data from libpq's buffer to Tcl's. */
  74. avail = PQgetlineAsync(conn, buf, bufSize);
  75. if (avail < 0)
  76. {
  77. /* Endmarker detected, change state and return 0 */
  78. return PgEndCopy(connid, errorCodePtr);
  79. }
  80. return avail;
  81. }
  82. /*
  83.  * Called when writing data (via puts) for a copy <rel> from stdin
  84.  */
  85. int
  86. PgOutputProc(DRIVER_OUTPUT_PROTO)
  87. {
  88. Pg_ConnectionId *connid;
  89. PGconn    *conn;
  90. connid = (Pg_ConnectionId *) cData;
  91. conn = connid->conn;
  92. if (connid->res_copy < 0 ||
  93.   PQresultStatus(connid->results[connid->res_copy]) != PGRES_COPY_IN)
  94. {
  95. *errorCodePtr = EBUSY;
  96. return -1;
  97. }
  98. if (PQputnbytes(conn, buf, bufSize))
  99. {
  100. *errorCodePtr = EIO;
  101. return -1;
  102. }
  103. /*
  104.  * This assumes Tcl script will write the terminator line in a single
  105.  * operation; maybe not such a good assumption?
  106.  */
  107. if (bufSize >= 3 && strncmp(&buf[bufSize - 3], "\.n", 3) == 0)
  108. {
  109. if (PgEndCopy(connid, errorCodePtr) == -1)
  110. return -1;
  111. }
  112. return bufSize;
  113. }
  114. #if HAVE_TCL_GETFILEPROC
  115. Tcl_File
  116. PgGetFileProc(ClientData cData, int direction)
  117. {
  118. return (Tcl_File) NULL;
  119. }
  120. #endif
  121. Tcl_ChannelType Pg_ConnType = {
  122. "pgsql", /* channel type */
  123. NULL, /* blockmodeproc */
  124. PgDelConnectionId, /* closeproc */
  125. PgInputProc, /* inputproc */
  126. PgOutputProc, /* outputproc */
  127. /*
  128.  * Note the additional stuff can be left NULL, or is initialized
  129.  * during a PgSetConnectionId
  130.  */
  131. };
  132. /*
  133.  * Create and register a new channel for the connection
  134.  */
  135. void
  136. PgSetConnectionId(Tcl_Interp *interp, PGconn *conn)
  137. {
  138. Tcl_Channel conn_chan;
  139. Pg_ConnectionId *connid;
  140. int i;
  141. connid = (Pg_ConnectionId *) ckalloc(sizeof(Pg_ConnectionId));
  142. connid->conn = conn;
  143. connid->res_count = 0;
  144. connid->res_last = -1;
  145. connid->res_max = RES_START;
  146. connid->res_hardmax = RES_HARD_MAX;
  147. connid->res_copy = -1;
  148. connid->res_copyStatus = RES_COPY_NONE;
  149. connid->results = (PGresult **) ckalloc(sizeof(PGresult *) * RES_START);
  150. for (i = 0; i < RES_START; i++)
  151. connid->results[i] = NULL;
  152. connid->notify_list = NULL;
  153. connid->notifier_running = 0;
  154. connid->notifier_socket = -1;
  155. sprintf(connid->id, "pgsql%d", PQsocket(conn));
  156. #if TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION == 5
  157. /* Original signature (only seen in Tcl 7.5) */
  158. conn_chan = Tcl_CreateChannel(&Pg_ConnType, connid->id, NULL, NULL, (ClientData) connid);
  159. #else
  160. /* Tcl 7.6 and later use this */
  161. conn_chan = Tcl_CreateChannel(&Pg_ConnType, connid->id, (ClientData) connid,
  162.   TCL_READABLE | TCL_WRITABLE);
  163. #endif
  164. Tcl_SetChannelOption(interp, conn_chan, "-buffering", "line");
  165. Tcl_SetResult(interp, connid->id, TCL_VOLATILE);
  166. Tcl_RegisterChannel(interp, conn_chan);
  167. }
  168. /*
  169.  * Get back the connection from the Id
  170.  */
  171. PGconn *
  172. PgGetConnectionId(Tcl_Interp *interp, char *id, Pg_ConnectionId **connid_p)
  173. {
  174. Tcl_Channel conn_chan;
  175. Pg_ConnectionId *connid;
  176. conn_chan = Tcl_GetChannel(interp, id, 0);
  177. if (conn_chan == NULL || Tcl_GetChannelType(conn_chan) != &Pg_ConnType)
  178. {
  179. Tcl_ResetResult(interp);
  180. Tcl_AppendResult(interp, id, " is not a valid postgresql connection", 0);
  181. return (PGconn *) NULL;
  182. }
  183. connid = (Pg_ConnectionId *) Tcl_GetChannelInstanceData(conn_chan);
  184. if (connid_p)
  185. *connid_p = connid;
  186. return connid->conn;
  187. }
  188. /*
  189.  * Remove a connection Id from the hash table and
  190.  * close all portals the user forgot.
  191.  */
  192. int
  193. PgDelConnectionId(DRIVER_DEL_PROTO)
  194. {
  195. Tcl_HashEntry *entry;
  196. Tcl_HashSearch hsearch;
  197. Pg_ConnectionId *connid;
  198. Pg_TclNotifies *notifies;
  199. int i;
  200. connid = (Pg_ConnectionId *) cData;
  201. for (i = 0; i < connid->res_max; i++)
  202. {
  203. if (connid->results[i])
  204. PQclear(connid->results[i]);
  205. }
  206. ckfree((void *) connid->results);
  207. /* Release associated notify info */
  208. while ((notifies = connid->notify_list) != NULL)
  209. {
  210. connid->notify_list = notifies->next;
  211. for (entry = Tcl_FirstHashEntry(&notifies->notify_hash, &hsearch);
  212.  entry != NULL;
  213.  entry = Tcl_NextHashEntry(&hsearch))
  214. ckfree((char *) Tcl_GetHashValue(entry));
  215. Tcl_DeleteHashTable(&notifies->notify_hash);
  216. Tcl_DontCallWhenDeleted(notifies->interp, PgNotifyInterpDelete,
  217. (ClientData) notifies);
  218. ckfree((void *) notifies);
  219. }
  220. /*
  221.  * Turn off the Tcl event source for this connection, and delete any
  222.  * pending notify events.
  223.  */
  224. PgStopNotifyEventSource(connid);
  225. /* Close the libpq connection too */
  226. PQfinish(connid->conn);
  227. connid->conn = NULL;
  228. /*
  229.  * We must use Tcl_EventuallyFree because we don't want the connid
  230.  * struct to vanish instantly if Pg_Notify_EventProc is active for it.
  231.  * (Otherwise, closing the connection from inside a pg_listen callback
  232.  * could lead to coredump.)  Pg_Notify_EventProc can detect that the
  233.  * connection has been deleted from under it by checking connid->conn.
  234.  */
  235. Tcl_EventuallyFree((ClientData) connid, TCL_DYNAMIC);
  236. return 0;
  237. }
  238. /*
  239.  * Find a slot for a new result id.  If the table is full, expand it by
  240.  * a factor of 2.  However, do not expand past the hard max, as the client
  241.  * is probably just not clearing result handles like they should.
  242.  */
  243. int
  244. PgSetResultId(Tcl_Interp *interp, char *connid_c, PGresult *res)
  245. {
  246. Tcl_Channel conn_chan;
  247. Pg_ConnectionId *connid;
  248. int resid,
  249. i;
  250. char buf[32];
  251. conn_chan = Tcl_GetChannel(interp, connid_c, 0);
  252. if (conn_chan == NULL)
  253. return TCL_ERROR;
  254. connid = (Pg_ConnectionId *) Tcl_GetChannelInstanceData(conn_chan);
  255. for (resid = connid->res_last + 1; resid != connid->res_last; resid++)
  256. {
  257. if (resid == connid->res_max)
  258. resid = 0;
  259. if (!connid->results[resid])
  260. {
  261. connid->res_last = resid;
  262. break;
  263. }
  264. }
  265. if (connid->results[resid])
  266. {
  267. if (connid->res_max == connid->res_hardmax)
  268. {
  269. Tcl_SetResult(interp, "hard limit on result handles reached",
  270.   TCL_STATIC);
  271. return TCL_ERROR;
  272. }
  273. connid->res_last = connid->res_max;
  274. resid = connid->res_max;
  275. connid->res_max *= 2;
  276. if (connid->res_max > connid->res_hardmax)
  277. connid->res_max = connid->res_hardmax;
  278. connid->results = (PGresult **) ckrealloc((void *) connid->results,
  279.    sizeof(PGresult *) * connid->res_max);
  280. for (i = connid->res_last; i < connid->res_max; i++)
  281. connid->results[i] = NULL;
  282. }
  283. connid->results[resid] = res;
  284. sprintf(buf, "%s.%d", connid_c, resid);
  285. Tcl_SetResult(interp, buf, TCL_VOLATILE);
  286. return resid;
  287. }
  288. static int
  289. getresid(Tcl_Interp *interp, char *id, Pg_ConnectionId **connid_p)
  290. {
  291. Tcl_Channel conn_chan;
  292. char    *mark;
  293. int resid;
  294. Pg_ConnectionId *connid;
  295. if (!(mark = strchr(id, '.')))
  296. return -1;
  297. *mark = '';
  298. conn_chan = Tcl_GetChannel(interp, id, 0);
  299. *mark = '.';
  300. if (conn_chan == NULL || Tcl_GetChannelType(conn_chan) != &Pg_ConnType)
  301. {
  302. Tcl_SetResult(interp, "Invalid connection handle", TCL_STATIC);
  303. return -1;
  304. }
  305. if (Tcl_GetInt(interp, mark + 1, &resid) == TCL_ERROR)
  306. {
  307. Tcl_SetResult(interp, "Poorly formated result handle", TCL_STATIC);
  308. return -1;
  309. }
  310. connid = (Pg_ConnectionId *) Tcl_GetChannelInstanceData(conn_chan);
  311. if (resid < 0 || resid >= connid->res_max || connid->results[resid] == NULL)
  312. {
  313. Tcl_SetResult(interp, "Invalid result handle", TCL_STATIC);
  314. return -1;
  315. }
  316. *connid_p = connid;
  317. return resid;
  318. }
  319. /*
  320.  * Get back the result pointer from the Id
  321.  */
  322. PGresult   *
  323. PgGetResultId(Tcl_Interp *interp, char *id)
  324. {
  325. Pg_ConnectionId *connid;
  326. int resid;
  327. if (!id)
  328. return NULL;
  329. resid = getresid(interp, id, &connid);
  330. if (resid == -1)
  331. return NULL;
  332. return connid->results[resid];
  333. }
  334. /*
  335.  * Remove a result Id from the hash tables
  336.  */
  337. void
  338. PgDelResultId(Tcl_Interp *interp, char *id)
  339. {
  340. Pg_ConnectionId *connid;
  341. int resid;
  342. resid = getresid(interp, id, &connid);
  343. if (resid == -1)
  344. return;
  345. connid->results[resid] = 0;
  346. }
  347. /*
  348.  * Get the connection Id from the result Id
  349.  */
  350. int
  351. PgGetConnByResultId(Tcl_Interp *interp, char *resid_c)
  352. {
  353. char    *mark;
  354. Tcl_Channel conn_chan;
  355. if (!(mark = strchr(resid_c, '.')))
  356. goto error_out;
  357. *mark = '';
  358. conn_chan = Tcl_GetChannel(interp, resid_c, 0);
  359. *mark = '.';
  360. if (conn_chan && Tcl_GetChannelType(conn_chan) == &Pg_ConnType)
  361. {
  362. Tcl_SetResult(interp, Tcl_GetChannelName(conn_chan), TCL_VOLATILE);
  363. return TCL_OK;
  364. }
  365. error_out:
  366. Tcl_ResetResult(interp);
  367. Tcl_AppendResult(interp, resid_c, " is not a valid connectionn", 0);
  368. return TCL_ERROR;
  369. }
  370. /*-------------------------------------------
  371.   Notify event source
  372.   These functions allow asynchronous notify messages arriving from
  373.   the SQL server to be dispatched as Tcl events.  See the Tcl
  374.   Notifier(3) man page for more info.
  375.   The main trick in this code is that we have to cope with status changes
  376.   between the queueing and the execution of a Tcl event.  For example,
  377.   if the user changes or cancels the pg_listen callback command, we should
  378.   use the new setting; we do that by not resolving the notify relation
  379.   name until the last possible moment.
  380.   We also have to handle closure of the channel or deletion of the interpreter
  381.   to be used for the callback (note that with multiple interpreters,
  382.   the channel can outlive the interpreter it was created by!)
  383.   Upon closure of the channel, we immediately delete the file event handler
  384.   for it, which has the effect of disabling any file-ready events that might
  385.   be hanging about in the Tcl event queue. But for interpreter deletion,
  386.   we just set any matching interp pointers in the Pg_TclNotifies list to NULL.
  387.   The list item stays around until the connection is deleted.  (This avoids
  388.   trouble with walking through a list whose members may get deleted under us.)
  389.   Another headache is that Ousterhout keeps changing the Tcl I/O interfaces.
  390.   libpgtcl currently claims to work with Tcl 7.5, 7.6, and 8.0, and each of
  391.   'em is different.  Worse, the Tcl_File type went away in 8.0, which means
  392.   there is no longer any platform-independent way of waiting for file ready.
  393.   So we now have to use a Unix-specific interface. Grumble.
  394.   In the current design, Pg_Notify_FileHandler is a file handler that
  395.   we establish by calling Tcl_CreateFileHandler(). It gets invoked from
  396.   the Tcl event loop whenever the underlying PGconn's socket is read-ready.
  397.   We suck up any available data (to clear the OS-level read-ready condition)
  398.   and then transfer any available PGnotify events into the Tcl event queue.
  399.   Eventually these events will be dispatched to Pg_Notify_EventProc.  When
  400.   we do an ordinary PQexec, we must also transfer PGnotify events into Tcl's
  401.   event queue, since libpq might have read them when we weren't looking.
  402.   ------------------------------------------*/
  403. typedef struct
  404. {
  405. Tcl_Event header; /* Standard Tcl event info */
  406. PGnotify info; /* Notify name from SQL server */
  407. Pg_ConnectionId *connid; /* Connection for server */
  408. } NotifyEvent;
  409. /* Dispatch a NotifyEvent that has reached the front of the event queue */
  410. static int
  411. Pg_Notify_EventProc(Tcl_Event *evPtr, int flags)
  412. {
  413. NotifyEvent *event = (NotifyEvent *) evPtr;
  414. Pg_TclNotifies *notifies;
  415. Tcl_HashEntry *entry;
  416. char    *callback;
  417. char    *svcallback;
  418. /* We classify SQL notifies as Tcl file events. */
  419. if (!(flags & TCL_FILE_EVENTS))
  420. return 0;
  421. /* If connection's been closed, just forget the whole thing. */
  422. if (event->connid == NULL)
  423. return 1;
  424. /*
  425.  * Preserve/Release to ensure the connection struct doesn't disappear
  426.  * underneath us.
  427.  */
  428. Tcl_Preserve((ClientData) event->connid);
  429. /*
  430.  * Loop for each interpreter that has ever registered on the
  431.  * connection. Each one can get a callback.
  432.  */
  433. for (notifies = event->connid->notify_list;
  434.  notifies != NULL;
  435.  notifies = notifies->next)
  436. {
  437. Tcl_Interp *interp = notifies->interp;
  438. if (interp == NULL)
  439. continue; /* ignore deleted interpreter */
  440. /*
  441.  * Find the callback to be executed for this interpreter, if any.
  442.  */
  443. entry = Tcl_FindHashEntry(&notifies->notify_hash,
  444.   event->info.relname);
  445. if (entry == NULL)
  446. continue; /* no pg_listen in this interpreter */
  447. callback = (char *) Tcl_GetHashValue(entry);
  448. if (callback == NULL)
  449. continue; /* safety check -- shouldn't happen */
  450. /*
  451.  * We have to copy the callback string in case the user executes a
  452.  * new pg_listen during the callback.
  453.  */
  454. svcallback = (char *) ckalloc((unsigned) (strlen(callback) + 1));
  455. strcpy(svcallback, callback);
  456. /*
  457.  * Execute the callback.
  458.  */
  459. Tcl_Preserve((ClientData) interp);
  460. if (Tcl_GlobalEval(interp, svcallback) != TCL_OK)
  461. {
  462. Tcl_AddErrorInfo(interp, "n    ("pg_listen" script)");
  463. Tcl_BackgroundError(interp);
  464. }
  465. Tcl_Release((ClientData) interp);
  466. ckfree(svcallback);
  467. /*
  468.  * Check for the possibility that the callback closed the
  469.  * connection.
  470.  */
  471. if (event->connid->conn == NULL)
  472. break;
  473. }
  474. Tcl_Release((ClientData) event->connid);
  475. return 1;
  476. }
  477. /*
  478.  * Transfer any notify events available from libpq into the Tcl event queue.
  479.  * Note that this must be called after each PQexec (to capture notifies
  480.  * that arrive during command execution) as well as in Pg_Notify_FileHandler
  481.  * (to capture notifies that arrive when we're idle).
  482.  */
  483. void
  484. PgNotifyTransferEvents(Pg_ConnectionId *connid)
  485. {
  486. PGnotify   *notify;
  487. while ((notify = PQnotifies(connid->conn)) != NULL)
  488. {
  489. NotifyEvent *event = (NotifyEvent *) ckalloc(sizeof(NotifyEvent));
  490. event->header.proc = Pg_Notify_EventProc;
  491. event->info = *notify;
  492. event->connid = connid;
  493. Tcl_QueueEvent((Tcl_Event *) event, TCL_QUEUE_TAIL);
  494. free(notify);
  495. }
  496. /*
  497.  * This is also a good place to check for unexpected closure of the
  498.  * connection (ie, backend crash), in which case we must shut down the
  499.  * notify event source to keep Tcl from trying to select() on the now-
  500.  * closed socket descriptor.
  501.  */
  502. if (PQsocket(connid->conn) < 0)
  503. PgStopNotifyEventSource(connid);
  504. }
  505. /*
  506.  * Cleanup code for coping when an interpreter or a channel is deleted.
  507.  *
  508.  * PgNotifyInterpDelete is registered as an interpreter deletion callback
  509.  * for each extant Pg_TclNotifies structure.
  510.  * NotifyEventDeleteProc is used by PgStopNotifyEventSource to cancel
  511.  * pending Tcl NotifyEvents that reference a dying connection.
  512.  */
  513. void
  514. PgNotifyInterpDelete(ClientData clientData, Tcl_Interp *interp)
  515. {
  516. /* Mark the interpreter dead, but don't do anything else yet */
  517. Pg_TclNotifies *notifies = (Pg_TclNotifies *) clientData;
  518. notifies->interp = NULL;
  519. }
  520. /*
  521.  * Comparison routine for detecting events to be removed by Tcl_DeleteEvents.
  522.  * NB: In (at least) Tcl versions 7.6 through 8.0.3, there is a serious
  523.  * bug in Tcl_DeleteEvents: if there are multiple events on the queue and
  524.  * you tell it to delete the last one, the event list pointers get corrupted,
  525.  * with the result that events queued immediately thereafter get lost.
  526.  * Therefore we daren't tell Tcl_DeleteEvents to actually delete anything!
  527.  * We simply use it as a way of scanning the event queue.  Events matching
  528.  * the about-to-be-deleted connid are marked dead by setting their connid
  529.  * fields to NULL. Then Pg_Notify_EventProc will do nothing when those
  530.  * events are executed.
  531.  */
  532. static int
  533. NotifyEventDeleteProc(Tcl_Event *evPtr, ClientData clientData)
  534. {
  535. Pg_ConnectionId *connid = (Pg_ConnectionId *) clientData;
  536. if (evPtr->proc == Pg_Notify_EventProc)
  537. {
  538. NotifyEvent *event = (NotifyEvent *) evPtr;
  539. if (event->connid == connid)
  540. event->connid = NULL;
  541. }
  542. return 0;
  543. }
  544. /*
  545.  * File handler callback: called when Tcl has detected read-ready on socket.
  546.  * The clientData is a pointer to the associated connection.
  547.  * We can ignore the condition mask since we only ever ask about read-ready.
  548.  */
  549. static void
  550. Pg_Notify_FileHandler(ClientData clientData, int mask)
  551. {
  552. Pg_ConnectionId *connid = (Pg_ConnectionId *) clientData;
  553. /*
  554.  * Consume any data available from the SQL server (this just buffers
  555.  * it internally to libpq; but it will clear the read-ready
  556.  * condition).
  557.  */
  558. PQconsumeInput(connid->conn);
  559. /* Transfer notify events from libpq to Tcl event queue. */
  560. PgNotifyTransferEvents(connid);
  561. }
  562. /*
  563.  * Start and stop the notify event source for a connection.
  564.  *
  565.  * We do not bother to run the notifier unless at least one pg_listen
  566.  * has been executed on the connection.  Currently, once started the
  567.  * notifier is run until the connection is closed.
  568.  *
  569.  * FIXME: if PQreset is executed on the underlying PGconn, the active
  570.  * socket number could change. How and when should we test for this
  571.  * and update the Tcl file handler linkage?  (For that matter, we'd
  572.  * also have to reissue LISTEN commands for active LISTENs, since the
  573.  * new backend won't know about 'em.  I'm leaving this problem for
  574.  * another day.)
  575.  */
  576. void
  577. PgStartNotifyEventSource(Pg_ConnectionId *connid)
  578. {
  579. /* Start the notify event source if it isn't already running */
  580. if (!connid->notifier_running)
  581. {
  582. int pqsock = PQsocket(connid->conn);
  583. if (pqsock >= 0)
  584. {
  585. #if TCL_MAJOR_VERSION >= 8
  586. /* In Tcl 8, Tcl_CreateFileHandler takes a socket directly. */
  587. Tcl_CreateFileHandler(pqsock, TCL_READABLE,
  588.  Pg_Notify_FileHandler, (ClientData) connid);
  589. #else
  590. /* In Tcl 7.5 and 7.6, we need to gin up a Tcl_File. */
  591. Tcl_File tclfile = Tcl_GetFile((ClientData) pqsock, TCL_UNIX_FD);
  592. Tcl_CreateFileHandler(tclfile, TCL_READABLE,
  593.  Pg_Notify_FileHandler, (ClientData) connid);
  594. #endif
  595. connid->notifier_running = 1;
  596. connid->notifier_socket = pqsock;
  597. }
  598. }
  599. }
  600. void
  601. PgStopNotifyEventSource(Pg_ConnectionId *connid)
  602. {
  603. /* Remove the event source */
  604. if (connid->notifier_running)
  605. {
  606. #if TCL_MAJOR_VERSION >= 8
  607. /* In Tcl 8, Tcl_DeleteFileHandler takes a socket directly. */
  608. Tcl_DeleteFileHandler(connid->notifier_socket);
  609. #else
  610. /* In Tcl 7.5 and 7.6, we need to gin up a Tcl_File. */
  611. Tcl_File tclfile = Tcl_GetFile((ClientData) connid->notifier_socket,
  612.   TCL_UNIX_FD);
  613. Tcl_DeleteFileHandler(tclfile);
  614. #endif
  615. connid->notifier_running = 0;
  616. }
  617. /* Kill any queued Tcl events that reference this channel */
  618. Tcl_DeleteEvents(NotifyEventDeleteProc, (ClientData) connid);
  619. }