1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2018 NetDEF, Inc.
11 #include "lib_errors.h"
14 #include "northbound.h"
15 #include "northbound_db.h"
19 #ifdef HAVE_CONFIG_ROLLBACKS
21 * NOTE: the delete_tail SQL trigger is used to implement a ring buffer
22 * where only the last N transactions are recorded in the configuration
26 "BEGIN TRANSACTION;\n"
27 " CREATE TABLE IF NOT EXISTS transactions(\n"
28 " client CHAR(32) NOT NULL,\n"
29 " date DATETIME DEFAULT CURRENT_TIMESTAMP,\n"
30 " comment CHAR(80) ,\n"
31 " configuration TEXT NOT NULL\n"
33 " CREATE TRIGGER IF NOT EXISTS delete_tail\n"
34 " AFTER INSERT ON transactions\n"
41 " rowid%%%u=NEW.rowid%%%u AND rowid!=NEW.rowid;\n"
44 NB_DLFT_MAX_CONFIG_ROLLBACKS
, NB_DLFT_MAX_CONFIG_ROLLBACKS
)
47 #endif /* HAVE_CONFIG_ROLLBACKS */
52 int nb_db_transaction_save(const struct nb_transaction
*transaction
,
53 uint32_t *transaction_id
)
55 #ifdef HAVE_CONFIG_ROLLBACKS
56 struct sqlite3_stmt
*ss
;
57 const char *client_name
;
58 char *config_str
= NULL
;
62 * Use a transaction to ensure consistency between the INSERT and SELECT
65 if (db_execute("BEGIN TRANSACTION;") != 0)
69 "INSERT INTO transactions\n"
70 " (client, comment, configuration)\n"
76 client_name
= nb_client_name(transaction
->context
->client
);
78 * Always record configurations in the XML format, save the default
79 * values too, as this covers the case where defaults may change.
81 if (lyd_print_mem(&config_str
, transaction
->config
->dnode
, LYD_XML
,
82 LYD_PRINT_WITHSIBLINGS
| LYD_PRINT_WD_ALL
)
86 if (db_bindf(ss
, "%s%s%s", client_name
, strlen(client_name
),
87 transaction
->comment
, strlen(transaction
->comment
),
88 config_str
? config_str
: "",
89 config_str
? strlen(config_str
) : 0)
93 if (db_run(ss
) != SQLITE_OK
)
99 * transaction_id is an optional output parameter that provides the ID
100 * of the recorded transaction.
102 if (transaction_id
) {
103 ss
= db_prepare("SELECT last_insert_rowid();");
107 if (db_run(ss
) != SQLITE_ROW
)
110 if (db_loadf(ss
, "%i", transaction_id
) != 0)
116 if (db_execute("COMMIT;") != 0)
127 (void)db_execute("ROLLBACK TRANSACTION;");
130 #else /* HAVE_CONFIG_ROLLBACKS */
132 #endif /* HAVE_CONFIG_ROLLBACKS */
135 struct nb_config
*nb_db_transaction_load(uint32_t transaction_id
)
137 struct nb_config
*config
= NULL
;
138 #ifdef HAVE_CONFIG_ROLLBACKS
139 struct lyd_node
*dnode
;
140 const char *config_str
;
141 struct sqlite3_stmt
*ss
;
154 if (db_bindf(ss
, "%d", transaction_id
) != 0)
157 if (db_run(ss
) != SQLITE_ROW
)
160 if (db_loadf(ss
, "%s", &config_str
) != 0)
163 err
= lyd_parse_data_mem(ly_native_ctx
, config_str
, LYD_XML
,
164 LYD_PARSE_STRICT
| LYD_PARSE_NO_STATE
,
165 LYD_VALIDATE_NO_STATE
, &dnode
);
167 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_parse_data_mem() failed",
170 config
= nb_config_new(dnode
);
174 #endif /* HAVE_CONFIG_ROLLBACKS */
179 int nb_db_clear_transactions(unsigned int n_oldest
)
181 #ifdef HAVE_CONFIG_ROLLBACKS
182 /* Delete oldest N entries. */
183 if (db_execute("DELETE\n"
192 " ORDER BY ROWID ASC LIMIT %u\n"
197 #endif /* HAVE_CONFIG_ROLLBACKS */
202 int nb_db_set_max_transactions(unsigned int max
)
204 #ifdef HAVE_CONFIG_ROLLBACKS
206 * Delete old entries if necessary and update the SQL trigger that
207 * auto-deletes old entries.
209 if (db_execute("BEGIN TRANSACTION;\n"
219 " ORDER BY ROWID DESC LIMIT -1 OFFSET %u\n"
221 " DROP TRIGGER delete_tail;\n"
222 " CREATE TRIGGER delete_tail\n"
223 " AFTER INSERT ON transactions\n"
230 " rowid%%%u=NEW.rowid%%%u AND rowid!=NEW.rowid;\n"
236 #endif /* HAVE_CONFIG_ROLLBACKS */
241 int nb_db_transactions_iterate(void (*func
)(void *arg
, int transaction_id
,
242 const char *client_name
,
244 const char *comment
),
247 #ifdef HAVE_CONFIG_ROLLBACKS
248 struct sqlite3_stmt
*ss
;
250 /* Send SQL query and parse the result. */
253 " rowid, client, date, comment\n"
261 while (db_run(ss
) == SQLITE_ROW
) {
263 const char *client_name
;
268 ret
= db_loadf(ss
, "%i%s%s%s", &transaction_id
, &client_name
,
273 (*func
)(arg
, transaction_id
, client_name
, date
, comment
);
277 #endif /* HAVE_CONFIG_ROLLBACKS */