sql_list.h 15.5 KB
Newer Older
1 2
#ifndef INCLUDES_MYSQL_SQL_LIST_H
#define INCLUDES_MYSQL_SQL_LIST_H
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3
/* Copyright (C) 2000-2003 MySQL AB
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4

bk@work.mysql.com's avatar
bk@work.mysql.com committed
5 6
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
7
   the Free Software Foundation; version 2 of the License.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
8

bk@work.mysql.com's avatar
bk@work.mysql.com committed
9 10 11 12
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
13

bk@work.mysql.com's avatar
bk@work.mysql.com committed
14 15 16 17 18
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */


19
#ifdef USE_PRAGMA_INTERFACE
bk@work.mysql.com's avatar
bk@work.mysql.com committed
20 21 22
#pragma interface			/* gcc class implementation */
#endif

23
/* mysql standard class memory allocator */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
24 25 26 27

class Sql_alloc
{
public:
28
  static void *operator new(size_t size) throw ()
29
  {
30
    return sql_alloc(size);
31
  }
32
  static void *operator new[](size_t size) throw ()
33
  {
34
    return sql_alloc(size);
35
  }
36
  static void *operator new[](size_t size, MEM_ROOT *mem_root) throw ()
37
  { return alloc_root(mem_root, size); }
38
  static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
39
  { return alloc_root(mem_root, size); }
40
  static void operator delete(void *ptr, size_t size) { TRASH(ptr, size); }
41 42
  static void operator delete(void *ptr, MEM_ROOT *mem_root)
  { /* never called */ }
43 44
  static void operator delete[](void *ptr, MEM_ROOT *mem_root)
  { /* never called */ }
45
  static void operator delete[](void *ptr, size_t size) { TRASH(ptr, size); }
46 47 48 49 50 51 52 53 54
#ifdef HAVE_purify
  bool dummy;
  inline Sql_alloc() :dummy(0) {}
  inline ~Sql_alloc() {}
#else
  inline Sql_alloc() {}
  inline ~Sql_alloc() {}
#endif

bk@work.mysql.com's avatar
bk@work.mysql.com committed
55 56
};

57

bk@work.mysql.com's avatar
bk@work.mysql.com committed
58
/*
59 60 61 62 63 64 65
  Basic single linked list
  Used for item and item_buffs.
  All list ends with a pointer to the 'end_of_list' element, which
  data pointer is a null pointer and the next pointer points to itself.
  This makes it very fast to traverse lists as we don't have to
  test for a specialend condition for list that can't contain a null
  pointer.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
66 67
*/

68 69 70 71 72 73 74

/**
  list_node - a node of a single-linked list.
  @note We never call a destructor for instances of this class.
*/

struct list_node :public Sql_alloc
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
75 76 77 78 79
{
  list_node *next;
  void *info;
  list_node(void *info_par,list_node *next_par)
    :next(next_par),info(info_par)
80
  {}
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
81
  list_node()					/* For end_of_list */
82 83 84 85
  {
    info= 0;
    next= this;
  }
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
86 87
};

88

monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
89 90
extern list_node end_of_list;

