+ ASSERTZH;
+ zebra_select_default_database(zh);
+ if (!zh->res)
+ {
+ zebra_setError(zh, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR,
+ "zebra_begin_trans: no database selected");
+ return ZEBRA_FAIL;
+ }
+ ASSERTZHRES;
+ yaz_log(log_level, "zebra_begin_trans rw=%d",rw);
+
+ if (zh->user_perm)
+ {
+ if (rw && !strchr(zh->user_perm, 'w'))
+ {
+ zebra_setError(
+ zh,
+ YAZ_BIB1_ES_PERMISSION_DENIED_ON_ES_CANNOT_MODIFY_OR_DELETE,
+ 0);
+ return ZEBRA_FAIL;
+ }
+ }
+
+ assert (zh->res);
+ if (rw)
+ {
+ int pass;
+ int seqno = 0;
+ char val = '?';
+ const char *rval = 0;
+
+ (zh->trans_no++);
+ if (zh->trans_w_no)
+ {
+ read_res_for_transaction(zh);
+ return 0;
+ }
+ if (zh->trans_no != 1)
+ {
+ zebra_setError(zh, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR,
+ "zebra_begin_trans: no write trans within read");
+ return ZEBRA_FAIL;
+ }
+ if (zh->reg)
+ {
+ resultSetInvalidate (zh);
+ zebra_register_close (zh->service, zh->reg);
+ }
+ zh->trans_w_no = zh->trans_no;
+
+ zebra_clearError(zh);
+
+ zh->records_inserted = 0;
+ zh->records_updated = 0;
+ zh->records_deleted = 0;
+ zh->records_processed = 0;
+
+#if HAVE_SYS_TIMES_H
+ times (&zh->tms1);
+#endif
+ /* lock */
+ if (zh->shadow_enable)
+ rval = res_get (zh->res, "shadow");
+
+ for (pass = 0; pass < 2; pass++)
+ {
+ if (rval)
+ {
+ zebra_lock_r (zh->lock_normal);
+ zebra_lock_w (zh->lock_shadow);
+ }
+ else
+ {
+ zebra_lock_w (zh->lock_normal);
+ zebra_lock_w (zh->lock_shadow);
+ }
+
+ zebra_get_state (zh, &val, &seqno);
+ if (val == 'c')
+ {
+ yaz_log (YLOG_WARN, "previous transaction didn't finish commit");
+ zebra_unlock (zh->lock_shadow);
+ zebra_unlock (zh->lock_normal);
+ zebra_commit (zh);
+ continue;
+ }
+ else if (val == 'd')
+ {
+ if (rval)
+ {
+ BFiles bfs = bfs_create (res_get (zh->res, "shadow"),
+ zh->path_reg);
+ yaz_log (YLOG_WARN, "previous transaction didn't reach commit");
+ bf_commitClean (bfs, rval);
+ bfs_destroy (bfs);
+ }
+ else
+ {
+ yaz_log (YLOG_WARN, "your previous transaction didn't finish");
+ }
+ }
+ break;
+ }
+ if (pass == 2)
+ {
+ yaz_log (YLOG_FATAL, "zebra_begin_trans couldn't finish commit");
+ abort();
+ return ZEBRA_FAIL;
+ }
+ zebra_set_state (zh, 'd', seqno);
+
+ zh->reg = zebra_register_open(zh->service, zh->reg_name,
+ 1, rval ? 1 : 0, zh->res,
+ zh->path_reg);
+ if (zh->reg)
+ zh->reg->seqno = seqno;
+ else
+ {
+ zebra_set_state (zh, 'o', seqno);
+
+ zebra_unlock (zh->lock_shadow);
+ zebra_unlock (zh->lock_normal);
+
+ zh->trans_no--;
+ zh->trans_w_no = 0;
+
+ zebra_setError(zh, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR,
+ "zebra_begin_trans: cannot open register");
+ yaz_log(YLOG_FATAL, "%s", zh->errString);
+ return ZEBRA_FAIL;
+ }
+ }
+ else
+ {
+ int dirty = 0;
+ char val;
+ int seqno;
+
+ (zh->trans_no)++;
+
+ if (zh->trans_no != 1)
+ {
+ zebra_flush_reg (zh);
+ return ZEBRA_OK;
+ }
+ zebra_clearError(zh);
+#if HAVE_SYS_TIMES_H
+ times (&zh->tms1);
+#endif
+ if (!zh->res)
+ {
+ (zh->trans_no)--;
+ zh->errCode = YAZ_BIB1_DATABASE_UNAVAILABLE;
+ return ZEBRA_FAIL;
+ }
+ if (!zh->lock_normal || !zh->lock_shadow)
+ {
+ (zh->trans_no)--;
+ zh->errCode = YAZ_BIB1_TEMPORARY_SYSTEM_ERROR;
+ return ZEBRA_FAIL;
+ }
+ zebra_get_state (zh, &val, &seqno);
+ if (val == 'd')
+ val = 'o';
+
+ if (!zh->reg)
+ dirty = 1;
+ else if (seqno != zh->reg->seqno)
+ {
+ yaz_log (YLOG_DEBUG, "reopen seqno cur/old %d/%d",
+ seqno, zh->reg->seqno);
+ dirty = 1;
+ }
+ else if (zh->reg->last_val != val)
+ {
+ yaz_log (YLOG_DEBUG, "reopen last cur/old %d/%d",
+ val, zh->reg->last_val);
+ dirty = 1;
+ }
+ if (!dirty)
+ return ZEBRA_OK;
+
+ if (val == 'c')
+ zebra_lock_r (zh->lock_shadow);
+ else
+ zebra_lock_r (zh->lock_normal);
+
+ if (zh->reg)
+ {
+ resultSetInvalidate (zh);
+ zebra_register_close (zh->service, zh->reg);
+ }
+ zh->reg = zebra_register_open(zh->service, zh->reg_name,
+ 0, val == 'c' ? 1 : 0,
+ zh->res, zh->path_reg);
+ if (!zh->reg)
+ {
+ zebra_unlock (zh->lock_normal);
+ zebra_unlock (zh->lock_shadow);
+ zh->trans_no--;
+ zh->errCode = YAZ_BIB1_DATABASE_UNAVAILABLE;
+ return ZEBRA_FAIL;
+ }
+ zh->reg->last_val = val;
+ zh->reg->seqno = seqno;
+ }
+ read_res_for_transaction(zh);
+ return ZEBRA_OK;
+}
+
+ZEBRA_RES zebra_end_trans (ZebraHandle zh)
+{
+ ZebraTransactionStatus dummy;
+ ASSERTZH;
+ yaz_log(log_level, "zebra_end_trans");
+ return zebra_end_transaction(zh, &dummy);
+}
+
+ZEBRA_RES zebra_end_transaction (ZebraHandle zh, ZebraTransactionStatus *status)
+{
+ char val;
+ int seqno;
+ const char *rval;
+
+ ASSERTZH;
+ assert(status);
+ yaz_log(log_level, "zebra_end_transaction");
+
+ status->processed = 0;
+ status->inserted = 0;
+ status->updated = 0;
+ status->deleted = 0;
+ status->utime = 0;
+ status->stime = 0;
+
+ if (!zh->res || !zh->reg)
+ {
+ zebra_setError(zh, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR,
+ "zebra_end_trans: no open transaction");
+ return ZEBRA_FAIL;
+ }
+ if (zh->trans_no != zh->trans_w_no)
+ {
+ zh->trans_no--;
+ if (zh->trans_no != 0)
+ return ZEBRA_OK;
+
+ /* release read lock */
+
+ zebra_unlock (zh->lock_normal);
+ zebra_unlock (zh->lock_shadow);
+ }
+ else
+ { /* release write lock */
+ zh->trans_no--;
+ zh->trans_w_no = 0;
+
+ yaz_log (YLOG_DEBUG, "zebra_end_trans");
+ rval = res_get (zh->res, "shadow");
+
+ zebraExplain_runNumberIncrement (zh->reg->zei, 1);
+
+ zebra_flush_reg (zh);
+
+ resultSetInvalidate (zh);
+
+ zebra_register_close (zh->service, zh->reg);
+ zh->reg = 0;
+
+ yaz_log (YLOG_LOG, "Records: "ZINT_FORMAT" i/u/d "
+ ZINT_FORMAT"/"ZINT_FORMAT"/"ZINT_FORMAT,
+ zh->records_processed, zh->records_inserted,
+ zh->records_updated, zh->records_deleted);
+
+ status->processed = (int) zh->records_processed;
+ status->inserted = (int) zh->records_inserted;
+ status->updated = (int) zh->records_updated;
+ status->deleted = (int) zh->records_deleted;
+
+ zebra_get_state (zh, &val, &seqno);
+ if (val != 'd')
+ {
+ BFiles bfs = bfs_create (rval, zh->path_reg);
+ yaz_log (YLOG_DEBUG, "deleting shadow val=%c", val);
+ bf_commitClean (bfs, rval);
+ bfs_destroy (bfs);
+ }
+ if (!rval)
+ seqno++;
+ zebra_set_state (zh, 'o', seqno);
+
+ zebra_unlock (zh->lock_shadow);
+ zebra_unlock (zh->lock_normal);
+
+ }
+#if HAVE_SYS_TIMES_H
+ times (&zh->tms2);
+ yaz_log (log_level, "user/system: %ld/%ld",
+ (long) (zh->tms2.tms_utime - zh->tms1.tms_utime),
+ (long) (zh->tms2.tms_stime - zh->tms1.tms_stime));
+
+ status->utime = (long) (zh->tms2.tms_utime - zh->tms1.tms_utime);
+ status->stime = (long) (zh->tms2.tms_stime - zh->tms1.tms_stime);
+#endif
+ return ZEBRA_OK;
+}
+
+int zebra_repository_update (ZebraHandle zh, const char *path)
+{
+ ASSERTZH;
+ assert(path);
+ zebra_clearError(zh);
+ yaz_log (log_level, "updating %s", path);
+ repositoryUpdate (zh, path);
+ return zh->errCode;
+}
+
+int zebra_repository_delete (ZebraHandle zh, const char *path)
+{
+ ASSERTZH;
+ assert(path);
+ zebra_clearError(zh);
+ yaz_log (log_level, "deleting %s", path);
+ repositoryDelete (zh, path);
+ return zh->errCode;
+}
+
+int zebra_repository_show (ZebraHandle zh, const char *path)
+{
+ ASSERTZH;
+ assert(path);
+ yaz_log(log_level, "zebra_repository_show");
+ zebra_clearError(zh);
+ repositoryShow (zh, path);
+ return zh->errCode;
+}
+
+static int zebra_commit_ex(ZebraHandle zh, int clean_only)
+{
+ int seqno;
+ char val;
+ const char *rval;
+ BFiles bfs;
+ ASSERTZH;
+ zebra_clearError(zh);
+
+ zebra_select_default_database(zh);
+ if (!zh->res)
+ {
+ zh->errCode = YAZ_BIB1_DATABASE_UNAVAILABLE;
+ return -1;
+ }
+ rval = res_get (zh->res, "shadow");
+ if (!rval)
+ {
+ yaz_log (YLOG_WARN, "Cannot perform commit - No shadow area defined");
+ return 0;
+ }
+
+ zebra_lock_w (zh->lock_normal);
+ zebra_lock_r (zh->lock_shadow);
+
+ bfs = bfs_create (res_get (zh->res, "register"), zh->path_reg);
+
+ zebra_get_state (zh, &val, &seqno);
+
+ if (rval && *rval)
+ bf_cache (bfs, rval);
+ if (bf_commitExists (bfs))
+ {
+ if (clean_only)
+ zebra_set_state (zh, 'd', seqno);
+ else
+ {
+ zebra_set_state (zh, 'c', seqno);
+
+ yaz_log (YLOG_DEBUG, "commit start");
+ bf_commitExec (bfs);
+#ifndef WIN32
+ sync ();
+#endif
+ }
+ yaz_log (YLOG_DEBUG, "commit clean");
+ bf_commitClean (bfs, rval);
+ seqno++;
+ zebra_set_state (zh, 'o', seqno);
+ }
+ else
+ {
+ yaz_log (log_level, "nothing to commit");
+ }
+ bfs_destroy (bfs);
+
+ zebra_unlock (zh->lock_shadow);
+ zebra_unlock (zh->lock_normal);
+ return 0;
+}
+
+ZEBRA_RES zebra_clean(ZebraHandle zh)
+{
+ ASSERTZH;
+ yaz_log(log_level, "zebra_clean");
+ return zebra_commit_ex(zh, 1);
+}
+
+ZEBRA_RES zebra_commit(ZebraHandle zh)
+{
+ ASSERTZH;
+ yaz_log(log_level, "zebra_commit");
+ return zebra_commit_ex(zh, 0);
+}
+
+ZEBRA_RES zebra_init(ZebraHandle zh)
+{
+ const char *rval;
+ BFiles bfs = 0;
+ ASSERTZH;
+ yaz_log(log_level, "zebra_init");
+ zebra_clearError(zh);
+
+ zebra_select_default_database(zh);
+ if (!zh->res)
+ {
+ zebra_setError(zh, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR,
+ "cannot select default database");
+ return ZEBRA_FAIL;
+ }
+ rval = res_get (zh->res, "shadow");
+
+ bfs = bfs_create (res_get (zh->res, "register"), zh->path_reg);
+ if (!bfs)
+ {
+ zebra_setError(zh, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR, "bfs_create");
+ return ZEBRA_FAIL;
+ }
+ if (rval && *rval)
+ bf_cache (bfs, rval);
+
+ bf_reset (bfs);
+ bfs_destroy (bfs);
+ zebra_set_state (zh, 'o', 0);
+ return ZEBRA_OK;
+}
+
+ZEBRA_RES zebra_compact(ZebraHandle zh)
+{
+ BFiles bfs;
+ ASSERTZH;
+ yaz_log(log_level, "zebra_compact");
+ zebra_clearError(zh);
+ if (!zh->res)
+ {
+ zh->errCode = YAZ_BIB1_DATABASE_UNAVAILABLE;
+ return ZEBRA_FAIL;
+ }
+ bfs = bfs_create (res_get (zh->res, "register"), zh->path_reg);
+ inv_compact (bfs);
+ bfs_destroy (bfs);
+ return ZEBRA_OK;
+}
+
+void zebra_result(ZebraHandle zh, int *code, char **addinfo)
+{
+ ASSERTZH;
+ yaz_log(log_level, "zebra_result");
+ *code = zh->errCode;
+ *addinfo = zh->errString;
+}
+
+void zebra_shadow_enable(ZebraHandle zh, int value)
+{
+ ASSERTZH;
+ yaz_log(log_level, "zebra_shadow_enable");
+ zebra_clearError(zh);
+ zh->shadow_enable = value;
+}
+
+ZEBRA_RES zebra_octet_term_encoding(ZebraHandle zh, const char *encoding)
+{
+ ASSERTZH;
+ assert(encoding);
+ yaz_log(log_level, "zebra_octet_term_encoding %s", encoding);
+ zebra_clearError(zh);
+
+ if (zh->iconv_to_utf8 != 0)
+ yaz_iconv_close(zh->iconv_to_utf8);
+ if (zh->iconv_from_utf8 != 0)
+ yaz_iconv_close(zh->iconv_from_utf8);
+
+ zh->iconv_to_utf8 =
+ yaz_iconv_open ("UTF-8", encoding);
+ if (zh->iconv_to_utf8 == 0)
+ yaz_log (YLOG_WARN, "iconv: %s to UTF-8 unsupported", encoding);
+ zh->iconv_from_utf8 =
+ yaz_iconv_open (encoding, "UTF-8");
+ if (zh->iconv_to_utf8 == 0)
+ yaz_log (YLOG_WARN, "iconv: UTF-8 to %s unsupported", encoding);
+
+ return ZEBRA_OK;
+}