1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
3 #ident "$Id$"
4 /*======
5 This file is part of TokuDB
6 
7 
8 Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
9 
10     TokuDBis is free software: you can redistribute it and/or modify
11     it under the terms of the GNU General Public License, version 2,
12     as published by the Free Software Foundation.
13 
14     TokuDB is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18 
19     You should have received a copy of the GNU General Public License
20     along with TokuDB.  If not, see <http://www.gnu.org/licenses/>.
21 
22 ======= */
23 
24 #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
25 
26 // Test cardinality algorithm on a 2 level key where the first level is random in a space of size maxrand
27 // and the second level is unique.
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stdint.h>
32 #include <memory.h>
33 #include <assert.h>
34 #include <errno.h>
35 #include <db.h>
36 #if __linux__
37 #include <endian.h>
38 #endif
39 #include <sys/stat.h>
40 typedef unsigned long long ulonglong;
41 #include "tokudb_status.h"
42 #include "tokudb_buffer.h"
43 #include "fake_mysql.h"
44 #if __APPLE__
45 typedef unsigned long ulong;
46 #endif
47 #include "tokudb_card.h"
48 
hton32(uint32_t n)49 static uint32_t hton32(uint32_t n) {
50 #if BYTE_ORDER == LITTLE_ENDIAN
51     return __builtin_bswap32(n);
52 #else
53     return n;
54 #endif
55 }
56 
57 struct key {
58     uint32_t r;
59     uint32_t seq;
60 }; //  __attribute__((packed));
61 
62 struct val {
63     uint32_t v0;
64 }; //  __attribute__((packed));
65 
66 // load nrows into the db
load_db(DB_ENV * env,DB * db,uint32_t nrows,uint32_t maxrand)67 static void load_db(DB_ENV *env, DB *db, uint32_t nrows, uint32_t maxrand) {
68     DB_TXN *txn = NULL;
69     int r = env->txn_begin(env, NULL, &txn, 0);
70     assert(r == 0);
71 
72     DB_LOADER *loader = NULL;
73     uint32_t db_flags[1] = { 0 };
74     uint32_t dbt_flags[1] = { 0 };
75     uint32_t loader_flags = 0;
76     r = env->create_loader(env, txn, &loader, db, 1, &db, db_flags, dbt_flags, loader_flags);
77     assert(r == 0);
78 
79     for (uint32_t seq = 0; seq < nrows ; seq++) {
80         struct key k = { hton32(random() % maxrand), hton32(seq) };
81         struct val v = { seq };
82         DBT key = { .data = &k, .size = sizeof k };
83         DBT val = { .data = &v, .size = sizeof v };
84         r = loader->put(loader, &key, &val);
85         assert(r == 0);
86     }
87 
88     r = loader->close(loader);
89     assert(r == 0);
90 
91     r = txn->commit(txn, 0);
92     assert(r == 0);
93 }
94 
analyze_key_compare(DB * db,const DBT * a,const DBT * b,uint level)95 static int analyze_key_compare(DB *db __attribute__((unused)), const DBT *a, const DBT *b, uint level) {
96     assert(a->size == b->size);
97     switch (level) {
98     default:
99         assert(0);
100     case 1:
101         return memcmp(a->data, b->data, sizeof (uint32_t));
102     case 2:
103         assert(a->size == sizeof (struct key));
104         return memcmp(a->data, b->data, sizeof (struct key));
105     }
106 }
107 
test_card(DB_ENV * env,DB * db,uint64_t expect[])108 static void test_card(DB_ENV *env, DB *db, uint64_t expect[]) {
109     int r;
110 
111     DB_TXN *txn = NULL;
112     r = env->txn_begin(env, NULL, &txn, 0);
113     assert(r == 0);
114 
115     uint64_t num_key_parts = 2;
116     uint64_t rec_per_key[num_key_parts];
117 
118     r = tokudb::analyze_card(db, txn, false, num_key_parts, rec_per_key, analyze_key_compare, NULL, NULL);
119     assert(r == 0);
120 
121     assert(rec_per_key[0] == expect[0]);
122     assert(rec_per_key[1] == expect[1]);
123 
124     r = txn->commit(txn, 0);
125     assert(r == 0);
126 }
127 
main(int argc,char * const argv[])128 int main(int argc, char * const argv[]) {
129     uint64_t nrows = 1000000;
130     uint32_t maxrand = 10;
131     for (int i = 1; i < argc; i++) {
132         if (strcmp(argv[i], "--nrows") == 0 && i+1 < argc) {
133             nrows = atoll(argv[++i]);
134             continue;
135         }
136         if (strcmp(argv[i], "--maxrand") == 0 && i+1 < argc) {
137             maxrand = atoi(argv[++i]);
138             continue;
139         }
140     }
141 
142     int r;
143     r = system("rm -rf " __FILE__ ".testdir");
144     assert(r == 0);
145     r = mkdir(__FILE__ ".testdir", S_IRWXU+S_IRWXG+S_IRWXO);
146     assert(r == 0);
147 
148     DB_ENV *env = NULL;
149     r = db_env_create(&env, 0);
150     assert(r == 0);
151 
152     r = env->open(env, __FILE__ ".testdir", DB_INIT_MPOOL + DB_INIT_LOG + DB_INIT_LOCK + DB_INIT_TXN + DB_PRIVATE + DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
153     assert(r == 0);
154 
155     // create the db
156     DB *db = NULL;
157     r = db_create(&db, env, 0);
158     assert(r == 0);
159 
160     r = db->open(db, NULL, "test.db", 0, DB_BTREE, DB_CREATE + DB_AUTO_COMMIT, S_IRWXU+S_IRWXG+S_IRWXO);
161     assert(r == 0);
162 
163     // load the db
164     load_db(env, db, nrows, maxrand);
165 
166     uint64_t expect[2] = { nrows/maxrand, 1 };
167     test_card(env, db, expect);
168 
169     r = db->close(db, 0);
170     assert(r == 0);
171 
172     r = env->close(env, 0);
173     assert(r == 0);
174 
175     return 0;
176 }
177