4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * This file only contains the transaction commit logic.
39 #include <sys/sysmacros.h>
42 #define INVALID_OBJ_ID ((uint32_t)-1)
43 #define INVALID_TYPE ((uint32_t)-1)
46 const struct rep_protocol_transaction_cmd
*tx_cmd
;
50 uint32_t tx_orig_value_id
;
57 tx_cmd_compare(const void *key
, const void *elem_arg
)
59 const struct tx_cmd
*elem
= elem_arg
;
61 return (strcmp((const char *)key
, elem
->tx_prop
));
64 struct tx_commit_data
{
70 backend_query_t
*txc_inserts
;
72 rep_protocol_responseid_t txc_result
;
73 struct tx_cmd txc_cmds
[1]; /* actually txc_count */
75 #define TX_COMMIT_DATA_SIZE(count) \
76 offsetof(struct tx_commit_data, txc_cmds[count])
80 tx_check_genid(void *data_arg
, int columns
, char **vals
, char **names
)
82 tx_commit_data_t
*data
= data_arg
;
84 if (atoi(vals
[0]) != data
->txc_oldgen
)
85 data
->txc_result
= REP_PROTOCOL_FAIL_NOT_LATEST
;
87 data
->txc_result
= REP_PROTOCOL_SUCCESS
;
88 return (BACKEND_CALLBACK_CONTINUE
);
92 * tx_process_property() is called once for each property in current
93 * property group generation. Its purpose is threefold:
95 * 1. copy properties not mentioned in the transaction over unchanged.
96 * 2. mark DELETEd properties as seen (they will be left out of the new
98 * 3. consistancy-check NEW, CLEAR, and REPLACE commands.
100 * Any consistancy problems set tx_bad, and seen properties are marked
101 * tx_found. These is used later, in tx_process_cmds().
105 tx_process_property(void *data_arg
, int columns
, char **vals
, char **names
)
107 tx_commit_data_t
*data
= data_arg
;
110 const char *prop_name
= vals
[0];
111 const char *prop_type
= vals
[1];
112 const char *lnk_val_id
= vals
[2];
116 assert(columns
== 3);
118 elem
= bsearch(prop_name
, data
->txc_cmds
, data
->txc_count
,
119 sizeof (*data
->txc_cmds
), tx_cmd_compare
);
122 backend_query_add(data
->txc_inserts
,
123 "INSERT INTO prop_lnk_tbl"
124 " (lnk_pg_id, lnk_gen_id, lnk_prop_name, lnk_prop_type,"
126 "VALUES ( %d, %d, '%q', '%q', %Q );",
127 data
->txc_pg_id
, data
->txc_gen
, prop_name
, prop_type
,
130 assert(!elem
->tx_found
);
133 if (lnk_val_id
!= NULL
) {
135 elem
->tx_orig_value_id
=
136 strtoul(lnk_val_id
, &endptr
, 10);
137 if (elem
->tx_orig_value_id
== 0 || *endptr
!= 0 ||
139 return (BACKEND_CALLBACK_ABORT
);
142 elem
->tx_orig_value_id
= 0;
145 switch (elem
->tx_cmd
->rptc_action
) {
146 case REP_PROTOCOL_TX_ENTRY_NEW
:
148 data
->txc_result
= REP_PROTOCOL_FAIL_EXISTS
;
150 case REP_PROTOCOL_TX_ENTRY_CLEAR
:
151 if (REP_PROTOCOL_BASE_TYPE(elem
->tx_cmd
->rptc_type
) !=
153 REP_PROTOCOL_SUBTYPE(elem
->tx_cmd
->rptc_type
) !=
157 REP_PROTOCOL_FAIL_TYPE_MISMATCH
;
160 case REP_PROTOCOL_TX_ENTRY_REPLACE
:
162 case REP_PROTOCOL_TX_ENTRY_DELETE
:
163 elem
->tx_processed
= 1;
170 return (BACKEND_CALLBACK_CONTINUE
);
174 * tx_process_cmds() finishes the job tx_process_property() started:
176 * 1. if tx_process_property() marked a command as bad, we skip it.
177 * 2. if a DELETE, REPLACE, or CLEAR operated on a non-existant property,
179 * 3. we complete the work of NEW, REPLACE, and CLEAR, by inserting the
180 * appropriate values into the database.
181 * 4. we delete all replaced data, if it is no longer referenced.
183 * Finally, we check all of the commands, and fail if anything was marked bad.
186 tx_process_cmds(tx_commit_data_t
*data
)
190 int count
= data
->txc_count
;
199 * For persistent pgs, we use backend_fail_if_seen to abort the
200 * deletion if there is a snapshot using our current state.
202 * All of the deletions in this function are safe, since
203 * rc_tx_commit() guarantees that all the data is in-cache.
205 q
= backend_query_alloc();
207 if (data
->txc_backend
!= BACKEND_TYPE_NONPERSIST
) {
209 "SELECT 1 FROM snaplevel_lnk_tbl "
210 " WHERE (snaplvl_pg_id = %d AND snaplvl_gen_id = %d); ",
211 data
->txc_pg_id
, data
->txc_oldgen
);
214 "DELETE FROM prop_lnk_tbl"
215 " WHERE (lnk_pg_id = %d AND lnk_gen_id = %d)",
216 data
->txc_pg_id
, data
->txc_oldgen
);
217 r
= backend_tx_run(data
->txc_tx
, q
, backend_fail_if_seen
, NULL
);
218 backend_query_free(q
);
220 if (r
== REP_PROTOCOL_SUCCESS
)
222 else if (r
== REP_PROTOCOL_DONE
)
223 do_delete
= 0; /* old gen_id is in use */
227 for (idx
= 0; idx
< count
; idx
++) {
228 elem
= &data
->txc_cmds
[idx
];
233 switch (elem
->tx_cmd
->rptc_action
) {
234 case REP_PROTOCOL_TX_ENTRY_DELETE
:
235 case REP_PROTOCOL_TX_ENTRY_REPLACE
:
236 case REP_PROTOCOL_TX_ENTRY_CLEAR
:
237 if (!elem
->tx_found
) {
242 case REP_PROTOCOL_TX_ENTRY_NEW
:
250 elem
->tx_cmd
->rptc_action
!= REP_PROTOCOL_TX_ENTRY_NEW
&&
251 elem
->tx_orig_value_id
!= 0) {
253 * delete the old values, if they are not in use
255 q
= backend_query_alloc();
257 "SELECT 1 FROM prop_lnk_tbl "
258 " WHERE (lnk_val_id = %d); "
259 "DELETE FROM value_tbl"
260 " WHERE (value_id = %d)",
261 elem
->tx_orig_value_id
, elem
->tx_orig_value_id
);
262 r
= backend_tx_run(data
->txc_tx
, q
,
263 backend_fail_if_seen
, NULL
);
264 backend_query_free(q
);
265 if (r
!= REP_PROTOCOL_SUCCESS
&& r
!= REP_PROTOCOL_DONE
)
269 if (elem
->tx_cmd
->rptc_action
== REP_PROTOCOL_TX_ENTRY_DELETE
)
270 continue; /* no further work to do */
272 type
[0] = REP_PROTOCOL_BASE_TYPE(elem
->tx_cmd
->rptc_type
);
273 type
[1] = REP_PROTOCOL_SUBTYPE(elem
->tx_cmd
->rptc_type
);
276 if (elem
->tx_nvalues
== 0) {
277 r
= backend_tx_run_update(data
->txc_tx
,
278 "INSERT INTO prop_lnk_tbl"
279 " (lnk_pg_id, lnk_gen_id, "
280 " lnk_prop_name, lnk_prop_type, lnk_val_id) "
281 "VALUES ( %d, %d, '%q', '%q', NULL );",
282 data
->txc_pg_id
, data
->txc_gen
, elem
->tx_prop
,
288 val_id
= backend_new_id(data
->txc_tx
, BACKEND_ID_VALUE
);
290 return (REP_PROTOCOL_FAIL_NO_RESOURCES
);
291 r
= backend_tx_run_update(data
->txc_tx
,
292 "INSERT INTO prop_lnk_tbl "
293 " (lnk_pg_id, lnk_gen_id, "
294 " lnk_prop_name, lnk_prop_type, lnk_val_id) "
295 "VALUES ( %d, %d, '%q', '%q', %d );",
296 data
->txc_pg_id
, data
->txc_gen
, elem
->tx_prop
,
301 for (i
= 0; i
< elem
->tx_nvalues
; i
++) {
302 str
= (const char *)&v
[1];
305 * Update values in backend, imposing
306 * ordering via the value_order column.
307 * This ordering is then used in subseqent
308 * value retrieval operations. We can
309 * safely assume that the repository schema
310 * has been upgraded (and hence has the
311 * value_order column in value_tbl), since
312 * it is upgraded as soon as the repository
315 r
= backend_tx_run_update(data
->txc_tx
,
316 "INSERT INTO value_tbl (value_id, "
317 "value_type, value_value, "
318 "value_order) VALUES (%d, '%c', "
320 val_id
, elem
->tx_cmd
->rptc_type
,
322 if (r
!= REP_PROTOCOL_SUCCESS
)
326 v
= (uint32_t *)((caddr_t
)str
+ TX_SIZE(*v
));
329 if (r
!= REP_PROTOCOL_SUCCESS
)
330 return (REP_PROTOCOL_FAIL_UNKNOWN
);
331 elem
->tx_processed
= 1;
334 for (idx
= 0; idx
< count
; idx
++) {
335 elem
= &data
->txc_cmds
[idx
];
338 return (REP_PROTOCOL_FAIL_BAD_TX
);
340 return (REP_PROTOCOL_SUCCESS
);
344 check_string(uintptr_t loc
, uint32_t len
, uint32_t sz
)
346 const char *ptr
= (const char *)loc
;
348 if (len
== 0 || len
> sz
|| ptr
[len
- 1] != 0 || strlen(ptr
) != len
- 1)
354 tx_check_and_setup(tx_commit_data_t
*data
, const void *cmds_arg
,
357 const struct rep_protocol_transaction_cmd
*cmds
;
359 struct tx_cmd
*prev
= NULL
;
365 loc
= (uintptr_t)cmds_arg
;
367 for (idx
= 0; idx
< count
; idx
++) {
368 cur
= &data
->txc_cmds
[idx
];
370 cmds
= (struct rep_protocol_transaction_cmd
*)loc
;
373 sz
= cmds
->rptc_size
;
375 loc
+= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE
;
376 sz
-= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE
;
378 len
= cmds
->rptc_name_len
;
379 if (len
<= 1 || !check_string(loc
, len
, sz
)) {
380 return (REP_PROTOCOL_FAIL_BAD_REQUEST
);
382 cur
->tx_prop
= (const char *)loc
;
389 cur
->tx_values
= (uint32_t *)loc
;
392 if (sz
< sizeof (uint32_t))
393 return (REP_PROTOCOL_FAIL_BAD_REQUEST
);
397 len
= *(uint32_t *)loc
;
398 loc
+= sizeof (uint32_t);
399 sz
-= sizeof (uint32_t);
401 if (!check_string(loc
, len
, sz
))
402 return (REP_PROTOCOL_FAIL_BAD_REQUEST
);
405 * XXX here, we should be checking that the values
406 * match the purported type
412 return (REP_PROTOCOL_FAIL_BAD_REQUEST
);
418 if (prev
!= NULL
&& strcmp(prev
->tx_prop
, cur
->tx_prop
) >= 0)
419 return (REP_PROTOCOL_FAIL_BAD_REQUEST
);
423 return (REP_PROTOCOL_SUCCESS
);
427 * Free the memory associated with a tx_commit_data structure.
430 tx_commit_data_free(tx_commit_data_t
*tx_data
)
436 * Parse the data of a REP_PROTOCOL_PROPERTYGRP_TX_COMMIT message into a
437 * more useful form. The data in the message will be represented by a
438 * tx_commit_data_t structure which is allocated by this function. The
439 * address of the allocated structure is returned to *tx_data and must be
440 * freed by calling tx_commit_data_free().
443 * cmds_arg Address of the commands in the
444 * REP_PROTOCOL_PROPERTYGRP_TX_COMMIT message.
446 * cmds_sz Number of message bytes at cmds_arg.
448 * tx_data Points to the place to receive the address of the
456 tx_commit_data_new(const void *cmds_arg
, size_t cmds_sz
,
457 tx_commit_data_t
**tx_data
)
459 const struct rep_protocol_transaction_cmd
*cmds
;
460 tx_commit_data_t
*data
;
467 * First, verify that the reported sizes make sense, and count
468 * the number of commands.
471 loc
= (uintptr_t)cmds_arg
;
473 while (cmds_sz
> 0) {
474 cmds
= (struct rep_protocol_transaction_cmd
*)loc
;
476 if (cmds_sz
<= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE
)
477 return (REP_PROTOCOL_FAIL_BAD_REQUEST
);
479 sz
= cmds
->rptc_size
;
480 if (sz
<= REP_PROTOCOL_TRANSACTION_CMD_MIN_SIZE
)
481 return (REP_PROTOCOL_FAIL_BAD_REQUEST
);
485 return (REP_PROTOCOL_FAIL_BAD_REQUEST
);
492 data
= uu_zalloc(TX_COMMIT_DATA_SIZE(count
));
494 return (REP_PROTOCOL_FAIL_NO_RESOURCES
);
497 * verify that everything looks okay, and set up our command
500 data
->txc_count
= count
;
501 ret
= tx_check_and_setup(data
, cmds_arg
, count
);
502 if (ret
== REP_PROTOCOL_SUCCESS
) {
512 * The following are a set of accessor functions to retrieve data from a
513 * tx_commit_data_t that has been allocated by tx_commit_data_new().
517 * Return the action of the transaction command whose command number is
518 * cmd_no. The action is placed at *action.
521 * _FAIL_BAD_REQUEST cmd_no is out of range.
524 tx_cmd_action(tx_commit_data_t
*tx_data
, size_t cmd_no
,
525 enum rep_protocol_transaction_action
*action
)
529 assert(cmd_no
< tx_data
->txc_count
);
530 if (cmd_no
>= tx_data
->txc_count
)
531 return (REP_PROTOCOL_FAIL_BAD_REQUEST
);
533 cur
= &tx_data
->txc_cmds
[cmd_no
];
534 *action
= cur
->tx_cmd
->rptc_action
;
535 return (REP_PROTOCOL_SUCCESS
);
539 * Return the number of transaction commands held in tx_data.
542 tx_cmd_count(tx_commit_data_t
*tx_data
)
544 return (tx_data
->txc_count
);
548 * Return the number of property values that are associated with the
549 * transaction command whose number is cmd_no. The number of values is
550 * returned to *nvalues.
553 * _FAIL_BAD_REQUEST cmd_no is out of range.
556 tx_cmd_nvalues(tx_commit_data_t
*tx_data
, size_t cmd_no
, uint32_t *nvalues
)
560 assert(cmd_no
< tx_data
->txc_count
);
561 if (cmd_no
>= tx_data
->txc_count
)
562 return (REP_PROTOCOL_FAIL_BAD_REQUEST
);
564 cur
= &tx_data
->txc_cmds
[cmd_no
];
565 *nvalues
= cur
->tx_nvalues
;
566 return (REP_PROTOCOL_SUCCESS
);
570 * Return a pointer to the property name of the command whose number is
571 * cmd_no. The property name pointer is returned to *pname.
574 * _FAIL_BAD_REQUEST cmd_no is out of range.
577 tx_cmd_prop(tx_commit_data_t
*tx_data
, size_t cmd_no
, const char **pname
)
581 assert(cmd_no
< tx_data
->txc_count
);
582 if (cmd_no
>= tx_data
->txc_count
)
583 return (REP_PROTOCOL_FAIL_BAD_REQUEST
);
585 cur
= &tx_data
->txc_cmds
[cmd_no
];
586 *pname
= cur
->tx_prop
;
587 return (REP_PROTOCOL_SUCCESS
);
591 * Return the property type of the property whose command number is
592 * cmd_no. The property type is returned to *ptype.
595 * _FAIL_BAD_REQUEST cmd_no is out of range.
598 tx_cmd_prop_type(tx_commit_data_t
*tx_data
, size_t cmd_no
, uint32_t *ptype
)
602 assert(cmd_no
< tx_data
->txc_count
);
603 if (cmd_no
>= tx_data
->txc_count
)
604 return (REP_PROTOCOL_FAIL_BAD_REQUEST
);
606 cur
= &tx_data
->txc_cmds
[cmd_no
];
607 *ptype
= cur
->tx_cmd
->rptc_type
;
608 return (REP_PROTOCOL_SUCCESS
);
612 * This function is used to retrieve a property value from the transaction
613 * data. val_no specifies which value is to be retrieved from the
614 * transaction command whose number is cmd_no. A pointer to the specified
615 * value is placed in *val.
618 * _FAIL_BAD_REQUEST cmd_no or val_no is out of range.
621 tx_cmd_value(tx_commit_data_t
*tx_data
, size_t cmd_no
, uint32_t val_no
,
629 assert(cmd_no
< tx_data
->txc_count
);
630 if (cmd_no
>= tx_data
->txc_count
)
631 return (REP_PROTOCOL_FAIL_BAD_REQUEST
);
633 cur
= &tx_data
->txc_cmds
[cmd_no
];
634 assert(val_no
< cur
->tx_nvalues
);
635 if (val_no
>= cur
->tx_nvalues
)
636 return (REP_PROTOCOL_FAIL_BAD_REQUEST
);
638 /* Find the correct value */
639 bp
= (char *)cur
->tx_values
;
640 for (i
= 0; i
< val_no
; i
++) {
641 /* LINTED alignment */
642 value_len
= *(uint32_t *)bp
;
643 bp
+= sizeof (uint32_t) + TX_SIZE(value_len
);
646 /* Bypass the count & return pointer to value. */
647 bp
+= sizeof (uint32_t);
649 return (REP_PROTOCOL_SUCCESS
);
653 object_tx_commit(rc_node_lookup_t
*lp
, tx_commit_data_t
*data
, uint32_t *gen
)
657 rep_protocol_responseid_t r
;
660 int backend
= lp
->rl_backend
;
662 ret
= backend_tx_begin(backend
, &tx
);
663 if (ret
!= REP_PROTOCOL_SUCCESS
)
666 /* Make sure the pg is up-to-date. */
667 data
->txc_oldgen
= *gen
;
668 data
->txc_backend
= backend
;
669 data
->txc_result
= REP_PROTOCOL_FAIL_NOT_FOUND
;
671 q
= backend_query_alloc();
672 backend_query_add(q
, "SELECT pg_gen_id FROM pg_tbl WHERE (pg_id = %d);",
674 r
= backend_tx_run(tx
, q
, tx_check_genid
, data
);
675 backend_query_free(q
);
677 if (r
!= REP_PROTOCOL_SUCCESS
||
678 (r
= data
->txc_result
) != REP_PROTOCOL_SUCCESS
) {
679 backend_tx_rollback(tx
);
683 /* If the transaction is empty, cut out early. */
684 if (data
->txc_count
== 0) {
685 backend_tx_rollback(tx
);
686 r
= REP_PROTOCOL_DONE
;
690 new_gen
= backend_new_id(tx
, BACKEND_ID_GENERATION
);
692 backend_tx_rollback(tx
);
693 return (REP_PROTOCOL_FAIL_NO_RESOURCES
);
696 data
->txc_pg_id
= lp
->rl_main_id
;
697 data
->txc_gen
= new_gen
;
700 r
= backend_tx_run_update(tx
,
701 "UPDATE pg_tbl SET pg_gen_id = %d "
702 " WHERE (pg_id = %d AND pg_gen_id = %d);",
703 new_gen
, lp
->rl_main_id
, *gen
);
705 if (r
!= REP_PROTOCOL_SUCCESS
) {
706 backend_tx_rollback(tx
);
710 q
= backend_query_alloc();
713 "SELECT lnk_prop_name, lnk_prop_type, lnk_val_id "
715 "WHERE (lnk_pg_id = %d AND lnk_gen_id = %d)",
716 lp
->rl_main_id
, *gen
);
718 data
->txc_inserts
= backend_query_alloc();
719 r
= backend_tx_run(tx
, q
, tx_process_property
, data
);
720 backend_query_free(q
);
722 if (r
== REP_PROTOCOL_DONE
)
723 r
= REP_PROTOCOL_FAIL_UNKNOWN
; /* corruption */
725 if (r
!= REP_PROTOCOL_SUCCESS
||
726 (r
= data
->txc_result
) != REP_PROTOCOL_SUCCESS
) {
727 backend_query_free(data
->txc_inserts
);
728 backend_tx_rollback(tx
);
732 r
= backend_tx_run(tx
, data
->txc_inserts
, NULL
, NULL
);
733 backend_query_free(data
->txc_inserts
);
735 if (r
!= REP_PROTOCOL_SUCCESS
) {
736 backend_tx_rollback(tx
);
740 r
= tx_process_cmds(data
);
741 if (r
!= REP_PROTOCOL_SUCCESS
) {
742 backend_tx_rollback(tx
);
745 r
= backend_tx_commit(tx
);
747 if (r
== REP_PROTOCOL_SUCCESS
)