3 docCopyright("Min-hee Hong", 2007)
4 docLicense("MIT License")
6 docDescription("""<a href="http://www.mysql.com/">MySQL</a> is a fast, multi-threaded, multi-user SQL database server. IoMySQL is a MySQL binding for Io, by <a href="http://dahlia.pe.kr/">Min-hee Hong</a>.
9 my := MySQL establish("localhost", "user", "password", "database")
12 my queryThenMap("SELECT * FROM rel") foreach(at("col") println)
14 my query("SELECT * FROM rel") foreach(at(0) println)
19 docCategory("Databases")
23 #include "IoMessage.h"
30 #define DATA(self) ((IoMySQLData*) IoObject_dataPointer(self))
32 IoTag
* IoMySQL_newTag(void* state
) {
33 IoTag
* tag
= IoTag_newWithName_("MySQL");
34 IoTag_state_(tag
, state
);
35 IoTag_freeFunc_(tag
, (IoTagFreeFunc
*) IoMySQL_free
);
36 IoTag_cloneFunc_(tag
, (IoTagCloneFunc
*) IoMySQL_rawClone
);
40 IoObject
* IoMySQL_proto(void* state
) {
41 IoObject
* self
= IoObject_new(state
);
42 IoObject_tag_(self
, IoMySQL_newTag(state
));
44 IoObject_setDataPointer_(self
, calloc(1, sizeof(IoMySQLData
)));
46 IoState_registerProtoWithFunc_(state
, self
, IoMySQL_proto
);
49 IoMethodTable methodTable
[] = {
50 {"establish", IoMySQL_establish
},
51 {"connect", IoMySQL_connect
},
52 {"connected", IoMySQL_connected
},
53 {"close", IoMySQL_close
},
54 {"query", IoMySQL_query
},
55 {"lastInsertRowId", IoMySQL_lastInsertRowId
},
59 IoObject_addMethodTable_(self
, methodTable
);
64 IoObject
* IoMySQL_rawClone(IoObject
* proto
) {
65 IoObject
* self
= IoObject_rawClonePrimitive(proto
);
66 IoObject_setDataPointer_(self
, calloc(1, sizeof(IoMySQLData
)));
70 IoObject
* IoMySQL_new(void* state
) {
71 IoObject
* proto
= IoState_protoWithInitFunction_(state
, IoMySQL_proto
);
72 return IOCLONE(proto
);
75 void IoMySQL_free(IoObject
* self
) {
76 if(DATA(self
)->connected
)
77 mysql_close(&DATA(self
)->connection
);
78 free(IoObject_dataPointer(self
));
81 /* ----------------------------------------------------------- */
83 IoObject
* IoMySQL_establish(IoObject
* self
, IoObject
* locals
, IoMessage
* m
) {
86 "Establish a connection to a MySQL database.")
88 IoObject
* result
= IoMySQL_new(IOSTATE
);
89 IoMySQL_connect(result
, locals
, m
);
93 IoObject
* IoMySQL_connect(IoObject
* self
, IoObject
* locals
, IoMessage
* m
) {
94 IoObject
*host
= NULL
, *user
= NULL
, *password
= NULL
, *database
= NULL
, *port
= NULL
, *socket
= NULL
, *ssl
= NULL
;
97 docSlot("connect(host, user, password, database, port, unixSocket, useSSL)",
98 "Connect to a MySQL database.")
101 switch(IoMessage_argCount(m
)) {
102 case 7: ssl
= IoMessage_locals_quickValueArgAt_(m
, locals
, 6);
103 case 6: socket
= IoMessage_locals_quickValueArgAt_(m
, locals
, 5);
104 case 5: port
= IoMessage_locals_quickValueArgAt_(m
, locals
, 4);
105 case 4: database
= IoMessage_locals_quickValueArgAt_(m
, locals
, 3);
106 case 3: password
= IoMessage_locals_quickValueArgAt_(m
, locals
, 2);
107 case 2: user
= IoMessage_locals_quickValueArgAt_(m
, locals
, 1);
108 case 1: host
= IoMessage_locals_quickValueArgAt_(m
, locals
, 0);
111 if(DATA(self
)->connected
)
112 mysql_close(&DATA(self
)->connection
);
114 if(mysql_real_connect(
115 &DATA(self
)->connection
,
116 host
&& ISSEQ(host
) ? IoSeq_asCString(host
) : NULL
,
117 user
&& ISSEQ(user
) ? IoSeq_asCString(user
) : NULL
,
118 password
&& ISSEQ(password
) ? IoSeq_asCString(password
) : NULL
,
119 database
&& ISSEQ(database
) ? IoSeq_asCString(database
) : NULL
,
120 port
&& ISNUMBER(port
) ? (unsigned) IoNumber_asInt(port
) : 0,
121 socket
&& ISSEQ(socket
) ? IoSeq_asCString(socket
) : NULL
,
122 ssl
&& ISFALSE(ssl
) ? 0 : CLIENT_SSL
124 DATA(self
)->connected
= 1;
126 IoObject_setSlot_to_(self
, IOSYMBOL("host"), host
? host
: IONIL(self
));
127 IoObject_setSlot_to_(self
, IOSYMBOL("user"), user
? user
: IONIL(self
));
128 IoObject_setSlot_to_(self
, IOSYMBOL("password"), password
? password
: IONIL(self
));
129 IoObject_setSlot_to_(self
, IOSYMBOL("database"), database
? database
: IONIL(self
));
130 IoObject_setSlot_to_(self
, IOSYMBOL("port"), port
? port
: IONIL(self
));
131 IoObject_setSlot_to_(self
, IOSYMBOL("socket"), socket
? socket
: IONIL(self
));
132 IoObject_setSlot_to_(self
, IOSYMBOL("usingSSL"), ssl
? IOBOOL(self
, ISTRUE(ssl
)) : IOFALSE(self
));
135 IoState_error_(IOSTATE
, m
, "connection error(%d): %s", mysql_errno(&DATA(self
)->connection
), mysql_error(&DATA(self
)->connection
));
140 IoObject
* IoMySQL_connected(IoObject
* self
, IoObject
* locals
, IoMessage
* m
) {
141 return IOBOOL(self
, DATA(self
)->connected
);
144 IoObject
* IoMySQL_close(IoObject
* self
, IoObject
* locals
, IoMessage
* m
) {
147 "Closes a previously opened connection.")
150 if(DATA(self
)->connected
)
151 mysql_close(&DATA(self
)->connection
);
153 IoObject_removeSlot_(self
, IOSYMBOL("host"));
154 IoObject_removeSlot_(self
, IOSYMBOL("user"));
155 IoObject_removeSlot_(self
, IOSYMBOL("password"));
156 IoObject_removeSlot_(self
, IOSYMBOL("database"));
157 IoObject_removeSlot_(self
, IOSYMBOL("port"));
158 IoObject_removeSlot_(self
, IOSYMBOL("socket"));
159 IoObject_removeSlot_(self
, IOSYMBOL("usingSSL"));
164 IoObject
* IoMySQL_query(IoObject
* self
, IoObject
* locals
, IoMessage
* m
) {
165 IoObject
* queryString
;
168 MYSQL
* conn
= &DATA(self
)->connection
;
173 unsigned c
, colLength
;
174 unsigned long* colLengths
;
175 IoObject
*list
, *rowObject
, *tmpObject
;
177 if(IoMessage_argCount(m
) < 1 || !ISSEQ(queryString
= IoMessage_locals_quickValueArgAt_(m
, locals
, 0)))
178 IoState_error_(IOSTATE
, m
, "argument 0 to method 'query' must be a Sequence");
180 useMap
= IoMessage_argCount(m
) > 1 && ISTRUE(IoMessage_locals_quickValueArgAt_(m
, locals
, 1));
182 if(!DATA(self
)->connected
)
183 IoState_error_(IOSTATE
, m
, "not connected yet");
185 if(mysql_real_query(conn
, CSTRING(queryString
), IOSEQ_LENGTH(queryString
)))
186 IoState_error_(IOSTATE
, m
, "query error(%d): %s", mysql_errno(&DATA(self
)->connection
), mysql_error(&DATA(self
)->connection
));
188 if((result
= mysql_store_result(conn
)) && (colLength
= mysql_num_fields(result
))) {
189 list
= IoList_new(IOSTATE
);
192 columnNames
= (char**) malloc(colLength
* sizeof(char*));
193 for(c
= 0; c
< colLength
&& (column
= mysql_fetch_field(result
)); ++c
)
194 columnNames
[c
] = column
->name
;
196 while(row
= mysql_fetch_row(result
)) {
197 colLengths
= mysql_fetch_lengths(result
);
198 rowObject
= IoMap_new(IOSTATE
);
200 for(c
= 0; c
< colLength
; ++c
)
201 IoMap_rawAtPut(rowObject
, IOSYMBOL(columnNames
[c
]), IOSEQ((unsigned char *)row
[c
], (size_t)colLengths
[c
]));
203 IoList_rawAppend_(list
, rowObject
);
209 while(row
= mysql_fetch_row(result
)) {
210 colLengths
= mysql_fetch_lengths(result
);
211 rowObject
= IoList_new(IOSTATE
);
213 for(c
= 0; c
< colLength
; ++c
)
214 IoList_rawAppend_(rowObject
, IOSEQ((unsigned char *)row
[c
], (size_t)colLengths
[c
]));
216 IoList_rawAppend_(list
, rowObject
);
220 mysql_free_result(result
);
224 return IONUMBER(mysql_affected_rows(conn
));
227 IoObject
* IoMySQL_lastInsertRowId(IoObject
* self
, IoObject
* locals
, IoMessage
* m
) {
229 docSlot("lastInsertRowId",
230 "Returns the value generated for an AUTO_INCREMENT column by the previous INSERT or UPDATE statement.")
233 if(DATA(self
)->connected
)
234 return IONUMBER(mysql_insert_id(&DATA(self
)->connection
));
236 return IOSTATE
->ioNil
;