Initial import

This commit is contained in:
Pierre Pronchery 2012-10-29 02:49:21 +01:00
parent efc80b939e
commit e933ec7618
13 changed files with 1524 additions and 0 deletions

45
Makefile Normal file
View File

@ -0,0 +1,45 @@
PACKAGE = libDatabase
VERSION = 0.0.0
SUBDIRS = src
RM ?= rm -f
LN ?= ln -f
TAR ?= tar -czvf
all: subdirs
subdirs:
@for i in $(SUBDIRS); do (cd $$i && $(MAKE)) || exit; done
clean:
@for i in $(SUBDIRS); do (cd $$i && $(MAKE) clean) || exit; done
distclean:
@for i in $(SUBDIRS); do (cd $$i && $(MAKE) distclean) || exit; done
dist:
$(RM) -r -- $(PACKAGE)-$(VERSION)
$(LN) -s -- . $(PACKAGE)-$(VERSION)
@$(TAR) $(PACKAGE)-$(VERSION).tar.gz -- \
$(PACKAGE)-$(VERSION)/src/database.c \
$(PACKAGE)-$(VERSION)/src/Makefile \
$(PACKAGE)-$(VERSION)/src/project.conf \
$(PACKAGE)-$(VERSION)/src/database/pgsql.c \
$(PACKAGE)-$(VERSION)/src/database/sqlite2.c \
$(PACKAGE)-$(VERSION)/src/database/sqlite3.c \
$(PACKAGE)-$(VERSION)/src/database/template.c \
$(PACKAGE)-$(VERSION)/src/database/Makefile \
$(PACKAGE)-$(VERSION)/src/database/database.h \
$(PACKAGE)-$(VERSION)/src/database/project.conf \
$(PACKAGE)-$(VERSION)/Makefile \
$(PACKAGE)-$(VERSION)/config.h \
$(PACKAGE)-$(VERSION)/project.conf
$(RM) -- $(PACKAGE)-$(VERSION)
install:
@for i in $(SUBDIRS); do (cd $$i && $(MAKE) install) || exit; done
uninstall:
@for i in $(SUBDIRS); do (cd $$i && $(MAKE) uninstall) || exit; done
.PHONY: all subdirs clean distclean dist install uninstall

10
config.h Normal file
View File

@ -0,0 +1,10 @@
#define PACKAGE "libDatabase"
#define VERSION "0.0.0"
#ifndef PREFIX
# define PREFIX "/usr/local"
#endif
#ifndef LIBDIR
# define LIBDIR PREFIX "/lib"
#endif

6
project.conf Normal file
View File

@ -0,0 +1,6 @@
package=libDatabase
version=0.0.0
config=h
subdirs=src
dist=Makefile,config.h

44
src/Makefile Normal file
View File

@ -0,0 +1,44 @@
SUBDIRS = database
TARGETS = database.o
PREFIX = /usr/local
DESTDIR =
BINDIR = $(PREFIX)/bin
SBINDIR = $(PREFIX)/sbin
CC ?= cc
CPPFLAGSF?=
CPPFLAGS?=
CFLAGSF = -W -fPIC
CFLAGS = -Wall -g -O2 -pedantic
RM ?= rm -f
LN ?= ln -f
MKDIR ?= mkdir -p
INSTALL ?= install
all: subdirs $(TARGETS)
subdirs:
@for i in $(SUBDIRS); do (cd $$i && $(MAKE)) || exit; done
database.o_OBJS = database.o
database.o_CFLAGS = $(CPPFLAGSF) $(CPPFLAGS) $(CFLAGSF) $(CFLAGS)
database.o: database.c database/database.h
$(CC) $(database.o_CFLAGS) -c database.c
clean:
@for i in $(SUBDIRS); do (cd $$i && $(MAKE) clean) || exit; done
$(RM) -- $(database.o_OBJS)
distclean:
@for i in $(SUBDIRS); do (cd $$i && $(MAKE) distclean) || exit; done
$(RM) -- $(database.o_OBJS)
$(RM) -- $(TARGETS)
install: $(TARGETS)
@for i in $(SUBDIRS); do (cd $$i && $(MAKE) install) || exit; done
uninstall:
@for i in $(SUBDIRS); do (cd $$i && $(MAKE) uninstall) || exit; done
.PHONY: all subdirs clean distclean install uninstall

115
src/database.c Normal file
View File