91 92
class base_list :public Sql_alloc
{
bk@work.mysql.com's avatar
bk@work.mysql.com committed
93 94 95 96 97 98
protected:
  list_node *first,**last;

public:
  uint elements;

monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
99
  inline void empty() { elements=0; first= &end_of_list; last=&first;}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
100
  inline base_list() { empty(); }
101 102 103 104 105 106 107 108 109
  /**
    This is a shallow copy constructor that implicitly passes the ownership
    from the source list to the new instance. The old instance is not
    updated, so both objects end up sharing the same nodes. If one of
    the instances then adds or removes a node, the other becomes out of
    sync ('last' pointer), while still operational. Some old code uses and
    relies on this behaviour. This logic is quite tricky: please do not use
    it in any new code.
  */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
110 111
  inline base_list(const base_list &tmp) :Sql_alloc()
  {
112 113 114
    elements= tmp.elements;
    first= tmp.first;
    last= elements ? tmp.last : &first;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
115
  }
116 117 118 119 120 121 122
  /**
    Construct a deep copy of the argument in memory root mem_root.
    The elements themselves are copied by pointer. If you also
    need to copy elements by value, you should employ
    list_copy_and_replace_each_value after creating a copy.
  */
  base_list(const base_list &rhs, MEM_ROOT *mem_root);
venu@myvenu.com's avatar
venu@myvenu.com committed
123
  inline base_list(bool error) { }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
124 125
  inline bool push_back(void *info)
  {
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
126
    if (((*last)=new list_node(info, &end_of_list)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
127 128 129 130 131 132 133
    {
      last= &(*last)->next;
      elements++;
      return 0;
    }
    return 1;
  }
monty@mysql.com's avatar
monty@mysql.com committed
134 135 136 137 138 139 140 141 142 143
  inline bool push_back(void *info, MEM_ROOT *mem_root)
  {
    if (((*last)=new (mem_root) list_node(info, &end_of_list)))
    {
      last= &(*last)->next;
      elements++;
      return 0;
    }
    return 1;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
144 145 146 147 148
  inline bool push_front(void *info)
  {
    list_node *node=new list_node(info,first);
    if (node)
    {
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
149
      if (last == &first)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
150 151 152 153 154 155 156 157 158 159 160 161
	last= &node->next;
      first=node;
      elements++;
      return 0;
    }
    return 1;
  }
  void remove(list_node **prev)
  {
    list_node *node=(*prev)->next;
    if (!--elements)
      last= &first;
162 163 164 165
    else if (last == &(*prev)->next)
      last= prev;
    delete *prev;
    *prev=node;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
166
  }
167 168 169 170 171 172 173 174 175
  inline void concat(base_list *list)
  {
    if (!list->is_empty())
    {
      *last= list->first;
      last= list->last;
      elements+= list->elements;
    }
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
176 177
  inline void *pop(void)
  {
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
178
    if (first == &end_of_list) return 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
179 180 181 182 183 184
    list_node *tmp=first;
    first=first->next;
    if (!--elements)
      last= &first;
    return tmp->info;
  }
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
  inline void disjoin(base_list *list)
  {
    list_node **prev= &first;
    list_node *node= first;
    list_node *list_first= list->first;
    elements=0;
    while (node && node != list_first)
    {
      prev= &node->next;
      node= node->next;
      elements++;
    }
    *prev= *last;
    last= prev;
  }
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
200 201 202 203 204 205 206 207
  inline void prepand(base_list *list)
  {
    if (!list->is_empty())
    {
      *list->last= first;
      first= list->first;
      elements+= list->elements;
    }
208
  }
209 210 211 212 213 214 215 216 217
  /**
    Swap two lists.
  */
  inline void swap(base_list &rhs)
  {
    swap_variables(list_node *, first, rhs.first);
    swap_variables(list_node **, last, rhs.last);
    swap_variables(uint, elements, rhs.elements);
  }
218 219
  inline list_node* last_node() { return *last; }
  inline list_node* first_node() { return first;}
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
220 221 222 223
  inline void *head() { return first->info; }
  inline void **head_ref() { return first != &end_of_list ? &first->info : 0; }
  inline bool is_empty() { return first == &end_of_list ; }
  inline list_node *last_ref() { return &end_of_list; }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
224
  friend class base_list_iterator;
225
  friend class error_list;
venu@myvenu.com's avatar
venu@myvenu.com committed
226
  friend class error_list_iterator;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
227

228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
#ifdef LIST_EXTRA_DEBUG
  /*
    Check list invariants and print results into trace. Invariants are:
      - (*last) points to end_of_list
      - There are no NULLs in the list.
      - base_list::elements is the number of elements in the list.

    SYNOPSIS
      check_list()
        name  Name to print to trace file

    RETURN 
      1  The list is Ok.
      0  List invariants are not met.
  */

  bool check_list(const char *name)
  {
    base_list *list= this;
    list_node *node= first;
    uint cnt= 0;

    while (node->next != &end_of_list)
    {
      if (!node->info)
      {
        DBUG_PRINT("list_invariants",("%s: error: NULL element in the list", 
                                      name));
        return FALSE;
      }
      node= node->next;
      cnt++;
    }
    if (last != &(node->next))
    {
      DBUG_PRINT("list_invariants", ("%s: error: wrong last pointer", name));
      return FALSE;
    }
    if (cnt+1 != elements)
    {
      DBUG_PRINT("list_invariants", ("%s: error: wrong element count", name));
      return FALSE;
    }
    DBUG_PRINT("list_invariants", ("%s: list is ok", name));
    return TRUE;
  }
#endif // LIST_EXTRA_DEBUG

bk@work.mysql.com's avatar
bk@work.mysql.com committed
276 277 278 279 280 281 282 283 284 285 286 287 288 289
protected:
  void after(void *info,list_node *node)
  {
    list_node *new_node=new list_node(info,node->next);
    node->next=new_node;
    elements++;
    if (last == &(node->next))
      last= &new_node->next;
  }
};


class base_list_iterator
{
290
protected:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
291
  base_list *list;
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
292
  list_node **el,**prev,*current;
293 294 295 296 297 298
  void sublist(base_list &ls, uint elm)
  {
    ls.first= *el;
    ls.last= list->last;
    ls.elements= elm;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
299
public:
300 301
  base_list_iterator() 
    :list(0), el(0), prev(0), current(0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
302
  {}
303

304 305 306 307 308 309 310 311 312 313 314
  base_list_iterator(base_list &list_par) 
  { init(list_par); }

  inline void init(base_list &list_par)
  {
    list= &list_par;
    el= &list_par.first;
    prev= 0;
    current= 0;
  }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
315 316 317
  inline void *next(void)
  {
    prev=el;
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
318
    current= *el;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
319 320 321
    el= &current->next;
    return current->info;
  }
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
322 323 324 325 326 327 328
  inline void *next_fast(void)
  {
    list_node *tmp;
    tmp= *el;
    el= &tmp->next;
    return tmp->info;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
329 330 331 332
  inline void rewind(void)
  {
    el= &list->first;
  }
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
333
  inline void *replace(void *element)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
334 335
  {						// Return old element
    void *tmp=current->info;
336
    DBUG_ASSERT(current->info != 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
337 338 339 340 341 342
    current->info=element;
    return tmp;
  }
  void *replace(base_list &new_list)
  {
    void *ret_value=current->info;
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
343
    if (!new_list.is_empty())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
344 345 346 347
    {
      *new_list.last=current->next;
      current->info=new_list.first->info;
      current->next=new_list.first->next;
hf@deer.mysql.r18.ru's avatar
hf@deer.mysql.r18.ru committed
348 349
      if ((list->last == &current->next) && (new_list.elements > 1))
	list->last= new_list.last;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
      list->elements+=new_list.elements-1;
    }
    return ret_value;				// return old element
  }
  inline void remove(void)			// Remove current
  {
    list->remove(prev);
    el=prev;
    current=0;					// Safeguard
  }
  void after(void *element)			// Insert element after current
  {
    list->after(element,current);
    current=current->next;
    el= &current->next;
  }
  inline void **ref(void)			// Get reference pointer
  {
    return &current->info;
  }
  inline bool is_last(void)
  {
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
372
    return el == &list->last_ref()->next;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
373
  }
374
  friend class error_list_iterator;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
375 376 377 378 379 380 381
};

template <class T> class List :public base_list
{
public:
  inline List() :base_list() {}
  inline List(const List<T> &tmp) :base_list(tmp) {}
382 383
  inline List(const List<T> &tmp, MEM_ROOT *mem_root) :
    base_list(tmp, mem_root) {}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
384
  inline bool push_back(T *a) { return base_list::push_back(a); }
monty@mysql.com's avatar
monty@mysql.com committed
385 386
  inline bool push_back(T *a, MEM_ROOT *mem_root)
  { return base_list::push_back(a, mem_root); }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
387 388 389 390
  inline bool push_front(T *a) { return base_list::push_front(a); }
  inline T* head() {return (T*) base_list::head(); }
  inline T** head_ref() {return (T**) base_list::head_ref(); }
  inline T* pop()  {return (T*) base_list::pop(); }
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
391 392
  inline void concat(List<T> *list) { base_list::concat(list); }
  inline void disjoin(List<T> *list) { base_list::disjoin(list); }
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
393
  inline void prepand(List<T> *list) { base_list::prepand(list); }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
394 395 396
  void delete_elements(void)
  {
    list_node *element,*next;
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
397
    for (element=first; element != &end_of_list; element=next)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
398 399 400 401 402 403 404 405 406 407 408 409 410
    {
      next=element->next;
      delete (T*) element->info;
    }
    empty();
  }
};


template <class T> class List_iterator :public base_list_iterator
{
public:
  List_iterator(List<T> &a) : base_list_iterator(a) {}
411 412
  List_iterator() : base_list_iterator() {}
  inline void init(List<T> &a) { base_list_iterator::init(a); }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
413 414 415
  inline T* operator++(int) { return (T*) base_list_iterator::next(); }
  inline T *replace(T *a)   { return (T*) base_list_iterator::replace(a); }
  inline T *replace(List<T> &a) { return (T*) base_list_iterator::replace(a); }
416 417
  inline void rewind(void)  { base_list_iterator::rewind(); }
  inline void remove()      { base_list_iterator::remove(); }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
418 419
  inline void after(T *a)   { base_list_iterator::after(a); }
  inline T** ref(void)	    { return (T**) base_list_iterator::ref(); }
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
420 421
};

422

monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
423 424 425 426 427 428 429 430 431 432
template <class T> class List_iterator_fast :public base_list_iterator
{
protected:
  inline T *replace(T *a)   { return (T*) 0; }
  inline T *replace(List<T> &a) { return (T*) 0; }
  inline void remove(void)  { }
  inline void after(T *a)   { }
  inline T** ref(void)	    { return (T**) 0; }

public:
serg@serg.mylan's avatar
serg@serg.mylan committed
433
  inline List_iterator_fast(List<T> &a) : base_list_iterator(a) {}
434 435
  inline List_iterator_fast() : base_list_iterator() {}
  inline void init(List<T> &a) { base_list_iterator::init(a); }
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
436 437
  inline T* operator++(int) { return (T*) base_list_iterator::next_fast(); }
  inline void rewind(void)  { base_list_iterator::rewind(); }
438
  void sublist(List<T> &list_arg, uint el_arg)
439
  {
440
    base_list_iterator::sublist(list_arg, el_arg);
441
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
442 443 444 445
};


/*
446 447
  A simple intrusive list which automaticly removes element from list
  on delete (for THD element)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
448 449
*/

450 451
struct ilink
{
bk@work.mysql.com's avatar
bk@work.mysql.com committed
452
  struct ilink **prev,*next;
453
  static void *operator new(size_t size) throw ()
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
454 455 456 457 458
  {
    return (void*)my_malloc((uint)size, MYF(MY_WME | MY_FAE));
  }
  static void operator delete(void* ptr_arg, size_t size)
  {
459
     my_free((uchar*)ptr_arg, MYF(MY_WME|MY_ALLOW_ZERO_PTR));
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
460 461
  }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
462 463 464 465 466 467 468 469 470 471 472 473 474 475
  inline ilink()
  {
    prev=0; next=0;
  }
  inline void unlink()
  {
    /* Extra tests because element doesn't have to be linked */
    if (prev) *prev= next;
    if (next) next->prev=prev;
    prev=0 ; next=0;
  }
  virtual ~ilink() { unlink(); }		/*lint -e1740 */
};

476

477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
/* Needed to be able to have an I_List of char* strings in mysqld.cc. */

class i_string: public ilink
{
public:
  const char* ptr;
  i_string():ptr(0) { }
  i_string(const char* s) : ptr(s) {}
};

/* needed for linked list of two strings for replicate-rewrite-db */
class i_string_pair: public ilink
{
public:
  const char* key;
  const char* val;
  i_string_pair():key(0),val(0) { }
  i_string_pair(const char* key_arg, const char* val_arg) : 
    key(key_arg),val(val_arg) {}
};


bk@work.mysql.com's avatar
bk@work.mysql.com committed
499 500
template <class T> class I_List_iterator;

501 502 503 504 505
/*
  WARNING: copy constructor of this class does not create a usable
  copy, as its members may point at each other.
*/

506 507
class base_ilist
{
508
public:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
509
  struct ilink *first,last;
510 511
  inline void empty() { first= &last; last.prev= &first; }
  base_ilist() { empty(); }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
  inline bool is_empty() {  return first == &last; }
  inline void append(ilink *a)
  {
    first->prev= &a->next;
    a->next=first; a->prev= &first; first=a;
  }
  inline void push_back(ilink *a)
  {
    *last.prev= a;
    a->next= &last;
    a->prev= last.prev;
    last.prev= &a->next;
  }
  inline struct ilink *get()
  {
    struct ilink *first_link=first;
    if (first_link == &last)
      return 0;
    first_link->unlink();			// Unlink from list
    return first_link;
  }
533 534 535 536
  inline struct ilink *head()
  {
    return (first != &last) ? first : 0;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
  friend class base_list_iterator;
};


class base_ilist_iterator
{
  base_ilist *list;
  struct ilink **el,*current;
public:
  base_ilist_iterator(base_ilist &list_par) :list(&list_par),
    el(&list_par.first),current(0) {}
  void *next(void)
  {
    /* This is coded to allow push_back() while iterating */
    current= *el;
    if (current == &list->last) return 0;
    el= &current->next;
    return current;
  }
};


template <class T>
560 561
class I_List :private base_ilist
{
bk@work.mysql.com's avatar
bk@work.mysql.com committed
562 563
public:
  I_List() :base_ilist()	{}
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
564
  inline void empty()		{ base_ilist::empty(); }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
565 566 567 568
  inline bool is_empty()        { return base_ilist::is_empty(); } 
  inline void append(T* a)	{ base_ilist::append(a); }
  inline void push_back(T* a)	{ base_ilist::push_back(a); }
  inline T* get()		{ return (T*) base_ilist::get(); }
569
  inline T* head()		{ return (T*) base_ilist::head(); }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
570 571 572 573 574 575 576 577 578 579 580 581
#ifndef _lint
  friend class I_List_iterator<T>;
#endif
};


template <class T> class I_List_iterator :public base_ilist_iterator
{
public:
  I_List_iterator(I_List<T> &a) : base_ilist_iterator(a) {}
  inline T* operator++(int) { return (T*) base_ilist_iterator::next(); }
};
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610

/**
  Make a deep copy of each list element.

  @note A template function and not a template method of class List
  is employed because of explicit template instantiation:
  in server code there are explicit instantiations of List<T> and
  an explicit instantiation of a template requires that any method
  of the instantiated class used in the template can be resolved.
  Evidently not all template arguments have clone() method with
  the right signature.

  @return You must query the error state in THD for out-of-memory
  situation after calling this function.
*/

template <typename T>
inline
void
list_copy_and_replace_each_value(List<T> &list, MEM_ROOT *mem_root)
{
  /* Make a deep copy of each element */
  List_iterator<T> it(list);
  T *el;
  while ((el= it++))
    it.replace(el->clone(mem_root));
}

#endif // INCLUDES_MYSQL_SQL_LIST_H