Merge lp:~cuteyw/drizzle/cloud-storage-engine-using-json-row-buffer-format into lp:~drizzle-trunk/drizzle/development

Proposed by Wei Ye
Status: Work in progress
Proposed branch: lp:~cuteyw/drizzle/cloud-storage-engine-using-json-row-buffer-format
Merge into: lp:~drizzle-trunk/drizzle/development
Diff against target: 3103 lines (+2930/-0)
34 files modified
plugin/cloud_storage/awss3_service.cc (+451/-0)
plugin/cloud_storage/awss3_service.h (+75/-0)
plugin/cloud_storage/base64.cc (+78/-0)
plugin/cloud_storage/base64.h (+32/-0)
plugin/cloud_storage/cloud_storage_service.h (+84/-0)
plugin/cloud_storage/curl.cc (+281/-0)
plugin/cloud_storage/curl.h (+130/-0)
plugin/cloud_storage/ha_cloud.cc (+1187/-0)
plugin/cloud_storage/ha_cloud.h (+148/-0)
plugin/cloud_storage/plugin.ac (+3/-0)
plugin/cloud_storage/plugin.ini (+8/-0)
plugin/cloud_storage/str_percent.cc (+67/-0)
plugin/cloud_storage/str_percent.h (+36/-0)
plugin/cloud_storage/tests/r/basic_create_table.result (+19/-0)
plugin/cloud_storage/tests/r/basic_delete.result (+24/-0)
plugin/cloud_storage/tests/r/basic_insert.result (+21/-0)
plugin/cloud_storage/tests/r/basic_select.result (+18/-0)
plugin/cloud_storage/tests/r/basic_update.result (+21/-0)
plugin/cloud_storage/tests/r/multi_column.result (+26/-0)
plugin/cloud_storage/tests/r/nullable_or_empty_column.result (+16/-0)
plugin/cloud_storage/tests/t/basic_create_table-master.opt (+1/-0)
plugin/cloud_storage/tests/t/basic_create_table.test (+31/-0)
plugin/cloud_storage/tests/t/basic_delete-master.opt (+1/-0)
plugin/cloud_storage/tests/t/basic_delete.test (+29/-0)
plugin/cloud_storage/tests/t/basic_insert-master.opt (+1/-0)
plugin/cloud_storage/tests/t/basic_insert.test (+26/-0)
plugin/cloud_storage/tests/t/basic_select-master.opt (+1/-0)
plugin/cloud_storage/tests/t/basic_select.test (+21/-0)
plugin/cloud_storage/tests/t/basic_update-master.opt (+1/-0)
plugin/cloud_storage/tests/t/basic_update.test (+28/-0)
plugin/cloud_storage/tests/t/multi_column-master.opt (+1/-0)
plugin/cloud_storage/tests/t/multi_column.test (+41/-0)
plugin/cloud_storage/tests/t/nullable_or_empty_column.opt (+1/-0)
plugin/cloud_storage/tests/t/nullable_or_empty_column.test (+22/-0)
To merge this branch: bzr merge lp:~cuteyw/drizzle/cloud-storage-engine-using-json-row-buffer-format
Reviewer Review Type Date Requested Status
Drizzle Merge Team Pending
Stewart Smith Pending
Mark Atwood Pending
Monty Taylor Pending
Review via email: mp+32831@code.launchpad.net

This proposal supersedes a proposal from 2010-08-16.

To post a comment you must log in.
1696. By cactus <cactus@cactus-desktop>

merge

Unmerged revisions

1696. By cactus <cactus@cactus-desktop>

merge

1695. By cactus <cactus@cactus-desktop>

update header of ha_cloud.cc

1694. By cactus <cactus@cactus-desktop>

change row buffer fomat from csv to json, and now support nullable column

1693. By cactus <cactus@cactus-desktop>

AWSS3Service can send and receive binary data now

1692. By cactus <cactus@cactus-desktop>

adjust to lock api

1691. By cactus <cactus@cactus-desktop>

add update test

1690. By cactus <cactus@cactus-desktop>

merge

1689. By cactus <cactus@cactus-desktop>

update test

1688. By cactus <cactus@cactus-desktop>

update test

1687. By cactus <cactus@cactus-desktop>

