1 /*-------------------------------------------------------------------------
2  *
3  * libpq-events.c
4  *	  functions for supporting the libpq "events" API
5  *
6  * Portions Copyright (c) 1996-2021, 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
PQregisterEventProc(PGconn * conn,PGEventProc proc,const char * name,void * passThrough)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, &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  */
97 int
PQsetInstanceData(PGconn * conn,PGEventProc proc,void * data)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 *
PQinstanceData(const PGconn * conn,PGEventProc proc)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
PQresultSetInstanceData(PGresult * result,PGEventProc proc,void * data)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 *
PQresultInstanceData(const PGresult * result,PGEventProc proc)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
PQfireResultCreateEvents(PGconn * conn,PGresult * res)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