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) {
153 $self->source->_execute($sth,
155 $self->get('topic_id'),
161 my $sth = $self->source->_prepare(
162 'SELECT tag FROM tags WHERE topic_id = ? AND id = ?');
163 $self->source->_execute($sth, $self->get('topic_id'), $self->get('id'));
165 while (my $r = $sth->fetchrow_arrayref()) {
173 package Gruta
::Data
::DBI
::Topic
;
175 use base
'Gruta::Data::Topic';
176 use base
'Gruta::Data::DBI::BASE';
178 sub table
{ return 'topics'; }
180 package Gruta
::Data
::DBI
::User
;
182 use base
'Gruta::Data::User';
183 use base
'Gruta::Data::DBI::BASE';
185 sub table
{ return 'users'; }
187 package Gruta
::Data
::DBI
::Session
;
189 use base
'Gruta::Data::Session';
190 use base
'Gruta::Data::DBI::BASE';
192 sub table
{ return 'sids'; }
194 package Gruta
::Source
::DBI
;
196 sub _assert
{ return $_[0]; }
204 my $sth = $self->_prepare("SELECT id FROM $table");
205 $self->_execute($sth);
207 while(my @r = $sth->fetchrow_array()) {
219 my $o = ${class}->new( id
=> $id );
220 return $o->load( $self );
224 sub topic
{ return _one
( @_, 'Gruta::Data::DBI::Topic' ); }
225 sub topics
{ return $_[0]->_all('topics'); }
227 sub user
{ return _one
( @_, 'Gruta::Data::DBI::User' ); }
228 sub users
{ return $_[0]->_all('users'); }
232 my $topic_id = shift;
235 my $s = Gruta
::Data
::DBI
::Story
->new( topic_id
=> $topic_id, id
=> $id );
236 return $s->load( $self );
242 my $topic_id = shift;
246 my $sth = $self->_prepare("SELECT id FROM stories WHERE topic_id = ?");
247 $self->_execute($sth, $topic_id);
249 while(my @r = $sth->fetchrow_array()) {
257 sub stories_by_date
{
259 my $topic_id = shift;
263 $args{offset
} = 0 if $args{offset
} < 0;
265 my @args = ( $topic_id );
266 my $sql = 'SELECT id FROM stories WHERE topic_id = ?';
269 $sql .= ' AND date > ?';
270 push(@args, $args{from
});
274 $sql .= ' AND date < ?';
275 push(@args, $args{to
});
278 if (!$args{future
}) {
279 $sql .= ' AND date <= ?';
280 push(@args, Gruta
::Data
::today
());
283 $sql .= ' ORDER BY date DESC';
285 if ($args{num
} || $args{offset
}) {
288 push(@args, $args{num
} || -1);
292 push(@args, $args{offset
});
296 my $sth = $self->_prepare($sql);
297 $self->_execute($sth, @args);
301 while(my $r = $sth->fetchrow_arrayref()) {
311 my $topic_id = shift;
315 my @q = map { '%' . $_ . '%' } split(/\s+/, $query);
316 my $cond = 'AND content LIKE ? ' x
scalar(@q);
319 $cond .= 'AND date <= ? ';
320 push(@q, Gruta
::Data
::today
());
323 my $sql = 'SELECT id FROM stories WHERE topic_id = ? ' . $cond .
324 'ORDER BY date DESC';
326 my $sth = $self->_prepare($sql);
328 $self->_execute($sth, $topic_id, @q);
332 while(my $r = $sth->fetchrow_arrayref()) {
340 sub stories_top_ten
{
344 my $sql = 'SELECT hits, topic_id, id FROM stories ' .
345 'ORDER BY hits DESC LIMIT ?';
347 my $sth = $self->_prepare($sql);
348 $self->_execute($sth, $num);
352 while (my @a = $sth->fetchrow_array()) {
360 sub search_stories_by_tag
{
365 my @tags = map { lc($_) } split(/\s*,\s*/, $tag);
373 $sql = 'SELECT DISTINCT topic_id, id FROM tags WHERE ' .
374 join(' OR ', map { 'tag = ?' } @tags);
377 $sql = 'SELECT DISTINCT tags.topic_id, tags.id FROM tags, stories WHERE ' .
378 'tags.topic_id = stories.topic_id AND tags.id = stories.id AND ' .
379 "stories.date <= '" . Gruta
::Data
::today
() . "' AND (" .
380 join(' OR ', map { 'tag = ?' } @tags) . ')';
383 my $sth = $self->_prepare($sql);
384 $self->_execute($sth, map { lc($_) } @tags);
386 while (my @a = $sth->fetchrow_array()) {
400 my $sth = $self->_prepare(
401 'SELECT tag, count(tag) FROM tags GROUP BY tag');
402 $self->_execute($sth);
404 while (my @a = $sth->fetchrow_array()) {
411 sub session
{ return _one
( @_, 'Gruta::Data::DBI::Session' ); }
413 sub purge_old_sessions
{
416 my $sth = $self->_prepare('DELETE FROM sids WHERE time < ?');
417 $self->_execute($sth, time() - (60 * 60 * 24));
434 if (not $sth = $self->{sth
}->{insert
}->{ref($obj)}) {
435 my $sql = 'INSERT INTO ' . $table .
436 ' (' . join(', ', $obj->fields()) . ')' .
437 ' VALUES (' . join(', ', map { '?' } $obj->fields()) . ')';
439 $sth = $self->{sth
}->{insert
}->{ref($obj)} = $self->_prepare($sql);
442 $self->_execute($sth, map { $obj->get($_) } $obj->fields());
447 sub insert_topic
{ $_[0]->_insert($_[1], 'topics', 'Gruta::Data::DBI::Topic'); }
448 sub insert_user
{ $_[0]->_insert($_[1], 'users', 'Gruta::Data::DBI::User'); }
455 if (not $story->get('id')) {
456 # alloc an id for the story
459 my $sth = $self->_prepare(
460 'SELECT 1 FROM stories WHERE topic_id = ? AND id = ?');
463 $id = $story->new_id();
464 $self->_execute($sth, $story->get('topic_id'), $id);
466 } while $sth->fetchrow_arrayref();
468 $story->set('id', $id);
471 $self->_insert($story, 'stories', 'Gruta::Data::DBI::Story');
476 sub insert_session
{ $_[0]->_insert($_[1], 'sids', 'Gruta::Data::DBI::Session'); }
482 my $sth = $self->_prepare('SELECT 1 FROM users');
495 $self->{dbh
}->do($sql);
510 my $s = bless( { @_ }, $class);
512 $s->{dbh
} = DBI
->connect($s->{string
},
513 $s->{user
}, $s->{passwd
}, { RaiseError
=> 1 });
520 CREATE TABLE topics
(
521 id VARCHAR PRIMARY KEY
,
528 CREATE TABLE stories
(
530 topic_id VARCHAR NOT NULL
,
536 hits INTEGER DEFAULT
0,
539 PRIMARY KEY
(id
, topic_id
)
543 id VARCHAR PRIMARY KEY
,
553 id VARCHAR PRIMARY KEY
,
561 topic_id VARCHAR NOT NULL
,
565 CREATE INDEX stories_by_date ON stories
(date
)
567 CREATE INDEX stories_by_hits ON stories
(hits
)
569 CREATE INDEX tags_by_tag ON tags
(tag
)