1 /* src/interfaces/ecpg/ecpglib/prepare.c */
2 
3 #define POSTGRES_ECPG_INTERNAL
4 #include "postgres_fe.h"
5 
6 #include <ctype.h>
7 
8 #include "ecpgtype.h"
9 #include "ecpglib.h"
10 #include "ecpgerrno.h"
11 #include "extern.h"
12 #include "sqlca.h"
13 
14 #define STMTID_SIZE 32
15 
16 typedef struct
17 {
18 	int			lineno;
19 	char		stmtID[STMTID_SIZE];
20 	char	   *ecpgQuery;
21 	long		execs;			/* # of executions		*/
22 	const char *connection;		/* connection for the statement		*/
23 } stmtCacheEntry;
24 
25 static int	nextStmtID = 1;
26 static const int stmtCacheNBuckets = 2039;	/* # buckets - a prime # */
27 static const int stmtCacheEntPerBucket = 8; /* # entries/bucket		*/
28 static stmtCacheEntry stmtCacheEntries[16384] = {{0, {0}, 0, 0, 0}};
29 
30 static bool deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con,
31 			   struct prepared_statement *prev, struct prepared_statement *this);
32 
33 static bool
isvarchar(unsigned char c)34 isvarchar(unsigned char c)
35 {
36 	if (isalnum(c))
37 		return true;
38 
39 	if (c == '_' || c == '>' || c == '-' || c == '.')
40 		return true;
41 
42 	if (c >= 128)
43 		return true;
44 
45 	return false;
46 }
47 
48 static bool
replace_variables(char ** text,int lineno)49 replace_variables(char **text, int lineno)
50 {
51 	bool		string = false;
52 	int			counter = 1,
53 				ptr = 0;
54 
55 	for (; (*text)[ptr] != '\0'; ptr++)
56 	{
57 		if ((*text)[ptr] == '\'')
58 			string = string ? false : true;
59 
60 		if (string || (((*text)[ptr] != ':') && ((*text)[ptr] != '?')))
61 			continue;
62 
63 		if (((*text)[ptr] == ':') && ((*text)[ptr + 1] == ':'))
64 			ptr += 2;			/* skip  '::' */
65 		else
66 		{
67 			int			len;
68 			int			buffersize = sizeof(int) * CHAR_BIT * 10 / 3;	/* a rough guess of the
69 																		 * size we need */
70 			char	   *buffer,
71 					   *newcopy;
72 
73 			if (!(buffer = (char *) ecpg_alloc(buffersize, lineno)))
74 				return false;
75 
76 			snprintf(buffer, buffersize, "$%d", counter++);
77 
78 			for (len = 1; (*text)[ptr + len] && isvarchar((*text)[ptr + len]); len++);
79 			if (!(newcopy = (char *) ecpg_alloc(strlen(*text) -len + strlen(buffer) + 1, lineno)))
80 			{
81 				ecpg_free(buffer);
82 				return false;
83 			}
84 
85 			memcpy(newcopy, *text, ptr);
86 			strcpy(newcopy + ptr, buffer);
87 			strcat(newcopy, (*text) +ptr + len);
88 
89 			ecpg_free(*text);
90 			ecpg_free(buffer);
91 
92 			*text = newcopy;
93 
94 			if ((*text)[ptr] == '\0')	/* we reached the end */
95 				ptr--;			/* since we will (*text)[ptr]++ in the top
96 								 * level for loop */
97 		}
98 	}
99 	return true;
100 }
101 
102 static bool
prepare_common(int lineno,struct connection * con,const char * name,const char * variable)103 prepare_common(int lineno, struct connection *con, const char *name, const char *variable)
104 {
105 	struct statement *stmt;
106 	struct prepared_statement *this;
107 	PGresult   *query;
108 
109 	/* allocate new statement */
110 	this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno);
111 	if (!this)
112 		return false;
113 
114 	stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
115 	if (!stmt)
116 	{
117 		ecpg_free(this);
118 		return false;
119 	}
120 
121 	/* create statement */
122 	stmt->lineno = lineno;
123 	stmt->connection = con;
124 	stmt->command = ecpg_strdup(variable, lineno);
125 	stmt->inlist = stmt->outlist = NULL;
126 
127 	/* if we have C variables in our statement replace them with '?' */
128 	replace_variables(&(stmt->command), lineno);
129 
130 	/* add prepared statement to our list */
131 	this->name = ecpg_strdup(name, lineno);
132 	this->stmt = stmt;
133 
134 	/* and finally really prepare the statement */
135 	query = PQprepare(stmt->connection->connection, name, stmt->command, 0, NULL);
136 	if (!ecpg_check_PQresult(query, stmt->lineno, stmt->connection->connection, stmt->compat))
137 	{
138 		ecpg_free(stmt->command);
139 		ecpg_free(this->name);
140 		ecpg_free(this);
141 		ecpg_free(stmt);
142 		return false;
143 	}
144 
145 	ecpg_log("prepare_common on line %d: name %s; query: \"%s\"\n", stmt->lineno, name, stmt->command);
146 	PQclear(query);
147 	this->prepared = true;
148 
149 	if (con->prep_stmts == NULL)
150 		this->next = NULL;
151 	else
152 		this->next = con->prep_stmts;
153 
154 	con->prep_stmts = this;
155 	return true;
156 }
157 
158 /* handle the EXEC SQL PREPARE statement */
159 /* questionmarks is not needed but remains in there for the time being to not change the API */
160 bool
ECPGprepare(int lineno,const char * connection_name,const bool questionmarks,const char * name,const char * variable)161 ECPGprepare(int lineno, const char *connection_name, const bool questionmarks, const char *name, const char *variable)
162 {
163 	struct connection *con;
164 	struct prepared_statement *this,
165 			   *prev;
166 
167 	(void) questionmarks;		/* quiet the compiler */
168 	con = ecpg_get_connection(connection_name);
169 
170 	if (!ecpg_init(con, connection_name, lineno))
171 		return false;
172 
173 	/* check if we already have prepared this statement */
174 	this = ecpg_find_prepared_statement(name, con, &prev);
175 	if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
176 		return false;
177 
178 	return prepare_common(lineno, con, name, variable);
179 }
180 
181 struct prepared_statement *
ecpg_find_prepared_statement(const char * name,struct connection * con,struct prepared_statement ** prev_)182 ecpg_find_prepared_statement(const char *name,
183 							 struct connection *con, struct prepared_statement **prev_)
184 {
185 	struct prepared_statement *this,
186 			   *prev;
187 
188 	for (this = con->prep_stmts, prev = NULL; this != NULL; prev = this, this = this->next)
189 	{
190 		if (strcmp(this->name, name) == 0)
191 		{
192 			if (prev_)
193 				*prev_ = prev;
194 			return this;
195 		}
196 	}
197 	return NULL;
198 }
199 
200 static bool
deallocate_one(int lineno,enum COMPAT_MODE c,struct connection * con,struct prepared_statement * prev,struct prepared_statement * this)201 deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con, struct prepared_statement *prev, struct prepared_statement *this)
202 {
203 	bool		r = false;
204 
205 	ecpg_log("deallocate_one on line %d: name %s\n", lineno, this->name);
206 
207 	/* first deallocate the statement in the backend */
208 	if (this->prepared)
209 	{
210 		char	   *text;
211 		PGresult   *query;
212 
213 		text = (char *) ecpg_alloc(strlen("deallocate \"\" ") + strlen(this->name), this->stmt->lineno);
214 
215 		if (text)
216 		{
217 			sprintf(text, "deallocate \"%s\"", this->name);
218 			query = PQexec(this->stmt->connection->connection, text);
219 			ecpg_free(text);
220 			if (ecpg_check_PQresult(query, lineno, this->stmt->connection->connection, this->stmt->compat))
221 			{
222 				PQclear(query);
223 				r = true;
224 			}
225 		}
226 	}
227 
228 	/*
229 	 * Just ignore all errors since we do not know the list of cursors we are
230 	 * allowed to free. We have to trust the software.
231 	 */
232 	if (!r && !INFORMIX_MODE(c))
233 	{
234 		ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, this->name);
235 		return false;
236 	}
237 
238 	/* okay, free all the resources */
239 	ecpg_free(this->stmt->command);
240 	ecpg_free(this->stmt);
241 	ecpg_free(this->name);
242 	if (prev != NULL)
243 		prev->next = this->next;
244 	else
245 		con->prep_stmts = this->next;
246 
247 	ecpg_free(this);
248 	return true;
249 }
250 
251 /* handle the EXEC SQL DEALLOCATE PREPARE statement */
252 bool
ECPGdeallocate(int lineno,int c,const char * connection_name,const char * name)253 ECPGdeallocate(int lineno, int c, const char *connection_name, const char *name)
254 {
255 	struct connection *con;
256 	struct prepared_statement *this,
257 			   *prev;
258 
259 	con = ecpg_get_connection(connection_name);
260 
261 	if (!ecpg_init(con, connection_name, lineno))
262 		return false;
263 
264 	this = ecpg_find_prepared_statement(name, con, &prev);
265 	if (this)
266 		return deallocate_one(lineno, c, con, prev, this);
267 
268 	/* prepared statement is not found */
269 	if (INFORMIX_MODE(c))
270 		return true;
271 	ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, name);
272 	return false;
273 }
274 
275 bool
ecpg_deallocate_all_conn(int lineno,enum COMPAT_MODE c,struct connection * con)276 ecpg_deallocate_all_conn(int lineno, enum COMPAT_MODE c, struct connection *con)
277 {
278 	/* deallocate all prepared statements */
279 	while (con->prep_stmts)
280 	{
281 		if (!deallocate_one(lineno, c, con, NULL, con->prep_stmts))
282 			return false;
283 	}
284 
285 	return true;
286 }
287 
288 bool
ECPGdeallocate_all(int lineno,int compat,const char * connection_name)289 ECPGdeallocate_all(int lineno, int compat, const char *connection_name)
290 {
291 	return ecpg_deallocate_all_conn(lineno, compat, ecpg_get_connection(connection_name));
292 }
293 
294 char *
ecpg_prepared(const char * name,struct connection * con)295 ecpg_prepared(const char *name, struct connection *con)
296 {
297 	struct prepared_statement *this;
298 
299 	this = ecpg_find_prepared_statement(name, con, NULL);
300 	return this ? this->stmt->command : NULL;
301 }
302 
303 /* return the prepared statement */
304 /* lineno is not used here, but kept in to not break API */
305 char *
ECPGprepared_statement(const char * connection_name,const char * name,int lineno)306 ECPGprepared_statement(const char *connection_name, const char *name, int lineno)
307 {
308 	(void) lineno;				/* keep the compiler quiet */
309 	return ecpg_prepared(name, ecpg_get_connection(connection_name));
310 }
311 
312 /*
313  * hash a SQL statement -  returns entry # of first entry in the bucket
314  */
315 static int
HashStmt(const char * ecpgQuery)316 HashStmt(const char *ecpgQuery)
317 {
318 	int			stmtIx,
319 				bucketNo,
320 				hashLeng,
321 				stmtLeng;
322 	long long	hashVal,
323 				rotVal;
324 
325 	stmtLeng = strlen(ecpgQuery);
326 	hashLeng = 50;				/* use 1st 50 characters of statement		*/
327 	if (hashLeng > stmtLeng)	/* if the statement isn't that long         */
328 		hashLeng = stmtLeng;	/* use its actual length			   */
329 
330 	hashVal = 0;
331 	for (stmtIx = 0; stmtIx < hashLeng; ++stmtIx)
332 	{
333 		hashVal = hashVal + (int) ecpgQuery[stmtIx];
334 		hashVal = hashVal << 13;
335 		rotVal = (hashVal & 0x1fff00000000LL) >> 32;
336 		hashVal = (hashVal & 0xffffffffLL) | rotVal;
337 	}
338 
339 	bucketNo = hashVal % stmtCacheNBuckets;
340 	bucketNo += 1;				/* don't use bucket # 0         */
341 
342 	return (bucketNo * stmtCacheEntPerBucket);
343 }
344 
345 /*
346  * search the statement cache - search for entry with matching ECPG-format query
347  * Returns entry # in cache if found
348  *	 OR  zero if not present (zero'th entry isn't used)
349  */
350 static int
SearchStmtCache(const char * ecpgQuery)351 SearchStmtCache(const char *ecpgQuery)
352 {
353 	int			entNo,
354 				entIx;
355 
356 /* hash the statement			*/
357 	entNo = HashStmt(ecpgQuery);
358 
359 /* search the cache		*/
360 	for (entIx = 0; entIx < stmtCacheEntPerBucket; ++entIx)
361 	{
362 		if (stmtCacheEntries[entNo].stmtID[0])	/* check if entry is in use		*/
363 		{
364 			if (strcmp(ecpgQuery, stmtCacheEntries[entNo].ecpgQuery) == 0)
365 				break;			/* found it		*/
366 		}
367 		++entNo;				/* incr entry #		*/
368 	}
369 
370 /* if entry wasn't found - set entry # to zero  */
371 	if (entIx >= stmtCacheEntPerBucket)
372 		entNo = 0;
373 
374 	return entNo;
375 }
376 
377 /*
378  * free an entry in the statement cache
379  * Returns entry # in cache used
380  *	 OR  negative error code
381  */
382 static int
ecpg_freeStmtCacheEntry(int lineno,int compat,int entNo)383 ecpg_freeStmtCacheEntry(int lineno, int compat, int entNo)	/* entry # to free */
384 {
385 	stmtCacheEntry *entry;
386 	struct connection *con;
387 	struct prepared_statement *this,
388 			   *prev;
389 
390 	entry = &stmtCacheEntries[entNo];
391 	if (!entry->stmtID[0])		/* return if the entry isn't in use     */
392 		return 0;
393 
394 	con = ecpg_get_connection(entry->connection);
395 
396 	/* free the 'prepared_statement' list entry		  */
397 	this = ecpg_find_prepared_statement(entry->stmtID, con, &prev);
398 	if (this && !deallocate_one(lineno, compat, con, prev, this))
399 		return -1;
400 
401 	entry->stmtID[0] = '\0';
402 
403 	/* free the memory used by the cache entry		*/
404 	if (entry->ecpgQuery)
405 	{
406 		ecpg_free(entry->ecpgQuery);
407 		entry->ecpgQuery = 0;
408 	}
409 
410 	return entNo;
411 }
412 
413 /*
414  * add an entry to the statement cache
415  * returns entry # in cache used  OR  negative error code
416  */
417 static int
AddStmtToCache(int lineno,const char * stmtID,const char * connection,int compat,const char * ecpgQuery)418 AddStmtToCache(int lineno,		/* line # of statement		*/
419 			   const char *stmtID,	/* statement ID				*/
420 			   const char *connection,	/* connection				*/
421 			   int compat,		/* compatibility level */
422 			   const char *ecpgQuery)	/* query					*/
423 {
424 	int			ix,
425 				initEntNo,
426 				luEntNo,
427 				entNo;
428 	stmtCacheEntry *entry;
429 
430 /* hash the statement																	*/
431 	initEntNo = HashStmt(ecpgQuery);
432 
433 /* search for an unused entry															*/
434 	entNo = initEntNo;			/* start with the initial entry # for the
435 								 * bucket	 */
436 	luEntNo = initEntNo;		/* use it as the initial 'least used' entry			*/
437 	for (ix = 0; ix < stmtCacheEntPerBucket; ++ix)
438 	{
439 		entry = &stmtCacheEntries[entNo];
440 		if (!entry->stmtID[0])	/* unused entry  -	use it			*/
441 			break;
442 		if (entry->execs < stmtCacheEntries[luEntNo].execs)
443 			luEntNo = entNo;	/* save new 'least used' entry		*/
444 		++entNo;				/* increment entry #				*/
445 	}
446 
447 /* if no unused entries were found - use the 'least used' entry found in the bucket		*/
448 	if (ix >= stmtCacheEntPerBucket)	/* if no unused entries were found	*/
449 		entNo = luEntNo;		/* re-use the 'least used' entry	*/
450 
451 /* 'entNo' is the entry to use - make sure its free										*/
452 	if (ecpg_freeStmtCacheEntry(lineno, compat, entNo) < 0)
453 		return -1;
454 
455 /* add the query to the entry															*/
456 	entry = &stmtCacheEntries[entNo];
457 	entry->lineno = lineno;
458 	entry->ecpgQuery = ecpg_strdup(ecpgQuery, lineno);
459 	entry->connection = connection;
460 	entry->execs = 0;
461 	memcpy(entry->stmtID, stmtID, sizeof(entry->stmtID));
462 
463 	return entNo;
464 }
465 
466 /* handle cache and preparation of statements in auto-prepare mode */
467 bool
ecpg_auto_prepare(int lineno,const char * connection_name,const int compat,char ** name,const char * query)468 ecpg_auto_prepare(int lineno, const char *connection_name, const int compat, char **name, const char *query)
469 {
470 	int			entNo;
471 
472 	/* search the statement cache for this statement	*/
473 	entNo = SearchStmtCache(query);
474 
475 	/* if not found - add the statement to the cache	*/
476 	if (entNo)
477 	{
478 		char	   *stmtID;
479 		struct connection *con;
480 		struct prepared_statement *prep;
481 
482 		ecpg_log("ecpg_auto_prepare on line %d: statement found in cache; entry %d\n", lineno, entNo);
483 
484 		stmtID = stmtCacheEntries[entNo].stmtID;
485 
486 		con = ecpg_get_connection(connection_name);
487 		prep = ecpg_find_prepared_statement(stmtID, con, NULL);
488 		/* This prepared name doesn't exist on this connection. */
489 		if (!prep && !prepare_common(lineno, con, stmtID, query))
490 			return false;
491 
492 		*name = ecpg_strdup(stmtID, lineno);
493 	}
494 	else
495 	{
496 		char		stmtID[STMTID_SIZE];
497 
498 		ecpg_log("ecpg_auto_prepare on line %d: statement not in cache; inserting\n", lineno);
499 
500 		/* generate a statement ID */
501 		sprintf(stmtID, "ecpg%d", nextStmtID++);
502 
503 		if (!ECPGprepare(lineno, connection_name, 0, stmtID, query))
504 			return false;
505 		if (AddStmtToCache(lineno, stmtID, connection_name, compat, query) < 0)
506 			return false;
507 
508 		*name = ecpg_strdup(stmtID, lineno);
509 	}
510 
511 	/* increase usage counter */
512 	stmtCacheEntries[entNo].execs++;
513 
514 	return true;
515 }
516