1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1998,2008 Oracle. All rights reserved. 5 * 6 * $Id: xa.c,v 12.21 2008/01/08 20:59:00 bostic Exp $ 7 */ 8 9#include "db_config.h" 10 11#include "db_int.h" 12#include "dbinc/txn.h" 13 14static int __db_xa_close __P((char *, int, long)); 15static int __db_xa_commit __P((XID *, int, long)); 16static int __db_xa_complete __P((int *, int *, int, long)); 17static int __db_xa_end __P((XID *, int, long)); 18static int __db_xa_forget __P((XID *, int, long)); 19static int __db_xa_open __P((char *, int, long)); 20static int __db_xa_prepare __P((XID *, int, long)); 21static int __db_xa_recover __P((XID *, long, int, long)); 22static int __db_xa_rollback __P((XID *, int, long)); 23static int __db_xa_start __P((XID *, int, long)); 24static int __xa_txn_continue __P((ENV *, DB_TXN *, TXN_DETAIL *)); 25static int __xa_put_txn __P((ENV *, DB_TXN *)); 26static int __xa_txn_get_prepared 27 __P((ENV *, XID *, DB_PREPLIST *, long, long *, u_int32_t)); 28 29/* 30 * Possible flag values: 31 * Dynamic registration 0 => no dynamic registration 32 * TMREGISTER => dynamic registration 33 * Asynchronous operation 0 => no support for asynchrony 34 * TMUSEASYNC => async support 35 * Migration support 0 => migration of transactions across 36 * threads is possible 37 * TMNOMIGRATE => no migration across threads 38 */ 39const struct xa_switch_t db_xa_switch = { 40 "Berkeley DB", /* name[RMNAMESZ] */ 41 TMNOMIGRATE, /* flags */ 42 0, /* version */ 43 __db_xa_open, /* xa_open_entry */ 44 __db_xa_close, /* xa_close_entry */ 45 __db_xa_start, /* xa_start_entry */ 46 __db_xa_end, /* xa_end_entry */ 47 __db_xa_rollback, /* xa_rollback_entry */ 48 __db_xa_prepare, /* xa_prepare_entry */ 49 __db_xa_commit, /* xa_commit_entry */ 50 __db_xa_recover, /* xa_recover_entry */ 51 __db_xa_forget, /* xa_forget_entry */ 52 __db_xa_complete /* xa_complete_entry */ 53}; 54 55/* 56 * If you want your XA server to be multi-threaded, then you must (at least) 57 * edit this file and change: 58 * #undef XA_MULTI_THREAD 59 * to: 60 * #define XA_MULTI_THREAD 1 61 */ 62#undef XA_MULTI_THREAD 63 64/* 65 * __xa_get_txn -- 66 * Return a pointer to the current transaction structure for the 67 * designated environment. If do_init is non-zero and we don't find a 68 * structure for the current thread, then create a new structure for it. 69 * 70 * PUBLIC: int __xa_get_txn __P((ENV *, DB_TXN **, int)); 71 */ 72int 73__xa_get_txn(env, txnp, do_init) 74 ENV *env; 75 DB_TXN **txnp; 76 int do_init; 77{ 78#ifdef XA_MULTI_THREAD 79 DB_TXN *t; 80 DB_TXNMGR *mgr; 81 TXN_DETAIL *td; 82 db_threadid_t tid; 83 pid_t pid; 84#endif 85 int ret; 86 87 ret = 0; 88 89#ifdef XA_MULTI_THREAD 90 env->thread_id(env, &pid, &tid); 91 *txnp = NULL; 92 93 DB_ASSERT(env, env->tx_handle != NULL); 94 mgr = env->tx_handle; 95 96 /* 97 * We need to protect the xa_txn linked list, but the environment does 98 * not have a mutex. Since we are in an XA transaction environment, 99 * we know there is a transaction structure, we can use its mutex. 100 */ 101 MUTEX_LOCK(env, mgr->mutex); 102 TAILQ_FOREACH(t, &env->xa_txn, xalinks) { 103 td = t->td; 104 if (td->pid != pid) 105 continue; 106#ifdef HAVE_SIMPLE_THREAD_TYPE 107 if (t->tid == tid) { 108 *txnp = t; 109 break; 110 } 111#else 112 if (memcmp(&t->tid, &tid, sizeof(tid)) == 0) { 113 *txnp = t; 114 break; 115 } 116#endif 117 } 118 MUTEX_UNLOCK(env, mgr->mutex); 119 120 if (*txnp == NULL) { 121 if (!do_init) 122 ret = EINVAL; 123 else if ((ret = 124 __os_malloc(env, sizeof(DB_TXN), txnp)) == 0) { 125 (*txnp)->tid = tid; 126 MUTEX_LOCK(env, mgr->mutex); 127 TAILQ_INSERT_HEAD(&env->xa_txn, *txnp, xalinks); 128 MUTEX_UNLOCK(env, mgr->mutex); 129 } 130 } 131#else 132 COMPQUIET(do_init, 0); 133 134 *txnp = TAILQ_FIRST(&env->xa_txn); 135 if (*txnp == NULL && 136 (ret = __os_calloc(env, 1, sizeof(DB_TXN), txnp)) == 0) { 137 (*txnp)->txnid = TXN_INVALID; 138 TAILQ_INSERT_HEAD(&env->xa_txn, *txnp, xalinks); 139 } 140#endif 141 142 return (ret); 143} 144 145static int 146__xa_put_txn(env, txnp) 147 ENV *env; 148 DB_TXN *txnp; 149{ 150#ifdef XA_MULTI_THREAD 151 DB_TXNMGR *mgr; 152 mgr = env->tx_handle; 153 154 MUTEX_LOCK(env, mgr->mutex); 155 TAILQ_REMOVE(&env->xa_txn, txnp, xalinks); 156 MUTEX_UNLOCK(env, mgr->mutex); 157 __os_free(env, txnp); 158#else 159 COMPQUIET(env, NULL); 160 txnp->txnid = TXN_INVALID; 161#endif 162 return (0); 163} 164 165/* 166 * __xa_txn_continue -- 167 * This call wraps the __txn_continue function with the environment 168 * wrapper, so we can properly failchk environments running XA. 169 */ 170static int 171__xa_txn_continue(env, txn, td) 172 ENV *env; 173 DB_TXN *txn; 174 TXN_DETAIL *td; 175{ 176 DB_THREAD_INFO *ip; 177 int ret; 178 179 ENV_ENTER(env, ip); 180 ret = __txn_continue(env, txn, td); 181 ENV_LEAVE(env, ip); 182 183 return (ret); 184} 185 186/* 187 * __xa_txn_get_prepared -- 188 * Wrap the internal call to __txn_get_prepared so that we can call 189 * it from XA. XA routines are not considered to be running "inside" the 190 * library, so when they make calls into the library, we need to use interface 191 * routines that support replication and failchk. Since __txn_get_prepared 192 * is internal, there is no user API to call, so we use this wrapper routine 193 * instead. 194 */ 195static int 196__xa_txn_get_prepared(env, xids, txns, count, retp, flags) 197 ENV *env; 198 XID *xids; 199 DB_PREPLIST *txns; 200 long count; /* This is long for XA compatibility. */ 201 long *retp; 202 u_int32_t flags; 203{ 204 DB_THREAD_INFO *ip; 205 int ret; 206 207 ENV_ENTER(env, ip); 208 REPLICATION_WRAP(env, 209 (__txn_get_prepared(env, xids, txns, count, retp, flags)), 0, ret); 210 ENV_LEAVE(env, ip); 211 return (ret); 212} 213 214#ifdef XA_MULTI_THREAD 215#define XA_FLAGS \ 216 (DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | \ 217 DB_INIT_TXN | DB_THREAD) 218#else 219#define XA_FLAGS \ 220 (DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | \ 221 DB_INIT_TXN) 222#endif 223 224/* 225 * __db_xa_open -- 226 * The open call in the XA protocol. The rmid field is an id number 227 * that the TM assigned us and will pass us on every xa call. We need to 228 * map that rmid number into a env structure that we create during 229 * initialization. Since this id number is thread specific, we do not 230 * need to store it in shared memory. The file xa_map.c implements all 231 * such xa->db mappings. 232 * The xa_info field is instance specific information. We require 233 * that the value of DB_HOME be passed in xa_info. Since xa_info is the 234 * only thing that we get to pass to db_env_create, any config information 235 * will have to be done via a config file instead of via the db_env_create 236 * call. 237 */ 238static int 239__db_xa_open(xa_info, rmid, arg_flags) 240 char *xa_info; 241 int rmid; 242 long arg_flags; 243{ 244 DB_ENV *dbenv; 245 ENV *env; 246 u_long flags; 247 248 flags = (u_long)arg_flags; /* Conversion for bit operations. */ 249 250 if (LF_ISSET(TMASYNC)) 251 return (XAER_ASYNC); 252 if (flags != TMNOFLAGS) 253 return (XAER_INVAL); 254 255 /* Verify if we already have this environment open. */ 256 if (__db_rmid_to_env(rmid, &env) == 0) 257 return (XA_OK); 258 259 /* Open a new environment. */ 260 if (db_env_create(&dbenv, 0) != 0) 261 return (XAER_RMERR); 262 env = dbenv->env; 263 if (dbenv->open(dbenv, xa_info, XA_FLAGS, 0) != 0) 264 goto err; 265 266 /* Create the mapping. */ 267 if (__db_map_rmid(rmid, env) != 0) 268 goto err; 269 270 /* Allocate space for the current transaction. */ 271 TAILQ_INIT(&env->xa_txn); 272 273 return (XA_OK); 274 275err: (void)dbenv->close(dbenv, 0); 276 277 return (XAER_RMERR); 278} 279 280/* 281 * __db_xa_close -- 282 * The close call of the XA protocol. The only trickiness here 283 * is that if there are any active transactions, we must fail. It is 284 * *not* an error to call close on an environment that has already been 285 * closed (I am interpreting that to mean it's OK to call close on an 286 * environment that has never been opened). 287 */ 288static int 289__db_xa_close(xa_info, rmid, arg_flags) 290 char *xa_info; 291 int rmid; 292 long arg_flags; 293{ 294 DB_TXN *t; 295 ENV *env; 296 int ret, t_ret; 297 u_long flags; 298 299 COMPQUIET(xa_info, NULL); 300 301 flags = (u_long)arg_flags; /* Conversion for bit operations. */ 302 303 if (LF_ISSET(TMASYNC)) 304 return (XAER_ASYNC); 305 if (flags != TMNOFLAGS) 306 return (XAER_INVAL); 307 308 /* If the environment is closed, then we're done. */ 309 if (__db_rmid_to_env(rmid, &env) != 0) 310 return (XA_OK); 311 312 /* Check if there are any pending transactions. */ 313 if ((t = TAILQ_FIRST(&env->xa_txn)) != NULL && 314 t->txnid != TXN_INVALID) 315 return (XAER_PROTO); 316 317 /* Destroy the mapping. */ 318 ret = __db_unmap_rmid(rmid); 319 320 /* Discard space held for the current transaction. */ 321 while ((t = TAILQ_FIRST(&env->xa_txn)) != NULL) { 322 TAILQ_REMOVE(&env->xa_txn, t, xalinks); 323 __os_free(env, t); 324 } 325 326 /* Close the environment. */ 327 if ((t_ret = env->dbenv->close(env->dbenv, 0)) != 0 && ret == 0) 328 ret = t_ret; 329 330 return (ret == 0 ? XA_OK : XAER_RMERR); 331} 332 333/* 334 * __db_xa_start -- 335 * Begin a transaction for the current resource manager. 336 */ 337static int 338__db_xa_start(xid, rmid, arg_flags) 339 XID *xid; 340 int rmid; 341 long arg_flags; 342{ 343 DB_TXN *txnp; 344 ENV *env; 345 TXN_DETAIL *td; 346 roff_t off; 347 u_long flags; 348 int is_known; 349 350 flags = (u_long)arg_flags; /* Conversion for bit operations. */ 351 352#define OK_FLAGS (TMJOIN | TMRESUME | TMNOWAIT | TMASYNC | TMNOFLAGS) 353 if (LF_ISSET(~OK_FLAGS)) 354 return (XAER_INVAL); 355 356 if (LF_ISSET(TMJOIN) && LF_ISSET(TMRESUME)) 357 return (XAER_INVAL); 358 359 if (LF_ISSET(TMASYNC)) 360 return (XAER_ASYNC); 361 362 if (__db_rmid_to_env(rmid, &env) != 0) 363 return (XAER_PROTO); 364 365 is_known = __db_xid_to_txn(env, xid, &off) == 0; 366 367 if (is_known && !LF_ISSET(TMRESUME) && !LF_ISSET(TMJOIN)) 368 return (XAER_DUPID); 369 370 if (!is_known && LF_ISSET(TMRESUME | TMJOIN)) 371 return (XAER_NOTA); 372 373 /* 374 * This can't block, so we can ignore TMNOWAIT. 375 * 376 * Other error conditions: RMERR, RMFAIL, OUTSIDE, PROTO, RB* 377 */ 378 if (is_known) { 379 td = R_ADDR(&env->tx_handle->reginfo, off); 380 if (td->xa_status == TXN_XA_SUSPENDED && 381 !LF_ISSET(TMRESUME | TMJOIN)) 382 return (XAER_PROTO); 383 if (td->xa_status == TXN_XA_DEADLOCKED) 384 return (XA_RBDEADLOCK); 385 if (td->xa_status == TXN_XA_ABORTED) 386 return (XA_RBOTHER); 387 388 /* Now, fill in the global transaction structure. */ 389 if (__xa_get_txn(env, &txnp, 1) != 0) 390 return (XAER_RMERR); 391 if (__xa_txn_continue(env, txnp, td) != 0) 392 return (XAER_RMERR); 393 td->xa_status = TXN_XA_STARTED; 394 } else { 395 if (__xa_get_txn(env, &txnp, 1) != 0) 396 return (XAER_RMERR); 397 if (__txn_xa_begin(env, txnp)) 398 return (XAER_RMERR); 399 (void)__db_map_xid(env, xid, txnp->td); 400 td = txnp->td; 401 td->xa_status = TXN_XA_STARTED; 402 } 403 return (XA_OK); 404} 405 406/* 407 * __db_xa_end -- 408 * Disassociate the current transaction from the current process. 409 */ 410static int 411__db_xa_end(xid, rmid, flags) 412 XID *xid; 413 int rmid; 414 long flags; 415{ 416 DB_TXN *txn; 417 ENV *env; 418 TXN_DETAIL *td; 419 roff_t off; 420 421 if (flags != TMNOFLAGS && !LF_ISSET(TMSUSPEND | TMSUCCESS | TMFAIL)) 422 return (XAER_INVAL); 423 424 if (__db_rmid_to_env(rmid, &env) != 0) 425 return (XAER_PROTO); 426 427 if (__db_xid_to_txn(env, xid, &off) != 0) 428 return (XAER_NOTA); 429 430 if (__xa_get_txn(env, &txn, 0) != 0) 431 return (XAER_RMERR); 432 433 td = R_ADDR(&env->tx_handle->reginfo, off); 434 if (td != txn->td) 435 return (XAER_PROTO); 436 437 if (td->xa_status == TXN_XA_DEADLOCKED) 438 return (XA_RBDEADLOCK); 439 440 if (td->status == TXN_ABORTED) 441 return (XA_RBOTHER); 442 443 if (td->xa_status != TXN_XA_STARTED) 444 return (XAER_PROTO); 445 446 /* 447 * If we ever support XA migration, we cannot keep SUSPEND/END 448 * status in the shared region; it would have to be process local. 449 */ 450 if (LF_ISSET(TMSUSPEND)) 451 td->xa_status = TXN_XA_SUSPENDED; 452 else 453 td->xa_status = TXN_XA_ENDED; 454 455 /* 456 * XXX 457 * This can fail in XA_MULTI_THREAD mode. 458 */ 459 (void)__xa_put_txn(env, txn); 460 return (XA_OK); 461} 462 463/* 464 * __db_xa_prepare -- 465 * Sync the log to disk so we can guarantee recoverability. 466 */ 467static int 468__db_xa_prepare(xid, rmid, arg_flags) 469 XID *xid; 470 int rmid; 471 long arg_flags; 472{ 473 DB_TXN *txnp; 474 ENV *env; 475 TXN_DETAIL *td; 476 roff_t off; 477 u_long flags; 478 479 flags = (u_long)arg_flags; /* Conversion for bit operations. */ 480 481 if (LF_ISSET(TMASYNC)) 482 return (XAER_ASYNC); 483 if (flags != TMNOFLAGS) 484 return (XAER_INVAL); 485 486 /* 487 * We need to know if we've ever called prepare on this. 488 * As part of the prepare, we set the xa_status field to 489 * reflect that fact that prepare has been called, and if 490 * it's ever called again, it's an error. 491 */ 492 if (__db_rmid_to_env(rmid, &env) != 0) 493 return (XAER_PROTO); 494 495 if (__db_xid_to_txn(env, xid, &off) != 0) 496 return (XAER_NOTA); 497 td = R_ADDR(&env->tx_handle->reginfo, off); 498 if (td->xa_status == TXN_XA_DEADLOCKED) 499 return (XA_RBDEADLOCK); 500 501 if (td->xa_status != TXN_XA_ENDED && td->xa_status != TXN_XA_SUSPENDED) 502 return (XAER_PROTO); 503 504 /* Now, fill in the global transaction structure. */ 505 if (__xa_get_txn(env, &txnp, 0) != 0) 506 return (XAER_PROTO); 507 if (__xa_txn_continue(env, txnp, td) != 0) 508 return (XAER_RMERR); 509 510 if (txnp->prepare(txnp, (u_int8_t *)xid->data) != 0) 511 return (XAER_RMERR); 512 513 td->xa_status = TXN_XA_PREPARED; 514 515 /* 516 * XXX 517 * This can fail in XA_MULTI_THREAD mode. 518 */ 519 (void)__xa_put_txn(env, txnp); 520 return (XA_OK); 521} 522 523/* 524 * __db_xa_commit -- 525 * Commit the transaction 526 */ 527static int 528__db_xa_commit(xid, rmid, arg_flags) 529 XID *xid; 530 int rmid; 531 long arg_flags; 532{ 533 DB_TXN *txnp; 534 ENV *env; 535 TXN_DETAIL *td; 536 roff_t off; 537 u_long flags; 538 539 flags = (u_long)arg_flags; /* Conversion for bit operations. */ 540 541 if (LF_ISSET(TMASYNC)) 542 return (XAER_ASYNC); 543#undef OK_FLAGS 544#define OK_FLAGS (TMNOFLAGS | TMNOWAIT | TMONEPHASE) 545 if (LF_ISSET(~OK_FLAGS)) 546 return (XAER_INVAL); 547 548 /* 549 * We need to know if we've ever called prepare on this. 550 * We can verify this by examining the xa_status field. 551 */ 552 if (__db_rmid_to_env(rmid, &env) != 0) 553 return (XAER_PROTO); 554 555 if (__db_xid_to_txn(env, xid, &off) != 0) 556 return (XAER_NOTA); 557 558 td = R_ADDR(&env->tx_handle->reginfo, off); 559 if (td->xa_status == TXN_XA_DEADLOCKED) 560 return (XA_RBDEADLOCK); 561 562 if (td->xa_status == TXN_XA_ABORTED) 563 return (XA_RBOTHER); 564 565 if (LF_ISSET(TMONEPHASE) && 566 td->xa_status != TXN_XA_ENDED && td->xa_status != TXN_XA_SUSPENDED) 567 return (XAER_PROTO); 568 569 if (!LF_ISSET(TMONEPHASE) && td->xa_status != TXN_XA_PREPARED) 570 return (XAER_PROTO); 571 572 /* Now, fill in the global transaction structure. */ 573 if (__xa_get_txn(env, &txnp, 0) != 0) 574 return (XAER_RMERR); 575 if (__xa_txn_continue(env, txnp, td) != 0) 576 return (XAER_RMERR); 577 578 if (txnp->commit(txnp, 0) != 0) 579 return (XAER_RMERR); 580 581 /* 582 * XXX 583 * This can fail in XA_MULTI_THREAD mode. 584 */ 585 (void)__xa_put_txn(env, txnp); 586 return (XA_OK); 587} 588 589/* 590 * __db_xa_recover -- 591 * Returns a list of prepared and heuristically completed transactions. 592 * 593 * The return value is the number of xids placed into the xid array (less 594 * than or equal to the count parameter). The flags are going to indicate 595 * whether we are starting a scan or continuing one. 596 */ 597static int 598__db_xa_recover(xids, count, rmid, flags) 599 XID *xids; 600 long count, flags; 601 int rmid; 602{ 603 ENV *env; 604 u_int32_t newflags; 605 long rval; 606 607 /* If the environment is closed, then we're done. */ 608 if (__db_rmid_to_env(rmid, &env) != 0) 609 return (XAER_PROTO); 610 611 if (LF_ISSET(TMSTARTRSCAN)) 612 newflags = DB_FIRST; 613 else if (LF_ISSET(TMENDRSCAN)) 614 newflags = DB_LAST; 615 else 616 newflags = DB_NEXT; 617 618 rval = 0; 619 if (__xa_txn_get_prepared(env, 620 xids, NULL, count, &rval, newflags) != 0) 621 return (XAER_RMERR); 622 else 623 return (rval); 624} 625 626/* 627 * __db_xa_rollback 628 * Abort an XA transaction. 629 */ 630static int 631__db_xa_rollback(xid, rmid, arg_flags) 632 XID *xid; 633 int rmid; 634 long arg_flags; 635{ 636 DB_TXN *txnp; 637 ENV *env; 638 TXN_DETAIL *td; 639 roff_t off; 640 u_long flags; 641 642 flags = (u_long)arg_flags; /* Conversion for bit operations. */ 643 644 if (LF_ISSET(TMASYNC)) 645 return (XAER_ASYNC); 646 if (flags != TMNOFLAGS) 647 return (XAER_INVAL); 648 649 if (__db_rmid_to_env(rmid, &env) != 0) 650 return (XAER_PROTO); 651 652 if (__db_xid_to_txn(env, xid, &off) != 0) 653 return (XAER_NOTA); 654 655 td = R_ADDR(&env->tx_handle->reginfo, off); 656 if (td->xa_status == TXN_XA_DEADLOCKED) 657 return (XA_RBDEADLOCK); 658 659 if (td->xa_status == TXN_XA_ABORTED) 660 return (XA_RBOTHER); 661 662 if (td->xa_status != TXN_XA_ENDED && 663 td->xa_status != TXN_XA_SUSPENDED && 664 td->xa_status != TXN_XA_PREPARED) 665 return (XAER_PROTO); 666 667 /* Now, fill in the global transaction structure. */ 668 if (__xa_get_txn(env, &txnp, 0) != 0) 669 return (XAER_RMERR); 670 if (__xa_txn_continue(env, txnp, td) != 0) 671 return (XAER_RMERR); 672 if (txnp->abort(txnp) != 0) 673 return (XAER_RMERR); 674 675 /* 676 * XXX 677 * This can fail in XA_MULTI_THREAD mode. 678 */ 679 (void)__xa_put_txn(env, txnp); 680 return (XA_OK); 681} 682 683/* 684 * __db_xa_forget -- 685 * Forget about an XID for a transaction that was heuristically 686 * completed. Since we do not heuristically complete anything, I 687 * don't think we have to do anything here, but we should make sure 688 * that we reclaim the slots in the txnid table. 689 */ 690static int 691__db_xa_forget(xid, rmid, arg_flags) 692 XID *xid; 693 int rmid; 694 long arg_flags; 695{ 696 ENV *env; 697 roff_t off; 698 u_long flags; 699 700 flags = (u_long)arg_flags; /* Conversion for bit operations. */ 701 702 if (LF_ISSET(TMASYNC)) 703 return (XAER_ASYNC); 704 if (flags != TMNOFLAGS) 705 return (XAER_INVAL); 706 707 if (__db_rmid_to_env(rmid, &env) != 0) 708 return (XAER_PROTO); 709 710 /* 711 * If mapping is gone, then we're done. 712 */ 713 if (__db_xid_to_txn(env, xid, &off) != 0) 714 return (XA_OK); 715 716 __db_unmap_xid(env, xid, off); 717 718 /* No fatal value that would require an XAER_RMFAIL. */ 719 return (XA_OK); 720} 721 722/* 723 * __db_xa_complete -- 724 * Used to wait for asynchronous operations to complete. Since we're 725 * not doing asynch, this is an invalid operation. 726 */ 727static int 728__db_xa_complete(handle, retval, rmid, flags) 729 int *handle, *retval, rmid; 730 long flags; 731{ 732 COMPQUIET(handle, NULL); 733 COMPQUIET(retval, NULL); 734 COMPQUIET(rmid, 0); 735 COMPQUIET(flags, 0); 736 737 return (XAER_INVAL); 738} 739