@ -0,0 +1,115 @@
/* $Id$ */
/* Copyright (c) 2012 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Database libDatabase */
/* 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, version 3 of the License.
*
* 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, see <http://www.gnu.org/licenses/>. */
#include <string.h>
#include <System.h>
#include "database/database.h"
#include "../config.h"
/* Database */
/* private */
/* types */
struct _Database
{
Plugin * plugin;
DatabasePluginDefinition * dplugin;
DatabasePlugin * database;
};
/* public */
/* functions */
/* database_new */
Database * database_new(char const * engine, Config * config,
char const * section)
{
Database * database;
if((database = object_new(sizeof(*database))) == NULL)
return NULL;
memset(database, 0, sizeof(*database));
if((database->plugin = plugin_new(LIBDIR, PACKAGE, "database",
engine)) == NULL
|| (database->dplugin = plugin_lookup(database->plugin,
"database")) == NULL
|| (database->database = database->dplugin->init(config,
section)) == NULL)
{
database_delete(database);
return NULL;
}
return database;
}
/* database_delete */
void database_delete(Database * database)
{
if(database->plugin != NULL)
plugin_delete(database->plugin);
object_delete(database);
}
/* accessors */
/* database_get_last_id */
int database_get_last_id(Database * database)
{
return database->dplugin->get_last_id(database->database);
}
/* database_prepare_new */
DatabaseStatement * database_prepare_new(
Database * database, char const * query)
{
return database->dplugin->prepare_new(database->database, query);
}
/* database_prepare_delete */
void database_prepare_delete(Database * database,
DatabaseStatement * statement)
{
database->dplugin->prepare_delete(database->database, statement);
}
/* database_prepare_query */
int database_prepare_query(Database * database,
DatabaseStatement * statement,
DatabaseCallback callback, void * data, ...)
{
int ret;
va_list ap;
va_start(ap, data);
ret = database->dplugin->prepare_query(database->database, statement,
callback, data, ap);
va_end(ap);
return ret;
}
/* database_query */
int database_query(Database * database, char const * query,
DatabaseCallback callback, void * data)
{
return database->dplugin->query(database->database, query, callback,
data);
}

80
src/database/Makefile Normal file
View File

@ -0,0 +1,80 @@
TARGETS = pgsql.so sqlite2.so sqlite3.so template.so
PREFIX = /usr/local
DESTDIR =
LIBDIR = $(PREFIX)/lib
CC ?= cc
CPPFLAGSF?=
CPPFLAGS?=
CFLAGSF = -W -fPIC
CFLAGS = -Wall -g -O2 -pedantic
AR ?= ar
RANLIB ?= ranlib
CCSHARED?= $(CC) -shared
RM ?= rm -f
LN ?= ln -f
MKDIR ?= mkdir -p
INSTALL ?= install
all: $(TARGETS)
pgsql_OBJS = pgsql.o
pgsql_CFLAGS = $(CPPFLAGSF) $(CPPFLAGS) -I `pg_config --includedir` $(CFLAGSF) $(CFLAGS)
pgsql_LDFLAGS = $(LDFLAGSF) $(LDFLAGS) -L `pg_config --libdir` -Wl,-rpath,`pg_config --libdir` `pg_config --libs` -lpq
pgsql.so: $(pgsql_OBJS)
$(CCSHARED) -o pgsql.so $(pgsql_OBJS) $(pgsql_LDFLAGS)
sqlite2_OBJS = sqlite2.o
sqlite2_CFLAGS = $(CPPFLAGSF) $(CPPFLAGS) $(CFLAGSF) $(CFLAGS) `pkg-config --cflags sqlite`
sqlite2_LDFLAGS = $(LDFLAGSF) $(LDFLAGS) `pkg-config --libs sqlite`
sqlite2.so: $(sqlite2_OBJS)
$(CCSHARED) -o sqlite2.so $(sqlite2_OBJS) $(sqlite2_LDFLAGS)
sqlite3_OBJS = sqlite3.o
sqlite3_CFLAGS = $(CPPFLAGSF) $(CPPFLAGS) $(CFLAGSF) $(CFLAGS) `pkg-config --cflags sqlite3`
sqlite3_LDFLAGS = $(LDFLAGSF) $(LDFLAGS) `pkg-config --libs sqlite3`
sqlite3.so: $(sqlite3_OBJS)
$(CCSHARED) -o sqlite3.so $(sqlite3_OBJS) $(sqlite3_LDFLAGS)
template_OBJS = template.o
template_CFLAGS = $(CPPFLAGSF) $(CPPFLAGS) $(CFLAGSF) $(CFLAGS)
template_LDFLAGS = $(LDFLAGSF) $(LDFLAGS)
template.so: $(template_OBJS)
$(CCSHARED) -o template.so $(template_OBJS) $(template_LDFLAGS)
pgsql.o: pgsql.c database.h
$(CC) $(pgsql_CFLAGS) -c pgsql.c
sqlite2.o: sqlite2.c database.h
$(CC) $(sqlite2_CFLAGS) -c sqlite2.c
sqlite3.o: sqlite3.c database.h
$(CC) $(sqlite3_CFLAGS) -c sqlite3.c
template.o: template.c database.h
$(CC) $(template_CFLAGS) -c template.c
clean:
$(RM) -- $(pgsql_OBJS) $(sqlite2_OBJS) $(sqlite3_OBJS) $(template_OBJS)
distclean: clean
$(RM) -- $(TARGETS)
install: $(TARGETS)
$(MKDIR) $(DESTDIR)$(LIBDIR)/Database/database
$(INSTALL) -m 0644 -- pgsql.so $(DESTDIR)$(LIBDIR)/Database/database/pgsql.so
$(MKDIR) $(DESTDIR)$(LIBDIR)/Database/database
$(INSTALL) -m 0644 -- sqlite2.so $(DESTDIR)$(LIBDIR)/Database/database/sqlite2.so
$(MKDIR) $(DESTDIR)$(LIBDIR)/Database/database
$(INSTALL) -m 0644 -- sqlite3.so $(DESTDIR)$(LIBDIR)/Database/database/sqlite3.so
uninstall:
$(RM) -- $(DESTDIR)$(LIBDIR)/Database/database/pgsql.so
$(RM) -- $(DESTDIR)$(LIBDIR)/Database/database/sqlite2.so
$(RM) -- $(DESTDIR)$(LIBDIR)/Database/database/sqlite3.so
.PHONY: all clean distclean install uninstall

88
src/database/database.h Normal file
View File

@ -0,0 +1,88 @@
/* $Id$ */
/* Copyright (c) 2012 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Database libDatabase */
/* 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, version 3 of the License.
*
* 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, see <http://www.gnu.org/licenses/>. */
#ifndef LIBDATABASE_DATABASE_DATABASE_H
# define LIBDATABASE_DATABASE_DATABASE_H
# include <stdarg.h>
# include <System.h>
/* Database */
/* private */
/* types */
typedef enum _DatabaseType
{
DT_INTEGER = 0,
DT_TIMESTAMP,
DT_VARCHAR
} DatabaseType;
typedef struct _Database Database;
typedef struct _DatabaseStatement DatabaseStatement;
typedef int (*DatabaseCallback)(void * data, int argc, char ** argv,
char ** columns);
typedef struct _DatabasePlugin DatabasePlugin;
typedef struct _DatabasePluginDefinition
{
char const * name;
char const * description;
/* essential */
DatabasePlugin * (*init)(Config * config, char const * section);
void (*destroy)(DatabasePlugin * plugin);
/* accessors */
int (*get_last_id)(DatabasePlugin * plugin);
/* useful */
int (*query)(DatabasePlugin * plugin, char const * query,
DatabaseCallback callback, void * data);
/* prepared statements */
DatabaseStatement * (*prepare_new)(DatabasePlugin * plugin,
char const * query);
void (*prepare_delete)(DatabasePlugin * plugin,
DatabaseStatement * statement);
int (*prepare_query)(DatabasePlugin * plugin,
DatabaseStatement * statement,
DatabaseCallback callback, void * data,
va_list args);
} DatabasePluginDefinition;
/* public */
/* functions */
Database * database_new(char const * engine, Config * config,
char const * section);
void database_delete(Database * database);
/* accessors */
int database_get_last_id(Database * database);
/* useful */
DatabaseStatement * database_prepare_new(
Database * database, char const * query);
void database_prepare_delete(Database * database,
DatabaseStatement * statement);
int database_prepare_query(Database * database,
DatabaseStatement * statement,
DatabaseCallback callback, void * data, ...);
int database_query(Database * database, char const * query,
DatabaseCallback callback, void * data);
#endif /* !LIBDATABASE_DATABASE_DATABASE_H */

376
src/database/pgsql.c Normal file
View File

@ -0,0 +1,376 @@
/* $Id$ */
/* Copyright (c) 2012 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Database libDatabase */
/* 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, version 3 of the License.
*
* 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, see <http://www.gnu.org/licenses/>. */
#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <libpq-fe.h>
#include <System.h>
#include "database.h"
/* PgSQL */
/* private */
/* types */
typedef struct _DatabasePlugin
{
PGconn * handle;
Oid last;
} PgSQL;
typedef struct _DatabaseStatement
{
PGresult * res;
char * query;
} PgSQLStatement;
/* prototypes */
/* useful */
static int _pgsql_process(PgSQL * pgsql, PGresult * res,
DatabaseCallback callback, void * data);
/* protected */
/* prototypes */
/* plug-in */
static PgSQL * _pgsql_init(Config * config, char const * section);
static void _pgsql_destroy(PgSQL * pgsql);
static int _pgsql_get_last_id(PgSQL * pgsql);
static int _pgsql_query(PgSQL * pgsql, char const * query,
DatabaseCallback callback, void * data);
static PgSQLStatement * _pgsql_prepare_new(PgSQL * pgsql, char const * query);
static void _pgsql_prepare_delete(PgSQL * pgsql, PgSQLStatement * statement);
static int _pgsql_prepare_query(PgSQL * pgsql, PgSQLStatement * statement,
DatabaseCallback callback, void * data, va_list args);
/* public */
/* variables */
DatabasePluginDefinition database =
{
"PostgreSQL",
NULL,
_pgsql_init,
_pgsql_destroy,
_pgsql_get_last_id,
_pgsql_query,
_pgsql_prepare_new,
_pgsql_prepare_delete,
_pgsql_prepare_query
};
/* protected */
/* functions */
/* plug-in */
/* pgsql_init */
static void _init_append(char * buf, size_t size, char const * variable,
char const * value);
static PgSQL * _pgsql_init(Config * config, char const * section)
{
PgSQL * pgsql;
char buf[256] = "";
if((pgsql = object_new(sizeof(*pgsql))) == NULL)
return NULL;
pgsql->last = InvalidOid;
/* build the connection string */
_init_append(buf, sizeof(buf), "user", config_get(config, section,
"username"));
_init_append(buf, sizeof(buf), "password", config_get(config, section,
"password"));
_init_append(buf, sizeof(buf), "dbname", config_get(config, section,
"database"));
_init_append(buf, sizeof(buf), "host", config_get(config, section,
"hostname"));
_init_append(buf, sizeof(buf), "port", config_get(config, section,
"port"));
/* connect to the database */
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() \"%s\"\n", __func__, buf);
#endif
if((pgsql->handle = PQconnectdb(buf)) == NULL
|| PQstatus(pgsql->handle) != CONNECTION_OK)
{
error_set_code(1, "%s", (pgsql->handle != NULL)
? PQerrorMessage(pgsql->handle)
: "Unable to obtain the connection string");
_pgsql_destroy(pgsql);
return NULL;
}
return pgsql;
}
static void _init_append(char * buf, size_t size, char const * variable,
char const * value)
{
size_t len;
if(value == NULL)
return;
len = strlen(buf);
snprintf(&buf[len], size - len, "%s%s=%s", (len > 0) ? " " : "",
variable, value);
}
/* pgsql_destroy */
static void _pgsql_destroy(PgSQL * pgsql)
{
if(pgsql->handle != NULL)
PQfinish(pgsql->handle);
object_delete(pgsql);
}
/* accessors */
/* pgsql_get_last_id */
static int _pgsql_get_last_id(PgSQL * pgsql)
{
/* FIXME use currval() of the relevant instead */
if(pgsql->last == InvalidOid)
return -1;
return pgsql->last;
}
/* useful */
/* pgsql_prepare_new */
static void _prepare_new_adapt(char * q);
static PgSQLStatement * _pgsql_prepare_new(PgSQL * pgsql,
char const * query)
{
PgSQLStatement * statement;
char * q;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, query);
#endif
if((statement = object_new(sizeof(*statement))) == NULL)
return NULL;
statement->query = strdup(query);
statement->res = NULL;
q = strdup(query);
if(statement->query == NULL || q == NULL)
{
free(q);
_pgsql_prepare_delete(pgsql, statement);
return NULL;
}
/* adapt the statement for PostgreSQL */
_prepare_new_adapt(q);
if((statement->res = PQprepare(pgsql->handle, query, q, 0, NULL))
== NULL
|| PQresultStatus(statement->res) != PGRES_COMMAND_OK)
{
error_set_code(1, "%s", PQerrorMessage(pgsql->handle));
_pgsql_prepare_delete(pgsql, statement);
return NULL;
}
return statement;
}
static void _prepare_new_adapt(char * query)
{
int i;
char * p;
int j;
size_t len;
int c;
/* FIXME this only works for up to 9 arguments */
for(i = 0; (p = strchr(query, ':')) != NULL; i++)
{
*(p++) = '$';
*(p++) = '1' + i;
for(j = 0; isalpha((c = p[j])) || p[j] == '_'; j++);
len = strlen(p) + 1;
memmove(p, &p[j], len - j);
}
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() => \"%s\"\n", __func__, query);
#endif
}
/* pgsql_prepare_delete */
static void _pgsql_prepare_delete(PgSQL * pgsql,
PgSQLStatement * statement)
{
PQclear(statement->res);
free(statement->query);
object_delete(statement);
}
/* pgsql_prepare_query */
static int _pgsql_prepare_query(PgSQL * pgsql, PgSQLStatement * statement,
DatabaseCallback callback, void * data, va_list args)
{
int ret = 0;
size_t cnt;
int type;
char const * name;
long l;
char buf[32];
time_t t;
struct tm tm;
char const * s;
char ** v = NULL;
char ** p;
size_t i;
PGresult * res;
/* FIXME this assumes the same order as in the prepared statement */
for(cnt = 0; ret == 0 && (type = va_arg(args, int)) != -1; cnt++)
{
name = va_arg(args, char const *);
if((p = realloc(v, sizeof(*v) * (cnt + 1))) == NULL)
{
ret = -error_set_code(1, "%s", strerror(errno));
break;
}
v = p;
v[cnt] = NULL;
switch(type)
{
case DT_INTEGER:
l = va_arg(args, long);
snprintf(buf, sizeof(buf), "%ld", l);
v[cnt] = strdup(buf);
break;
case DT_TIMESTAMP:
t = va_arg(args, time_t);
if(gmtime_r(&t, &tm) == NULL)
break;
if(strftime(buf, sizeof(buf), "%Y-%m-%d"
" %H:%M:%S", &tm) == 0)
break;
v[cnt] = strdup(buf);
break;
case DT_VARCHAR:
s = va_arg(args, char const *);
v[cnt] = strdup(s);
break;
default:
errno = ENOSYS;
break;
}
if(v[cnt] == NULL)
ret = -error_set_code(1, "%s", strerror(errno));
}
if(ret != 0)
{
for(i = 0; i < cnt; i++)
free(v[i]);
free(v);
return ret;
}
pgsql->last = InvalidOid;
res = PQexecPrepared(pgsql->handle, statement->query, cnt, v, NULL,
NULL, 0);
for(i = 0; i < cnt; i++)
free(v[i]);
free(v);
ret = _pgsql_process(pgsql, res, callback, data);
PQclear(res);
return ret;
}
/* pgsql_query */
static int _pgsql_query(PgSQL * pgsql, char const * query,
DatabaseCallback callback, void * data)
{
int ret;
PGresult * res;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, query);
#endif
pgsql->last = InvalidOid;
if((res = PQexec(pgsql->handle, query)) == NULL)
{
error_set_code(1, "%s", PQerrorMessage(pgsql->handle));
PQclear(res);
return -1;
}
ret = _pgsql_process(pgsql, res, callback, data);
PQclear(res);
return ret;
}
/* private */
/* functions */
/* useful */
/* pgsql_process */
static int _pgsql_process(PgSQL * pgsql, PGresult * res,
DatabaseCallback callback, void * data)
{
char ** columns;
char ** fields;
size_t cnt;
size_t i;
int n;
int j;
if(PQresultStatus(res) == PGRES_COMMAND_OK)
{
pgsql->last = PQoidValue(res);
return 0;
}
else if(PQresultStatus(res) != PGRES_TUPLES_OK)
{
error_set_code(1, "%s", PQerrorMessage(pgsql->handle));
return -1;
}
cnt = PQnfields(res);
columns = malloc(sizeof(*columns) * cnt); /* XXX may fail */
fields = malloc(sizeof(*fields) * cnt); /* XXX may fail */
/* obtain the names of the columns */
for(i = 0; i < cnt; i++)
{
columns[i] = PQfname(res, i);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() %zu/%zu \"%s\"\n", __func__, i + 1,
cnt, columns[i]);
#endif
}
n = PQntuples(res);
for(j = 0; j < n; j++)
{
/* obtain the values of the fields */
for(i = 0; i < cnt; i++)
fields[i] = PQgetvalue(res, j, i);
/* call the callback */
callback(data, cnt, fields, columns);
}
free(fields);
free(columns);
return 0;
}

41
src/database/project.conf Normal file
View File

@ -0,0 +1,41 @@
targets=pgsql,sqlite2,sqlite3,template
cflags_force=-W -fPIC
cflags=-Wall -g -O2 -pedantic
dist=Makefile,database.h
[pgsql]
type=plugin
cppflags=-I `pg_config --includedir`
ldflags=-L `pg_config --libdir` -Wl,-rpath,`pg_config --libdir` `pg_config --libs` -lpq
sources=pgsql.c
install=$(LIBDIR)/Database/database
[pgsql.c]
depends=database.h
[sqlite2]
type=plugin
cflags=`pkg-config --cflags sqlite`
ldflags=`pkg-config --libs sqlite`
sources=sqlite2.c
install=$(LIBDIR)/Database/database
[sqlite2.c]
depends=database.h
[sqlite3]
type=plugin
cflags=`pkg-config --cflags sqlite3`
ldflags=`pkg-config --libs sqlite3`
sources=sqlite3.c
install=$(LIBDIR)/Database/database
[sqlite3.c]
depends=database.h
[template]
type=plugin
sources=template.c
[template.c]
depends=database.h

205
src/database/sqlite2.c Normal file
View File

@ -0,0 +1,205 @@
/* $Id$ */
/* Copyright (c) 2012 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Database libDatabase */
/* 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, version 3 of the License.
*
* 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, see <http://www.gnu.org/licenses/>. */
#include <stdarg.h>
#include <stdlib.h>
#ifdef DEBUG
# include <stdio.h>
#endif
#include <string.h>
#include <sqlite.h>
#include <System.h>
#include "database.h"
/* SQLite2 */
/* private */
/* types */
typedef struct _DatabasePlugin
{
sqlite * handle;
} SQLite2;
typedef struct _DatabaseStatement
{
char * query;
} SQLite2Statement;
/* protected */
/* prototypes */
/* plug-in */
static SQLite2 * _sqlite2_init(Config * config, char const * section);
static void _sqlite2_destroy(SQLite2 * pgsql);
static int _sqlite2_get_last_id(SQLite2 * pgsql);
static int _sqlite2_query(SQLite2 * pgsql, char const * query,
DatabaseCallback callback, void * data);
static SQLite2Statement * _sqlite2_prepare_new(SQLite2 * pgsql,
char const * query);
static void _sqlite2_prepare_delete(SQLite2 * pgsql, SQLite2Statement * statement);
static int _sqlite2_prepare_query(SQLite2 * sqlite,
SQLite2Statement * statement, DatabaseCallback callback,
void * data, va_list args);
/* public */
/* variables */
DatabasePluginDefinition database =
{
"SQLite2",
NULL,
_sqlite2_init,
_sqlite2_destroy,
_sqlite2_get_last_id,
_sqlite2_query,
_sqlite2_prepare_new,
_sqlite2_prepare_delete,
_sqlite2_prepare_query
};
/* private */
/* functions */
/* _sqlite2_init */
static SQLite2 * _sqlite2_init(Config * config, char const * section)
{
SQLite2 * sqlite;
char const * name;
char * error = NULL;
if((sqlite = object_new(sizeof(*sqlite))) == NULL)
return NULL;
sqlite->handle = NULL;
if((name = config_get(config, section, "filename")) != NULL
&& (sqlite->handle = sqlite_open(name, 0, &error))
== NULL)
{
error_set_code(1, "%s: %s", name, (error != NULL) ? error
: "Unknown error");
free(error);
}
/* check for errors */
if(sqlite->handle == NULL)
{
_sqlite2_destroy(sqlite);
return NULL;
}
return sqlite;
}
/* _sqlite2_destroy */
static void _sqlite2_destroy(SQLite2 * sqlite)
{
if(sqlite->handle != NULL)
sqlite_close(sqlite->handle);
object_delete(sqlite);
}
/* accessors */
/* _sqlite2_get_last_id */
static int _sqlite2_get_last_id(SQLite2 * sqlite)
{
/* FIXME really implement */
return -1;
}
/* useful */
/* _sqlite2_prepare_new */
static SQLite2Statement * _sqlite2_prepare_new(SQLite2 * sqlite,
char const * query)
{
SQLite2Statement * statement;
if((statement = object_new(sizeof(*statement))) == NULL)
return NULL;
/* XXX this version of SQLite2 doesn't support prepared statements */
statement->query = string_new(query);
return statement;
}
/* _sqlite2_prepare_delete */
static void _sqlite2_prepare_delete(SQLite2 * sqlite, SQLite2Statement * statement)
{
string_delete(statement->query);
object_delete(statement);
}
/* _sqlite2_prepare_query */
static int _sqlite2_prepare_query(SQLite2 * sqlite,
SQLite2Statement * statement, DatabaseCallback callback,
void * data, va_list args)
{
int type = -1;
char const * name;
char const * s;
char * query = NULL;
sqlite_vm * vm;
const char * tail = NULL;
char * error = NULL;
/* FIXME really implement */
while((type = va_arg(args, int)) != -1)
{
name = va_arg(args, char const *);
switch(type)
{
case DT_VARCHAR:
s = va_arg(args, char const *);
/* FIXME implement */
break;
}
}
query = statement->query; /* XXX remove once really implemented */
if(sqlite_compile(sqlite->handle, query, &tail, &vm, &error)
!= SQLITE_OK)
{
error_set_code(1, "%s", error);
free(error);
return -1;
}
/* ignore errors */
if(sqlite_finalize(vm, &error) != SQLITE_OK)
free(error);
return 0;
}
/* _sqlite2_query */
static int _sqlite2_query(SQLite2 * sqlite, char const * query,
DatabaseCallback callback, void * data)
{
int ret;
char * error = NULL;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, query);
#endif
ret = (sqlite_exec(sqlite->handle, query, callback, data, &error)
== SQLITE_OK) ? 0 : -error_set_code(1, "%s",
(error != NULL) ? error : "Unknown error");
if(error != NULL)
free(error);
return ret;
}

336
src/database/sqlite3.c Normal file
View File

@ -0,0 +1,336 @@
/* $Id$ */
/* Copyright (c) 2012 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Database libDatabase */
/* 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, version 3 of the License.
*
* 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, see <http://www.gnu.org/licenses/>. */
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <sqlite3.h>
#include <System.h>
#include "database.h"
/* SQLite3 */
/* private */
/* types */
typedef struct _DatabasePlugin
{
sqlite3 * handle;
} SQLite3;
typedef struct _DatabaseStatement
{
sqlite3_stmt * stmt;
} SQLite3Statement;
/* protected */
/* prototypes */
/* plug-in */
static SQLite3 * _sqlite3_init(Config * config, char const * section);
static void _sqlite3_destroy(SQLite3 * sqlite3);
static int _sqlite3_get_last_id(SQLite3 * sqlite3);
static int _sqlite3_query(SQLite3 * sqlite3, char const * query,
DatabaseCallback callback, void * data);
static SQLite3Statement * _sqlite3_prepare_new(SQLite3 * sqlite3,
char const * query);
static void _sqlite3_prepare_delete(SQLite3 * sqlite3,
SQLite3Statement * statement);
static int _sqlite3_prepare_query(SQLite3 * sqlite3,
SQLite3Statement * statement, DatabaseCallback callback,
void * data, va_list args);
/* public */
/* variables */
DatabasePluginDefinition database =
{
"SQLite3",
NULL,
_sqlite3_init,
_sqlite3_destroy,
_sqlite3_get_last_id,
_sqlite3_query,
_sqlite3_prepare_new,
_sqlite3_prepare_delete,
_sqlite3_prepare_query
};
/* private */
/* functions */
/* _sqlite3_init */
static SQLite3 * _sqlite3_init(Config * config, char const * section)
{
SQLite3 * sqlite3;
char const * name;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, section);
#endif
if((sqlite3 = object_new(sizeof(*sqlite3))) == NULL)
return NULL;
sqlite3->handle = NULL;
if((name = config_get(config, section, "filename")) != NULL
&& sqlite3_open(name, &sqlite3->handle) != SQLITE_OK)
error_set_code(1, "%s: %s", name, (sqlite3->handle != NULL)
? sqlite3_errmsg(sqlite3->handle)
: "Unknown error");
if(sqlite3->handle == NULL)
{
_sqlite3_destroy(sqlite3);
return NULL;
}
return sqlite3;
}
/* _sqlite3_destroy */
static void _sqlite3_destroy(SQLite3 * sqlite3)
{
if(sqlite3->handle != NULL)
sqlite3_close(sqlite3->handle);
object_delete(sqlite3);
}
/* accessors */
/* _sqlite3_get_last_id */
static int _sqlite3_get_last_id(SQLite3 * sqlite3)
{
/* FIXME really implement */
return -1;
}
/* useful */
/* _sqlite3_prepare_new */
static SQLite3Statement * _sqlite3_prepare_new(SQLite3 * sqlite3,
char const * query)
{
SQLite3Statement * statement;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, query);
#endif
if((statement = object_new(sizeof(*statement))) == NULL)
return NULL;
if(sqlite3_prepare_v2(sqlite3->handle, query, -1, &statement->stmt,
NULL) != SQLITE_OK)
{
error_set_code(1, "%s", sqlite3_errmsg(sqlite3->handle));
object_delete(statement);
return NULL;
}
return statement;
}
/* _sqlite3_prepare_delete */
static void _sqlite3_prepare_delete(SQLite3 * sqlite3,
SQLite3Statement * statement)
{
/* XXX ignore errors */
sqlite3_finalize(statement->stmt);
object_delete(statement);
}
/* _sqlite3_prepare_query */
static int _sqlite3_prepare_query(SQLite3 * sqlite3,
SQLite3Statement * statement, DatabaseCallback callback,
void * data, va_list args)
{
int ret = 0;
int type;
char const * name;
int i;
int cnt;
int argc;
char ** argv;
char ** columns;
char ** p;
long l;
time_t t;
struct tm tm;
char buf[32];
char const * s;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
sqlite3_reset(statement->stmt);
while(ret == 0 && (type = va_arg(args, int)) != -1)
{
name = va_arg(args, char const *);
if((i = sqlite3_bind_parameter_index(statement->stmt, name))
== 0)
{
ret = -error_set_code(1, "%s", "Unknown parameter");
break;
}
switch(type)
{
case DT_INTEGER:
l = va_arg(args, long);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() %s=\"%ld\"\n",
__func__, name, l);
#endif
if(sqlite3_bind_int(statement->stmt, i, l)
== SQLITE_OK)
break;
ret = -error_set_code(1, "%s", sqlite3_errmsg(
sqlite3->handle));
break;
case DT_TIMESTAMP:
t = va_arg(args, time_t);
if(gmtime_r(&t, &tm) == NULL)
break;
if(strftime(buf, sizeof(buf), "%Y-%m-%d"
" %H:%M:%S", &tm) == 0)
{
ret = -error_set_code(1, "%s", strerror(
errno));
break;
}
if(sqlite3_bind_text(statement->stmt, i, buf,
-1, SQLITE_TRANSIENT)
== SQLITE_OK)
break;
ret = -error_set_code(1, "%s", sqlite3_errmsg(
sqlite3->handle));
break;
case DT_VARCHAR:
s = va_arg(args, char const *);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() %s=\"%s\" (%d)\n",
__func__, name, s, i);
#endif
if(sqlite3_bind_text(statement->stmt, i, s, -1,
SQLITE_STATIC)
== SQLITE_OK)
break;
ret = -error_set_code(1, "%s", sqlite3_errmsg(
sqlite3->handle));
break;
default:
ret = -error_set_code(1, "%s (%d)",
"Unsupported type", type);
break;
}
}
if(ret != 0)
return ret;
/* return directly if there are no results */
if((argc = sqlite3_column_count(statement->stmt)) == 0)
{
/* FIXME should really execute until done */
if(sqlite3_step(statement->stmt) != SQLITE_DONE)
ret = -error_set_code(1, "%s", sqlite3_errmsg(
sqlite3->handle));
return ret;
}
/* pre-allocate the results */
argv = malloc(sizeof(*argv) * argc);
columns = malloc(sizeof(*columns) * argc);
if(argv == NULL || columns == NULL)
{
free(argv);
free(columns);
return -error_set_code(1, "%s", strerror(errno));
}
/* obtain the column names */
for(i = 0; i < argc; i++)
/* XXX cast or duplicate name */
columns[i] = sqlite3_column_origin_name(statement->stmt, i);
for(cnt = 0; (i = sqlite3_step(statement->stmt)) == SQLITE_ROW; cnt++)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() cnt=%d\n", __func__, cnt);
#endif
/* reset the result line */
memset(argv, 0, sizeof(*argv) * argc);
for(i = 0; (type = sqlite3_column_type(statement->stmt, i))
!= SQLITE_NULL; i++)
{
switch(type)
{
case SQLITE_INTEGER:
l = sqlite3_column_int(statement->stmt,
i);
snprintf(buf, sizeof(buf), "%ld", l);
s = buf;
break;
case SQLITE_TEXT:
s = (char const *)sqlite3_column_text(
statement->stmt, i);
break;
case SQLITE_BLOB:
case SQLITE_FLOAT:
default:
/* FIXME really implement */
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() i=%d\n",
__func__, i);
#endif
continue;
}
p = &argv[i];
/* XXX check errors */
*p = strdup(s);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() argv[%d]=\"%s\"\n",
__func__, i, argv[i]);
#endif
}
if(ret == 0 && callback != NULL)
/* FIXME really implement the callback */
callback(data, argc, argv, columns);
for(i = 0; i < argc; i++)
free(argv[i]);
}
free(argv);
free(columns);
if(ret == 0 && i != SQLITE_DONE)
ret = -error_set_code(1, "%s", sqlite3_errmsg(sqlite3->handle));
return ret;
}
/* _sqlite3_query */
int _sqlite3_query(SQLite3 * sqlite3, char const * query,
DatabaseCallback callback, void * data)
{
int ret;
char * error = NULL;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, query);
#endif
ret = (sqlite3_exec(sqlite3->handle, query, callback, data, &error)
== SQLITE_OK) ? 0 : -error_set_code(1, "%s",
(error != NULL) ? error : "Unknown error");
if(error != NULL)
sqlite3_free(error);
return ret;
}

