int sr_cset(src *c, srcstmt *stmt, char *value) { int type = c->flags & ~SR_CRO; if (c->flags & SR_CRO) { sr_error(stmt->r->e, "%s is read-only", stmt->path); return -1; } switch (type) { case SR_CU32: *((uint32_t*)c->value) = sr_atoi(value); break; case SR_CU64: *((uint64_t*)c->value) = sr_atoi(value); break; case SR_CSZREF: { char *nsz = NULL; if (value) { nsz = sr_strdup(stmt->r->a, value); if (srunlikely(nsz == NULL)) { sr_error(stmt->r->e, "%s", "memory allocation failed"); return -1; } } char **sz = (char**)c->value; if (*sz) sr_free(stmt->r->a, *sz); *sz = nsz; break; } default: assert(0); } return 0; }
int sd_buildbegin(sdbuild *b, sr *r, int crc, int compress) { b->crc = crc; b->compress = compress; int rc = sr_bufensure(&b->list, r->a, sizeof(sdbuildref)); if (srunlikely(rc == -1)) return sr_error(r->e, "%s", "memory allocation failed"); sdbuildref *ref = (sdbuildref*)sr_bufat(&b->list, sizeof(sdbuildref), b->n); ref->k = sr_bufused(&b->k); ref->ksize = 0; ref->v = sr_bufused(&b->v); ref->vsize = 0; ref->c = sr_bufused(&b->c); ref->csize = 0; rc = sr_bufensure(&b->k, r->a, sizeof(sdpageheader)); if (srunlikely(rc == -1)) return sr_error(r->e, "%s", "memory allocation failed"); sdpageheader *h = sd_buildheader(b); memset(h, 0, sizeof(*h)); h->lsnmin = UINT64_MAX; h->lsnmindup = UINT64_MAX; h->tsmin = 0; memset(h->reserve, 0, sizeof(h->reserve)); sr_bufadvance(&b->list, sizeof(sdbuildref)); sr_bufadvance(&b->k, sizeof(sdpageheader)); return 0; }
int sd_buildadd(sdbuild *b, sr *r, sv *v, uint32_t flags) { /* prepare metadata reference */ int rc = sr_bufensure(&b->k, r->a, sizeof(sdv)); if (srunlikely(rc == -1)) return sr_error(r->e, "%s", "memory allocation failed"); sdpageheader *h = sd_buildheader(b); sdv *sv = (sdv*)b->k.p; sv->lsn = sv_lsn(v); sv->flags = sv_flags(v) | flags; sv->size = sv_size(v); sv->offset = sr_bufused(&b->v) - sd_buildref(b)->v; /* copy object */ rc = sr_bufensure(&b->v, r->a, sv->size); if (srunlikely(rc == -1)) return sr_error(r->e, "%s", "memory allocation failed"); memcpy(b->v.p, sv_pointer(v), sv->size); sr_bufadvance(&b->v, sv->size); sr_bufadvance(&b->k, sizeof(sdv)); /* update page header */ h->count++; h->size += sv->size + sizeof(sdv); if (sv->lsn > h->lsnmax) h->lsnmax = sv->lsn; if (sv->lsn < h->lsnmin) h->lsnmin = sv->lsn; if (sv->flags & SVDUP) { h->countdup++; if (sv->lsn < h->lsnmindup) h->lsnmindup = sv->lsn; } return 0; }
int sr_meta_write(srmeta *m, srmetastmt *s) { if (m->flags & SR_RO) { sr_error(s->r->e, "%s is read-only", s->path); return -1; } switch (m->type) { case SS_U32: if (s->valuetype == SS_I64) { *((uint32_t*)m->value) = *(int64_t*)s->value; } else if (s->valuetype == SS_U32) { *((uint32_t*)m->value) = *(uint32_t*)s->value; } else if (s->valuetype == SS_U64) { *((uint32_t*)m->value) = *(uint64_t*)s->value; } else { goto bad_type; } break; case SS_U64: if (s->valuetype == SS_I64) { *((uint64_t*)m->value) = *(int64_t*)s->value; } else if (s->valuetype == SS_U32) { *((uint64_t*)m->value) = *(uint32_t*)s->value; } else if (s->valuetype == SS_U64) { *((uint64_t*)m->value) = *(uint64_t*)s->value; } else { goto bad_type; } break; case SS_STRINGPTR: { char **string = m->value; if (s->valuetype == SS_STRING) { char *sz = s->value; if (s->valuesize > 0) { sz = ss_malloc(s->r->a, s->valuesize); if (ssunlikely(sz == NULL)) return sr_oom(s->r->e); memcpy(sz, s->value, s->valuesize); } if (*string) ss_free(s->r->a, *string); *string = sz; } else { goto bad_type; } break; } default: assert(0); return -1; } return 0; bad_type: return sr_error(s->r->e, "bad meta write type (%s) for (%s) %s", ss_typeof(s->valuetype), ss_typeof(m->type), s->path); }
static inline int se_txwrite(setx *t, sev *o, uint8_t flags) { se *e = se_of(&t->o); sedb *db = se_cast(o->o.parent, sedb*, SEDB); /* validate req */ if (ssunlikely(t->t.state == SXPREPARE)) { sr_error(&e->error, "%s", "transaction is in 'prepare' state (read-only)"); goto error; } /* validate database status */ int status = se_status(&db->status); switch (status) { case SE_SHUTDOWN: if (ssunlikely(! se_dbvisible(db, t->t.id))) { sr_error(&e->error, "%s", "database is invisible for the transaction"); goto error; } break; case SE_RECOVER: case SE_ONLINE: break; default: goto error; } if (flags == SVUPDATE && !sf_updatehas(&db->scheme.fmt_update)) flags = 0; /* prepare object */ svv *v; int rc = se_dbv(db, o, 0, &v); if (ssunlikely(rc == -1)) goto error; v->flags = flags; v->log = o->log; sv vp; sv_init(&vp, &sv_vif, v, NULL); so_destroy(&o->o); /* ensure quota */ int size = sizeof(svv) + sv_size(&vp); ss_quota(&e->quota, SS_QADD, size); /* concurrent index only */ rc = sx_set(&t->t, &db->coindex, v); if (ssunlikely(rc == -1)) { ss_quota(&e->quota, SS_QREMOVE, size); return -1; } return 0; error: so_destroy(&o->o); return -1; }
int sc_backupbegin(sc *s) { /* * a. create backup_path/<bsn.incomplete> directory * b. create database directories * c. create log directory */ char path[1024]; snprintf(path, sizeof(path), "%s/%" PRIu32 ".incomplete", s->backup_path, s->backup_bsn); int rc = ss_vfsmkdir(s->r->vfs, path, 0755); if (ssunlikely(rc == -1)) { sr_error(s->r->e, "backup directory '%s' create error: %s", path, strerror(errno)); return -1; } int i = 0; while (i < s->count) { scdb *db = &s->i[i]; snprintf(path, sizeof(path), "%s/%" PRIu32 ".incomplete/%s", s->backup_path, s->backup_bsn, db->index->scheme.name); rc = ss_vfsmkdir(s->r->vfs, path, 0755); if (ssunlikely(rc == -1)) { sr_error(s->r->e, "backup directory '%s' create error: %s", path, strerror(errno)); return -1; } i++; } snprintf(path, sizeof(path), "%s/%" PRIu32 ".incomplete/log", s->backup_path, s->backup_bsn); rc = ss_vfsmkdir(s->r->vfs, path, 0755); if (ssunlikely(rc == -1)) { sr_error(s->r->e, "backup directory '%s' create error: %s", path, strerror(errno)); return -1; } ss_mutexlock(&s->lock); s->backup = 2; s->backup_in_progress = s->count; i = 0; while (i < s->count) { sc_task_backup(&s->i[i]); i++; } ss_mutexunlock(&s->lock); return 0; }
int sr_confexec(srconf *start, srconfstmt *s) { if (s->op == SR_SERIALIZE) return sr_confexec_serialize(start, s, NULL); char path[256]; snprintf(path, sizeof(path), "%s", s->path); char *ptr = NULL; char *token; token = strtok_r(path, ".", &ptr); if (ssunlikely(token == NULL)) return -1; srconf *c = start; while (c) { if (strcmp(token, c->key) != 0) { c = c->next; continue; } if (c->flags & SR_NS) { token = strtok_r(NULL, ".", &ptr); if (ssunlikely(token == NULL)) { if (s->op == SR_WRITE && c->type != SS_UNDEF) { int rc = sr_conf_write_cast(c->type, s->valuetype); if (ssunlikely(rc == -1)) goto bad_type; } s->match = c; if (c->function) return c->function(c, s); /* not supported */ goto bad_path; } c = (srconf*)c->value; continue; } s->match = c; token = strtok_r(NULL, ".", &ptr); if (ssunlikely(token != NULL)) goto bad_path; return c->function(c, s); } bad_path: return sr_error(s->r->e, "bad configuration path: %s", s->path); bad_type: return sr_error(s->r->e, "incompatible type (%s) for (%s) %s", ss_typeof(s->valuetype), ss_typeof(c->type), s->path); }
int se_scheduler_backup(void *arg) { se *e = arg; sescheduler *s = &e->sched; if (ssunlikely(e->conf.backup_path == NULL)) { sr_error(&e->error, "%s", "backup is not enabled"); return -1; } /* begin backup procedure * state 0 * * disable log garbage-collection */ sl_poolgc_enable(&e->lp, 0); ss_mutexlock(&s->lock); if (ssunlikely(s->backup > 0)) { ss_mutexunlock(&s->lock); sl_poolgc_enable(&e->lp, 1); /* in progress */ return 0; } uint64_t bsn = sr_seq(&e->seq, SR_BSNNEXT); s->backup = 1; s->backup_bsn = bsn; ss_mutexunlock(&s->lock); return 0; }
int sd_indexbegin(sdindex *i, sr *r, uint64_t offset) { int rc = sr_bufensure(&i->i, r->a, sizeof(sdindexheader)); if (srunlikely(rc == -1)) return sr_error(r->e, "%s", "memory allocation failed"); sdindexheader *h = sd_indexheader(i); sr_version(&h->version); h->crc = 0; h->size = 0; h->count = 0; h->keys = 0; h->total = 0; h->totalorigin = 0; h->extension = 0; h->lsnmin = UINT64_MAX; h->lsnmax = 0; h->tsmin = 0; h->offset = offset; h->dupkeys = 0; h->dupmin = UINT64_MAX; memset(h->reserve, 0, sizeof(h->reserve)); sd_idinit(&h->id, 0, 0, 0); i->h = NULL; sr_bufadvance(&i->i, sizeof(sdindexheader)); return 0; }
int se_document_create(sedocument *o, uint8_t flags) { sedb *db = (sedb*)o->o.parent; se *e = se_of(&db->o); assert(o->created == 0); assert(o->v == NULL); /* create document from raw data */ if (o->raw) { o->v = sv_vbuildraw(db->r, o->raw); if (ssunlikely(o->v == NULL)) return sr_oom(&e->error); o->created = 1; return 0; } /* ensure all keys are set */ if (ssunlikely(o->fields_count_keys != db->scheme->scheme.keys_count)) return sr_error(&e->error, "%s", "incomplete key"); /* set auto fields */ uint32_t timestamp = UINT32_MAX; if (db->scheme->scheme.has_timestamp) { timestamp = ss_timestamp(); sf_autoset(&db->scheme->scheme, o->fields, ×tamp); } o->v = sv_vbuild(db->r, o->fields); if (ssunlikely(o->v == NULL)) return sr_oom(&e->error); sf_flagsset(db->r->scheme, sv_vpointer(o->v), flags); o->created = 1; return 0; }
int si_recover(si *i) { sr *r = i->r; int exist = ss_vfsexists(r->vfs, i->scheme->path); if (exist == 0) goto deploy; if (i->scheme->path_fail_on_exists) { sr_error(r->e, "directory '%s' already exists", i->scheme->path); return -1; } int rc = si_recoverdrop(i, r); switch (rc) { case -1: return -1; case 1: goto deploy; } rc = si_schemerecover(i->scheme, r); if (ssunlikely(rc == -1)) return -1; r->scheme = &i->scheme->scheme; r->fmt = i->scheme->fmt; r->fmt_storage = i->scheme->fmt_storage; sdsnapshot snapshot; sd_snapshot_init(&snapshot); rc = si_recoversnapshot(i, r, &snapshot); if (ssunlikely(rc == -1)) { sd_snapshot_free(&snapshot, r); return -1; } rc = si_recoverindex(i, r, &snapshot); sd_snapshot_free(&snapshot, r); if (sslikely(rc <= 0)) return rc; deploy: return si_deploy(i, r, !exist); }
static inline int sy_recoverbackup(sy *i, sr *r) { if (i->conf->path_backup == NULL) return 0; int rc; int exists = ss_vfsexists(r->vfs, i->conf->path_backup); if (! exists) { rc = ss_vfsmkdir(r->vfs, i->conf->path_backup, 0755); if (ssunlikely(rc == -1)) { sr_error(r->e, "backup directory '%s' create error: %s", i->conf->path_backup, strerror(errno)); return -1; } } /* recover backup sequential number */ DIR *dir = opendir(i->conf->path_backup); if (ssunlikely(dir == NULL)) { sr_error(r->e, "backup directory '%s' open error: %s", i->conf->path_backup, strerror(errno)); return -1; } uint32_t bsn = 0; struct dirent *de; while ((de = readdir(dir))) { if (ssunlikely(de->d_name[0] == '.')) continue; uint32_t id = 0; rc = sy_process(de->d_name, &id); switch (rc) { case 1: case 0: if (id > bsn) bsn = id; break; case -1: /* skip unknown file */ continue; } } closedir(dir); r->seq->bsn = bsn; return 0; }
static inline int se_backupstart(sescheduler *s) { se *e = (se*)s->env; /* * a. create backup_path/<bsn.incomplete> directory * b. create database directories * c. create log directory */ char path[1024]; snprintf(path, sizeof(path), "%s/%" PRIu32 ".incomplete", e->conf.backup_path, s->backup_bsn); int rc = ss_vfsmkdir(&e->vfs, path, 0755); if (ssunlikely(rc == -1)) { sr_error(&e->error, "backup directory '%s' create error: %s", path, strerror(errno)); return -1; } int i = 0; while (i < s->count) { sedb *db = s->i[i]; snprintf(path, sizeof(path), "%s/%" PRIu32 ".incomplete/%s", e->conf.backup_path, s->backup_bsn, db->scheme.name); rc = ss_vfsmkdir(&e->vfs, path, 0755); if (ssunlikely(rc == -1)) { sr_error(&e->error, "backup directory '%s' create error: %s", path, strerror(errno)); return -1; } i++; } snprintf(path, sizeof(path), "%s/%" PRIu32 ".incomplete/log", e->conf.backup_path, s->backup_bsn); rc = ss_vfsmkdir(&e->vfs, path, 0755); if (ssunlikely(rc == -1)) { sr_error(&e->error, "backup directory '%s' create error: %s", path, strerror(errno)); return -1; } return 0; }
int sd_indexcopy(sdindex *i, sr *r, sdindexheader *h) { int size = sd_indexsize(h); int rc = sr_bufensure(&i->i, r->a, size); if (srunlikely(rc == -1)) return sr_error(r->e, "%s", "memory allocation failed"); memcpy(i->i.s, (char*)h, size); sr_bufadvance(&i->i, size); i->h = sd_indexheader(i); return 0; }
int si_recover(si *i, sr *r) { int exist = sr_fileexists(i->conf->path); if (exist == 0) return si_deploy(i, r); if (i->conf->path_fail_on_exists) { sr_error(r->e, "directory '%s' exists.", i->conf->path); return -1; } return si_recoverindex(i, r); }
static int se_deploy(se *e, sr *r) { int rc; rc = sr_filemkdir(e->conf->path); if (srunlikely(rc == -1)) { sr_error(r->e, "directory '%s' create error: %s", e->conf->path, strerror(errno)); return -1; } return 0; }
static int sy_deploy(sy *e, sr *r) { int rc; rc = ss_vfsmkdir(r->vfs, e->conf->path, 0755); if (ssunlikely(rc == -1)) { sr_error(r->e, "directory '%s' create error: %s", e->conf->path, strerror(errno)); return -1; } return 0; }
static inline int se_metav_offline(srmeta *c, srmetastmt *s) { se *e = s->ptr; if (s->op == SR_WRITE) { if (se_status(&e->status)) { sr_error(s->r->e, "write to %s is offline-only", s->path); return -1; } } return se_metav(c, s); }
static inline int se_backupcomplete(sescheduler *s, seworker *w) { /* * a. rotate log file * b. copy log files * c. enable log gc * d. rename <bsn.incomplete> into <bsn> * e. set last backup, set COMPLETE */ se *e = (se*)s->env; /* force log rotation */ ss_trace(&w->trace, "%s", "log rotation for backup"); int rc = sl_poolrotate(&e->lp); if (ssunlikely(rc == -1)) return -1; /* copy log files */ ss_trace(&w->trace, "%s", "log files backup"); char path[1024]; snprintf(path, sizeof(path), "%s/%" PRIu32 ".incomplete/log", e->conf.backup_path, s->backup_bsn); rc = sl_poolcopy(&e->lp, path, &w->dc.c); if (ssunlikely(rc == -1)) { sr_errorrecover(&e->error); return -1; } /* enable log gc */ sl_poolgc_enable(&e->lp, 1); /* complete backup */ snprintf(path, sizeof(path), "%s/%" PRIu32 ".incomplete", e->conf.backup_path, s->backup_bsn); char newpath[1024]; snprintf(newpath, sizeof(newpath), "%s/%" PRIu32, e->conf.backup_path, s->backup_bsn); rc = rename(path, newpath); if (ssunlikely(rc == -1)) { sr_error(&e->error, "backup directory '%s' rename error: %s", path, strerror(errno)); return -1; } /* complete */ s->backup_last = s->backup_bsn; s->backup_last_complete = 1; s->backup = 0; s->backup_bsn = 0; return 0; }
int sr_concat_create(struct sr_discipline *sd, struct bioc_createraid *bc, int no_chunk, int64_t coerced_size) { if (no_chunk < 2) { sr_error(sd->sd_sc, "%s requires two or more chunks", sd->sd_name); return EINVAL; } return sr_concat_init(sd); }
int sc_backupend(sc *s, scworker *w) { /* * a. rotate log file * b. copy log files * c. enable log gc * d. rename <bsn.incomplete> into <bsn> * e. set last backup, set COMPLETE */ /* force log rotation */ ss_trace(&w->trace, "%s", "log rotation for backup"); int rc = sl_poolrotate(s->lp); if (ssunlikely(rc == -1)) return -1; /* copy log files */ ss_trace(&w->trace, "%s", "log files backup"); char path[1024]; snprintf(path, sizeof(path), "%s/%" PRIu32 ".incomplete/log", s->backup_path, s->backup_bsn); rc = sl_poolcopy(s->lp, path, &w->dc.c); if (ssunlikely(rc == -1)) return -1; /* complete backup */ snprintf(path, sizeof(path), "%s/%" PRIu32 ".incomplete", s->backup_path, s->backup_bsn); char newpath[1024]; snprintf(newpath, sizeof(newpath), "%s/%" PRIu32, s->backup_path, s->backup_bsn); rc = ss_vfsrename(s->r->vfs, path, newpath); if (ssunlikely(rc == -1)) { sr_error(s->r->e, "backup directory '%s' rename error: %s", path, strerror(errno)); return -1; } /* enable log gc */ sl_poolgc_enable(s->lp, 1); /* complete */ ss_mutexlock(&s->lock); s->backup_bsn_last = s->backup_bsn; s->backup_bsn_last_complete = 1; s->backup_in_progress = 0; s->backup = 0; s->backup_bsn = 0; ss_mutexunlock(&s->lock); return 0; }
static inline void sp_error_unsupported_method(soobj *o, const char *method, ...) { assert(o->env != NULL); assert(o->env->id == SOENV); va_list args; va_start(args, method); so *e = (so*)o->env; sr_error(&e->error, "unsupported %s(%s) operation", (char*)method, (char*)o->i->type(o, args)); va_end(args); }
int sr_cexecv(src *start, srcstmt *stmt, va_list args) { if (stmt->op == SR_CSERIALIZE) return sr_cserializer(start, stmt, NULL, args); char path[256]; snprintf(path, sizeof(path), "%s", stmt->path); char *ptr = NULL; char *token; token = strtok_r(path, ".", &ptr); if (srunlikely(token == NULL)) return -1; src *c = start; while (c) { if (strcmp(token, c->name) != 0) { c = c->next; continue; } int type = c->flags & ~SR_CRO; switch (type) { case SR_CU32: case SR_CU64: case SR_CSZREF: case SR_CSZ: case SR_CVOID: token = strtok_r(NULL, ".", &ptr); if (srunlikely(token != NULL)) goto error; return c->function(c, stmt, args); case SR_CC: token = strtok_r(NULL, ".", &ptr); if (srunlikely(token == NULL)) { if (c->function) return c->function(c, stmt, args); /* not supported */ goto error; } c = (src*)c->value; continue; } assert(0); } error: sr_error(stmt->r->e, "bad ctl path: %s", stmt->path); return -1; }
int sy_open(sy *e, sr *r, syconf *conf) { e->conf = conf; int rc = sy_recoverbackup(e, r); if (ssunlikely(rc == -1)) return -1; int exists = ss_fileexists(conf->path); if (exists == 0) { if (ssunlikely(! conf->path_create)) { sr_error(r->e, "directory '%s' does not exist", conf->path); return -1; } return sy_deploy(e, r); } return 0; }
int sd_indexcommit(sdindex *i, sr *r, sdid *id) { int size = sr_bufused(&i->v); int rc = sr_bufensure(&i->i, r->a, size); if (srunlikely(rc == -1)) { sr_error(r->e, "%s", "memory allocation failed"); return -1; } memcpy(i->i.p, i->v.s, size); sr_bufadvance(&i->i, size); sr_buffree(&i->v, r->a); i->h = sd_indexheader(i); i->h->id = *id; i->h->crc = sr_crcs(r->crc, i->h, sizeof(sdindexheader), 0); return 0; }
int sr_raid0_init(struct sr_discipline *sd) { /* Initialise runtime values. */ sd->mds.mdd_raid0.sr0_strip_bits = sr_validate_stripsize(sd->sd_meta->ssdi.ssd_strip_size); if (sd->mds.mdd_raid0.sr0_strip_bits == -1) { sr_error(sd->sd_sc, "invalid strip size", sd->sd_name); return EINVAL; } sd->sd_max_ccb_per_wu = (MAXPHYS / sd->sd_meta->ssdi.ssd_strip_size + 1) * SR_RAID0_NOWU * sd->sd_meta->ssdi.ssd_chunk_no; return 0; }
static void* so_snapshotget(srobj *o, va_list args) { sosnapshot *s = (sosnapshot*)o; so *e = so_of(o); va_list va; va_copy(va, args); sov *v = va_arg(va, sov*); va_end(va); if (ssunlikely(v->o.id != SOV)) { sr_error(&e->error, "%s", "bad arguments"); return NULL; } sodb *db = (sodb*)v->parent; return so_txdbget(db, 0, s->vlsn, 0, args); }
static inline int se_txwrite(setx *t, sedocument *o, uint8_t flags) { se *e = se_of(&t->o); sedb *db = se_cast(o->o.parent, sedb*, SEDB); int auto_close = o->created <= 1; /* validate database status */ if (ssunlikely(! se_active(e))) goto error; /* ensure memory quota */ int rc; rc = sr_quota(&e->quota, &e->stat); if (ssunlikely(rc)) { sr_error(&e->error, "%s", "memory quota limit reached"); goto error; } /* create document */ rc = se_document_create(o); if (ssunlikely(rc == -1)) goto error; rc = se_document_validate(o, &db->o, flags); if (ssunlikely(rc == -1)) goto error; svv *v = o->v.v; sv_vref(v); v->log = o->log; /* destroy document object */ if (auto_close) so_destroy(&o->o); /* concurrent index only */ rc = sx_set(&t->t, &db->coindex, v); if (ssunlikely(rc == -1)) return -1; return 0; error: if (auto_close) so_destroy(&o->o); return -1; }
static void* so_snapshotcursor(srobj *o, va_list args) { sosnapshot *s = (sosnapshot*)o; so *e = so_of(o); va_list va; va_copy(va, args); sov *v = va_arg(va, sov*); va_end(va); if (ssunlikely(v->o.id != SOV)) goto error; if (ssunlikely(v->parent == NULL || v->parent->id != SODB)) goto error; sodb *db = (sodb*)v->parent; return so_cursornew(db, s->vlsn, 0, args); error: sr_error(&e->error, "%s", "bad arguments"); return NULL; }
int se_document_createkey(sedocument *o) { sedb *db = (sedb*)o->o.parent; se *e = se_of(&db->o); if (o->created) return 0; assert(o->v == NULL); /* set prefix */ if (o->prefix) { if (db->scheme->scheme.keys[0]->type != SS_STRING) return sr_error(&e->error, "%s", "prefix search is only " "supported for a string key"); void *copy = ss_malloc(&e->a, o->prefix_size); if (ssunlikely(copy == NULL)) return sr_oom(&e->error); memcpy(copy, o->prefix, o->prefix_size); o->prefix_copy = copy; } /* set unspecified min/max keys, depending on * iteration order */ if (ssunlikely(o->fields_count_keys != db->scheme->scheme.keys_count)) { if (o->prefix && o->fields_count_keys == 0) { memset(o->fields, 0, sizeof(o->fields)); o->fields[0].pointer = o->prefix; o->fields[0].size = o->prefix_size; } sf_limitapply(&db->limit, &db->scheme->scheme, o->fields, o->order); o->fields_count = db->scheme->scheme.fields_count; o->fields_count_keys = db->scheme->scheme.keys_count; } o->v = sv_vbuild(db->r, o->fields); if (ssunlikely(o->v == NULL)) return sr_oom(&e->error); sf_flagsset(db->r->scheme, sv_vpointer(o->v), SVGET); o->created = 1; return 0; }