| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | #=============================================================================== | 
| 2 |  |  |  |  |  |  | # | 
| 3 |  |  |  |  |  |  | #         FILE:  Table.pm | 
| 4 |  |  |  |  |  |  | # | 
| 5 |  |  |  |  |  |  | #  DESCRIPTION:  NetSDS::DBI::Table - CRUD implementation for NetSDS | 
| 6 |  |  |  |  |  |  | # | 
| 7 |  |  |  |  |  |  | #       AUTHOR:  Michael Bochkaryov (Rattler), | 
| 8 |  |  |  |  |  |  | #      COMPANY:  Net.Style | 
| 9 |  |  |  |  |  |  | #      CREATED:  25.07.2008 01:06:46 EEST | 
| 10 |  |  |  |  |  |  | #=============================================================================== | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | =head1 NAME | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | NetSDS::DBI::Table | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | use NetSDS::DBI::Table; | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | my $q = NetSDS::DBI::Table->new( | 
| 21 |  |  |  |  |  |  | dsn    => 'dbi:Pg:dbname=netsdsdb;host=127.0.0.1', | 
| 22 |  |  |  |  |  |  | user   => 'netsds', | 
| 23 |  |  |  |  |  |  | passwd => 'test', | 
| 24 |  |  |  |  |  |  | table  => 'public.messages', | 
| 25 |  |  |  |  |  |  | ) or warn NetSDS::DBI::Table->errstr(); | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | C module provides commonly used CRUD functionality for | 
| 31 |  |  |  |  |  |  | data stored in single database. | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | Main idea was that we can agree about some limitations: | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | * every such table contains C field that is primary key | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | * we use PostgreSQL DBMS with all it's features | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | =cut | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | package NetSDS::DBI::Table; | 
| 42 |  |  |  |  |  |  |  | 
| 43 | 2 |  |  | 2 |  | 9766 | use 5.8.0; | 
|  | 2 |  |  |  |  | 7 |  | 
|  | 2 |  |  |  |  | 95 |  | 
| 44 | 2 |  |  | 2 |  | 12 | use strict; | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 69 |  | 
| 45 | 2 |  |  | 2 |  | 11 | use warnings; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 62 |  | 
| 46 |  |  |  |  |  |  |  | 
| 47 | 2 |  |  | 2 |  | 10 | use base 'NetSDS::DBI'; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 286 |  | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | use version; our $VERSION = '1.301'; | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | #=============================================================================== | 
| 52 |  |  |  |  |  |  | # | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | =head1 CLASS API | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | =over | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | =item B - class constructor | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | my $tbl = NetSDS::DBI::Table->new( | 
| 61 |  |  |  |  |  |  | dsn => 'dbi:Pg:dbname=content', | 
| 62 |  |  |  |  |  |  | login => 'netsds', | 
| 63 |  |  |  |  |  |  | passwd => 'topsecret, | 
| 64 |  |  |  |  |  |  | table => 'content.meta', | 
| 65 |  |  |  |  |  |  | ); | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | =cut | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | #----------------------------------------------------------------------- | 
| 70 |  |  |  |  |  |  | sub new { | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | my ( $class, %params ) = @_; | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | # Initialize base DBMS connector | 
| 75 |  |  |  |  |  |  | my $self = $class->SUPER::new(%params); | 
| 76 |  |  |  |  |  |  |  | 
| 77 |  |  |  |  |  |  | # Set table name | 
| 78 |  |  |  |  |  |  | if ( $params{table} ) { | 
| 79 |  |  |  |  |  |  | $self->{table} = $params{table}; | 
| 80 |  |  |  |  |  |  | } else { | 
| 81 |  |  |  |  |  |  | return $class->error('Table name is not specified to NetSDS::DBI::Table'); | 
| 82 |  |  |  |  |  |  | } | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | # 'fields' paramter is hash reference describing supported/allowed fields | 
| 85 |  |  |  |  |  |  | if ( $params{fields} ) { | 
| 86 |  |  |  |  |  |  | } | 
| 87 |  |  |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | return $self; | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | } ## end sub new | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | #*********************************************************************** | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | =item B - get records from table as array of hashrefs | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | Paramters (hash): | 
| 97 |  |  |  |  |  |  |  | 
| 98 |  |  |  |  |  |  | * fields - fetch fields by list | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | * filter - arrayref of SQL expressions like C for C clause | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | * order - arrayref of SQL expressions like C for C clause | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | * limit - max number of records to fetch (LIMIT N) | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | * offset - records to skip from beginning (OFFSET N) | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | * for_update - records selected for further update within current transaction | 
| 109 |  |  |  |  |  |  |  | 
| 110 |  |  |  |  |  |  | Returns: message as array of hashrefs | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | Sample: | 
| 113 |  |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | my @messages = $q->fetch( | 
| 115 |  |  |  |  |  |  | fields => ['id', 'now() as time'], | 
| 116 |  |  |  |  |  |  | filter => ['msg_status = 5', 'date_received < now()'], # where msg_status=5 and date_received < now() | 
| 117 |  |  |  |  |  |  | order  => ['id desc', 'src_addr'], # order by id desc, src_addr | 
| 118 |  |  |  |  |  |  | limit => 3, # fetch 3 records | 
| 119 |  |  |  |  |  |  | offset => 5, # from 6-th record | 
| 120 |  |  |  |  |  |  | for_update => 1, # for update | 
| 121 |  |  |  |  |  |  | ) | 
| 122 |  |  |  |  |  |  |  | 
| 123 |  |  |  |  |  |  | =cut | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | #----------------------------------------------------------------------- | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  | sub fetch { | 
| 128 |  |  |  |  |  |  |  | 
| 129 |  |  |  |  |  |  | my ( $self, %params ) = @_; | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | # Prepare expected fields list | 
| 132 |  |  |  |  |  |  | my $req_fields = $params{fields} ? join( ',', @{ $params{fields} } ) : '*'; | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | # Set default filter | 
| 135 |  |  |  |  |  |  | my $default_filter = $self->{default_filter} ? " where " . join( " and ", @{ $self->{default_filter} } ) : ''; | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  | # Prepare WHERE filter | 
| 138 |  |  |  |  |  |  | my $req_filter = ($params{filter} and grep { $_ } @{ $params{filter} }) ? | 
| 139 |  |  |  |  |  |  | " where " . join( " and ", grep { $_ } @{ $params{filter} } ) : $default_filter; | 
| 140 |  |  |  |  |  |  |  | 
| 141 |  |  |  |  |  |  | # Prepare results order | 
| 142 |  |  |  |  |  |  | my $req_order = $params{order} ? " order by " . join( ", ", @{ $params{order} } ) : ''; | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | # Set limit and offset for fetching | 
| 145 |  |  |  |  |  |  | my $req_limit  = $params{limit}  ? " limit " . $params{limit}   : ''; | 
| 146 |  |  |  |  |  |  | my $req_offset = $params{offset} ? " offset " . $params{offset} : ''; | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  | # Request for messages | 
| 149 |  |  |  |  |  |  | my $sql = "select $req_fields from " . $self->{table} . " $req_filter $req_order $req_limit $req_offset"; | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  | # Set FOR UPDATE if necessary | 
| 152 |  |  |  |  |  |  | if ( $params{for_update} ) { | 
| 153 |  |  |  |  |  |  | $sql .= " for update"; | 
| 154 |  |  |  |  |  |  | } | 
| 155 |  |  |  |  |  |  |  | 
| 156 |  |  |  |  |  |  | # Execute SQL query and fetch results | 
| 157 |  |  |  |  |  |  | my @ret = (); | 
| 158 |  |  |  |  |  |  | my $sth = $self->call($sql); | 
| 159 |  |  |  |  |  |  | while ( my $row = $sth->fetchrow_hashref() ) { | 
| 160 |  |  |  |  |  |  | push @ret, $row; | 
| 161 |  |  |  |  |  |  | } | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | return @ret; | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | } ## end sub fetch | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | #*********************************************************************** | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | =item B - insert record into table | 
| 170 |  |  |  |  |  |  |  | 
| 171 |  |  |  |  |  |  | Paramters: record fields as hash | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | Returns: id of inserted record | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | my $user_id = $tbl->insert_row( | 
| 176 |  |  |  |  |  |  | 'login' => 'vasya', | 
| 177 |  |  |  |  |  |  | 'password' => $encrypted_passwd, | 
| 178 |  |  |  |  |  |  | ); | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  | =cut | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | #----------------------------------------------------------------------- | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | sub insert_row { | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | my ( $self, %params ) = @_; | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | my @fields = ();    # Fields list | 
| 189 |  |  |  |  |  |  | my @values = ();    # Values list | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | # Prepare fields and values lists from input hash | 
| 192 |  |  |  |  |  |  | foreach my $key ( keys %params ) { | 
| 193 |  |  |  |  |  |  | push @fields, $key; | 
| 194 |  |  |  |  |  |  | push @values, $self->dbh->quote( $params{$key} ); | 
| 195 |  |  |  |  |  |  | } | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | my $return_value = $self->has_field('id') ? ' returning id' : ''; | 
| 198 |  |  |  |  |  |  | # Prepare SQL statement from fields and values lists | 
| 199 |  |  |  |  |  |  | my $sql = 'insert into ' . $self->{table} . ' (' . join( ',', @fields ) . ')'    # fields list | 
| 200 |  |  |  |  |  |  | . ' values (' . join( ',', @values ) . ')'                                     # values list | 
| 201 |  |  |  |  |  |  | . $return_value;                                                               # return "id" field | 
| 202 |  |  |  |  |  |  |  | 
| 203 |  |  |  |  |  |  | # Execute SQL query and fetch result | 
| 204 |  |  |  |  |  |  | my $sth = $self->call($sql); | 
| 205 |  |  |  |  |  |  | my ($row_id) = $return_value ? $sth->fetchrow_array : $sth->rows; | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  | # Return "id" field from inserted row | 
| 208 |  |  |  |  |  |  | return $row_id || $self->error( "Can't insert table record: " . $self->dbh->errstr ); | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | } ## end sub insert_row | 
| 211 |  |  |  |  |  |  |  | 
| 212 |  |  |  |  |  |  | #*********************************************************************** | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | =item B - mass insert | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  | Paramters: list of records (as hashrefs) | 
| 217 |  |  |  |  |  |  |  | 
| 218 |  |  |  |  |  |  | Returns: array of inserted records "id" | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  | This method allows mass insert of records. | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | my @user_ids = $tbl->insert( | 
| 223 |  |  |  |  |  |  | { login => 'vasya', password => $str1 }, | 
| 224 |  |  |  |  |  |  | { login => 'masha', password => $str2 }, | 
| 225 |  |  |  |  |  |  | { login => 'petya', password => $str3, active => 'false' }, | 
| 226 |  |  |  |  |  |  | ); | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | B This method use separate INSERT queries and in fact is only | 
| 229 |  |  |  |  |  |  | wrapper for multiple C calls. So it's not so fast as | 
| 230 |  |  |  |  |  |  | one insert but allows to use different key-value pairs for different records. | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | =cut | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | #----------------------------------------------------------------------- | 
| 235 |  |  |  |  |  |  |  | 
| 236 |  |  |  |  |  |  | sub insert { | 
| 237 |  |  |  |  |  |  |  | 
| 238 |  |  |  |  |  |  | my ( $self, @rows ) = @_; | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | my @ids = (); | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | # Go through records and insert each one | 
| 243 |  |  |  |  |  |  | foreach my $rec (@rows) { | 
| 244 |  |  |  |  |  |  | push @ids, ( $self->insert_row( %{$rec} ) ); | 
| 245 |  |  |  |  |  |  | } | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | return @ids; | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | } | 
| 250 |  |  |  |  |  |  |  | 
| 251 |  |  |  |  |  |  | #*********************************************************************** | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | =item B - update record parameters | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | Paramters: id, new parameters as hash | 
| 256 |  |  |  |  |  |  |  | 
| 257 |  |  |  |  |  |  | Returns: updated record as hash | 
| 258 |  |  |  |  |  |  |  | 
| 259 |  |  |  |  |  |  | Example: | 
| 260 |  |  |  |  |  |  |  | 
| 261 |  |  |  |  |  |  |  | 
| 262 |  |  |  |  |  |  | my %upd = $table->update_row($msg_id, | 
| 263 |  |  |  |  |  |  | status => 'failed', | 
| 264 |  |  |  |  |  |  | dst_addr => '380121234567', | 
| 265 |  |  |  |  |  |  | ); | 
| 266 |  |  |  |  |  |  |  | 
| 267 |  |  |  |  |  |  |  | 
| 268 |  |  |  |  |  |  | After this %upd hash will contain updated table record. | 
| 269 |  |  |  |  |  |  |  | 
| 270 |  |  |  |  |  |  | =cut | 
| 271 |  |  |  |  |  |  |  | 
| 272 |  |  |  |  |  |  | #----------------------------------------------------------------------- | 
| 273 |  |  |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | sub update_row { | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | my ( $self, $id, %params ) = @_; | 
| 277 |  |  |  |  |  |  | my @up = (); | 
| 278 |  |  |  |  |  |  | foreach my $key ( keys %params ) { | 
| 279 |  |  |  |  |  |  | push @up, "$key = " . $self->dbh->quote( $params{$key} ); | 
| 280 |  |  |  |  |  |  | } | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  | my $sql = "update " . $self->{table} . " set " . join( ', ', @up ) . " where id=$id"; | 
| 283 |  |  |  |  |  |  |  | 
| 284 |  |  |  |  |  |  | my $res = $self->call($sql); | 
| 285 |  |  |  |  |  |  |  | 
| 286 |  |  |  |  |  |  | if ($res) { | 
| 287 |  |  |  |  |  |  | return %{$res}; | 
| 288 |  |  |  |  |  |  | } else { | 
| 289 |  |  |  |  |  |  | return $self->error( "Can't update table row: " . $self->dbh->errstr ); | 
| 290 |  |  |  |  |  |  | } | 
| 291 |  |  |  |  |  |  |  | 
| 292 |  |  |  |  |  |  | } | 
| 293 |  |  |  |  |  |  |  | 
| 294 |  |  |  |  |  |  | #*********************************************************************** | 
| 295 |  |  |  |  |  |  |  | 
| 296 |  |  |  |  |  |  | =item B - update records by filter | 
| 297 |  |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | Paramters: filter, new values | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  | $tbl->update( | 
| 301 |  |  |  |  |  |  | filter => ['active = true', 'created > '2008-01-01'], | 
| 302 |  |  |  |  |  |  | set => { | 
| 303 |  |  |  |  |  |  | info => 'Created after 2007 year', | 
| 304 |  |  |  |  |  |  | } | 
| 305 |  |  |  |  |  |  | ); | 
| 306 |  |  |  |  |  |  |  | 
| 307 |  |  |  |  |  |  | =cut | 
| 308 |  |  |  |  |  |  |  | 
| 309 |  |  |  |  |  |  | #----------------------------------------------------------------------- | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | sub update { | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | my ( $self, %params ) = @_; | 
| 314 |  |  |  |  |  |  |  | 
| 315 |  |  |  |  |  |  | # Prepare WHERE filter | 
| 316 |  |  |  |  |  |  | my $req_filter = $params{filter} ? " where " . join( " and ", @{ $params{filter} } ) : ''; | 
| 317 |  |  |  |  |  |  |  | 
| 318 |  |  |  |  |  |  | my @up = (); | 
| 319 |  |  |  |  |  |  | foreach my $key ( keys %{ $params{set} } ) { | 
| 320 |  |  |  |  |  |  | push @up, "$key = " . $self->dbh->quote( $params{set}->{$key} ); | 
| 321 |  |  |  |  |  |  | } | 
| 322 |  |  |  |  |  |  |  | 
| 323 |  |  |  |  |  |  | my $sql = "update " . $self->{table} . " set " . join( ', ', @up ) . $req_filter; | 
| 324 |  |  |  |  |  |  | my $res = $self->call($sql); | 
| 325 |  |  |  |  |  |  |  | 
| 326 |  |  |  |  |  |  | if ($self->dbh->errstr) { | 
| 327 |  |  |  |  |  |  | $self->error( "Can't update table: " . $self->dbh->errstr ); | 
| 328 |  |  |  |  |  |  | return; | 
| 329 |  |  |  |  |  |  | }; | 
| 330 |  |  |  |  |  |  |  | 
| 331 |  |  |  |  |  |  | return 1; | 
| 332 |  |  |  |  |  |  | } | 
| 333 |  |  |  |  |  |  |  | 
| 334 |  |  |  |  |  |  | #*********************************************************************** | 
| 335 |  |  |  |  |  |  |  | 
| 336 |  |  |  |  |  |  | =item B - retrieve number of records | 
| 337 |  |  |  |  |  |  |  | 
| 338 |  |  |  |  |  |  | Just return total number of records by calling: | 
| 339 |  |  |  |  |  |  |  | 
| 340 |  |  |  |  |  |  | # SELECT COUNT(id) FROM schema.table | 
| 341 |  |  |  |  |  |  | my $count = $tbl->get_count(); | 
| 342 |  |  |  |  |  |  |  | 
| 343 |  |  |  |  |  |  | my $count_active = $tbl->get_count(filter => ['active = true']); | 
| 344 |  |  |  |  |  |  |  | 
| 345 |  |  |  |  |  |  | =cut | 
| 346 |  |  |  |  |  |  |  | 
| 347 |  |  |  |  |  |  | #----------------------------------------------------------------------- | 
| 348 |  |  |  |  |  |  |  | 
| 349 |  |  |  |  |  |  | ## Returns number of records | 
| 350 |  |  |  |  |  |  | sub get_count { | 
| 351 |  |  |  |  |  |  |  | 
| 352 |  |  |  |  |  |  | my $self   = shift; | 
| 353 |  |  |  |  |  |  | my $filter = \@_; | 
| 354 |  |  |  |  |  |  |  | 
| 355 |  |  |  |  |  |  | # Fetch number of records | 
| 356 |  |  |  |  |  |  | # SQL: select count(id) as c from $table where [filter] | 
| 357 |  |  |  |  |  |  | my @count = $self->fetch( | 
| 358 |  |  |  |  |  |  | fields => ['count(id) as c'], | 
| 359 |  |  |  |  |  |  | filter => $filter, | 
| 360 |  |  |  |  |  |  | ); | 
| 361 |  |  |  |  |  |  |  | 
| 362 |  |  |  |  |  |  | return $count[0]->{c}; | 
| 363 |  |  |  |  |  |  | } | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | #*********************************************************************** | 
| 366 |  |  |  |  |  |  |  | 
| 367 |  |  |  |  |  |  | =item B - delete records by identifier | 
| 368 |  |  |  |  |  |  |  | 
| 369 |  |  |  |  |  |  | Paramters: list of record id | 
| 370 |  |  |  |  |  |  |  | 
| 371 |  |  |  |  |  |  | Returns: 1 if ok, undef if error | 
| 372 |  |  |  |  |  |  |  | 
| 373 |  |  |  |  |  |  | Method deletes records from SQL table by it's identifiers. | 
| 374 |  |  |  |  |  |  |  | 
| 375 |  |  |  |  |  |  | if ($tbl->remove(5, 8 ,19)) { | 
| 376 |  |  |  |  |  |  | print "Records successfully removed."; | 
| 377 |  |  |  |  |  |  | } | 
| 378 |  |  |  |  |  |  |  | 
| 379 |  |  |  |  |  |  | =cut | 
| 380 |  |  |  |  |  |  |  | 
| 381 |  |  |  |  |  |  | #----------------------------------------------------------------------- | 
| 382 |  |  |  |  |  |  |  | 
| 383 |  |  |  |  |  |  | sub delete_by_id { | 
| 384 |  |  |  |  |  |  |  | 
| 385 |  |  |  |  |  |  | my ( $self, @ids ) = @_; | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | # TODO check for too long @id list | 
| 388 |  |  |  |  |  |  |  | 
| 389 |  |  |  |  |  |  | # Prepare SQL condition | 
| 390 |  |  |  |  |  |  | my $in_cond = "id in (" . join( ", ", @ids ) . ")"; | 
| 391 |  |  |  |  |  |  | my $sql     = "delete from " . $self->{table} . " where $in_cond"; | 
| 392 |  |  |  |  |  |  |  | 
| 393 |  |  |  |  |  |  | if ( $self->call($sql) ) { | 
| 394 |  |  |  |  |  |  | return 1; | 
| 395 |  |  |  |  |  |  | } else { | 
| 396 |  |  |  |  |  |  | return $self->error( "Can't delete records by Id: table='" . $self->{table} . "'" ); | 
| 397 |  |  |  |  |  |  | } | 
| 398 |  |  |  |  |  |  |  | 
| 399 |  |  |  |  |  |  | } | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | #*********************************************************************** | 
| 402 |  |  |  |  |  |  |  | 
| 403 |  |  |  |  |  |  | =item B - delete records | 
| 404 |  |  |  |  |  |  |  | 
| 405 |  |  |  |  |  |  | Paramters: list of filters | 
| 406 |  |  |  |  |  |  |  | 
| 407 |  |  |  |  |  |  | Returns: 1 if ok, undef if error | 
| 408 |  |  |  |  |  |  |  | 
| 409 |  |  |  |  |  |  | $tbl->delete( | 
| 410 |  |  |  |  |  |  | 'active = false', | 
| 411 |  |  |  |  |  |  | 'expire < now()', | 
| 412 |  |  |  |  |  |  | ); | 
| 413 |  |  |  |  |  |  |  | 
| 414 |  |  |  |  |  |  | =cut | 
| 415 |  |  |  |  |  |  |  | 
| 416 |  |  |  |  |  |  | #----------------------------------------------------------------------- | 
| 417 |  |  |  |  |  |  |  | 
| 418 |  |  |  |  |  |  | sub delete { | 
| 419 |  |  |  |  |  |  |  | 
| 420 |  |  |  |  |  |  | my ( $self, @filter ) = @_; | 
| 421 |  |  |  |  |  |  |  | 
| 422 |  |  |  |  |  |  | # Prepare WHERE filter | 
| 423 |  |  |  |  |  |  | my $req_filter = " where " . join( " and ", @filter ); | 
| 424 |  |  |  |  |  |  |  | 
| 425 |  |  |  |  |  |  | # Remove records | 
| 426 |  |  |  |  |  |  | $self->call( "delete from " . $self->{table} . $req_filter ); | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | } | 
| 429 |  |  |  |  |  |  |  | 
| 430 |  |  |  |  |  |  | #*********************************************************************** | 
| 431 |  |  |  |  |  |  |  | 
| 432 |  |  |  |  |  |  | =item B - get list of fields | 
| 433 |  |  |  |  |  |  |  | 
| 434 |  |  |  |  |  |  | Example: | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | my @fields = @{ $tbl->get_fields() }; | 
| 437 |  |  |  |  |  |  | print "Table fields: " . join (', ', @fields); | 
| 438 |  |  |  |  |  |  |  | 
| 439 |  |  |  |  |  |  | =cut | 
| 440 |  |  |  |  |  |  |  | 
| 441 |  |  |  |  |  |  | #----------------------------------------------------------------------- | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | sub get_fields { | 
| 444 |  |  |  |  |  |  | return [ keys %{ +shift->{'fields'} } ]; | 
| 445 |  |  |  |  |  |  | } | 
| 446 |  |  |  |  |  |  |  | 
| 447 |  |  |  |  |  |  |  | 
| 448 |  |  |  |  |  |  | #*********************************************************************** | 
| 449 |  |  |  |  |  |  |  | 
| 450 |  |  |  |  |  |  | =item B - check if field exists | 
| 451 |  |  |  |  |  |  |  | 
| 452 |  |  |  |  |  |  | Paramters: field name | 
| 453 |  |  |  |  |  |  |  | 
| 454 |  |  |  |  |  |  | Example: | 
| 455 |  |  |  |  |  |  |  | 
| 456 |  |  |  |  |  |  | if ($tbl->has_field('uuid')) { | 
| 457 |  |  |  |  |  |  | $tbl->call("delete tbldata where uuid=?", $uuid); | 
| 458 |  |  |  |  |  |  | } | 
| 459 |  |  |  |  |  |  |  | 
| 460 |  |  |  |  |  |  | B: this method works only for restricted tables that | 
| 461 |  |  |  |  |  |  | use C parameter at construction time. | 
| 462 |  |  |  |  |  |  |  | 
| 463 |  |  |  |  |  |  | =cut | 
| 464 |  |  |  |  |  |  |  | 
| 465 |  |  |  |  |  |  | #----------------------------------------------------------------------- | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | sub has_field { | 
| 468 |  |  |  |  |  |  |  | 
| 469 |  |  |  |  |  |  | # TODO | 
| 470 |  |  |  |  |  |  | # - check if fields defined at all | 
| 471 |  |  |  |  |  |  | # - think about multiple values | 
| 472 |  |  |  |  |  |  | return $_[0]->{'fields'}{ $_[1] } | 
| 473 |  |  |  |  |  |  | } | 
| 474 |  |  |  |  |  |  |  | 
| 475 |  |  |  |  |  |  | 1; | 
| 476 |  |  |  |  |  |  |  | 
| 477 |  |  |  |  |  |  | __END__ |