166
src/database/template.c Normal file
View File

@ -0,0 +1,166 @@
/* $Id$ */
/* Copyright (c) 2012 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Database libDatabase */
/* 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, version 3 of the License.
*
* 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, see <http://www.gnu.org/licenses/>. */
#include <stdarg.h>
#include <stdlib.h>
#ifdef DEBUG
# include <stdio.h>
#endif
#include <string.h>
#include <System.h>
#include "database.h"
/* Template */
/* private */
/* types */
typedef struct _DatabasePlugin
{
void * handle;
} Template;
typedef struct _DatabaseStatement
{
char * query;
} TemplateStatement;
/* protected */
/* prototypes */
/* plug-in */
static Template * _template_init(Config * config, char const * section);
static void _template_destroy(Template * pgsql);
static int _template_get_last_id(Template * pgsql);
static int _template_query(Template * pgsql, char const * query,
DatabaseCallback callback, void * data);
static TemplateStatement * _template_prepare_new(Template * pgsql,
char const * query);
static void _template_prepare_delete(Template * pgsql, TemplateStatement * statement);
static int _template_prepare_query(Template * template,
TemplateStatement * statement, DatabaseCallback callback,
void * data, va_list args);
/* public */
/* variables */
DatabasePluginDefinition database =
{
"Template",
NULL,
_template_init,
_template_destroy,
_template_get_last_id,
_template_query,
_template_prepare_new,
_template_prepare_delete,
_template_prepare_query
};
/* private */
/* functions */
/* _template_init */
static Template * _template_init(Config * config, char const * section)
{
Template * template;
if((template = object_new(sizeof(*template))) == NULL)
return NULL;
template->handle = NULL;
return template;
}
/* _template_destroy */
static void _template_destroy(Template * template)
{
object_delete(template);
}
/* accessors */
/* _template_get_last_id */
static int _template_get_last_id(Template * template)
{
/* FIXME really implement */
return -1;
}
/* useful */
/* _template_prepare_new */
static TemplateStatement * _template_prepare_new(Template * template,
char const * query)
{
TemplateStatement * statement;
if((statement = object_new(sizeof(*statement))) == NULL)
return NULL;
statement->query = string_new(query);
return statement;
}
/* _template_prepare_delete */
static void _template_prepare_delete(Template * template, TemplateStatement * statement)
{
string_delete(statement->query);
object_delete(statement);
}
/* _template_prepare_query */
static int _template_prepare_query(Template * template,
TemplateStatement * statement, DatabaseCallback callback,
void * data, va_list args)
{
int type = -1;
char const * name;
char const * s;
void * p;
/* FIXME really implement */
while((type = va_arg(args, int)) != -1)
{
name = va_arg(args, char const *);
switch(type)
{
case DT_VARCHAR:
s = va_arg(args, char const *);
/* FIXME implement */
break;
default:
p = va_arg(args, void *);
break;
}
}
return -1;
}
/* _template_query */
static int _template_query(Template * template, char const * query,
DatabaseCallback callback, void * data)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, query);
#endif
return -1;
}

12
src/project.conf Normal file
View File

@ -0,0 +1,12 @@
subdirs=database
targets=database.o
cflags_force=-W -fPIC
cflags=-Wall -g -O2 -pedantic
dist=Makefile
[database.o]
type=object
sources=database.c
[database.c]
depends=database/database.h