/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "istream.h"
#include "hex-binary.h"
#include "hash.h"
#include "str.h"
#include "sql-api-private.h"
#include "sql-db-cache.h"
#include "dict-private.h"
#include "dict-sql-settings.h"
#include "dict-sql.h"
#include "dict-sql-private.h"
#include <unistd.h>
#include <fcntl.h>
enum sql_recurse_type {
};
struct sql_dict_param {
const char *value_str;
const void *value_binary;
};
struct sql_dict_iterate_context {
const char **paths;
bool synchronous_result;
bool iter_query_sent;
const char *error;
};
struct sql_dict_inc_row {
unsigned int rows;
};
struct sql_dict_transaction_context {
char *prev_inc_key;
long long prev_inc_diff;
char *prev_set_key;
char *prev_set_value;
void *async_context;
char *error;
};
static int
const struct dict_settings *set,
{
pool_unref(&pool);
return -1;
}
/* currently pgsql and sqlite don't support "ON DUPLICATE KEY" */
}
return 0;
}
{
const char *query;
return;
}
{
}
{
/* FIXME: lib-sql doesn't support this yet */
}
static bool
{
if (*pat == '$') {
/* variable */
pat++;
if (*pat == '\0') {
/* pattern ended with this variable,
it'll match the rest of the path */
if (partial_ok) {
/* iterating - the last field never
matches fully. if there's a trailing
'/', drop it. */
pat--;
} else {
}
} else {
}
return TRUE;
}
/* pattern matches until the next '/' in path */
if (p != NULL) {
path = p;
} else {
/* no '/' anymore, but it'll still match a
partial */
pat++;
}
pat++;
path++;
} else {
return FALSE;
}
}
if (*pat == '\0')
return *path == '\0';
else if (!partial_ok)
return FALSE;
else {
/* partial matches must end with '/'. */
return FALSE;
/* if we're not recursing, there should be only one $variable
left. */
if (recurse)
return TRUE;
}
}
static const struct dict_sql_map *
{
unsigned int i, count;
for (i = 0; i < count; i++) {
return &maps[i];
}
return NULL;
}
static void
const struct sql_dict_param *param)
{
switch (param->value_type) {
case DICT_SQL_TYPE_STRING:
break;
case DICT_SQL_TYPE_INT:
case DICT_SQL_TYPE_UINT:
break;
case DICT_SQL_TYPE_HEXBLOB:
break;
}
}
static struct sql_statement *
{
}
} else {
/* Prepared statements not supported by the backend.
Just use regular statements to avoid wasting memory. */
}
param);
}
return stmt;
}
static int
const char *value, const char *value_suffix,
{
switch (value_type) {
case DICT_SQL_TYPE_STRING:
if (value_suffix[0] != '\0')
return 0;
case DICT_SQL_TYPE_INT:
if (value_suffix[0] != '\0' ||
"%s field's value isn't 64bit signed integer: %s%s (in pattern: %s)",
return -1;
}
return 0;
case DICT_SQL_TYPE_UINT:
"%s field's value isn't 64bit unsigned integer: %s%s (in pattern: %s)",
return -1;
}
return 0;
case DICT_SQL_TYPE_HEXBLOB:
break;
}
/* we shouldn't get untrusted input here. it's also a bit
annoying to handle this error. */
return -1;
}
return 0;
}
static int
const struct dict_sql_field *field,
const char *value, const char *value_suffix,
const char **error_r)
{
}
static int
const char **error_r)
{
const char *const *values;
/* if we came here from iteration code there may be less values */
/* we want everything */
return 0;
}
for (i = 0; i < exact_count; i++) {
if (i > 0)
return -1;
}
switch (recurse_type) {
case SQL_DICT_RECURSE_NONE:
break;
case SQL_DICT_RECURSE_ONE:
if (i > 0)
if (i < count2) {
values[i], "/%",
return -1;
values[i], "/%/%",
return -1;
} else {
"%s NOT LIKE '%%/%%'",
}
break;
case SQL_DICT_RECURSE_FULL:
if (i < count2) {
if (i > 0)
sql_fields[i].name);
values[i], "/%",
return -1;
}
break;
}
if (priv) {
if (count2 > 0)
}
return 0;
}
static int
const struct dict_sql_map **map_r,
struct sql_statement **stmt_r,
const char **error_r)
{
const char *error;
return -1;
}
return -1;
}
return 0;
}
static const char *
{
const unsigned char *data;
const char *value;
switch (type) {
case DICT_SQL_TYPE_STRING:
case DICT_SQL_TYPE_INT:
case DICT_SQL_TYPE_UINT:
case DICT_SQL_TYPE_HEXBLOB:
break;
}
}
static const char *
struct sql_result *result)
{
}
static const char *const *
struct sql_result *result)
{
const char **values;
unsigned int i;
for (i = 0; i < map->values_count; i++) {
}
return values;
}
static const char *
unsigned int sql_field_idx)
{
result, result_idx);
}
{
int ret;
return -1;
if (ret < 0) {
} else if (ret > 0) {
}
return ret;
}
struct sql_dict_lookup_context {
void *context;
};
static void
struct sql_dict_lookup_context *ctx)
{
/* NULL value returned. we'll treat this as
"not found", which is probably what is usually
wanted. */
}
}
}
static void
{
const char *error;
} else {
}
}
static const struct dict_sql_map *
{
unsigned int i, count;
(recurse ||
return &maps[i];
}
}
/* try the next path, if there is any */
return NULL;
}
static int
struct sql_statement **stmt_r,
const char **error_r)
{
unsigned int i, count;
/* NULL map is allowed if we have already done some lookups */
if (!ctx->allow_null_map) {
return -1;
}
return 0;
}
}
/* get all missing fields */
i = array_count(&values);
if (i == count) {
/* we always want to know the last field since we're
iterating its children */
i_assert(i > 0);
i--;
}
ctx->sql_fields_start_idx = i;
for (; i < count; i++)
else
return -1;
for (i = 0; i < count; i++) {
if (i < count-1)
}
}
return 1;
}
struct sql_dict_iterate_context *ctx)
{
}
{
const char *error;
int ret;
if (ret <= 0) {
/* this is expected error */
if (ret == 0)
return ret;
/* failed */
"sql dict iterate failed for %s: %s",
return -1;
}
} else {
}
return ret;
}
static struct dict_iterate_context *
enum dict_iterate_flags flags)
{
unsigned int i, path_count;
for (i = 0; i < path_count; i++)
}
{
(struct sql_dict_iterate_context *)_ctx;
const char *p, *value;
int ret;
return FALSE;
if (!ctx->iter_query_sent) {
if (sql_dict_iterate_next_query(ctx) <= 0)
return FALSE;
}
/* wait for async lookup to finish */
return FALSE;
}
while (ret == SQL_RESULT_NEXT_MORE) {
else {
/* get more results asynchronously */
return FALSE;
}
}
}
if (ret == 0) {
/* see if there are more results in the next map.
don't do it if we're looking for an exact match, since we
already should have handled it. */
return FALSE;
/* we have gotten *SOME* results, so can allow
unmapped next key now. */
}
if (ret < 0) {
"dict sql iterate failed: %s",
return FALSE;
}
/* convert fetched row to dict key */
if (ctx->key_prefix_len > 0 &&
if (*p != '$')
else {
i++; sql_field_i++;
}
}
*value_r = "";
else {
}
return TRUE;
}
const char **error_r)
{
(struct sql_dict_iterate_context *)_ctx;
return ret;
}
static struct dict_transaction_context *
{
}
{
}
static bool
{
return TRUE;
}
return FALSE;
}
static void
struct sql_dict_transaction_context *ctx)
{
else {
sql_result->error);
switch (sql_result->error_type) {
default:
break;
break;
}
}
}
static void
void *context)
{
(struct sql_dict_transaction_context *)_ctx;
const char *error;
/* note that the above calls might still set ctx->error */
/* nothing changed, no need to commit */
} else if (async) {
return;
"sql dict: commit failed: %s", error);
} else {
else
}
}
{
(struct sql_dict_transaction_context *)_ctx;
}
static struct sql_statement *
const char *query,
{
return stmt;
}
struct dict_sql_build_query_field {
const char *value;
};
struct dict_sql_build_query {
char key1;
};
const struct dict_sql_build_query *build,
struct sql_statement **stmt_r,
const char **error_r)
{
const char *const *extra_values;
i_assert(field_count > 0);
for (i = 0; i < field_count; i++) {
if (i > 0) {
}
return -1;
}
}
/* add the other fields from the key */
for (i = 0; i < count; i++) {
extra_values[i], "",
return -1;
}
if (!dict->has_on_duplicate_key) {
return 0;
}
for (i = 0; i < field_count; i++) {
const char *first_value_field =
if (i > 0)
return -1;
}
return 0;
}
static int
const char **error_r)
{
unsigned int i, field_count;
i_assert(field_count > 0);
for (i = 0; i < field_count; i++) {
const char *first_value_field =
if (i > 0)
}
return -1;
return 0;
}
{
(struct sql_dict_transaction_context *)_ctx;
const char *error;
return;
return;
}
} else {
}
}
const char *key)
{
(struct sql_dict_transaction_context *)_ctx;
const char *error;
return;
return;
}
} else {
}
}
static unsigned int *
{
ctx->inc_row_pool =
}
}
{
return;
} else {
}
}
{
}
{
}
static bool
const struct dict_sql_map *map1,
const struct dict_sql_map *map2,
{
return FALSE;
return FALSE;
if (map1_key[0] == DICT_PATH_PRIVATE[0]) {
return FALSE;
}
return FALSE;
for (i = 0; i < count1; i++) {
return FALSE;
}
return TRUE;
}
{
(struct sql_dict_transaction_context *)_ctx;
return;
return;
}
/* see if we can merge this increment SQL query with the
next one */
return;
}
} else {
const char *error;
} else {
}
}
}
{
(struct sql_dict_transaction_context *)_ctx;
return;
return;
}
/* see if we can merge this increment SQL query with the
next one */
return;
}
} else {
/* field->value is unused */
} else {
}
}
}
.name = "sql",
{
.init = sql_dict_init,
.wait = sql_dict_wait,
.set = sql_dict_set,
.unset = sql_dict_unset,
}
};
void dict_sql_register(void)
{
unsigned int i, count;
/* @UNSAFE */
for (i = 0; i < count; i++) {
dict_sql_drivers[i] = sql_dict;
}
}
void dict_sql_unregister(void)
{
int i;
}