use
overload
'""'
=>
'stringify'
,
'fallback'
=> 1;
our
$VERSION
=
'0.010'
;
my
$debug
=
$ENV
{PERL_DEBUG} || 0;
sub
stringify {
my
$self
=
shift
;
return
$self
->_unwind;
}
my
%op_map
= (
'+'
=>
'AND'
,
''
=>
'OR'
,
'-'
=>
'AND'
,
);
sub
dbi {
my
$self
=
shift
;
$self
->{opts}->{delims} = 1;
my
$sql
=
$self
->_unwind;
my
@values
;
my
$start
=
chr
(2);
my
$end
=
chr
(3);
my
$opstart
=
chr
(5);
my
$opend
=
chr
(6);
$sql
=~ s/(
$opstart
|
$opend
)//go;
while
(
$sql
=~ s/
$start
(.+?)
$end
/\?/o ) {
push
(
@values
, $1 );
}
delete
$self
->{opts}->{delims};
return
[
$sql
, \
@values
];
}
sub
pairs {
my
$self
=
shift
;
my
@pairs
;
my
$vstart
=
chr
(2);
my
$vend
=
chr
(3);
my
$opstart
=
chr
(5);
my
$opend
=
chr
(6);
$self
->{opts}->{delims} = 1;
my
$sql
=
$self
->_unwind;
while
(
$sql
=~ m/([\.\w]+)\ ?
$opstart
(.+?)
$opend
\ ?
$vstart
(.+?)
$vend
/go )
{
push
(
@pairs
, [ $1, $2, $3 ] );
}
delete
$self
->{opts}->{delims};
return
\
@pairs
;
}
sub
rdbo {
my
$self
=
shift
;
$debug
and
warn
'='
x 80 .
"\n"
;
$debug
and
warn
"STRING: $self->{_string}\n"
;
$debug
and
warn
"PARSER: "
.
dump
(
$self
->{_parser} ) .
"\n"
;
my
$q
=
$self
->_orm;
$debug
and
warn
"rdbo q: "
.
dump
$q
;
my
$joiner
=
$self
->{_implicit_AND} ?
'AND'
:
'OR'
;
if
(
defined
$self
->{
'-'
} ) {
$joiner
=
'AND'
;
}
if
(
scalar
@$q
> 2 ) {
$debug
and
warn
"rdbo \$q > 2, joiner=$joiner"
;
return
[
$joiner
=>
$q
];
}
else
{
return
$q
;
}
}
sub
dbic {
my
$self
=
shift
;
$debug
and
warn
'='
x 80 .
"\n"
;
$debug
and
warn
"STRING: $self->{_string}\n"
;
$debug
and
warn
"PARSER: "
.
dump
(
$self
->{_parser} ) .
"\n"
;
$self
->{opts}->{dbic} = 1;
my
$q
=
$self
->_orm;
$debug
and
warn
"dbic q: "
.
dump
$q
;
delete
$self
->{opts}->{dbic};
my
$joiner
=
$self
->{_implicit_AND} ?
'-and'
:
'-or'
;
if
(
defined
$self
->{
'-'
} ) {
$joiner
=
'-and'
;
}
if
(
scalar
@$q
> 2 ) {
$debug
and
warn
"dbic \$q > 2, joiner=$joiner"
;
return
[
$joiner
=>
$q
];
}
else
{
return
$q
;
}
}
sub
parser {
shift
->{_parser};
}
sub
_orm {
my
$self
=
shift
;
my
$q
=
shift
||
$self
;
my
$query
;
my
$OR
=
$self
->{opts}->{dbic} ?
'-or'
:
'OR'
;
my
$AND
=
$self
->{opts}->{dbic} ?
'-and'
:
'AND'
;
for
my
$prefix
(
'+'
,
''
,
'-'
) {
next
unless
(
defined
$q
->{
$prefix
} and @{
$q
->{
$prefix
} } );
my
$joiner
=
$op_map
{
$prefix
};
$joiner
=
'-'
.
lc
(
$joiner
)
if
$self
->{opts}->{dbic};
$debug
and
warn
"prefix '$prefix' ($joiner): "
.
dump
$q
->{
$prefix
};
my
@op_subq
;
for
my
$subq
( @{
$q
->{
$prefix
} } ) {
my
$q
=
$self
->_orm_subq(
$subq
,
$prefix
);
my
$items
=
scalar
(
@$q
);
$debug
and
warn
"items $items $joiner : "
.
dump
$q
;
my
$sub_joiner
=
$prefix
eq
'-'
?
$AND
:
$OR
;
push
(
@op_subq
, (
$items
> 2 ) ? (
$sub_joiner
=>
$q
) :
@$q
);
}
$debug
and
warn
sprintf
(
"n subq == %d, joiner=%s, dump: %s\n"
,
scalar
(
@op_subq
),
$joiner
,
dump
\
@op_subq
);
if
(
$self
->{_parser}->{lower}
and
grep
{
ref
(
$_
) eq
'ARRAY'
}
@op_subq
)
{
push
@$query
,
$joiner
=> \
@op_subq
;
}
else
{
push
(
@$query
,
(
scalar
(
@op_subq
) > 2 )
? (
$joiner
=> \
@op_subq
)
:
@op_subq
);
}
}
return
$query
;
}
sub
_orm_subq {
my
$self
=
shift
;
my
$subQ
=
shift
;
my
$prefix
=
shift
;
my
$opts
=
$self
->{opts} || {};
return
$self
->_orm(
$subQ
->{value} )
if
$subQ
->{op} eq
'()'
;
my
@columns
=
$subQ
->{field}
? (
$subQ
->{field} )
: ( @{
$self
->{_parser}->{default_column} } );
my
$value
=
$self
->_doctor_value(
$subQ
);
my
$op
=
$subQ
->{op};
if
(
$op
eq
':'
) {
$op
=
'='
;
}
if
(
$prefix
eq
'-'
) {
$op
=
'!'
.
$op
;
}
if
(
$value
=~ m/\%/ ) {
$op
=
$prefix
eq
'-'
?
'!~'
:
'~'
;
}
my
@buf
;
for
my
$colname
(
@columns
) {
my
$column
=
$self
->{_parser}->get_column(
$colname
);
$value
=~ s/\%//g
if
$column
->is_int;
my
@pair
;
if
(
defined
$column
->orm_callback ) {
@pair
=
$column
->orm_callback->(
$column
,
$op
,
$value
);
}
elsif
(
$op
eq
'='
) {
@pair
= (
$colname
,
$value
);
}
elsif
(
$op
eq
'!='
) {
@pair
= (
$colname
, {
$op
=>
$value
} );
}
elsif
(
$op
eq
'~'
) {
@pair
= (
$colname
, {
$column
->
fuzzy_op
=>
$value
} );
}
elsif
(
$op
eq
'!~'
) {
@pair
= (
$colname
, {
$column
->
fuzzy_not_op
=>
$value
} );
}
else
{
croak
"unknown operator logic for column '$colname' op '$op' value '$value'"
;
}
if
( !
$column
->is_int and
$self
->{_parser}->{lower} ) {
my
$col
=
$pair
[0];
my
$val
=
$pair
[1];
my
$this_op
=
$op
;
if
(
ref
$val
) {
(
$this_op
,
$val
) =
each
%$val
;
}
@pair
= ( [ \
qq/lower($pair[0]) $this_op lower(?)/
,
$val
] );
}
push
@buf
,
@pair
;
}
return
\
@buf
;
}
sub
_unwind {
my
$self
=
shift
;
my
$q
=
shift
||
$self
;
my
@subQ
;
for
my
$prefix
(
'+'
,
''
,
'-'
) {
my
@clause
;
my
$joiner
=
$op_map
{
$prefix
};
for
my
$subq
( @{
$q
->{
$prefix
} } ) {
push
@clause
,
$self
->_unwind_subQ(
$subq
,
$prefix
);
}
next
if
!
@clause
;
push
(
@subQ
,
join
(
" $joiner "
,
grep
{
defined
&&
length
}
@clause
) );
}
return
join
(
" AND "
,
@subQ
);
}
sub
_doctor_value {
my
(
$self
,
$subQ
) =
@_
;
my
$value
=
$subQ
->{value};
if
(
$self
->{_parser}->{fuzzify} ) {
$value
.=
'*'
unless
$value
=~ m/[\*\%]/;
}
elsif
(
$self
->{_parser}->{fuzzify2} ) {
$value
=
"*$value*"
unless
$value
=~ m/[\*\%]/;
}
$value
=~ s/\*/\%/g;
return
$value
;
}
sub
_unwind_subQ {
my
$self
=
shift
;
my
$subQ
=
shift
;
my
$prefix
=
shift
;
my
$opts
=
$self
->{opts} || {};
return
"("
.
$self
->_unwind(
$subQ
->{value} ) .
")"
if
$subQ
->{op} eq
'()'
;
my
$col_quote
=
$self
->{_parser}->{quote_columns};
my
$use_lower
=
$self
->{_parser}->{lower};
my
@columns
=
$subQ
->{field}
? (
$subQ
->{field} )
: ( @{
$self
->{_parser}->{default_column} } );
my
$value
=
$self
->_doctor_value(
$subQ
);
my
$op
=
$subQ
->{op};
if
(
$op
eq
':'
) {
$op
=
'='
;
}
if
(
$prefix
eq
'-'
) {
$op
=
'!'
.
$op
;
}
if
(
$value
=~ m/\%/ ) {
$op
=
$prefix
eq
'-'
?
'!~'
:
'~'
;
}
my
@buf
;
COLNAME:
for
my
$colname
(
@columns
) {
my
$column
=
$self
->{_parser}->get_column(
$colname
);
$value
=~ s/\%//g
if
$column
->is_int;
my
$this_op
;
my
$quote
=
$column
->is_int ?
""
:
"'"
;
my
$prefix
=
''
;
my
$suffix
=
''
;
if
( !
$column
->is_int and
$use_lower
) {
$prefix
=
'lower('
;
$suffix
=
')'
;
}
if
(
$op
=~ m/\~/ ) {
if
(
$op
eq
'!~'
) {
if
(
$column
->is_int ) {
$this_op
=
$column
->fuzzy_not_op;
}
else
{
$this_op
=
' '
.
$column
->fuzzy_not_op .
' '
;
}
}
else
{
if
(
$column
->is_int ) {
$this_op
=
$column
->fuzzy_op;
}
else
{
$this_op
=
' '
.
$column
->fuzzy_op .
' '
;
}
}
}
else
{
$this_op
=
$op
;
}
if
(
defined
$column
->callback ) {
push
(
@buf
,
$column
->callback->(
$column
,
$this_op
,
$value
) );
next
COLNAME;
}
if
(
$opts
->{delims} ) {
push
(
@buf
,
join
(
''
,
$prefix
,
$col_quote
,
$colname
,
$col_quote
,
$suffix
,
chr
(5),
$this_op
,
chr
(6),
$prefix
,
chr
(2),
$value
,
chr
(3),
$suffix
, )
);
}
else
{
push
(
@buf
,
join
(
''
,
$prefix
,
$col_quote
,
$colname
,
$col_quote
,
$suffix
,
$this_op
,
$prefix
,
$quote
,
$value
,
$quote
,
$suffix
, )
);
}
}
my
$joiner
=
$prefix
eq
'-'
?
' AND '
:
' OR '
;
return
(
scalar
(
@buf
) > 1 ?
'('
:
''
)
.
join
(
$joiner
,
@buf
)
. (
scalar
(
@buf
) > 1 ?
')'
:
''
);
}
1;