2 * Copyright (C) 2018 NetDEF, Inc.
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 #include "lib_errors.h"
27 #include "northbound.h"
28 #include "northbound_db.h"
32 #ifdef HAVE_CONFIG_ROLLBACKS
34 * NOTE: the delete_tail SQL trigger is used to implement a ring buffer
35 * where only the last N transactions are recorded in the configuration
39 "BEGIN TRANSACTION;\n"
40 " CREATE TABLE IF NOT EXISTS transactions(\n"
41 " client CHAR(32) NOT NULL,\n"
42 " date DATETIME DEFAULT CURRENT_TIMESTAMP,\n"
43 " comment CHAR(80) ,\n"
44 " configuration TEXT NOT NULL\n"
46 " CREATE TRIGGER IF NOT EXISTS delete_tail\n"
47 " AFTER INSERT ON transactions\n"
54 " rowid%%%u=NEW.rowid%%%u AND rowid!=NEW.rowid;\n"
57 NB_DLFT_MAX_CONFIG_ROLLBACKS
, NB_DLFT_MAX_CONFIG_ROLLBACKS
)
60 #endif /* HAVE_CONFIG_ROLLBACKS */
65 int nb_db_transaction_save(const struct nb_transaction
*transaction
,
66 uint32_t *transaction_id
)
68 #ifdef HAVE_CONFIG_ROLLBACKS
69 struct sqlite3_stmt
*ss
;
70 const char *client_name
;
71 char *config_str
= NULL
;
75 * Use a transaction to ensure consistency between the INSERT and SELECT
78 if (db_execute("BEGIN TRANSACTION;") != 0)
82 "INSERT INTO transactions\n"
83 " (client, comment, configuration)\n"
89 client_name
= nb_client_name(transaction
->client
);
90 /* Always record configurations in the XML format. */
91 if (lyd_print_mem(&config_str
, transaction
->config
->dnode
, LYD_XML
,
92 LYP_FORMAT
| LYP_WITHSIBLINGS
)
96 if (db_bindf(ss
, "%s%s%s", client_name
, strlen(client_name
),
97 transaction
->comment
, strlen(transaction
->comment
),
98 config_str
? config_str
: "",
99 config_str
? strlen(config_str
) : 0)
103 if (db_run(ss
) != SQLITE_OK
)
109 * transaction_id is an optional output parameter that provides the ID
110 * of the recorded transaction.
112 if (transaction_id
) {
113 ss
= db_prepare("SELECT last_insert_rowid();");
117 if (db_run(ss
) != SQLITE_ROW
)
120 if (db_loadf(ss
, "%i", transaction_id
) != 0)
126 if (db_execute("COMMIT;") != 0)
137 (void)db_execute("ROLLBACK TRANSACTION;");
140 #else /* HAVE_CONFIG_ROLLBACKS */
142 #endif /* HAVE_CONFIG_ROLLBACKS */
145 struct nb_config
*nb_db_transaction_load(uint32_t transaction_id
)
147 struct nb_config
*config
= NULL
;
148 #ifdef HAVE_CONFIG_ROLLBACKS
149 struct lyd_node
*dnode
;
150 const char *config_str
;
151 struct sqlite3_stmt
*ss
;
163 if (db_bindf(ss
, "%d", transaction_id
) != 0)
166 if (db_run(ss
) != SQLITE_ROW
)
169 if (db_loadf(ss
, "%s", &config_str
) != 0)
172 dnode
= lyd_parse_mem(ly_native_ctx
, config_str
, LYD_XML
,
175 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_parse_mem() failed",
178 config
= nb_config_new(dnode
);
182 #endif /* HAVE_CONFIG_ROLLBACKS */
187 int nb_db_clear_transactions(unsigned int n_oldest
)
189 #ifdef HAVE_CONFIG_ROLLBACKS
190 /* Delete oldest N entries. */
191 if (db_execute("DELETE\n"
200 " ORDER BY ROWID ASC LIMIT %u\n"
205 #endif /* HAVE_CONFIG_ROLLBACKS */
210 int nb_db_set_max_transactions(unsigned int max
)
212 #ifdef HAVE_CONFIG_ROLLBACKS
214 * Delete old entries if necessary and update the SQL trigger that
215 * auto-deletes old entries.
217 if (db_execute("BEGIN TRANSACTION;\n"
227 " ORDER BY ROWID DESC LIMIT -1 OFFSET %u\n"
229 " DROP TRIGGER delete_tail;\n"
230 " CREATE TRIGGER delete_tail\n"
231 " AFTER INSERT ON transactions\n"
238 " rowid%%%u=NEW.rowid%%%u AND rowid!=NEW.rowid;\n"
244 #endif /* HAVE_CONFIG_ROLLBACKS */
249 int nb_db_transactions_iterate(void (*func
)(void *arg
, int transaction_id
,
250 const char *client_name
,
252 const char *comment
),
255 #ifdef HAVE_CONFIG_ROLLBACKS
256 struct sqlite3_stmt
*ss
;
258 /* Send SQL query and parse the result. */
261 " rowid, client, date, comment\n"
269 while (db_run(ss
) == SQLITE_ROW
) {
271 const char *client_name
;
276 ret
= db_loadf(ss
, "%i%s%s%s", &transaction_id
, &client_name
,
281 (*func
)(arg
, transaction_id
, client_name
, date
, comment
);
285 #endif /* HAVE_CONFIG_ROLLBACKS */