Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F120824874
cyrusdb_sql.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
22 KB
Referenced Files
None
Subscribers
None
cyrusdb_sql.c
View Options
/* cyrusdb_sql: SQL db backends
*
* Copyright (c) 1998-2004 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any other legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: cyrusdb_sql.c,v 1.3 2010/01/06 17:01:45 murch Exp $ */
#include
<config.h>
#include
<syslog.h>
#include
<assert.h>
#include
<string.h>
#include
<errno.h>
#include
<stdlib.h>
#include
<unistd.h>
#include
"cyrusdb.h"
#include
"exitcodes.h"
#include
"libcyr_cfg.h"
#include
"xmalloc.h"
#include
"util.h"
extern
void
fatal
(
const
char
*
,
int
);
typedef
int
exec_cb
(
void
*
rock
,
const
char
*
key
,
size_t
keylen
,
const
char
*
data
,
size_t
datalen
);
typedef
struct
sql_engine
{
const
char
*
name
;
const
char
*
binary_type
;
void
*
(
*
sql_open
)(
char
*
host
,
char
*
port
,
int
usessl
,
const
char
*
user
,
const
char
*
password
,
const
char
*
database
);
char
*
(
*
sql_escape
)(
void
*
conn
,
char
**
to
,
const
char
*
from
,
size_t
fromlen
);
int
(
*
sql_begin_txn
)(
void
*
conn
);
int
(
*
sql_commit_txn
)(
void
*
conn
);
int
(
*
sql_rollback_txn
)(
void
*
conn
);
int
(
*
sql_exec
)(
void
*
conn
,
const
char
*
cmd
,
exec_cb
*
cb
,
void
*
rock
);
void
(
*
sql_close
)(
void
*
conn
);
}
sql_engine_t
;
struct
db
{
void
*
conn
;
/* connection to database */
char
*
table
;
/* table that we are operating on */
char
*
esc_key
;
/* allocated buffer for escaped key */
char
*
esc_data
;
/* allocated buffer for escaped data */
char
*
data
;
/* allocated buffer for fetched data */
};
struct
txn
{
char
*
lastkey
;
/* allocated buffer for last SELECTed key */
size_t
keylen
;
};
static
int
dbinit
=
0
;
static
const
sql_engine_t
*
dbengine
=
NULL
;
#ifdef HAVE_MYSQL
#include
<mysql.h>
static
void
*
_mysql_open
(
char
*
host
,
char
*
port
,
int
usessl
,
const
char
*
user
,
const
char
*
password
,
const
char
*
database
)
{
MYSQL
*
mysql
;
if
(
!
(
mysql
=
mysql_init
(
NULL
)))
{
syslog
(
LOG_ERR
,
"DBERROR: SQL backend could not execute mysql_init()"
);
return
NULL
;
}
return
mysql_real_connect
(
mysql
,
host
,
user
,
password
,
database
,
port
?
strtoul
(
port
,
NULL
,
10
)
:
0
,
NULL
,
usessl
?
CLIENT_SSL
:
0
);
}
static
char
*
_mysql_escape
(
void
*
conn
,
char
**
to
,
const
char
*
from
,
size_t
fromlen
)
{
size_t
tolen
;
*
to
=
xrealloc
(
*
to
,
2
*
fromlen
+
1
);
/* +1 for NUL */
tolen
=
mysql_real_escape_string
(
conn
,
*
to
,
from
,
fromlen
);
return
*
to
;
}
static
int
_mysql_exec
(
void
*
conn
,
const
char
*
cmd
,
exec_cb
*
cb
,
void
*
rock
)
{
MYSQL_RES
*
result
;
MYSQL_ROW
row
;
int
len
,
r
=
0
;
syslog
(
LOG_DEBUG
,
"executing SQL cmd: %s"
,
cmd
);
len
=
strlen
(
cmd
);
/* mysql_real_query() doesn't want a terminating ';' */
if
(
cmd
[
len
-1
]
==
';'
)
len
--
;
/* run the query */
if
((
mysql_real_query
(
conn
,
cmd
,
len
)
<
0
)
||
*
mysql_error
(
conn
))
{
syslog
(
LOG_ERR
,
"DBERROR: SQL query failed: %s"
,
mysql_error
(
conn
));
return
CYRUSDB_INTERNAL
;
}
/* see if we should expect some results */
if
(
!
mysql_field_count
(
conn
))
{
/* no results (BEGIN, COMMIT, ROLLBACK, CREATE, INSERT, UPDATE, DELETE) */
syslog
(
LOG_DEBUG
,
"no results from SQL cmd"
);
return
0
;
}
/* get the results */
result
=
mysql_store_result
(
conn
);
/* process the results */
while
(
!
r
&&
(
row
=
mysql_fetch_row
(
result
)))
{
unsigned
long
*
length
=
mysql_fetch_lengths
(
result
);
r
=
cb
(
rock
,
row
[
0
],
length
[
0
],
row
[
1
],
length
[
1
]);
}
/* free result */
mysql_free_result
(
result
);
return
r
;
}
static
int
_mysql_begin_txn
(
void
*
conn
)
{
return
_mysql_exec
(
conn
,
#if MYSQL_VERSION_ID >= 40011
"START TRANSACTION"
,
#else
"BEGIN"
,
#endif
NULL
,
NULL
);
}
static
int
_mysql_commit_txn
(
void
*
conn
)
{
return
_mysql_exec
(
conn
,
"COMMIT"
,
NULL
,
NULL
);
}
static
int
_mysql_rollback_txn
(
void
*
conn
)
{
return
_mysql_exec
(
conn
,
"ROLLBACK"
,
NULL
,
NULL
);
}
static
void
_mysql_close
(
void
*
conn
)
{
mysql_close
(
conn
);
}
#endif
/* HAVE_MYSQL */
#ifdef HAVE_PGSQL
#include
<libpq-fe.h>
#define sql_max(a, b) ((a) > (b) ? (a) : (b))
#define sql_len(input) ((input) ? strlen(input) : 0)
#define sql_exists(input) ((input) && (*input))
static
void
*
_pgsql_open
(
char
*
host
,
char
*
port
,
int
usessl
,
const
char
*
user
,
const
char
*
password
,
const
char
*
database
)
{
PGconn
*
conn
=
NULL
;
char
*
conninfo
,
*
p
;
/* create the connection info string */
/* The 64 represents the number of characters taken by
* the keyword tokens, plus a small pad
*/
p
=
conninfo
=
xzmalloc
(
64
+
sql_len
(
host
)
+
sql_len
(
port
)
+
sql_len
(
user
)
+
sql_len
(
password
)
+
sql_len
(
database
));
/* add each term that exists */
if
(
sql_exists
(
host
))
p
+=
sprintf
(
p
,
" host='%s'"
,
host
);
if
(
sql_exists
(
port
))
p
+=
sprintf
(
p
,
" port='%s'"
,
port
);
if
(
sql_exists
(
user
))
p
+=
sprintf
(
p
,
" user='%s'"
,
user
);
if
(
sql_exists
(
password
))
p
+=
sprintf
(
p
,
" password='%s'"
,
password
);
if
(
sql_exists
(
database
))
p
+=
sprintf
(
p
,
" dbname='%s'"
,
database
);
p
+=
sprintf
(
p
,
" requiressl='%d'"
,
usessl
);
conn
=
PQconnectdb
(
conninfo
);
free
(
conninfo
);
if
((
PQstatus
(
conn
)
!=
CONNECTION_OK
))
{
syslog
(
LOG_ERR
,
"DBERROR: SQL backend: %s"
,
PQerrorMessage
(
conn
));
return
NULL
;
}
return
conn
;
}
static
char
*
_pgsql_escape
(
void
*
conn
__attribute__
((
unused
)),
char
**
to
__attribute__
((
unused
)),
const
char
*
from
,
size_t
fromlen
)
{
size_t
tolen
;
/* returned buffer MUST be freed by caller */
return
(
char
*
)
PQescapeBytea
((
unsigned
char
*
)
from
,
fromlen
,
&
tolen
);
}
static
int
_pgsql_exec
(
void
*
conn
,
const
char
*
cmd
,
exec_cb
*
cb
,
void
*
rock
)
{
PGresult
*
result
;
int
row_count
,
i
,
r
=
0
;
ExecStatusType
status
;
syslog
(
LOG_DEBUG
,
"executing SQL cmd: %s"
,
cmd
);
/* run the query */
result
=
PQexec
(
conn
,
cmd
);
/* check the status */
status
=
PQresultStatus
(
result
);
if
(
status
==
PGRES_COMMAND_OK
)
{
/* no results (BEGIN, COMMIT, ROLLBACK, CREATE, INSERT, UPDATE, DELETE) */
PQclear
(
result
);
return
0
;
}
else
if
(
status
!=
PGRES_TUPLES_OK
)
{
/* error */
syslog
(
LOG_DEBUG
,
"SQL backend: %s "
,
PQresStatus
(
status
));
PQclear
(
result
);
return
CYRUSDB_INTERNAL
;
}
row_count
=
PQntuples
(
result
);
for
(
i
=
0
;
!
r
&&
i
<
row_count
;
i
++
)
{
char
*
key
,
*
data
;
size_t
keylen
,
datalen
;
key
=
(
char
*
)
PQunescapeBytea
((
unsigned
char
*
)
PQgetvalue
(
result
,
i
,
0
),
&
keylen
);
data
=
(
char
*
)
PQunescapeBytea
((
unsigned
char
*
)
PQgetvalue
(
result
,
i
,
1
),
&
datalen
);
r
=
cb
(
rock
,
key
,
keylen
,
data
,
datalen
);
free
(
key
);
free
(
data
);
}
/* free result */
PQclear
(
result
);
return
r
;
}
static
int
_pgsql_begin_txn
(
void
*
conn
)
{
return
_pgsql_exec
(
conn
,
"BEGIN;"
,
NULL
,
NULL
);
}
static
int
_pgsql_commit_txn
(
void
*
conn
)
{
return
_pgsql_exec
(
conn
,
"COMMIT;"
,
NULL
,
NULL
);
}
static
int
_pgsql_rollback_txn
(
void
*
conn
)
{
return
_pgsql_exec
(
conn
,
"ROLLBACK;"
,
NULL
,
NULL
);
}
static
void
_pgsql_close
(
void
*
conn
)
{
PQfinish
(
conn
);
}
#endif
/* HAVE_PGSQL */
#ifdef HAVE_SQLITE
#include
<sqlite3.h>
static
void
*
_sqlite_open
(
char
*
host
__attribute__
((
unused
)),
char
*
port
__attribute__
((
unused
)),
int
usessl
__attribute__
((
unused
)),
const
char
*
user
__attribute__
((
unused
)),
const
char
*
password
__attribute__
((
unused
)),
const
char
*
database
)
{
int
rc
;
sqlite3
*
db
;
rc
=
sqlite3_open
(
database
,
&
db
);
if
(
rc
!=
SQLITE_OK
)
{
syslog
(
LOG_ERR
,
"DBERROR: SQL backend: %s"
,
sqlite3_errmsg
(
db
));
sqlite3_close
(
db
);
}
return
db
;
}
static
char
*
_sqlite_escape
(
void
*
conn
__attribute__
((
unused
)),
char
**
to
,
const
char
*
from
,
size_t
fromlen
)
{
size_t
tolen
;
#if 0
*to = xrealloc(*to, 2 + (257 * fromlen) / 254);
tolen = sqlite3_encode_binary(from, fromlen, *to);
#else
*
to
=
xrealloc
(
*
to
,
fromlen
+
1
);
memcpy
(
*
to
,
from
,
fromlen
);
tolen
=
fromlen
;
(
*
to
)[
tolen
]
=
'\0'
;
#endif
return
*
to
;
}
static
int
_sqlite_exec
(
void
*
conn
,
const
char
*
cmd
,
exec_cb
*
cb
,
void
*
rock
)
{
int
rc
,
r
=
0
;
sqlite3_stmt
*
stmt
=
NULL
;
const
char
*
tail
;
syslog
(
LOG_DEBUG
,
"executing SQL cmd: %s"
,
cmd
);
/* compile the SQL cmd */
rc
=
sqlite3_prepare
(
conn
,
cmd
,
strlen
(
cmd
),
&
stmt
,
&
tail
);
if
(
rc
!=
SQLITE_OK
)
{
syslog
(
LOG_DEBUG
,
"SQL backend: %s "
,
sqlite3_errmsg
(
conn
));
return
CYRUSDB_INTERNAL
;
}
/* process the results */
while
(
!
r
&&
(
rc
=
sqlite3_step
(
stmt
))
==
SQLITE_ROW
)
{
const
unsigned
char
*
key
=
sqlite3_column_text
(
stmt
,
0
);
int
keylen
=
sqlite3_column_bytes
(
stmt
,
0
);
const
unsigned
char
*
data
=
sqlite3_column_text
(
stmt
,
1
);
int
datalen
=
sqlite3_column_bytes
(
stmt
,
1
);
r
=
cb
(
rock
,
(
char
*
)
key
,
keylen
,
(
char
*
)
data
,
datalen
);
}
/* cleanup */
rc
=
sqlite3_finalize
(
stmt
);
if
(
rc
!=
SQLITE_OK
)
{
syslog
(
LOG_DEBUG
,
"SQL backend: %s "
,
sqlite3_errmsg
(
conn
));
return
CYRUSDB_INTERNAL
;
}
return
r
;
}
static
int
_sqlite_begin_txn
(
void
*
conn
)
{
return
_sqlite_exec
(
conn
,
"BEGIN TRANSACTION"
,
NULL
,
NULL
);
}
static
int
_sqlite_commit_txn
(
void
*
conn
)
{
return
_sqlite_exec
(
conn
,
"COMMIT TRANSACTION"
,
NULL
,
NULL
);
}
static
int
_sqlite_rollback_txn
(
void
*
conn
)
{
return
_sqlite_exec
(
conn
,
"ROLLBACK TRANSACTION"
,
NULL
,
NULL
);
}
static
void
_sqlite_close
(
void
*
conn
)
{
sqlite3_close
(
conn
);
}
#endif
/* HAVE_SQLITE */
static
const
sql_engine_t
sql_engines
[]
=
{
#ifdef HAVE_MYSQL
{
"mysql"
,
"BLOB"
,
&
_mysql_open
,
&
_mysql_escape
,
&
_mysql_begin_txn
,
&
_mysql_commit_txn
,
&
_mysql_rollback_txn
,
&
_mysql_exec
,
&
_mysql_close
},
#endif
/* HAVE_MYSQL */
#ifdef HAVE_PGSQL
{
"pgsql"
,
"BYTEA"
,
&
_pgsql_open
,
&
_pgsql_escape
,
&
_pgsql_begin_txn
,
&
_pgsql_commit_txn
,
&
_pgsql_rollback_txn
,
&
_pgsql_exec
,
&
_pgsql_close
},
#endif
#ifdef HAVE_SQLITE
{
"sqlite"
,
"BLOB"
,
&
_sqlite_open
,
&
_sqlite_escape
,
&
_sqlite_begin_txn
,
&
_sqlite_commit_txn
,
&
_sqlite_rollback_txn
,
&
_sqlite_exec
,
&
_sqlite_close
},
#endif
{
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
}
};
static
int
init
(
const
char
*
dbdir
__attribute__
((
unused
)),
int
flags
__attribute__
((
unused
)))
{
const
char
*
engine_name
;
int
r
=
0
;
if
(
dbinit
++
)
return
0
;
engine_name
=
libcyrus_config_getstring
(
CYRUSOPT_SQL_ENGINE
);
/* find the correct engine */
dbengine
=
sql_engines
;
while
(
dbengine
->
name
)
{
if
(
!
engine_name
||
!
strcasecmp
(
engine_name
,
dbengine
->
name
))
break
;
dbengine
++
;
}
if
(
!
dbengine
->
name
)
{
char
errbuf
[
1024
];
snprintf
(
errbuf
,
sizeof
(
errbuf
),
"SQL engine %s not supported"
,
engine_name
);
fatal
(
errbuf
,
EC_CONFIG
);
}
if
(
!
engine_name
)
{
syslog
(
LOG_DEBUG
,
"SQL backend defaulting to engine '%s'"
,
dbengine
->
name
);
}
dbinit
=
1
;
return
r
;
}
static
int
done
(
void
)
{
--
dbinit
;
return
0
;
}
static
int
mysync
(
void
)
{
return
0
;
}
static
int
myarchive
(
const
char
**
fnames
__attribute__
((
unused
)),
const
char
*
dirname
__attribute__
((
unused
)))
{
return
0
;
}
static
int
myopen
(
const
char
*
fname
,
int
flags
,
struct
db
**
ret
)
{
const
char
*
database
,
*
hostnames
,
*
user
,
*
passwd
;
char
*
host_ptr
,
*
host
,
*
cur_host
,
*
cur_port
;
int
usessl
;
void
*
conn
=
NULL
;
char
*
p
,
*
table
,
cmd
[
1024
];
if
(
!
fname
||
!
ret
)
return
CYRUSDB_BADPARAM
;
/* make a connection to the database */
database
=
libcyrus_config_getstring
(
CYRUSOPT_SQL_DATABASE
);
hostnames
=
libcyrus_config_getstring
(
CYRUSOPT_SQL_HOSTNAMES
);
user
=
libcyrus_config_getstring
(
CYRUSOPT_SQL_USER
);
passwd
=
libcyrus_config_getstring
(
CYRUSOPT_SQL_PASSWD
);
usessl
=
libcyrus_config_getswitch
(
CYRUSOPT_SQL_USESSL
);
/* loop around hostnames until we get a connection */
syslog
(
LOG_DEBUG
,
"SQL backend trying to connect to a host"
);
/* create a working version of the hostnames */
host_ptr
=
hostnames
?
xstrdup
(
hostnames
)
:
NULL
;
/* make sqlite clever */
if
(
!
database
)
database
=
fname
;
cur_host
=
host
=
host_ptr
;
while
(
cur_host
!=
NULL
)
{
host
=
strchr
(
host
,
','
);
if
(
host
!=
NULL
)
{
host
[
0
]
=
'\0'
;
/* loop till we find some text */
while
(
!
Uisalnum
(
host
[
0
]))
host
++
;
}
syslog
(
LOG_DEBUG
,
"SQL backend trying to open db '%s' on host '%s'%s"
,
database
,
cur_host
,
usessl
?
" using SSL"
:
""
);
/* set the optional port */
if
((
cur_port
=
strchr
(
cur_host
,
':'
)))
*
cur_port
++
=
'\0'
;
conn
=
dbengine
->
sql_open
(
cur_host
,
cur_port
,
usessl
,
user
,
passwd
,
database
);
if
(
conn
)
break
;
syslog
(
LOG_WARNING
,
"DBERROR: SQL backend could not connect to host %s"
,
cur_host
);
cur_host
=
host
;
}
if
(
host_ptr
)
free
(
host_ptr
);
if
(
!
conn
)
{
syslog
(
LOG_ERR
,
"DBERROR: could not open SQL database '%s'"
,
database
);
return
CYRUSDB_IOERROR
;
}
/* get the name of the table and CREATE it if necessary */
/* strip any path from the fname */
p
=
strrchr
(
fname
,
'/'
);
table
=
xstrdup
(
p
?
++
p
:
fname
);
/* convert '.' to '_' */
if
((
p
=
strrchr
(
table
,
'.'
)))
*
p
=
'_'
;
/* check if the table exists */
/* XXX is this the best way to do this? */
snprintf
(
cmd
,
sizeof
(
cmd
),
"SELECT * FROM %s LIMIT 0;"
,
table
);
if
(
dbengine
->
sql_exec
(
conn
,
cmd
,
NULL
,
NULL
))
{
if
(
flags
&
CYRUSDB_CREATE
)
{
/* create the table */
snprintf
(
cmd
,
sizeof
(
cmd
),
"CREATE TABLE %s (dbkey %s NOT NULL, data %s);"
,
table
,
dbengine
->
binary_type
,
dbengine
->
binary_type
);
if
(
dbengine
->
sql_exec
(
conn
,
cmd
,
NULL
,
NULL
))
{
syslog
(
LOG_ERR
,
"DBERROR: SQL failed: %s"
,
cmd
);
dbengine
->
sql_close
(
conn
);
return
CYRUSDB_INTERNAL
;
}
}
else
{
return
CYRUSDB_NOTFOUND
;
}
}
*
ret
=
(
struct
db
*
)
xzmalloc
(
sizeof
(
struct
db
));
(
*
ret
)
->
conn
=
conn
;
(
*
ret
)
->
table
=
table
;
return
0
;
}
static
int
myclose
(
struct
db
*
db
)
{
if
(
!
db
)
return
CYRUSDB_BADPARAM
;
dbengine
->
sql_close
(
db
->
conn
);
free
(
db
->
table
);
if
(
db
->
esc_key
)
free
(
db
->
esc_key
);
if
(
db
->
esc_data
)
free
(
db
->
esc_data
);
if
(
db
->
data
)
free
(
db
->
data
);
free
(
db
);
return
0
;
}
static
struct
txn
*
start_txn
(
struct
db
*
db
)
{
/* start a transaction */
if
(
dbengine
->
sql_begin_txn
(
db
->
conn
))
{
syslog
(
LOG_ERR
,
"DBERROR: failed to start txn on %s"
,
db
->
table
);
return
NULL
;
}
return
xzmalloc
(
sizeof
(
struct
txn
));
}
struct
select_rock
{
int
found
;
struct
txn
*
tid
;
foreach_cb
*
goodp
;
foreach_cb
*
cb
;
void
*
rock
;
};
static
int
select_cb
(
void
*
rock
,
const
char
*
key
,
size_t
keylen
,
const
char
*
data
,
size_t
datalen
)
{
struct
select_rock
*
srock
=
(
struct
select_rock
*
)
rock
;
int
r
=
CYRUSDB_OK
;
/* if we're in a transaction, save this key */
if
(
srock
->
tid
)
{
srock
->
tid
->
lastkey
=
xrealloc
(
srock
->
tid
->
lastkey
,
keylen
);
memcpy
(
srock
->
tid
->
lastkey
,
key
,
keylen
);
srock
->
tid
->
keylen
=
keylen
;
}
/* see if we want this entry */
if
(
!
srock
->
goodp
||
srock
->
goodp
(
srock
->
rock
,
key
,
keylen
,
data
,
datalen
))
{
srock
->
found
=
1
;
/* make callback */
if
(
srock
->
cb
)
r
=
srock
->
cb
(
srock
->
rock
,
key
,
keylen
,
data
,
datalen
);
}
return
r
;
}
struct
fetch_rock
{
char
**
data
;
size_t
*
datalen
;
};
static
int
fetch_cb
(
void
*
rock
,
const
char
*
key
__attribute__
((
unused
)),
size_t
keylen
__attribute__
((
unused
)),
const
char
*
data
,
size_t
datalen
)
{
struct
fetch_rock
*
frock
=
(
struct
fetch_rock
*
)
rock
;
if
(
frock
->
data
)
{
*
(
frock
->
data
)
=
xrealloc
(
*
(
frock
->
data
),
datalen
);
memcpy
(
*
(
frock
->
data
),
data
,
datalen
);
}
if
(
frock
->
datalen
)
*
(
frock
->
datalen
)
=
datalen
;
return
0
;
}
static
int
fetch
(
struct
db
*
db
,
const
char
*
key
,
size_t
keylen
,
const
char
**
data
,
size_t
*
datalen
,
struct
txn
**
tid
)
{
char
cmd
[
1024
],
*
esc_key
;
size_t
len
=
0
;
struct
fetch_rock
frock
=
{
&
db
->
data
,
&
len
};
struct
select_rock
srock
=
{
0
,
NULL
,
NULL
,
&
fetch_cb
,
&
frock
};
int
r
;
if
(
!
db
||
!
key
||
!
keylen
)
return
CYRUSDB_BADPARAM
;
if
(
data
&&
!
datalen
)
return
CYRUSDB_BADPARAM
;
if
(
data
)
*
data
=
NULL
;
if
(
datalen
)
*
datalen
=
0
;
if
(
tid
)
{
if
(
!*
tid
&&
!
(
*
tid
=
start_txn
(
db
)))
return
CYRUSDB_INTERNAL
;
srock
.
tid
=
*
tid
;
}
/* fetch the data */
esc_key
=
dbengine
->
sql_escape
(
db
->
conn
,
&
db
->
esc_key
,
key
,
keylen
);
snprintf
(
cmd
,
sizeof
(
cmd
),
"SELECT * FROM %s WHERE dbkey = '%s';"
,
db
->
table
,
esc_key
);
r
=
dbengine
->
sql_exec
(
db
->
conn
,
cmd
,
&
select_cb
,
&
srock
);
if
(
esc_key
!=
db
->
esc_key
)
free
(
esc_key
);
if
(
r
)
{
syslog
(
LOG_ERR
,
"DBERROR: SQL failed %s"
,
cmd
);
if
(
tid
)
dbengine
->
sql_rollback_txn
(
db
->
conn
);
return
CYRUSDB_INTERNAL
;
}
if
(
!
srock
.
found
)
return
CYRUSDB_NOTFOUND
;
if
(
data
)
*
data
=
db
->
data
;
if
(
datalen
)
*
datalen
=
len
;
return
0
;
}
static
int
foreach
(
struct
db
*
db
,
const
char
*
prefix
,
size_t
prefixlen
,
foreach_p
*
goodp
,
foreach_cb
*
cb
,
void
*
rock
,
struct
txn
**
tid
)
{
char
cmd
[
1024
],
*
esc_key
=
NULL
;
struct
select_rock
srock
=
{
0
,
NULL
,
goodp
,
cb
,
rock
};
int
r
;
if
(
!
db
||
!
cb
)
return
CYRUSDB_BADPARAM
;
if
(
prefixlen
&&
!
prefix
)
return
CYRUSDB_BADPARAM
;
if
(
tid
)
{
if
(
!*
tid
&&
!
(
*
tid
=
start_txn
(
db
)))
return
CYRUSDB_INTERNAL
;
srock
.
tid
=
*
tid
;
}
/* fetch the data */
if
(
prefixlen
)
/* XXX hack for SQLite */
esc_key
=
dbengine
->
sql_escape
(
db
->
conn
,
&
db
->
esc_key
,
prefix
,
prefixlen
);
snprintf
(
cmd
,
sizeof
(
cmd
),
"SELECT * FROM %s WHERE dbkey LIKE '%s%%' ORDER BY dbkey;"
,
db
->
table
,
esc_key
?
esc_key
:
""
);
r
=
dbengine
->
sql_exec
(
db
->
conn
,
cmd
,
&
select_cb
,
&
srock
);
if
(
esc_key
&&
esc_key
!=
db
->
esc_key
)
free
(
esc_key
);
if
(
r
)
{
syslog
(
LOG_ERR
,
"DBERROR: SQL failed %s"
,
cmd
);
if
(
tid
)
dbengine
->
sql_rollback_txn
(
db
->
conn
);
return
CYRUSDB_INTERNAL
;
}
return
0
;
}
static
int
mystore
(
struct
db
*
db
,
const
char
*
key
,
int
keylen
,
const
char
*
data
,
int
datalen
,
struct
txn
**
tid
,
int
overwrite
,
int
isdelete
)
{
char
cmd
[
1024
],
*
esc_key
;
const
char
dummy
=
0
;
int
r
=
0
;
if
(
!
db
||
!
key
||
!
keylen
)
return
CYRUSDB_BADPARAM
;
if
(
datalen
&&
!
data
)
return
CYRUSDB_BADPARAM
;
if
(
!
data
)
data
=
&
dummy
;
if
(
tid
&&
!*
tid
&&
!
(
*
tid
=
start_txn
(
db
)))
return
CYRUSDB_INTERNAL
;
esc_key
=
dbengine
->
sql_escape
(
db
->
conn
,
&
db
->
esc_key
,
key
,
keylen
);
if
(
isdelete
)
{
/* DELETE the entry */
snprintf
(
cmd
,
sizeof
(
cmd
),
"DELETE FROM %s WHERE dbkey = '%s';"
,
db
->
table
,
esc_key
);
r
=
dbengine
->
sql_exec
(
db
->
conn
,
cmd
,
NULL
,
NULL
);
/* see if we just removed the previously SELECTed key */
if
(
!
r
&&
tid
&&
*
tid
&&
(
*
tid
)
->
keylen
==
strlen
(
esc_key
)
&&
!
memcmp
((
*
tid
)
->
lastkey
,
esc_key
,
(
*
tid
)
->
keylen
))
{
(
*
tid
)
->
keylen
=
0
;
}
}
else
{
/* INSERT/UPDATE the entry */
struct
select_rock
srock
=
{
0
,
NULL
,
NULL
,
NULL
,
NULL
};
char
*
esc_data
=
dbengine
->
sql_escape
(
db
->
conn
,
&
db
->
esc_data
,
data
,
datalen
);
/* see if we just SELECTed this key in this transaction */
if
(
tid
&&
*
tid
)
{
if
((
*
tid
)
->
keylen
==
strlen
(
esc_key
)
&&
!
memcmp
((
*
tid
)
->
lastkey
,
esc_key
,
(
*
tid
)
->
keylen
))
{
srock
.
found
=
1
;
}
srock
.
tid
=
*
tid
;
}
/* check if the entry exists */
if
(
!
srock
.
found
)
{
snprintf
(
cmd
,
sizeof
(
cmd
),
"SELECT * FROM %s WHERE dbkey = '%s';"
,
db
->
table
,
esc_key
);
r
=
dbengine
->
sql_exec
(
db
->
conn
,
cmd
,
&
select_cb
,
&
srock
);
}
if
(
!
r
&&
srock
.
found
)
{
if
(
overwrite
)
{
/* already have this entry, UPDATE it */
snprintf
(
cmd
,
sizeof
(
cmd
),
"UPDATE %s SET data = '%s' WHERE dbkey = '%s';"
,
db
->
table
,
esc_data
,
esc_key
);
r
=
dbengine
->
sql_exec
(
db
->
conn
,
cmd
,
NULL
,
NULL
);
}
else
{
if
(
tid
)
dbengine
->
sql_rollback_txn
(
db
->
conn
);
return
CYRUSDB_EXISTS
;
}
}
else
if
(
!
r
&&
!
srock
.
found
)
{
/* INSERT the new entry */
snprintf
(
cmd
,
sizeof
(
cmd
),
"INSERT INTO %s VALUES ('%s', '%s');"
,
db
->
table
,
esc_key
,
esc_data
);
r
=
dbengine
->
sql_exec
(
db
->
conn
,
cmd
,
NULL
,
NULL
);
}
if
(
esc_data
!=
db
->
esc_data
)
free
(
esc_data
);
}
if
(
esc_key
!=
db
->
esc_key
)
free
(
esc_key
);
if
(
r
)
{
syslog
(
LOG_ERR
,
"DBERROR: SQL failed: %s"
,
cmd
);
if
(
tid
)
dbengine
->
sql_rollback_txn
(
db
->
conn
);
return
CYRUSDB_INTERNAL
;
}
return
0
;
}
static
int
create
(
struct
db
*
db
,
const
char
*
key
,
size_t
keylen
,
const
char
*
data
,
size_t
datalen
,
struct
txn
**
tid
)
{
return
mystore
(
db
,
key
,
keylen
,
data
,
datalen
,
tid
,
0
,
0
);
}
static
int
store
(
struct
db
*
db
,
const
char
*
key
,
size_t
keylen
,
const
char
*
data
,
size_t
datalen
,
struct
txn
**
tid
)
{
return
mystore
(
db
,
key
,
keylen
,
data
,
datalen
,
tid
,
1
,
0
);
}
static
int
delete
(
struct
db
*
db
,
const
char
*
key
,
size_t
keylen
,
struct
txn
**
tid
,
int
force
__attribute__
((
unused
)))
{
return
mystore
(
db
,
key
,
keylen
,
NULL
,
0
,
tid
,
1
,
1
);
}
static
int
finish_txn
(
struct
db
*
db
,
struct
txn
*
tid
,
int
commit
)
{
if
(
tid
)
{
int
rc
=
commit
?
dbengine
->
sql_commit_txn
(
db
->
conn
)
:
dbengine
->
sql_rollback_txn
(
db
->
conn
);
if
(
tid
->
lastkey
)
free
(
tid
->
lastkey
);
free
(
tid
);
if
(
rc
)
{
syslog
(
LOG_ERR
,
"DBERROR: failed to %s txn on %s"
,
commit
?
"commit"
:
"abort"
,
db
->
table
);
return
CYRUSDB_INTERNAL
;
}
}
return
0
;
}
static
int
commit_txn
(
struct
db
*
db
,
struct
txn
*
tid
)
{
if
(
!
db
||
!
tid
)
return
CYRUSDB_BADPARAM
;
return
finish_txn
(
db
,
tid
,
1
);
}
static
int
abort_txn
(
struct
db
*
db
,
struct
txn
*
tid
)
{
if
(
!
db
||
!
tid
)
return
CYRUSDB_BADPARAM
;
return
finish_txn
(
db
,
tid
,
0
);
}
struct
cyrusdb_backend
cyrusdb_sql
=
{
"sql"
,
/* name */
&
init
,
&
done
,
&
mysync
,
&
myarchive
,
&
myopen
,
&
myclose
,
&
fetch
,
&
fetch
,
NULL
,
&
foreach
,
&
create
,
&
store
,
&
delete
,
&
commit_txn
,
&
abort_txn
,
NULL
,
NULL
};
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Fri, Apr 24, 10:29 AM (1 d, 11 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18896511
Default Alt Text
cyrusdb_sql.c (22 KB)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline