A zero-boilerplate, ORM for PostgreSQL
git clone git://soophie.de/glue
1
GNU GENERAL PUBLIC LICENSE2
Version 3, 29 June 20074
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>5
Everyone is permitted to copy and distribute verbatim copies6
of this license document, but changing it is not allowed.8
Preamble10
The GNU General Public License is a free, copyleft license for11
software and other kinds of works.13
The licenses for most software and other practical works are designed14
to take away your freedom to share and change the works. By contrast,15
the GNU General Public License is intended to guarantee your freedom to16
share and change all versions of a program--to make sure it remains free17
software for all its users. We, the Free Software Foundation, use the18
GNU General Public License for most of our software; it applies also to19
any other work released this way by its authors. You can apply it to20
your programs, too.22
When we speak of free software, we are referring to freedom, not23
price. Our General Public Licenses are designed to make sure that you24
have the freedom to distribute copies of free software (and charge for25
them if you wish), that you receive source code or can get it if you26
want it, that you can change the software or use pieces of it in new27
free programs, and that you know you can do these things.29
To protect your rights, we need to prevent others from denying you30
these rights or asking you to surrender the rights. Therefore, you have31
certain responsibilities if you distribute copies of the software, or if32
you modify it: responsibilities to respect the freedom of others.34
For example, if you distribute copies of such a program, whether35
gratis or for a fee, you must pass on to the recipients the same36
freedoms that you received. You must make sure that they, too, receive37
or can get the source code. And you must show them these terms so they38
know their rights.40
Developers that use the GNU GPL protect your rights with two steps:41
(1) assert copyright on the software, and (2) offer you this License42
giving you legal permission to copy, distribute and/or modify it.44
For the developers' and authors' protection, the GPL clearly explains45
that there is no warranty for this free software. For both users' and46
authors' sake, the GPL requires that modified versions be marked as47
changed, so that their problems will not be attributed erroneously to48
authors of previous versions.50
Some devices are designed to deny users access to install or run51
modified versions of the software inside them, although the manufacturer52
can do so. This is fundamentally incompatible with the aim of53
protecting users' freedom to change the software. The systematic54
pattern of such abuse occurs in the area of products for individuals to55
use, which is precisely where it is most unacceptable. Therefore, we56
have designed this version of the GPL to prohibit the practice for those57
products. If such problems arise substantially in other domains, we58
stand ready to extend this provision to those domains in future versions59
of the GPL, as needed to protect the freedom of users.61
Finally, every program is threatened constantly by software patents.62
States should not allow patents to restrict development and use of63
software on general-purpose computers, but in those that do, we wish to64
avoid the special danger that patents applied to a free program could65
make it effectively proprietary. To prevent this, the GPL assures that66
patents cannot be used to render the program non-free.68
The precise terms and conditions for copying, distribution and69
modification follow.
1
CC=cc2
CFLAGS=-Wall -Wextra -Werror -pedantic3
LIBS=-lpq5
install:6
sudo cp src/glue.h /usr/local/include/
1
# glue3
A zero-boilerplate PostgreSQL ORM for C5
## Installation7
```8
git clone git@soophie.de:/srv/git/glue9
cd glue10
sudo make install11
```13
## Quick Start15
```c16
#define GLUE_DB_INFO "host= port= user= password= dbname="18
#define SO_GLUE_IMPL19
#include <glue.h>21
// CREATE TABLE items (22
// id serial PRIMARY KEY,23
// code text NOT NULL,24
// name text NOT NULL25
// );26
#define ITEM_COLUMNS(X) \27
COLDEF_INT(X, id) \28
COLDEF_TEXT(X, code) \29
COLDEF_TEXT(X, name)30
TABLEDEF(item, ITEM_COLUMNS)32
int main(void) {33
item_t *item_list = NULL;34
size_t item_list_len = 0;35
item_fetch("SELECT * FROM items;", 0, NULL, &item_list, &item_list_len);36
for (size_t i = 0; i < item_list_len; i++) {37
item_dbg(&item_list[i]);38
}39
item_list_free(item_list, item_list_len);40
return 0;41
}42
```44
### Compiling46
```bash47
cc -o main -lpq main.c48
```
1
#ifndef _SO_GLUE_H2
#define _SO_GLUE_H4
#include <stddef.h>5
#include <stdbool.h>7
typedef void (*glue_fetch_fn)(char *, char *, void *);9
void glue_fetch(const char *query, size_t params_len, const char *params[params_len], glue_fetch_fn fn, void **data, size_t *len, size_t size);10
void glue_execute(const char *query, size_t params_len, const char *params[params_len]);11
void glue_get_string(char *src, char **dest);12
void glue_get_int(char *src, int *dest);13
void glue_get_double(char *src, double *dest);14
void glue_get_float(char *src, float *dest);15
void glue_get_bool(char *src, bool *dest);18
#define TABLEDEF($name, $cols) \19
typedef struct { \20
$cols(X_COLDEF) \21
} $name##_t; \22
\23
void _##$name##_fetch_fn(char *column, char *value, void *data) { \24
$name##_t *self = ($name##_t *) data; \25
$cols(X_ASSIGN) \26
} \27
void $name##_fetch(const char *query, size_t params_len, const char *params[params_len], $name##_t **data, size_t *len) { \28
glue_fetch(query, params_len, params, _##$name##_fetch_fn, (void *) data, len, sizeof($name##_t)); \29
} \30
void $name##_free($name##_t *self) { \31
$cols(X_FREE) \32
} \33
\34
void $name##_list_free($name##_t *list, size_t len) { \35
if (!list) return; \36
for (size_t i = 0; i < len; i++) { \37
$name##_free(&list[i]); \38
} \39
free(list); \40
} \41
\42
void $name##_dbg($name##_t *self) { \43
fprintf(stdout, "(%s_t) {\n", #$name); \44
$cols(X_DEBUG) \45
fprintf(stdout, "}\n"); \46
}48
#define X_COLDEF($type, $name, $getter, $freeable, $format) \49
$type $name;51
#define X_FREE($type, $name, $getter, $freeable, $format) \52
if ($freeable) { \53
if (self->$name) { \54
void **name = (void **) &self->$name; \55
free(*name); \56
*name = NULL; \57
} \58
}60
#define X_ASSIGN($type, $name, $getter, $freeable, $format) \61
if (strcmp(column, #$name) == 0) { \62
$getter(value, &self->$name); \63
}65
#define X_DEBUG($type, $name, $getter, $freeable, $format) \66
fprintf(stdout, " .%s = ", #$name); \67
$format($name) \68
fprintf(stdout, "\n");70
#define FORMAT_INT($name) \71
fprintf(stdout, "%d,", (int) self->$name);73
#define FORMAT_TEXT($name) \74
fprintf(stdout, "%s,", (const char *) self->$name);76
#define COLDEF_INT(X, $name) \77
X(int, $name, glue_get_int, 0, FORMAT_INT)79
#define COLDEF_TEXT(X, $name) \80
X(char *, $name, glue_get_string, 1, FORMAT_TEXT)82
#ifdef SO_GLUE_IMPL84
#include <stdio.h>85
#include <stdlib.h>86
#include <string.h>87
#include <inttypes.h>88
#include <libpq-fe.h>90
void glue_execute(const char *query, size_t params_len, const char *params[params_len]) {91
PGconn *conn = PQconnectdb(GLUE_DB_INFO);92
if (PQstatus(conn) != CONNECTION_OK) {93
fprintf(stderr, "%s\n", PQerrorMessage(conn));94
PQfinish(conn);95
return;96
}97
PGresult *result = PQexecParams(conn, query, params_len, NULL, params, NULL, NULL, 0);98
ExecStatusType status = PQresultStatus(result);99
if (status != PGRES_COMMAND_OK) {100
fprintf(stderr, "%s\n", PQerrorMessage(conn));101
PQclear(result);102
return;103
}104
PQclear(result);105
PQfinish(conn);106
}108
void glue_fetch(const char *query, size_t params_len, const char *params[params_len], glue_fetch_fn fn, void **data, size_t *len, size_t size) {109
PGconn *conn = PQconnectdb(GLUE_DB_INFO);110
if (PQstatus(conn) != CONNECTION_OK) {111
fprintf(stderr, "%s\n", PQerrorMessage(conn));112
PQfinish(conn);113
return;114
}115
PGresult *result = PQexecParams(conn, query, params_len, NULL, params, NULL, NULL, 0);116
ExecStatusType status = PQresultStatus(result);117
if (status != PGRES_TUPLES_OK) {118
fprintf(stderr, "%s\n", PQerrorMessage(conn));119
PQclear(result);120
PQfinish(conn);121
return;122
}123
size_t rows = PQntuples(result);124
size_t cols = PQnfields(result);125
if (rows > 0) {126
for (size_t i = 0; i < rows; i++) {127
void *ptr = realloc(*data, size * (*len + 1));128
if (ptr == NULL) {129
// TODO error message130
PQclear(result);131
PQfinish(conn);132
return;133
}134
*data = ptr;135
void *item = (char *) *data + size * (*len);136
memset(item, 0, size);137
(*len)++;138
for (size_t j = 0; j < cols; j++) {139
if (PQgetisnull(result, i, j)) {140
continue;141
}142
char *column = PQfname(result, j);143
char *value = PQgetvalue(result, i, j);144
fn(column, value, item);145
}146
}147
}148
PQclear(result);149
PQfinish(conn);150
}152
void glue_get_string(char *src, char **dest) {153
size_t value_len = strlen(src);154
if (value_len == 0) {155
*dest = NULL;156
}157
*dest = malloc(sizeof(char) * (value_len + 1));158
memcpy(*dest, src, value_len);159
(*dest)[value_len] = '\0';160
}162
void glue_get_int(char *src, int *dest) {163
*dest = strtoimax(src, NULL, 10);164
}166
void glue_get_double(char *src, double *dest) {167
*dest = strtod(src, NULL);168
}170
void glue_get_float(char *src, float *dest) {171
*dest = strtof(src, NULL);172
}174
void glue_get_bool(char *src, bool *dest) {175
*dest = strcmp("t", src) == 0;176
}178
#endif /* SO_GLUE_IMPL */179
#endif /* _SO_GLUE_H */