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
->context
->client
);
91 * Always record configurations in the XML format, save the default
92 * values too, as this covers the case where defaults may change.
94 if (lyd_print_mem(&config_str
, transaction
->config
->dnode
, LYD_XML
,
95 LYD_PRINT_WITHSIBLINGS
| LYD_PRINT_WD_ALL
)
99 if (db_bindf(ss
, "%s%s%s", client_name
, strlen(client_name
),
100 transaction
->comment
, strlen(transaction
->comment
),
101 config_str
? config_str
: "",
102 config_str
? strlen(config_str
) : 0)
106 if (db_run(ss
) != SQLITE_OK
)
112 * transaction_id is an optional output parameter that provides the ID
113 * of the recorded transaction.
115 if (transaction_id
) {
116 ss
= db_prepare("SELECT last_insert_rowid();");
120 if (db_run(ss
) != SQLITE_ROW
)
123 if (db_loadf(ss
, "%i", transaction_id
) != 0)
129 if (db_execute("COMMIT;") != 0)
140 (void)db_execute("ROLLBACK TRANSACTION;");
143 #else /* HAVE_CONFIG_ROLLBACKS */
145 #endif /* HAVE_CONFIG_ROLLBACKS */
148 struct nb_config
*nb_db_transaction_load(uint32_t transaction_id
)
150 struct nb_config
*config
= NULL
;
151 #ifdef HAVE_CONFIG_ROLLBACKS
152 struct lyd_node
*dnode
;
153 const char *config_str
;
154 struct sqlite3_stmt
*ss
;
167 if (db_bindf(ss
, "%d", transaction_id
) != 0)
170 if (db_run(ss
) != SQLITE_ROW
)
173 if (db_loadf(ss
, "%s", &config_str
) != 0)
176 err
= lyd_parse_data_mem(ly_native_ctx
, config_str
, LYD_XML
,
177 LYD_PARSE_STRICT
| LYD_PARSE_NO_STATE
,
178 LYD_VALIDATE_NO_STATE
, &dnode
);
180 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_parse_data_mem() failed",
183 config
= nb_config_new(dnode
);
187 #endif /* HAVE_CONFIG_ROLLBACKS */
192 int nb_db_clear_transactions(unsigned int n_oldest
)
194 #ifdef HAVE_CONFIG_ROLLBACKS
195 /* Delete oldest N entries. */
196 if (db_execute("DELETE\n"
205 " ORDER BY ROWID ASC LIMIT %u\n"
210 #endif /* HAVE_CONFIG_ROLLBACKS */
215 int nb_db_set_max_transactions(unsigned int max
)
217 #ifdef HAVE_CONFIG_ROLLBACKS
219 * Delete old entries if necessary and update the SQL trigger that
220 * auto-deletes old entries.
222 if (db_execute("BEGIN TRANSACTION;\n"
232 " ORDER BY ROWID DESC LIMIT -1 OFFSET %u\n"
234 " DROP TRIGGER delete_tail;\n"
235 " CREATE TRIGGER delete_tail\n"
236 " AFTER INSERT ON transactions\n"
243 " rowid%%%u=NEW.rowid%%%u AND rowid!=NEW.rowid;\n"
249 #endif /* HAVE_CONFIG_ROLLBACKS */
254 int nb_db_transactions_iterate(void (*func
)(void *arg
, int transaction_id
,
255 const char *client_name
,
257 const char *comment
),
260 #ifdef HAVE_CONFIG_ROLLBACKS
261 struct sqlite3_stmt
*ss
;
263 /* Send SQL query and parse the result. */
266 " rowid, client, date, comment\n"
274 while (db_run(ss
) == SQLITE_ROW
) {
276 const char *client_name
;
281 ret
= db_loadf(ss
, "%i%s%s%s", &transaction_id
, &client_name
,
286 (*func
)(arg
, transaction_id
, client_name
, date
, comment
);
290 #endif /* HAVE_CONFIG_ROLLBACKS */