]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ndb/tools.cpp
8dc22c29bcb9564f919aed20c3d6b196c65aa647
[openldap] / servers / slapd / back-ndb / tools.cpp
1 /* tools.cpp - tools for slap tools */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2008-2017 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* ACKNOWLEDGEMENTS:
17  * This work was initially developed by Howard Chu for inclusion
18  * in OpenLDAP Software. This work was sponsored by MySQL.
19  */
20
21 #include "portable.h"
22
23 #include <stdio.h>
24 #include <ac/string.h>
25 #include <ac/errno.h>
26
27 #include "lutil.h"
28
29 #include "back-ndb.h"
30
31 typedef struct dn_id {
32         ID id;
33         struct berval dn;
34 } dn_id;
35
36 #define HOLE_SIZE       4096
37 static dn_id hbuf[HOLE_SIZE], *holes = hbuf;
38 static unsigned nhmax = HOLE_SIZE;
39 static unsigned nholes;
40 static Avlnode *myParents;
41
42 static Ndb *myNdb;
43 static NdbTransaction *myScanTxn;
44 static NdbIndexScanOperation *myScanOp;
45
46 static NdbRecAttr *myScanID, *myScanOC;
47 static NdbRecAttr *myScanDN[NDB_MAX_RDNS];
48 static char myDNbuf[2048];
49 static char myIdbuf[2*sizeof(ID)];
50 static char myOcbuf[NDB_OC_BUFLEN];
51 static NdbRdns myRdns;
52
53 static NdbTransaction *myPutTxn;
54 static int myPutCnt;
55
56 static struct berval *myOcList;
57 static struct berval myDn;
58
59 extern "C"
60 int ndb_tool_entry_open(
61         BackendDB *be, int mode )
62 {
63         struct ndb_info *ni = (struct ndb_info *) be->be_private;
64
65         myNdb = new Ndb( ni->ni_cluster[0], ni->ni_dbname );
66         return myNdb->init(1024);
67 }
68
69 extern "C"
70 int ndb_tool_entry_close(
71         BackendDB *be )
72 {
73         if ( myPutTxn ) {
74                 int rc = myPutTxn->execute(NdbTransaction::Commit);
75                 if( rc != 0 ) {
76                         char text[1024];
77                         snprintf( text, sizeof(text),
78                                         "txn_commit failed: %s (%d)",
79                                         myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
80                         Debug( LDAP_DEBUG_ANY,
81                                 "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
82                                 text, 0, 0 );
83                 }
84                 myPutTxn->close();
85                 myPutTxn = NULL;
86         }
87         myPutCnt = 0;
88
89         if( nholes ) {
90                 unsigned i;
91                 fprintf( stderr, "Error, entries missing!\n");
92                 for (i=0; i<nholes; i++) {
93                         fprintf(stderr, "  entry %ld: %s\n",
94                                 holes[i].id, holes[i].dn.bv_val);
95                 }
96                 return -1;
97         }
98
99         return 0;
100 }
101
102 extern "C"
103 ID ndb_tool_entry_next(
104         BackendDB *be )
105 {
106         struct ndb_info *ni = (struct ndb_info *) be->be_private;
107         char *ptr;
108         ID id;
109         int i;
110
111         assert( be != NULL );
112         assert( slapMode & SLAP_TOOL_MODE );
113
114         if ( myScanOp->nextResult() ) {
115                 myScanOp->close();
116                 myScanOp = NULL;
117                 myScanTxn->close();
118                 myScanTxn = NULL;
119                 return NOID;
120         }
121         id = myScanID->u_64_value();
122
123         if ( myOcList ) {
124                 ber_bvarray_free( myOcList );
125         }
126         myOcList = ndb_ref2oclist( myOcbuf, NULL );
127         for ( i=0; i<NDB_MAX_RDNS; i++ ) {
128                 if ( myScanDN[i]->isNULL() || !myRdns.nr_buf[i][0] )
129                         break;
130         }
131         myRdns.nr_num = i;
132         ptr = myDNbuf;
133         for ( --i; i>=0; i-- ) {
134                 char *buf;
135                 int len;
136                 buf = myRdns.nr_buf[i];
137                 len = *buf++;
138                 ptr = lutil_strncopy( ptr, buf, len );
139                 if ( i )
140                         *ptr++ = ',';
141         }
142         *ptr = '\0';
143         myDn.bv_val = myDNbuf;
144         myDn.bv_len = ptr - myDNbuf;
145
146         return id;
147 }
148
149 extern "C"
150 ID ndb_tool_entry_first(
151         BackendDB *be )
152 {
153         struct ndb_info *ni = (struct ndb_info *) be->be_private;
154         int i;
155
156         myScanTxn = myNdb->startTransaction();
157         if ( !myScanTxn )
158                 return NOID;
159
160         myScanOp = myScanTxn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
161         if ( !myScanOp )
162                 return NOID;
163
164         if ( myScanOp->readTuples( NdbOperation::LM_CommittedRead, NdbScanOperation::SF_KeyInfo ))
165                 return NOID;
166
167         myScanID = myScanOp->getValue( EID_COLUMN, myIdbuf );
168         myScanOC = myScanOp->getValue( OCS_COLUMN, myOcbuf );
169         for ( i=0; i<NDB_MAX_RDNS; i++ ) {
170                 myScanDN[i] = myScanOp->getValue( i+RDN_COLUMN, myRdns.nr_buf[i] );
171         }
172         if ( myScanTxn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 ))
173                 return NOID;
174
175         return ndb_tool_entry_next( be );
176 }
177
178 extern "C"
179 ID ndb_tool_dn2id_get(
180         Backend *be,
181         struct berval *dn
182 )
183 {
184         struct ndb_info *ni = (struct ndb_info *) be->be_private;
185         NdbArgs NA;
186         NdbRdns rdns;
187         Entry e;
188         char text[1024];
189         Operation op = {0};
190         Opheader ohdr = {0};
191         int rc;
192
193         if ( BER_BVISEMPTY(dn) )
194                 return 0;
195
196         NA.ndb = myNdb;
197         NA.txn = myNdb->startTransaction();
198         if ( !NA.txn ) {
199                 snprintf( text, sizeof(text),
200                         "startTransaction failed: %s (%d)",
201                         myNdb->getNdbError().message, myNdb->getNdbError().code );
202                 Debug( LDAP_DEBUG_ANY,
203                         "=> " LDAP_XSTRING(ndb_tool_dn2id_get) ": %s\n",
204                          text, 0, 0 );
205                 return NOID;
206         }
207         if ( myOcList ) {
208                 ber_bvarray_free( myOcList );
209                 myOcList = NULL;
210         }
211         op.o_hdr = &ohdr;
212         op.o_bd = be;
213         op.o_tmpmemctx = NULL;
214         op.o_tmpmfuncs = &ch_mfuncs;
215
216         NA.e = &e;
217         e.e_name = *dn;
218         NA.rdns = &rdns;
219         NA.ocs = NULL;
220         rc = ndb_entry_get_info( &op, &NA, 0, NULL );
221         myOcList = NA.ocs;
222         NA.txn->close();
223         if ( rc )
224                 return NOID;
225         
226         myDn = *dn;
227
228         return e.e_id;
229 }
230
231 extern "C"
232 Entry* ndb_tool_entry_get( BackendDB *be, ID id )
233 {
234         NdbArgs NA;
235         int rc;
236         char text[1024];
237         Operation op = {0};
238         Opheader ohdr = {0};
239
240         assert( be != NULL );
241         assert( slapMode & SLAP_TOOL_MODE );
242
243         NA.txn = myNdb->startTransaction();
244         if ( !NA.txn ) {
245                 snprintf( text, sizeof(text),
246                         "start_transaction failed: %s (%d)",
247                         myNdb->getNdbError().message, myNdb->getNdbError().code );
248                 Debug( LDAP_DEBUG_ANY,
249                         "=> " LDAP_XSTRING(ndb_tool_entry_get) ": %s\n",
250                          text, 0, 0 );
251                 return NULL;
252         }
253
254         NA.e = entry_alloc();
255         NA.e->e_id = id;
256         ber_dupbv( &NA.e->e_name, &myDn );
257         dnNormalize( 0, NULL, NULL, &NA.e->e_name, &NA.e->e_nname, NULL );
258
259         op.o_hdr = &ohdr;
260         op.o_bd = be;
261         op.o_tmpmemctx = NULL;
262         op.o_tmpmfuncs = &ch_mfuncs;
263
264         NA.ndb = myNdb;
265         NA.ocs = myOcList;
266         rc = ndb_entry_get_data( &op, &NA, 0 );
267
268         if ( rc ) {
269                 entry_free( NA.e );
270                 NA.e = NULL;
271         }
272         NA.txn->close();
273
274         return NA.e;
275 }
276
277 static struct berval glueval[] = {
278         BER_BVC("glue"),
279         BER_BVNULL
280 };
281
282 static int ndb_dnid_cmp( const void *v1, const void *v2 )
283 {
284         struct dn_id *dn1 = (struct dn_id *)v1,
285                 *dn2 = (struct dn_id *)v2;
286         return ber_bvcmp( &dn1->dn, &dn2->dn );
287 }
288
289 static int ndb_tool_next_id(
290         Operation *op,
291         NdbArgs *NA,
292         struct berval *text,
293         int hole )
294 {
295         struct berval ndn = NA->e->e_nname;
296         int rc;
297
298         if (ndn.bv_len == 0) {
299                 NA->e->e_id = 0;
300                 return 0;
301         }
302
303         rc = ndb_entry_get_info( op, NA, 0, NULL );
304         if ( rc ) {
305                 Attribute *a, tmp = {0};
306                 if ( !be_issuffix( op->o_bd, &ndn ) ) {
307                         struct dn_id *dptr;
308                         struct berval npdn;
309                         dnParent( &ndn, &npdn );
310                         NA->e->e_nname = npdn;
311                         NA->rdns->nr_num--;
312                         rc = ndb_tool_next_id( op, NA, text, 1 );
313                         NA->e->e_nname = ndn;
314                         NA->rdns->nr_num++;
315                         if ( rc ) {
316                                 return rc;
317                         }
318                         /* If parent didn't exist, it was created just now
319                          * and its ID is now in e->e_id.
320                          */
321                         dptr = (struct dn_id *)ch_malloc( sizeof( struct dn_id ) + npdn.bv_len + 1);
322                         dptr->id = NA->e->e_id;
323                         dptr->dn.bv_val = (char *)(dptr+1);
324                         strcpy(dptr->dn.bv_val, npdn.bv_val );
325                         dptr->dn.bv_len = npdn.bv_len;
326                         if ( avl_insert( &myParents, dptr, ndb_dnid_cmp, avl_dup_error )) {
327                                 ch_free( dptr );
328                         }
329                 }
330                 rc = ndb_next_id( op->o_bd, myNdb, &NA->e->e_id );
331                 if ( rc ) {
332                         snprintf( text->bv_val, text->bv_len,
333                                 "next_id failed: %s (%d)",
334                                 myNdb->getNdbError().message, myNdb->getNdbError().code );
335                         Debug( LDAP_DEBUG_ANY,
336                                 "=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
337                         return rc;
338                 }
339                 if ( hole ) {
340                         a = NA->e->e_attrs;
341                         NA->e->e_attrs = &tmp;
342                         tmp.a_desc = slap_schema.si_ad_objectClass;
343                         tmp.a_vals = glueval;
344                         tmp.a_nvals = tmp.a_vals;
345                         tmp.a_numvals = 1;
346                 }
347                 rc = ndb_entry_put_info( op->o_bd, NA, 0 );
348                 if ( hole ) {
349                         NA->e->e_attrs = a;
350                 }
351                 if ( rc ) {
352                         snprintf( text->bv_val, text->bv_len, 
353                                 "ndb_entry_put_info failed: %s (%d)",
354                                 myNdb->getNdbError().message, myNdb->getNdbError().code );
355                 Debug( LDAP_DEBUG_ANY,
356                         "=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
357                 } else if ( hole ) {
358                         if ( nholes == nhmax - 1 ) {
359                                 if ( holes == hbuf ) {
360                                         holes = (dn_id *)ch_malloc( nhmax * sizeof(dn_id) * 2 );
361                                         AC_MEMCPY( holes, hbuf, sizeof(hbuf) );
362                                 } else {
363                                         holes = (dn_id *)ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
364                                 }
365                                 nhmax *= 2;
366                         }
367                         ber_dupbv( &holes[nholes].dn, &ndn );
368                         holes[nholes++].id = NA->e->e_id;
369                 }
370         } else if ( !hole ) {
371                 unsigned i;
372
373                 for ( i=0; i<nholes; i++) {
374                         if ( holes[i].id == NA->e->e_id ) {
375                                 int j;
376                                 free(holes[i].dn.bv_val);
377                                 for (j=i;j<nholes;j++) holes[j] = holes[j+1];
378                                 holes[j].id = 0;
379                                 nholes--;
380                                 rc = ndb_entry_put_info( op->o_bd, NA, 1 );
381                                 break;
382                         } else if ( holes[i].id > NA->e->e_id ) {
383                                 break;
384                         }
385                 }
386         }
387         return rc;
388 }
389
390 extern "C"
391 ID ndb_tool_entry_put(
392         BackendDB *be,
393         Entry *e,
394         struct berval *text )
395 {
396         struct ndb_info *ni = (struct ndb_info *) be->be_private;
397         struct dn_id dtmp, *dptr;
398         NdbArgs NA;
399         NdbRdns rdns;
400         int rc, slow = 0;
401         Operation op = {0};
402         Opheader ohdr = {0};
403
404         assert( be != NULL );
405         assert( slapMode & SLAP_TOOL_MODE );
406
407         assert( text != NULL );
408         assert( text->bv_val != NULL );
409         assert( text->bv_val[0] == '\0' );      /* overconservative? */
410
411         Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(ndb_tool_entry_put)
412                 "( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 );
413
414         if ( !be_issuffix( be, &e->e_nname )) {
415                 dnParent( &e->e_nname, &dtmp.dn );
416                 dptr = (struct dn_id *)avl_find( myParents, &dtmp, ndb_dnid_cmp );
417                 if ( !dptr )
418                         slow = 1;
419         }
420
421         rdns.nr_num = 0;
422
423         op.o_hdr = &ohdr;
424         op.o_bd = be;
425         op.o_tmpmemctx = NULL;
426         op.o_tmpmfuncs = &ch_mfuncs;
427
428         if ( !slow ) {
429                 rc = ndb_next_id( be, myNdb, &e->e_id );
430                 if ( rc ) {
431                         snprintf( text->bv_val, text->bv_len,
432                                 "next_id failed: %s (%d)",
433                                 myNdb->getNdbError().message, myNdb->getNdbError().code );
434                         Debug( LDAP_DEBUG_ANY,
435                                 "=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
436                         return rc;
437                 }
438         }
439
440         if ( !myPutTxn )
441                 myPutTxn = myNdb->startTransaction();
442         if ( !myPutTxn ) {
443                 snprintf( text->bv_val, text->bv_len,
444                         "start_transaction failed: %s (%d)",
445                         myNdb->getNdbError().message, myNdb->getNdbError().code );
446                 Debug( LDAP_DEBUG_ANY,
447                         "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
448                          text->bv_val, 0, 0 );
449                 return NOID;
450         }
451
452         /* add dn2id indices */
453         ndb_dn2rdns( &e->e_name, &rdns );
454         NA.rdns = &rdns;
455         NA.e = e;
456         NA.ndb = myNdb;
457         NA.txn = myPutTxn;
458         if ( slow ) {
459                 rc = ndb_tool_next_id( &op, &NA, text, 0 );
460                 if( rc != 0 ) {
461                         goto done;
462                 }
463         } else {
464                 rc = ndb_entry_put_info( be, &NA, 0 );
465                 if ( rc != 0 ) {
466                         goto done;
467                 }
468         }
469
470         /* id2entry index */
471         rc = ndb_entry_put_data( be, &NA );
472         if( rc != 0 ) {
473                 snprintf( text->bv_val, text->bv_len,
474                                 "ndb_entry_put_data failed: %s (%d)",
475                                 myNdb->getNdbError().message, myNdb->getNdbError().code );
476                 Debug( LDAP_DEBUG_ANY,
477                         "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
478                         text->bv_val, 0, 0 );
479                 goto done;
480         }
481
482 done:
483         if( rc == 0 ) {
484                 myPutCnt++;
485                 if ( !( myPutCnt & 0x0f )) {
486                         rc = myPutTxn->execute(NdbTransaction::Commit);
487                         if( rc != 0 ) {
488                                 snprintf( text->bv_val, text->bv_len,
489                                         "txn_commit failed: %s (%d)",
490                                         myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
491                                 Debug( LDAP_DEBUG_ANY,
492                                         "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
493                                         text->bv_val, 0, 0 );
494                                 e->e_id = NOID;
495                         }
496                         myPutTxn->close();
497                         myPutTxn = NULL;
498                 }
499         } else {
500                 snprintf( text->bv_val, text->bv_len,
501                         "txn_aborted! %s (%d)",
502                         myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
503                 Debug( LDAP_DEBUG_ANY,
504                         "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
505                         text->bv_val, 0, 0 );
506                 e->e_id = NOID;
507                 myPutTxn->close();
508         }
509
510         return e->e_id;
511 }
512
513 extern "C"
514 int ndb_tool_entry_reindex(
515         BackendDB *be,
516         ID id,
517         AttributeDescription **adv )
518 {
519         struct ndb_info *ni = (struct ndb_info *) be->be_private;
520
521         Debug( LDAP_DEBUG_ARGS,
522                 "=> " LDAP_XSTRING(ndb_tool_entry_reindex) "( %ld )\n",
523                 (long) id, 0, 0 );
524
525         return 0;
526 }
527
528 extern "C"
529 ID ndb_tool_entry_modify(
530         BackendDB *be,
531         Entry *e,
532         struct berval *text )
533 {
534         struct ndb_info *ni = (struct ndb_info *) be->be_private;
535         int rc;
536
537         Debug( LDAP_DEBUG_TRACE,
538                 "=> " LDAP_XSTRING(ndb_tool_entry_modify) "( %ld, \"%s\" )\n",
539                 (long) e->e_id, e->e_dn, 0 );
540
541 done:
542         return e->e_id;
543 }
544