1/*-------------------------------------------------------------------------
2 *
3 * libpq-events.c
4 * functions for supporting the libpq "events" API
5 *
6 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/interfaces/libpq/libpq-events.c
12 *
13 *-------------------------------------------------------------------------
14 */
15#include "postgres_fe.h"
16
17#include "libpq-fe.h"
18#include "libpq-int.h"
19
20
21/*
22 * Registers an event proc with the given PGconn.
23 *
24 * The same proc can't be registered more than once in a PGconn. This
25 * restriction is required because we use the proc address to identify
26 * the event for purposes such as PQinstanceData().
27 *
28 * The name argument is used within error messages to aid in debugging.
29 * A name must be supplied, but it needn't be unique. The string is
30 * copied, so the passed value needn't be long-lived.
31 *
32 * The passThrough argument is an application specific pointer and can be set
33 * to NULL if not required. It is passed through to the event proc whenever
34 * the event proc is called, and is not otherwise touched by libpq.
35 *
36 * The function returns a non-zero if successful. If the function fails,
37 * zero is returned.
38 */
39int
40PQregisterEventProc(PGconn *conn, PGEventProc proc,
41 const char *name, void *passThrough)
42{
43 int i;
44 PGEventRegister regevt;
45
46 if (!proc || !conn || !name || !*name)
47 return false; /* bad arguments */
48
49 for (i = 0; i < conn->nEvents; i++)
50 {
51 if (conn->events[i].proc == proc)
52 return false; /* already registered */
53 }
54
55 if (conn->nEvents >= conn->eventArraySize)
56 {
57 PGEvent *e;
58 int newSize;
59
60 newSize = conn->eventArraySize ? conn->eventArraySize * 2 : 8;
61 if (conn->events)
62 e = (PGEvent *) realloc(conn->events, newSize * sizeof(PGEvent));
63 else
64 e = (PGEvent *) malloc(newSize * sizeof(PGEvent));
65
66 if (!e)
67 return false;
68
69 conn->eventArraySize = newSize;
70 conn->events = e;
71 }
72
73 conn->events[conn->nEvents].proc = proc;
74 conn->events[conn->nEvents].name = strdup(name);
75 if (!conn->events[conn->nEvents].name)
76 return false;
77 conn->events[conn->nEvents].passThrough = passThrough;
78 conn->events[conn->nEvents].data = NULL;
79 conn->events[conn->nEvents].resultInitialized = false;
80 conn->nEvents++;
81
82 regevt.conn = conn;
83 if (!proc(PGEVT_REGISTER, &regevt, passThrough))
84 {
85 conn->nEvents--;
86 free(conn->events[conn->nEvents].name);
87 return false;
88 }
89
90 return true;
91}
92
93/*
94 * Set some "instance data" for an event within a PGconn.
95 * Returns nonzero on success, zero on failure.
96 */
97int
98PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data)
99{
100 int i;
101
102 if (!conn || !proc)
103 return false;
104
105 for (i = 0; i < conn->nEvents; i++)
106 {
107 if (conn->events[i].proc == proc)
108 {
109 conn->events[i].data = data;
110 return true;
111 }
112 }
113
114 return false;
115}
116
117/*
118 * Obtain the "instance data", if any, for the event.
119 */
120void *
121PQinstanceData(const PGconn *conn, PGEventProc proc)
122{
123 int i;
124
125 if (!conn || !proc)
126 return NULL;
127
128 for (i = 0; i < conn->nEvents; i++)
129 {
130 if (conn->events[i].proc == proc)
131 return conn->events[i].data;
132 }
133
134 return NULL;
135}
136
137/*
138 * Set some "instance data" for an event within a PGresult.
139 * Returns nonzero on success, zero on failure.
140 */
141int
142PQresultSetInstanceData(PGresult *result, PGEventProc proc, void *data)
143{
144 int i;
145
146 if (!result || !proc)
147 return false;
148
149 for (i = 0; i < result->nEvents; i++)
150 {
151 if (result->events[i].proc == proc)
152 {
153 result->events[i].data = data;
154 return true;
155 }
156 }
157
158 return false;
159}
160
161/*
162 * Obtain the "instance data", if any, for the event.
163 */
164void *
165PQresultInstanceData(const PGresult *result, PGEventProc proc)
166{
167 int i;
168
169 if (!result || !proc)
170 return NULL;
171
172 for (i = 0; i < result->nEvents; i++)
173 if (result->events[i].proc == proc)
174 return result->events[i].data;
175
176 return NULL;
177}
178
179/*
180 * Fire RESULTCREATE events for an application-created PGresult.
181 *
182 * The conn argument can be NULL if event procedures won't use it.
183 */
184int
185PQfireResultCreateEvents(PGconn *conn, PGresult *res)
186{
187 int i;
188
189 if (!res)
190 return false;
191
192 for (i = 0; i < res->nEvents; i++)
193 {
194 if (!res->events[i].resultInitialized)
195 {
196 PGEventResultCreate evt;
197
198 evt.conn = conn;
199 evt.result = res;
200 if (!res->events[i].proc(PGEVT_RESULTCREATE, &evt,
201 res->events[i].passThrough))
202 return false;
203
204 res->events[i].resultInitialized = true;
205 }
206 }
207
208 return true;
209}
210