1/*
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996-2009 Oracle.  All rights reserved.
5 *
6 */
7
8/*
9 * These are functions related to parsing and handling hint comments
10 * embedded in the input SQL DDL source.  Hint comments convey BDB
11 * configuration information that cannot be represented in SQL DDL.
12 */
13
14#include <ctype.h>
15#include "db_sql.h"
16
17static void
18hc_warn(char *fmt, ...)
19{
20	va_list ap;
21	va_start(ap, fmt);
22	fprintf(stderr, "Warning: ");
23	vfprintf(stderr, fmt, ap);
24	fprintf(stderr, ", near line %d\n", line_number);
25
26	va_end(ap);
27}
28
29/*
30 * Return a static copy of the given string, with the given length, in
31 * which all whitespace has been removed
32 */
33static char *
34static_copy_minus_whitespace(in, len)
35	const char *in;
36	int len;
37{
38#define smw_bufsiz 10240
39	static char out[smw_bufsiz];
40
41	int in_i;
42	int out_i;
43	int in_quote;
44
45	in_quote = 0;
46	for (in_i = out_i = 0; in_i < len && in[in_i] != '\0'; in_i++) {
47		if (in[in_i] == '"') {
48			if (in_quote) in_quote = 0;
49			else in_quote = 1;
50		}
51
52		if (in_quote || ! isspace(in[in_i])) {
53			out[out_i++] = in[in_i];
54			assert(out_i < smw_bufsiz);
55		}
56	}
57
58	out[out_i] = '\0';
59
60	return out;
61}
62
63/*
64 * Extract a string from the given token.  The returned copy is static
65 * and has had all whitespace removed.
66 */
67static char *
68hint_comment_from_token(t)
69	Token *t;
70{
71	int len;
72	char *p;
73
74	len = 0;
75	p = NULL;
76	if (t == NULL)
77		return NULL;
78
79	/* The token should be a whole comment; verify that */
80
81	if (t->z[0] == '/') {
82		assert(t->n >= 4 &&
83		       t->z[1] == '*' &&
84		       t->z[t->n - 2] == '*' &&
85		       t->z[t->n - 1] == '/');
86		p = ((char *)t->z) + 2;
87		len = t->n - 4;
88	} else if (t->z[0] == '-') {
89		assert(t->n >= 3 &&
90		       t->z[1] == '-');
91		p = ((char *)t->z) + 2;
92		len = t->n - 2;
93	}
94
95	assert(p != NULL);
96
97	if (*p != '+')              /* the hint comment indicator */
98		return NULL;
99
100	return static_copy_minus_whitespace(p+1, len-1);
101}
102
103/*
104 * Break a string into two parts at the delimiting char.  The left
105 * token is returned, while the right token, if any, is placed in
106 * *rest.  If found, the delimiting char in the input string is
107 * replaced with a null char, to terminate the left token string.
108 */
109static char *
110split(in, delimiter, rest)
111	char *in;
112	char delimiter;
113	char **rest;
114{
115	char *p;
116
117	*rest = NULL;
118
119	for (p = in; ! (*p == delimiter || *p == '\0'); p++)
120		;
121
122	if (*p != '\0') {
123		*rest = p + 1;
124		*p = '\0';
125	}
126
127	return in;
128}
129
130/*
131 * This is basically strtoul with multipliers for suffixes such as k,
132 * m, g for kilobytes, megabytes, and gigabytes
133 */
134static
135unsigned long int parse_integer(s)
136	char *s;
137{
138	unsigned long int x;
139	char *t;
140
141	x = strtoul(s, &t, 0);
142	if (s == t)
143		hc_warn("unparseable integer string %s", s);
144
145
146	switch(*t) {
147	case '\0':
148		break;
149	case 'k':
150	case 'K':
151		x = x * KILO;
152		t++;
153		break;
154	case 'm':
155	case 'M':
156		x = x * MEGA;
157		t++;
158		break;
159	case 'g':
160	case 'G':
161		x = x * GIGA;
162		t++;
163		break;
164	}
165
166	if (*t != '\0')
167		hc_warn("unrecognized characters in integer string %s", s);
168
169	return x;
170}
171
172static void
173apply_environment_property(key, value)
174	char *key;
175	char *value;
176{
177	if (strcasecmp(key, "CACHESIZE") == 0) {
178		the_schema.environment.cache_size = parse_integer(value);
179	} else {
180		hc_warn("Unrecognized environment property %s", key);
181	}
182}
183
184static void
185set_dbtype(entity, value)
186	ENTITY *entity;
187	char *value;
188{
189	if (strcasecmp(value, "btree") == 0) {
190		entity->dbtype = "DB_BTREE";
191	} else if (strcasecmp(value, "hash") == 0) {
192		entity->dbtype = "DB_HASH";
193	} else {
194		hc_warn(
195"unknown DBTYPE %s for antecedent %s, using default of DB_BTREE",
196			value, entity->name);
197		entity->dbtype = "DB_BTREE";
198	}
199}
200
201static void
202set_idx_dbtype(idx, value)
203	DB_INDEX *idx;
204	char *value;
205{
206	if (strcasecmp(value, "btree") == 0) {
207		idx->dbtype = "DB_BTREE";
208	} else if (strcasecmp(value, "hash") == 0) {
209		idx->dbtype = "DB_HASH";
210	} else {
211		hc_warn(
212"unknown DBTYPE %s for antecedent %s, using default of DB_BTREE",
213			value, idx->name);
214		idx->dbtype = "DB_BTREE";
215	}
216}
217
218static void
219apply_entity_property(key, value, entity)
220	char *key;
221	char *value;
222	ENTITY *entity;
223{
224	if (strcasecmp(key, "DBTYPE") == 0) {
225		set_dbtype(entity, value);
226	} else {
227		hc_warn("Unrecognized entity property %s", key);
228	}
229}
230
231static void
232apply_index_property(key, value, idx)
233	char *key;
234	char *value;
235	DB_INDEX *idx;
236{
237	if (strcasecmp(key, "DBTYPE") == 0) {
238		set_idx_dbtype(idx, value);
239	} else {
240		hc_warn("Unrecognized index property %s", key);
241	}
242}
243
244/*
245 * Apply a configuration keyword and parameter.
246 */
247
248static void
249apply_configuration_property(key, value)
250	char * key;
251	char *value;
252{
253	switch (the_parse_progress.last_event) {
254	case PE_NONE:
255		hc_warn(
256		      "Property setting (%s) with no antecedent SQL statement",
257		      key);
258		break;
259	case PE_ENVIRONMENT:
260		apply_environment_property(key, value);
261		break;
262	case PE_ENTITY:
263	case PE_ATTRIBUTE:  /* no per-attribute properties yet */
264		apply_entity_property(key, value,
265				      the_parse_progress.last_entity);
266		break;
267	case PE_INDEX:
268		apply_index_property(key, value, the_parse_progress.last_index);
269	}
270}
271
272/*
273 * Extract property assignments from a SQL comment, if it is marked
274 * as a hint comment.
275 */
276void parse_hint_comment(t)
277	Token *t;
278{
279	char *assignment, *key, *value;
280	char *comment;
281
282        comment = hint_comment_from_token(t);
283
284	if (comment == NULL)
285		return;
286
287	while (! (comment == NULL || *comment == '\0')) {
288		assignment = split(comment, ',', &comment);
289
290		/*
291		 * Split the assignment into key, value tokens on the
292		 * equals sign.  Verify that there is only one equals
293		 * sign.
294		 */
295		key = split(assignment, '=', &value);
296
297		if (value == NULL) {
298			hc_warn("No value specified for property %s\n",
299				key);
300			break;
301		}
302
303		apply_configuration_property(key, value);
304
305		key = split(key, '=', &value);
306		if (value != NULL)
307			hc_warn(
308		      "Warning: incorrect hint comment syntax with property %s",
309				key);
310	}
311}
312