1c623b18f0
No existing binary packages are affected so I didn't bump the revision...
261 lines
7.6 KiB
Text
261 lines
7.6 KiB
Text
$NetBSD: patch-at,v 1.4 2008/10/16 09:40:20 martti Exp $
|
|
|
|
Support for sqlite.
|
|
|
|
--- src/global/dict_sqlite.c.orig 2008-10-17 00:40:21.000000000 +0200
|
|
+++ src/global/dict_sqlite.c
|
|
@@ -0,0 +1,254 @@
|
|
+/*++
|
|
+/* NAME
|
|
+/* dict_sqlite 3
|
|
+/* SUMMARY
|
|
+/* dictionary manager interface to SQLite3 databases
|
|
+/* SYNOPSIS
|
|
+/* #include <dict_sqlite.h>
|
|
+/*
|
|
+/* DICT *dict_sqlite_open(name, open_flags, dict_flags)
|
|
+/* const char *name;
|
|
+/* int open_flags;
|
|
+/* int dict_flags;
|
|
+/* DESCRIPTION
|
|
+/* dict_sqlite_open() creates a dictionary of type 'sqlite'. This
|
|
+/* dictionary is an interface for the postfix key->value mappings
|
|
+/* to SQLite. The result is a pointer to the installed dictionary,
|
|
+/* or a null pointer in case of problems.
|
|
+/* .PP
|
|
+/* Arguments:
|
|
+/* .IP name
|
|
+/* Either the path to the SQLite configuration file (if it starts
|
|
+/* with '/' or '.'), or the prefix which will be used to obtain
|
|
+/* main.cf configuration parameters for this search.
|
|
+/*
|
|
+/* In the first case, the configuration parameters below are
|
|
+/* specified in the file as \fIname\fR=\fBvalue\fR pairs.
|
|
+/*
|
|
+/* In the second case, the configuration parameters are
|
|
+/* prefixed with the value of \fIname\fR and an underscore,
|
|
+/* and they are specified in main.cf. For example, if this
|
|
+/* value is \fIsqlitecon\fR, the parameters would look like
|
|
+/* \fIsqlitecon_user\fR, \fIsqlitecon_table\fR, and so on.
|
|
+/*
|
|
+/* .IP open_flags
|
|
+/* Must be O_RDONLY.
|
|
+/* .IP dict_flags
|
|
+/* See dict_open(3).
|
|
+/* .PP
|
|
+/* Configuration parameters:
|
|
+/*
|
|
+/* The parameters encodes a number of pieces of information:
|
|
+/* dbpath, query, table, select_field and where_field:
|
|
+/* .IP \fIdbpath\fR
|
|
+/* Path to SQLite database
|
|
+/* .IP \fIquery\fR
|
|
+/* Query template, before the query is actually issued, variable
|
|
+/* substitutions are performed. See sqlite_table(5) for details. If
|
|
+/* No query is specified, the legacy variables \fItable\fR,
|
|
+/* \fIselect_field\fR, \fIwhere_field\fR and \fIadditional_conditions\fR
|
|
+/* are used to construct the query template.
|
|
+/* .IP \fIresult_format\fR
|
|
+/* The format used to expand results from queries. Substitutions
|
|
+/* are performed as described in sqlite_table(5). Defaults to returning
|
|
+/* the lookup result unchanged.
|
|
+/* .IP expansion_limit
|
|
+/* Limit (if any) on the total number of lookup result values. Lookups which
|
|
+/* exceed the limit fail with dict_errno=DICT_ERR_RETRY. Note that each
|
|
+/* non-empty (and non-NULL) column of a multi-column result row counts as
|
|
+/* one result.
|
|
+/*
|
|
+/* SEE ALSO
|
|
+/* dict(3) generic dictionary manager
|
|
+/* AUTHOR(S)
|
|
+/* Axel Steiner
|
|
+/* ast@treibsand.com
|
|
+/*--*/
|
|
+
|
|
+/* System library. */
|
|
+#include "sys_defs.h"
|
|
+
|
|
+#ifdef HAS_SQLITE
|
|
+#include <sqlite3.h>
|
|
+
|
|
+/* Utility library. */
|
|
+
|
|
+#include "msg.h"
|
|
+#include "dict.h"
|
|
+#include "vstring.h"
|
|
+#include "stringops.h"
|
|
+
|
|
+/* Global library. */
|
|
+
|
|
+#include "cfg_parser.h"
|
|
+#include "db_common.h"
|
|
+
|
|
+/* Application-specific. */
|
|
+
|
|
+#include "dict_sqlite.h"
|
|
+
|
|
+typedef struct {
|
|
+ DICT dict;
|
|
+ CFG_PARSER *parser;
|
|
+ sqlite3 *db;
|
|
+ char *dbpath;
|
|
+ char *query;
|
|
+ char *result_format;
|
|
+ int expansion_limit;
|
|
+ void *ctx;
|
|
+} DICT_SQLITE;
|
|
+
|
|
+typedef sqlite3_stmt *SQL;
|
|
+
|
|
+/* internal function declarations */
|
|
+
|
|
+static const char *dict_sqlite_lookup(DICT *, const char *);
|
|
+DICT *dict_sqlite_open(const char *, int, int);
|
|
+static void dict_sqlite_close(DICT *);
|
|
+static void sqlite_parse_config(DICT_SQLITE *, const char *);
|
|
+
|
|
+
|
|
+/* dict_sqlite_close - close the database */
|
|
+
|
|
+static void dict_sqlite_close(DICT *dict) {
|
|
+ const char *myname = "dict_sqlite_close";
|
|
+ DICT_SQLITE *dict_sqlite = (DICT_SQLITE *) dict;
|
|
+
|
|
+ if (msg_verbose)
|
|
+ msg_info("%s: dict_sqlite_close", myname);
|
|
+ if (sqlite3_close(dict_sqlite->db) != SQLITE_OK)
|
|
+ msg_fatal("%s: DB close failed", myname);
|
|
+ cfg_parser_free(dict_sqlite->parser);
|
|
+ if (dict->fold_buf)
|
|
+ vstring_free(dict->fold_buf);
|
|
+ dict_free(dict);
|
|
+}
|
|
+
|
|
+
|
|
+/* dict_sqlite_lookup - find database entry */
|
|
+
|
|
+static const char *dict_sqlite_lookup(DICT *dict, const char *name) {
|
|
+ const char *myname = "dict_sqlite_lookup";
|
|
+ DICT_SQLITE *dict_sqlite = (DICT_SQLITE *) dict;
|
|
+ SQL sql;
|
|
+ const char *zErrMsg;
|
|
+ static VSTRING *query;
|
|
+ static VSTRING *result;
|
|
+ const char *r;
|
|
+ int expansion = 0;
|
|
+
|
|
+ /*
|
|
+ * Optionally fold the key.
|
|
+ */
|
|
+ if (dict->fold_buf) {
|
|
+ vstring_strcpy(dict->fold_buf, name);
|
|
+ name = lowercase(vstring_str(dict->fold_buf));
|
|
+ }
|
|
+
|
|
+ if (db_common_check_domain(dict_sqlite->ctx, name) == 0) {
|
|
+ if (msg_verbose)
|
|
+ msg_info("%s: Skipping lookup of '%s'", myname, name);
|
|
+ return (0);
|
|
+ }
|
|
+
|
|
+#define INIT_VSTR(buf, len) do { \
|
|
+ if (buf == 0) \
|
|
+ buf = vstring_alloc(len); \
|
|
+ VSTRING_RESET(buf); \
|
|
+ VSTRING_TERMINATE(buf); \
|
|
+ } while (0)
|
|
+
|
|
+ INIT_VSTR(query, 10);
|
|
+
|
|
+ if (!db_common_expand(dict_sqlite->ctx, dict_sqlite->query,
|
|
+ name, 0, query, NULL))
|
|
+ return (0);
|
|
+
|
|
+ if (msg_verbose)
|
|
+ msg_info("%s: %s: Searching with query %s", myname,
|
|
+ dict_sqlite->parser->name, vstring_str(query));
|
|
+
|
|
+ if(sqlite3_prepare_v2(dict_sqlite->db,vstring_str(query),-1,&sql,&zErrMsg)!=SQLITE_OK) {
|
|
+ msg_fatal("%s: sql prepare %s\n",myname,sqlite3_errmsg(dict_sqlite->db));
|
|
+ }
|
|
+
|
|
+ INIT_VSTR(result, 10);
|
|
+ while (sqlite3_step(sql) == SQLITE_ROW ) {
|
|
+ if (db_common_expand(dict_sqlite->ctx, dict_sqlite->result_format,
|
|
+ sqlite3_column_text(sql, 0), name, result, 0)
|
|
+ && dict_sqlite->expansion_limit > 0
|
|
+ && ++expansion > dict_sqlite->expansion_limit) {
|
|
+ msg_warn("%s: %s: Expansion limit exceeded for key: '%s'",
|
|
+ myname, dict_sqlite->parser->name, name);
|
|
+ dict_errno = DICT_ERR_RETRY;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if(sqlite3_finalize(sql)){
|
|
+ msg_fatal("%s: sql finalize for %s; %s\n",myname,vstring_str(query),sqlite3_errmsg(dict_sqlite->db));
|
|
+ return(0);
|
|
+ }
|
|
+
|
|
+
|
|
+ r = vstring_str(result);
|
|
+ return ((dict_errno == 0 && *r) ? r : 0);
|
|
+}
|
|
+
|
|
+/* sqlite_parse_config - parse sqlite configuration file */
|
|
+
|
|
+static void sqlite_parse_config(DICT_SQLITE *dict_sqlite, const char *sqlitecf) {
|
|
+ CFG_PARSER *p;
|
|
+ VSTRING *buf;
|
|
+
|
|
+ p = dict_sqlite->parser = cfg_parser_alloc(sqlitecf);
|
|
+ dict_sqlite->dbpath = cfg_get_str(p, "dbpath", "", 1, 0);
|
|
+ dict_sqlite->result_format = cfg_get_str(p, "result_format", "%s", 1, 0);
|
|
+
|
|
+ if ((dict_sqlite->query = cfg_get_str(p, "query", NULL, 0, 0)) == 0) {
|
|
+ buf = vstring_alloc(64);
|
|
+ db_common_sql_build_query(buf, p);
|
|
+ dict_sqlite->query = vstring_export(buf);
|
|
+ }
|
|
+ dict_sqlite->expansion_limit = cfg_get_int(p,"expansion_limit", 0, 0, 0);
|
|
+ dict_sqlite->ctx = 0;
|
|
+
|
|
+ (void) db_common_parse(&dict_sqlite->dict, &dict_sqlite->ctx, dict_sqlite->query, 1);
|
|
+ (void) db_common_parse(0, &dict_sqlite->ctx, dict_sqlite->result_format, 0);
|
|
+
|
|
+ db_common_parse_domain(p, dict_sqlite->ctx);
|
|
+
|
|
+ if (db_common_dict_partial(dict_sqlite->ctx))
|
|
+ dict_sqlite->dict.flags |= DICT_FLAG_PATTERN;
|
|
+ else
|
|
+ dict_sqlite->dict.flags |= DICT_FLAG_FIXED;
|
|
+
|
|
+ if (dict_sqlite->dict.flags & DICT_FLAG_FOLD_FIX)
|
|
+ dict_sqlite->dict.fold_buf = vstring_alloc(10);
|
|
+}
|
|
+
|
|
+/* dict_sqlite_open - open sqlite database */
|
|
+
|
|
+DICT *dict_sqlite_open(const char *name, int open_flags, int dict_flags) {
|
|
+ DICT_SQLITE *dict_sqlite;
|
|
+
|
|
+ /*
|
|
+ * Sanity checks.
|
|
+ */
|
|
+ if (open_flags != O_RDONLY)
|
|
+ msg_fatal("%s:%s map requires O_RDONLY access mode", DICT_TYPE_SQLITE, name);
|
|
+
|
|
+ dict_sqlite = (DICT_SQLITE *) dict_alloc(DICT_TYPE_SQLITE, name, sizeof(DICT_SQLITE));
|
|
+ dict_sqlite->dict.lookup = dict_sqlite_lookup;
|
|
+ dict_sqlite->dict.close = dict_sqlite_close;
|
|
+ dict_sqlite->dict.flags = dict_flags;
|
|
+ sqlite_parse_config(dict_sqlite, name);
|
|
+
|
|
+ if (sqlite3_open(dict_sqlite->dbpath, &dict_sqlite->db)) {
|
|
+ msg_fatal("Can't open database: %s\n", sqlite3_errmsg(dict_sqlite->db));
|
|
+ sqlite3_close(dict_sqlite->db);
|
|
+ }
|
|
+
|
|
+ return (DICT_DEBUG (&dict_sqlite->dict));
|
|
+}
|
|
+#endif
|