+ mdb->field_number = field;
+}
+
+/*
+ * Note, if this routine returns 1 (failure), Bacula expects
+ * that no result has been stored.
+ * This is where QUERY_DB comes with Postgresql.
+ *
+ * Returns: 0 on success
+ * 1 on failure
+ *
+ */
+int my_postgresql_query(B_DB *mdb, const char *query)
+{
+ Dmsg0(500, "my_postgresql_query started\n");
+ // We are starting a new query. reset everything.
+ mdb->num_rows = -1;
+ mdb->row_number = -1;
+ mdb->field_number = -1;
+
+ if (mdb->result) {
+ PQclear(mdb->result); /* hmm, someone forgot to free?? */
+ mdb->result = NULL;
+ }
+
+ Dmsg1(500, "my_postgresql_query starts with '%s'\n", query);
+
+ for (int i=0; i < 10; i++) {
+ mdb->result = PQexec(mdb->db, query);
+ if (mdb->result) {
+ break;
+ }
+ bmicrosleep(5, 0);
+ }
+ if (!mdb->result) {
+ Dmsg1(50, "Query failed: %s\n", query);
+ goto bail_out;
+ }
+
+ mdb->status = PQresultStatus(mdb->result);
+ if (mdb->status == PGRES_TUPLES_OK || mdb->status == PGRES_COMMAND_OK) {
+ Dmsg1(500, "we have a result\n", query);
+
+ // how many fields in the set?
+ mdb->num_fields = (int)PQnfields(mdb->result);
+ Dmsg1(500, "we have %d fields\n", mdb->num_fields);
+
+ mdb->num_rows = PQntuples(mdb->result);
+ Dmsg1(500, "we have %d rows\n", mdb->num_rows);
+
+ mdb->status = 0; /* succeed */
+ } else {
+ Dmsg1(50, "Result status failed: %s\n", query);
+ goto bail_out;
+ }
+
+ Dmsg0(500, "my_postgresql_query finishing\n");
+ return mdb->status;
+
+bail_out:
+ Dmsg1(500, "we failed\n", query);
+ PQclear(mdb->result);
+ mdb->result = NULL;
+ mdb->status = 1; /* failed */
+ return mdb->status;
+}
+
+void my_postgresql_free_result(B_DB *mdb)
+{
+
+ db_lock(mdb);
+ if (mdb->result) {
+ PQclear(mdb->result);
+ mdb->result = NULL;
+ }
+
+ if (mdb->row) {
+ free(mdb->row);
+ mdb->row = NULL;
+ }
+
+ if (mdb->fields) {
+ free(mdb->fields);
+ mdb->fields = NULL;
+ }
+ db_unlock(mdb);
+}
+
+int my_postgresql_currval(B_DB *mdb, char *table_name)
+{
+ // Obtain the current value of the sequence that
+ // provides the serial value for primary key of the table.
+
+ // currval is local to our session. It is not affected by
+ // other transactions.
+
+ // Determine the name of the sequence.
+ // PostgreSQL automatically creates a sequence using
+ // <table>_<column>_seq.
+ // At the time of writing, all tables used this format for
+ // for their primary key: <table>id
+ // Except for basefiles which has a primary key on baseid.
+ // Therefore, we need to special case that one table.
+
+ // everything else can use the PostgreSQL formula.
+
+ char sequence[NAMEDATALEN-1];
+ char query [NAMEDATALEN+50];
+ PGresult *result;
+ int id = 0;
+
+ if (strcasecmp(table_name, "basefiles") == 0) {
+ bstrncpy(sequence, "basefiles_baseid", sizeof(sequence));
+ } else {
+ bstrncpy(sequence, table_name, sizeof(sequence));
+ bstrncat(sequence, "_", sizeof(sequence));
+ bstrncat(sequence, table_name, sizeof(sequence));
+ bstrncat(sequence, "id", sizeof(sequence));
+ }
+
+ bstrncat(sequence, "_seq", sizeof(sequence));
+ bsnprintf(query, sizeof(query), "SELECT currval('%s')", sequence);
+
+ Dmsg1(500, "my_postgresql_currval invoked with '%s'\n", query);
+ for (int i=0; i < 10; i++) {
+ result = PQexec(mdb->db, query);
+ if (result) {
+ break;
+ }
+ bmicrosleep(5, 0);
+ }
+ if (!result) {
+ Dmsg1(50, "Query failed: %s\n", query);
+ goto bail_out;
+ }
+
+ Dmsg0(500, "exec done");
+
+ if (PQresultStatus(result) == PGRES_TUPLES_OK) {
+ Dmsg0(500, "getting value");
+ id = atoi(PQgetvalue(result, 0, 0));
+ Dmsg2(500, "got value '%s' which became %d\n", PQgetvalue(result, 0, 0), id);
+ } else {
+ Dmsg1(50, "Result status failed: %s\n", query);
+ Mmsg1(&mdb->errmsg, _("error fetching currval: %s\n"), PQerrorMessage(mdb->db));
+ }
+
+bail_out:
+ PQclear(result);
+
+ return id;