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 | */ |
39 | int |
40 | PQregisterEventProc(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, ®evt, 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 | */ |
97 | int |
98 | PQsetInstanceData(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 | */ |
120 | void * |
121 | PQinstanceData(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 | */ |
141 | int |
142 | PQresultSetInstanceData(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 | */ |
164 | void * |
165 | PQresultInstanceData(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 | */ |
184 | int |
185 | PQfireResultCreateEvents(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 | |