update test result

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'plugin/cloud_storage'
2=== added file 'plugin/cloud_storage/awss3_service.cc'
3--- plugin/cloud_storage/awss3_service.cc 1970-01-01 00:00:00 +0000
4+++ plugin/cloud_storage/awss3_service.cc 2010-08-19 16:21:41 +0000
5@@ -0,0 +1,451 @@
6+/* Copyright (C) 2010 Wei Ye
7+
8+ This program is free software; you can redistribute it and/or modify
9+ it under the terms of the GNU General Public License as published by
10+ the Free Software Foundation; version 2 of the License.
11+
12+ This program is distributed in the hope that it will be useful,
13+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ GNU General Public License for more details.
16+
17+ You should have received a copy of the GNU General Public License
18+ along with this program; if not, write to the Free Software
19+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
20+
21+#include "config.h"
22+
23+#include <cassert>
24+#include <cstring>
25+#include <iostream>
26+
27+#include <drizzled/algorithm/sha1.h>
28+#include <gcrypt.h>
29+
30+#include "curl.h"
31+#include "base64.h"
32+#include "awss3_service.h"
33+
34+AWSS3Service::AWSS3Service(const std::string& bkt) :
35+ CloudStorageService(bkt), connection(NULL)
36+{
37+ const std::string& host= std::string("http://") + bucket + "." + serviceURL();
38+ connection= HTTP::ConnectionFactory::openHttpConnection(host);
39+}
40+
41+AWSS3Service::~AWSS3Service()
42+{
43+ assert(connection);
44+ HTTP::ConnectionFactory::destroyHttpConnection(connection);
45+}
46+
47+void AWSS3Service::parseItemKey(BucketInfo* bucket_info,
48+ xmlDocPtr doc,
49+ xmlNodePtr cur)
50+{
51+ xmlChar *str;
52+ cur= cur->xmlChildrenNode;
53+
54+ while (cur != NULL)
55+ {
56+ if ((!xmlStrcmp(cur->name, (const xmlChar *) "Key")))
57+ {
58+ str= xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
59+ bucket_info->keys.push_back((char *) str);
60+ //TODO: save itemkey_z as well
61+ xmlFree(str);
62+ }
63+ //TODO: save/cache LastModified, ETag, Size, Owner, etc
64+ cur= cur->next;
65+ }
66+
67+ return;
68+}
69+
70+BucketInfo* AWSS3Service::parseBucketInfo(xmlDocPtr doc)
71+{
72+ xmlNodePtr cur;
73+ xmlChar *str;
74+
75+ cur= xmlDocGetRootElement(doc);
76+
77+ if (cur == NULL)
78+ {
79+ // empty document
80+ xmlFreeDoc(doc);
81+ return NULL;
82+ }
83+ if (xmlStrcmp(cur->name, (const xmlChar *) "ListBucketResult"))
84+ {
85+ // document of the wrong type
86+ xmlFreeDoc(doc);
87+ return NULL;
88+ }
89+
90+ BucketInfo* bucket_info= new BucketInfo();
91+
92+ cur= cur->xmlChildrenNode;
93+ while (cur != NULL)
94+ {
95+ if ((!xmlStrcmp(cur->name, (const xmlChar *) "IsTruncated")))
96+ {
97+ str= xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
98+ bucket_info->isTrucated= (strcasecmp("true", (char*) str) == 0);
99+ xmlFree(str);
100+ }
101+ else if ((!xmlStrcmp(cur->name, (const xmlChar *) "Contents")))
102+ {
103+ parseItemKey(bucket_info, doc, cur);
104+ }
105+ cur= cur->next;
106+ }
107+
108+ return bucket_info;
109+}
110+
111+BucketInfo* AWSS3Service::parseBucketInfo(const std::string &content)
112+{
113+ xmlDocPtr doc= xmlReadMemory(content.c_str(), content.length(), "list.xml",
114+ NULL, 0);
115+ if (!doc)
116+ {
117+ return NULL;
118+ }
119+
120+ return parseBucketInfo(doc);
121+}
122+
123+std::string* AWSS3Service::makeAWSS3CanonicalString(
124+ std::string *gp, const char *http_verb,
125+ AWSS3Service::HTTPHeaderMap& headers, const char *aws_s3_objectname)
126+{
127+ const char *aws_s3_bucket= bucket.c_str();
128+
129+ gp->clear();
130+
131+ /* the HTTP method verb */
132+ gp->append(http_verb);
133+ gp->append("\n");
134+
135+ {
136+ /* the Content-Md5, from the headers, if known */
137+ HTTPHeaderMap::const_iterator iter= headers.find("Content-MD5");
138+ if (iter != headers.end())
139+ {
140+ const std::string& md5= iter->second;
141+ gp->append(md5);
142+ }
143+ gp->append("\n");
144+ }
145+
146+ {
147+ /* the Content-Type, from the headers, if known */
148+ HTTPHeaderMap::const_iterator iter= headers.find("Content-Type");
149+ if (iter != headers.end())
150+ {
151+ const std::string& type= iter->second;
152+ gp->append(type);
153+ }
154+ gp->append("\n");
155+ }
156+
157+ {
158+ /* The Date header, if set. Otherwise create one */
159+ HTTPHeaderMap::const_iterator iter= headers.find("Date");
160+ assert(iter != headers.end());
161+ const std::string& date= iter->second;
162+ gp->append(date);
163+ gp->append("\n");
164+ }
165+
166+ // TODO process x-amz headers
167+
168+ gp->append("/");
169+ if (aws_s3_bucket)
170+ {
171+ gp->append(aws_s3_bucket);
172+ if (aws_s3_objectname)
173+ {
174+ gp->append("/");
175+ gp->append(aws_s3_objectname);
176+ }
177+ }
178+
179+ return gp;
180+}
181+
182+std::string* AWSS3Service::makeAWSS3Authorization(
183+ std::string *returnstr, const char *http_verb,
184+ HTTPHeaderMap& headers, const char *aws_s3_objectname)
185+{
186+ const char *aws_access_key_id= keyID.c_str();
187+ const char *aws_access_key_secret= keyValue.c_str();
188+
189+ std::string g;
190+ std::string *gp;
191+
192+ if ((aws_access_key_id == NULL) || (aws_access_key_id[0] == '\0')
193+ || (aws_access_key_secret == NULL)
194+ || (aws_access_key_secret[0] == '\0')
195+ || (http_verb == NULL) || (http_verb[0] == '\0'))
196+ {
197+ returnstr->clear();
198+ return returnstr;
199+ }
200+
201+ gp= makeAWSS3CanonicalString(&g, http_verb, headers, aws_s3_objectname);
202+
203+ // calculate authorization signature value
204+ gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
205+ gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
206+ gcry_md_hd_t sha1_context;
207+ gcry_md_open(&sha1_context, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC);
208+ gcry_md_setkey(sha1_context, aws_access_key_secret,
209+ strlen(aws_access_key_secret));
210+ gcry_md_write(sha1_context, gp->c_str(), gp->length());
211+
212+ unsigned char *digest= gcry_md_read(sha1_context, GCRY_MD_FLAG_HMAC);
213+ char* cry= new char[(uint32_t) base64_needed_encoded_length(
214+ SHA1_DIGEST_LENGTH) + 1];
215+ (void) base64_encode(digest, SHA1_DIGEST_LENGTH, cry);
216+
217+ gcry_md_close(sha1_context);
218+
219+ returnstr->clear();
220+ returnstr->append("AWS ");
221+ returnstr->append(aws_access_key_id);
222+ returnstr->append(":");
223+ returnstr->append(cry);
224+
225+ delete[] cry;
226+
227+ return returnstr;
228+}
229+
230+const std::string AWSS3Service::currentDateTime() const
231+{
232+ char st[40];
233+ time_t t= time(NULL);
234+ struct tm tm;
235+ gmtime_r(&t, &tm);
236+
237+ /* TODO: set locale temporarily to "C" for call to strftime */
238+ strftime(st, sizeof(st), "%a, %d %b %Y %T GMT", &tm);
239+
240+ return std::string(st);
241+}
242+
243+long AWSS3Service::getBucket(std::string *result,
244+ int limit, const char *parameter)
245+{
246+ (void) limit;
247+
248+ std::string path= "/?";
249+ char limit_no[10]= {0};
250+
251+ snprintf(limit_no, sizeof(limit_no), "%d", limit);
252+ path.append("max-keys=");
253+ path.append(limit_no);
254+
255+ if (parameter)
256+ {
257+ path.append("&");
258+ path.append(parameter);
259+ }
260+
261+ HTTPHeaderMap httpRequestHeaders;
262+
263+ std::string t= currentDateTime();
264+ httpRequestHeaders.insert(HTTPHeaderMap::value_type("Date", t));
265+ connection->reset();
266+ connection->setRequestMethod(HTTP::GET);
267+ connection->setRequestURI(path);
268+ connection->addRequestHeader("Date", t);
269+ connection->setDebugOS(&std::cout);
270+
271+ std::string auth;
272+ makeAWSS3Authorization(&auth, "GET", httpRequestHeaders, "");
273+ connection->addRequestHeader("Authorization", auth);
274+
275+ bool res= connection->request();
276+
277+ if (!res)
278+ return -1;
279+
280+ *result= connection->getResponse();
281+
282+ return connection->getResponseCode();
283+}
284+
285+long AWSS3Service::headBucket(const char *parameter)
286+{
287+ std::string path= "/";
288+
289+ if (parameter)
290+ {
291+ path.append("?");
292+ path.append(parameter);
293+ }
294+
295+ HTTPHeaderMap httpRequestHeaders;
296+
297+ std::string t= currentDateTime();
298+ httpRequestHeaders.insert(HTTPHeaderMap::value_type("Date", t));
299+ connection->reset();
300+ connection->setRequestMethod(HTTP::GET);
301+ connection->setRequestURI(path);
302+ connection->addRequestHeader("Date", t);
303+
304+ std::string auth;
305+ makeAWSS3Authorization(&auth, "GET", httpRequestHeaders, "");
306+ connection->addRequestHeader("Authorization", auth);
307+
308+ bool res= connection->request();
309+
310+ if (!res)
311+ return -1;
312+
313+ return connection->getResponseCode();
314+}
315+
316+long AWSS3Service::getItem(const char *key, std::string *result,
317+ const char *parameter)
318+{
319+ std::string path= "/";
320+
321+ if (key)
322+ {
323+ path.append(key);
324+ }
325+
326+ if (parameter)
327+ {
328+ path.append("?");
329+ path.append(parameter);
330+ }
331+
332+ HTTPHeaderMap httpRequestHeaders;
333+ connection->reset();
334+ connection->setRequestMethod(HTTP::GET);
335+ connection->setRequestURI(path);
336+ httpRequestHeaders["Date"]= currentDateTime();
337+ connection->addRequestHeader("Date", httpRequestHeaders["Date"]);
338+
339+ std::string auth;
340+ makeAWSS3Authorization(&auth, "GET", httpRequestHeaders, key);
341+ connection->addRequestHeader("Authorization", auth);
342+
343+ bool res= connection->request();
344+
345+ if (!res)
346+ return -1;
347+
348+ result->clear();
349+ result->append(connection->getResponse());
350+
351+ return connection->getResponseCode();
352+}
353+
354+long AWSS3Service::headItem(const char *key, const char *parameter)
355+{
356+ std::string path= "/";
357+
358+ if (key)
359+ {
360+ path.append(key);
361+ }
362+
363+ if (parameter)
364+ {
365+ path.append("?");
366+ path.append(parameter);
367+ }
368+
369+ HTTPHeaderMap httpRequestHeaders;
370+ connection->reset();
371+ connection->setRequestMethod(HTTP::GET);
372+ connection->setRequestURI(path);
373+ httpRequestHeaders["Date"]= currentDateTime();
374+ connection->addRequestHeader("Date", httpRequestHeaders["Date"]);
375+
376+ std::string auth;
377+ makeAWSS3Authorization(&auth, "GET", httpRequestHeaders, key);
378+ connection->addRequestHeader("Authorization", auth);
379+
380+ bool res= connection->request();
381+
382+ if (!res)
383+ return -1;
384+
385+ return connection->getResponseCode();
386+}
387+
388+long AWSS3Service::putItem(const char *key, const char *content, size_t len,
389+ const char *parameter)
390+{
391+ std::string path= "/";
392+
393+ if (key)
394+ {
395+ path.append(key);
396+ }
397+
398+ if (parameter)
399+ {
400+ path.append("?");
401+ path.append(parameter);
402+ }
403+
404+ HTTPHeaderMap httpRequestHeaders;
405+ connection->reset();
406+ connection->setRequestMethod(HTTP::PUT);
407+ connection->setRequestURI(path);
408+ httpRequestHeaders["Date"]= currentDateTime();
409+ connection->addRequestHeader("Date", httpRequestHeaders["Date"]);
410+
411+ std::string auth;
412+ makeAWSS3Authorization(&auth, "PUT", httpRequestHeaders, key);
413+ connection->addRequestHeader("Authorization", auth);
414+
415+ bool res= connection->request(std::string(content, len));
416+
417+ if (!res)
418+ return -1;
419+
420+ return connection->getResponseCode();
421+}
422+
423+long AWSS3Service::deleteItem(const char *key, const char *parameter)
424+{
425+ std::string path= "/";
426+
427+ if (key)
428+ {
429+ path.append(key);
430+ }
431+
432+ if (parameter)
433+ {
434+ path.append("?");
435+ path.append(parameter);
436+ }
437+
438+ HTTPHeaderMap httpRequestHeaders;
439+ connection->reset();
440+ connection->setRequestMethod(HTTP::DELETE);
441+ connection->setRequestURI(path);
442+ httpRequestHeaders["Date"]= currentDateTime();
443+ connection->addRequestHeader("Date", httpRequestHeaders["Date"]);
444+
445+ std::string auth;
446+ makeAWSS3Authorization(&auth, "DELETE", httpRequestHeaders, key);
447+ connection->addRequestHeader("Authorization", auth);
448+
449+ bool res= connection->request();
450+
451+ if (!res)
452+ return -1;
453+
454+ return connection->getResponseCode();
455+}
456+
457
458=== added file 'plugin/cloud_storage/awss3_service.h'
459--- plugin/cloud_storage/awss3_service.h 1970-01-01 00:00:00 +0000
460+++ plugin/cloud_storage/awss3_service.h 2010-08-19 16:21:41 +0000
461@@ -0,0 +1,75 @@
462+/* Copyright (C) 2010 Wei Ye
463+
464+ This program is free software; you can redistribute it and/or modify
465+ it under the terms of the GNU General Public License as published by
466+ the Free Software Foundation; version 2 of the License.
467+
468+ This program is distributed in the hope that it will be useful,
469+ but WITHOUT ANY WARRANTY; without even the implied warranty of
470+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
471+ GNU General Public License for more details.
472+
473+ You should have received a copy of the GNU General Public License
474+ along with this program; if not, write to the Free Software
475+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
476+
477+#ifndef PLUGIN_CLOUD_AWSS3_SERVICE_H
478+#define PLUGIN_CLOUD_AWSS3_SERVICE_H
479+
480+#include <libxml/xmlmemory.h>
481+#include <libxml/parser.h>
482+#include <libxml/tree.h>
483+
484+#include <string>
485+#include <map>
486+
487+#include "cloud_storage_service.h"
488+
489+namespace HTTP {
490+ class Connection;
491+}
492+
493+class AWSS3Service: public CloudStorageService
494+{
495+public:
496+ AWSS3Service(const std::string& bkt);
497+ virtual ~AWSS3Service();
498+
499+ const char *serviceURL(void) const
500+ {
501+ return "s3.amazonaws.com";
502+ }
503+
504+ virtual BucketInfo* parseBucketInfo(const std::string &content);
505+ virtual long getBucket(std::string *result,
506+ int maxKeys= 100, const char *parameter= NULL);
507+ virtual long headBucket(const char *parameter= NULL);
508+ virtual long getItem(const char *key, std::string *result,
509+ const char *parameter= NULL);
510+ virtual long headItem(const char *key, const char *parameter= NULL);
511+ virtual long putItem(const char *key, const char *content, size_t len,
512+ const char *parameter= NULL);
513+ virtual long deleteItem(const char *key, const char *parameter= NULL);
514+
515+private:
516+ typedef std::map<std::string, std::string> HTTPHeaderMap;
517+
518+ BucketInfo* parseBucketInfo(xmlDocPtr doc);
519+ void parseItemKey(BucketInfo* bucket_info, xmlDocPtr doc, xmlNodePtr cur);
520+
521+ std::string* makeAWSS3CanonicalString(std::string *gp, const char *http_verb,
522+ HTTPHeaderMap& headers,
523+ const char *aws_s3_objectname);
524+
525+ std::string* makeAWSS3Authorization(std::string *returnstr,
526+ const char *http_verb,
527+ HTTPHeaderMap& headers,
528+ const char *aws_s3_objectname);
529+
530+ const std::string currentDateTime() const;
531+
532+private:
533+ HTTP::Connection* connection;
534+};
535+
536+#endif /* undef PLUGIN_CLOUD_AWSS3_SERVICE_H */
537
538=== added file 'plugin/cloud_storage/base64.cc'
539--- plugin/cloud_storage/base64.cc 1970-01-01 00:00:00 +0000
540+++ plugin/cloud_storage/base64.cc 2010-08-19 16:21:41 +0000
541@@ -0,0 +1,78 @@
542+/* Copyright (C) 2010 Wei Ye
543+
544+ This program is free software; you can redistribute it and/or modify
545+ it under the terms of the GNU General Public License as published by
546+ the Free Software Foundation; version 2 of the License.
547+
548+ This program is distributed in the hope that it will be useful,
549+ but WITHOUT ANY WARRANTY; without even the implied warranty of
550+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
551+ GNU General Public License for more details.
552+
553+ You should have received a copy of the GNU General Public License
554+ along with this program; if not, write to the Free Software
555+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
556+
557+#include "config.h"
558+#include "base64.h"
559+
560+static char base64_table[]= "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
561+ "abcdefghijklmnopqrstuvwxyz"
562+ "0123456789+/";
563+
564+int base64_needed_encoded_length(int length_of_data)
565+{
566+ int nb_base64_chars;
567+ nb_base64_chars= (length_of_data + 2) / 3 * 4;
568+
569+ return (nb_base64_chars + /* base64 char incl padding */
570+ (nb_base64_chars - 1) / 76 + /* newlines */
571+ 1); /* NUL termination of string */
572+}
573+
574+int base64_encode(const void *src, size_t src_len, char *dst)
575+{
576+ const unsigned char *s= (const unsigned char*) src;
577+ size_t i= 0;
578+ size_t len= 0;
579+
580+ for (; i < src_len; len+= 4)
581+ {
582+ unsigned c= 0;
583+
584+ if (len == 76)
585+ {
586+ len= 0;
587+ *dst++= '\n';
588+ }
589+
590+ c= s[i++];
591+ c <<= 8;
592+
593+ if (i < src_len)
594+ c+= s[i];
595+ c <<= 8;
596+ i++;
597+
598+ if (i < src_len)
599+ c+= s[i];
600+ i++;
601+
602+ *dst++= base64_table[(c >> 18) & 0x3f];
603+ *dst++= base64_table[(c >> 12) & 0x3f];
604+
605+ if (i > (src_len + 1))
606+ *dst++= '=';
607+ else
608+ *dst++= base64_table[(c >> 6) & 0x3f];
609+
610+ if (i > src_len)
611+ *dst++= '=';
612+ else
613+ *dst++= base64_table[(c >> 0) & 0x3f];
614+ }
615+ *dst= '\0';
616+
617+ return 0;
618+}
619+
620
621=== added file 'plugin/cloud_storage/base64.h'
622--- plugin/cloud_storage/base64.h 1970-01-01 00:00:00 +0000
623+++ plugin/cloud_storage/base64.h 2010-08-19 16:21:41 +0000
624@@ -0,0 +1,32 @@
625+/* Copyright (C) 2010 Wei Ye
626+
627+ This program is free software; you can redistribute it and/or modify
628+ it under the terms of the GNU General Public License as published by
629+ the Free Software Foundation; version 2 of the License.
630+
631+ This program is distributed in the hope that it will be useful,
632+ but WITHOUT ANY WARRANTY; without even the implied warranty of
633+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
634+ GNU General Public License for more details.
635+
636+ You should have received a copy of the GNU General Public License
637+ along with this program; if not, write to the Free Software
638+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
639+
640+#ifndef PLUGIN_CLOUD_BASE64_H
641+#define PLUGIN_CLOUD_BASE64_H
642+
643+#include <cstddef>
644+
645+extern int base64_needed_encoded_length(int length_of_data);
646+
647+/*
648+Encode a data as base64.
649+
650+Note: We require that dst is pre-allocated to correct size.
651+See base64_needed_encoded_length().
652+*/
653+extern int base64_encode(const void *src, size_t src_len, char *dst);
654+
655+#endif /* undef PLUGIN_CLOUD_BASE64_H */
656+
657
658=== added file 'plugin/cloud_storage/cloud_storage_service.h'
659--- plugin/cloud_storage/cloud_storage_service.h 1970-01-01 00:00:00 +0000
660+++ plugin/cloud_storage/cloud_storage_service.h 2010-08-19 16:21:41 +0000
661@@ -0,0 +1,84 @@
662+/* Copyright (C) 2010 Wei Ye
663+
664+ This program is free software; you can redistribute it and/or modify
665+ it under the terms of the GNU General Public License as published by
666+ the Free Software Foundation; version 2 of the License.
667+
668+ This program is distributed in the hope that it will be useful,
669+ but WITHOUT ANY WARRANTY; without even the implied warranty of
670+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
671+ GNU General Public License for more details.
672+
673+ You should have received a copy of the GNU General Public License
674+ along with this program; if not, write to the Free Software
675+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
676+
677+#ifndef PLUGIN_CLOUD_CLOUD_STORAGE_H
678+#define PLUGIN_CLOUD_CLOUD_STORAGE_H
679+
680+#include <string>
681+#include <vector>
682+
683+class BucketInfo
684+{
685+private:
686+ BucketInfo(const BucketInfo&);
687+ BucketInfo& operator=(const BucketInfo &);
688+
689+public:
690+ BucketInfo() :
691+ isTrucated(false), keys()
692+ {
693+ }
694+
695+ ~BucketInfo()
696+ {
697+ }
698+
699+ bool isTrucated;
700+ std::vector<std::string> keys;
701+};
702+
703+class CloudStorageService
704+{
705+public:
706+ CloudStorageService(const std::string& bkt) :
707+ bucket(bkt)
708+ {
709+ }
710+
711+ virtual ~CloudStorageService()
712+ {
713+ }
714+
715+ void setKeyID(const std::string &key_id) {
716+ keyID= key_id;
717+ }
718+
719+ void setKeyValue(const std::string &key_value) {
720+ keyValue= key_value;
721+ }
722+
723+ virtual BucketInfo* parseBucketInfo(const std::string &content) = 0;
724+ virtual long getBucket(std::string *result,
725+ int maxKeys= 100, const char *parameter= NULL) = 0;
726+ virtual long headBucket(const char *parameter= NULL) = 0;
727+ virtual long getItem(const char *key, std::string *result,
728+ const char *parameter= NULL) = 0;
729+ virtual long headItem(const char *key, const char *parameter= NULL) = 0;
730+ virtual long putItem(const char *key, const char *content, size_t len,
731+ const char *parameter= NULL) = 0;
732+ virtual long deleteItem(const char *key, const char *parameter= NULL) = 0;
733+
734+private:
735+ CloudStorageService();
736+ CloudStorageService& operator=(const CloudStorageService &);
737+ CloudStorageService(const CloudStorageService&);
738+
739+protected:
740+ std::string bucket;
741+ std::string keyID;
742+ std::string keyValue;
743+};
744+
745+#endif /* undef PLUGIN_CLOUD_CLOUD_STORAGE_H */
746
747=== added file 'plugin/cloud_storage/curl.cc'
748--- plugin/cloud_storage/curl.cc 1970-01-01 00:00:00 +0000
749+++ plugin/cloud_storage/curl.cc 2010-08-19 16:21:41 +0000
750@@ -0,0 +1,281 @@
751+/* Copyright (C) 2010 Wei Ye
752+
753+ This program is free software; you can redistribute it and/or modify
754+ it under the terms of the GNU General Public License as published by
755+ the Free Software Foundation; version 2 of the License.
756+
757+ This program is distributed in the hope that it will be useful,
758+ but WITHOUT ANY WARRANTY; without even the implied warranty of
759+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
760+ GNU General Public License for more details.
761+
762+ You should have received a copy of the GNU General Public License
763+ along with this program; if not, write to the Free Software
764+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
765+
766+#include "config.h"
767+
768+#include <cassert>
769+#include <cstring>
770+#include <string>
771+
772+#include <boost/algorithm/string.hpp>
773+
774+#include "curl.h"
775+
776+namespace HTTP
777+{
778+
779+void ConnectionFactory::init()
780+{
781+ curl_global_init(CURL_GLOBAL_ALL);
782+}
783+
784+void ConnectionFactory::reset()
785+{
786+ curl_global_cleanup();
787+}
788+
789+Connection* ConnectionFactory::openHttpConnection(const std::string& host)
790+{
791+ return new Connection(host);
792+}
793+
794+void ConnectionFactory::destroyHttpConnection(const Connection* http)
795+{
796+ delete http;
797+}
798+
799+Connection::Connection(const std::string& h) :
800+ curl(NULL), host(h), method(GET), requestHeaders(NULL), responseHeaders(),
801+ responseCode(0), response(), debugOS(NULL)
802+{
803+ curl= curl_easy_init();
804+ assert(curl);
805+}
806+
807+Connection::~Connection()
808+{
809+ curl_easy_cleanup(curl);
810+ curl= NULL;
811+
812+ curl_slist_free_all(requestHeaders);
813+ requestHeaders= NULL;
814+}
815+
816+void Connection::setRequestURI(const std::string& url)
817+{
818+ assert(!url.empty() && url[0] == '/');
819+ curl_easy_setopt(curl, CURLOPT_URL, (host+url).c_str());
820+}
821+
822+void Connection::setRequestMethod(METHOD m)
823+{
824+ this->method= m;
825+}
826+
827+void Connection::addRequestHeader(const std::string& key,
828+ const std::string& value)
829+{
830+ std::string header;
831+ header.reserve(key.size() + value.size() + 1);
832+ header.append(boost::trim_copy(key));
833+ header.append(": ");
834+ header.append(boost::trim_copy(value));
835+
836+ requestHeaders= curl_slist_append(requestHeaders, header.c_str());
837+ assert(requestHeaders != NULL);
838+}
839+
840+bool Connection::request(const std::string& data)
841+{
842+
843+ assert(data.empty() || method == PUT);
844+
845+ curl_io_context io_ctx_rt;
846+ curl_io_context io_ctx_st;
847+
848+ switch (method)
849+ {
850+ case GET:
851+ {
852+ io_ctx_rt.ioPos= 0;
853+ io_ctx_rt.ioBuffer= &response;
854+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
855+ &Connection::curlWriteCallback);
856+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &io_ctx_rt);
857+ break;
858+ }
859+ case PUT:
860+ {
861+ io_ctx_rt.ioPos= 0;
862+ io_ctx_rt.ioBuffer= &response;
863+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
864+ &Connection::curlWriteCallback);
865+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &io_ctx_rt);
866+
867+ io_ctx_st.ioPos= 0;
868+ io_ctx_st.ioBuffer= const_cast<std::string*> (&data);
869+ curl_easy_setopt(curl, CURLOPT_READFUNCTION,
870+ &Connection::curlReadCallback);
871+ curl_easy_setopt(curl, CURLOPT_READDATA, (void *) &io_ctx_st);
872+
873+ curl_easy_setopt(curl, CURLOPT_PUT, true);
874+ curl_easy_setopt(curl, CURLOPT_UPLOAD, true);
875+ curl_easy_setopt(curl, CURLOPT_INFILESIZE, data.size());
876+ break;
877+ }
878+ case HEAD:
879+ curl_easy_setopt(curl, CURLOPT_NOBODY, true);
880+ break;
881+ case DELETE:
882+ // be aware that 200, 202, and 204 are valid Ok response codes
883+ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
884+ break;
885+ default:
886+ assert (0 && "Not implement http method");
887+ }
888+
889+ curl_easy_setopt(curl, CURLOPT_VERBOSE, true);
890+ curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &responseHeaders);
891+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, Connection::curlHeaderCallback);
892+ //curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, Connection::curlDebugCallback);
893+
894+ errorBuffer[0]= '\0';
895+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer);
896+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, requestHeaders);
897+
898+ CURLcode res= curl_easy_perform(curl);
899+ if (res == CURLE_OK)
900+ {
901+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode);
902+ }
903+
904+ return (res == CURLE_OK);
905+}
906+
907+long Connection::getResponseCode() const
908+{
909+ return responseCode;
910+}
911+
912+const std::string& Connection::getResponseHeader(const std::string& key) const
913+{
914+ HTTPHeaderIterator it= responseHeaders.find(key);
915+ assert (it != responseHeaders.end());
916+ return it->second;
917+}
918+
919+Connection::HTTPHeaderIterator Connection::responseHeaderBegin() const
920+{
921+ return responseHeaders.begin();
922+}
923+
924+Connection::HTTPHeaderIterator Connection::responseHeaderEnd() const
925+{
926+ return responseHeaders.end();
927+}
928+
929+const std::string& Connection::getResponse() const
930+{
931+ assert(method != HEAD || method != PUT || response.empty());
932+ return response;
933+}
934+
935+void Connection::reset()
936+{
937+ curl_easy_reset(curl);
938+
939+ method= GET;
940+
941+ curl_slist_free_all(requestHeaders);
942+ requestHeaders= NULL;
943+
944+ responseCode= 0;
945+ response= "";
946+ responseHeaders.clear();
947+
948+ debugOS= NULL;
949+
950+ errorBuffer[0]= '\0';
951+}
952+
953+void Connection::setDebugOS(std::ostream* os)
954+{
955+ debugOS= os;
956+ if (debugOS != NULL)
957+ {
958+ *debugOS << "******debug information for curl" << std::endl;
959+ *debugOS << "CURL version: " << curl_version() << std::endl;
960+ }
961+}
962+
963+size_t Connection::curlHeaderCallback(void *ptr, size_t size, size_t nmemb,
964+ void *stream)
965+{
966+ HTTPHeaderMap* headers= reinterpret_cast<HTTPHeaderMap*> (stream);
967+ std::string response(reinterpret_cast<const char*> (ptr), size * nmemb);
968+
969+ boost::trim(response);
970+
971+ if (not response.empty() && response.find("HTTP/1.1") == std::string::npos
972+ && response.find("HTTP/1.0") == std::string::npos)
973+ {
974+ std::string::size_type pos_of_colon= response.find(':');
975+ assert(pos_of_colon != std::string::npos);
976+ std::string key= response.substr(0, pos_of_colon);
977+ std::string value= response.substr(pos_of_colon + 1);
978+
979+ boost::trim(key);
980+ boost::trim(value);
981+
982+ headers->insert(HTTPHeaderMap::value_type(key, value));
983+ }
984+
985+ return size * nmemb;
986+}
987+
988+size_t Connection::curlWriteCallback(void *ptr, size_t size, size_t nmemb,
989+ void *data)
990+{
991+ curl_io_context *ctx= (curl_io_context *) data;
992+ size_t realsize= size * nmemb;
993+
994+ ctx->ioBuffer->append((const char *) ptr, realsize);
995+ ctx->ioPos+= realsize;
996+
997+ return realsize;
998+}
999+
1000+size_t Connection::curlReadCallback(void *ptr, size_t size, size_t nmemb,
1001+ void *data)
1002+{
1003+ curl_io_context *ctx= (curl_io_context *) data;
1004+ size_t willsend= size * nmemb;
1005+
1006+ size_t maxtosend= ctx->ioBuffer->size() - ctx->ioPos;
1007+ if (willsend > maxtosend)
1008+ willsend= maxtosend;
1009+
1010+ memcpy(ptr, ctx->ioBuffer->c_str() + ctx->ioPos, willsend);
1011+ ctx->ioPos+= willsend;
1012+
1013+ return willsend;
1014+}
1015+
1016+int Connection::curlDebugCallback(CURL *, curl_infotype type, char *str,
1017+ size_t n, void *)
1018+{
1019+ (void)type;
1020+ (void)str;
1021+ (void)n;
1022+
1023+ return 0;
1024+}
1025+
1026+const char* Connection::getErrorMessage() const
1027+{
1028+ return errorBuffer;
1029+}
1030+
1031+} // end of namespace
1032
1033=== added file 'plugin/cloud_storage/curl.h'
1034--- plugin/cloud_storage/curl.h 1970-01-01 00:00:00 +0000
1035+++ plugin/cloud_storage/curl.h 2010-08-19 16:21:41 +0000
1036@@ -0,0 +1,130 @@
1037+/* Copyright (C) 2010 Wei Ye
1038+
1039+ This program is free software; you can redistribute it and/or modify
1040+ it under the terms of the GNU General Public License as published by
1041+ the Free Software Foundation; version 2 of the License.
1042+
1043+ This program is distributed in the hope that it will be useful,
1044+ but WITHOUT ANY WARRANTY; without even the implied warranty of
1045+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1046+ GNU General Public License for more details.
1047+
1048+ You should have received a copy of the GNU General Public License
1049+ along with this program; if not, write to the Free Software
1050+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
1051+
1052+#ifndef PLUGIN_CLOUD_CURL_H
1053+#define PLUGIN_CLOUD_CURL_H
1054+
1055+#include <string>
1056+#include <map>
1057+
1058+#include <boost/utility.hpp>
1059+
1060+#include <curl/curl.h>
1061+
1062+namespace HTTP
1063+{
1064+
1065+enum METHOD
1066+{
1067+ GET= 0, HEAD, PUT, DELETE
1068+};
1069+
1070+class Connection;
1071+
1072+class ConnectionFactory
1073+{
1074+public:
1075+ /**
1076+ * NOTE
1077+ * *) These two functions could be called in multiple threads.
1078+ * *) These two functions should be called once in one application.
1079+ */
1080+ static void init();
1081+ static void reset();
1082+
1083+ static Connection* openHttpConnection(const std::string& host);
1084+ static void destroyHttpConnection(const Connection* http);
1085+};
1086+
1087+class Connection: boost::noncopyable
1088+{
1089+private:
1090+ typedef struct curl_io_context
1091+ {
1092+ std::string* ioBuffer;
1093+ size_t ioPos;
1094+ } curl_io_context;
1095+
1096+ typedef std::map<std::string, std::string> HTTPHeaderMap;
1097+
1098+public:
1099+ typedef HTTPHeaderMap::const_iterator HTTPHeaderIterator;
1100+ explicit Connection(const std::string& host);
1101+ ~Connection();
1102+
1103+ /**
1104+ * Specify requested url, must specify by user.
1105+ * You could re-use current connection to request multiple URIs.
1106+ */
1107+ void setRequestURI(const std::string& url);
1108+
1109+ /**
1110+ * Default GET method, if not set explicitly.
1111+ */
1112+ void setRequestMethod(METHOD method);
1113+
1114+ void addRequestHeader(const std::string& key, const std::string& value);
1115+
1116+ /**
1117+ * For form data, user should encoding the data.
1118+ * For GET/HEAD/DELETE method, ignore data.
1119+ */
1120+ bool request(const std::string& data= "");
1121+
1122+ /**
1123+ * @return zero if no server response code has been received.
1124+ */
1125+ long getResponseCode() const;
1126+
1127+ const std::string& getResponseHeader(const std::string& key) const;
1128+
1129+ HTTPHeaderIterator responseHeaderBegin() const;
1130+ HTTPHeaderIterator responseHeaderEnd() const;
1131+
1132+ const std::string& getResponse() const;
1133+
1134+ void reset();
1135+
1136+ /**
1137+ * set debug information to be output to @os.
1138+ */
1139+ void setDebugOS(std::ostream* os);
1140+
1141+ const char* getErrorMessage() const;
1142+
1143+private:
1144+ static size_t curlHeaderCallback(void *ptr, size_t size, size_t nmemb,
1145+ void *stream);
1146+ static int curlDebugCallback(CURL *curl, curl_infotype type, char *str,
1147+ size_t n, void *vp);
1148+ static size_t curlWriteCallback(void *ptr, size_t size, size_t nmemb,
1149+ void *data);
1150+ static size_t curlReadCallback(void *ptr, size_t size, size_t nmemb,
1151+ void *data);
1152+private:
1153+ CURL *curl;
1154+ const std::string host;
1155+ METHOD method;
1156+ struct curl_slist *requestHeaders;
1157+ HTTPHeaderMap responseHeaders;
1158+ long responseCode;
1159+ std::string response;
1160+
1161+ std::ostream* debugOS;
1162+ char errorBuffer[CURL_ERROR_SIZE];
1163+};
1164+}
1165+
1166+#endif /* undef PLUGIN_CLOUD_CURL_H */
1167
1168=== added file 'plugin/cloud_storage/ha_cloud.cc'
1169--- plugin/cloud_storage/ha_cloud.cc 1970-01-01 00:00:00 +0000
1170+++ plugin/cloud_storage/ha_cloud.cc 2010-08-19 16:21:41 +0000
1171@@ -0,0 +1,1187 @@
1172+/* Copyright (C) 2010 Wei Ye
1173+
1174+ This program is free software; you can redistribute it and/or modify
1175+ it under the terms of the GNU General Public License as published by
1176+ the Free Software Foundation; version 2 of the License.
1177+
1178+ This program is distributed in the hope that it will be useful,
1179+ but WITHOUT ANY WARRANTY; without even the implied warranty of
1180+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1181+ GNU General Public License for more details.
1182+
1183+ You should have received a copy of the GNU General Public License
1184+ along with this program; if not, write to the Free Software
1185+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
1186+
1187+#include "config.h"
1188+
1189+#include <drizzled/field.h>
1190+#include <drizzled/field/blob.h>
1191+#include <drizzled/field/timestamp.h>
1192+#include <drizzled/error.h>
1193+#include <drizzled/table.h>
1194+#include <drizzled/session.h>
1195+#include <drizzled/internal/my_sys.h>
1196+#include <drizzled/enum.h>
1197+#include <drizzled/sql_bitmap.h>
1198+
1199+#include <google/protobuf/io/zero_copy_stream.h>
1200+#include <google/protobuf/io/zero_copy_stream_impl.h>
1201+#include <boost/algorithm/string.hpp>
1202+
1203+#include <fcntl.h>
1204+#include <ctype.h>
1205+#include <fstream>
1206+#include <cstring>
1207+
1208+#include "ha_cloud.h"
1209+
1210+#include "str_percent.h"
1211+#include "curl.h"
1212+#include "cloud_storage_service.h"
1213+#include "awss3_service.h"
1214+
1215+using namespace drizzled;
1216+using namespace std;
1217+
1218+#define CLOUD_EXT ".cld"
1219+
1220+/* Stuff for shares */
1221+static pthread_mutex_t cloud_mutex;
1222+
1223+#define BLOB_MEMROOT_ALLOC_SIZE 8192
1224+
1225+class CloudEngine;
1226+static CloudEngine *cloud_engine= NULL;
1227+static const char* ha_cloud_exts[]= { CLOUD_EXT, NULL };
1228+
1229+typedef drizzled::system_status_var SSV;
1230+
1231+static const char BUCKET_OPTION[]= "BUCKET";
1232+static const char KEY_ID_OPTION[]= "KEY_ID";
1233+static const char KEY_OPTION[]= "KEY_VALUE";
1234+static const char SERVICE_OPTION[]= "SERVICE";
1235+
1236+static const char AWSS3[]= "awss3";
1237+static const char RACKSPACE[]= "rackspace";
1238+
1239+CloudShare::CloudShare(const char *table_name_arg) :
1240+ table_name(table_name_arg),
1241+ use_count(0),
1242+ storageService(NULL)
1243+{
1244+ pthread_mutex_init(&mutex, MY_MUTEX_INIT_FAST);
1245+ thr_lock_init(&lock);
1246+}
1247+
1248+CloudShare::~CloudShare()
1249+{
1250+ delete storageService;
1251+ lock.deinit();
1252+ pthread_mutex_destroy(&mutex);
1253+}
1254+
1255+class CloudEngine: public drizzled::plugin::StorageEngine
1256+{
1257+ typedef std::map<std::string, CloudShare*> CloudMap;
1258+ CloudMap cloud_open_tables;
1259+
1260+public:
1261+ CloudEngine(const string &name_arg) :
1262+ drizzled::plugin::StorageEngine(name_arg,
1263+ drizzled::HTON_ALTER_NOT_SUPPORTED |
1264+ drizzled::HTON_REQUIRE_PRIMARY_KEY |
1265+ drizzled::HTON_SKIP_STORE_LOCK |
1266+ drizzled::HTON_AUTO_PART_KEY),
1267+ cloud_open_tables()
1268+ {
1269+ table_definition_ext= CLOUD_EXT;
1270+ }
1271+
1272+ virtual ~CloudEngine()
1273+ {
1274+ pthread_mutex_destroy(&cloud_mutex);
1275+ HTTP::ConnectionFactory::reset();
1276+ }
1277+
1278+ virtual drizzled::Cursor *create(drizzled::TableShare &table)
1279+ {
1280+ return new ha_cloud(*this, table);
1281+ }
1282+
1283+ int doCreateTable(drizzled::Session &session,
1284+ drizzled::Table &table_arg,
1285+ const drizzled::TableIdentifier &identifier,
1286+ drizzled::message::Table &proto);
1287+
1288+ int doGetTableDefinition(drizzled::Session &session,
1289+ const drizzled::TableIdentifier &identifier,
1290+ drizzled::message::Table &table_message);
1291+
1292+ void doGetTableNames(drizzled::CachedDirectory &directory,
1293+ const SchemaIdentifier &,
1294+ set<string> &set_of_names);
1295+
1296+ int doDropTable(drizzled::Session&, const drizzled::TableIdentifier &identifier);
1297+
1298+ int doRenameTable(drizzled::Session&, const drizzled::TableIdentifier&,
1299+ const drizzled::TableIdentifier&);
1300+
1301+ void doGetTableIdentifiers(drizzled::CachedDirectory&,
1302+ const drizzled::SchemaIdentifier&,
1303+ drizzled::TableIdentifiers&);
1304+
1305+ bool doDoesTableExist(drizzled::Session&,
1306+ const drizzled::TableIdentifier &identifier);
1307+
1308+ bool validateCreateTableOption(const std::string &key, const std::string &state);
1309+
1310+ const char** bas_ext() const
1311+ {
1312+ return ha_cloud_exts;
1313+ }
1314+
1315+ CloudShare *findOpenTable(const std::string &table_name);
1316+ bool addOpenTable(const std::string &table_name, CloudShare *);
1317+ void deleteOpenTable(const std::string &table_name);
1318+
1319+ uint32_t max_supported_keys() const { return 1; }
1320+ uint32_t max_supported_key_parts() const { return 1; }
1321+
1322+ uint32_t max_supported_record_length() const
1323+ {
1324+ return HA_MAX_REC_LENGTH;
1325+ }
1326+
1327+ uint32_t max_supported_key_part_length() const
1328+ {
1329+ return MAX_PRIMARY_KEY_LENGTH;
1330+ }
1331+
1332+ uint32_t index_flags(enum drizzled::ha_key_alg) const
1333+ {
1334+ return (HA_ONLY_WHOLE_INDEX | HA_KEYREAD_ONLY);
1335+ }
1336+};
1337+
1338+void CloudEngine::doGetTableNames(drizzled::CachedDirectory &directory,
1339+ const SchemaIdentifier &,
1340+ set<string> &set_of_names)
1341+{
1342+ drizzled::CachedDirectory::Entries entries= directory.getEntries();
1343+
1344+ for (drizzled::CachedDirectory::Entries::iterator entry_iter= entries.begin();
1345+ entry_iter != entries.end(); ++entry_iter)
1346+ {
1347+ drizzled::CachedDirectory::Entry *entry= *entry_iter;
1348+ const string *filename= &entry->filename;
1349+
1350+ assert(filename->size());
1351+
1352+ const char *ext= strchr(filename->c_str(), '.');
1353+
1354+ if (ext == NULL || my_strcasecmp(system_charset_info, ext, CLOUD_EXT) ||
1355+ (filename->compare(0, strlen(TMP_FILE_PREFIX), TMP_FILE_PREFIX) == 0))
1356+ { }
1357+ else
1358+ {
1359+ char uname[NAME_LEN + 1];
1360+ uint32_t file_name_len;
1361+
1362+ file_name_len= TableIdentifier::filename_to_tablename(filename->c_str(), uname, sizeof(uname));
1363+ // TODO: Remove need for memory copy here
1364+ uname[file_name_len - sizeof(CLOUD_EXT) + 1]= '\0'; // Subtract ending, place NULL
1365+ set_of_names.insert(uname);
1366+ }
1367+ }
1368+}
1369+
1370+bool CloudEngine::doDoesTableExist(drizzled::Session&,
1371+ const drizzled::TableIdentifier &identifier)
1372+{
1373+ string proto_path(identifier.getPath());
1374+ proto_path.append(CLOUD_EXT);
1375+
1376+ if (access(proto_path.c_str(), F_OK))
1377+ {
1378+ return false;
1379+ }
1380+
1381+ return true;
1382+}
1383+
1384+bool CloudEngine::validateCreateTableOption(const std::string &key,
1385+ const std::string &state)
1386+{
1387+ // if buckety is not set, table name is used as bucket name
1388+ // if key or key_value is not set, will do anonymous access to cloud service
1389+ if (boost::iequals(key, SERVICE_OPTION))
1390+ {
1391+ if (boost::iequals(state, AWSS3))
1392+ return true;
1393+ else
1394+ return false;
1395+ }
1396+
1397+ return true;
1398+}
1399+
1400+int CloudEngine::doRenameTable(Session&, const TableIdentifier &from, const TableIdentifier &to)
1401+{
1402+ int error= 0;
1403+
1404+ for (const char **ext= bas_ext(); *ext ; ext++)
1405+ {
1406+ if (rename_file_ext(from.getPath().c_str(), to.getPath().c_str(), *ext))
1407+ {
1408+ if ((error=errno) != ENOENT)
1409+ break;
1410+ error= 0;
1411+ }
1412+ }
1413+
1414+ return error;
1415+}
1416+
1417+void CloudEngine::doGetTableIdentifiers(drizzled::CachedDirectory &directory,
1418+ const drizzled::SchemaIdentifier &schema_identifier,
1419+ drizzled::TableIdentifiers &set_of_identifiers)
1420+{
1421+ drizzled::CachedDirectory::Entries entries= directory.getEntries();
1422+
1423+ for (drizzled::CachedDirectory::Entries::iterator entry_iter= entries.begin(); entry_iter
1424+ != entries.end(); ++entry_iter)
1425+ {
1426+ drizzled::CachedDirectory::Entry *entry= *entry_iter;
1427+ const string *filename= &entry->filename;
1428+
1429+ assert(filename->size());
1430+
1431+ const char *ext= strchr(filename->c_str(), '.');
1432+
1433+ if (ext == NULL || my_strcasecmp(system_charset_info, ext, CLOUD_EXT)
1434+ || (filename->compare(0, strlen(TMP_FILE_PREFIX), TMP_FILE_PREFIX) == 0))
1435+ {
1436+ }
1437+ else
1438+ {
1439+ char uname[NAME_LEN + 1];
1440+ uint32_t file_name_len;
1441+
1442+ file_name_len= TableIdentifier::filename_to_tablename(filename->c_str(), uname,
1443+ sizeof(uname));
1444+ // TODO: Remove need for memory copy here
1445+ uname[file_name_len - sizeof(CLOUD_EXT) + 1]= '\0'; // Subtract ending, place NULL
1446+
1447+ set_of_identifiers.push_back(TableIdentifier(schema_identifier, uname));
1448+ }
1449+ }
1450+}
1451+
1452+int CloudEngine::doDropTable(drizzled::Session&,
1453+ const drizzled::TableIdentifier &identifier)
1454+{
1455+ string new_path(identifier.getPath());
1456+
1457+ new_path+= CLOUD_EXT;
1458+
1459+ int error= unlink(new_path.c_str());
1460+
1461+ if (error != 0)
1462+ {
1463+ error= errno;
1464+ }
1465+
1466+ return error;
1467+}
1468+
1469+CloudShare *CloudEngine::findOpenTable(const std::string &table_name)
1470+{
1471+ CloudMap::iterator find_iter= cloud_open_tables.find(table_name);
1472+
1473+ if (find_iter != cloud_open_tables.end())
1474+ return (*find_iter).second;
1475+ else
1476+ return NULL;
1477+}
1478+
1479+bool CloudEngine::addOpenTable(const std::string &table_name, CloudShare *share)
1480+{
1481+ CloudMap::iterator find_iter= cloud_open_tables.find(table_name);
1482+
1483+ if (find_iter != cloud_open_tables.end())
1484+ return true;
1485+ else
1486+ {
1487+ cloud_open_tables[table_name]= share;
1488+ return false;
1489+ }
1490+}
1491+
1492+void CloudEngine::deleteOpenTable(const std::string &table_name)
1493+{
1494+ cloud_open_tables.erase(table_name);
1495+}
1496+
1497+int CloudEngine::doGetTableDefinition(drizzled::Session&,
1498+ const drizzled::TableIdentifier &identifier,
1499+ drizzled::message::Table &table_proto)
1500+{
1501+ string new_path;
1502+
1503+ new_path= identifier.getPath();
1504+ new_path+= CLOUD_EXT;
1505+
1506+ int fd= open(new_path.c_str(), O_RDONLY);
1507+
1508+ if (fd == -1)
1509+ {
1510+ return errno;
1511+ }
1512+
1513+ google::protobuf::io::ZeroCopyInputStream* input=
1514+ new google::protobuf::io::FileInputStream(fd);
1515+
1516+ if (not input)
1517+ return HA_ERR_CRASHED_ON_USAGE;
1518+
1519+ if (not table_proto.ParseFromZeroCopyStream(input))
1520+ {
1521+ close(fd);
1522+ delete input;
1523+ if (not table_proto.IsInitialized())
1524+ {
1525+ my_error(ER_CORRUPT_TABLE_DEFINITION, MYF(0),
1526+ table_proto.InitializationErrorString().c_str());
1527+ return ER_CORRUPT_TABLE_DEFINITION;
1528+ }
1529+
1530+ return HA_ERR_CRASHED_ON_USAGE;
1531+ }
1532+
1533+ delete input;
1534+ close(fd);
1535+
1536+ return EEXIST;
1537+}
1538+
1539+int CloudEngine::doCreateTable(drizzled::Session&,
1540+ drizzled::Table &table_arg,
1541+ const drizzled::TableIdentifier &identifier,
1542+ drizzled::message::Table &proto)
1543+{
1544+ const drizzled::TableShare::Fields fields(table_arg.getShare()->getFields());
1545+ for (drizzled::TableShare::Fields::const_iterator iter= fields.begin();
1546+ iter != fields.end();
1547+ iter++)
1548+ {
1549+ if (not *iter) // Historical legacy for NULL array end.
1550+ continue;
1551+ }
1552+
1553+ string serialized_proto;
1554+ string new_path;
1555+
1556+ new_path= identifier.getPath();
1557+ new_path+= CLOUD_EXT;
1558+ fstream output(new_path.c_str(), ios::out | ios::binary);
1559+
1560+ if (!output)
1561+ return 1;
1562+
1563+ if (!proto.SerializeToOstream(&output))
1564+ {
1565+ output.close();
1566+ unlink(new_path.c_str());
1567+ return 1;
1568+ }
1569+
1570+ return 0;
1571+}
1572+
1573+ha_cloud::ha_cloud(drizzled::plugin::StorageEngine &engine_arg,
1574+ drizzled::TableShare &table_arg) :
1575+ Cursor(engine_arg, table_arg)
1576+{
1577+ rnd_state_is_have= false;
1578+ key_built_buffer[0]= '\0';
1579+ current_key= key_built_buffer;
1580+ current_key_length= 0;
1581+
1582+ key_quoted[0]= '\0';
1583+}
1584+
1585+ha_cloud::~ha_cloud()
1586+{
1587+}
1588+
1589+CloudShare* ha_cloud::getShare(const char *table_name, drizzled::Table*)
1590+{
1591+ CloudShare* shared;
1592+
1593+ pthread_mutex_lock(&cloud_mutex);
1594+
1595+ if ((shared= cloud_engine->findOpenTable(table_name)) == NULL)
1596+ {
1597+ shared= new CloudShare(table_name);
1598+ if (shared == NULL)
1599+ {
1600+ pthread_mutex_unlock(&cloud_mutex);
1601+ return NULL;
1602+ }
1603+
1604+ shared->use_count= 0;
1605+ shared->table_name= table_name;
1606+
1607+ if (parse_connect_string(shared))
1608+ {
1609+ pthread_mutex_unlock(&cloud_mutex);
1610+ return NULL;
1611+ }
1612+
1613+ if (cloud_engine->addOpenTable(table_name, shared))
1614+ {
1615+ pthread_mutex_destroy(&shared->mutex);
1616+ delete shared;
1617+ }
1618+ thr_lock_init(&shared->lock);
1619+ pthread_mutex_init(&shared->mutex, MY_MUTEX_INIT_FAST);
1620+ }
1621+
1622+ assert(shared != NULL);
1623+ shared->use_count++;
1624+ pthread_mutex_unlock(&cloud_mutex);
1625+ return shared;
1626+}
1627+
1628+int ha_cloud::freeShare(CloudShare *shared)
1629+{
1630+ pthread_mutex_lock(&cloud_mutex);
1631+ if (!--shared->use_count)
1632+ {
1633+ cloud_engine->deleteOpenTable(shared->table_name);
1634+ shared->lock.deinit();
1635+ pthread_mutex_destroy(&shared->mutex);
1636+
1637+ delete shared;
1638+ }
1639+ pthread_mutex_unlock(&cloud_mutex);
1640+
1641+ return 0;
1642+}
1643+
1644+int ha_cloud::open(const char *name, int mode, uint test_if_locked)
1645+{
1646+ (void) mode;
1647+ (void) test_if_locked;
1648+
1649+ if (!(share= getShare(name, table)))
1650+ return (1);
1651+
1652+ lock.init(&share->lock);
1653+
1654+ // size of a length plus max size of a key
1655+ ref_length= sizeof(size_t) + MAX_CLOUD_SERVICE_KEY_LENGTH;
1656+
1657+ in_table_scan= false;
1658+ onlyPrimaryKeyFiledNeeded= false;
1659+ http_response_code= 0;
1660+
1661+ return (0);
1662+}
1663+
1664+int ha_cloud::close(void)
1665+{
1666+ return (freeShare(share));
1667+}
1668+
1669+int ha_cloud::parse_connect_string(CloudShare *shared)
1670+{
1671+ assert(table && shared);
1672+
1673+ string service;
1674+ string bucket;
1675+ string keyID;
1676+ string keyValue;
1677+ size_t num_engine_options= table->s->getTableProto()->engine().options_size();
1678+
1679+ for (size_t y= 0; y < num_engine_options; ++y)
1680+ {
1681+ string name= table->s->getTableProto()->engine().options(y).name();
1682+ string value= table->s->getTableProto()->engine().options(y).state();
1683+
1684+ if (boost::iequals(name, BUCKET_OPTION))
1685+ bucket= value;
1686+ else if (boost::iequals(name, KEY_ID_OPTION))
1687+ keyID= value;
1688+ else if (boost::iequals(name, KEY_OPTION))
1689+ keyValue= value;
1690+ else if (boost::iequals(name, SERVICE_OPTION))
1691+ service= value;
1692+ }
1693+
1694+ // use table name as default bucket name
1695+ if (bucket.empty())
1696+ bucket= string(table->s->getTableName(),table->s->getTableNameSize());
1697+
1698+ if (boost::iequals(service, AWSS3))
1699+ shared->storageService= new AWSS3Service(bucket);
1700+
1701+ if (shared->storageService == NULL)
1702+ return (1);
1703+
1704+ shared->storageService->setKeyID(keyID);
1705+ shared->storageService->setKeyValue(keyValue);
1706+
1707+ return (0);
1708+}
1709+
1710+/*
1711+ Encode a buffer into the quoted format.
1712+*/
1713+
1714+int ha_cloud::encode_quote(unsigned char *)
1715+{
1716+ String attribute;
1717+
1718+ buffer.clear();
1719+ buffer.push_back('{');
1720+
1721+ Field *key_field= getPrimaryKeyField();
1722+
1723+ for (Field **field= table->getFields() ; *field ; field++)
1724+ {
1725+ const char *ptr;
1726+ const char *end_ptr;
1727+
1728+ if (*field == key_field)
1729+ continue;
1730+
1731+ buffer.append((*field)->field_name);
1732+ buffer.push_back(':');
1733+
1734+ if ((*field)->is_null())
1735+ {
1736+ (*field)->set_null();
1737+ buffer.append("null");
1738+ buffer.push_back(',');
1739+ continue;
1740+ }
1741+
1742+ (*field)->setReadSet();
1743+ (*field)->val_str(&attribute,&attribute);
1744+
1745+ if ((*field)->str_needs_quotes())
1746+ {
1747+ ptr= attribute.ptr();
1748+ end_ptr= attribute.length() + ptr;
1749+
1750+ buffer.push_back('"');
1751+
1752+ while (ptr < end_ptr)
1753+ {
1754+ if (*ptr == '"')
1755+ {
1756+ buffer.push_back('\\');
1757+ buffer.push_back('"');
1758+ *ptr++;
1759+ }
1760+ else if (*ptr == '\r')
1761+ {
1762+ buffer.push_back('\\');
1763+ buffer.push_back('r');
1764+ *ptr++;
1765+ }
1766+ else if (*ptr == '\\')
1767+ {
1768+ buffer.push_back('\\');
1769+ buffer.push_back('\\');
1770+ *ptr++;
1771+ }
1772+ else if (*ptr == '\n')
1773+ {
1774+ buffer.push_back('\\');
1775+ buffer.push_back('n');
1776+ *ptr++;
1777+ }
1778+ else
1779+ buffer.push_back(*ptr++);
1780+ }
1781+ buffer.push_back('"');
1782+ }
1783+ else
1784+ {
1785+ buffer.append(string(attribute.ptr(), attribute.length()));
1786+ }
1787+
1788+ buffer.push_back(',');
1789+ }
1790+ // Remove the comma
1791+ if (!buffer.empty())
1792+ buffer.erase(buffer.length() - 1);
1793+
1794+ buffer.push_back('}');
1795+
1796+ return (buffer.length());
1797+}
1798+
1799+/*
1800+ fill a row based on a csv string.
1801+*/
1802+int ha_cloud::fill_row(const std::string &row, unsigned char *buf)
1803+{
1804+ const char *end_offset= row.c_str() + row.length();
1805+ const char *curr_offset= row.c_str();
1806+ int error;
1807+ bool quoted= false;
1808+ Field *key_field= getPrimaryKeyField();
1809+ Field **field=table->getFields();
1810+
1811+ blobroot.free_root(MYF(drizzled::memory::MARK_BLOCKS_FREE));
1812+
1813+ // invalid json string
1814+ if (*curr_offset != '{' || *(end_offset - 1) != '}')
1815+ goto err;
1816+
1817+ curr_offset++; // skip '{'
1818+ end_offset--; // ignore '}'
1819+ memset(buf, 0, table->getShare()->null_bytes);
1820+
1821+ for (; *field; field++)
1822+ {
1823+ char curr_char;
1824+
1825+ if (*field == key_field)
1826+ continue;
1827+
1828+ quoted= false;
1829+ buffer.clear();
1830+ if (curr_offset >= end_offset)
1831+ goto err;
1832+ // skip filed name
1833+ size_t field_name_len= strlen((*field)->field_name);
1834+
1835+
1836+ if (strncmp((*field)->field_name, curr_offset, field_name_len) != 0)
1837+ goto err;
1838+ curr_offset+= field_name_len;
1839+ curr_char= *curr_offset;
1840+ // skip ':'
1841+ if (curr_char != ':')
1842+ goto err;
1843+ curr_char= *(++curr_offset);
1844+
1845+ if (curr_char == '"')
1846+ {
1847+ quoted= true;
1848+ curr_offset++; // Incrementpast the first quote
1849+
1850+ for(; curr_offset < end_offset; curr_offset++)
1851+ {
1852+ curr_char= *curr_offset;
1853+ if (curr_char == '"' &&
1854+ (curr_offset == end_offset - 1 ||
1855+ *(curr_offset + 1) == ','))
1856+ {
1857+ curr_offset+= 2; // Move past the , and "
1858+ break;
1859+ }
1860+ if (curr_char == '\\' && curr_offset != (end_offset - 1))
1861+ {
1862+ curr_offset++;
1863+ curr_char= *curr_offset;
1864+ if (curr_char == 'r')
1865+ buffer.push_back('\r');
1866+ else if (curr_char == 'n' )
1867+ buffer.push_back('\n');
1868+ else if (curr_char == '\\' || curr_char == '"')
1869+ buffer.push_back(curr_char);
1870+ else /* This could only happed with an externally created file */
1871+ {
1872+ buffer.push_back('\\');
1873+ buffer.push_back(curr_char);
1874+ }
1875+ }
1876+ else // ordinary symbol
1877+ {
1878+ if (curr_offset == end_offset - 1)
1879+ goto err; // should end with '"'
1880+ buffer.push_back(curr_char);
1881+ }
1882+ }
1883+ }
1884+ else
1885+ {
1886+ for(; curr_offset < end_offset; curr_offset++)
1887+ {
1888+ curr_char= *curr_offset;
1889+ if (curr_char == ',')
1890+ {
1891+ curr_offset++; // Skip the ,
1892+ break;
1893+ }
1894+ buffer.push_back(curr_char);
1895+ }
1896+ }
1897+
1898+ // the field is null
1899+ if (!quoted && strncmp(buffer.c_str(), "null", 4) == 0)
1900+ {
1901+ (*field)->set_null();
1902+ continue;
1903+ }
1904+
1905+ (*field)->setWriteSet();
1906+ if ((*field)->store(buffer.c_str(), buffer.length(), &drizzled::my_charset_bin))
1907+ goto err;
1908+
1909+ if ((*field)->flags & BLOB_FLAG)
1910+ {
1911+ Field_blob *blob= *(Field_blob**) field;
1912+ unsigned char *src, *tgt;
1913+ uint32_t length, packlength;
1914+
1915+ packlength= blob->pack_length_no_ptr();
1916+ length= blob->get_length(blob->ptr);
1917+ memcpy(&src, blob->ptr + packlength, sizeof(char*));
1918+ if (src)
1919+ {
1920+ tgt= (unsigned char*) blobroot.alloc_root(length);
1921+ memmove(tgt, src, length);
1922+ memcpy(blob->ptr + packlength, &tgt, sizeof(char*));
1923+ }
1924+ }
1925+ }
1926+ error= 0;
1927+
1928+err:
1929+ for (; *field; field++)
1930+ {
1931+ if ((*field)->is_null())
1932+ (*field)->set_null();
1933+ else
1934+ (*field)->set_default();
1935+ }
1936+
1937+ return(error);
1938+}
1939+
1940+int ha_cloud::drizzle_error_code()
1941+{
1942+ int rc= 0;
1943+
1944+ if (http_response_code == 0)
1945+ rc= 0;
1946+ else if (http_response_code == -1)
1947+ rc= drizzled::ER_CONNECT_TO_FOREIGN_DATA_SOURCE;
1948+ else if (http_response_code == 401)
1949+ rc= drizzled::ER_PASSWORD_ANONYMOUS_USER;
1950+ else if (http_response_code == 403)
1951+ rc= drizzled::ER_PASSWORD_NOT_ALLOWED;
1952+ else if (http_response_code == 404)
1953+ rc= HA_ERR_END_OF_FILE;
1954+ else if ((http_response_code < 200) || (http_response_code > 299))
1955+ rc= drizzled::ER_UNKNOWN_ERROR;
1956+
1957+ return(rc);
1958+}
1959+
1960+inline drizzled::Field* ha_cloud::getPrimaryKeyField()
1961+{
1962+ return table->key_info[table->s->getPrimaryKey()].key_part->field;
1963+}
1964+
1965+int ha_cloud::doInsertRecord(unsigned char* buf)
1966+{
1967+ uint rc= 0;
1968+
1969+ drizzled::Field *key_field= getPrimaryKeyField();
1970+ make_key(key_field);
1971+
1972+ encode_quote(buf);
1973+ http_response_code= share->storageService->putItem(key_quoted,
1974+ buffer.c_str(), buffer.size());
1975+ rc= drizzle_error_code();
1976+
1977+ return(rc);
1978+}
1979+
1980+int ha_cloud::doUpdateRecord(const unsigned char*, unsigned char *new_data)
1981+{
1982+ int rc= 0;
1983+
1984+ encode_quote(new_data);
1985+
1986+ // We need to save our old key to make sure that the primary key has
1987+ // not been updated if it has we will need to delete first.
1988+ char prev_key[1024 + 1];
1989+ memcpy(prev_key, current_key, current_key_length);
1990+ size_t prev_key_length= current_key_length;
1991+
1992+ // now grab the new key out of table, and stuff it into current_key
1993+ drizzled::Field *key_field= getPrimaryKeyField();
1994+ make_key(key_field);
1995+
1996+ if (current_key_length != prev_key_length || memcmp(current_key, prev_key,
1997+ current_key_length))
1998+ {
1999+ if (onlyPrimaryKeyFiledNeeded)
2000+ {
2001+ // if onlyPrimaryKeyFiledNeeded, row buffer will be empty.
2002+ // we need first fetch the itme content, since s3
2003+ // do not provide a method to update item key
2004+ std::string item_content;
2005+
2006+ str_percent(key_quoted, sizeof(key_quoted),
2007+ (const unsigned char *) prev_key, prev_key_length, "/");
2008+ http_response_code= share->storageService->getItem(key_quoted, &item_content);
2009+ rc= drizzle_error_code();
2010+
2011+ if (rc != 0)
2012+ return rc;
2013+
2014+ // now set item for the new key as the old item just fetched
2015+ str_percent(key_quoted, sizeof(key_quoted),
2016+ (const unsigned char *) current_key, current_key_length, "/");
2017+ http_response_code= share->storageService->putItem(key_quoted,
2018+ item_content.c_str(),item_content.size());
2019+ }
2020+ else
2021+ http_response_code= share->storageService->putItem(key_quoted,
2022+ buffer.c_str(), buffer.size());
2023+
2024+ rc= drizzle_error_code();
2025+
2026+ if (rc != 0)
2027+ return rc;
2028+
2029+ // delete based on prev_key, prev_key_length
2030+ str_percent(key_quoted, sizeof(key_quoted),
2031+ (const unsigned char *) prev_key, prev_key_length, "/");
2032+ http_response_code= share->storageService->deleteItem(key_quoted);
2033+ rc= drizzle_error_code();
2034+ }
2035+ else
2036+ {
2037+ http_response_code= share->storageService->putItem(key_quoted,
2038+ buffer.c_str(), buffer.size());
2039+ rc= drizzle_error_code();
2040+ }
2041+
2042+ return (rc);
2043+}
2044+
2045+int ha_cloud::doDeleteRecord(const unsigned char*)
2046+{
2047+ uint rc= 0;
2048+
2049+ drizzled::Field *key_field= getPrimaryKeyField();
2050+ make_key(key_field);
2051+
2052+ http_response_code= share->storageService->deleteItem(key_quoted);
2053+ rc= drizzle_error_code();
2054+
2055+ return (rc);
2056+}
2057+
2058+int ha_cloud::doStartIndexScan(uint idx, bool sorted)
2059+{
2060+ (void)sorted;
2061+ active_index= idx;
2062+
2063+ onlyPrimaryKeyFiledNeeded= true;
2064+ blobroot.init_alloc_root(BLOB_MEMROOT_ALLOC_SIZE);
2065+
2066+ drizzled::Field *key_field= getPrimaryKeyField();
2067+ for (Field **field=table->getFields(); *field; field++)
2068+ {
2069+ if (*field != key_field && ((*field)->isReadSet() || (*field)->isWriteSet()))
2070+ {
2071+ onlyPrimaryKeyFiledNeeded= false;
2072+ break;
2073+ }
2074+ }
2075+
2076+ return 0;
2077+}
2078+
2079+int ha_cloud::doEndIndexScan()
2080+{
2081+ onlyPrimaryKeyFiledNeeded= false;
2082+ blobroot.free_root(MYF(0));
2083+
2084+ return 0;
2085+}
2086+
2087+int ha_cloud::index_read(unsigned char* buf, const unsigned char* key,
2088+ uint key_len, drizzled::ha_rkey_function)
2089+{
2090+ uint rc= 0;
2091+
2092+ drizzled::Field *key_field= getPrimaryKeyField();
2093+ key_field->set_key_image(key, key_len);
2094+ key_field->set_notnull();
2095+
2096+ find_row(buf, key_field);
2097+ rc= drizzle_error_code();
2098+
2099+ return (rc);
2100+}
2101+
2102+void ha_cloud::rnd_state_dealloc(void)
2103+{
2104+ rnd_state_is_have= false;
2105+ rnd_state_is_trunc= false;
2106+ rnd_state_itemkeys.clear();
2107+ rnd_state_ndx= 0;
2108+}
2109+
2110+void ha_cloud::update_rnd_state(BucketInfo *itemList)
2111+{
2112+ if (itemList)
2113+ {
2114+ rnd_state_is_trunc= itemList->isTrucated;
2115+ rnd_state_itemkeys= itemList->keys;
2116+ rnd_state_is_have= true;
2117+ }
2118+}
2119+
2120+int ha_cloud::bucket_look(void)
2121+{
2122+ uint rc= 0;
2123+ string bucket_info;
2124+
2125+ // reset rnd_state_ndx for each bucket_look
2126+ rnd_state_ndx= 0;
2127+
2128+ if (rnd_state_is_have)
2129+ {
2130+ if (rnd_state_is_trunc)
2131+ {
2132+ // there is more to read
2133+ rnd_state_is_trunc= false;
2134+ char key_marker[MAX_CLOUD_SERVICE_KEY_LENGTH + 1]= {0};
2135+
2136+ str_percent(key_marker, sizeof(key_marker),
2137+ (const unsigned char *) rnd_state_itemkeys.back().c_str(),
2138+ rnd_state_itemkeys.back().length(), "/");
2139+
2140+ string marker= string("marker=") + string(key_marker);
2141+ http_response_code= share->storageService->getBucket(&bucket_info, 2, marker.c_str());
2142+
2143+ rc= drizzle_error_code();
2144+ if (rc)
2145+ return rc;
2146+
2147+ BucketInfo *itemList= share->storageService->parseBucketInfo(bucket_info);
2148+ if (itemList == NULL)
2149+ return (1);
2150+
2151+ update_rnd_state(itemList);
2152+ delete itemList;
2153+ }
2154+ else // no more keys
2155+ rnd_state_dealloc();
2156+ }
2157+ else
2158+ {
2159+ // we're starting a bucket key scan
2160+ http_response_code= share->storageService->getBucket(&bucket_info);
2161+
2162+ if (http_response_code != 200)
2163+ return drizzle_error_code();
2164+
2165+ BucketInfo *itemList= share->storageService->parseBucketInfo(bucket_info);
2166+ if (itemList == NULL)
2167+ return (1);
2168+
2169+ update_rnd_state(itemList);
2170+ delete itemList;
2171+ }
2172+
2173+ return (0);
2174+}
2175+
2176+int ha_cloud::doStartTableScan(bool)
2177+{
2178+ int rc= 0;
2179+
2180+ if (in_table_scan)
2181+ doEndTableScan();
2182+
2183+ in_table_scan= true;
2184+ onlyPrimaryKeyFiledNeeded= true;
2185+ blobroot.init_alloc_root(BLOB_MEMROOT_ALLOC_SIZE);
2186+
2187+ drizzled::Field *key_field= getPrimaryKeyField();
2188+ for (Field **field=table->getFields(); *field; field++)
2189+ {
2190+ if (*field != key_field && ((*field)->isReadSet() || (*field)->isWriteSet()))
2191+ {
2192+ onlyPrimaryKeyFiledNeeded= false;
2193+ break;
2194+ }
2195+ }
2196+
2197+ rc= bucket_look();
2198+
2199+ if (rc)
2200+ rnd_state_dealloc();
2201+
2202+ return (rc);
2203+}
2204+
2205+int ha_cloud::doEndTableScan()
2206+{
2207+ blobroot.free_root(MYF(0));
2208+ rnd_state_dealloc();
2209+ in_table_scan= false;
2210+ onlyPrimaryKeyFiledNeeded= false;
2211+
2212+ return (0);
2213+}
2214+
2215+int ha_cloud::rnd_next(unsigned char* buf)
2216+{
2217+ while (true)
2218+ {
2219+ if (rnd_state_ndx == (int)rnd_state_itemkeys.size())
2220+ {
2221+ if (!rnd_state_is_trunc)
2222+ return (HA_ERR_END_OF_FILE);
2223+
2224+ // keys are truncated, bucket_look() again
2225+ if (bucket_look())
2226+ return (HA_ERR_END_OF_FILE);
2227+
2228+ // no keys anymore
2229+ if (rnd_state_itemkeys.empty())
2230+ return (HA_ERR_END_OF_FILE);
2231+ }
2232+
2233+ drizzled::Field *key_field= getPrimaryKeyField();
2234+ key_field->setWriteSet();
2235+ key_field->store(rnd_state_itemkeys[rnd_state_ndx].c_str(),
2236+ rnd_state_itemkeys[rnd_state_ndx].length(),
2237+ &drizzled::my_charset_bin);
2238+ key_field->set_notnull();
2239+
2240+ rnd_state_ndx++;
2241+
2242+ if (!find_row(buf, key_field))
2243+ break;
2244+ }
2245+
2246+ return (0);
2247+}
2248+
2249+void ha_cloud::position(const unsigned char*)
2250+{
2251+ drizzled::Field *key_field= getPrimaryKeyField();
2252+ unsigned int key_length= key_field->data_length();
2253+
2254+ // ref and ref_length are inherited members of this class
2255+ memcpy(ref, &key_length, sizeof(unsigned int));
2256+ memcpy(ref + sizeof(unsigned int), key_field->ptr, key_length);
2257+}
2258+
2259+int ha_cloud::rnd_pos(unsigned char* buf, unsigned char* pos)
2260+{
2261+ drizzled::Field *key_field= getPrimaryKeyField();
2262+
2263+ uint32_t key_length;
2264+ memcpy(&key_length, pos, sizeof(unsigned int));
2265+ key_field->store((char*) pos + sizeof(unsigned int), key_length,
2266+ &drizzled::my_charset_bin);
2267+
2268+ find_row(buf, key_field);
2269+
2270+ return drizzle_error_code();
2271+}
2272+
2273+void ha_cloud::make_key(drizzled::Field *primary)
2274+{
2275+ char *end_ptr= key_built_buffer;
2276+
2277+ char attribute_buffer[1024];
2278+ drizzled::String attribute(attribute_buffer, sizeof(attribute_buffer),
2279+ &drizzled::my_charset_bin);
2280+
2281+ // key_build_buffer is a member of this class
2282+ // current_key is a member of this class
2283+ current_key= key_built_buffer;
2284+
2285+ primary->setReadSet();
2286+
2287+ // magically transform the primary key data into a string
2288+ // no really, magic, have brian explain this method to me one more time
2289+ primary->val_str(&attribute, &attribute);
2290+
2291+ // copy that string data into key_built_buffer (pointed to by end_ptr)
2292+ // key_built_buffer is a member of this class
2293+ memcpy(end_ptr, attribute.ptr(), attribute.length());
2294+ end_ptr+= attribute.length();
2295+
2296+ // current_key_length is a member of this class
2297+ current_key_length= (size_t) (end_ptr - key_built_buffer);
2298+
2299+ // make URL quoted version of the current key
2300+ str_percent(key_quoted, sizeof(key_quoted),
2301+ (const unsigned char *) current_key, current_key_length, "/");
2302+}
2303+
2304+int ha_cloud::find_row(unsigned char *buf, drizzled::Field *primary)
2305+{
2306+ int rc= 0;
2307+ make_key(primary);
2308+
2309+ // if only primary key is needed, no need to request cloud service
2310+ if (onlyPrimaryKeyFiledNeeded)
2311+ {
2312+ http_response_code= 0;
2313+ return rc;
2314+ }
2315+
2316+ string item_content;
2317+ http_response_code= share->storageService->getItem(key_quoted, &item_content);
2318+
2319+ if ((http_response_code >= 200) && (http_response_code < 300))
2320+ rc= fill_row(item_content, buf);
2321+ else
2322+ rc= drizzle_error_code();
2323+
2324+ return rc;
2325+}
2326+
2327+int ha_cloud::info(uint)
2328+{
2329+ if (stats.records < 2)
2330+ stats.records= 2;
2331+
2332+ return (0);
2333+}
2334+
2335+static int cloud_init_func(drizzled::module::Context &context)
2336+{
2337+ cloud_engine= new CloudEngine("CLOUD_STORAGE");
2338+ context.add(cloud_engine);
2339+
2340+ HTTP::ConnectionFactory::init();
2341+
2342+ pthread_mutex_init(&cloud_mutex, MY_MUTEX_INIT_FAST);
2343+ return 0;
2344+}
2345+
2346+DRIZZLE_DECLARE_PLUGIN {
2347+ DRIZZLE_VERSION_ID,
2348+ "CLOUD_STORAGE",
2349+ "1.0",
2350+ "Wei Ye",
2351+ "cloud storage engine",
2352+ drizzled::PLUGIN_LICENSE_GPL,
2353+ cloud_init_func, /* Plugin Init */
2354+ NULL, /* system variables */
2355+ NULL /* config options */
2356+}
2357+
2358+DRIZZLE_DECLARE_PLUGIN_END;
2359
2360=== added file 'plugin/cloud_storage/ha_cloud.h'
2361--- plugin/cloud_storage/ha_cloud.h 1970-01-01 00:00:00 +0000
2362+++ plugin/cloud_storage/ha_cloud.h 2010-08-19 16:21:41 +0000
2363@@ -0,0 +1,148 @@
2364+/* Copyright (C) 2010 Wei Ye
2365+
2366+ This program is free software; you can redistribute it and/or modify
2367+ it under the terms of the GNU General Public License as published by
2368+ the Free Software Foundation; version 2 of the License.
2369+
2370+ This program is distributed in the hope that it will be useful,
2371+ but WITHOUT ANY WARRANTY; without even the implied warranty of
2372+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2373+ GNU General Public License for more details.
2374+
2375+ You should have received a copy of the GNU General Public License
2376+ along with this program; if not, write to the Free Software
2377+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
2378+
2379+
2380+#ifndef PLUGIN_CLOUD_HA_CLOUD_H
2381+#define PLUGIN_CLOUD_HA_CLOUD_H
2382+
2383+#include <drizzled/table_share.h>
2384+#include <drizzled/cursor.h>
2385+#include <drizzled/thr_lock.h>
2386+#include <drizzled/sql_string.h>
2387+
2388+#include <sys/types.h>
2389+#include <sys/stat.h>
2390+
2391+#include <string>
2392+#include <vector>
2393+
2394+static const int CLOUD_VERSION= 1;
2395+static const int MAX_CLOUD_SERVICE_KEY_LENGTH= 1024;
2396+static const int MAX_PRIMARY_KEY_LENGTH= MAX_CLOUD_SERVICE_KEY_LENGTH * 4 / 3;
2397+
2398+class CloudStorageService;
2399+class BucketInfo;
2400+
2401+class CloudShare
2402+{
2403+ CloudShare();
2404+ CloudShare(const CloudShare &);
2405+ CloudShare& operator=(const CloudShare &);
2406+public:
2407+ explicit CloudShare(const char *name);
2408+ ~CloudShare();
2409+
2410+ std::string table_name;
2411+ uint32_t use_count;
2412+ CloudStorageService *storageService;
2413+ pthread_mutex_t mutex;
2414+ drizzled::THR_LOCK lock;
2415+};
2416+
2417+class ha_cloud: public drizzled::Cursor
2418+{
2419+private:
2420+ drizzled::THR_LOCK_DATA lock; /* Drizzle lock */
2421+ CloudShare *share; /* Shared lock info */
2422+
2423+ // an key can be up to 1024 bytes long
2424+ char key_built_buffer[MAX_CLOUD_SERVICE_KEY_LENGTH + 1];
2425+ char *current_key;
2426+ size_t current_key_length;
2427+ char key_quoted[MAX_CLOUD_SERVICE_KEY_LENGTH + 1];
2428+
2429+ // state for a table scan
2430+ bool rnd_state_is_have;
2431+ bool rnd_state_is_trunc;
2432+ int rnd_state_ndx;
2433+ std::vector<std::string> rnd_state_itemkeys;
2434+ bool in_table_scan;
2435+ bool onlyPrimaryKeyFiledNeeded;
2436+
2437+ long http_response_code;
2438+
2439+ drizzled::memory::Root blobroot;
2440+ std::string buffer;
2441+
2442+private:
2443+ void update_rnd_state(BucketInfo *bucket_info);
2444+ void rnd_state_dealloc(void);
2445+
2446+ int bucket_look(void);
2447+
2448+ int drizzle_error_code();
2449+
2450+ inline drizzled::Field* getPrimaryKeyField();
2451+ void make_key(drizzled::Field *primary);
2452+
2453+ int parse_connect_string (CloudShare *shared);
2454+ int open_connection(void);
2455+
2456+ int encode_quote(unsigned char *);
2457+ int fill_row(const std::string &row, unsigned char *buf);
2458+ int find_row(unsigned char *buf, drizzled::Field *primary);
2459+
2460+public:
2461+ ha_cloud(drizzled::plugin::StorageEngine &engine, drizzled::TableShare &table_arg);
2462+ ~ha_cloud();
2463+
2464+ /** @brief
2465+ The name that will be used for display purposes.
2466+ */
2467+ const char *table_type() const { return "CLOUD"; }
2468+
2469+ /*
2470+ Called in test_quick_select to determine if indexes should be used.
2471+ */
2472+ virtual double scan_time() { return (double) (stats.records+stats.deleted) / 20.0+10; }
2473+
2474+ uint32_t index_flags(uint inx, uint part, bool all_parts) const
2475+ {
2476+ (void)inx;
2477+ (void)part;
2478+ (void)all_parts;
2479+
2480+ return HA_ONLY_WHOLE_INDEX;
2481+ }
2482+
2483+ const char *index_type(uint inx) { (void)inx; return ("HASH"); }
2484+ int doStartIndexScan(uint idx, bool sorted);
2485+ int index_read(unsigned char* buf, const unsigned char* key, uint key_len,
2486+ drizzled::ha_rkey_function find_flag);
2487+ int doEndIndexScan();
2488+
2489+ int open(const char *name, int mode, uint test_if_locked);
2490+ int close(void);
2491+ CloudShare* getShare(const char* table_name, drizzled::Table* table);
2492+ int freeShare( CloudShare* share);
2493+
2494+ int info(uint);
2495+ int doStartTableScan(bool scan);
2496+ int rnd_next(unsigned char *buf);
2497+ void position(const unsigned char *record);
2498+ int rnd_pos(unsigned char * buf, unsigned char *pos);
2499+ int doEndTableScan();
2500+
2501+ int doInsertRecord(unsigned char *buf);
2502+ int doDeleteRecord(const unsigned char *buf);
2503+ int doUpdateRecord(const unsigned char *old_data, unsigned char *new_data);
2504+
2505+ void get_auto_increment(uint64_t, uint64_t,
2506+ uint64_t,
2507+ uint64_t *,
2508+ uint64_t *) {}
2509+};
2510+
2511+#endif /* PLUGIN_CLOUD_HA_CLOUD_H */
2512
2513=== added file 'plugin/cloud_storage/plugin.ac'
2514--- plugin/cloud_storage/plugin.ac 1970-01-01 00:00:00 +0000
2515+++ plugin/cloud_storage/plugin.ac 2010-08-19 16:21:41 +0000
2516@@ -0,0 +1,3 @@
2517+PANDORA_HAVE_LIBXML2
2518+PANDORA_HAVE_LIBCURL
2519+PANDORA_HAVE_LIBGCRYPT
2520
2521=== added file 'plugin/cloud_storage/plugin.ini'
2522--- plugin/cloud_storage/plugin.ini 1970-01-01 00:00:00 +0000
2523+++ plugin/cloud_storage/plugin.ini 2010-08-19 16:21:41 +0000
2524@@ -0,0 +1,8 @@
2525+[plugin]
2526+title=Cloud Storage Engine
2527+description=Cloud Storage Engine
2528+headers=ha_cloud.h str_percent.h curl.h cloud_storage_service.h awss3_service.h base64.h
2529+sources=ha_cloud.cc str_percent.cc curl.cc awss3_service.cc base64.cc
2530+load_by_default=yes
2531+build_conditional="x${ac_cv_libxml2}" = "xyes" -a "x${ac_cv_libcurl}" = "xyes"
2532+ldflags=${LTLIBXML2} ${LTLIBCURL} ${LTLIBGCRYPT}
2533
2534=== added file 'plugin/cloud_storage/str_percent.cc'
2535--- plugin/cloud_storage/str_percent.cc 1970-01-01 00:00:00 +0000
2536+++ plugin/cloud_storage/str_percent.cc 2010-08-19 16:21:41 +0000
2537@@ -0,0 +1,67 @@
2538+/* str_percent.cc -- create a C backslashed escaped string
2539+
2540+ Copyright (C) 2007 by Mark Atwood <me@mark.atwood.name>
2541+
2542+ This program is free software; you can redistribute it and/or modify
2543+ it under the terms of the GNU General Public License as published by
2544+ the Free Software Foundation; either version 2, or (at your option)
2545+ any later version.
2546+
2547+ This program is distributed in the hope that it will be useful,
2548+ but WITHOUT ANY WARRANTY; without even the implied warranty of
2549+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2550+ GNU General Public License for more details.
2551+
2552+ You should have received a copy of the GNU General Public License
2553+ along with this program; if not, write to the Free Software Foundation,
2554+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
2555+
2556+#include <string.h>
2557+#include "str_percent.h"
2558+
2559+char *str_percent (char *dst, size_t dstlen,
2560+ const unsigned char *src, size_t srclen,
2561+ const char *alsosafe)
2562+{
2563+ static char hexit[]= { '0', '1', '2', '3', '4', '5', '6', '7',
2564+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
2565+ size_t i; /* index down the dst */
2566+ size_t n; /* index down the src */
2567+
2568+ // gcc generate Wlogical-op warns about strchr
2569+ // here we simulate strchr by a scan
2570+ size_t j;
2571+ size_t len= strlen(alsosafe);
2572+ const char *target;
2573+
2574+ for (i=0,n=0; n<srclen; n++) {
2575+
2576+ target= NULL;
2577+ if (src[n]) {
2578+ for (j= 0; j < len; j++) {
2579+ if (alsosafe[j] == src[n]) {
2580+ target= alsosafe + j;
2581+ break;
2582+ }
2583+ }
2584+ }
2585+
2586+ /* Worst case, need 4 dst bytes for the next src byte.
2587+ percent hexit hexit null */
2588+ if ((dstlen - i) < 4) { dst[i]= '\0'; return dst; }
2589+
2590+ if (((src[n] >= 'A') && (src[n] <= 'Z'))
2591+ || ((src[n] >= 'a') && (src[n] <= 'z'))
2592+ || ((src[n] >= '0') && (src[n] <= '9'))) {
2593+ dst[i++]= src[n];
2594+ } else if ((src[n]) && (target != NULL)) {
2595+ dst[i++]= src[n];
2596+ } else {
2597+ dst[i++]= '%';
2598+ dst[i++]= hexit[(src[n] >> 4) & 0x0f];
2599+ dst[i++]= hexit[src[n] & 0x0f];
2600+ }
2601+ dst[i]= '\0';
2602+ }
2603+ return dst;
2604+}
2605
2606=== added file 'plugin/cloud_storage/str_percent.h'
2607--- plugin/cloud_storage/str_percent.h 1970-01-01 00:00:00 +0000
2608+++ plugin/cloud_storage/str_percent.h 2010-08-19 16:21:41 +0000
2609@@ -0,0 +1,36 @@
2610+/* str_percent.h -- create a C backslashed escaped string
2611+
2612+ Copyright (C) 2007 by Mark Atwood <me@mark.atwood.name>
2613+
2614+ This program is free software; you can redistribute it and/or modify
2615+ it under the terms of the GNU General Public License as published by
2616+ the Free Software Foundation; either version 2, or (at your option)
2617+ any later version.
2618+
2619+ This program is distributed in the hope that it will be useful,
2620+ but WITHOUT ANY WARRANTY; without even the implied warranty of
2621+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2622+ GNU General Public License for more details.
2623+
2624+ You should have received a copy of the GNU General Public License
2625+ along with this program; if not, write to the Free Software Foundation,
2626+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
2627+
2628+#ifndef _INCLUDE_STRPERCENT
2629+#define _INCLUDE_STRPERCENT
2630+
2631+/* Copy src into dst, converting it a C style backslashed escaped
2632+ string. All control characters, high bit characters, the ASCII
2633+ NULL, and all whitespace except the space character itself will be
2634+ converted. The backslash and the quotation characters will be
2635+ escaped as well. If the dst buffer isn't long enough, as much as
2636+ will fit will be done. The dst buffer will be null terminated,
2637+ so it can safely be treated as a C string. The dst buffer will
2638+ also be returned.
2639+*/
2640+
2641+extern char *str_percent (char *dst, size_t dstlen,
2642+ const char unsigned *src, size_t srclen,
2643+ const char *alsosafe);
2644+
2645+#endif /* ndef _INCLUDE_STRPERCENT */
2646
2647=== added directory 'plugin/cloud_storage/tests'
2648=== added directory 'plugin/cloud_storage/tests/r'
2649=== added file 'plugin/cloud_storage/tests/r/basic_create_table.result'
2650--- plugin/cloud_storage/tests/r/basic_create_table.result 1970-01-01 00:00:00 +0000
2651+++ plugin/cloud_storage/tests/r/basic_create_table.result 2010-08-19 16:21:41 +0000
2652@@ -0,0 +1,19 @@
2653+DROP TABLE IF EXISTS t1;
2654+CREATE TABLE t1 (a varchar(20), b int not null) ENGINE=cloud_storage;
2655+ERROR 42000: This table type requires a primary key
2656+CREATE TABLE t1 (a varchar(20), b int not null, constraint c primary key (a, b)) ENGINE=cloud_storage;
2657+ERROR 42000: Too many key parts specified; max 1 parts allowed
2658+CREATE TABLE t1 (a varchar(20) primary key, b int not null) ENGINE=cloud_storage SERVICE=unknow;
2659+ERROR HY000: Unknown table engine option key/pair SERVICE = unknow.
2660+CREATE TABLE t1 (a varchar(342) primary key, b int not null) ENGINE=cloud_storage;
2661+ERROR 42000: Specified key was too long; max key length is 1365 bytes
2662+CREATE TABLE t1 (a varchar(20) primary key, b int not null) ENGINE=cloud_storage;
2663+CREATE TABLE t2 (a varchar(20) primary key,b int not null, c blob not null) ENGINE=cloud_storage;
2664+CREATE TABLE t3 (a varchar(341) primary key) ENGINE=cloud_storage;
2665+CREATE TABLE t4 (a int primary key) ENGINE=cloud_storage;
2666+CREATE TABLE t5 (a int primary key) ENGINE=cloud_storage SERVICE=awss3;
2667+DROP TABLE t1;
2668+DROP TABLE t2;
2669+DROP TABLE t3;
2670+DROP TABLE t4;
2671+DROP TABLE t5;
2672
2673=== added file 'plugin/cloud_storage/tests/r/basic_delete.result'
2674--- plugin/cloud_storage/tests/r/basic_delete.result 1970-01-01 00:00:00 +0000
2675+++ plugin/cloud_storage/tests/r/basic_delete.result 2010-08-19 16:21:41 +0000
2676@@ -0,0 +1,24 @@
2677+DROP TABLE IF EXISTS t1;
2678+CREATE TABLE t1 (a int primary key, b blob not null)
2679+ENGINE=cloud_storage
2680+BUCKET="pku-cactus_cactus"
2681+KEY_ID="AKIAJOZX7SPNWPXWXHVQ"
2682+KEY_VALUE="i+v6L7Vu9jsTWKq0Qr6Q4cbX768JcedgGl21IigV"
2683+SERVICE="awss3";
2684+DELETE FROM t1;
2685+INSERT INTO t1 VALUES (222, "bbb");
2686+INSERT INTO t1 VALUES (444, "ddd");
2687+INSERT INTO t1 VALUES (555, "eee");
2688+SELECT * FROM t1;
2689+a b
2690+222 bbb
2691+444 ddd
2692+555 eee
2693+DELETE FROM t1 WHERE a>=444;
2694+DELETE FROM t1 WHERE b="bbb";
2695+SELECT * FROM t1;
2696+a b
2697+DELETE FROM t1 WHERE a!=111;
2698+SELECT * FROM t1;
2699+a b
2700+DROP TABLE t1;
2701
2702=== added file 'plugin/cloud_storage/tests/r/basic_insert.result'
2703--- plugin/cloud_storage/tests/r/basic_insert.result 1970-01-01 00:00:00 +0000
2704+++ plugin/cloud_storage/tests/r/basic_insert.result 2010-08-19 16:21:41 +0000
2705@@ -0,0 +1,21 @@
2706+DROP TABLE IF EXISTS t1;
2707+CREATE TABLE t1 (a int primary key, b blob not null)
2708+ENGINE=cloud_storage
2709+BUCKET="pku-cactus_cactus"
2710+KEY_ID="AKIAJOZX7SPNWPXWXHVQ"
2711+KEY_VALUE="i+v6L7Vu9jsTWKq0Qr6Q4cbX768JcedgGl21IigV"
2712+SERVICE="awss3";
2713+DELETE FROM t1;
2714+INSERT INTO t1 VALUES (222, "bbb");
2715+INSERT INTO t1 VALUES (333, "ccc");
2716+SELECT * FROM t1;
2717+a b
2718+222 bbb
2719+333 ccc
2720+DELETE FROM t1;
2721+INSERT INTO t1 VALUES (111, "aaa");
2722+SELECT * FROM t1;
2723+a b
2724+111 aaa
2725+DELETE FROM t1;
2726+DROP TABLE t1;
2727
2728=== added file 'plugin/cloud_storage/tests/r/basic_select.result'
2729--- plugin/cloud_storage/tests/r/basic_select.result 1970-01-01 00:00:00 +0000
2730+++ plugin/cloud_storage/tests/r/basic_select.result 2010-08-19 16:21:41 +0000
2731@@ -0,0 +1,18 @@
2732+DROP TABLE IF EXISTS t1;
2733+CREATE TABLE t1 (a int primary key, b blob not null)
2734+ENGINE=cloud_storage
2735+BUCKET="pku-cactus_cactus"
2736+KEY_ID="AKIAJOZX7SPNWPXWXHVQ"
2737+KEY_VALUE="i+v6L7Vu9jsTWKq0Qr6Q4cbX768JcedgGl21IigV"
2738+SERVICE="awss3";
2739+DELETE FROM t1;
2740+INSERT INTO t1 VALUES (222, "bbb");
2741+INSERT INTO t1 VALUES (444, "ddd");
2742+INSERT INTO t1 VALUES (555, "eee");
2743+SELECT * FROM t1;
2744+a b
2745+222 bbb
2746+444 ddd
2747+555 eee
2748+DELETE FROM t1;
2749+DROP TABLE t1;
2750
2751=== added file 'plugin/cloud_storage/tests/r/basic_update.result'
2752--- plugin/cloud_storage/tests/r/basic_update.result 1970-01-01 00:00:00 +0000
2753+++ plugin/cloud_storage/tests/r/basic_update.result 2010-08-19 16:21:41 +0000
2754@@ -0,0 +1,21 @@
2755+DROP TABLE IF EXISTS t1;
2756+CREATE TABLE t1 (a int primary key, b blob not null)
2757+ENGINE=cloud_storage
2758+BUCKET="pku-cactus_cactus"
2759+KEY_ID="AKIAJOZX7SPNWPXWXHVQ"
2760+KEY_VALUE="i+v6L7Vu9jsTWKq0Qr6Q4cbX768JcedgGl21IigV"
2761+SERVICE="awss3";
2762+DELETE FROM t1;
2763+INSERT INTO t1 VALUES (111, "aaa");
2764+INSERT INTO t1 VALUES (222, "bbb");
2765+INSERT INTO t1 VALUES (333, "ccc");
2766+UPDATE t1 SET a=444 WHERE a=222;
2767+UPDATE t1 SET a=555 WHERE b="bbb";
2768+UPDATE t1 SET b="ddd" WHERE a=111;
2769+SELECT * FROM t1;
2770+a b
2771+111 ddd
2772+333 ccc
2773+555 bbb
2774+DELETE FROM t1;
2775+DROP TABLE t1;
2776
2777=== added file 'plugin/cloud_storage/tests/r/multi_column.result'
2778--- plugin/cloud_storage/tests/r/multi_column.result 1970-01-01 00:00:00 +0000
2779+++ plugin/cloud_storage/tests/r/multi_column.result 2010-08-19 16:21:41 +0000
2780@@ -0,0 +1,26 @@
2781+drop table if exists t1;
2782+CREATE TABLE t1 (
2783+s3id varchar(20) not null primary key,
2784+s3val1 blob not null,
2785+s3val2 blob not null,
2786+s3val3 int not null default 0)
2787+engine=CLOUD_STORAGE
2788+BUCKET="pku-cactus_cactus"
2789+KEY_ID="AKIAJOZX7SPNWPXWXHVQ"
2790+KEY_VALUE="i+v6L7Vu9jsTWKq0Qr6Q4cbX768JcedgGl21IigV"
2791+SERVICE="awss3";
2792+DELETE FROM t1;
2793+INSERT INTO t1 VALUES ("aaaa", "aaaa", "aaaa", 0);
2794+INSERT INTO t1 VALUES ("bbbb", "bbbb", "bbbb", 1);
2795+INSERT INTO t1 VALUES ("cccc", "cccc", "cccc", 0);
2796+UPDATE t1 SET s3val1 = "xxxx" WHERE s3id = "aaaa";
2797+DELETE FROM t1 WHERE s3id = "bbbb";
2798+SELECT * FROM t1 WHERE s3id = "cccc";
2799+s3id s3val1 s3val2 s3val3
2800+cccc cccc cccc 0
2801+SELECT * FROM t1;
2802+s3id s3val1 s3val2 s3val3
2803+aaaa xxxx aaaa 0
2804+cccc cccc cccc 0
2805+DELETE FROM t1;
2806+DROP TABLE t1;
2807
2808=== added file 'plugin/cloud_storage/tests/r/nullable_or_empty_column.result'
2809--- plugin/cloud_storage/tests/r/nullable_or_empty_column.result 1970-01-01 00:00:00 +0000
2810+++ plugin/cloud_storage/tests/r/nullable_or_empty_column.result 2010-08-19 16:21:41 +0000
2811@@ -0,0 +1,16 @@
2812+DROP TABLE IF EXISTS t1;
2813+CREATE TABLE t1 (a int primary key, b blob, c int)
2814+ENGINE=cloud_storage
2815+BUCKET="pku-cactus_cactus"
2816+KEY_ID="AKIAJOZX7SPNWPXWXHVQ"
2817+KEY_VALUE="i+v6L7Vu9jsTWKq0Qr6Q4cbX768JcedgGl21IigV"
2818+SERVICE="awss3";
2819+DELETE FROM t1;
2820+INSERT INTO t1 (a,b) VALUES (222, "bbb");
2821+INSERT INTO t1 (a,b) VALUES (333, "");
2822+SELECT * FROM t1;
2823+a b c
2824+222 bbb NULL
2825+333 NULL
2826+DELETE FROM t1;
2827+DROP TABLE t1;
2828
2829=== added directory 'plugin/cloud_storage/tests/t'
2830=== added file 'plugin/cloud_storage/tests/t/basic_create_table-master.opt'
2831--- plugin/cloud_storage/tests/t/basic_create_table-master.opt 1970-01-01 00:00:00 +0000
2832+++ plugin/cloud_storage/tests/t/basic_create_table-master.opt 2010-08-19 16:21:41 +0000
2833@@ -0,0 +1,1 @@
2834+--plugin_add=cloud_storage
2835
2836=== added file 'plugin/cloud_storage/tests/t/basic_create_table.test'
2837--- plugin/cloud_storage/tests/t/basic_create_table.test 1970-01-01 00:00:00 +0000
2838+++ plugin/cloud_storage/tests/t/basic_create_table.test 2010-08-19 16:21:41 +0000
2839@@ -0,0 +1,31 @@
2840+--disable_warnings
2841+DROP TABLE IF EXISTS t1;
2842+--enable_warnings
2843+
2844+# test requires a primary key
2845+--error 1173
2846+CREATE TABLE t1 (a varchar(20), b int not null) ENGINE=cloud_storage;
2847+
2848+# test too many key parts specified
2849+--error 1070
2850+CREATE TABLE t1 (a varchar(20), b int not null, constraint c primary key (a, b)) ENGINE=cloud_storage;
2851+
2852+# test has invalid service
2853+--error 1698
2854+CREATE TABLE t1 (a varchar(20) primary key, b int not null) ENGINE=cloud_storage SERVICE=unknow;
2855+
2856+# test specified key was too long
2857+--error 1071
2858+CREATE TABLE t1 (a varchar(342) primary key, b int not null) ENGINE=cloud_storage;
2859+
2860+CREATE TABLE t1 (a varchar(20) primary key, b int not null) ENGINE=cloud_storage;
2861+CREATE TABLE t2 (a varchar(20) primary key,b int not null, c blob not null) ENGINE=cloud_storage;
2862+CREATE TABLE t3 (a varchar(341) primary key) ENGINE=cloud_storage;
2863+CREATE TABLE t4 (a int primary key) ENGINE=cloud_storage;
2864+CREATE TABLE t5 (a int primary key) ENGINE=cloud_storage SERVICE=awss3;
2865+
2866+DROP TABLE t1;
2867+DROP TABLE t2;
2868+DROP TABLE t3;
2869+DROP TABLE t4;
2870+DROP TABLE t5;
2871
2872=== added file 'plugin/cloud_storage/tests/t/basic_delete-master.opt'
2873--- plugin/cloud_storage/tests/t/basic_delete-master.opt 1970-01-01 00:00:00 +0000
2874+++ plugin/cloud_storage/tests/t/basic_delete-master.opt 2010-08-19 16:21:41 +0000
2875@@ -0,0 +1,1 @@
2876+--plugin_add=cloud_storage
2877
2878=== added file 'plugin/cloud_storage/tests/t/basic_delete.test'
2879--- plugin/cloud_storage/tests/t/basic_delete.test 1970-01-01 00:00:00 +0000
2880+++ plugin/cloud_storage/tests/t/basic_delete.test 2010-08-19 16:21:41 +0000
2881@@ -0,0 +1,29 @@
2882+--disable_warnings
2883+DROP TABLE IF EXISTS t1;
2884+--enable_warnings
2885+
2886+CREATE TABLE t1 (a int primary key, b blob not null)
2887+ENGINE=cloud_storage
2888+BUCKET="pku-cactus_cactus"
2889+KEY_ID="AKIAJOZX7SPNWPXWXHVQ"
2890+KEY_VALUE="i+v6L7Vu9jsTWKq0Qr6Q4cbX768JcedgGl21IigV"
2891+SERVICE="awss3";
2892+
2893+DELETE FROM t1;
2894+
2895+INSERT INTO t1 VALUES (222, "bbb");
2896+INSERT INTO t1 VALUES (444, "ddd");
2897+INSERT INTO t1 VALUES (555, "eee");
2898+
2899+SELECT * FROM t1;
2900+
2901+DELETE FROM t1 WHERE a>=444;
2902+DELETE FROM t1 WHERE b="bbb";
2903+
2904+SELECT * FROM t1;
2905+
2906+DELETE FROM t1 WHERE a!=111;
2907+
2908+SELECT * FROM t1;
2909+
2910+DROP TABLE t1;
2911
2912=== added file 'plugin/cloud_storage/tests/t/basic_insert-master.opt'
2913--- plugin/cloud_storage/tests/t/basic_insert-master.opt 1970-01-01 00:00:00 +0000
2914+++ plugin/cloud_storage/tests/t/basic_insert-master.opt 2010-08-19 16:21:41 +0000
2915@@ -0,0 +1,1 @@
2916+--plugin_add=cloud_storage
2917
2918=== added file 'plugin/cloud_storage/tests/t/basic_insert.test'
2919--- plugin/cloud_storage/tests/t/basic_insert.test 1970-01-01 00:00:00 +0000
2920+++ plugin/cloud_storage/tests/t/basic_insert.test 2010-08-19 16:21:41 +0000
2921@@ -0,0 +1,26 @@
2922+--disable_warnings
2923+DROP TABLE IF EXISTS t1;
2924+--enable_warnings
2925+
2926+CREATE TABLE t1 (a int primary key, b blob not null)
2927+ENGINE=cloud_storage
2928+BUCKET="pku-cactus_cactus"
2929+KEY_ID="AKIAJOZX7SPNWPXWXHVQ"
2930+KEY_VALUE="i+v6L7Vu9jsTWKq0Qr6Q4cbX768JcedgGl21IigV"
2931+SERVICE="awss3";
2932+
2933+DELETE FROM t1;
2934+
2935+INSERT INTO t1 VALUES (222, "bbb");
2936+INSERT INTO t1 VALUES (333, "ccc");
2937+
2938+SELECT * FROM t1;
2939+
2940+DELETE FROM t1;
2941+
2942+INSERT INTO t1 VALUES (111, "aaa");
2943+
2944+SELECT * FROM t1;
2945+
2946+DELETE FROM t1;
2947+DROP TABLE t1;
2948
2949=== added file 'plugin/cloud_storage/tests/t/basic_select-master.opt'
2950--- plugin/cloud_storage/tests/t/basic_select-master.opt 1970-01-01 00:00:00 +0000
2951+++ plugin/cloud_storage/tests/t/basic_select-master.opt 2010-08-19 16:21:41 +0000
2952@@ -0,0 +1,1 @@
2953+--plugin_add=cloud_storage
2954
2955=== added file 'plugin/cloud_storage/tests/t/basic_select.test'
2956--- plugin/cloud_storage/tests/t/basic_select.test 1970-01-01 00:00:00 +0000
2957+++ plugin/cloud_storage/tests/t/basic_select.test 2010-08-19 16:21:41 +0000
2958@@ -0,0 +1,21 @@
2959+--disable_warnings
2960+DROP TABLE IF EXISTS t1;
2961+--enable_warnings
2962+
2963+CREATE TABLE t1 (a int primary key, b blob not null)
2964+ENGINE=cloud_storage
2965+BUCKET="pku-cactus_cactus"
2966+KEY_ID="AKIAJOZX7SPNWPXWXHVQ"
2967+KEY_VALUE="i+v6L7Vu9jsTWKq0Qr6Q4cbX768JcedgGl21IigV"
2968+SERVICE="awss3";
2969+
2970+DELETE FROM t1;
2971+
2972+INSERT INTO t1 VALUES (222, "bbb");
2973+INSERT INTO t1 VALUES (444, "ddd");
2974+INSERT INTO t1 VALUES (555, "eee");
2975+
2976+SELECT * FROM t1;
2977+
2978+DELETE FROM t1;
2979+DROP TABLE t1;
2980
2981=== added file 'plugin/cloud_storage/tests/t/basic_update-master.opt'
2982--- plugin/cloud_storage/tests/t/basic_update-master.opt 1970-01-01 00:00:00 +0000
2983+++ plugin/cloud_storage/tests/t/basic_update-master.opt 2010-08-19 16:21:41 +0000
2984@@ -0,0 +1,1 @@
2985+--plugin_add=cloud_storage
2986
2987=== added file 'plugin/cloud_storage/tests/t/basic_update.test'
2988--- plugin/cloud_storage/tests/t/basic_update.test 1970-01-01 00:00:00 +0000
2989+++ plugin/cloud_storage/tests/t/basic_update.test 2010-08-19 16:21:41 +0000
2990@@ -0,0 +1,28 @@
2991+--disable_warnings
2992+DROP TABLE IF EXISTS t1;
2993+--enable_warnings
2994+
2995+CREATE TABLE t1 (a int primary key, b blob not null)
2996+ENGINE=cloud_storage
2997+BUCKET="pku-cactus_cactus"
2998+KEY_ID="AKIAJOZX7SPNWPXWXHVQ"
2999+KEY_VALUE="i+v6L7Vu9jsTWKq0Qr6Q4cbX768JcedgGl21IigV"
3000+SERVICE="awss3";
3001+
3002+DELETE FROM t1;
3003+
3004+INSERT INTO t1 VALUES (111, "aaa");
3005+INSERT INTO t1 VALUES (222, "bbb");
3006+INSERT INTO t1 VALUES (333, "ccc");
3007+
3008+# test update key
3009+UPDATE t1 SET a=444 WHERE a=222;
3010+UPDATE t1 SET a=555 WHERE b="bbb";
3011+
3012+# test update none-key field
3013+UPDATE t1 SET b="ddd" WHERE a=111;
3014+
3015+SELECT * FROM t1;
3016+
3017+DELETE FROM t1;
3018+DROP TABLE t1;
3019
3020=== added file 'plugin/cloud_storage/tests/t/multi_column-master.opt'
3021--- plugin/cloud_storage/tests/t/multi_column-master.opt 1970-01-01 00:00:00 +0000
3022+++ plugin/cloud_storage/tests/t/multi_column-master.opt 2010-08-19 16:21:41 +0000
3023@@ -0,0 +1,1 @@
3024+--plugin_add=cloud_storage
3025
3026=== added file 'plugin/cloud_storage/tests/t/multi_column.test'
3027--- plugin/cloud_storage/tests/t/multi_column.test 1970-01-01 00:00:00 +0000
3028+++ plugin/cloud_storage/tests/t/multi_column.test 2010-08-19 16:21:41 +0000
3029@@ -0,0 +1,41 @@
3030+# Basic test routine for the Cloud Engine
3031+
3032+--disable_warnings
3033+drop table if exists t1;
3034+--enable_warnings
3035+
3036+# Test multi-column table
3037+
3038+CREATE TABLE t1 (
3039+s3id varchar(20) not null primary key,
3040+s3val1 blob not null,
3041+s3val2 blob not null,
3042+s3val3 int not null default 0)
3043+engine=CLOUD_STORAGE
3044+BUCKET="pku-cactus_cactus"
3045+KEY_ID="AKIAJOZX7SPNWPXWXHVQ"
3046+KEY_VALUE="i+v6L7Vu9jsTWKq0Qr6Q4cbX768JcedgGl21IigV"
3047+SERVICE="awss3";
3048+
3049+DELETE FROM t1;
3050+
3051+# Test Insert
3052+
3053+INSERT INTO t1 VALUES ("aaaa", "aaaa", "aaaa", 0);
3054+INSERT INTO t1 VALUES ("bbbb", "bbbb", "bbbb", 1);
3055+INSERT INTO t1 VALUES ("cccc", "cccc", "cccc", 0);
3056+
3057+# Test Update
3058+UPDATE t1 SET s3val1 = "xxxx" WHERE s3id = "aaaa";
3059+
3060+# Test Delete
3061+DELETE FROM t1 WHERE s3id = "bbbb";
3062+
3063+# Test Select
3064+SELECT * FROM t1 WHERE s3id = "cccc";
3065+SELECT * FROM t1;
3066+
3067+DELETE FROM t1;
3068+
3069+DROP TABLE t1;
3070+
3071
3072=== added file 'plugin/cloud_storage/tests/t/nullable_or_empty_column.opt'
3073--- plugin/cloud_storage/tests/t/nullable_or_empty_column.opt 1970-01-01 00:00:00 +0000
3074+++ plugin/cloud_storage/tests/t/nullable_or_empty_column.opt 2010-08-19 16:21:41 +0000
3075@@ -0,0 +1,1 @@
3076+--plugin_add=cloud_storage
3077
3078=== added file 'plugin/cloud_storage/tests/t/nullable_or_empty_column.test'
3079--- plugin/cloud_storage/tests/t/nullable_or_empty_column.test 1970-01-01 00:00:00 +0000
3080+++ plugin/cloud_storage/tests/t/nullable_or_empty_column.test 2010-08-19 16:21:41 +0000
3081@@ -0,0 +1,22 @@
3082+--disable_warnings
3083+DROP TABLE IF EXISTS t1;
3084+--enable_warnings
3085+
3086+CREATE TABLE t1 (a int primary key, b blob, c int)
3087+ENGINE=cloud_storage
3088+BUCKET="pku-cactus_cactus"
3089+KEY_ID="AKIAJOZX7SPNWPXWXHVQ"
3090+KEY_VALUE="i+v6L7Vu9jsTWKq0Qr6Q4cbX768JcedgGl21IigV"
3091+SERVICE="awss3";
3092+
3093+DELETE FROM t1;
3094+
3095+# has null column
3096+INSERT INTO t1 (a,b) VALUES (222, "bbb");
3097+# has null and empty column
3098+INSERT INTO t1 (a,b) VALUES (333, "");
3099+
3100+SELECT * FROM t1;
3101+
3102+DELETE FROM t1;
3103+DROP TABLE t1;