File Coverage

blib/lib/Rose/DB/SQLite.pm
Criterion Covered Total %
statement 40 211 18.9
branch 0 66 0.0
condition 0 46 0.0
subroutine 14 39 35.9
pod 7 22 31.8
total 61 384 15.8


line stmt bran cond sub pod time code
1             package Rose::DB::SQLite;
2              
3 16     16   113 use strict;
  16         37  
  16         476  
4              
5 16     16   78 use Carp();
  16         41  
  16         245  
6              
7 16     16   70 use Rose::DB;
  16         30  
  16         275  
8 16     16   7221 use SQL::ReservedWords::SQLite();
  16         20085  
  16         723  
9              
10             our $VERSION = '0.765';
11              
12             #our $Debug = 0;
13              
14             use Rose::Class::MakeMethods::Generic
15             (
16 16         111 inheritable_scalar => 'coerce_autoincrement_to_serial',
17 16     16   126 );
  16         32  
18              
19             __PACKAGE__->coerce_autoincrement_to_serial(1);
20              
21             #
22             # Object methods
23             #
24              
25             sub build_dsn
26             {
27 0     0 0 0 my($self_or_class, %args) = @_;
28              
29 0         0 my %info;
30              
31 0   0     0 $info{'dbname'} = $args{'db'} || $args{'database'};
32              
33             return
34             "dbi:SQLite:" .
35 0         0 join(';', map { "$_=$info{$_}" } grep { defined $info{$_} } qw(dbname));
  0         0  
  0         0  
36             }
37              
38 4     4 0 16 sub dbi_driver { 'SQLite' }
39              
40 0     0 1   sub sqlite_unicode { shift->dbh_attribute_boolean('sqlite_unicode', @_) }
41              
42             sub init_dbh
43             {
44 0     0 0   my($self) = shift;
45              
46 0           my $database = $self->database;
47              
48 0 0 0       unless($self->auto_create || -e $database)
49             {
50 0           Carp::croak "Refusing to create non-existent SQLite database ",
51             "file: '$database'";
52             }
53              
54 0           my $method = ref($self)->parent_class . '::init_dbh';
55              
56 16     16   6514 no strict 'refs';
  16         49  
  16         1658  
57 0           return $self->$method(@_);
58             }
59              
60 0     0 0   sub last_insertid_from_sth { shift->dbh->func('last_insert_rowid') }
61              
62 0     0 0   sub supports_multi_column_count_distinct { 0 }
63              
64             sub validate_date_keyword
65             {
66 16     16   122 no warnings;
  16         46  
  16         2726  
67 0 0 0 0 1   !ref $_[1] && (lc $_[1] eq 'current_timestamp' ||
      0        
68             ($_[0]->keyword_function_calls && $_[1] =~ /^\w+\(.*\)$/));
69             }
70              
71             sub validate_datetime_keyword
72             {
73 16     16   118 no warnings;
  16         32  
  16         2372  
74 0 0 0 0 1   !ref $_[1] && (lc $_[1] eq 'current_timestamp' ||
      0        
75             ($_[0]->keyword_function_calls && $_[1] =~ /^\w+\(.*\)$/));
76             }
77              
78             sub validate_timestamp_keyword
79             {
80 16     16   115 no warnings;
  16         30  
  16         9743  
81 0 0 0 0 1   !ref $_[1] && (lc $_[1] eq 'current_timestamp' ||
      0        
82             ($_[0]->keyword_function_calls && $_[1] =~ /^\w+\(.*\)$/));
83             }
84              
85 0     0 0   sub should_inline_date_keyword { 1 }
86 0     0 0   sub should_inline_datetime_keyword { 1 }
87 0     0 0   sub should_inline_time_keyword { 1 }
88 0     0 0   sub should_inline_timestamp_keyword { 1 }
89              
90             sub parse_date
91             {
92 0     0 1   my($self, $value) = @_;
93              
94 0 0 0       if(UNIVERSAL::isa($value, 'DateTime') ||
95             $self->validate_date_keyword($value))
96             {
97 0           return $value;
98             }
99              
100 0           my($dt, $error);
101              
102             TRY:
103             {
104 0           local $@;
  0            
105 0           eval { $dt = Rose::DateTime::Util::parse_date($value) };
  0            
106 0           $error = $@;
107             }
108              
109 0 0         if($error)
110             {
111 0           $self->error("Could not parse date '$value' - $error");
112 0           return undef;
113             }
114              
115 0           return $dt;
116             }
117              
118             sub parse_datetime
119             {
120 0     0 1   my($self, $value) = @_;
121              
122 0 0 0       if(UNIVERSAL::isa($value, 'DateTime') ||
123             $self->validate_datetime_keyword($value))
124             {
125 0           return $value;
126             }
127              
128 0           my($dt, $error);
129              
130             TRY:
131             {
132 0           local $@;
  0            
133 0           eval { $dt = Rose::DateTime::Util::parse_date($value) };
  0            
134 0           $error = $@;
135             }
136              
137 0 0         if($error)
138             {
139 0           $self->error("Could not parse datetime '$value' - $error");
140 0           return undef;
141             }
142              
143 0           return $dt;
144             }
145              
146             sub parse_timestamp
147             {
148 0     0 1   my($self, $value) = @_;
149              
150 0 0 0       if(UNIVERSAL::isa($value, 'DateTime') ||
151             $self->validate_timestamp_keyword($value))
152             {
153 0           return $value;
154             }
155              
156 0           my($dt, $error);
157              
158             TRY:
159             {
160 0           local $@;
  0            
161 0           eval { $dt = Rose::DateTime::Util::parse_date($value) };
  0            
162 0           $error = $@;
163             }
164              
165 0 0         if($error)
166             {
167 0           $self->error("Could not parse timestamp '$value' - $error");
168 0           return undef;
169             }
170              
171 0           return $dt;
172             }
173              
174             sub format_bitfield
175             {
176 0     0 0   my($self, $vec, $size) = @_;
177 0 0         $vec = Bit::Vector->new_Bin($size, $vec->to_Bin) if($size);
178 0           return q(b') . $vec->to_Bin . q(');
179             }
180              
181             sub refine_dbi_column_info
182             {
183 0     0 0   my($self, $col_info) = @_;
184              
185 0           my $method = ref($self)->parent_class . '::refine_dbi_column_info';
186              
187 16     16   143 no strict 'refs';
  16         32  
  16         18564  
188 0           $self->$method($col_info);
189              
190 0 0 0       if($col_info->{'TYPE_NAME'} eq 'bit')
    0          
191             {
192 0           $col_info->{'TYPE_NAME'} = 'bits';
193             }
194              
195             elsif($col_info->{'TYPE_NAME'} eq 'datetime' && defined $col_info->{'COLUMN_DEF'})
196             {
197 0 0         if(lc $col_info->{'COLUMN_DEF'} eq 'current_timestamp')
198             {
199             # Translate "current time" value into something that our date parser
200             # will understand.
201 0           $col_info->{'COLUMN_DEF'} = 'now';
202              
203             # ...or let the database handle this?
204             #$col_info->{'COLUMN_DEF'} = undef;
205             }
206             }
207              
208 0           return;
209             }
210              
211 0     0 0   sub likes_implicit_joins { 1 }
212              
213             *is_reserved_word = \&SQL::ReservedWords::SQLite::is_reserved;
214              
215             sub quote_column_name
216             {
217 0     0 0   my $name = $_[1];
218 0           $name =~ s/"/""/g;
219 0           return qq("$name");
220             }
221              
222             sub quote_table_name
223             {
224 0     0 0   my $name = $_[1];
225 0           $name =~ s/"/""/g;
226 0           return qq("$name");
227             }
228              
229             #
230             # Introspection
231             #
232              
233             sub list_tables
234             {
235 0     0 0   my($self, %args) = @_;
236              
237 0 0         my $types = $args{'include_views'} ? q('table', 'view') : q('table');
238              
239 0           my(@tables, $error);
240              
241             TRY:
242             {
243 0           local $@;
  0            
244              
245             eval
246 0           {
247 0 0         my $dbh = $self->dbh or die $self->error;
248              
249 0           local $dbh->{'RaiseError'} = 1;
250              
251 0           my $sth = $dbh->prepare("SELECT name FROM sqlite_master WHERE type IN($types)");
252 0           $sth->execute;
253              
254 0           my $name;
255 0           $sth->bind_columns(\$name);
256              
257 0           while($sth->fetch)
258             {
259 0           push(@tables, $name);
260             }
261             };
262              
263 0           $error = $@;
264             }
265              
266 0 0         if($error)
267             {
268 0           Carp::croak "Could not list tables from ", $self->dsn, " - $error";
269             }
270              
271 0 0         return wantarray ? @tables : \@tables;
272             }
273              
274             sub _get_primary_key_column_names
275             {
276 0     0     my($self, $catalog, $schema, $table) = @_;
277 0   0       my $pk_columns = ($self->_table_info($table))[1] || [];
278 0           return $pk_columns;
279             }
280              
281             sub _table_info
282             {
283 0     0     my($self, $table) = @_;
284              
285 0 0         my $dbh = $self->dbh or Carp::croak $self->error;
286              
287 0           my $table_unquoted = $self->unquote_table_name($table);
288              
289 0           my $sth = $dbh->prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?");
290 0           my $sql;
291              
292 0           $sth->execute($table_unquoted);
293 0           $sth->bind_columns(\$sql);
294 0           $sth->fetch;
295 0           $sth->finish;
296              
297 0           return $self->_info_from_sql($sql);
298             }
299              
300             ## Yay! A Giant Wad o' Regexes "parser"! Yeah, this is lame, but I really
301             ## don't want to load an actual parser, or even a regex lib or helper...
302              
303             our $Paren_Depth = 15;
304             our $Nested_Parens = '\(' . '([^()]|\(' x $Paren_Depth . '[^()]*' . '\))*' x $Paren_Depth . '\)';
305              
306             # This doesn't seem to work...
307             #$Nested_Parens = qr{\( (?: (?> [^()]+ ) | (??{ $Nested_Parens }) )* \)}x;
308              
309             our $Quoted =
310             qr{(?: ' (?: [^'] | '' )+ '
311             | " (?: [^"] | "" )+ "
312             | ` (?: [^`] | `` )+ `)}six;
313              
314             our $Name = qr{(?: $Quoted | \w+ )}six;
315              
316             our $Type =
317             qr{\w+ (?: \s* \( \s* \d+ \s* (?: , \s* \d+ \s*)? \) )?}six;
318              
319             our $Conflict_Algorithm =
320             qr{(?: ROLLBACK | ABORT | FAIL | IGNORE | REPLACE )}six;
321              
322             our $Conflict_Clause =
323             qr{(?: ON \s+ CONFLICT \s+ $Conflict_Algorithm )}six;
324              
325             our $Sort_Order =
326             qr{(?: COLLATE \s+ \S+ \s+)? (?:ASC | DESC)}six;
327              
328             our $Column_Constraint =
329             qr{(?: NOT \s+ NULL (?: \s+ $Conflict_Clause)?
330             | PRIMARY \s+ KEY (?: \s+ $Sort_Order)? (?: \s+ $Conflict_Clause)? (?: \s+ AUTOINCREMENT)?
331             | UNIQUE (?: \s+ $Conflict_Clause)?
332             | CHECK \s* $Nested_Parens (?: \s+ $Conflict_Clause)?
333             | REFERENCES \s+ $Name \s* \( \s* $Name \s* \)
334             | DEFAULT \s+ (?: $Name | \w+ \s* $Nested_Parens | [^,)]+ )
335             | COLLATE \s+ \S+)}six;
336              
337             our $Table_Constraint =
338             qr{(?: (?: PRIMARY \s+ KEY | UNIQUE | CHECK ) \s* $Nested_Parens
339             | FOREIGN \s+ KEY \s+ (?: $Name \s+ )? $Nested_Parens \s+ REFERENCES \s+ $Name \s+ $Nested_Parens )}six;
340              
341             our $Column_Def =
342             qr{($Name) (?:\s+ ($Type))? ( (?: \s+ (?:CONSTRAINT \s+ $Name \s+)? $Column_Constraint )* )}six;
343              
344             # SQLite allows C comments to be unterminated if they're at the end of the
345             # input stream. Crazy, but true: http://www.sqlite.org/lang_comment.html
346             our $C_Comment_Cont = qr{/\*.*$}six;
347             our $C_Comment = qr{/\*[^*]*\*+(?:[^/*][^*]*\*+)*/}six;
348             our $SQL_Comment = qr{--[^\r\n]*(\r?\n)}six;
349             our $Comment = qr{($Quoted)|($C_Comment|$SQL_Comment|$C_Comment_Cont)}six;
350              
351             # These constants are from the DBI documentation. Is there somewhere
352             # I can load these from?
353 16     16   152 use constant SQL_NO_NULLS => 0;
  16         46  
  16         1079  
354 16     16   113 use constant SQL_NULLABLE => 1;
  16         35  
  16         2607  
355              
356             sub _info_from_sql
357             {
358 0     0     my($self, $sql) = @_;
359              
360 0           my(@col_info, @pk_columns, @uk_info);
361              
362 0           my($new_sql, $pos);
363              
364 0   0       my $class = ref($self) || $self;
365              
366             # Remove comments
367 0           while($sql =~ /\G((.*?)$Comment)/sgix)
368             {
369 0           $pos = pos($sql);
370              
371 0 0         if(defined $4) # caught comment
372             {
373 16     16   116 no warnings 'uninitialized';
  16         41  
  16         16296  
374 0           $new_sql .= "$2$3";
375             }
376             else
377             {
378 0           $new_sql .= $1;
379             }
380             }
381              
382 0 0         $sql = $new_sql . substr($sql, $pos) if(defined $new_sql);
383              
384             # Remove the start and end
385 0           $sql =~ s/^\s* CREATE \s+ (?:TEMP(?:ORARY)? \s+)? TABLE \s+ $Name \s*\(\s*//sgix;
386 0           $sql =~ s/\s*\)\s*$//six;
387              
388             # Remove leading space from lines
389 0           $sql =~ s/^\s+//mg;
390              
391 0           my $i = 1;
392              
393             # Column definitions
394 0           while($sql =~ s/^$Column_Def (?:\s*,\s*|\s*$)//six)
395             {
396 0           my $col_name = _unquote_name($1);
397 0   0       my $col_type = $2 || 'scalar';
398 0           my $constraints = $3;
399              
400 0 0         unless(defined $col_name)
401             {
402 0           Carp::croak "Could not extract column name from SQL: $sql";
403             }
404              
405 0           my %col_info =
406             (
407             COLUMN_NAME => $col_name,
408             TYPE_NAME => $col_type,
409             ORDINAL_POSITION => $i++,
410             );
411              
412 0 0         if($col_type =~ /^(\w+) \s* \( \s* (\d+) \s* \)$/x)
    0          
413             {
414 0           $col_info{'TYPE_NAME'} = $1;
415 0           $col_info{'COLUMN_SIZE'} = $2;
416 0           $col_info{'CHAR_OCTET_LENGTH'} = $2;
417             }
418             elsif($col_type =~ /^\s* (\w+) \s* \( \s* (\d+) \s* , \s* (\d+) \s* \) \s*$/x)
419             {
420 0           $col_info{'TYPE_NAME'} = $1;
421 0           $col_info{'DECIMAL_DIGITS'} = $2;
422 0           $col_info{'COLUMN_SIZE'} = $3;
423             }
424              
425 0           while($constraints =~ s/^\s* (?:CONSTRAINT \s+ $Name \s+)? ($Column_Constraint) \s*//six)
426             {
427 0           local $_ = $1;
428              
429 0 0         if(/^DEFAULT \s+ ( $Name | \w+ \s* $Nested_Parens | [^,)]+ )/six)
    0          
    0          
    0          
430             {
431 0           $col_info{'COLUMN_DEF'} = _unquote_name($1);
432             }
433             elsif(/^PRIMARY (?: \s+ KEY )? \b (?: .*? (AUTOINCREMENT) )?/six)
434             {
435 0           push(@pk_columns, $col_name);
436              
437 0 0 0       if($1 && $class->coerce_autoincrement_to_serial)
438             {
439 0           $col_info{'TYPE_NAME'} = 'serial';
440             }
441             }
442             elsif(/^\s* UNIQUE (?: \s+ KEY)? \b/six)
443             {
444 0           push(@uk_info, [ $col_name ]);
445             }
446             elsif(/^NOT \s+ NULL \b/six)
447             {
448 0           $col_info{'NULLABLE'} = SQL_NO_NULLS;
449             }
450             }
451              
452 0 0         $col_info{'NULLABLE'} = SQL_NULLABLE unless(defined $col_info{'NULLABLE'});
453              
454 0           push(@col_info, \%col_info);
455             }
456              
457 0           while($sql =~ s/^($Table_Constraint) (?:\s*,\s*|\s*$)//six)
458             {
459 0           my $constraint = $1;
460              
461 0 0         if($constraint =~ /^\s* PRIMARY \s+ KEY \s* ($Nested_Parens)/six)
    0          
462             {
463 0           @pk_columns = ();
464              
465 0           my $columns = $1;
466 0           $columns =~ s/^\(\s*//;
467 0           $columns =~ s/\s*\)\s*$//;
468              
469 0           while($columns =~ s/^\s* ($Name) (?:\s*,\s*|\s*$)//six)
470             {
471 0           push(@pk_columns, _unquote_name($1));
472             }
473             }
474             elsif($constraint =~ /^\s* UNIQUE \s* ($Nested_Parens)/six)
475             {
476 0           my $columns = $1;
477 0           $columns =~ s/^\(\s*//;
478 0           $columns =~ s/\s*\)\s*$//;
479              
480 0           my @uk_columns;
481              
482 0           while($columns =~ s/^\s* ($Name) (?:\s*,\s*|\s*$)//six)
483             {
484 0           push(@uk_columns, _unquote_name($1));
485             }
486              
487 0           push(@uk_info, \@uk_columns);
488             }
489             }
490              
491 0           return(\@col_info, \@pk_columns, \@uk_info);
492             }
493              
494             sub _unquote_name
495             {
496 0     0     my $name = shift;
497              
498 0 0         if($name =~ s/^(['`"]) ( (?: [^\1]+ | \1\1 )+ ) \1 $/$2/six)
499             {
500 0           my $q = $1;
501 0           $name =~ s/$q$q/$q/g;
502             }
503              
504 0           return $name;
505             }
506              
507             1;
508              
509             __END__
510              
511             =head1 NAME
512              
513             Rose::DB::SQLite - SQLite driver class for Rose::DB.
514              
515             =head1 SYNOPSIS
516              
517             use Rose::DB;
518              
519             Rose::DB->register_db(
520             domain => 'development',
521             type => 'main',
522             driver => 'sqlite',
523             database => '/path/to/some/file.db',
524             );
525              
526              
527             Rose::DB->default_domain('development');
528             Rose::DB->default_type('main');
529             ...
530              
531             # Set max length of varchar columns used to emulate the array data type
532             Rose::DB::SQLite->max_array_characters(128);
533              
534             $db = Rose::DB->new; # $db is really a Rose::DB::SQLite-derived object
535             ...
536              
537             =head1 DESCRIPTION
538              
539             L<Rose::DB> blesses objects into a class derived from L<Rose::DB::SQLite> when the L<driver|Rose::DB/driver> is "sqlite". This mapping of driver names to class names is configurable. See the documentation for L<Rose::DB>'s L<new()|Rose::DB/new> and L<driver_class()|Rose::DB/driver_class> methods for more information.
540              
541             This class cannot be used directly. You must use L<Rose::DB> and let its L<new()|Rose::DB/new> method return an object blessed into the appropriate class for you, according to its L<driver_class()|Rose::DB/driver_class> mappings.
542              
543             This class supports SQLite version 3 only. See the SQLite web site for more information on the major versions of SQLite:
544              
545             L<http://www.sqlite.org/>
546              
547             Only the methods that are new or have different behaviors than those in L<Rose::DB> are documented here. See the L<Rose::DB> documentation for the full list of methods.
548              
549             =head1 DATA TYPES
550              
551             SQLite doesn't care what value you pass for a given column, regardless of that column's nominal data type. L<Rose::DB> does care, however. The following data type formats are enforced by L<Rose::DB::SQLite>'s L<parse_*|Rose::DB/"Value Parsing and Formatting"> and L<format_*|Rose::DB/"Value Parsing and Formatting"> functions.
552              
553             Type Format
554             --------- ------------------------------
555             DATE YYYY-MM-DD
556             DATETIME YYYY-MM-DD HH:MM::SS
557             TIMESTAMP YYYY-MM-DD HH:MM::SS.NNNNNNNNN
558              
559             =head1 CLASS METHODS
560              
561             =over 4
562              
563             =item B<coerce_autoincrement_to_serial [BOOL]>
564              
565             Get or set a boolean value that indicates whether or not "auto-increment" columns will be considered to have the column type "serial." The default value is true.
566              
567             This setting comes into play when L<Rose::DB::Object::Loader> is used to auto-create column metadata based on an existing database schema.
568              
569             =item B<max_array_characters [INT]>
570              
571             Get or set the maximum length of varchar columns used to emulate the array data type. The default value is 255.
572              
573             SQLite does not have a native "ARRAY" data type, but it can be emulated using a "VARCHAR" column and a specially formatted string. The formatting and parsing of this string is handled by the C<format_array()> and C<parse_array()> object methods. The maximum length limit is honored by the C<format_array()> object method.
574              
575             =back
576              
577             =head1 OBJECT METHODS
578              
579             =over 4
580              
581             =item B<auto_create [BOOL]>
582              
583             Get or set a boolean value indicating whether or not a new SQLite L<database|Rose::DB/database> should be created if it does not already exist. Defaults to true.
584              
585             If false, and if the specified L<database|Rose::DB/database> does not exist, then a fatal error will occur when an attempt is made to L<connect|Rose::DB/connect> to the database.
586              
587             =item B<sqlite_unicode [BOOL]>
588              
589             Get or set a boolean value that indicates whether or not Perl's UTF-8 flag will be set for all text strings coming out of the database. See the L<DBD::SQLite|DBD::SQLite/sqlite_unicode> documentation for more information.
590              
591             =back
592              
593             =head2 Value Parsing and Formatting
594              
595             =over 4
596              
597             =item B<format_array ARRAYREF | LIST>
598              
599             Given a reference to an array or a list of values, return a specially formatted string. Undef is returned if ARRAYREF points to an empty array or if LIST is not passed. The array or list must not contain undefined values.
600              
601             If the resulting string is longer than C<max_array_characters()>, a fatal error will occur.
602              
603             =item B<parse_array STRING | LIST | ARRAYREF>
604              
605             Parse STRING and return a reference to an array. STRING should be formatted according to the SQLite array data type emulation format returned by C<format_array()>. Undef is returned if STRING is undefined.
606              
607             If a LIST of more than one item is passed, a reference to an array containing the values in LIST is returned.
608              
609             If a an ARRAYREF is passed, it is returned as-is.
610              
611             =item B<parse_date STRING>
612              
613             Parse STRING and return a L<DateTime> object. STRING should be formatted according to the Informix "DATE" data type.
614              
615             If STRING is a valid date keyword (according to L<validate_date_keyword|/validate_date_keyword>) it is returned unmodified. Returns undef if STRING could not be parsed as a valid "DATE" value.
616              
617             =item B<parse_datetime STRING>
618              
619             Parse STRING and return a L<DateTime> object. STRING should be formatted according to the Informix "DATETIME" data type.
620              
621             If STRING is a valid datetime keyword (according to L<validate_datetime_keyword|/validate_datetime_keyword>) it is returned unmodified. Returns undef if STRING could not be parsed as a valid "DATETIME" value.
622              
623             =item B<parse_timestamp STRING>
624              
625             Parse STRING and return a L<DateTime> object. STRING should be formatted according to the Informix "DATETIME" data type.
626              
627             If STRING is a valid timestamp keyword (according to L<validate_timestamp_keyword|/validate_timestamp_keyword>) it is returned unmodified. Returns undef if STRING could not be parsed as a valid "DATETIME" value.
628              
629             =item B<validate_date_keyword STRING>
630              
631             Returns true if STRING is a valid keyword for the "date" data type. Valid date keywords are:
632              
633             current_timestamp
634              
635             The keywords are not case sensitive. Any string that looks like a function call (matches /^\w+\(.*\)$/) is also considered a valid date keyword if L<keyword_function_calls|Rose::DB/keyword_function_calls> is true.
636              
637             =item B<validate_datetime_keyword STRING>
638              
639             Returns true if STRING is a valid keyword for the "datetime" data type, false otherwise. Valid datetime keywords are:
640              
641             current_timestamp
642              
643             The keywords are not case sensitive. Any string that looks like a function call (matches /^\w+\(.*\)$/) is also considered a valid datetime keyword if L<keyword_function_calls|Rose::DB/keyword_function_calls> is true.
644              
645             =item B<validate_timestamp_keyword STRING>
646              
647             Returns true if STRING is a valid keyword for the "timestamp" data type, false otherwise. Valid timestamp keywords are:
648              
649             current_timestamp
650              
651             The keywords are not case sensitive. Any string that looks like a function call (matches /^\w+\(.*\)$/) is also considered a valid timestamp keyword if L<keyword_function_calls|Rose::DB/keyword_function_calls> is true.
652              
653             =back
654              
655             =head1 AUTHOR
656              
657             John C. Siracusa (siracusa@gmail.com)
658              
659             =head1 LICENSE
660              
661             Copyright (c) 2010 by John C. Siracusa. All rights reserved. This program is
662             free software; you can redistribute it and/or modify it under the same terms
663             as Perl itself.