CUBRID Engine  latest
work_space.c
Go to the documentation of this file.
1 /*
2  * Copyright 2008 Search Solution Corporation
3  * Copyright 2016 CUBRID Corporation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 /*
20  * work_space.c - Workspace Manager
21  */
22 
23 #ident "$Id$"
24 
25 #include "config.h"
26 
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <assert.h>
31 
32 #include "memory_alloc.h"
33 #include "area_alloc.h"
34 #include "message_catalog.h"
35 #include "memory_hash.h"
36 #include "error_manager.h"
37 #include "oid.h"
38 #include "work_space.h"
39 #include "schema_manager.h"
40 #include "authenticate.h"
41 #include "object_accessor.h"
42 #include "locator_cl.h"
43 #include "storage_common.h"
44 #include "system_parameter.h"
45 #include "set_object.h"
46 #include "virtual_object.h"
47 #include "object_primitive.h"
48 #include "object_representation.h"
49 #include "class_object.h"
50 #include "environment_variable.h"
51 #include "db.h"
52 #include "transaction_cl.h"
53 #include "object_template.h"
54 #include "server_interface.h"
55 #include "view_transform.h"
56 #include "dbtype.h"
57 
58 extern unsigned int db_on_server;
59 
60 /*
61  * need these to get the allocation areas initialized, avoid including
62  * the entire file
63  */
64 
65 /*
66  * ws_Commit_mops
67  * Linked list of mops to be reset at commit/abort.
68  */
69 
71 
72 /*
73  * ws_Mop_table
74  * This is the OID to MOP hash table. This is public ONLY to allow
75  * some performance related mapping macros to be used by the
76  * transaction manager.
77  */
78 
80 
81 /*
82  * ws_Mop_table_size
83  * Records the current size of the OID to MOP hash table.
84  */
85 
86 unsigned int ws_Mop_table_size = 0;
87 
88 /*
89  * ws_Resident_classes
90  * This is a global list of resident class objects.
91  * Since the root of the class' resident instance list is kept in
92  * the class_link field of the class MOP, we can't use this field
93  * to chain the list of resident class objects. Instead keep an object
94  * list.
95  */
96 
98 
99 /*
100  * ws_Stats
101  * Workspace statistics structure.
102  * This contains random information about the state of the workspace.
103  */
104 
105 WS_STATISTICS ws_Stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
106 
108 
109 /*
110  * We used to keep a global dirty list here. But for more efficient traversals
111  * the dirty list has been reorganized into a dirty list by class. To visit
112  * all the dirty objects in this workspace, start with the resident class list
113  * and visit each class' dirty list. The dirty flag here is consulted by
114  * ws_has_updated to determine if there are dirty objects in this workspace.
115  */
116 static bool Ws_dirty;
117 
118 /*
119  * Null_object
120  * This is used at the terminator for the dirty_link and class_link fieids
121  * of the MOP if they are the last in the list. This allows us to
122  * determine if the MOP has been added to a class list simply by
123  * checking to see if the field is NULL. If it is non-NULL, we know it
124  * must be in the list (even if it is at the end of the list). This
125  * avoids having to keep an extra bit in the MOP structre.
126  */
127 
129 
130 /*
131  * Classname_cache
132  * This is a hash table used to cache the class name to MOP mapping
133  * on the client side. This avoids repeated calls to the server to
134  * find class OIDs.
135  */
136 
138 
139 
140 /*
141  * Objlist_area
142  * Area for allocating external object list links.
143  */
145 
146 /* When MVCC is enabled, fetched objects are not locked. Which means next
147  * fetch call would go to server and check if object was changed. However,
148  * if the same snapshot is used, the visible object is not changed. To avoid
149  * checking on server, mark fetched object with the snapshot version and
150  * don't re-fetch until snapshot version is changed.
151  */
152 static unsigned int ws_MVCC_snapshot_version = 0;
153 
154 /*
155  * ws_area_init
156  * Initialize the areas used by the workspace manager.
157  *
158  */
159 
162 
163 #define OBJLIST_AREA_COUNT 4096
164 
166 
168 
169 static MOP ws_make_mop (const OID * oid);
170 static void ws_free_mop (MOP op);
171 static void emergency_remove_dirty (MOP op);
172 static void ws_unlink_from_commit_mops_list (MOP op);
173 static int ws_map_dirty_internal (MAPFUNC function, void *args, bool classes_only);
174 static int add_class_object (MOP class_mop, MOP obj);
175 static void remove_class_object (MOP class_mop, MOP obj);
176 static int mark_instance_deleted (MOP op, void *args);
177 static void ws_clear_internal (bool clear_vmop_keys);
178 static void ws_print_oid (OID * oid);
179 #if defined (CUBRID_DEBUG)
180 static int ws_describe_mop (MOP mop, void *args);
181 #endif
182 static int ws_check_hash_link (int slot);
183 static void ws_insert_mop_on_hash_link (MOP mop, int slot);
184 static void ws_insert_mop_on_hash_link_with_position (MOP mop, int slot, MOP prev);
185 
186 #if !defined (NDEBUG)
187 static void ws_examine_no_mop_has_cached_lock (void);
188 #endif /* !NDEBUG */
189 
190 /*
191  * MEMORY CRISES
192  */
193 
194 /*
195  * ws_abort_transaction - callback routine for the qf module that is called
196  * when storage is exhausted and an allocation can
197  * not be serviced
198  * return: void
199  */
200 void
202 {
204  {
206  {
208  }
209  }
210  else
211  {
212  /* might want to keep a chunk of memory in reserve here so we can free it in case we need to do any small
213  * allocations during the abort process */
214 
215  (void) tran_unilaterally_abort ();
216 
217  /* couldn't get to the catalog, use hard coded strings */
218  fprintf (stdout, "CUBRID cannot allocate main memory and must halt execution.\n");
219  fprintf (stdout, "The current transaction has been aborted.\n");
220  fprintf (stdout, "Data integrity has been preserved.\n");
221  }
222 }
223 
224 /*
225  * MOP ALLOCATION AND TABLE MAINTENANCE
226  */
227 
228 /*
229  * ws_make_mop - allocates a storate for a new mop
230  * return: MOP structure
231  * oid(in): oid for a new mop
232  */
233 static MOP
234 ws_make_mop (const OID * oid)
235 {
236  MOP op;
237 
238  op = (MOP) malloc (sizeof (DB_OBJECT));
239  if (op != NULL)
240  {
241  op->class_mop = NULL;
242  op->object = NULL;
243  op->class_link = NULL;
244  op->dirty_link = NULL;
245  op->dirty = 0;
246  op->deleted = 0;
247  op->pinned = 0;
248  op->no_objects = 0;
249  op->lock = NULL_LOCK;
251  op->hash_link = NULL;
252  op->commit_link = NULL;
253  op->oid_info.oid.volid = 0;
254  op->oid_info.oid.pageid = 0;
255  op->oid_info.oid.slotid = 0;
256  op->is_vid = 0;
257  op->is_temp = 0;
258  op->released = 0;
259  op->decached = 0;
260  op->label_value_list = NULL;
261  /* Initialize mvcc snapshot version to be sure it doesn't match with current mvcc snapshot version. */
263 
264  /* this is NULL only for the Null_object hack */
265  if (oid != NULL)
266  {
267  COPY_OID (WS_REAL_OID (op), oid);
268  }
269  else
270  {
271  OID_SET_NULL (WS_REAL_OID (op));
272  }
273 
274  ws_Stats.mops_allocated++;
275  }
276  else
277  {
279 
280  /* couldnt' allocate a MOP, mgc should have set an error by now */
282  }
283 
284  return (op);
285 }
286 
287 /*
288  * ws_free_mop - frees a MOP
289  * return: void
290  * op(in/out): MOP pointer
291  *
292  * Note: This was introduced primarily to handle the new MOP property
293  * lists. MOPS can only really be freed through garbage collection.
294  */
295 static void
297 {
298  DB_VALUE *keys = NULL;
299  unsigned int flags;
300 
301  if (op->commit_link != NULL)
302  {
303  /* safe-guard to prevent FMR to access ws_Commit_mops list */
305  }
306 
308 
309  if (op->is_vid)
310  {
311  keys = ws_keys (op, &flags);
312  if (keys != NULL)
313  {
314  pr_clear_value (keys);
315  }
316 
317  if (WS_VID_INFO (op))
318  {
319  free (WS_VID_INFO (op));
320  WS_VID_INFO (op) = NULL;
321  }
322  }
323 
324  free (op);
325 }
326 
327 /*
328  * ws_make_temp_mop - create a temporary MOP
329  * return: new temporary MOP
330  * Note: It is assumed the caller will set up the MOP fields in the right way.
331  * The ws_ module will ensure that MOPs with the is_temp field set are not
332  * subject to garbage collection scans and are kept off the resident instance
333  * lists of the classes.
334  */
335 MOP
337 {
338  MOP op;
339 
340  op = ws_make_mop (NULL);
341 
342  if (op != NULL)
343  {
344  op->is_temp = 1;
345  ws_Stats.temp_mops_allocated++;
346  }
347  return (op);
348 }
349 
350 /*
351  * ws_free_temp_mop - frees a temporary MOP.
352  * return: void
353  * op(in/out): temporary mop to free
354  */
355 void
357 {
358  if (op != NULL && op->is_temp)
359  {
360  ws_free_mop (op);
361  ws_Stats.temp_mops_freed++;
362  }
363 }
364 
365 static int
367 {
368  MOP head, tail;
369  MOP p, q;
370  int c;
371 
372  head = ws_Mop_table[slot].head;
373  tail = ws_Mop_table[slot].tail;
374 
375  p = head;
376  if (p == NULL)
377  {
378  /* empty list */
379  assert (head == NULL && tail == NULL);
380  }
381  else if (p->hash_link == NULL)
382  {
383  /* only one node */
384  assert (head == p && tail == p);
385  }
386  else
387  {
388  /* more than one node */
389  for (q = p->hash_link; q; p = q, q = q->hash_link)
390  {
391  c = oid_compare (WS_OID (p), WS_OID (q));
392  assert (c <= 0);
393  }
394  assert (p == tail);
395  }
396 
397  return NO_ERROR;
398 }
399 
400 /*
401  * ws_insert_mop_on_hash_link () - Insert a new mop in hash table at the given
402  * slot. The list of mop's is kept ordered
403  * by OID's.
404  *
405  * return : Void.
406  * mop (in) : New mop.
407  * slot (in) : Hash slot.
408  *
409  * NOTE: There are cases when real objects may have duplicate OID's,
410  * especially when MVCC is enabled. Duplicates cannot be removed
411  * because they may be still referenced. We can only discard the cached
412  * object and remove the mop from class.
413  */
414 static void
416 {
417  MOP p;
418  MOP prev = NULL;
419  int c;
420 
421  /* to find the appropriate position */
422  p = ws_Mop_table[slot].tail;
423  if (p)
424  {
425  c = oid_compare (WS_OID (mop), WS_OID (p));
426 
427  if (c > 0)
428  {
429  /* mop is greater than the tail */
430  p->hash_link = mop;
431  mop->hash_link = NULL;
432  ws_Mop_table[slot].tail = mop;
433 
434  return;
435  }
436 
437  /* Unfortunately, we have to navigate the list when c == 0, because there can be redundancies of mops which have
438  * the same oid, in case of VID. Under 'Create table A -> rollback -> Create table B' scenario, the oid of the
439  * mop of table B can be same as that of table A. Because the newest one is located at the head of redundancies
440  * in that case, we use the first fit method. */
441  }
442 
443  for (p = ws_Mop_table[slot].head; p != NULL; prev = p, p = p->hash_link)
444  {
445  c = oid_compare (WS_OID (mop), WS_OID (p));
446 
447  if (c == 0)
448  {
449  if (WS_ISVID (mop))
450  {
451  break;
452  }
453 
454  /* For real objects, must first discard the duplicate object and remove it from class_mop. */
455  /* TODO: This can happen in non-mvcc now. We can have a duplicate mop in a insert->rollback->insert, where
456  * the duplicate is the first inserted object. That object does not exist anymore and should probably be
457  * marked accordingly. */
458 
459  /* Decache object */
460  ws_decache (p);
461 
462  if (p->class_mop != NULL)
463  {
464  if (p->class_mop->class_link != NULL)
465  {
466  remove_class_object (p->class_mop, p);
467  }
468  else
469  {
470  p->class_mop = NULL;
471  p->class_link = NULL;
472  }
473  }
474 
475  break;
476  }
477 
478  if (c < 0)
479  {
480  break;
481  }
482  }
483 
484  if (p == NULL)
485  {
486  /* empty or reach at the tail of the list */
487  ws_Mop_table[slot].tail = mop;
488  }
489 
490  if (prev == NULL)
491  {
492  mop->hash_link = ws_Mop_table[slot].head;
493  ws_Mop_table[slot].head = mop;
494  }
495  else
496  {
497  mop->hash_link = prev->hash_link;
498  prev->hash_link = mop;
499  }
500 }
501 
502 /*
503  * ws_insert_mop_on_hash_link_with_position () - Insert a mop in hash table
504  * at the given slot, after prev
505  * mop.
506  *
507  * return : Void.
508  * mop (in) : New mop.
509  * slot (in) : Hash slot.
510  * prev (in) : Mop in hash list after which the new_mop should be added.
511  *
512  * NOTE: Real objects should have only one mop instance. This function does
513  * not check for duplicates. Therefore, make sure to use it only if
514  * OID conflict is not possible, or if duplicate was removed beforehand.
515  */
516 static void
518 {
519  if (prev == NULL)
520  {
521  if (ws_Mop_table[slot].tail == NULL)
522  {
523  /* empty list */
524  ws_Mop_table[slot].tail = mop;
525  }
526  mop->hash_link = ws_Mop_table[slot].head;
527  ws_Mop_table[slot].head = mop;
528  }
529  else
530  {
531  if (prev->hash_link == NULL)
532  {
533  /* append mop on the tail of the list */
534  ws_Mop_table[slot].tail = mop;
535  }
536  mop->hash_link = prev->hash_link;
537  prev->hash_link = mop;
538  }
539 }
540 
541 /*
542  * ws_mop_if_exists () - Get object mop if it exists in mop table.
543  *
544  * return : MOP or NULL if not found.
545  * oid (in) : Object identifier.
546  */
547 MOP
549 {
550  MOP mop = NULL;
551  unsigned int slot;
552  int c;
553 
554  if (OID_ISNULL (oid))
555  {
556  return NULL;
557  }
558 
559  /* look for existing entry */
560  slot = OID_PSEUDO_KEY (oid);
561  if (slot >= ws_Mop_table_size)
562  {
563  slot = slot % ws_Mop_table_size;
564  }
565 
566  /* compare with the last mop */
567  mop = ws_Mop_table[slot].tail;
568  if (mop)
569  {
570  c = oid_compare (oid, WS_OID (mop));
571  if (c > 0)
572  {
573  /* 'oid' is greater than the tail, which means 'oid' does not exist in the list NO need to traverse the
574  * list! */
575  return NULL;
576  }
577  else
578  {
579  /* c <= 0 */
580 
581  /* Unfortunately, we have to navigate the list when c == 0 */
582  /* See the comment of ws_insert_mop_on_hash_link() */
583 
584  for (mop = ws_Mop_table[slot].head; mop != NULL; mop = mop->hash_link)
585  {
586  c = oid_compare (oid, WS_OID (mop));
587  if (c == 0)
588  {
589  return mop;
590  }
591  else if (c < 0)
592  {
593  return NULL;
594  }
595  }
596  }
597  }
598 
599  return NULL;
600 }
601 
602 /*
603  * ws_mop - given a oid, find or create the corresponding MOP and add it to
604  * the workspace object table.
605  * return: MOP
606  * oid(in): oid
607  * class_mop(in): optional class MOP (can be null if not known)
608  *
609  * Note: If the class argument is NULL, it will be added to the class list
610  * when the object is cached.
611  */
612 
613 MOP
614 ws_mop (const OID * oid, MOP class_mop)
615 {
616  MOP mop, new_mop, prev;
617  unsigned int slot;
618  int c;
619 
620  if (OID_ISNULL (oid))
621  {
623  return NULL;
624  }
625 
626  /* look for existing entry */
627  slot = OID_PSEUDO_KEY (oid);
628  if (slot >= ws_Mop_table_size)
629  {
630  slot = slot % ws_Mop_table_size;
631  }
632 
633  /* compare with the last mop */
634  prev = NULL;
635  mop = ws_Mop_table[slot].tail;
636  if (mop)
637  {
638  c = oid_compare (oid, WS_OID (mop));
639  if (c > 0)
640  {
641  /* 'oid' is greater than the tail, which means 'oid' does not exist in the list NO need to traverse the
642  * list! */
643  prev = ws_Mop_table[slot].tail;
644  }
645  else
646  {
647  /* c <= 0 */
648 
649  /* Unfortunately, we have to navigate the list when c == 0 */
650  /* See the comment of ws_insert_mop_on_hash_link() */
651 
652  for (mop = ws_Mop_table[slot].head; mop != NULL; prev = mop, mop = mop->hash_link)
653  {
654  c = oid_compare (oid, WS_OID (mop));
655  if (c == 0)
656  {
657  if (mop->decached)
658  {
659  /*
660  * If a decached instance object has a class mop,
661  * we need to clear the information related the class mop,
662  * such as class_mop and class_link.
663  * Actually the information should be cleared when the mop
664  * is decached. The current implementation, however, assumes
665  * that decached objects have the information and there are
666  * many codes based on the assumption. So we clear them here,
667  * when reusing decached objects.
668  */
669  if (mop->class_mop != sm_Root_class_mop && class_mop != mop->class_mop)
670  {
671  if (mop->class_mop != NULL)
672  {
673  remove_class_object (mop->class_mop, mop);
674  }
675  /* temporary disable assert */
676  /* assert (mop->class_mop == NULL && mop->class_link == NULL); */
677  mop->class_mop = mop->class_link = NULL;
678  if (class_mop != NULL)
679  {
680  add_class_object (class_mop, mop);
681  }
682  }
683  mop->decached = 0;
684  }
685  return mop;
686  }
687  else if (c < 0)
688  {
689  /* find the node which is greater than I */
690  break;
691  }
692  }
693  }
694  }
695 
696  /* make a new mop entry */
697  new_mop = ws_make_mop (oid);
698  if (new_mop == NULL)
699  {
700  return NULL;
701  }
702 
703  if (class_mop != NULL)
704  {
705  if (add_class_object (class_mop, new_mop))
706  {
707  ws_free_mop (new_mop);
708  return NULL;
709  }
710  }
711 
712  /* install it into this slot list */
713  ws_insert_mop_on_hash_link_with_position (new_mop, slot, prev);
714  assert (ws_check_hash_link (slot) == NO_ERROR);
715 
716  return new_mop;
717 }
718 
719 /*
720  * ws_keys - return vid's keys and flags
721  * return: vid's keys
722  * vid(in): a virtual object if all OK, NULL otherwise
723  * flags(out): bit encoded properties of vid
724  */
725 DB_VALUE *
726 ws_keys (MOP vid, unsigned int *flags)
727 {
729 
730  if (!vid || !vid->is_vid)
731  {
732  return NULL;
733  }
734 
735  vid_info = WS_VID_INFO (vid);
736 
737  if (vid_info == NULL)
738  {
739  return NULL;
740  }
741 
742  *flags = vid_info->flags;
743  return &vid_info->keys;
744 }
745 
746 /*
747  * ws_vmop -
748  * return:
749  * class(in):
750  * flags(in):
751  * keys(in):
752  */
753 MOP
754 ws_vmop (MOP class_mop, int flags, DB_VALUE * keys)
755 {
756  MOP mop, new_mop;
757  int slot, is_vclass = 0;
759  DB_TYPE keytype;
760 
761  vid_info = NULL;
762  keytype = DB_VALUE_DOMAIN_TYPE (keys);
763 
764  switch (keytype)
765  {
766  case DB_TYPE_OBJECT:
767  /*
768  * a non-virtual object mop
769  * This will occur when reading the oid keys field of a vobject
770  * if it was read thru some interface that automatically
771  * swizzles oid's to objects.
772  */
773  mop = db_get_object (keys);
774  is_vclass = db_is_vclass (class_mop);
775  if (is_vclass < 0)
776  {
777  return NULL;
778  }
779  if (!is_vclass)
780  {
781  return mop;
782  }
783  mop = db_real_instance (mop);
784  if (mop == NULL)
785  {
786  return NULL;
787  }
788 
789  /* In this case, we need to set the class_mop. Or we may fail to get mop->object when fetch dirty version. See
790  * xlocator_fetch, locator_cache and locator_cache_have_object */
791  if (mop->class_mop == NULL)
792  {
793  mop->class_mop = mq_fetch_one_real_class (class_mop);
794  }
795 
796  db_make_object (keys, mop);
797  break;
798  case DB_TYPE_OID:
799  /*
800  * a non-virtual object mop
801  * This will occur when reading the oid keys field of a virtual object
802  * if it was read through some interface that does NOT swizzle.
803  * oid's to objects.
804  */
805  mop = ws_mop (&keys->data.oid, class_mop);
806  is_vclass = db_is_vclass (class_mop);
807  if (is_vclass < 0)
808  {
809  return NULL;
810  }
811  if (!is_vclass)
812  {
813  return mop;
814  }
815  db_make_object (keys, mop);
816  break;
817  default:
818  /* otherwise fall through to generic keys case */
819  break;
820  }
821 
822  slot = mht_valhash (keys, ws_Mop_table_size);
823  if (!(flags & VID_NEW))
824  {
825  for (mop = ws_Mop_table[slot].head; mop != NULL; mop = mop->hash_link)
826  {
827  if (mop->is_vid)
828  {
829  vid_info = WS_VID_INFO (mop);
830  if (class_mop == mop->class_mop)
831  {
832  /*
833  * NOTE, formerly called pr_value_equal. Don't coerce
834  * with the new tp_value_equal function but that may
835  * actually be desired here.
836  */
837  if (tp_value_equal (keys, &vid_info->keys, 0))
838  {
839  return mop;
840  }
841  }
842  }
843  }
844  }
845 
846  new_mop = ws_make_mop (NULL);
847  if (new_mop == NULL)
848  {
849  return NULL;
850  }
851 
852  new_mop->is_vid = 1;
853 
854  vid_info = WS_VID_INFO (new_mop) = (VID_INFO *) malloc (sizeof (VID_INFO));
855  if (vid_info == NULL)
856  {
858  goto abort_it;
859  }
860 
861  vid_info->flags = flags;
862  db_make_null (&vid_info->keys);
863 
864  if (pr_clone_value (keys, &vid_info->keys))
865  {
866  goto abort_it;
867  }
868 
869  if (add_class_object (class_mop, new_mop))
870  {
871  goto abort_it;
872  }
873 
874  /* install it into this slot list */
875  ws_insert_mop_on_hash_link (new_mop, slot);
876  assert (ws_check_hash_link (slot) == NO_ERROR);
877 
878  return new_mop;
879 
880 abort_it:
881  if (new_mop != NULL)
882  {
883  ws_free_mop (new_mop);
884  }
885 
886  return NULL;
887 }
888 
889 /*
890  * ws_rehash_vmop - remove the old hash entry, copy the object id attribute
891  * values and rehash.
892  * return: true if success
893  * false if mop is not vid or vid_fetch_instance failed or not found
894  * mop(in): Mop of virtual object to rehash
895  * classobj(in): Object for the class
896  * newkey(in): NULL for relational mop; newkey for OO mop
897  */
898 bool
899 ws_rehash_vmop (MOP mop, MOBJ classobj, DB_VALUE * newkey)
900 {
901  SM_CLASS *class_ = (SM_CLASS *) classobj;
902  DB_VALUE *keys, new_key;
903  MOP found, prev;
904  int slot;
906  SM_ATTRIBUTE *att;
907  char *mem;
908  int no_keys;
909  int key_index;
910  DB_VALUE val;
911  DB_VALUE *value = &val;
912  int att_seq_val;
913  MOBJ inst;
914 
915  if (!mop->is_vid)
916  {
917  return false;
918  }
919  ws_find (mop, &inst);
920  if (!inst)
921  {
923  }
924  if (!inst)
925  {
926  return false;
927  }
928 
929  vid_info = WS_VID_INFO (mop);
930  keys = &vid_info->keys;
931 
932  slot = mht_valhash (keys, ws_Mop_table_size);
933 
934  for (found = ws_Mop_table[slot].head, prev = NULL; found != mop && found != NULL; found = found->hash_link)
935  {
936  prev = found;
937  }
938 
939  if (found != mop)
940  {
941  return false;
942  }
943 
944  /* get new relational key */
945  no_keys = 0;
946  for (att = class_->attributes; att != NULL; att = (SM_ATTRIBUTE *) att->header.next)
947  {
948  if (att->flags & SM_ATTFLAG_VID)
949  {
950  ++no_keys;
951  }
952  }
953  if (no_keys > 1)
954  {
955  db_make_sequence (&new_key, set_create_sequence (no_keys));
956  }
957 
958  for (key_index = 0; key_index < no_keys; ++key_index)
959  {
960  for (att = class_->attributes; att != NULL; att = (SM_ATTRIBUTE *) att->header.next)
961  {
962  if ((att->flags & SM_ATTFLAG_VID) && (classobj_get_prop (att->properties, SM_PROPERTY_VID_KEY, &val)))
963  {
964  att_seq_val = db_get_int (&val);
965  if (att_seq_val == key_index)
966  {
967  /* Sets won't work as key components */
968  mem = inst + att->offset;
969  db_value_domain_init (&val, att->type->id, att->domain->precision, att->domain->scale);
970  att->type->getmem (mem, att->domain, &val);
971  if ((DB_VALUE_TYPE (value) == DB_TYPE_STRING) && (db_get_string (value) == NULL))
972  {
973  db_make_null (value);
974  }
975 
976  if (no_keys > 1)
977  {
978  if (set_put_element (db_get_set (&new_key), key_index, &val) < 0)
979  {
980  return false;
981  }
982  }
983  else
984  {
985  pr_clone_value (&val, &new_key);
986  }
987  pr_clear_value (&val);
988  }
989  }
990  }
991  }
992 
993  pr_clear_value (keys);
994  pr_clone_value (&new_key, keys);
995  pr_clear_value (&new_key);
996 
997  /* remove it from the original list */
998  if (prev == NULL)
999  {
1000  ws_Mop_table[slot].head = mop->hash_link;
1001  }
1002  else
1003  {
1004  prev->hash_link = mop->hash_link;
1005  }
1006 
1007  if (ws_Mop_table[slot].tail == mop)
1008  {
1009  /* I was the tail of the list */
1010  ws_Mop_table[slot].tail = prev;
1011  }
1012 
1013  assert (ws_check_hash_link (slot) == NO_ERROR);
1014 
1015  mop->hash_link = NULL;
1016 
1017  /* move to the new list */
1018  slot = mht_valhash (keys, ws_Mop_table_size);
1019  ws_insert_mop_on_hash_link (mop, slot);
1020  assert (ws_check_hash_link (slot) == NO_ERROR);
1021 
1022  return true;
1023 }
1024 
1025 /*
1026  * ws_new_mop - optimized version of ws_mop when OID being entered into the
1027  * workspace is guaranteed to be unique.
1028  * return: new MOP
1029  * oid(in): object OID
1030  * class_mop(in): class mop of object
1031  *
1032  * Note:
1033  * This happens when temporary OIDs are generated for newly created objects.
1034  * It assumes that the MOP must be created and does not bother searching
1035  * the hash table collision list looking for duplicates.
1036  */
1037 MOP
1038 ws_new_mop (OID * oid, MOP class_mop)
1039 {
1040  MOP mop;
1041  unsigned int slot;
1042 
1043  mop = NULL;
1044  if (OID_ISNULL (oid))
1045  {
1047  return NULL;
1048  }
1049 
1050  slot = OID_PSEUDO_KEY (oid);
1051  if (slot >= ws_Mop_table_size)
1052  {
1053  slot = slot % ws_Mop_table_size;
1054  }
1055 
1056  mop = ws_make_mop (oid);
1057  if (mop == NULL)
1058  {
1059  return NULL;
1060  }
1061 
1062  if (class_mop != NULL)
1063  {
1064  if (add_class_object (class_mop, mop))
1065  {
1066  ws_free_mop (mop);
1067  return NULL;
1068  }
1069  }
1070 
1071  ws_insert_mop_on_hash_link (mop, slot);
1072  assert (ws_check_hash_link (slot) == NO_ERROR);
1073 
1074  return (mop);
1075 }
1076 
1077 /*
1078  * ws_update_oid_and_class - change the OID of a MOP and recache the class mop
1079  * if it has been changed
1080  * return: void
1081  * mop(in/out) : MOP whose OID needs to be changed
1082  * newoid(in) : new OID
1083  * new_class_oid : new class OID
1084  *
1085  * Note:
1086  * This is called in three cases:
1087  * 1. Newly created objects are flushed and are given a permanent OID.
1088  * 2. An object changes partition after update.
1089  *
1090  * If the object belongs to a partitioned class, it will
1091  * have a different class oid here (i.e. the partition in
1092  * which it was placed). We have to fetch the partition mop
1093  * and recache it here.
1094  */
1095 int
1096 ws_update_oid_and_class (MOP mop, OID * new_oid, OID * new_class_oid)
1097 {
1098  MOP class_mop = NULL;
1099  bool relink = false;
1100  class_mop = ws_class_mop (mop);
1101 
1102  if (class_mop == NULL || !OID_EQ (WS_OID (class_mop), new_class_oid))
1103  {
1104  /* we also need to disconnect this instance from class_mop and add it to new_class_oid */
1105  if (class_mop != NULL)
1106  {
1107  remove_class_object (class_mop, mop);
1108  }
1109  relink = true;
1110  class_mop = ws_mop (new_class_oid, NULL);
1111  if (class_mop == NULL)
1112  {
1113  assert (false);
1114  return ER_FAILED;
1115  }
1116  }
1117 
1118  /* Make sure that we have the new class in workspace */
1119  if (class_mop->object == NULL)
1120  {
1121  int error = NO_ERROR;
1122  SM_CLASS *smclass = NULL;
1123  /* No need to check authorization here */
1124  error = au_fetch_class_force (class_mop, &smclass, AU_FETCH_READ);
1125  if (error != NO_ERROR)
1126  {
1127  return error;
1128  }
1129  }
1130  mop->class_mop = class_mop;
1131  ws_update_oid (mop, new_oid);
1132  if (relink)
1133  {
1134  add_class_object (class_mop, mop);
1135  }
1136  return NO_ERROR;
1137 }
1138 
1139 /*
1140  * ws_update_oid - change the OID of a MOP. Also update position in
1141  * hash table.
1142  *
1143  * return: void
1144  * mop(in/out): MOP whose OID needs to be changed
1145  * newoid(in): new OID
1146  *
1147  * Note:
1148  * This is called in three cases:
1149  * 1. Newly created objects are flushed and are given a permanent OID.
1150  * 2. An object changes partition after update.
1151  * 3. MVCC is enabled and object changes OID after update.
1152  */
1153 void
1154 ws_update_oid (MOP mop, OID * newoid)
1155 {
1156  MOP mops, prev;
1157  unsigned int slot;
1158 
1159  /* find current entry */
1160  slot = OID_PSEUDO_KEY (WS_OID (mop));
1161  if (slot >= ws_Mop_table_size)
1162  {
1163  slot = slot % ws_Mop_table_size;
1164  }
1165 
1166  mops = ws_Mop_table[slot].head;
1167  for (prev = NULL; mops != mop && mops != NULL; mops = mops->hash_link)
1168  {
1169  prev = mops;
1170  }
1171 
1172  if (mops != mop)
1173  {
1175  return;
1176  }
1177 
1178  /* remove the current entry */
1179  if (prev == NULL)
1180  {
1181  ws_Mop_table[slot].head = mop->hash_link;
1182  }
1183  else
1184  {
1185  prev->hash_link = mop->hash_link;
1186  }
1187 
1188  if (ws_Mop_table[slot].tail == mop)
1189  {
1190  /* I was the tail of the list */
1191  ws_Mop_table[slot].tail = prev;
1192  }
1193 
1194  assert (ws_check_hash_link (slot) == NO_ERROR);
1195 
1196  mop->hash_link = NULL;
1197 
1198  /* assign the new oid */
1199  COPY_OID (WS_REAL_OID (mop), newoid);
1200 
1201  /* force the MOP into the table at the new slot position */
1202  slot = OID_PSEUDO_KEY (newoid);
1203  if (slot >= ws_Mop_table_size)
1204  {
1205  slot = slot % ws_Mop_table_size;
1206  }
1207 
1208  ws_insert_mop_on_hash_link (mop, slot);
1209  assert (ws_check_hash_link (slot) == NO_ERROR);
1210 }
1211 
1212 /*
1213  * INTERNAL GARBAGE COLLECTOR
1214  */
1215 
1216 /*
1217  * ws_disconnect_deleted_instances - called when a class MOP is being garbage
1218  * collected
1219  * return: void
1220  * classop(in/out): class MOP
1221  *
1222  * Note:
1223  * This should only happen if the class has been deleted and is no
1224  * longer on the resident class list.
1225  * At this point, all instances should have been flushed and decached.
1226  * Here we make sure that any instance MOPs connected to this class
1227  * get disconnected.
1228  */
1229 void
1231 {
1232  MOP m, next;
1233 
1234  if (classop == sm_Root_class_mop)
1235  {
1236  return;
1237  }
1238 
1239  for (m = classop->class_link, next = NULL; m != Null_object && m != NULL; m = next)
1240  {
1241  next = m->class_link;
1242 
1243  if (m->object != NULL)
1244  {
1245  /*
1246  * there should be no cached object here ! since the class is gone,
1247  * we no longer no how to free this. If this becomes a normal case,
1248  * we'll have to wait and decache the class AFTER all the instances
1249  * have been decached
1250  */
1252  m->object = NULL;
1253  }
1254 
1255  m->class_link = NULL;
1256  m->class_mop = NULL;
1257  }
1258  classop->class_link = Null_object;
1259 
1260 }
1261 
1262 /*
1263  * ws_remove_resident_class - remove a class from the resident class list.
1264  * return: void
1265  * classop(in/out): class mop
1266  *
1267  * Note:
1268  * This should ONLY be called if the class has been deleted and all of
1269  * the instances have been decached.
1270  * Once the class MOP is removed from the resident class list, it will
1271  * be subject to garbage collection.
1272  * This can be called both from ws_cull_mops where the class has
1273  * already been decached and from the schema manager when it
1274  * finishes deleting a class.
1275  */
1276 void
1278 {
1279  if (classop != sm_Root_class_mop)
1280  {
1281  /* make sure we don't have anyone referencing us */
1283  ml_remove (&ws_Resident_classes, classop);
1284  }
1285 }
1286 
1287 /*
1288  * emergency_remove_dirty - reclaim a MOP that is on the dirty list.
1289  * return: void
1290  * op(in/out): mop that needs to be garbage collected
1291  *
1292  * Note:
1293  * This should never be called. It will be called by ws_cull_mops if
1294  * the garbage collector attempts to reclaim a MOP that is on the
1295  * dirty list. If this happens, try to avoid system crash by removing
1296  * it gracefully but an error must be signalled because the database
1297  * will be in an inconsistent state since the dirty objects are being
1298  * freed without flushing them to the server.
1299  */
1300 static void
1302 {
1303  MOP mop, prev;
1304 
1305  /*
1306  * make sure we can get to op's class dirty list because without that
1307  * there is no dirty list from which we can remove op.
1308  */
1309  if (op->dirty_link != NULL && op->class_mop != NULL && op->class_mop->dirty_link != NULL)
1310  {
1311  /* search for op in op's class' dirty list */
1312  prev = NULL;
1313 
1314  for (mop = op->class_mop->dirty_link; mop != Null_object && mop != op; mop = mop->dirty_link)
1315  {
1316  prev = mop;
1317  }
1318 
1319  /* remove op from op's class' dirty list */
1320  if (mop == op)
1321  {
1322  if (prev == NULL)
1323  {
1324  op->class_mop->dirty_link = op->dirty_link;
1325  }
1326  else
1327  {
1328  prev->dirty_link = op->dirty_link;
1329  }
1330  op->dirty_link = NULL;
1331  }
1332  }
1333 }
1334 
1335 /*
1336  * ws_unlink_from_commit_mops_list -
1337  * return: void
1338  * op(in): mop that needs to be unlinked from ws_Commit_mops list
1339  *
1340  */
1341 static void
1343 {
1344  MOP mop, prev, next, to_be_next;
1345 
1346  assert (op != NULL);
1347 
1348  prev = NULL;
1349  mop = ws_Commit_mops;
1350  while (mop)
1351  {
1352  next = mop->commit_link;
1353  if (next == mop)
1354  {
1355  /* the last node */
1356  to_be_next = prev;
1357  }
1358  else
1359  {
1360  to_be_next = next;
1361  }
1362 
1363  if (mop == op)
1364  {
1365  if (prev == NULL)
1366  {
1367  assert (ws_Commit_mops == mop);
1368 
1369  ws_Commit_mops = to_be_next;
1370  }
1371  else
1372  {
1373  prev->commit_link = to_be_next;
1374  }
1375 
1376  op->commit_link = NULL;
1377  return;
1378  }
1379 
1380  prev = mop;
1381  if (next == mop)
1382  {
1383  /* the last node */
1384  mop = NULL;
1385  }
1386  else
1387  {
1388  mop = next;
1389  }
1390  }
1391 }
1392 
1393 /*
1394  * ws_cull_mops - callback function for the garbage collector
1395  * return: void
1396  *
1397  * Note:
1398  * This is a callback function for the garbage collector. Here we map
1399  * through the MOP table and check for unmarked mops. Unmarked mops
1400  * may be removed from the table and freed. This is the only
1401  * function that is allowed to remove MOPs from the table once they
1402  * have been interned.
1403  * I forget exactly what the two tables here are for.
1404  */
1405 void
1407 {
1408  MOP mops, prev, next;
1409  DB_OBJLIST *m;
1410  unsigned int slot, count;
1411 
1412  for (m = ws_Resident_classes; m != NULL; m = m->next)
1413  {
1414  prev = NULL;
1415  for (mops = m->op->class_link; mops != NULL && mops != Null_object; mops = next)
1416  {
1417  next = mops->class_link;
1418  if (!mops->released)
1419  {
1420  prev = mops;
1421  }
1422  else
1423  {
1424  if (prev == NULL)
1425  {
1426  m->op->class_link = next;
1427  }
1428  else
1429  {
1430  prev->class_link = next;
1431  }
1432  mops->class_link = NULL;
1433  }
1434  }
1435  }
1436 
1437  /* should make sure table is the same as ws_Mop_table */
1438  count = 0;
1439  for (slot = 0; slot < ws_Mop_table_size; slot++)
1440  {
1441  prev = NULL;
1442  for (mops = ws_Mop_table[slot].head; mops != NULL; mops = next)
1443  {
1444  next = mops->hash_link;
1445  if (!mops->released)
1446  {
1447  prev = mops;
1448  }
1449  else
1450  {
1451  if (mops == sm_Root_class_mop)
1452  {
1453  /* can't have rootclass on the garbage list */
1455  ws_Stats.corruptions++;
1456  /* probably fatal to continue here */
1457  }
1458 
1459  if (mops->class_mop == sm_Root_class_mop && mops->dirty)
1460  {
1462  ws_Stats.dirty_list_emergencies++;
1463  emergency_remove_dirty (mops);
1464  }
1465  if (mops->class_mop != sm_Root_class_mop && mops->dirty_link)
1466  {
1468  ws_Stats.dirty_list_emergencies++;
1469  emergency_remove_dirty (mops);
1470  }
1471 
1472  /* remove the mop from the hash table */
1473  if (prev == NULL)
1474  {
1475  ws_Mop_table[slot].head = next;
1476  }
1477  else
1478  {
1479  prev->hash_link = next;
1480  }
1481 
1482  if (ws_Mop_table[slot].tail == mops)
1483  {
1484  /* I was the tail of the list */
1485  ws_Mop_table[slot].tail = prev;
1486  }
1487 
1488  assert (ws_check_hash_link (slot) == NO_ERROR);
1489 
1490  /* free the associated object, note for classes, this could be a fairly complex operation since all the
1491  * instances are decached as well */
1492  ws_decache (mops);
1493 
1494  /* if this is a vmop, we need to clear the keys */
1495  if (mops->is_vid && WS_VID_INFO (mops))
1496  {
1497  pr_clear_value (&WS_VID_INFO (mops)->keys);
1498  }
1499 
1500  if (mops->class_mop != NULL)
1501  {
1502  if (mops->class_mop != sm_Root_class_mop)
1503  {
1504  /*
1505  * Since we removed the GC'd MOPs from the resident
1506  * instance list before we started the hash table
1507  * map, we shouldn't see any at this point. If
1508  * we do, its either a corruption or the class
1509  * wasn't on the resident class list for some
1510  * reason. Remove it and increment the counter.
1511  */
1512  if (mops->class_link != NULL)
1513  {
1514  remove_class_object (mops->class_mop, mops);
1515  ws_Stats.instance_list_emergencies++;
1516  }
1517  }
1518  else
1519  {
1520  /*
1521  * carefully remove the class from the resident
1522  * instance list and make sure no instances are
1523  * still going to be referencing this thing
1524  */
1525  ws_remove_resident_class (mops);
1526  }
1527  }
1528 
1530 
1531  /* return mop to the garbage collector */
1532  ws_free_mop (mops);
1533 
1534  count++;
1535  }
1536  }
1537  }
1538 
1539  ws_Stats.mops_freed += count;
1540 }
1541 
1542 /*
1543  * ws_intern_instances - flush and cull all MOPs of given class MOP
1544  * return: void
1545  * class_mop(in): class MOP
1546  */
1547 void
1549 {
1550  if (locator_flush_all_instances (class_mop, DECACHE) != NO_ERROR)
1551  {
1552  return;
1553  }
1554 
1555  ws_filter_dirty ();
1556  ws_cull_mops ();
1557 }
1558 
1559 /*
1560  * ws_release_instance - set the released field of a mop
1561  * return: void
1562  * mop(out): mop to set released field
1563  */
1564 void
1566 {
1567  if (mop != NULL)
1568  {
1569  mop->released = 1;
1570  }
1571 }
1572 
1573 /*
1574  * ws_release_user_instance - set the released field only for a user mop
1575  * return: void
1576  * mop(out): mop to set released field
1577  */
1578 void
1580 {
1581  /* to keep instances of system classes, for instance, db_serial's. This prevents from dangling references to serial
1582  * objects during replication. The typical scenario is to update serials, cull mops which clears the mop up, and then
1583  * truncate the table which leads updating the serial mop to reset its values. */
1584  if (db_is_system_class (mop->class_mop) > 0)
1585  {
1586  return;
1587  }
1588 
1589  ws_release_instance (mop);
1590 }
1591 
1592 /*
1593  * DIRTY LIST MAINTENANCE
1594  */
1595 
1596 /*
1597  * The dirty list which used to be a global dirty list is now kept by class.
1598  * A dirty list (possibly empty) is rooted at each class' dirty_link and is
1599  * chained through the dirty_link field in the object_pointer. This makes
1600  * maintenance of the dirty_list very simple at the expense of an object_pointer
1601  * that is one word larger.
1602  *
1603  * When an object is marked as "clean" it is not immediately removed from the
1604  * dirty list. Since we don`t have a doubly linked list, we will need to
1605  * perform a linear search of the dirty list in order to remove the element.
1606  * Physicaly altering the dirty list as objects are "cleaned" also has
1607  * unpleasant side effects for the dirty object iterator function below.
1608  *
1609  * Instead, the dirty object iterator will remove objects from the dirty list
1610  * as it sweeps through them.
1611  *
1612  * Note that doing this also requires an extra "dirty bit" in addition to the
1613  * dirty list link field.
1614  */
1615 
1616 /*
1617  * ws_dirty - Add an object to the dirty list of its class.
1618  * return: void
1619  * op(in/out): mop to make dirty
1620  */
1621 void
1623 {
1624  /*
1625  * don't add the root class to any dirty list. otherwise, later traversals
1626  * of that dirty list will loop forever.
1627  */
1628 
1629  if (op == NULL || op == sm_Root_class_mop)
1630  {
1631  return;
1632  }
1633 
1634  WS_SET_DIRTY (op);
1635  /*
1636  * add_class_object makes sure each class' dirty list (even an empty one)
1637  * is always terminated by the magical Null_object. Therefore, this test
1638  * "op->dirty_link == NULL" makes sure class objects are not added to
1639  * the Rootclass' dirty list.
1640  */
1641  if (op->dirty_link != NULL)
1642  {
1643  return;
1644  }
1645 
1646  if (op->class_mop == NULL)
1647  {
1648  /* in case of SERIALIZABLE conflict the object will be decached later */
1650  {
1651  /* SERIOUS INTERNAL ERROR */
1653  }
1654 
1655  ws_Stats.uncached_classes++;
1656  }
1657  else
1658  {
1659  /*
1660  * add op to op's class' dirty list only if op is not yet there.
1661  * The preceding "op->dirty_link == NULL" asserts that op is not
1662  * on any dirty list so we can simply prepend op to op's class'
1663  * dirty list.
1664  */
1665  op->dirty_link = op->class_mop->dirty_link;
1666  op->class_mop->dirty_link = op;
1667  }
1668 
1669 }
1670 
1671 /*
1672  * ws_clean - clears the dirty bit of a mop
1673  * return: void
1674  * op(in/out): mop to mark clean
1675  *
1676  * Note:
1677  * Making dirty bit cleared will cause the object to be ignored by the
1678  * dirty list iterator
1679  */
1680 void
1682 {
1683  /*
1684  * because pinned objects can be in a state of direct modification, we
1685  * can't reset the dirty bit after a workspace panic flush because this
1686  * would lose any changes made to the pinned object after the flush
1687  */
1688 
1689  if (!op->pinned)
1690  {
1691  WS_RESET_DIRTY (op);
1692  }
1693  else
1694  {
1695  ws_Stats.pinned_cleanings++; /* need to know how often this happens */
1696  }
1697 }
1698 
1699 /*
1700  * ws_map_dirty_internal - iterate over elements in the dirty list calling map
1701  * function with the element.
1702  * return: map status code
1703  * function(in): function to apply to the dirty list elements
1704  * args(in): arguments to pass to map function
1705  * classes_only(in): flag indicating map over class objects only
1706  *
1707  * Note:
1708  * As a side effect, non-dirty objects that are still in the dirty list
1709  * are removed. The map function must return WS_MAP_CONTINUE each time
1710  * to map over the entire list. If the map function returns any other
1711  * value, the loop will terminate. The function will return
1712  * WS_MAP_SUCCESS if the loop completed or if the map function never
1713  * returned WS_MAP_FAIL. If the map function returns WS_MAP_FAIL, the
1714  * loop will terminate and this will be returned from the function.
1715  */
1716 static int
1717 ws_map_dirty_internal (MAPFUNC function, void *args, bool classes_only)
1718 {
1719  MOP op, op2, next, prev, class_mop;
1720  DB_OBJLIST *m;
1721  int status = WS_MAP_CONTINUE;
1722  int collected_num_dirty_mop = 0;
1723 
1724  /* traverse the resident classes to get to their dirty lists */
1725  for (m = ws_Resident_classes; m != NULL && status == WS_MAP_CONTINUE && (class_mop = m->op) != NULL; m = m->next)
1726  {
1727 
1728  /* is this a dirty class? */
1729  if (class_mop->class_mop == sm_Root_class_mop && class_mop->dirty)
1730  {
1731  if (!classes_only)
1732  {
1733  collected_num_dirty_mop++;
1734  }
1735 
1736  Ws_dirty = true;
1737  /* map given function over this dirty class */
1738  if (function != NULL)
1739  {
1740  status = (*function) (class_mop, args);
1741  }
1742 
1743  /* Don't continue if something bad happened */
1744  if (status == WS_MAP_FAIL)
1745  {
1746  break;
1747  }
1748  }
1749 
1750  /* skip over all non-dirty objects at the start of each dirty list */
1751  for (op = class_mop->dirty_link; op != Null_object && op->dirty == 0; op = next)
1752  {
1753  next = op->dirty_link;
1754  op->dirty_link = NULL;
1755  }
1756  class_mop->dirty_link = op;
1757 
1758  prev = NULL;
1759  next = Null_object;
1760 
1761  /* map given function over this class' dirty list */
1762  for (; op != Null_object && status == WS_MAP_CONTINUE; op = next)
1763  {
1764 
1765  /*
1766  * if we get here, then op must be dirty. So turn the static dirty
1767  * flag on (just in case we've been called from ws_has_updated).
1768  * ws_has_updated uses this static flag to check for the presence
1769  * of dirty objects.
1770  */
1771  if (!classes_only)
1772  {
1773  collected_num_dirty_mop++;
1774  }
1775 
1776  Ws_dirty = true;
1777 
1778  if (function != NULL)
1779  {
1780  if (!classes_only)
1781  {
1782  status = (*function) (op, args);
1783  }
1784 
1785  else if (op->class_mop == sm_Root_class_mop)
1786  {
1787  status = (*function) (op, args);
1788  }
1789  }
1790 
1791  /* Don't continue if something bad happened */
1792  if (status == WS_MAP_FAIL)
1793  {
1794  break;
1795  }
1796 
1797  next = op->dirty_link;
1798 
1799  /* remember the last dirty object in the list */
1800  if (op->dirty == 1)
1801  {
1802  prev = op;
1803  }
1804  else
1805  {
1806  op->dirty_link = NULL; /* remove it from the list */
1807  }
1808 
1809  /* find the next non-dirty object */
1810  for (op2 = next; op2 != Null_object && op2->dirty == 0; op2 = next)
1811  {
1812  next = op2->dirty_link;
1813  op2->dirty_link = NULL;
1814  }
1815  next = op2;
1816 
1817  /* remove intervening clean objects */
1818  if (prev == NULL)
1819  {
1820  class_mop->dirty_link = next;
1821  }
1822  else
1823  {
1824  prev->dirty_link = next;
1825  }
1826  }
1827  }
1828 
1829  if (status != WS_MAP_FAIL)
1830  {
1831  status = WS_MAP_SUCCESS;
1832 
1833  if (!classes_only && ws_Num_dirty_mop != collected_num_dirty_mop)
1834  {
1835  ws_Num_dirty_mop = collected_num_dirty_mop;
1836  }
1837  }
1838 
1839  return (status);
1840 }
1841 
1842 /*
1843  * ws_map_dirty - specializations of ws_map_dirty_internal function
1844  * return: map status code
1845  * function(in): map function
1846  * args(in): map function argument
1847  */
1848 int
1849 ws_map_dirty (MAPFUNC function, void *args)
1850 {
1851  return (ws_map_dirty_internal (function, args, false));
1852 }
1853 
1854 /*
1855  * ws_filter_dirty - remove any mops that don't have their dirty bit set.
1856  * return: void
1857  */
1858 void
1860 {
1861  ws_map_dirty_internal (NULL, NULL, false);
1862 }
1863 
1864 /*
1865  * RESIDENT INSTANCE LIST MAINTENANCE
1866  */
1867 /*
1868  * Each class object in the workspace maintains a list of all the instances
1869  * for that class. This list is rooted in the class_link field of the class
1870  * MOP and the instances are chained through their class_link field.
1871  */
1872 
1873 /*
1874  * add_class_object - Add an instance MOP to the class' resident instance list.
1875  * return: NO_ERROR if successful, error code otherwise
1876  * class_mop(in/out): class mop
1877  * obj(in/out): instance mop
1878  */
1879 static int
1880 add_class_object (MOP class_mop, MOP obj)
1881 {
1882  int error = NO_ERROR;
1883 
1884  if (class_mop == sm_Root_class_mop)
1885  {
1886  /*
1887  * class MOP, initialize the object list, do this only if it isn't
1888  * already initialized, this may happen if the workspace is cleared
1889  * and nothing is cached. In this case the class_link lists are still
1890  * valid. When the class comes back in, we don't want to destroy the
1891  * previously built instance lists.
1892  */
1893  if (obj->class_link == NULL)
1894  {
1895  obj->class_link = Null_object;
1896  }
1897  if (obj->dirty_link == NULL)
1898  {
1899  obj->dirty_link = Null_object;
1900  }
1901  obj->class_mop = class_mop;
1902 
1903  /* add the class object to the root memory resident class list */
1904  error = ml_add (&ws_Resident_classes, obj, NULL);
1905  }
1906  else
1907  {
1908  /* must make sure this gets initialized, should have been done already when the class was cached in the clause
1909  * above */
1910  if (class_mop->class_link == NULL)
1911  {
1912  class_mop->class_link = Null_object;
1913  }
1914 
1915  if (class_mop->object == NULL)
1916  {
1917  error = ER_WS_CLASS_NOT_CACHED;
1918  er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
1919  ws_Stats.uncached_classes++;
1920  }
1921  else
1922  {
1923  if ((obj->class_mop != NULL) && (obj->class_mop != class_mop))
1924  {
1926  er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
1927  ws_Stats.ignored_class_assignments++;
1928  }
1929  else
1930  {
1931  obj->class_mop = class_mop;
1932  if (obj->class_link == NULL)
1933  {
1934  obj->class_link = class_mop->class_link;
1935  class_mop->class_link = obj;
1936  }
1937  }
1938  }
1939  }
1940  return error;
1941 }
1942 
1943 /*
1944  * remove_class_object - Remove an instance from a class' resident instance
1945  * list.
1946  * return:void
1947  * class_mop(in/out): class mop
1948  * obj(in): instance mop
1949  */
1950 static void
1951 remove_class_object (MOP class_mop, MOP obj)
1952 {
1953  MOP o, prev;
1954 
1955  if (class_mop->class_link == NULL)
1956  {
1957  return;
1958  }
1959 
1960  for (o = class_mop->class_link, prev = NULL; o != Null_object && o != obj; o = o->class_link)
1961  {
1962  if (o != obj)
1963  {
1964  prev = o;
1965  }
1966  }
1967 
1968  if (o == Null_object)
1969  {
1970  return;
1971  }
1972 
1973  if (prev == NULL)
1974  {
1975  class_mop->class_link = o->class_link;
1976  }
1977  else
1978  {
1979  prev->class_link = o->class_link;
1980  }
1981  o->class_link = NULL;
1982  o->class_mop = NULL;
1983 
1984 }
1985 
1986 /*
1987  * ws_set_class - set the class of an instance mop.
1988  * return: void
1989  * inst(in/out): instance mop
1990  * class_mop(in/out): class mop
1991  *
1992  * Note:
1993  * This will make sure the MOP is tagged with the class and that the
1994  * instance is added to the class' resident instance list.
1995  */
1996 void
1997 ws_set_class (MOP inst, MOP class_mop)
1998 {
1999  if (inst->class_mop != class_mop)
2000  {
2001  (void) add_class_object (class_mop, inst);
2002  }
2003 }
2004 
2005 /*
2006  * ws_map_class_dirty - iterate over all of the dirty instances of a class and
2007  * calls supplied function.
2008  * return: WS_MAP_SUCCESS or WS_MAP_FAIL
2009  * class_op(in/out): class of a mop to iterate over
2010  * function(in): map function
2011  * args(in): map function argument
2012  *
2013  * Note:
2014  * The mapping (calling the map function) will continue as long as the
2015  * map function returns WS_MAP_CONTINUE
2016  */
2017 int
2018 ws_map_class_dirty (MOP class_op, MAPFUNC function, void *args)
2019 {
2020  MOP op, op2, next, prev;
2021  DB_OBJLIST *l;
2022  int status = WS_MAP_CONTINUE;
2023 
2024  if (class_op == sm_Root_class_mop)
2025  {
2026  /* rootclass, must map through dirty resident class list */
2027  for (l = ws_Resident_classes; l != NULL && status == WS_MAP_CONTINUE; l = l->next)
2028  {
2029  /* should we be ignoring deleted class MOPs ? */
2030  if (l->op && l->op->dirty && function != NULL)
2031  {
2032  status = (*function) (l->op, args);
2033  }
2034  }
2035  }
2036  else if (class_op->class_mop == sm_Root_class_mop)
2037  { /* normal class */
2038  /* skip over all non-dirty objects at the start of dirty list */
2039  for (op = class_op->dirty_link, next = Null_object; op != Null_object && op->dirty == 0; op = next)
2040  {
2041  next = op->dirty_link;
2042  op->dirty_link = NULL;
2043  }
2044  class_op->dirty_link = op;
2045 
2046  prev = NULL;
2047  next = Null_object;
2048 
2049  /* map given function over this class' dirty list */
2050  for (; op != Null_object && status == WS_MAP_CONTINUE; op = next)
2051  {
2052 
2053  /* what if it is deleted ? */
2054  if (function != NULL)
2055  {
2056  status = (*function) (op, args);
2057  }
2058 
2059  /* Don't continue if something bad happened */
2060  if (status == WS_MAP_FAIL)
2061  {
2062  break;
2063  }
2064 
2065  next = op->dirty_link;
2066 
2067  /* remember the last dirty object in the list */
2068  if (op->dirty == 1)
2069  {
2070  prev = op;
2071  }
2072  else
2073  {
2074  op->dirty_link = NULL; /* remove it from the list */
2075  }
2076 
2077  /* find the next non-dirty object */
2078  for (op2 = next; op2 != Null_object && op2->dirty == 0; op2 = next)
2079  {
2080  next = op2->dirty_link;
2081  op2->dirty_link = NULL;
2082  }
2083  next = op2;
2084 
2085  /* remove intervening clean objects */
2086  if (prev == NULL)
2087  {
2088  class_op->dirty_link = next;
2089  }
2090  else
2091  {
2092  prev->dirty_link = next;
2093  }
2094  }
2095  }
2096  /* else we got an object MOP, don't do anything */
2097 
2098  if (status != WS_MAP_FAIL)
2099  {
2100  status = WS_MAP_SUCCESS;
2101  }
2102 
2103  return (status);
2104 }
2105 
2106 /*
2107  * ws_map_class - iterates over all of the resident instances of a class
2108  * and calls the supplied function.
2109  * return: WS_MAP_SUCCESS or WS_MAP_FAIL
2110  * class_op(in): class of interest
2111  * function(in): map function
2112  * args(in): map function argument
2113  *
2114  * Note:
2115  * The map will continue as long as the map function returns WS_MAP_CONTINUE.
2116  */
2117 int
2118 ws_map_class (MOP class_op, MAPFUNC function, void *args)
2119 {
2120  MOP op = NULL, save_class_link = NULL;
2121  DB_OBJLIST *l = NULL;
2122  int status = WS_MAP_CONTINUE;
2123 
2124  if (class_op == sm_Root_class_mop)
2125  {
2126  /* rootclass, must map through resident class list */
2127  for (l = ws_Resident_classes; l != NULL && status == WS_MAP_CONTINUE; l = l->next)
2128  {
2129  /* should we be ignoring deleted class MOPs ? */
2130  status = (*function) (l->op, args);
2131  }
2132  }
2133  else if (class_op->class_mop == sm_Root_class_mop)
2134  {
2135  /* normal class */
2136  if (class_op->class_link != NULL)
2137  {
2138  for (op = class_op->class_link; op != Null_object && status == WS_MAP_CONTINUE; op = save_class_link)
2139  {
2140  save_class_link = op->class_link;
2141  /*
2142  * should we only call the function if the object has been
2143  * loaded ? what if it is deleted ?
2144  */
2145  status = (*function) (op, args);
2146  }
2147  }
2148  }
2149  /* else we got an object MOP, don't do anything */
2150 
2151  if (status != WS_MAP_FAIL)
2152  {
2153  status = WS_MAP_SUCCESS;
2154  }
2155 
2156  return (status);
2157 }
2158 
2159 /*
2160  * mark_instance_deleted - mark a mop as deleted
2161  * return: WS_MAP_CONTINUE
2162  * op(in/out): mop of interest
2163  * args(in): not used
2164  *
2165  */
2166 static int
2167 mark_instance_deleted (MOP op, void *args)
2168 {
2169  WS_SET_DELETED (op);
2170  if (op->pinned)
2171  {
2172 /* er_set(ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_WS_PIN_VIOLATION, 0); */
2173  op->pinned = 0;
2174  }
2175 
2176  return (WS_MAP_CONTINUE);
2177 }
2178 
2179 /*
2180  * ws_mark_instances_deleted - mark class mops as deleted
2181  * return: void
2182  * class_op(in): mop class of interest
2183  *
2184  * Note:
2185  * This is called by the schema manager when a class is deleted. It will
2186  * loop through all of the MOPs for instances of this class and mark
2187  * them as deleted. This makes it more effecient to detect deleted
2188  * objects in the upper layers. This may be something the locator should
2189  * do when locator_remove_class is called ?
2190  */
2191 void
2193 {
2195 }
2196 
2197 /*
2198  * CLASS NAME CACHE
2199  */
2200 
2201 /*
2202  * ws_add_classname - caches a classname in the workspace classname table.
2203  * return: void
2204  * classobj(in): pointer to class strucure
2205  * classmop(in): mop for this class
2206  * cl_name(in): class name
2207  *
2208  * Note:
2209  * It should be called by ws_cache when a class is given to the workspace.
2210  */
2211 void
2212 ws_add_classname (MOBJ classobj, MOP classmop, const char *cl_name)
2213 {
2214  MOP current;
2215 
2216  if (classobj == NULL || classmop == NULL)
2217  {
2218  return;
2219  }
2220 
2221  current = (MOP) mht_get (Classname_cache, sm_ch_name (classobj));
2222 
2223  if (current == NULL)
2224  {
2225  mht_put (Classname_cache, cl_name, classmop);
2226  }
2227  else
2228  {
2229  if (current != classmop)
2230  {
2231  mht_rem (Classname_cache, sm_ch_name (classobj), NULL, NULL);
2232  mht_put (Classname_cache, cl_name, classmop);
2233  }
2234  }
2235 }
2236 
2237 /*
2238  * ws_drop_classname - remove a classname from the workspace cache.
2239  * return: void
2240  * classobj(in): pointer to class strucutre
2241  *
2242  * Note:
2243  * It should be called by ws_cache and ws_decache or whenever the name
2244  * needs to be removed.
2245  */
2246 void
2248 {
2249  const char *class_name = NULL;
2250 
2251  if (classobj == NULL)
2252  {
2253  return;
2254  }
2255 
2256  class_name = sm_ch_name (classobj);
2257  if (class_name == NULL)
2258  {
2259  return; // ignore
2260  }
2261 
2262  mht_rem (Classname_cache, class_name, NULL, NULL);
2263 }
2264 
2265 /*
2266  * ws_find_class - search in the workspace classname cache for the MOP of
2267  * a class.
2268  * return: class pointer
2269  * name(in): class name to search
2270  *
2271  * Note:
2272  * This avoids going to the server each time a name to OID mapping needs
2273  * to be made. The cache will remain valid for the duration of the
2274  * current transaction. This should be called by locator_find_class to
2275  * check the schema before calling the server.
2276  */
2277 MOP
2278 ws_find_class (const char *name)
2279 {
2280  MOP class_mop;
2281 
2282  class_mop = (MOP) mht_get (Classname_cache, name);
2283 
2284  return (class_mop);
2285 }
2286 
2287 /*
2288  * MAIN INITIALIZATION AND SHUTDOWN
2289  */
2290 
2291 /*
2292  * ws_init - initialize workspace
2293  * return: NO_ERROR if successful, error code otherwise
2294  *
2295  * Note: This function should be called once early in the database
2296  * initialization phase.
2297  */
2298 int
2299 ws_init (void)
2300 {
2301  int error_code = NO_ERROR;
2302  unsigned int i;
2303  size_t allocsize;
2304 
2305  /* ws_init() shouldn't invoked twice without ws_final() */
2306  if (ws_Mop_table != NULL)
2307  {
2308  assert (false);
2310  return ER_FAILED;
2311  }
2312 
2313  if (db_create_workspace_heap () == 0)
2314  {
2315  return ER_OUT_OF_VIRTUAL_MEMORY;
2316  }
2317 
2318  /*
2319  * area_init() must have been called earlier.
2320  * These need to all be returning errors !
2321  */
2322  error_code = ws_area_init (); /* object lists */
2323  if (error_code != NO_ERROR)
2324  {
2325  goto error;
2326  }
2327  error_code = pr_area_init (); /* DB_VALUE */
2328  if (error_code != NO_ERROR)
2329  {
2330  goto error;
2331  }
2332  error_code = set_area_init (); /* set reference */
2333  if (error_code != NO_ERROR)
2334  {
2335  goto error;
2336  }
2337  error_code = obt_area_init (); /* object templates, assignment templates */
2338  if (error_code != NO_ERROR)
2339  {
2340  goto error;
2341  }
2342  error_code = classobj_area_init (); /* schema templates */
2343  if (error_code != NO_ERROR)
2344  {
2345  goto error;
2346  }
2347 
2348  /* build the MOP table */
2350  allocsize = sizeof (WS_MOP_TABLE_ENTRY) * ws_Mop_table_size;
2351  ws_Mop_table = (WS_MOP_TABLE_ENTRY *) malloc (allocsize);
2352 
2353  if (ws_Mop_table == NULL)
2354  {
2356  error_code = ER_OUT_OF_VIRTUAL_MEMORY;
2357  goto error;
2358  }
2359 
2360  for (i = 0; i < ws_Mop_table_size; i++)
2361  {
2362  ws_Mop_table[i].head = NULL;
2363  ws_Mop_table[i].tail = NULL;
2364  }
2365 
2366  /* create the internal Null object mop */
2367  Null_object = ws_make_mop (NULL);
2368  if (Null_object == NULL)
2369  {
2370  assert (er_errid () != NO_ERROR);
2371  error_code = er_errid ();
2372  goto error;
2373  }
2374 
2375  /* start with nothing dirty */
2376  Ws_dirty = false;
2377 
2378  /* build the classname cache */
2379  Classname_cache = mht_create ("Workspace class name cache", 256, mht_1strhash, mht_compare_strings_are_equal);
2380 
2381  if (Classname_cache == NULL)
2382  {
2383  error_code = ER_OUT_OF_VIRTUAL_MEMORY;
2384  goto error;
2385  }
2386 
2387  /* Can't have any resident classes yet */
2388  ws_Resident_classes = NULL;
2389 
2390  return NO_ERROR;
2391 
2392 error:
2394 
2395  ws_area_final ();
2396  pr_area_final ();
2397  set_area_final ();
2398  obt_area_final ();
2400 
2401  if (ws_Mop_table != NULL)
2402  {
2403  free (ws_Mop_table);
2404  ws_Mop_table = NULL;
2405  }
2406 
2407  if (Null_object != NULL)
2408  {
2409  ws_free_mop (Null_object);
2410  Null_object = NULL;
2411  }
2412 
2413  if (Classname_cache != NULL)
2414  {
2415  mht_destroy (Classname_cache);
2416  Classname_cache = NULL;
2417  }
2418 
2419  return error_code;
2420 }
2421 
2422 /*
2423  * ws_final - Close the workspace and release all allocated storage.
2424  * return: void
2425  *
2426  * Note: Must only be called prior to closing the database.
2427  */
2428 void
2429 ws_final (void)
2430 {
2431  MOP mop, next;
2432  unsigned int slot;
2433 
2434  tr_final ();
2435 
2437  {
2438  /* this is for debugging only */
2439  fprintf (stdout, "*** Database client statistics before shutdown ***\n");
2440  ws_dump (stdout);
2441  /*
2442  * Check for dangling allocations in the workspace.
2443  * First decache everything, must do this before the
2444  * MOP tables are destroyed.
2445  */
2446  ws_clear_internal (true);
2447  }
2449  ws_clear ();
2450 
2451  /* destroy the classname cache */
2452  if (Classname_cache != NULL)
2453  {
2454  mht_destroy (Classname_cache);
2455  Classname_cache = NULL;
2456  }
2457 
2458  /* destroy list of resident classes */
2459  ml_free (ws_Resident_classes);
2460  ws_Resident_classes = NULL;
2461 
2462  /* destroy the MOP table */
2463  if (ws_Mop_table != NULL)
2464  {
2465  for (slot = 0; slot < ws_Mop_table_size; slot++)
2466  {
2467  for (mop = ws_Mop_table[slot].head, next = NULL; mop != NULL; mop = next)
2468  {
2469  next = mop->hash_link;
2470  ws_free_mop (mop);
2471  }
2472  }
2473  ws_free_mop (Null_object);
2474  free_and_init (ws_Mop_table);
2475  }
2476 
2478 
2479  /* clean up misc globals */
2480  ws_Mop_table = NULL;
2481  ws_Mop_table_size = 0;
2482  Null_object = NULL;
2483  Ws_dirty = false;
2484 }
2485 
2486 /*
2487  * ws_clear_internal - Debugging function that decaches all objects in the
2488  * workspace and clears all locks.
2489  * return: void
2490  * clear_vmop_keys(in): if set clear the keys of a vmop
2491  *
2492  * Note: Used to make sure objects are flushed correctly.
2493  */
2494 static void
2495 ws_clear_internal (bool clear_vmop_keys)
2496 {
2497  MOP mop;
2498  unsigned int slot;
2499 
2500  for (slot = 0; slot < ws_Mop_table_size; slot++)
2501  {
2502  for (mop = ws_Mop_table[slot].head; mop != NULL; mop = mop->hash_link)
2503  {
2504  ws_decache (mop);
2505 
2506  /* if this is a vmop, we may need to clear the keys */
2507  if (mop->is_vid && WS_VID_INFO (mop) && clear_vmop_keys)
2508  {
2509  pr_clear_value (&WS_VID_INFO (mop)->keys);
2510  }
2511 
2512  mop->lock = NULL_LOCK;
2513  mop->deleted = 0;
2514  }
2515  }
2516  ws_Commit_mops = NULL;
2517  ws_filter_dirty ();
2518 }
2519 
2520 /*
2521  * ws_clear - ws_clear_internal wrapper. see comments of ws_clear_internal
2522  * return: void
2523  *
2524  */
2525 void
2526 ws_clear (void)
2527 {
2528  ws_clear_internal (false);
2529 }
2530 
2531 /*
2532  * ws_has_updated - see if there are any dirty objects in the workspace
2533  * return: true if updated, false otherwise
2534  */
2535 bool
2537 {
2538  /*
2539  * We used to be able to test the global dirty list (Dirty_objects) for
2540  * the presence of workspace updates. Now, we have to be a bit sneaky. To
2541  * do the same test, we set this static dirty flag to false and let the
2542  * ws_filter_dirty traversal turn this dirty flag on if it finds any
2543  * dirty objects in the workspace.
2544  */
2545  Ws_dirty = false;
2546 
2547  /*
2548  * wouldn't need to filter the whole list but this seems like
2549  * a reasonable time to do this
2550  */
2551  ws_filter_dirty ();
2552 
2553  return (Ws_dirty);
2554 }
2555 
2556 /*
2557  * MOP CACHING AND DECACHING
2558  */
2559 
2560 /*
2561  * ws_cache - sets the object content of a mop
2562  * return: void
2563  * obj(in): memory representation of object
2564  * mop(in): mop of the object
2565  * class_mop(in): class of the object
2566  *
2567  * Note:
2568  * First, we must check for any existing contents and free them.
2569  * Note that when a class is decached, all instances of that class must
2570  * also be decached because the class definition may have changed.
2571  * We force this here but it really has to be checked by the transaction
2572  * manager since the dirty instances must be flushed.
2573  */
2574 void
2575 ws_cache (MOBJ obj, MOP mop, MOP class_mop)
2576 {
2577  /* third clause applies if the sm_Root_class_mop is still being initialized */
2578  if ((class_mop == sm_Root_class_mop) || (mop->class_mop == sm_Root_class_mop) || (mop == class_mop))
2579  {
2580 
2581  /* caching a class */
2582  if ((mop->object != NULL) && (mop->object != (MOBJ) (&sm_Root_class)))
2583  {
2584  /* remove information for existing class */
2585  ws_drop_classname ((MOBJ) mop->object);
2587  classobj_free_class ((SM_CLASS *) mop->object);
2588  }
2589  mop->object = obj;
2590  mop->class_mop = class_mop;
2591 
2592  /*
2593  * must always call this when caching a class because we don't know
2594  * if there are any objects on disk
2595  */
2597 
2598  if (obj != (MOBJ) (&sm_Root_class))
2599  {
2600  /* this initializes the class_link list and adds it to the list of resident classes */
2601  if (add_class_object (class_mop, mop))
2602  {
2603  goto abort_it;
2604  }
2605 
2606  /* add to the classname cache */
2607  ws_add_classname (obj, mop, sm_ch_name (obj));
2608  }
2609 
2611  {
2612  er_print_callstack (ARG_FILE_LINE, "Cache class %s mop %d|%d|%d.\n", sm_ch_name ((MOBJ) mop->object),
2613  ws_oid (mop)->volid, ws_oid (mop)->pageid, ws_oid (mop)->slotid);
2614  }
2615  }
2616  else
2617  {
2618  if (mop->object != NULL)
2619  {
2620  /* free the current contents */
2621  if (mop->class_mop == NULL || mop->class_mop->object == NULL)
2622  {
2623  /* SERIOUS INTERNAL ERROR */
2625  ws_Stats.uncached_classes++;
2626  goto abort_it;
2627  }
2628  else
2629  {
2630  obj_free_memory ((SM_CLASS *) mop->class_mop->object, (MOBJ) mop->object);
2631  mop->object = NULL;
2632  }
2633  }
2634 
2635  mop->object = obj;
2637 
2638  if (mop->class_mop != class_mop)
2639  {
2640  if (mop->class_mop != NULL)
2641  {
2643  if (mop->class_link != NULL)
2644  {
2645  remove_class_object (mop->class_mop, mop);
2646  }
2647  }
2648  if (add_class_object (class_mop, mop))
2649  {
2650  goto abort_it;
2651  }
2652  }
2653  }
2654 
2655  return;
2656 
2657 abort_it:
2658  /*
2659  * NULL the MOP since we're in an unknown state, this function
2660  * should be returning an error
2661  */
2662  mop->object = NULL;
2663 }
2664 
2665 /*
2666  * ws_cache_with_oid - first find or create a MOP for the object's OID and
2667  * then cache the object.
2668  * return: mop of cached object
2669  * obj(in): memory representation of object
2670  * oid(in): object identifier
2671  * class(in): class of object
2672  *
2673  */
2674 MOP
2675 ws_cache_with_oid (MOBJ obj, OID * oid, MOP class_mop)
2676 {
2677  MOP mop;
2678 
2679  mop = ws_mop (oid, class_mop);
2680  if (mop != NULL)
2681  {
2682  ws_cache (obj, mop, class_mop);
2683  }
2684 
2685  return mop;
2686 }
2687 
2688 /*
2689  * ws_decache - Free the memory representation of an object.
2690  * return: void
2691  * mop(in/out): object to decache
2692  *
2693  * Note:
2694  * This must only be called from functions that understand the rules
2695  * involved with decaching objects. Specifically, you cannot decache
2696  * a class unless all the instances are decached first.
2697  * You must not decache an object if it is dirty and has not been
2698  * flushed to the server.
2699  */
2700 void
2702 {
2703  /* these should be caught before we get here, issue a warning message */
2704 #if 0
2705  if (mop->pinned)
2707 #endif
2708 
2709  if (mop->class_mop != sm_Root_class_mop)
2710  {
2711  if (mop->object != NULL)
2712  {
2713  if (mop->class_mop == NULL || mop->class_mop->object == NULL)
2714  {
2716  ws_Stats.uncached_classes++;
2717  mop->object = NULL;
2718  }
2719  else
2720  {
2721  obj_free_memory ((SM_CLASS *) mop->class_mop->object, (MOBJ) mop->object);
2722  if (WS_IS_DELETED (mop))
2723  {
2724  remove_class_object (mop->class_mop, mop);
2725  }
2726  mop->object = NULL;
2727  }
2728  }
2729  }
2730  else
2731  {
2733  {
2734  er_print_callstack (ARG_FILE_LINE, "Decache class %s " "mop %d|%d|%d.\n", sm_ch_name ((MOBJ) mop->object),
2735  ws_oid (mop)->volid, ws_oid (mop)->pageid, ws_oid (mop)->slotid);
2736  }
2737  /* free class object, not sure if this should be done here */
2738  if (mop->object != NULL && mop->object != (MOBJ) (&sm_Root_class))
2739  {
2740  ws_drop_classname ((MOBJ) mop->object);
2741 
2743  classobj_free_class ((SM_CLASS *) mop->object);
2744  }
2745  }
2746 
2747  mop->object = NULL;
2748  ws_clean (mop);
2749 
2750  /* this no longer apples */
2751  mop->composition_fetch = 0;
2752  mop->decached = 1;
2753 }
2754 
2755 /*
2756  * ws_decache_all_instances - Decache all the instances of a class.
2757  * return: void
2758  * mop(in): class whose instances are to be decached
2759  *
2760  * Note:
2761  * Commonly used after a schema modification. If the rootclass mop is given,
2762  * decache all classes (which in turn will decache all instances).
2763  */
2764 void
2766 {
2767  DB_OBJLIST *l;
2768  MOP obj = NULL, save_class_link = NULL;
2769 
2770  if (mop == sm_Root_class_mop)
2771  {
2772  /* decache all class objects */
2773  for (l = ws_Resident_classes; l != NULL; l = l->next)
2774  {
2775  ws_decache (l->op);
2776  }
2777  }
2778  else if (mop->class_mop == sm_Root_class_mop)
2779  {
2780  if (mop->class_link != NULL)
2781  {
2782  for (obj = mop->class_link; obj != Null_object; obj = save_class_link)
2783  {
2784  save_class_link = obj->class_link;
2785  ws_decache (obj);
2786  }
2787  }
2788  }
2789 }
2790 
2791 /*
2792  * MOP ACCESSOR FUNCTIONS
2793  */
2794 
2795 /*
2796  * ws_identifier() - This function returns the permanent object identifier of
2797  * the given object.
2798  * return : Pointer to object identifier
2799  * mop(in):
2800  * Note: This function should not be used if the object can be a
2801  * non-referable instance as it will return a reference to the object;
2802  * use db_identifier () instead to perform the needed check.
2803  */
2804 OID *
2806 {
2807  return ws_identifier_with_check (mop, false);
2808 }
2809 
2810 /*
2811  * ws_identifier_with_check() - This function returns the permanent object
2812  * identifier of the given object.
2813  * return : Pointer to object identifier
2814  * mop(in):
2815  * check_non_referable(in): whether to check that a reference to the instance
2816  * can be returned. Instances of reusable OID classes
2817  * are non-referable.
2818  */
2819 OID *
2820 ws_identifier_with_check (MOP mop, const bool check_non_referable)
2821 {
2822  OID *oid = NULL;
2823  MOP class_mop;
2824 
2825  if (mop == NULL || WS_IS_DELETED (mop))
2826  {
2827  goto end;
2828  }
2829 
2830  if (WS_ISVID (mop))
2831  {
2832  mop = db_real_instance (mop);
2833  if (mop == NULL)
2834  {
2835  /* non-updatable view has no oid */
2836  goto end;
2837  }
2838  if (WS_ISVID (mop))
2839  {
2840  /* a proxy has no oid */
2841  goto end;
2842  }
2843  }
2844 
2845  if (check_non_referable)
2846  {
2848 
2849  if (is_class < 0)
2850  {
2851  goto end;
2852  }
2853  class_mop = is_class > 0 ? mop : ws_class_mop (mop);
2854  if (sm_is_reuse_oid_class (class_mop))
2855  {
2856  /* should not return the oid of a non-referable instance */
2857  goto end;
2858  }
2859  }
2860 
2861  oid = ws_oid (mop);
2862  if (OID_ISTEMP (oid))
2863  {
2864  (void) locator_flush_instance (mop);
2865  oid = ws_oid (mop);
2866  }
2867 
2868 end:
2869  return oid;
2870 }
2871 
2872 /*
2873  * These provide access shells for the fields in the MOP structure. These
2874  * are simple enough that callers should change to use the corresponding
2875  * macros.
2876  */
2877 
2878 /*
2879  * ws_oid - oid field accessor
2880  * return: pointer to oid structure
2881  * mop(in): object pointer
2882  */
2883 OID *
2885 {
2886  if (mop && !WS_ISVID (mop))
2887  {
2888  return (WS_OID (mop));
2889  }
2890  if (mop)
2891  {
2892  mop = vid_base_instance (mop);
2893  if (mop && !WS_ISVID (mop))
2894  {
2895  return (WS_OID (mop));
2896  }
2897  }
2898  return NULL;
2899 }
2900 
2901 /*
2902  * ws_class_mop - class accessor
2903  * return: pointer to the class mop of an object
2904  * mop(in): object mop
2905  */
2906 MOP
2908 {
2909  return (mop->class_mop);
2910 }
2911 
2912 /*
2913  * ws_chn - get cache coherency number (chn) from an object.
2914  * return: cache coherency number
2915  * obj(in): memory representation of object
2916  *
2917  * Note:
2918  * Use WS_CHN macro only if you can *guarantee* that the pointer won't be
2919  * NULL.
2920  */
2921 int
2923 {
2924  if (obj)
2925  {
2926  WS_OBJECT_HEADER *mobj;
2927  mobj = (WS_OBJECT_HEADER *) obj;
2928  return mobj->chn;
2929  }
2930  else
2931  {
2932  return NULL_CHN;
2933  }
2934 }
2935 
2936 /*
2937  * ws_get_lock - lock field accessor
2938  * return: lock field of a mop
2939  * mop(in): object pointer
2940  */
2941 LOCK
2943 {
2944  return (mop->lock);
2945 }
2946 
2947 /*
2948  * ws_set_lock - lock field setter
2949  * return: void
2950  * mop(in): object pointer
2951  * lock(in): lock type
2952  */
2953 void
2954 ws_set_lock (MOP mop, LOCK lock)
2955 {
2957  && mop != sm_Root_class_mop && mop->object != NULL && lock != mop->lock)
2958  {
2959  er_print_callstack (ARG_FILE_LINE, "Change class %s mop %d|%d|%d " "lock from %d to %d.\n",
2960  sm_ch_name ((MOBJ) mop->object), ws_oid (mop)->volid, ws_oid (mop)->pageid,
2961  ws_oid (mop)->slotid, mop->lock, lock);
2962  }
2963  if (mop != NULL)
2964  {
2965  WS_SET_LOCK (mop, lock);
2966  }
2967 }
2968 
2969 /*
2970  * ws_pin - sets the pin flag for a MOP
2971  * return: previous pin flag value
2972  * mop(in/out): object pointer
2973  * pin(in): pin flag value
2974  *
2975  * Note:
2976  * Pinning a MOP will make sure that it is not decached
2977  * (garbage collected) for the duration of the transaction.
2978  * The pin flag will be cleared with the other mop flags when a
2979  * transaction is aborted or committed.
2980  * It is OK to call this for a MOP that has no current contents. This
2981  * would happen in the case where we have just prefetched some objects
2982  * and are attempting to load and cache all of them. Since a panic
2983  * can ocurr during the loading of one of the prefetched objects, we
2984  * must make sure that the original object we were attempting to fetch
2985  * is not swapped out as part of the panic. To prevent this, we pin
2986  * the mop before it is cached.
2987  */
2988 int
2989 ws_pin (MOP mop, int pin)
2990 {
2991  int old = 0;
2992 
2993  /* We don't deal with MOPs on the server */
2994  if (db_on_server)
2995  {
2996  return old;
2997  }
2998 
2999  if (mop != NULL)
3000  {
3001  if (mop->class_mop != sm_Root_class_mop)
3002  {
3003  old = mop->pinned;
3004  mop->pinned = pin;
3005  }
3006  /* else, its a class MOP, they're implicitly pinned */
3007  }
3008 
3009  return (old);
3010 }
3011 
3012 /*
3013  * ws_pin_instance_and_class - pin object and the class of the object
3014  * return: void
3015  * obj(in/out): object pointer
3016  * opin(out): previous pin flag value of a object
3017  * cpin(out): previous pin flag value of a class of the object
3018  */
3019 void
3020 ws_pin_instance_and_class (MOP obj, int *opin, int *cpin)
3021 {
3022  if (obj->class_mop != NULL && obj->class_mop != sm_Root_class_mop)
3023  {
3024  *opin = obj->pinned;
3025  obj->pinned = 1;
3026  if (obj->class_mop == NULL)
3027  {
3028  *cpin = 0;
3029  }
3030  else
3031  {
3032  *cpin = obj->class_mop->pinned;
3033  obj->class_mop->pinned = 1;
3034  }
3035  }
3036  else
3037  {
3038  /* classes have no explicit pinning */
3039  *opin = 0;
3040  *cpin = 0;
3041  }
3042 }
3043 
3044 /*
3045  * ws_restore_pin - resotre pin flag of a object and its class object
3046  * return: void
3047  * obj(in/out): object pointer
3048  * opin(in): class pin flag value to set
3049  * cpin(in): object pin flag value to set
3050  */
3051 void
3052 ws_restore_pin (MOP obj, int opin, int cpin)
3053 {
3054  obj->pinned = opin;
3055  if (obj->class_mop != NULL)
3056  {
3057  obj->class_mop->pinned = cpin;
3058  }
3059 }
3060 
3061 /*
3062  * ws_mark_deleted
3063  *
3064  * arguments:
3065  *
3066  * returns/side-effects:
3067  *
3068  * description:
3069  * This marks an object as deleted. It will also add the object to the
3070  * dirty list if it isn't already there. The object will be flushed
3071  * to disk at the end of the transaction.
3072  */
3073 
3074 /*
3075  * ws_mark_deleted - marks an object as deleted
3076  * return: void
3077  * mop(in): object pointer
3078  *
3079  */
3080 void
3082 {
3083  ws_dirty (mop);
3084 
3085  WS_SET_DELETED (mop);
3086 
3087  /* should be unpinning before deleting */
3088  if (mop->pinned)
3089  {
3090 /* er_set(ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_WS_PIN_VIOLATION, 0); */
3091  mop->pinned = 0;
3092  }
3093 
3094 }
3095 
3096 /*
3097  * MISC UTILITY FUNCTIONS
3098  */
3099 
3100 /*
3101  * ws_find -
3102  * return: mop status code (WS_FIND_MOP_DELETED, WS_FIND_MOP_NOTDELETED)
3103  * mop(in): object pointer
3104  * obj(out): return pointer to memory representation of object
3105  *
3106  * Note:
3107  * This is used to access the memory representation of an object.
3108  * The memory representation is returned through the supplied pointer
3109  * as long as the mop is not marked as deleted.
3110  */
3111 int
3112 ws_find (MOP mop, MOBJ * obj)
3113 {
3114  int status;
3115 
3116  *obj = NULL;
3117  if (mop && !WS_IS_DELETED (mop))
3118  {
3119  *obj = (MOBJ) mop->object;
3120 
3121  status = WS_FIND_MOP_NOTDELETED;
3122  }
3123  else
3124  {
3125  status = WS_FIND_MOP_DELETED;
3126  }
3127 
3128  return status;
3129 }
3130 
3131 /*
3132  * ws_mop_compare - compare MOPs
3133  * return: 0 if equal, non-zero if not equal
3134  * mop1(in): object pointer
3135  * mop2(in): object pointer
3136  *
3137  * Note:
3138  * Currently, MOPs with the same OID will always be exactly the same
3139  * structure so comparison with '==' in C is acceptable. It has been
3140  * discussed that this may not be acceptable for future use so this
3141  * function will compare based on the OIDs.
3142  */
3143 int
3144 ws_mop_compare (MOP mop1, MOP mop2)
3145 {
3146  return (oid_compare (WS_OID (mop1), WS_OID (mop2)));
3147 }
3148 
3149 /*
3150  * ws_class_has_object_dependencies - set no_object fields to 0
3151  * return: void
3152  * class(out): class mop
3153  *
3154  * Note:
3155  * This controls a flag that is used to optimize the generation of
3156  * represetations by the schema manager when a class is altered.
3157  * A bit is kept in the class MOP that is set whenever we are sure
3158  * that no object have been stored in the database that are dependent on
3159  * the current class representation. This bit is set after a
3160  * representation is installed but is cleared whenever a class is
3161  * cached, an object of the class is cached, an object of the class is
3162  * updated, or an object of the class is created. It will also be cleared
3163  * at the end of a transaction by ws_clear_hints.
3164  */
3165 void
3167 {
3168  if (class_mop != NULL)
3169  {
3170  class_mop->no_objects = 0;
3171  }
3172 }
3173 
3174 /*
3175  * ws_class_has_cached_objects - check if there are cached redident instance
3176  * return: non zero if there are cached redident instances
3177  * class(in): class to examin
3178  */
3179 int
3181 {
3182  MOP obj;
3183  int cached = 0;
3184 
3185  for (obj = class_mop->class_link; obj != Null_object && !cached; obj = obj->class_link)
3186  {
3187  if (obj->object != NULL)
3188  {
3189  cached = 1;
3190  }
3191  }
3192  return cached;
3193 }
3194 
3195 #if defined (CUBRID_DEBUG)
3196 /*
3197  * ws_map - map over all MOPs currently in the workspace.
3198  * return: WS_MAP_ status code
3199  * function(in): mapping function to alpply to the mops
3200  * args(in): map function argument
3201  *
3202  * Note:
3203  * The loop will continue as long as the mapping function returns
3204  * WS_MAP_CONTINUE.
3205  */
3206 int
3207 ws_map (MAPFUNC function, void *args)
3208 {
3209  MOP mop;
3210  unsigned int slot;
3211  int status = WS_MAP_CONTINUE;
3212  int num_ws_continue_on_error = 0;
3213 
3214  if (ws_Mop_table != NULL)
3215  {
3216  for (slot = 0; slot < ws_Mop_table_size && status == WS_MAP_CONTINUE; slot++)
3217  {
3218  for (mop = ws_Mop_table[slot].head; mop != NULL && status == WS_MAP_CONTINUE; mop = mop->hash_link)
3219  {
3220  status = (*(function)) (mop, args);
3221  if (status == WS_MAP_CONTINUE_ON_ERROR)
3222  {
3223  num_ws_continue_on_error++;
3224  stauts = WS_MAP_CONTINUE;
3225  }
3226  }
3227  }
3228  }
3229  if (status != WS_MAP_FAIL)
3230  {
3231  status = WS_MAP_SUCCESS;
3232  }
3233 
3234  return (status);
3235 }
3236 #endif
3237 
3238 /*
3239  * TRANSACTION MANAGEMENT SUPPORT
3240  */
3241 
3242 /*
3243  * ws_clear_hints - clear all of the hint bits in the MOP.
3244  * return: void
3245  * mop(in): object pointer
3246  * leave_pinned(in): flag to keep from modifying pinned field
3247  *
3248  * Note:
3249  * This is called by the transaction manager to clear all of the hint
3250  * bits in the MOP. This is guaranteed to be called at the end of a
3251  * transaction commit. Note that we always clear the no_objects field
3252  * for classes because once they are commited to a database, we must
3253  * assume that other users have access to the current representation and
3254  * can create instances with that representation.
3255  */
3256 void
3257 ws_clear_hints (MOP mop, bool leave_pinned)
3258 {
3259  /*
3260  * Don't decache non-updatable view objects because they cannot be
3261  * recreated. Let garbage collection eventually decache them.
3262  */
3264  && mop != sm_Root_class_mop && mop->object != NULL)
3265  {
3266  er_print_callstack (ARG_FILE_LINE, "Clear class %s mop %d|%d|%d.\n", sm_ch_name ((MOBJ) mop->object),
3267  ws_oid (mop)->volid, ws_oid (mop)->pageid, ws_oid (mop)->slotid);
3268  }
3269 
3270  if (WS_ISVID (mop))
3271  {
3272  if (!vid_is_updatable (mop))
3273  {
3274  return;
3275  }
3276  else
3277  {
3278  ws_decache (mop);
3279  }
3280  }
3281  mop->lock = NULL_LOCK;
3282  mop->composition_fetch = 0;
3283  mop->deleted = 0;
3284  WS_RESET_DIRTY (mop);
3285  mop->no_objects = 0;
3286 
3287  if (!leave_pinned)
3288  {
3289  mop->pinned = 0;
3290  }
3291 }
3292 
3293 /*
3294  * ws_clear_all_hints - reset all hint flags in the mops after a transaction
3295  * has been committeed.
3296  * return: void
3297  * retain_lock(in): if set to true retain locks (no operation)
3298  *
3299  * Note:
3300  * Called by the transaction manager to reset all hint flags in the mops
3301  * after a transaction has been committeed. Also reset the
3302  * authorization cache.
3303  */
3304 void
3305 ws_clear_all_hints (bool retain_lock)
3306 {
3307  MOP mop;
3308  MOP next;
3309 
3310  if (retain_lock)
3311  {
3312  return;
3313  }
3314 
3316 
3317  /* clear hints */
3318  mop = ws_Commit_mops;
3319  while (mop)
3320  {
3321  ws_clear_hints (mop, false);
3322  next = mop->commit_link;
3323  mop->commit_link = NULL; /* remove mop from commit link (it's done) */
3324  if (next == mop)
3325  {
3326  mop = NULL;
3327  }
3328  else
3329  {
3330  mop = next;
3331  }
3332  }
3333 
3334  ws_Commit_mops = NULL;
3335  ws_Num_dirty_mop = 0;
3336 
3337 #if !defined (NDEBUG)
3339  {
3341  }
3342 #endif /* NDEBUG */
3343 }
3344 
3345 /*
3346  * ws_abort_mops - called by the transaction manager when a transaction is
3347  * aborted
3348  * return: void
3349  * only_unpinned(in): flag whether is it safe to abort pinned mops
3350  *
3351  */
3352 void
3353 ws_abort_mops (bool only_unpinned)
3354 {
3355  MOP mop;
3356  MOP next;
3357 
3359 
3360  mop = ws_Commit_mops;
3361  while (mop)
3362  {
3363  next = mop->commit_link;
3364  mop->commit_link = NULL; /* remove mop from commit link (it's done) */
3365 
3366  /*
3367  * In some cases we cannot clear up pinned stuff, because we
3368  * may already be looping through the object or dirty list somewhere
3369  * in the calling chain, and we would be removing something out
3370  * from under them.
3371  */
3372  if (!only_unpinned || !mop->pinned)
3373  {
3374  /* always remove this so we can decache things without error */
3375  mop->pinned = 0;
3376 
3377  /*
3378  * Decache all objects in commit link. Even though they are not
3379  * marked as dirty and do not have exclusive locks, they may have
3380  * been decached (and reset) during a partial rollback. Now we are
3381  * not allowed to miss any cached objects that may have been
3382  * modified during last transaction.
3383  */
3384  ws_decache (mop);
3385  }
3386 
3387  /* clear all hint fields including the lock */
3388  ws_clear_hints (mop, only_unpinned);
3389 
3390  if (next == mop)
3391  {
3392  mop = NULL;
3393  }
3394  else
3395  {
3396  mop = next;
3397  }
3398  }
3399 
3400  ws_Commit_mops = NULL;
3401  ws_Num_dirty_mop = 0;
3402 
3403 #if !defined (NDEBUG)
3405  {
3407  }
3408 #endif /* NDEBUG */
3409 }
3410 
3411 /*
3412  * ws_decache_allxlockmops_but_norealclasses - called by the transaction
3413  * manager when a savepoint is only rolled back by CUBRID system
3414  * (not by the application/user).
3415  * return: void
3416  *
3417  * Note:
3418  * All mops that are not class mops of views and proxies are decached when
3419  * ther are not pinned.
3420  *
3421  * Technically this should also free the mop strucure but it may be
3422  * referenced in the application program space so we must rely on
3423  * garbage collection to reclaim the storage.
3424  */
3425 void
3427 {
3428  MOP mop = NULL, save_hash_link = NULL;
3429  unsigned int slot;
3430 
3431  for (slot = 0; slot < ws_Mop_table_size; slot++)
3432  {
3433  for (mop = ws_Mop_table[slot].head; mop != NULL; mop = save_hash_link)
3434  {
3435  save_hash_link = mop->hash_link;
3436  if (mop->pinned == 0 && (IS_WRITE_EXCLUSIVE_LOCK (ws_get_lock (mop)) || WS_ISDIRTY (mop))
3437  && (mop->class_mop != sm_Root_class_mop || mop->object == NULL
3438  || ((SM_CLASS *) mop->object)->class_type == SM_CLASS_CT))
3439  {
3440  ws_decache (mop);
3441  ws_clear_hints (mop, false);
3442  }
3443  }
3444  }
3445 }
3446 
3447 /*
3448  * STRING UTILITIES
3449  */
3450 
3451 /*
3452  * ws_copy_string - copies a string storage allocated whthin the workspace
3453  * return: copied string
3454  * str(in): string to copy
3455  */
3456 char *
3457 ws_copy_string (const char *str)
3458 {
3459  char *copy;
3460 
3461  copy = NULL;
3462  if (str != NULL)
3463  {
3464  copy = (char *) db_ws_alloc (strlen (str) + 1);
3465  if (copy != NULL)
3466  {
3467  strcpy ((char *) copy, (char *) str);
3468  }
3469  }
3470 
3471  return (copy);
3472 }
3473 
3474 /*
3475  * ws_free_string - frees a string that was allocated by ws_copy_string.
3476  * return: void
3477  * str(out): workspace string to free
3478  */
3479 void
3480 ws_free_string (const char *str)
3481 {
3482  char *s;
3483 
3484  if (str != NULL)
3485  {
3486  s = (char *) str; /* avoid compiler warnings */
3487  db_ws_free (s);
3488  }
3489 }
3490 
3491 /*
3492  * DEBUG FUNCTIONS
3493  */
3494 
3495 /*
3496  * ws_print_oid - print oid to standard out
3497  * return: void
3498  * oid(in): oid to print
3499  */
3500 static void
3502 {
3503  fprintf (stdout, "%d/%d/%d", (int) oid->volid, (int) oid->pageid, (int) oid->slotid);
3504 }
3505 
3506 #if defined (CUBRID_DEBUG)
3507 /*
3508  * ws_describe_mop - print MOP information
3509  * return: void
3510  * mop(in): object pointer to describe
3511  * args(in): not used
3512  */
3513 static int
3514 ws_describe_mop (MOP mop, void *args)
3515 {
3516  ws_print_oid (WS_OID (mop));
3517  fprintf (stdout, " ");
3518  if (ws_mop_compare (mop, sm_Root_class_mop) == 0)
3519  {
3520  fprintf (stdout, "Root class ");
3521  }
3522  else
3523  {
3524  if (mop->class_mop == NULL)
3525  {
3526  fprintf (stdout, "class MOP not available\n");
3527  }
3528  else
3529  {
3530  if (ws_mop_compare (mop->class_mop, sm_Root_class_mop) == 0)
3531  {
3532  fprintf (stdout, "class ");
3533  if (mop->object == NULL)
3534  {
3535  fprintf (stdout, "not cached ");
3536  }
3537  else
3538  {
3539  fprintf (stdout, "%s ", sm_get_ch_name (mop));
3540  }
3541  }
3542  else
3543  {
3544  fprintf (stdout, "instance of ");
3545  if (mop->class_mop->object == NULL)
3546  {
3547  fprintf (stdout, "uncached class ");
3548  }
3549  else
3550  {
3551  fprintf (stdout, "%s ", sm_get_ch_name (mop->class_mop));
3552  }
3553  }
3554  }
3555  }
3556  if (mop->dirty)
3557  {
3558  fprintf (stdout, " dirty");
3559  }
3560  if (WS_IS_DELETED (mop))
3561  {
3562  fprintf (stdout, " deleted");
3563  }
3564  if (mop->pinned)
3565  {
3566  fprintf (stdout, " pinned");
3567  }
3568  if (mop->no_objects)
3569  {
3570  fprintf (stdout, " no_objects");
3571  }
3572 
3573  fprintf (stdout, "\n");
3574  return (WS_MAP_CONTINUE);
3575 }
3576 
3577 /*
3578  * ws_dump_mops - print information of all mops
3579  * return: void
3580  */
3581 void
3582 ws_dump_mops (void)
3583 {
3584  fprintf (stdout, "WORKSPACE MOP TABLE:\n\n");
3585  (void) ws_map (ws_describe_mop, NULL);
3586  fprintf (stdout, "\n");
3587 }
3588 #endif
3589 
3590 /*
3591  * WORKSPACE STATISTICS
3592  */
3593 
3594 /*
3595  * ws_dump - print worksapce information to FILE output
3596  * return: void
3597  * fpp(in): FILE * to print the workspace information
3598  */
3599 void
3600 ws_dump (FILE * fpp)
3601 {
3602  int mops, root, unknown, classes, cached_classes, instances, cached_instances;
3603  int count, actual, decached, weird;
3604  unsigned int slot;
3605  int classtotal, insttotal, size, isize, icount, deleted;
3606  MOP mop, inst;
3607  DB_OBJLIST *m;
3608 
3609  /* get mop totals */
3610  mops = root = unknown = classes = cached_classes = instances = cached_instances = 0;
3611  weird = 0;
3612  for (slot = 0; slot < ws_Mop_table_size; slot++)
3613  {
3614  for (mop = ws_Mop_table[slot].head; mop != NULL; mop = mop->hash_link)
3615  {
3616  mops++;
3617 
3618  if (mop == sm_Root_class_mop)
3619  {
3620  continue;
3621  }
3622 
3623  if (mop->class_mop == NULL)
3624  {
3625  unknown++;
3626  if (mop->object != NULL)
3627  {
3628  weird++;
3629  }
3630  }
3631  else if (mop->class_mop == sm_Root_class_mop)
3632  {
3633  classes++;
3634  if (mop->object != NULL)
3635  {
3636  cached_classes++;
3637  }
3638  }
3639  else
3640  {
3641  instances++;
3642  if (mop->object != NULL)
3643  {
3644  cached_instances++;
3645  }
3646  }
3647 
3648  }
3649  }
3650 
3651  fprintf (fpp, "%d mops in the workspace (including one rootclass mop)\n", mops);
3652  fprintf (fpp, "%d class mops (%d cached, %d uncached)\n", classes, cached_classes, classes - cached_classes);
3653  fprintf (fpp, "%d instance mops (%d cached, %d uncached)\n", instances, cached_instances,
3654  instances - cached_instances);
3655 
3656  fprintf (fpp, "%d unknown mops\n", unknown);
3657  if (weird)
3658  {
3659  fprintf (fpp, "*** %d unknown mops with cached objects\n", weird);
3660  }
3661  fprintf (fpp, "%d attempts to clean pinned mops\n", ws_Stats.pinned_cleanings);
3662 
3663  /* gc stats */
3664  fprintf (fpp, "%d MOPs allocated, %d freed\n", ws_Stats.mops_allocated, ws_Stats.mops_freed);
3665 
3666  /* misc stats */
3667  fprintf (fpp, "%d dirty list emergencies, %d uncached classes, %d corruptions\n", ws_Stats.dirty_list_emergencies,
3668  ws_Stats.uncached_classes, ws_Stats.corruptions);
3669  fprintf (fpp, "%d ignored class assignments\n", ws_Stats.ignored_class_assignments);
3670 
3671 
3672  fprintf (fpp, "%d total set mops allocated, %d total set mops freed\n", ws_Stats.set_mops_allocated,
3673  ws_Stats.set_mops_freed);
3674 
3675  /* dirty stats */
3676  count = actual = 0;
3677  for (m = ws_Resident_classes; m != NULL; m = m->next)
3678  {
3679  for (mop = m->op->dirty_link; mop != Null_object; mop = mop->dirty_link)
3680  {
3681  count++;
3682  if (mop->dirty)
3683  {
3684  actual++;
3685  }
3686  }
3687  }
3688  fprintf (fpp, "%d dirty objects, %d clean objects in dirty list\n", actual, count - actual);
3689 
3690  /* get class totals */
3691  fprintf (fpp, "RESIDENT INSTANCE TOTALS: \n");
3692  count = classtotal = insttotal = deleted = 0;
3693  for (m = ws_Resident_classes; m != NULL; m = m->next)
3694  {
3695  mop = m->op;
3696  if (WS_IS_DELETED (mop))
3697  {
3698  deleted++;
3699  }
3700  else
3701  {
3702  count++;
3703  if (mop != sm_Root_class_mop && mop->object != NULL)
3704  {
3705  size = classobj_class_size ((SM_CLASS *) mop->object);
3706  classtotal += size;
3707  icount = isize = decached = 0;
3708  for (inst = mop->class_link; inst != Null_object; inst = inst->class_link)
3709  {
3710  icount++;
3711  if (inst->object != NULL)
3712  {
3713  isize += sm_object_size_quick ((SM_CLASS *) mop->object, (MOBJ) inst->object);
3714  }
3715  else
3716  {
3717  decached++;
3718  }
3719  }
3720  fprintf (fpp, " %-20s : %d instances, %d decached, %d bytes used\n", sm_ch_name ((MOBJ) (mop->object)),
3721  icount, decached, isize);
3722  insttotal += isize;
3723  }
3724  }
3725  }
3726  if (deleted)
3727  {
3728  fprintf (fpp, "*** %d deleted MOPs in the resident class list \n", deleted);
3729  }
3730 
3731  /* just to make sure */
3732  if (count != cached_classes)
3733  {
3734  fprintf (fpp, "*** Mops claiming to be classes %d, resident class list length %d\n", cached_classes, count);
3735  }
3736 
3737  fprintf (fpp, "Total bytes for class storage %d\n", classtotal);
3738  fprintf (fpp, "Total bytes for instance storage %d\n", insttotal);
3739  fprintf (fpp, "Total bytes for object storage %d\n", classtotal + insttotal);
3740 
3741  fprintf (fpp, "WORKSPACE AREAS:\n");
3742  area_dump (fpp);
3743 }
3744 
3745 /*
3746  * ws_has_dirty_objects - check if object has any dirty instance
3747  * return: nonzero iff op has any dirty instances
3748  * op(in): object pointer
3749  * isvirt(out): 1 iff op is a proxy of vclass
3750  */
3751 int
3752 ws_has_dirty_objects (MOP op, int *isvirt)
3753 {
3754  *isvirt = (op && !WS_IS_DELETED (op) && op->object && (((SM_CLASS *) (op->object))->class_type == SM_VCLASS_CT));
3755 
3756  return (op && !WS_IS_DELETED (op) && op->object && op->dirty_link && op->dirty_link != Null_object);
3757 }
3758 
3759 /*
3760  * ws_hide_new_old_trigger_obj - temporarily hide "new" or "old" object
3761  * return: 1 iff obj is a non-temp dirty pinned proxy instance
3762  * op(in/out): a trigger's "new" or "old" object instance
3763  */
3764 int
3766 {
3767  if (op && op->is_vid && !op->is_temp && op->dirty && op->pinned)
3768  {
3769  WS_RESET_DIRTY (op);
3770  return 1;
3771  }
3772  else
3773  {
3774  return 0;
3775  }
3776 }
3777 
3778 /*
3779  * ws_unhide_new_old_trigger_obj: unhide "new" or "old" trigger object
3780  *
3781  * arguments:
3782  * obj : (IN) a trigger's "new" or "old" object instance
3783  *
3784  * returns: none
3785  *
3786  * description:
3787  * requires: obj is a trigger's "new" or "old" object instance whose dirty
3788  * bit was temporarily turned off by ws_hide_new_old_trigger_obj
3789  * modifies: obj's dirty bit
3790  * effects : if obj is a non-temp pinned proxy instance then turn on its
3791  * dirty bit.
3792  */
3793 
3794 /*
3795  * ws_unhide_new_old_trigger_obj - unhide "new" or "old" trigger object
3796  * return: void
3797  * op(in/out): object pointer
3798  */
3799 void
3801 {
3802  if (op && op->is_vid && !op->is_temp && op->pinned)
3803  {
3804  WS_SET_DIRTY (op);
3805  }
3806 }
3807 
3808 /*
3809  * ws_need_flush - check if workspace has dirty mop
3810  * return: 1 | 0
3811  */
3812 bool
3814 {
3815  return (ws_Num_dirty_mop > 0);
3816 }
3817 
3818 /*
3819  * ws_area_init - initialize area for object list links.
3820  * return: NO_ERROR or error code.
3821  */
3822 int
3824 {
3825  Objlist_area = area_create ("Object list links", sizeof (DB_OBJLIST), OBJLIST_AREA_COUNT);
3826  if (Objlist_area == NULL)
3827  {
3828  assert (er_errid () != NO_ERROR);
3829  return er_errid ();
3830  }
3831 
3832  return NO_ERROR;
3833 }
3834 
3835 /*
3836  * ws_area_final - finalize area for object list links.
3837  * return: none.
3838  */
3839 void
3841 {
3842  if (Objlist_area != NULL)
3843  {
3844  area_destroy (Objlist_area);
3845  Objlist_area = NULL;
3846  }
3847 }
3848 
3849 /*
3850  * LIST UTILITIES
3851  */
3852 /*
3853  * These operations assume a structure with a single link field at the top.
3854  *
3855  * struct link {
3856  * struct link *next;
3857  * };
3858  *
3859  */
3860 
3861 
3862 /*
3863  * ws_list_append - append element to the end of a list
3864  * return: none
3865  * root(in/out): pointer to pointer to list head
3866  * element(in): element to add
3867  */
3868 void
3869 ws_list_append (DB_LIST ** root, DB_LIST * element)
3870 {
3871  DB_LIST *el;
3872 
3873  for (el = *root; (el != NULL) && (el->next != NULL); el = el->next);
3874  if (el == NULL)
3875  {
3876  *root = element;
3877  }
3878  else
3879  {
3880  el->next = element;
3881  }
3882 }
3883 
3884 /*
3885  * ws_list_remove - Removes an element from a list if it exists.
3886  * return: non-zero if the element was removed
3887  * root(): pointer to pointer to list head
3888  * element(): element to remove
3889  */
3890 int
3891 ws_list_remove (DB_LIST ** root, DB_LIST * element)
3892 {
3893  DB_LIST *el, *prev;
3894  int removed;
3895 
3896  removed = 0;
3897  for (el = *root, prev = NULL; el != NULL && el != element; el = el->next)
3898  {
3899  prev = el;
3900  }
3901 
3902  if (el != element)
3903  {
3904  return removed;
3905  }
3906  if (prev == NULL)
3907  {
3908  *root = element->next;
3909  }
3910  else
3911  {
3912  prev->next = element->next;
3913  }
3914  removed = 1;
3915 
3916  return (removed);
3917 }
3918 
3919 /*
3920  * ws_list_length - return the number of elements in a list
3921  * return: length of the list (zero if empty)
3922  * list(in): list to examine
3923  */
3924 int
3926 {
3927  DB_LIST *el;
3928  int length = 0;
3929 
3930  for (el = list; el != NULL; el = el->next)
3931  {
3932  length++;
3933  }
3934 
3935  return (length);
3936 }
3937 
3938 /*
3939  * ws_list_free - apply (free) function over the elements of a list
3940  * return: none
3941  * list(in): list to free
3942  * function(in): function to perform the freeing of elements
3943  */
3944 void
3945 ws_list_free (DB_LIST * list, LFREEER function)
3946 {
3947  DB_LIST *link, *next;
3948 
3949  for (link = list, next = NULL; link != NULL; link = next)
3950  {
3951  next = link->next;
3952  (*function) (link);
3953  }
3954 }
3955 
3956 /*
3957  * ws_list_total - maps a function over the elements of a list and totals up
3958  * the integers returned by the mapping function.
3959  * return: total of all calls to mapping function
3960  * list(in): list to examine
3961  * function(in): function to call on list elements
3962  */
3963 int
3964 ws_list_total (DB_LIST * list, LTOTALER function)
3965 {
3966  DB_LIST *el;
3967  int total = 0;
3968 
3969  for (el = list; el != NULL; el = el->next)
3970  {
3971  total += (*function) (el);
3972  }
3973 
3974  return (total);
3975 }
3976 
3977 /*
3978  * ws_list_copy - Copies a list by calling a copier function for each element.
3979  * return: new list
3980  * src(in): list to copy
3981  * copier(in): function to copy the elements
3982  * freeer(in): function to free the elements
3983  */
3984 DB_LIST *
3985 ws_list_copy (DB_LIST * src, LCOPIER copier, LFREEER freeer)
3986 {
3987  DB_LIST *list, *last, *new_;
3988 
3989  list = last = NULL;
3990  for (; src != NULL; src = src->next)
3991  {
3992  new_ = (DB_LIST *) (*copier) (src);
3993  if (new_ == NULL)
3994  {
3995  goto memory_error;
3996  }
3997 
3998  new_->next = NULL;
3999  if (list == NULL)
4000  {
4001  list = new_;
4002  }
4003  else
4004  {
4005  last->next = new_;
4006  }
4007  last = new_;
4008  }
4009  return (list);
4010 
4011 memory_error:
4012  if (freeer != NULL)
4013  {
4014  ws_list_free (list, freeer);
4015  }
4016  return NULL;
4017 }
4018 
4019 /*
4020  * ws_list_nconc - concatenate list2 to list1
4021  * return: list pointer
4022  * list1(out): first list
4023  * list2(in): list to concatenate
4024  * Note:
4025  * If list1 was NULL, it returns a pointer to list2.
4026  */
4027 DB_LIST *
4028 ws_list_nconc (DB_LIST * list1, DB_LIST * list2)
4029 {
4030  DB_LIST *el, *result;
4031 
4032  if (list1 == NULL)
4033  {
4034  result = list2;
4035  }
4036  else
4037  {
4038  result = list1;
4039  for (el = list1; el->next != NULL; el = el->next)
4040  ;
4041  el->next = list2;
4042  }
4043 
4044  return result;
4045 }
4046 
4047 /*
4048  * NAMED LIST UTILITIES
4049  */
4050 /*
4051  * These utilities assume elements with a link field and a name.
4052  * struct named_link {
4053  * struct named_link *next;
4054  * const char *name;
4055  * }
4056  */
4057 
4058 /*
4059  * nlist_find - Search a name list for an entry with the given name.
4060  * return: namelist entry
4061  * list(in): list to search
4062  * name(in): element name to look for
4063  * fcn(in): compare function
4064  */
4065 DB_NAMELIST *
4066 nlist_find (DB_NAMELIST * list, const char *name, NLSEARCHER fcn)
4067 {
4068  DB_NAMELIST *el, *found;
4069 
4070  found = NULL;
4071 
4072  if (fcn == NULL)
4073  {
4074  fcn = (NLSEARCHER) strcmp;
4075  }
4076 
4077  for (el = list; el != NULL && found == NULL; el = el->next)
4078  {
4079  if ((el->name == name) || ((el->name != NULL) && (name != NULL) && (*fcn) (el->name, name) == 0))
4080  {
4081  found = el;
4082  }
4083  }
4084 
4085  return found;
4086 }
4087 
4088 
4089 /*
4090  * nlist_remove - Removes a named element from a list.
4091  * return: removed element (if found), NULL otherwise
4092  * root(in/out): pointer to pointer to list head
4093  * name(in): name of entry to remove
4094  * fcn(in): compare function
4095  * Note:
4096  * If an element with the given name was found it is removed and returned.
4097  * If an element was not found, NULL is returned.
4098  */
4099 DB_NAMELIST *
4100 nlist_remove (DB_NAMELIST ** root, const char *name, NLSEARCHER fcn)
4101 {
4102  DB_NAMELIST *el, *prev, *found;
4103 
4104  if (fcn == NULL)
4105  {
4106  fcn = (NLSEARCHER) strcmp;
4107  }
4108 
4109  found = NULL;
4110 
4111  for (el = *root, prev = NULL; el != NULL && found == NULL; el = el->next)
4112  {
4113  if ((el->name == name) || ((el->name != NULL) && (name != NULL) && (*fcn) (el->name, name) == 0))
4114  {
4115  found = el;
4116  }
4117  else
4118  {
4119  prev = el;
4120  }
4121  }
4122 
4123  if (found != NULL)
4124  {
4125  if (prev == NULL)
4126  {
4127  *root = found->next;
4128  }
4129  else
4130  {
4131  prev->next = found->next;
4132  }
4133  }
4134 
4135  return found;
4136 }
4137 
4138 /*
4139  * nlist_add - Adds an element to a namelist if it does not already exist.
4140  * return: NO_ERROR if the element was added , error code otherwise
4141  * list(in/out): pointer to pointer to list head
4142  * name(in): element name to add
4143  * fcn(in): compare function
4144  * added_ptr(out): set to 1 if added
4145  */
4146 int
4147 nlist_add (DB_NAMELIST ** list, const char *name, NLSEARCHER fcn, int *added_ptr)
4148 {
4149  DB_NAMELIST *found, *new_;
4150  int status = 0;
4151 
4152  found = nlist_find (*list, name, fcn);
4153 
4154  if (found != NULL)
4155  {
4156  goto error;
4157  }
4158 
4159  new_ = (DB_NAMELIST *) db_ws_alloc (sizeof (DB_NAMELIST));
4160  if (new_ == NULL)
4161  {
4162  assert (er_errid () != NO_ERROR);
4163  return er_errid ();
4164  }
4165 
4166  new_->name = ws_copy_string (name);
4167  if (new_->name == NULL)
4168  {
4169  db_ws_free (new_);
4170 
4171  assert (er_errid () != NO_ERROR);
4172  return er_errid ();
4173  }
4174 
4175  new_->next = *list;
4176  *list = new_;
4177 
4178  status = 1;
4179 
4180 error:
4181  if (added_ptr != NULL)
4182  {
4183  *added_ptr = status;
4184  }
4185 
4186  return NO_ERROR;
4187 }
4188 
4189 /*
4190  * nlist_append - appends an element to a namelist if it does not exist.
4191  * return: NO_ERROR if the element was added , error code otherwise
4192  * list(in/out): pointer to pointer to list head
4193  * name(in): entry name to append
4194  * fcn(in): compare function
4195  * added_ptr(out): set to 1 if added
4196  */
4197 int
4198 nlist_append (DB_NAMELIST ** list, const char *name, NLSEARCHER fcn, int *added_ptr)
4199 {
4200  DB_NAMELIST *el, *found, *last, *new_;
4201  int status = 0;
4202 
4203  if (fcn == NULL)
4204  {
4205  fcn = (NLSEARCHER) strcmp;
4206  }
4207 
4208  if (name == NULL)
4209  {
4210  goto error;
4211  }
4212 
4213  found = NULL;
4214  last = NULL;
4215 
4216  for (el = *list; el != NULL && found == NULL; el = el->next)
4217  {
4218  if ((el->name == name) || ((el->name != NULL) && (name != NULL) && (*fcn) (el->name, name) == 0))
4219  {
4220  found = el;
4221  }
4222  last = el;
4223  }
4224 
4225  if (found != NULL)
4226  {
4227  goto error;
4228  }
4229 
4230  new_ = (DB_NAMELIST *) db_ws_alloc (sizeof (DB_NAMELIST));
4231  if (new_ == NULL)
4232  {
4233  assert (er_errid () != NO_ERROR);
4234  return er_errid ();
4235  }
4236 
4237  new_->name = ws_copy_string (name);
4238  if (new_->name == NULL)
4239  {
4240  db_ws_free (new_);
4241 
4242  assert (er_errid () != NO_ERROR);
4243  return er_errid ();
4244  }
4245 
4246  new_->next = NULL;
4247 
4248  if (last == NULL)
4249  {
4250  *list = new_;
4251  }
4252  else
4253  {
4254  last->next = new_;
4255  }
4256 
4257  status = 1;
4258 
4259 error:
4260  if (added_ptr != NULL)
4261  {
4262  *added_ptr = status;
4263  }
4264 
4265  return NO_ERROR;
4266 }
4267 
4268 
4269 #if defined (ENABLE_UNUSED_FUNCTION)
4270 /*
4271  * nlist_find_or_append - searches for a name or appends the element.
4272  * return: error code
4273  * list(in/out): pointer to pointer to list head
4274  * name(in): name of element to add
4275  * fcn(in): compare funciont
4276  * position(out): position of element if found or inserted
4277  */
4278 int
4279 nlist_find_or_append (DB_NAMELIST ** list, const char *name, NLSEARCHER fcn, int *position)
4280 {
4281  DB_NAMELIST *el, *found, *last, *new_;
4282  int psn = -1;
4283 
4284  if (fcn == NULL)
4285  {
4286  fcn = (NLSEARCHER) strcmp;
4287  }
4288 
4289  if (name != NULL)
4290  {
4291  found = last = NULL;
4292  for (el = *list, psn = 0; el != NULL && found == NULL; el = el->next)
4293  {
4294  if ((el->name == name) || ((el->name != NULL) && (*fcn) (el->name, name) == 0))
4295  {
4296  found = el;
4297  }
4298  else
4299  {
4300  psn++;
4301  }
4302  last = el;
4303  }
4304  if (found == NULL)
4305  {
4306  new_ = (DB_NAMELIST *) db_ws_alloc (sizeof (DB_NAMELIST));
4307  if (new_ == NULL)
4308  {
4309  assert (er_errid () != NO_ERROR);
4310  return er_errid ();
4311  }
4312 
4313  new_->name = ws_copy_string (name);
4314  if (new_->name == NULL)
4315  {
4316  db_ws_free (new_);
4317 
4318  assert (er_errid () != NO_ERROR);
4319  return er_errid ();
4320  }
4321 
4322  new_->next = NULL;
4323  if (last == NULL)
4324  {
4325  *list = new_;
4326  }
4327  else
4328  {
4329  last->next = new_;
4330  }
4331  }
4332  }
4333  *position = psn;
4334  return NO_ERROR;
4335 }
4336 #endif /* ENABLE_UNUSED_FUNCTION */
4337 
4338 /*
4339  * nlist_free - frees a name list
4340  * return: none
4341  * list(in/out): list to free
4342  */
4343 void
4345 {
4346  DB_NAMELIST *el, *next;
4347 
4348  for (el = list, next = NULL; el != NULL; el = next)
4349  {
4350  next = el->next;
4351  db_ws_free ((char *) el->name);
4352  db_ws_free (el);
4353  }
4354 }
4355 
4356 /*
4357  * nlist_copy - makes a copy of a named list
4358  * return: new namelist
4359  * list(in): namelist to copy
4360  */
4361 DB_NAMELIST *
4363 {
4364  DB_NAMELIST *first, *last, *el, *new_;
4365 
4366  first = last = NULL;
4367  for (el = list; el != NULL; el = el->next)
4368  {
4369  new_ = (DB_NAMELIST *) db_ws_alloc (sizeof (DB_NAMELIST));
4370  if (new_ == NULL)
4371  {
4372  goto memory_error;
4373  }
4374 
4375  new_->name = ws_copy_string (el->name);
4376  if (new_->name == NULL)
4377  {
4378  db_ws_free (new_);
4379  goto memory_error;
4380  }
4381 
4382  new_->next = NULL;
4383  if (first == NULL)
4384  {
4385  first = new_;
4386  }
4387  else
4388  {
4389  last->next = new_;
4390  }
4391  last = new_;
4392  }
4393 
4394  return first;
4395 
4396 memory_error:
4397  nlist_free (first);
4398  return NULL;
4399 }
4400 
4401 /*
4402  * nlist_filter - remove all elements with the given name from a list
4403  * and return a list of the removed elements.
4404  * return: filtered list of elements
4405  * root(in/out): pointer to pointer to list head
4406  * name(in): name of elements to filter
4407  * fcn(in): compare function
4408  */
4409 DB_NAMELIST *
4410 nlist_filter (DB_NAMELIST ** root, const char *name, NLSEARCHER fcn)
4411 {
4412  DB_NAMELIST *el, *prev, *next, *head, *filter;
4413 
4414  if (fcn == NULL)
4415  {
4416  fcn = (NLSEARCHER) strcmp;
4417  }
4418 
4419  filter = NULL;
4420  head = *root;
4421 
4422  for (el = head, prev = NULL, next = NULL; el != NULL; el = next)
4423  {
4424  next = el->next;
4425  if ((el->name == name) || ((el->name != NULL) && (name != NULL) && (*fcn) (el->name, name) == 0))
4426  {
4427  if (prev == NULL)
4428  {
4429  head = next;
4430  }
4431  else
4432  {
4433  prev->next = next;
4434  }
4435  el->next = filter;
4436  filter = el;
4437  }
4438  else
4439  {
4440  prev = el;
4441  }
4442  }
4443 
4444  *root = head;
4445  return filter;
4446 }
4447 
4448 /*
4449  * MOP LIST UTILITIES
4450  */
4451 /*
4452  * These utilities operate on a list of MOP links.
4453  * This is such a common operation for the workspace and schema manager that
4454  * it merits its own optimized implementation.
4455  *
4456  */
4457 
4458 /*
4459  * ml_find - searches a list for the given mop.
4460  * return: non-zero if mop was in the list
4461  * list(in): list to search
4462  * mop(in): mop we're looking for
4463  */
4464 int
4465 ml_find (DB_OBJLIST * list, MOP mop)
4466 {
4467  DB_OBJLIST *l;
4468  int found = 0;
4469 
4470  for (l = list; l != NULL && !found; l = l->next)
4471  {
4472  if (ws_is_same_object (l->op, mop))
4473  {
4474  found = 1;
4475  }
4476  }
4477 
4478  return found;
4479 }
4480 
4481 /*
4482  * ml_add - Adds a MOP to the list if it isn't already present.
4483  * return: NO_ERROR or error code
4484  * list(in/out): pointer to pointer to list head
4485  * mop(in): mop to add to the list
4486  * added_ptr(out): set to 1 if added
4487  * Note:
4488  * There is no guarentee where the MOP will be added in the list although
4489  * currently it will push it at the head of the list. Use ml_append
4490  * if you must ensure ordering.
4491  */
4492 int
4493 ml_add (DB_OBJLIST ** list, MOP mop, int *added_ptr)
4494 {
4495  DB_OBJLIST *l, *found, *new_;
4496  int added;
4497 
4498  added = 0;
4499  if (mop == NULL)
4500  {
4501  goto error;
4502  }
4503 
4504  for (l = *list, found = NULL; l != NULL && found == NULL; l = l->next)
4505  {
4506  if (ws_is_same_object (l->op, mop))
4507  {
4508  found = l;
4509  }
4510  }
4511 
4512  /* since we can get the end of list easily, may want to append here */
4513  if (found != NULL)
4514  {
4515  goto error;
4516  }
4517 
4518  new_ = (DB_OBJLIST *) db_ws_alloc (sizeof (DB_OBJLIST));
4519  if (new_ == NULL)
4520  {
4521  assert (er_errid () != NO_ERROR);
4522  return er_errid ();
4523  }
4524 
4525  new_->op = mop;
4526  new_->next = *list;
4527  *list = new_;
4528 
4529  added = 1;
4530 
4531 error:
4532  if (added_ptr != NULL)
4533  {
4534  *added_ptr = added;
4535  }
4536 
4537  return NO_ERROR;
4538 }
4539 
4540 /*
4541  * ml_append - Appends a MOP to the list if it isn't already present.
4542  * return: NO_ERROR or error code
4543  * list(in/out): pointer to pointer to list head
4544  * mop(in): mop to add
4545  * added_ptr(out): set to 1 if added
4546  */
4547 int
4548 ml_append (DB_OBJLIST ** list, MOP mop, int *added_ptr)
4549 {
4550  DB_OBJLIST *l, *found, *new_, *last;
4551  int added;
4552 
4553  added = 0;
4554  if (mop == NULL)
4555  {
4556  goto error;
4557  }
4558 
4559  last = NULL;
4560  for (l = *list, found = NULL; l != NULL && found == NULL; l = l->next)
4561  {
4562  if (ws_is_same_object (l->op, mop))
4563  {
4564  found = l;
4565  }
4566  last = l;
4567  }
4568 
4569  /* since we can get the end of list easily, may want to append here */
4570 
4571  if (found != NULL)
4572  {
4573  goto error;
4574  }
4575 
4576  new_ = (DB_OBJLIST *) db_ws_alloc (sizeof (DB_OBJLIST));
4577  if (new_ == NULL)
4578  {
4579  assert (er_errid () != NO_ERROR);
4580  return er_errid ();
4581  }
4582 
4583  new_->op = mop;
4584  new_->next = NULL;
4585  if (last == NULL)
4586  {
4587  *list = new_;
4588  }
4589  else
4590  {
4591  last->next = new_;
4592  }
4593 
4594  added = 1;
4595 
4596 error:
4597 
4598  if (added_ptr != NULL)
4599  {
4600  *added_ptr = added;
4601  }
4602 
4603  return NO_ERROR;
4604 }
4605 
4606 /*
4607  * ml_remove - removes a mop from a mop list if it is found.
4608  * return: non-zero if mop was removed
4609  * list(in/out): pointer to pointer to list head
4610  * mop(in): mop to remove from the list
4611  */
4612 int
4613 ml_remove (DB_OBJLIST ** list, MOP mop)
4614 {
4615  DB_OBJLIST *l, *found, *prev;
4616  int deleted;
4617 
4618  deleted = 0;
4619  for (l = *list, found = NULL, prev = NULL; l != NULL && found == NULL; l = l->next)
4620  {
4621  if (ws_is_same_object (l->op, mop))
4622  {
4623  found = l;
4624  }
4625  else
4626  {
4627  prev = l;
4628  }
4629  }
4630 
4631  if (found != NULL)
4632  {
4633  if (prev == NULL)
4634  {
4635  *list = found->next;
4636  }
4637  else
4638  {
4639  prev->next = found->next;
4640  }
4641  db_ws_free (found);
4642  deleted = 1;
4643  }
4644 
4645  return deleted;
4646 }
4647 
4648 /*
4649  * ml_free - free a list of MOPs.
4650  * return: none
4651  * list(in/out): list to free
4652  */
4653 void
4655 {
4656  DB_OBJLIST *l, *next;
4657 
4658  for (l = list, next = NULL; l != NULL; l = next)
4659  {
4660  next = l->next;
4661  db_ws_free (l);
4662  }
4663 }
4664 
4665 /*
4666  * ml_copy - copy a list of mops.
4667  * return: new list
4668  * list(in): list to copy
4669  */
4670 DB_OBJLIST *
4672 {
4673  DB_OBJLIST *l, *new_, *first, *last;
4674 
4675  first = last = NULL;
4676  for (l = list; l != NULL; l = l->next)
4677  {
4678  new_ = (DB_OBJLIST *) db_ws_alloc (sizeof (DB_OBJLIST));
4679  if (new_ == NULL)
4680  {
4681  goto memory_error;
4682  }
4683 
4684  new_->next = NULL;
4685  new_->op = l->op;
4686  if (first == NULL)
4687  {
4688  first = new_;
4689  }
4690  else
4691  {
4692  last->next = new_;
4693  }
4694  last = new_;
4695  }
4696 
4697  return first;
4698 
4699 memory_error:
4700  ml_free (first);
4701  return NULL;
4702 }
4703 
4704 /*
4705  * ml_size - This calculates the number of bytes of memory required for the
4706  * storage of a MOP list.
4707  * return: memory size of list
4708  * list(in): list to examine
4709  */
4710 int
4712 {
4713  int size = 0;
4714 
4715  size = ws_list_length ((DB_LIST *) list) * sizeof (DB_OBJLIST);
4716 
4717  return (size);
4718 }
4719 
4720 #if defined (ENABLE_UNUSED_FUNCTION)
4721 /*
4722  * ml_filter - maps a function over the mops in a list selectively removing
4723  * elements based on the results of the filter function.
4724  * return: void
4725  * list(in/out): pointer to pointer to mop list
4726  * filter(in): filter function
4727  * args(in): args to pass to filter function
4728  * Note:
4729  * If the filter function returns zero, the mop will be removed.
4730  */
4731 void
4732 ml_filter (DB_OBJLIST ** list, MOPFILTER filter, void *args)
4733 {
4734  DB_OBJLIST *l, *prev, *next;
4735  int keep;
4736 
4737  prev = NULL;
4738  next = NULL;
4739 
4740  for (l = *list; l != NULL; l = next)
4741  {
4742  next = l->next;
4743  keep = (*filter) (l->op, args);
4744  if (keep)
4745  {
4746  prev = l;
4747  }
4748  else
4749  {
4750  if (prev != NULL)
4751  {
4752  prev->next = next;
4753  }
4754  else
4755  {
4756  *list = next;
4757  }
4758  }
4759  }
4760 }
4761 #endif /* ENABLE_UNUSED_FUNCTION */
4762 
4763 /*
4764  * DB_OBJLIST AREA ALLOCATION
4765  */
4766 
4767 
4768 /*
4769  * ml_ext_alloc_link - This is used to allocate a mop list link for return to
4770  * the application layer.
4771  * return: new mop list link
4772  * Note:
4773  * These links must be allocated in areas outside the workspace
4774  * so they serve as roots to the garabage collector.
4775  */
4776 DB_OBJLIST *
4778 {
4779  return ((DB_OBJLIST *) area_alloc (Objlist_area));
4780 }
4781 
4782 
4783 /*
4784  * ml_ext_free_link - frees a mop list link that was allocated with
4785  * ml_ext_alloc_link.
4786  * return: void
4787  * link(in/out): link to free
4788  */
4789 void
4791 {
4792  if (link != NULL)
4793  {
4794  link->op = NULL; /* this is important */
4795  (void) area_free (Objlist_area, (void *) link);
4796  }
4797 }
4798 
4799 /*
4800  * ml_ext_free - frees a complete list of links allocated with the
4801  * ml_ext_alloc_link function.
4802  * return: void
4803  * list(in/out): list to free
4804  */
4805 void
4807 {
4808  DB_OBJLIST *l, *next;
4809 
4810  if (list == NULL)
4811  {
4812  return;
4813  }
4814 
4815  if (area_validate (Objlist_area, (void *) list) == NO_ERROR)
4816  {
4817  for (l = list, next = NULL; l != NULL; l = next)
4818  {
4819  next = l->next;
4820  ml_ext_free_link (l);
4821  }
4822  }
4823 }
4824 
4825 /*
4826  * ml_ext_copy - Like ml_copy except that it allocates the mop list links using
4827  * ml_ext_alloc_link so they can be returned to the application level.
4828  * return: new mop list
4829  * list(in): list to copy
4830  */
4831 DB_OBJLIST *
4833 {
4834  DB_OBJLIST *l, *new_, *first, *last;
4835 
4836  first = NULL;
4837  last = NULL;
4838 
4839  for (l = list; l != NULL; l = l->next)
4840  {
4841  new_ = ml_ext_alloc_link ();
4842  if (new_ == NULL)
4843  {
4844  goto memory_error;
4845  }
4846 
4847  new_->next = NULL;
4848  new_->op = l->op;
4849  if (first == NULL)
4850  {
4851  first = new_;
4852  }
4853  else
4854  {
4855  last->next = new_;
4856  }
4857 
4858  last = new_;
4859  }
4860 
4861  return first;
4862 
4863 memory_error:
4864  ml_ext_free (first);
4865  return NULL;
4866 }
4867 
4868 /*
4869  * ml_ext_add - same as ml_add except that it allocates a mop in the external
4870  * area.
4871  * return: NO_ERROR or error code
4872  * list(in/out): pointer to pointer to list head
4873  * mop(in): mop to add to the list
4874  * added_ptr(out): set to 1 if added
4875  */
4876 int
4877 ml_ext_add (DB_OBJLIST ** list, MOP mop, int *added_ptr)
4878 {
4879  DB_OBJLIST *l, *found, *new_;
4880  int added;
4881 
4882  added = 0;
4883  if (mop == NULL)
4884  {
4885  goto error;
4886  }
4887 
4888  for (l = *list, found = NULL; l != NULL && found == NULL; l = l->next)
4889  {
4890  if (ws_is_same_object (l->op, mop))
4891  {
4892  found = l;
4893  }
4894  }
4895 
4896  /* since we can get the end of list easily, may want to append here */
4897  if (found == NULL)
4898  {
4899  new_ = (DB_OBJLIST *) area_alloc (Objlist_area);
4900  if (new_ == NULL)
4901  {
4902  assert (er_errid () != NO_ERROR);
4903  return er_errid ();
4904  }
4905 
4906  new_->op = mop;
4907  new_->next = *list;
4908  *list = new_;
4909  added = 1;
4910  }
4911 
4912 error:
4913  if (added_ptr != NULL)
4914  {
4915  *added_ptr = added;
4916  }
4917  return NO_ERROR;
4918 }
4919 
4920 /*
4921  * ws_set_ignore_error_list_for_mflush() -
4922  * return: NO_ERROR or error code
4923  * error_count(in):
4924  * error_list(in):
4925  */
4926 int
4927 ws_set_ignore_error_list_for_mflush (int error_count, int *error_list)
4928 {
4929  if (error_count > 0)
4930  {
4931  ws_Error_ignore_count = error_count;
4932  memcpy (ws_Error_ignore_list, error_list, sizeof (int) * error_count);
4933  }
4934 
4935  return NO_ERROR;
4936 }
4937 
4938 /*
4939  * ws_set_error_into_error_link() -
4940  * return: void
4941  * mop(in):
4942  */
4943 void
4945 {
4946  WS_REPL_FLUSH_ERR *flush_err;
4947  char *ptr;
4948 
4949  flush_err = (WS_REPL_FLUSH_ERR *) malloc (sizeof (WS_REPL_FLUSH_ERR));
4950  if (flush_err == NULL)
4951  {
4953  return;
4954  }
4955 
4956  flush_err->class_oid = obj->class_oid;
4957  flush_err->operation = obj->operation;
4958 
4959  ptr = content_ptr;
4960  ptr = or_unpack_mem_value (ptr, &flush_err->pkey_value);
4961  ptr = or_unpack_int (ptr, &flush_err->error_code);
4962  ptr = or_unpack_string_alloc (ptr, &flush_err->error_msg);
4963 
4964  flush_err->error_link = ws_Repl_error_link;
4965  ws_Repl_error_link = flush_err;
4966 
4967  return;
4968 }
4969 
4970 /*
4971  * ws_get_mvcc_snapshot_version () - Get current snapshot version.
4972  *
4973  * return : Current snapshot version.
4974  */
4975 unsigned int
4977 {
4978  return ws_MVCC_snapshot_version;
4979 }
4980 
4981 /*
4982  * ws_increment_mvcc_snapshot_version () - Increment current snapshot version.
4983  *
4984  * return : Void.
4985  */
4986 void
4988 {
4990 }
4991 
4992 /*
4993  * ws_is_mop_fetched_with_current_snapshot () - Check if mop was fetched
4994  * during current snapshot.
4995  *
4996  * return : True if mop was already fetched during current snapshot.
4997  * mop (in) : Cached object pointer.
4998  */
4999 bool
5001 {
5003 }
5004 
5005 /*
5006  * ws_set_mop_fetched_with_current_snapshot () - Set current snapshot version
5007  * to mop.
5008  *
5009  * return : Void.
5010  * mop (in) : Mop fetched with current snapshot.
5011  */
5012 void
5014 {
5016 }
5017 
5018 /*
5019  * ws_is_dirty () - Is object dirty.
5020  *
5021  * return : True/false.
5022  * mop (in) : Checked object (latest mvcc version is checked).
5023  */
5024 int
5026 {
5027  return mop->dirty;
5028 }
5029 
5030 /*
5031  * ws_is_deleted () - Is object deleted.
5032  *
5033  * return : True if deleted, false otherwise
5034  * mop (in) : Checked object (latest mvcc version is checked).
5035  */
5036 int
5038 {
5039  return mop->deleted;
5040 }
5041 
5042 /*
5043  * ws_set_deleted () - Marks an object as deleted
5044  *
5045  * return :
5046  * mop (in) : Object to be set as deleted
5047  *
5048  * Note: Latest mvcc version is marked
5049  */
5050 void
5052 {
5053  mop->deleted = 1;
5054  WS_PUT_COMMIT_MOP (mop);
5055 }
5056 
5057 /*
5058  * ws_is_same_object () - Check if the mops are the same
5059  *
5060  * return : True if the same object.
5061  * mop1 (in) : First mop.
5062  * mop2 (in) : Second mop.
5063  */
5064 bool
5066 {
5067  return (mop1 == mop2);
5068 }
5069 
5070 /*
5071  * ws_get_error_from_error_link() -
5072  * return: void
5073  */
5076 {
5077  WS_REPL_FLUSH_ERR *flush_err;
5078 
5079  flush_err = ws_Repl_error_link;
5080  if (flush_err == NULL)
5081  {
5082  return NULL;
5083  }
5084 
5085  ws_Repl_error_link = flush_err->error_link;
5086  flush_err->error_link = NULL;
5087 
5088  return flush_err;
5089 }
5090 
5091 /*
5092  * ws_clear_all_errors_of_error_link() -
5093  * return: void
5094  */
5095 void
5097 {
5098  WS_REPL_FLUSH_ERR *flush_err, *next;
5099 
5100  for (flush_err = ws_Repl_error_link; flush_err; flush_err = next)
5101  {
5102  next = flush_err->error_link;
5103  ws_free_repl_flush_error (flush_err);
5104  }
5105  ws_Repl_error_link = NULL;
5106 
5107  return;
5108 }
5109 
5110 /*
5111  * ws_free_flush_error() -
5112  * return: void
5113  */
5114 void
5116 {
5117  assert (flush_err != NULL);
5118 
5119  if (flush_err->error_msg != NULL)
5120  {
5121  free_and_init (flush_err->error_msg);
5122  }
5123 
5124  db_value_clear (&flush_err->pkey_value);
5125 
5126  free_and_init (flush_err);
5127 
5128  return;
5129 }
5130 
5131 /*
5132  * ws_move_label_value_list() - move label value list
5133  * return: void
5134  * dest_mop (in) : destination mop
5135  * src_mop (in) : source mop
5136  */
5137 void
5138 ws_move_label_value_list (MOP dest_mop, MOP src_mop)
5139 {
5140  WS_VALUE_LIST *value_node;
5141 
5142  if (dest_mop == NULL || src_mop == NULL)
5143  {
5144  return;
5145  }
5146 
5147  /* move src_mop->label_value_list to dest_mop->label_value_list */
5148  if (dest_mop->label_value_list == NULL)
5149  {
5150  dest_mop->label_value_list = src_mop->label_value_list;
5151  }
5152  else
5153  {
5154  value_node = dest_mop->label_value_list;
5155  while (value_node->next != NULL)
5156  {
5157  value_node = value_node->next;
5158  }
5159 
5160  value_node->next = src_mop->label_value_list;
5161  }
5162 
5163  /* update mop for each db_value from src_mop->label_value_list */
5164  for (value_node = src_mop->label_value_list; value_node != NULL; value_node = value_node->next)
5165  {
5166  if (DB_VALUE_TYPE (value_node->val) == DB_TYPE_OBJECT)
5167  {
5168  value_node->val->data.op = dest_mop;
5169  }
5170  }
5171 
5172  src_mop->label_value_list = NULL;
5173 }
5174 
5175 /*
5176  * ws_remove_label_value_from_mop() - remove label value from mop value list
5177  * return: void
5178  * mop (in) : mop
5179  * val (in) : value to remove from mop value list
5180  */
5181 void
5183 {
5184  WS_VALUE_LIST *prev_value_node, *value_node;
5185 
5186  if (mop == NULL || val == NULL)
5187  {
5188  return;
5189  }
5190 
5191  if (mop->label_value_list == NULL)
5192  {
5193  return;
5194  }
5195 
5196  /* search for val into mop->label_value_list */
5197  prev_value_node = NULL;
5198  value_node = mop->label_value_list;
5199  while (value_node != NULL)
5200  {
5201  if (value_node->val == val)
5202  {
5203  break;
5204  }
5205 
5206  prev_value_node = value_node;
5207  value_node = value_node->next;
5208  }
5209 
5210  if (value_node == NULL)
5211  {
5212  /* not found */
5213  return;
5214  }
5215 
5216  /* remove val from mop->label_value_list */
5217  if (value_node == mop->label_value_list)
5218  {
5219  mop->label_value_list = mop->label_value_list->next;
5220  }
5221  else
5222  {
5223  prev_value_node->next = value_node->next;
5224  }
5225 
5226  value_node->val = NULL;
5227  db_ws_free (value_node);
5228 }
5229 
5230 /*
5231  * ws_add_label_value_to_mop() - add label value to mop value list
5232  * return: error code
5233  * mop (in) : mop.
5234  * val (in) : value to add to mop value list
5235  */
5236 int
5238 {
5239  WS_VALUE_LIST *value_node;
5240 
5241  if (mop == NULL || val == NULL)
5242  {
5243  return NO_ERROR;
5244  }
5245 
5246  value_node = (WS_VALUE_LIST *) db_ws_alloc (sizeof (WS_VALUE_LIST));
5247  if (value_node == NULL)
5248  {
5249  assert (er_errid () != NO_ERROR);
5250  return er_errid ();
5251  }
5252 
5253  value_node->val = val;
5254 
5255  if (mop->label_value_list == NULL)
5256  {
5257  value_node->next = NULL;
5258  mop->label_value_list = value_node;
5259  }
5260  else
5261  {
5262  value_node->next = mop->label_value_list;
5263  mop->label_value_list = value_node;
5264  }
5265 
5266  return NO_ERROR;
5267 }
5268 
5269 #if !defined (NDEBUG)
5270 static void
5272 {
5273  MOP mop;
5274  DB_OBJLIST *m = NULL;
5275  unsigned int slot;
5276 
5277  for (m = ws_Resident_classes; m != NULL; m = m->next)
5278  {
5279  for (mop = m->op->class_link; mop != NULL && mop != Null_object; mop = mop->class_link)
5280  {
5281  assert (mop->lock == NULL_LOCK);
5282  }
5283  }
5284 
5285  for (slot = 0; slot < ws_Mop_table_size; slot++)
5286  {
5287  for (mop = ws_Mop_table[slot].head; mop != NULL; mop = mop->hash_link)
5288  {
5289  assert (mop->lock == NULL_LOCK);
5290  }
5291  }
5292 }
5293 #endif /* !NDEBUG */
5294 
5295 /*
5296  * ws_clean_label_value_list() - clean mop value list
5297  * return: void
5298  * mop (in) : mop.
5299  */
5300 void
5302 {
5303  WS_VALUE_LIST *next_value_node, *value_node;
5304 
5305  value_node = mop->label_value_list;
5306  while (value_node != NULL)
5307  {
5308  next_value_node = value_node->next;
5309  value_node->val = NULL;
5310  db_ws_free (value_node);
5311  value_node = next_value_node;
5312  }
5313 
5314  mop->label_value_list = NULL;
5315 }
5316 
5317 /*
5318  * ws_init_repl_objs() -
5319  * free_func (in): function for freeing data in recdes
5320  * return: void
5321  */
5322 void
5324 {
5325  ws_Repl_objs.head = NULL;
5326  ws_Repl_objs.tail = NULL;
5327  ws_Repl_objs.num_items = 0;
5328 }
5329 
5330 /*
5331  * ws_get_repl_obj_from_list() -
5332  * return: repl object
5333  */
5334 WS_REPL_OBJ *
5336 {
5337  WS_REPL_OBJ *repl_obj;
5338 
5339  repl_obj = ws_Repl_objs.head;
5340  if (repl_obj != NULL)
5341  {
5342  ws_Repl_objs.head = repl_obj->next;
5343  if (ws_Repl_objs.head == NULL)
5344  {
5345  ws_Repl_objs.tail = NULL;
5346  }
5347 
5348  ws_Repl_objs.num_items--;
5349  }
5350 
5351  assert (ws_Repl_objs.num_items >= 0);
5352 
5353  return repl_obj;
5354 }
5355 
5356 /*
5357  * ws_free_repl_obj() -
5358  * return:
5359  */
5360 void
5362 {
5363  assert (obj != NULL);
5364 
5365  if (obj->packed_pkey_value != NULL)
5366  {
5368  }
5369  free_and_init (obj);
5370 }
5371 
5372 /*
5373  * ws_clear_all_repl_objs() -
5374  * return:
5375  */
5376 void
5378 {
5379  WS_REPL_OBJ *obj, *next_obj;
5380 
5381  obj = ws_Repl_objs.head;
5382  while (obj != NULL)
5383  {
5384  next_obj = obj->next;
5385  ws_free_repl_obj (obj);
5386 
5387  obj = next_obj;
5388  }
5389 
5390  ws_Repl_objs.head = NULL;
5391  ws_Repl_objs.tail = NULL;
5392  ws_Repl_objs.num_items = 0;
5393 
5394  return;
5395 }
5396 
5397 /*
5398  * ws_add_to_repl_obj_list() - create a repl object and add it
5399  * class_oid(in):
5400  * key(in):
5401  * recdes(in):
5402  * operation(in):
5403  * has_index(in):
5404  *
5405  * return:
5406  */
5407 int
5408 ws_add_to_repl_obj_list (OID * class_oid, char *packed_pkey_value, int packed_pkey_value_length, RECDES * recdes,
5409  int operation, bool has_index)
5410 {
5411  WS_REPL_OBJ *repl_obj = NULL;
5412 
5413  repl_obj = (WS_REPL_OBJ *) malloc (sizeof (WS_REPL_OBJ));
5414  if (repl_obj == NULL)
5415  {
5417  return ER_OUT_OF_VIRTUAL_MEMORY;
5418  }
5419 
5420  assert (class_oid != NULL && OID_ISNULL (class_oid) == false);
5421  assert (packed_pkey_value != NULL && 0 < packed_pkey_value_length);
5422 
5423  COPY_OID (&repl_obj->class_oid, class_oid);
5424 
5425  repl_obj->packed_pkey_value_length = packed_pkey_value_length;
5426  repl_obj->packed_pkey_value = (char *) malloc (packed_pkey_value_length);
5427  if (repl_obj->packed_pkey_value == NULL)
5428  {
5429  free (repl_obj);
5431  return ER_OUT_OF_VIRTUAL_MEMORY;
5432  }
5433  memcpy (repl_obj->packed_pkey_value, packed_pkey_value, packed_pkey_value_length);
5434 
5435  repl_obj->recdes = recdes;
5436  repl_obj->has_index = has_index;
5437  repl_obj->operation = operation;
5438  repl_obj->next = NULL;
5439 
5440  if (ws_Repl_objs.tail == NULL)
5441  {
5442  ws_Repl_objs.head = repl_obj;
5443  ws_Repl_objs.tail = repl_obj;
5444  }
5445  else
5446  {
5447  ws_Repl_objs.tail->next = repl_obj;
5448  ws_Repl_objs.tail = repl_obj;
5449  }
5450  ws_Repl_objs.num_items++;
5451 
5452  return NO_ERROR;
5453 }
unsigned decached
Definition: work_space.h:152
MOP ws_vmop(MOP class_mop, int flags, DB_VALUE *keys)
Definition: work_space.c:754
WS_STATISTICS ws_Stats
Definition: work_space.c:105
void ws_clear_hints(MOP mop, bool leave_pinned)
Definition: work_space.c:3257
int set_area_init(void)
Definition: set_object.c:149
int ml_append(DB_OBJLIST **list, MOP mop, int *added_ptr)
Definition: work_space.c:4548
WS_REPL_OBJ * ws_get_repl_obj_from_list(void)
Definition: work_space.c:5335
void ws_clean(MOP op)
Definition: work_space.c:1681
#define WS_IS_DELETED(mop)
Definition: work_space.h:284
#define NO_ERROR
Definition: error_code.h:46
struct ws_mop_table_entry WS_MOP_TABLE_ENTRY
Definition: work_space.h:429
unsigned released
Definition: work_space.h:150
void au_reset_authorization_caches(void)
DB_COLLECTION * db_get_set(const DB_VALUE *value)
MOBJ vid_fetch_instance(MOP mop, DB_FETCH_MODE purpose, LC_FETCH_VERSION_TYPE read_fetch_version_type)
void ws_class_has_object_dependencies(MOP class_mop)
Definition: work_space.c:3166
bool ws_has_updated(void)
Definition: work_space.c:2536
#define ER_WS_PIN_VIOLATION
Definition: error_code.h:405
void ws_set_deleted(MOP mop)
Definition: work_space.c:5051
unsigned int mht_1strhash(const void *key, const unsigned int ht_size)
Definition: memory_hash.c:447
MOP ws_mop(const OID *oid, MOP class_mop)
Definition: work_space.c:614
void ws_clear_all_repl_objs(void)
Definition: work_space.c:5377
struct db_object * dirty_link
Definition: work_space.h:128
#define ER_MVCC_SERIALIZABLE_CONFLICT
Definition: error_code.h:1484
int tran_unilaterally_abort(void)
DB_OBJECT * db_real_instance(DB_OBJECT *obj)
Definition: db_virt.c:247
void area_destroy(AREA *area)
Definition: area_alloc.c:247
char * MOBJ
Definition: work_space.h:174
int dirty_list_emergencies
Definition: work_space.h:225
void ws_set_class(MOP inst, MOP class_mop)
Definition: work_space.c:1997
OID class_oid
Definition: work_space.h:83
RECDES * recdes
Definition: work_space.h:88
void ws_dump(FILE *fpp)
Definition: work_space.c:3600
void ws_release_user_instance(MOP mop)
Definition: work_space.c:1579
int db_get_int(const DB_VALUE *value)
void ws_dirty(MOP op)
Definition: work_space.c:1622
int db_is_vclass(DB_OBJECT *op)
Definition: db_virt.c:681
void *(* LCOPIER)(void *)
Definition: work_space.h:561
int classobj_area_init(void)
Definition: class_object.c:167
DB_TYPE
Definition: dbtype_def.h:670
int tp_value_equal(const DB_VALUE *value1, const DB_VALUE *value2, int do_coercion)
void ws_mark_deleted(MOP mop)
Definition: work_space.c:3081
int instance_list_emergencies
Definition: work_space.h:235
int ws_map_class_dirty(MOP class_op, MAPFUNC function, void *args)
Definition: work_space.c:2018
void ws_area_final(void)
Definition: work_space.c:3840
#define ER_FAILED
Definition: error_code.h:47
DB_IDENTIFIER oid
Definition: dbtype_def.h:1068
WS_REPL_OBJ * head
Definition: work_space.h:94
#define WS_REAL_OID(mop)
Definition: work_space.h:294
void ws_move_label_value_list(MOP dest_mop, MOP src_mop)
Definition: work_space.c:5138
void obt_area_final(void)
int ws_add_to_repl_obj_list(OID *class_oid, char *packed_pkey_value, int packed_pkey_value_length, RECDES *recdes, int operation, bool has_index)
Definition: work_space.c:5408
char * packed_pkey_value
Definition: work_space.h:84
char * or_unpack_string_alloc(char *ptr, char **string)
int mht_rem(MHT_TABLE *ht, const void *key, int(*rem_func)(const void *key, void *data, void *args), void *func_args)
Definition: memory_hash.c:1952
TP_DOMAIN * domain
Definition: class_object.h:444
int ws_is_deleted(MOP mop)
Definition: work_space.c:5037
DB_NAMELIST * nlist_filter(DB_NAMELIST **root, const char *name, NLSEARCHER fcn)
Definition: work_space.c:4410
int classobj_get_prop(DB_SEQ *properties, const char *name, DB_VALUE *pvalue)
DB_VALUE * val
Definition: work_space.h:103
int pr_area_init(void)
DB_OBJLIST * ml_ext_alloc_link(void)
Definition: work_space.c:4777
SM_ATTRIBUTE * attributes
Definition: class_object.h:721
const void * mht_put(MHT_TABLE *ht, const void *key, void *data)
Definition: memory_hash.c:1778
void nlist_free(DB_NAMELIST *list)
Definition: work_space.c:4344
int ws_chn(MOBJ obj)
Definition: work_space.c:2922
DB_OBJLIST * ml_ext_copy(DB_OBJLIST *list)
Definition: work_space.c:4832
int classobj_class_size(SM_CLASS *class_)
static void ws_clear_internal(bool clear_vmop_keys)
Definition: work_space.c:2495
int db_make_object(DB_VALUE *value, DB_C_OBJECT *obj)
DB_COLLECTION * set_create_sequence(int size)
Definition: set_object.c:2432
void ws_clear_all_repl_errors_of_error_link(void)
Definition: work_space.c:5096
int ws_Error_ignore_count
Definition: work_space.c:161
int temp_mops_freed
Definition: work_space.h:238
int temp_mops_allocated
Definition: work_space.h:237
static int class_type(DB_OBJECT *class_obj)
Definition: cas_execute.c:8143
unsigned no_objects
Definition: work_space.h:146
OID * ws_oid(MOP mop)
Definition: work_space.c:2884
int uncached_classes
Definition: work_space.h:227
#define WS_ISVID(mop)
Definition: work_space.h:288
OID * ws_identifier(MOP mop)
Definition: work_space.c:2805
struct sm_component * next
Definition: class_object.h:384
#define OID_SET_NULL(oidp)
Definition: oid.h:85
#define SM_PROPERTY_VID_KEY
void ws_decache_allxlockmops_but_norealclasses(void)
Definition: work_space.c:3426
int ignored_class_assignments
Definition: work_space.h:230
MOP tail
Definition: work_space.h:433
Definition: work_space.h:430
void ws_set_mop_fetched_with_current_snapshot(MOP mop)
Definition: work_space.c:5013
MOP ws_Commit_mops
Definition: work_space.c:70
void ws_mark_instances_deleted(MOP class_op)
Definition: work_space.c:2192
int(* MOPFILTER)(MOP op, void *args)
Definition: work_space.h:620
int db_make_sequence(DB_VALUE *value, DB_C_SET *set)
int ws_update_oid_and_class(MOP mop, OID *new_oid, OID *new_class_oid)
Definition: work_space.c:1096
void db_destroy_workspace_heap(void)
Definition: quick_fit.c:58
void ws_free_temp_mop(MOP op)
Definition: work_space.c:356
unsigned int ws_get_mvcc_snapshot_version(void)
Definition: work_space.c:4976
#define WS_SET_DIRTY(mop)
Definition: work_space.h:261
#define OBJLIST_AREA_COUNT
Definition: work_space.c:163
int er_errid(void)
int ws_Num_dirty_mop
Definition: work_space.c:107
#define ER_WS_MOP_NOT_FOUND
Definition: error_code.h:397
#define WS_ISDIRTY(mop)
Definition: work_space.h:259
int getmem(void *memptr, const tp_domain *domain, DB_VALUE *value, bool copy=true) const
WS_REPL_OBJ * tail
Definition: work_space.h:95
HL_HEAPID db_create_workspace_heap(void)
Definition: quick_fit.c:43
void db_ws_free(void *ptr)
Definition: quick_fit.c:194
bool ws_is_same_object(MOP mop1, MOP mop2)
Definition: work_space.c:5065
int ws_map_class(MOP class_op, MAPFUNC function, void *args)
Definition: work_space.c:2118
int packed_pkey_value_length
Definition: work_space.h:85
int ml_find(DB_OBJLIST *list, MOP mop)
Definition: work_space.c:4465
void ws_increment_mvcc_snapshot_version(void)
Definition: work_space.c:4987
MOP ws_new_mop(OID *oid, MOP class_mop)
Definition: work_space.c:1038
WS_VALUE_LIST * label_value_list
Definition: work_space.h:133
void classobj_area_final(void)
Definition: class_object.c:184
static WS_REPL_LIST ws_Repl_objs
Definition: work_space.c:167
AREA * area_create(const char *name, size_t element_size, size_t alloc_count)
Definition: area_alloc.c:146
DB_VALUE keys
Definition: work_space.h:49
static int ws_map_dirty_internal(MAPFUNC function, void *args, bool classes_only)
Definition: work_space.c:1717
#define COPY_OID(dest_oid_ptr, src_oid_ptr)
Definition: oid.h:63
struct tr_schema_cache * next
void ws_free_string(const char *str)
Definition: work_space.c:3480
int set_mops_allocated
Definition: work_space.h:232
int ws_init(void)
Definition: work_space.c:2299
MOP ws_mop_if_exists(OID *oid)
Definition: work_space.c:548
static MHT_TABLE * Classname_cache
Definition: work_space.c:137
MOP ws_cache_with_oid(MOBJ obj, OID *oid, MOP class_mop)
Definition: work_space.c:2675
ROOT_CLASS sm_Root_class
unsigned int mvcc_snapshot_version
Definition: work_space.h:135
static DB_OBJECT * is_class(OID *obj_oid, OID *class_oid)
Definition: compactdb.c:637
LOCK
void ws_add_classname(MOBJ classobj, MOP classmop, const char *cl_name)
Definition: work_space.c:2212
#define OID_PSEUDO_KEY(oidp)
Definition: oid.h:130
void mht_destroy(MHT_TABLE *ht)
Definition: memory_hash.c:1140
DB_DATA data
Definition: dbtype_def.h:1083
#define ER_WS_CLASS_NOT_CACHED
Definition: error_code.h:399
const char * name
Definition: dbtype_def.h:431
struct ws_value_list * next
Definition: work_space.h:102
int(* MAPFUNC)(MOP mop, void *args)
Definition: work_space.h:197
void er_set(int severity, const char *file_name, const int line_no, int err_id, int num_args,...)
bool has_index
Definition: work_space.h:86
static void remove_class_object(MOP class_mop, MOP obj)
Definition: work_space.c:1951
DB_NAMELIST * nlist_copy(DB_NAMELIST *list)
Definition: work_space.c:4362
int area_validate(AREA *area, const void *address)
Definition: area_alloc.c:487
static WS_REPL_FLUSH_ERR * ws_Repl_error_link
Definition: work_space.c:165
void ws_set_repl_error_into_error_link(LC_COPYAREA_ONEOBJ *obj, char *content_ptr)
Definition: work_space.c:4944
const char * sm_ch_name(const MOBJ clobj)
WS_MOP_TABLE_ENTRY * ws_Mop_table
Definition: work_space.c:79
#define ER_WS_GC_DIRTY_MOP
Definition: error_code.h:400
int pinned_cleanings
Definition: work_space.h:229
#define assert(x)
static int add_class_object(MOP class_mop, MOP obj)
Definition: work_space.c:1880
void ws_final(void)
Definition: work_space.c:2429
int prm_get_integer_value(PARAM_ID prm_id)
#define ER_GENERIC_ERROR
Definition: error_code.h:49
void ws_list_free(DB_LIST *list, LFREEER function)
Definition: work_space.c:3945
int sm_object_size_quick(SM_CLASS *class_, MOBJ obj)
static void ws_unlink_from_commit_mops_list(MOP op)
Definition: work_space.c:1342
int ws_pin(MOP mop, int pin)
Definition: work_space.c:2989
static AREA * Objlist_area
Definition: work_space.c:144
void(* LFREEER)(void *)
Definition: work_space.h:562
struct db_object * class_mop
Definition: work_space.h:121
void ws_free_repl_obj(WS_REPL_OBJ *obj)
Definition: work_space.c:5361
void ws_cache(MOBJ obj, MOP mop, MOP class_mop)
Definition: work_space.c:2575
void obj_free_memory(SM_CLASS *class_, MOBJ obj)
#define ER_OUT_OF_VIRTUAL_MEMORY
Definition: error_code.h:50
#define OID_ISTEMP(oidp)
Definition: oid.h:80
void classobj_free_class(SM_CLASS *class_)
int mops_allocated
Definition: work_space.h:222
struct db_object * commit_link
Definition: work_space.h:132
DB_LIST * ws_list_nconc(DB_LIST *list1, DB_LIST *list2)
Definition: work_space.c:4028
static MOP ws_make_mop(const OID *oid)
Definition: work_space.c:234
#define WS_PUT_COMMIT_MOP(mop)
Definition: work_space.h:248
DB_NAMELIST * nlist_remove(DB_NAMELIST **root, const char *name, NLSEARCHER fcn)
Definition: work_space.c:4100
#define DB_VALUE_DOMAIN_TYPE(value)
Definition: dbtype.h:70
#define WS_RESET_DIRTY(mop)
Definition: work_space.h:273
int set_mops_freed
Definition: work_space.h:233
unsigned deleted
Definition: work_space.h:145
DB_OBJECT * mq_fetch_one_real_class(DB_OBJECT *vclass_object)
static void ws_print_oid(OID *oid)
Definition: work_space.c:3501
int db_Disable_modifications
Definition: db_macro.c:90
void ws_pin_instance_and_class(MOP obj, int *opin, int *cpin)
Definition: work_space.c:3020
void ws_list_append(DB_LIST **root, DB_LIST *element)
Definition: work_space.c:3869
void ws_restore_pin(MOP obj, int opin, int cpin)
Definition: work_space.c:3052
const char * sm_get_ch_name(MOP op)
DB_OBJECT * db_get_object(const DB_VALUE *value)
unsigned int mht_valhash(const void *key, const unsigned int ht_size)
Definition: memory_hash.c:561
#define OID_EQ(oidp1, oidp2)
Definition: oid.h:92
VID_OID oid_info
Definition: work_space.h:120
MOP ws_class_mop(MOP mop)
Definition: work_space.c:2907
static void ws_insert_mop_on_hash_link_with_position(MOP mop, int slot, MOP prev)
Definition: work_space.c:517
#define WS_SET_DELETED(mop)
Definition: work_space.h:286
void * mht_get(MHT_TABLE *ht, const void *key)
Definition: memory_hash.c:1419
struct db_namelist * next
Definition: dbtype_def.h:430
#define NULL
Definition: freelistheap.h:34
OID oid
Definition: work_space.h:65
#define WS_VID_INFO(mop)
Definition: work_space.h:295
static void ws_insert_mop_on_hash_link(MOP mop, int slot)
Definition: work_space.c:415
LOCK ws_get_lock(MOP mop)
Definition: work_space.c:2942
int ws_area_init(void)
Definition: work_space.c:3823
DB_OBJLIST * ws_Resident_classes
Definition: work_space.c:97
struct pr_type * type
Definition: class_object.h:443
int ws_is_dirty(MOP mop)
Definition: work_space.c:5025
void tr_final(void)
int ml_remove(DB_OBJLIST **list, MOP mop)
Definition: work_space.c:4613
#define TM_TRAN_READ_FETCH_VERSION()
MHT_TABLE * mht_create(const char *name, int est_size, unsigned int(*hash_func)(const void *key, unsigned int ht_size), int(*cmp_func)(const void *key1, const void *key2))
Definition: memory_hash.c:894
DB_OBJECT * op
Definition: dbtype_def.h:1055
char * or_unpack_int(char *ptr, int *number)
void ws_cull_mops(void)
Definition: work_space.c:1406
LOCK lock
Definition: work_space.h:134
int ml_ext_add(DB_OBJLIST **list, MOP mop, int *added_ptr)
Definition: work_space.c:4877
static int mark_instance_deleted(MOP op, void *args)
Definition: work_space.c:2167
void area_dump(FILE *fp)
Definition: area_alloc.c:853
void ml_free(DB_OBJLIST *list)
Definition: work_space.c:4654
int db_is_system_class(MOP op)
Definition: db_info.c:502
struct db_objlist * next
Definition: dbtype_def.h:442
void ws_disconnect_deleted_instances(MOP classop)
Definition: work_space.c:1230
int count(int &result, const cub_regex_object &reg, const std::string &src, const int position, const INTL_CODESET codeset)
int pr_clear_value(DB_VALUE *value)
void ws_update_oid(MOP mop, OID *newoid)
Definition: work_space.c:1154
DB_SEQ * properties
Definition: class_object.h:457
char * or_unpack_mem_value(char *buf, DB_VALUE *value)
int nlist_append(DB_NAMELIST **list, const char *name, NLSEARCHER fcn, int *added_ptr)
Definition: work_space.c:4198
DB_NAMELIST * nlist_find(DB_NAMELIST *list, const char *name, NLSEARCHER fcn)
Definition: work_space.c:4066
int ws_class_has_cached_objects(MOP class_mop)
Definition: work_space.c:3180
static MOP Null_object
Definition: work_space.c:128
void ws_init_repl_objs(void)
Definition: work_space.c:5323
void ws_drop_classname(MOBJ classobj)
Definition: work_space.c:2247
void set_area_final(void)
Definition: set_object.c:189
int ws_Error_ignore_list[-ER_LAST_ERROR]
Definition: work_space.c:160
int ws_mop_compare(MOP mop1, MOP mop2)
Definition: work_space.c:3144
int locator_flush_instance(MOP mop)
Definition: locator_cl.c:5236
struct db_object * op
Definition: dbtype_def.h:443
int obt_area_init(void)
int set_put_element(DB_COLLECTION *set, int index, DB_VALUE *value)
Definition: set_object.c:2719
static void error(const char *msg)
Definition: gencat.c:331
void ws_set_lock(MOP mop, LOCK lock)
Definition: work_space.c:2954
unsigned int flags
Definition: class_object.h:459
void ws_clear_all_hints(bool retain_lock)
Definition: work_space.c:3305
int ml_add(DB_OBJLIST **list, MOP mop, int *added_ptr)
Definition: work_space.c:4493
bool vid_is_updatable(MOP mop)
unsigned int flags
Definition: work_space.h:48
static void ws_examine_no_mop_has_cached_lock(void)
Definition: work_space.c:5271
unsigned dirty
Definition: work_space.h:144
#define ER_WS_CHANGING_OBJECT_CLASS
Definition: error_code.h:401
DB_OBJLIST * ml_copy(DB_OBJLIST *list)
Definition: work_space.c:4671
static unsigned int ws_MVCC_snapshot_version
Definition: work_space.c:152
#define ARG_FILE_LINE
Definition: error_manager.h:44
MOP ws_make_temp_mop(void)
Definition: work_space.c:336
void er_print_callstack(const char *file_name, const int line_no, const char *fmt,...)
int pr_clone_value(const DB_VALUE *src, DB_VALUE *dest)
void ws_decache(MOP mop)
Definition: work_space.c:2701
static void ws_free_mop(MOP op)
Definition: work_space.c:296
#define WS_OID(mop)
Definition: work_space.h:293
int ws_list_remove(DB_LIST **root, DB_LIST *element)
Definition: work_space.c:3891
unsigned char composition_fetch
Definition: work_space.h:139
int nlist_add(DB_NAMELIST **list, const char *name, NLSEARCHER fcn, int *added_ptr)
Definition: work_space.c:4147
#define ER_WS_CANT_INSTALL_NULL_OID
Definition: error_code.h:402
WS_REPL_FLUSH_ERR * ws_get_repl_error_from_error_link(void)
Definition: work_space.c:5075
void * object
Definition: work_space.h:123
#define free_and_init(ptr)
Definition: memory_alloc.h:147
#define strlen(s1)
Definition: intl_support.c:43
unsigned char pruning_type
Definition: work_space.h:138
bool sm_is_reuse_oid_class(MOP op)
int oid_compare(const void *a, const void *b)
Definition: oid.c:243
SM_COMPONENT header
Definition: class_object.h:441
struct db_object * hash_link
Definition: work_space.h:129
static bool Ws_dirty
Definition: work_space.c:116
int nlist_find_or_append(DB_NAMELIST **list, const char *name, NLSEARCHER fcn, int *position)
int ws_map_dirty(MAPFUNC function, void *args)
Definition: work_space.c:1849
DB_C_INT(* NLSEARCHER)(const void *, const void *)
Definition: work_space.h:599
void ws_clean_label_value_list(MOP mop)
Definition: work_space.c:5301
void ml_ext_free(DB_OBJLIST *list)
Definition: work_space.c:4806
void * area_alloc(AREA *area)
Definition: area_alloc.c:360
bool prm_get_bool_value(PARAM_ID prm_id)
void ws_intern_instances(MOP class_mop)
Definition: work_space.c:1548
OID * ws_identifier_with_check(MOP mop, const bool check_non_referable)
Definition: work_space.c:2820
MOP ws_find_class(const char *name)
Definition: work_space.c:2278
struct db_object * class_link
Definition: work_space.h:125
void ws_decache_all_instances(MOP mop)
Definition: work_space.c:2765
void * db_ws_alloc(size_t size)
Definition: quick_fit.c:73
#define IS_WRITE_EXCLUSIVE_LOCK(lock)
int area_free(AREA *area, void *ptr)
Definition: area_alloc.c:514
#define DB_VALUE_TYPE(value)
Definition: dbtype.h:72
void ws_remove_resident_class(MOP classop)
Definition: work_space.c:1277
int i
Definition: dynamic_load.c:954
#define DECACHE
Definition: locator_cl.h:48
int locator_flush_all_instances(MOP class_mop, bool decache)
Definition: locator_cl.c:5278
int db_make_null(DB_VALUE *value)
struct db_object * MOP
Definition: dbtype_def.h:409
DB_VALUE * ws_keys(MOP vid, unsigned int *flags)
Definition: work_space.c:726
DB_TYPE id
MOP vid_base_instance(MOP mop)
void ws_unhide_new_old_trigger_obj(MOP op)
Definition: work_space.c:3800
int ws_hide_new_old_trigger_obj(MOP op)
Definition: work_space.c:3765
unsigned is_temp
Definition: work_space.h:149
static void emergency_remove_dirty(MOP op)
Definition: work_space.c:1301
struct ws_repl_flush_err * error_link
Definition: work_space.h:71
MOP head
Definition: work_space.h:432
void ws_clear(void)
Definition: work_space.c:2526
int ws_list_length(DB_LIST *list)
Definition: work_space.c:3925
MOP sm_Root_class_mop
void ws_filter_dirty(void)
Definition: work_space.c:1859
DB_VALUE pkey_value
Definition: work_space.h:76
int db_value_clear(DB_VALUE *value)
Definition: db_macro.c:1588
void pr_area_final(void)
void ws_abort_mops(bool only_unpinned)
Definition: work_space.c:3353
#define OID_ISNULL(oidp)
Definition: oid.h:81
int locator_is_class(MOP mop, DB_FETCH_MODE hint_purpose)
Definition: locator_cl.c:239
unsigned int db_on_server
int operation
Definition: work_space.h:87
int ml_size(DB_OBJLIST *list)
Definition: work_space.c:4711
struct db_objlist DB_OBJLIST
Definition: dbtype_def.h:437
char * ws_copy_string(const char *str)
Definition: work_space.c:3457
void ws_release_instance(MOP mop)
Definition: work_space.c:1565
DB_LIST * ws_list_copy(DB_LIST *src, LCOPIER copier, LFREEER freeer)
Definition: work_space.c:3985
bool ws_is_mop_fetched_with_current_snapshot(MOP mop)
Definition: work_space.c:5000
unsigned pinned
Definition: work_space.h:147
int ws_add_label_value_to_mop(MOP mop, DB_VALUE *val)
Definition: work_space.c:5237
#define WS_SET_LOCK(mop, lock)
Definition: work_space.h:297
int ws_find(MOP mop, MOBJ *obj)
Definition: work_space.c:3112
void ws_remove_label_value_from_mop(MOP mop, DB_VALUE *val)
Definition: work_space.c:5182
static int ws_check_hash_link(int slot)
Definition: work_space.c:366
void ws_abort_transaction(void)
Definition: work_space.c:201
struct db_list * next
Definition: dbtype_def.h:417
struct ws_repl_obj * next
Definition: work_space.h:82
int mht_compare_strings_are_equal(const void *key1, const void *key2)
Definition: memory_hash.c:767
int ws_set_ignore_error_list_for_mflush(int error_count, int *error_list)
Definition: work_space.c:4927
int ws_list_total(DB_LIST *list, LTOTALER function)
Definition: work_space.c:3964
int(* LTOTALER)(void *)
Definition: work_space.h:563
bool ws_need_flush(void)
Definition: work_space.c:3813
int ws_has_dirty_objects(MOP op, int *isvirt)
Definition: work_space.c:3752
#define ER_LAST_ERROR
Definition: error_code.h:1646
LC_COPYAREA_OPERATION operation
Definition: locator.h:219
unsigned int ws_Mop_table_size
Definition: work_space.c:86
#define ER_WS_CORRUPTED
Definition: error_code.h:396
bool ws_rehash_vmop(MOP mop, MOBJ classobj, DB_VALUE *newkey)
Definition: work_space.c:899
int au_fetch_class_force(MOP op, SM_CLASS **class_, AU_FETCHMODE fetchmode)
const char ** p
Definition: dynamic_load.c:945
DB_CONST_C_CHAR db_get_string(const DB_VALUE *value)
void ml_ext_free_link(DB_OBJLIST *link)
Definition: work_space.c:4790
int db_value_domain_init(DB_VALUE *value, const DB_TYPE type, const int precision, const int scale)
Definition: db_macro.c:153
unsigned is_vid
Definition: work_space.h:148
void ws_free_repl_flush_error(WS_REPL_FLUSH_ERR *flush_err)
Definition: work_space.c:5115