1 package Gruta
::Source
::DBI
;
14 my $sth = $self->{dbh
}->prepare($sql) or
15 croak
$self->{dbh
}->errstr;
24 return $sth->execute( @_ ) or croak
$self->{dbh
}->errstr;
28 package Gruta
::Data
::DBI
::BASE
;
30 sub pk
{ return qw(id); }
36 $self->source( $driver );
40 if (not $sth = $self->source->{sth
}->{select}->{ref($self)}) {
41 my $sql = 'SELECT ' . join(', ', $self->fields()) .
42 ' FROM ' . $self->table() .
43 ' WHERE ' . join(' AND ', map { "$_ = ?" } $self->pk());
45 $sth = $self->source->{sth
}->{select}->{ref($self)} =
46 $self->source->_prepare($sql);
49 $self->source->_execute($sth, map { $self->get($_) } $self->pk());
51 my $r = $sth->fetchrow_hashref();
57 foreach my $k ($self->fields()) {
58 $self->set($k, $r->{$k});
69 $self->source( $driver ) if $driver;
73 if (not $sth = $self->source->{sth
}->{update
}->{ref($self)}) {
74 my $sql = 'UPDATE ' . $self->table() .
75 ' SET ' . join(', ', map { "$_ = ?" } $self->fields()) .
76 ' WHERE ' . join(' AND ', map { "$_ = ?" } $self->pk());
78 $sth = $self->source->{sth
}->{update
}->{ref($self)} =
79 $self->source->_prepare($sql);
82 $self->source->_execute($sth,
83 (map { $self->get($_) } $self->fields()),
84 (map { $self->get($_) } $self->pk())
95 $self->source( $driver ) if $driver;
99 if (not $sth = $self->source->{sth
}->{delete}->{ref($self)}) {
100 my $sql = 'DELETE FROM ' . $self->table() .
101 ' WHERE ' . join(' AND ', map { "$_ = ?" } $self->pk());
103 $sth = $self->source->{sth
}->{delete}->{ref($self)} =
104 $self->source->_prepare($sql);
107 $self->source->_execute($sth,
108 (map { $self->get($_) } $self->pk())
115 package Gruta
::Data
::DBI
::Story
;
117 use base
'Gruta::Data::Story';
118 use base
'Gruta::Data::DBI::BASE';
120 sub table
{ return 'stories'; }
121 sub pk
{ return qw(id topic_id); }
126 my $sth = $self->source->_prepare(
127 'UPDATE stories SET hits = hits + 1 WHERE topic_id = ? AND id = ?');
128 $self->source->_execute($sth, $self->get('topic_id'), $self->get('id'));
140 # first, delete all tags for this story
141 my $sth = $self->source->_prepare(
142 'DELETE FROM tags WHERE topic_id = ? AND id = ?');
143 $self->source->_execute($sth, $self->get('topic_id'), $self->get('id'));
145 # second, add all the new ones
146 $sth = $self->source->_prepare(
147 'INSERT INTO tags (id, topic_id, tag) VALUES (?, ?, ?)');
149 foreach my $t (@tags) {
150 $self->source->_execute($sth,
152 $self->get('topic_id'),
158 my $sth = $self->source->_prepare(
159 'SELECT tag FROM tags WHERE topic_id = ? AND id = ?');
160 $self->source->_execute($sth, $self->get('topic_id'), $self->get('id'));
162 while (my $r = $sth->fetchrow_arrayref()) {
170 package Gruta
::Data
::DBI
::Topic
;
172 use base
'Gruta::Data::Topic';
173 use base
'Gruta::Data::DBI::BASE';
175 sub table
{ return 'topics'; }
177 package Gruta
::Data
::DBI
::User
;
179 use base
'Gruta::Data::User';
180 use base
'Gruta::Data::DBI::BASE';
182 sub table
{ return 'users'; }
184 package Gruta
::Data
::DBI
::Session
;
186 use base
'Gruta::Data::Session';
187 use base
'Gruta::Data::DBI::BASE';
189 sub table
{ return 'sids'; }
191 package Gruta
::Source
::DBI
;
193 sub _assert
{ return $_[0]; }
201 my $sth = $self->_prepare("SELECT id FROM $table");
202 $self->_execute($sth);
204 while(my @r = $sth->fetchrow_array()) {
216 my $o = ${class}->new( id
=> $id );
217 return $o->load( $self );
221 sub topic
{ return _one
( @_, 'Gruta::Data::DBI::Topic' ); }
222 sub topics
{ return $_[0]->_all('topics'); }
224 sub user
{ return _one
( @_, 'Gruta::Data::DBI::User' ); }
225 sub users
{ return $_[0]->_all('users'); }
229 my $topic_id = shift;
232 my $s = Gruta
::Data
::DBI
::Story
->new( topic_id
=> $topic_id, id
=> $id );
233 return $s->load( $self );
239 my $topic_id = shift;
243 my $sth = $self->_prepare("SELECT id FROM stories WHERE topic_id = ?");
244 $self->_execute($sth, $topic_id);
246 while(my @r = $sth->fetchrow_array()) {
254 sub stories_by_date
{
256 my $topic_id = shift;
260 $args{offset
} = 0 if $args{offset
} < 0;
262 my @args = ( $topic_id );
263 my $sql = 'SELECT id FROM stories WHERE topic_id = ?';
266 $sql .= ' AND date > ?';
267 push(@args, $args{from
});
271 $sql .= ' AND date < ?';
272 push(@args, $args{to
});
275 if (!$args{future
} && $args{today
}) {
276 $sql .= ' AND date < ?';
277 push(@args, $args{today
});
280 $sql .= ' ORDER BY date DESC';
282 if ($args{num
} || $args{offset
}) {
285 push(@args, $args{num
} || -1);
289 push(@args, $args{offset
});
293 my $sth = $self->_prepare($sql);
294 $self->_execute($sth, @args);
298 while(my $r = $sth->fetchrow_arrayref()) {
308 my $topic_id = shift;
311 my @q = map { '%' . $_ . '%' } split(/\s+/, $query);
312 my $like = 'AND content LIKE ? ' x
scalar(@q);
314 my $sth = $self->_prepare(
315 'SELECT id FROM stories WHERE topic_id = ? ' . $like .
316 'ORDER BY date DESC');
318 $self->_execute($sth, $topic_id, @q);
322 while(my $r = $sth->fetchrow_arrayref()) {
330 sub stories_top_ten
{
334 my $sql = 'SELECT hits, topic_id, id FROM stories ' .
335 'ORDER BY hits DESC LIMIT ?';
337 my $sth = $self->_prepare($sql);
338 $self->_execute($sth, $num);
342 while (my @a = $sth->fetchrow_array()) {
350 sub search_stories_by_tag
{
357 my $sql = 'SELECT DISTINCT topic_id, id FROM tags WHERE ' .
358 join(' OR ', map { 'tag = ?' } @tags);
360 my $sth = $self->_prepare($sql);
361 $self->_execute($sth, map { lc($_) } @tags);
363 while (my @a = $sth->fetchrow_array()) {
377 my $sth = $self->_prepare(
378 'SELECT tag, count(tag) FROM tags GROUP BY tag ORDER BY 2 DESC, 1');
379 $self->_execute($sth);
381 while (my @a = $sth->fetchrow_array()) {
388 sub session
{ return _one
( @_, 'Gruta::Data::DBI::Session' ); }
390 sub purge_old_sessions
{
393 my $sth = $self->_prepare('DELETE FROM sids WHERE time < ?');
394 $self->_execute($sth, time() - (60 * 60 * 24));
411 if (not $sth = $self->{sth
}->{insert
}->{ref($obj)}) {
412 my $sql = 'INSERT INTO ' . $table .
413 ' (' . join(', ', $obj->fields()) . ')' .
414 ' VALUES (' . join(', ', map { '?' } $obj->fields()) . ')';
416 $sth = $self->{sth
}->{insert
}->{ref($obj)} = $self->_prepare($sql);
419 $self->_execute($sth, map { $obj->get($_) } $obj->fields());
424 sub insert_topic
{ $_[0]->_insert($_[1], 'topics', 'Gruta::Data::DBI::Topic'); }
425 sub insert_user
{ $_[0]->_insert($_[1], 'users', 'Gruta::Data::DBI::User'); }
432 if (not $story->get('id')) {
433 # alloc an id for the story
436 my $sth = $self->_prepare(
437 'SELECT 1 FROM stories WHERE topic_id = ? AND id = ?');
440 $id = $story->new_id();
441 $self->_execute($sth, $story->get('topic_id'), $id);
443 } while $sth->fetchrow_arrayref();
445 $story->set('id', $id);
448 $self->_insert($story, 'stories', 'Gruta::Data::DBI::Story');
453 sub insert_session
{ $_[0]->_insert($_[1], 'sids', 'Gruta::Data::DBI::Session'); }
464 $self->{dbh
}->do($sql);
477 my $s = bless( { @_ }, $class);
479 $s->{dbh
} = DBI
->connect($s->{string
},
480 $s->{user
}, $s->{passwd
}, { RaiseError
=> 1 });
487 CREATE TABLE topics
(
488 id VARCHAR PRIMARY KEY
,
495 CREATE TABLE stories
(
497 topic_id VARCHAR NOT NULL
,
505 PRIMARY KEY
(id
, topic_id
)
509 id VARCHAR PRIMARY KEY
,
518 id VARCHAR PRIMARY KEY
,
526 topic_id VARCHAR NOT NULL
,
530 CREATE INDEX stories_by_date ON stories
(date
)
532 CREATE INDEX stories_by_hits ON stories
(hits
)