File: mod_sql.c

package info (click to toggle)
spl 1.0~pre2-1
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 2,240 kB
  • ctags: 1,987
  • sloc: ansic: 15,272; yacc: 3,167; sh: 272; makefile: 186; xml: 156
file content (231 lines) | stat: -rw-r--r-- 6,416 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/*
 *  SPL - The SPL Programming Language
 *  Copyright (C) 2004, 2005  Clifford Wolf <clifford@clifford.at>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *  mod_sql.c: Wrapper module for calling SQL modules
 */

/**
 * The base module for accessing SQL databases.
 *
 * Additional database driver modules need to be loaded for the backend
 * databases.
 */

#define _GNU_SOURCE

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "spl.h"
#include "compat.h"
#include "mod_sql.h"

extern void SPL_ABI(spl_mod_sql_init)(struct spl_vm *vm, struct spl_module *mod, int restore);
extern void SPL_ABI(spl_mod_sql_done)(struct spl_vm *vm, struct spl_module *mod);

/**
 * This function encodes a string to be used in an SQL query. I.e. the string
 * will be put in single quotes and every single quote in it will be replaced
 * with two single quotes.
 *
 * This function is designed to be used with the encoding/quoting operator (::).
 */
// builtin encode_sql(text)
static struct spl_node *handler_encode_sql(struct spl_task *task, void *data UNUSED)
{
	char *text = spl_clib_get_string(task);
	int i, j;

	for (i=0, j=1; text[i]; i++, j++)
		if (text[i] == '\'') j++;

	char *result = malloc(j+2);
	result[0] = '\'';

	for (i=0, j=1; text[i]; i++, j++) {
		if (text[i] == '\'') result[j++] = '\'';
		result[j] = text[i];
	}
	result[j++] = '\'';
	result[j] = 0;

	return SPL_NEW_STRING(result);
}

/* reconnect and check if this is a valid sql connection node */
static int sql_connect(struct spl_task *task, struct spl_node *node)
{
	struct sql_backend *b = task->vm->sql_backends;

	if (!node->hnode_name || strcmp(node->hnode_name, "sql")) {
		spl_clib_exception(task, "SqlEx", "description",
			SPL_NEW_SPL_STRING(spl_string_printf(0, 0, 0,
				"SQL multiplexer error (connect): "
				"This is not a database handle!\n")),
		NULL);
		return 1;
	}

	char *s = spl_get_string(node);
	int driver_len = strcspn(s, ":");

	char driver_name[driver_len+1];
	memcpy(driver_name, s, driver_len);
	driver_name[driver_len] = 0;

	char *driver_data = s+driver_len;
	if (*driver_data) driver_data++;

	while (b) {
		if (!strcmp(driver_name, b->name)) {
			if (!node->hnode_data)
				b->open_callback(task, node, driver_data);
			if (!node->hnode_data)
				return 1;
			return 0;
		}
		b = b->next;
	}

	spl_clib_exception(task, "SqlEx", "description",
		SPL_NEW_SPL_STRING(spl_string_printf(0, 0, 0,
			"SQL multiplexer error (connect): "
			"Can't find/init SQL backend driver '%s'!\n",
			driver_name)),
		NULL);
	return 1;
}


/**
 * This function creates an SQL database handle.
 *
 * The backend driver for the database must be loaded already when this
 * function is called. E.g.:
 *
 *	load "sql_sqlite";
 *	var db = sql_connect("sqlite", "");
 *
 * The database handler returned by this function may then be used as first
 * argument for the [[sql()]] function.
 *
 * An [[SqlEx]] exception is thrown on errors.
 */
// builtin sql_connect(driver_name, driver_data)
static struct spl_node *handler_sql_connect(struct spl_task *task, void *data UNUSED)
{
	char *driver_name = spl_clib_get_string(task);
	char *driver_data = spl_clib_get_string(task);
	char *module, *driver;

	struct spl_node *conn = spl_get(0);

	my_asprintf(&module, "sql_%s", driver_name);
	spl_module_load(task->vm, module, 0);
	free(module);

	conn->hnode_name = strdup("sql");

	my_asprintf(&driver, "%s:%s", driver_name, driver_data);
	spl_set_string(conn, driver);

	if (sql_connect(task, conn)) {
		spl_put(task->vm, conn);
		return 0;
	}

	return conn;
}


/**
 * This function executes an SQL query.
 *
 * If the query returns data, it is passed back using the return value of this
 * function.
 *
 * The database handler must be created using [[sql_connect()]].
 *
 * The return value is an array with an element for every database tuple in the
 * result set. Those elements then have a named children for each field in the
 * tuple, with the field name returned from the database as key. E.g.:
 *
 *	var db = sql_connect("sqlite", "/var/lib/myapp/database.db");
 *
 *	var r = sql(db, "SELECT username, userid FROM users");
 *
 *	foreach i (r)
 *		debug "User '${r[i].username}' has ID '${r[i].userid}'.";
 *
 * An [[SqlEx]] exception is thrown on errors.
 */
// builtin sql(database_handler, query)
static struct spl_node *handler_sql(struct spl_task *task, void *data UNUSED)
{
	struct spl_node *conn = spl_cleanup(task, spl_clib_get_node(task));
	char *query = spl_clib_get_string(task);

	/* reconnect and check if this is a valid sql connection */
	if (sql_connect(task, conn))
		return 0;

	struct sql_hnode_data *d = conn->hnode_data;

	return d->query_callback(task, d->backend_data, query);
}

/**
 * An instance of this object is thrown on database errors.
 */
// object SqlEx

/**
 * A description text describing the error.
 * Some backend drivers might add additional object members.
 */
// var description;

void handler_sqlnode(struct spl_task *task UNUSED, struct spl_vm *vm,
	struct spl_node *node, struct spl_hnode_args *args, void *data UNUSED)
{
	if (args->action == SPL_HNODE_ACTION_PUT && node->hnode_data) {
		struct sql_hnode_data *hnd = node->hnode_data;
		hnd->close_callback(vm, hnd->backend_data);
		node->hnode_data = 0;
		free(hnd);
	}
}

void SPL_ABI(spl_mod_sql_init)(struct spl_vm *vm, struct spl_module *mod, int restore)
{
	if (!restore)
		spl_eval(vm, 0, strdup(mod->name), "object SqlEx { }");

	spl_clib_reg(vm, "encode_sql", handler_encode_sql, 0);
	spl_clib_reg(vm, "sql_connect", handler_sql_connect, 0);
	spl_clib_reg(vm, "sql", handler_sql, 0);

	spl_hnode_reg(vm, "sql", handler_sqlnode, 0);
}

void SPL_ABI(spl_mod_sql_done)(struct spl_vm *vm UNUSED, struct spl_module *mod UNUSED)
{
	return;
}