-
Notifications
You must be signed in to change notification settings - Fork 32
/
mysql.pm6
325 lines (270 loc) · 9.51 KB
/
mysql.pm6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# DBDish::mysql.pm6
use NativeCall; # from project 'zavolaj'
use DBDish; # roles for drivers
#module DBDish:auth<mberends>:ver<0.0.1>;
#------------ mysql library functions in alphabetical order ------------
sub mysql_affected_rows( OpaquePointer $mysql_client )
returns Int
is native('libmysqlclient')
{ * }
sub mysql_close( OpaquePointer $mysql_client )
returns OpaquePointer
is native('libmysqlclient')
{ * }
sub mysql_data_seek( OpaquePointer $result_set, Int $row_number )
returns OpaquePointer
is native('libmysqlclient')
{ * }
sub mysql_error( OpaquePointer $mysql_client)
returns Str
is native('libmysqlclient')
{ * }
sub mysql_fetch_field( OpaquePointer $result_set )
returns CArray[Str]
is native('libmysqlclient')
{ * }
sub mysql_fetch_lengths( OpaquePointer $result_set )
returns CArray[Int]
is native('libmysqlclient')
{ * }
sub mysql_fetch_row( OpaquePointer $result_set )
returns CArray[Str]
is native('libmysqlclient')
{ * }
sub mysql_field_count( OpaquePointer $mysql_client )
returns Int
is native('libmysqlclient')
{ * }
sub mysql_free_result( OpaquePointer $result_set )
is native('libmysqlclient')
{ * }
sub mysql_get_client_info( OpaquePointer $mysql_client)
returns Str
is native('libmysqlclient')
{ * }
sub mysql_init( OpaquePointer $mysql_client )
returns OpaquePointer
is native('libmysqlclient')
{ * }
sub mysql_insert_id( OpaquePointer $mysql_client )
returns Int # WRONG: actually returns an unsigned long long
is native('libmysqlclient')
{ * }
sub mysql_library_init( Int $argc, OpaquePointer $argv,
OpaquePointer $group )
returns Int
is native('libmysqlclient')
{ * }
sub mysql_library_end()
is native('libmysqlclient')
{ * }
sub mysql_num_rows( OpaquePointer $result_set )
returns Int
is native('libmysqlclient')
{ * }
sub mysql_query( OpaquePointer $mysql_client, Str $sql_command )
returns Int
is native('libmysqlclient')
{ * }
sub mysql_real_connect( OpaquePointer $mysql_client, Str $host, Str $user,
Str $password, Str $database, int32 $port, Str $socket, Int $flag )
returns OpaquePointer
is native('libmysqlclient')
{ * }
sub mysql_stat( OpaquePointer $mysql_client)
returns Str
is native('libmysqlclient')
{ * }
sub mysql_store_result( OpaquePointer $mysql_client )
returns OpaquePointer
is native('libmysqlclient')
{ * }
sub mysql_use_result( OpaquePointer $mysql_client )
returns OpaquePointer
is native('libmysqlclient')
{ * }
sub mysql_warning_count( OpaquePointer $mysql_client )
returns Int
is native('libmysqlclient')
{ * }
sub mysql_stmt_init( OpaquePointer $mysql_client )
returns OpaquePointer
is native('libmysqlclient')
{ * }
sub mysql_stmt_prepare( OpaquePointer $mysql_stmt, Str, Int $length )
returns OpaquePointer
is native('libmysqlclient')
{ * }
#-----------------------------------------------------------------------
class DBDish::mysql::StatementHandle does DBDish::StatementHandle {
has $!mysql_client;
has $!statement;
has $!result_set;
has $!affected_rows;
has @!column_names;
has $!field_count;
has $.mysql_warning_count is rw = 0;
submethod BUILD(:$!mysql_client, :$!statement) { }
method execute(*@params is copy) {
# warn "in DBDish::mysql::StatementHandle.execute()";
my $statement = $!statement;
while @params.elems>0 and $statement.index('?')>=0 {
my $param = @params.shift;
if $param ~~ /<-[0..9.]>/ {
$statement .= subst("?", self.quote($param.Str)); # quote non numerics
}
else {
$statement .= subst("?", $param); # do not quote numbers
}
}
# warn "in DBDish::mysql::StatementHandle.execute statement=$statement";
$!result_set = Mu;
my $status = mysql_query( $!mysql_client, $statement ); # 0 means OK
$.mysql_warning_count = mysql_warning_count( $!mysql_client );
self!reset_errstr();
if $status != 0 {
self!set_errstr(mysql_error( $!mysql_client ));
}
my $rows = self.rows;
return ($rows == 0) ?? "0E0" !! $rows;
}
method escape(Str $x) {
# XXX should really call mysql_real_scape_string
$x.trans(
[q['], q["], q[\\], chr(0), "\r", "\n"]
=> [q[\'], q[\"], q[\\\\], '\0', '\r', '\n']
);
}
method quote(Str $x) {
q['] ~ self.escape($x) ~ q['];
}
# do() and execute() return the number of affected rows directly or:
# rows() is called on the statement handle $sth.
method rows() {
unless defined $!affected_rows {
self!reset_errstr();
$!affected_rows = mysql_affected_rows($!mysql_client);
my $errstr = mysql_error( $!mysql_client );
if $errstr ne '' { self!set_errstr($errstr); }
}
if defined $!affected_rows {
return $!affected_rows;
}
}
method fetchrow() {
my @row_array;
unless defined $!result_set {
$!result_set = mysql_use_result( $!mysql_client);
$!field_count = mysql_field_count($!mysql_client);
}
if defined $!result_set {
# warn "fetching a row";
self!reset_errstr();
my $native_row = mysql_fetch_row($!result_set); # can return NULL
my $errstr = mysql_error( $!mysql_client );
if $errstr ne '' { self!set_errstr($errstr); }
if $native_row {
loop ( my $i=0; $i < $!field_count; $i++ ) {
@row_array.push($native_row[$i]);
}
}
else { self.finish; }
}
return @row_array;
}
method column_names {
unless @!column_names {
unless defined $!result_set {
$!result_set = mysql_use_result( $!mysql_client);
$!field_count = mysql_field_count($!mysql_client);
}
loop ( my $i=0; $i < $!field_count; $i++ ) {
my $field_info = mysql_fetch_field($!result_set);
my $column_name = $field_info[0];
@!column_names.push($column_name);
}
}
@!column_names;
}
method mysql_insertid() {
mysql_insert_id($!mysql_client);
# but Parrot NCI cannot return an unsigned long long :-(
}
method finish() {
if defined( $!result_set ) {
mysql_free_result($!result_set);
$!result_set = Mu;
@!column_names = Mu;
}
return Bool::True;
}
}
class DBDish::mysql::Connection does DBDish::Connection {
has $!mysql_client;
submethod BUILD(:$!mysql_client) { }
method prepare( Str $statement ) {
# warn "in DBDish::mysql::Connection.prepare()";
my $statement_handle = DBDish::mysql::StatementHandle.new(
mysql_client => $!mysql_client,
statement => $statement,
RaiseError => $.RaiseError
);
return $statement_handle;
}
method mysql_insertid() {
mysql_insert_id($!mysql_client);
# but Parrot NCI cannot return an unsigned long long :-(
}
method disconnect() {
mysql_close($!mysql_client);
True
}
}
class DBDish::mysql:auth<mberends>:ver<0.0.1> {
has $.Version = 0.01;
#------------------ methods to be called from DBIish ------------------
method connect(Str :$user, Str :$password, :$RaiseError, *%params ) {
# warn "in DBDish::mysql.connect('$user',*,'$params')";
my ( $mysql_client, $mysql_error );
unless defined $mysql_client {
$mysql_client = mysql_init( OpaquePointer );
$mysql_error = mysql_error( $mysql_client );
}
my $host = %params<host> // 'localhost';
my $port = (%params<port> // 0).Int;
my $database = %params<database> // 'mysql';
# real_connect() returns either the same client pointer or null
my $result = mysql_real_connect( $mysql_client, $host,
$user, $password, $database, $port, OpaquePointer, 0 );
my $error = mysql_error( $mysql_client );
my $connection;
if $error eq '' {
$connection = DBDish::mysql::Connection.new(
mysql_client => $mysql_client,
RaiseError => $RaiseError
);
}
else {
die "DBD::mysql connection failed: $error";
}
return $connection;
}
}
# warn "module DBDish::mysql.pm has loaded";
=begin pod
=head1 DESCRIPTION
# 'zavolaj' is a Native Call Interface for Rakudo/Parrot. 'DBIish' and
# 'DBDish::mysql' are Perl 6 modules that use 'zavolaj' to use the
# standard mysqlclient library. There is a long term Parrot based
# project to develop a new, comprehensive DBI architecture for Parrot
# and Perl 6. DBIish is not that, it is a naive rewrite of the
# similarly named Perl 5 modules. Hence the 'Mini' part of the name.
=head1 CLASSES
The DBDish::mysql module contains the same classes and methods as every
database driver. Therefore read the main documentation of usage in
L<doc:DBIish> and internal architecture in L<doc:DBDish>. Below are
only notes about code unique to the DBDish::mysql implementation.
=head1 SEE ALSO
The MySQL 5.1 Reference Manual, C API.
L<http://dev.mysql.com/doc/refman/5.1/en/c-api-function-overview.html>
=end pod