#!/usr/bin/env perl
use strict;
use Test::More;
use Test::Exception;

use RDF::Query;
use RDF::Query::Parser::SPARQL;

if ($ENV{RDFQUERY_DEV_MYSQL}) {
	plan 'no_plan';
} else {
	plan tests => 34;
}

use_ok( 'RDF::Query::Compiler::SQL' );

my $parser		= new RDF::Query::Parser::SPARQL ();


{
	my $uri	= 'http://xmlns.com/foaf/0.1/name';
	my $node	= RDF::Query::Node::Resource->new( $uri );
	my $hash	= RDF::Query::Compiler::SQL->_mysql_node_hash( $node );
	is( $hash, '14911999128994829034', 'URI hash' );
}

{
	my $node	= RDF::Query::Node::Literal->new( 'kasei' );
	my $hash	= RDF::Query::Compiler::SQL->_mysql_node_hash( $node );
	is( $hash, '12775641923308277283', 'literal hash' );
}

{
	my $hash	= RDF::Query::Compiler::SQL::_mysql_hash( 'LTom Croucher<en>' );
	is( $hash, '14336915341960534814', 'language-typed literal hash' );
}

{
	my $node	= RDF::Query::Node::Literal->new( 'Tom Croucher', 'en' );
	my $hash	= RDF::Query::Compiler::SQL->_mysql_node_hash( $node );
	is( $hash, '14336915341960534814', 'language-typed literal node hash 1' );
}

{
	my $node	= RDF::Query::Node::Literal->new( 'RDF', 'en' );
	my $hash	= RDF::Query::Compiler::SQL->_mysql_node_hash( $node );
	is( $hash, '16625494614570964497', 'language-typed literal node hash 2' );
}



{
	my $parsed	= $parser->parse(<<"END");
		PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
		SELECT	?person ?name
		WHERE	{ ?person foaf:name ?name }
END
	my $compiler	= RDF::Query::Compiler::SQL->new( $parsed, 'model' );
	isa_ok( $compiler, 'RDF::Query::Compiler::SQL' );
	
	my $sql		= $compiler->compile();
	is( $sql, "SELECT\n\ts2.subject AS person_Node,\n\tljr0.URI AS person_URI,\n\tljl0.Value AS person_Value,\n\tljl0.Language AS person_Language,\n\tljl0.Datatype AS person_Datatype,\n\tljb0.Name AS person_Name,\n\ts2.object AS name_Node,\n\tljr1.URI AS name_URI,\n\tljl1.Value AS name_Value,\n\tljl1.Language AS name_Language,\n\tljl1.Datatype AS name_Datatype,\n\tljb1.Name AS name_Name\nFROM\n\tStatements15799945864759145248 s2 LEFT JOIN Resources ljr0 ON (s2.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s2.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s2.subject = ljb0.ID) LEFT JOIN Resources ljr1 ON (s2.object = ljr1.ID) LEFT JOIN Literals ljl1 ON (s2.object = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s2.object = ljb1.ID)\nWHERE\n\ts2.predicate = 14911999128994829034", "select people and names" );
}

{
	my $parsed	= $parser->parse(<<"END");
		PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
		SELECT	?person ?name
		WHERE	{
					?person foaf:name ?name .
				}
END
	my $compiler	= RDF::Query::Compiler::SQL->new( $parsed );
	isa_ok( $compiler, 'RDF::Query::Compiler::SQL' );
	
	my $sql		= $compiler->compile();
	is( $sql, "SELECT\n\ts2.subject AS person_Node,\n\tljr0.URI AS person_URI,\n\tljl0.Value AS person_Value,\n\tljl0.Language AS person_Language,\n\tljl0.Datatype AS person_Datatype,\n\tljb0.Name AS person_Name,\n\ts2.object AS name_Node,\n\tljr1.URI AS name_URI,\n\tljl1.Value AS name_Value,\n\tljl1.Language AS name_Language,\n\tljl1.Datatype AS name_Datatype,\n\tljb1.Name AS name_Name\nFROM\n\tStatements s2 LEFT JOIN Resources ljr0 ON (s2.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s2.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s2.subject = ljb0.ID) LEFT JOIN Resources ljr1 ON (s2.object = ljr1.ID) LEFT JOIN Literals ljl1 ON (s2.object = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s2.object = ljb1.ID)\nWHERE\n\ts2.predicate = 14911999128994829034", "select people and names" );
}

{
	my $parsed	= $parser->parse(<<"END");
		PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
		SELECT	?person ?name ?homepage
		WHERE	{
					?person foaf:name ?name ; foaf:homepage ?homepage
				}
END

	my $compiler	= RDF::Query::Compiler::SQL->new( $parsed );
	my $sql		= $compiler->compile();
	is( $sql, "SELECT\n\ts2.subject AS person_Node,\n\tljr0.URI AS person_URI,\n\tljl0.Value AS person_Value,\n\tljl0.Language AS person_Language,\n\tljl0.Datatype AS person_Datatype,\n\tljb0.Name AS person_Name,\n\ts2.object AS name_Node,\n\tljr1.URI AS name_URI,\n\tljl1.Value AS name_Value,\n\tljl1.Language AS name_Language,\n\tljl1.Datatype AS name_Datatype,\n\tljb1.Name AS name_Name,\n\ts3.object AS homepage_Node,\n\tljr2.URI AS homepage_URI,\n\tljl2.Value AS homepage_Value,\n\tljl2.Language AS homepage_Language,\n\tljl2.Datatype AS homepage_Datatype,\n\tljb2.Name AS homepage_Name\nFROM\n\tStatements s2 LEFT JOIN Resources ljr0 ON (s2.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s2.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s2.subject = ljb0.ID) LEFT JOIN Resources ljr1 ON (s2.object = ljr1.ID) LEFT JOIN Literals ljl1 ON (s2.object = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s2.object = ljb1.ID),\n\tStatements s3 LEFT JOIN Resources ljr2 ON (s3.object = ljr2.ID) LEFT JOIN Literals ljl2 ON (s3.object = ljl2.ID) LEFT JOIN Bnodes ljb2 ON (s3.object = ljb2.ID)\nWHERE\n\ts2.predicate = 14911999128994829034 AND\n\ts3.subject = s2.subject AND\n\ts3.predicate = 9768710922987392204", "select people, names, and homepages" );
}

{
	my $parsed	= $parser->parse(<<"END");
		PREFIX foaf: <http://xmlns.com/foaf/0.1/>
		SELECT ?x ?name
		FROM NAMED <file://data/named_graphs/alice.rdf>
		FROM NAMED <file://data/named_graphs/bob.rdf>
		WHERE {
			GRAPH <foo:bar> { ?x foaf:name ?name }
		}
END

	my $compiler	= RDF::Query::Compiler::SQL->new( $parsed );
	my $sql		= $compiler->compile();
	is( $sql, "SELECT\n\ts3.subject AS x_Node,\n\tljr0.URI AS x_URI,\n\tljl0.Value AS x_Value,\n\tljl0.Language AS x_Language,\n\tljl0.Datatype AS x_Datatype,\n\tljb0.Name AS x_Name,\n\ts3.object AS name_Node,\n\tljr1.URI AS name_URI,\n\tljl1.Value AS name_Value,\n\tljl1.Language AS name_Language,\n\tljl1.Datatype AS name_Datatype,\n\tljb1.Name AS name_Name\nFROM\n\tStatements s3 LEFT JOIN Resources ljr0 ON (s3.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s3.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s3.subject = ljb0.ID) LEFT JOIN Resources ljr1 ON (s3.object = ljr1.ID) LEFT JOIN Literals ljl1 ON (s3.object = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s3.object = ljb1.ID)\nWHERE\n\ts3.predicate = 14911999128994829034 AND\n\ts3.context = 2618056589919804847", "select people and names of context-specific graph" );
}

{
	my $parsed	= $parser->parse(<<"END");
		PREFIX foaf: <http://xmlns.com/foaf/0.1/>
		SELECT ?src ?name
		FROM NAMED <file://data/named_graphs/alice.rdf>
		FROM NAMED <file://data/named_graphs/bob.rdf>
		WHERE {
			GRAPH ?src { ?x foaf:name ?name }
		}
END

	my $compiler	= RDF::Query::Compiler::SQL->new( $parsed );
	my $sql		= $compiler->compile();
	is( $sql, "SELECT\n\ts3.context AS src_Node,\n\tljr0.URI AS src_URI,\n\tljl0.Value AS src_Value,\n\tljl0.Language AS src_Language,\n\tljl0.Datatype AS src_Datatype,\n\tljb0.Name AS src_Name,\n\ts3.object AS name_Node,\n\tljr1.URI AS name_URI,\n\tljl1.Value AS name_Value,\n\tljl1.Language AS name_Language,\n\tljl1.Datatype AS name_Datatype,\n\tljb1.Name AS name_Name,\n\tljr2.URI AS x_URI,\n\tljl2.Value AS x_Value,\n\tljl2.Language AS x_Language,\n\tljl2.Datatype AS x_Datatype,\n\tljb2.Name AS x_Name\nFROM\n\tStatements s3 LEFT JOIN Resources ljr0 ON (s3.context = ljr0.ID) LEFT JOIN Literals ljl0 ON (s3.context = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s3.context = ljb0.ID) LEFT JOIN Resources ljr1 ON (s3.object = ljr1.ID) LEFT JOIN Literals ljl1 ON (s3.object = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s3.object = ljb1.ID) LEFT JOIN Resources ljr2 ON (s3.subject = ljr2.ID) LEFT JOIN Literals ljl2 ON (s3.subject = ljl2.ID) LEFT JOIN Bnodes ljb2 ON (s3.subject = ljb2.ID)\nWHERE\n\ts3.predicate = 14911999128994829034", "select context of people and names" );
}

{
	my $parsed	= $parser->parse(<<"END");
		PREFIX rss: <http://purl.org/rss/1.0/>
		SELECT ?title
		WHERE {
			<http://kasei.us/> rss:title ?title .
		}
END

	my $compiler	= RDF::Query::Compiler::SQL->new( $parsed );
	my $sql		= $compiler->compile();
	is( $sql, "SELECT\n\ts2.object AS title_Node,\n\tljr0.URI AS title_URI,\n\tljl0.Value AS title_Value,\n\tljl0.Language AS title_Language,\n\tljl0.Datatype AS title_Datatype,\n\tljb0.Name AS title_Name\nFROM\n\tStatements s2 LEFT JOIN Resources ljr0 ON (s2.object = ljr0.ID) LEFT JOIN Literals ljl0 ON (s2.object = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s2.object = ljb0.ID)\nWHERE\n\ts2.subject = 1083049239652454081 AND\n\ts2.predicate = 17858988500659793691", "select rss:title of uri" );
}

{
	my $parsed	= $parser->parse(<<"END");
		PREFIX	rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
		PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
		PREFIX	dcterms: <http://purl.org/dc/terms/>
		PREFIX	geo: <http://www.w3.org/2003/01/geo/wgs84_pos#>
		SELECT	?page
		WHERE	{
					?person foaf:name "Gregory Todd Williams" .
					?person foaf:homepage ?page .
				}
END

	my $compiler	= RDF::Query::Compiler::SQL->new( $parsed );
	my $sql		= $compiler->compile();
	is( $sql, "SELECT\n\ts3.object AS page_Node,\n\tljr0.URI AS page_URI,\n\tljl0.Value AS page_Value,\n\tljl0.Language AS page_Language,\n\tljl0.Datatype AS page_Datatype,\n\tljb0.Name AS page_Name,\n\tljr1.URI AS person_URI,\n\tljl1.Value AS person_Value,\n\tljl1.Language AS person_Language,\n\tljl1.Datatype AS person_Datatype,\n\tljb1.Name AS person_Name\nFROM\n\tStatements s2 LEFT JOIN Resources ljr1 ON (s2.subject = ljr1.ID) LEFT JOIN Literals ljl1 ON (s2.subject = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s2.subject = ljb1.ID),\n\tStatements s3 LEFT JOIN Resources ljr0 ON (s3.object = ljr0.ID) LEFT JOIN Literals ljl0 ON (s3.object = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s3.object = ljb0.ID)\nWHERE\n\ts2.predicate = 14911999128994829034 AND\n\ts2.object = 2782977400239829321 AND\n\ts3.subject = s2.subject AND\n\ts3.predicate = 9768710922987392204", "select homepage of person by name" );
}

{
	my $parsed	= $parser->parse(<<'END');
		PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
		SELECT	?s ?p
		WHERE	{
					?s ?p "RDF"@en .
				}
END

	my $compiler	= RDF::Query::Compiler::SQL->new( $parsed );
	my $sql		= $compiler->compile();
	is( $sql, "SELECT\n\ts2.subject AS s_Node,\n\tljr0.URI AS s_URI,\n\tljl0.Value AS s_Value,\n\tljl0.Language AS s_Language,\n\tljl0.Datatype AS s_Datatype,\n\tljb0.Name AS s_Name,\n\ts2.predicate AS p_Node,\n\tljr1.URI AS p_URI,\n\tljl1.Value AS p_Value,\n\tljl1.Language AS p_Language,\n\tljl1.Datatype AS p_Datatype,\n\tljb1.Name AS p_Name\nFROM\n\tStatements s2 LEFT JOIN Resources ljr0 ON (s2.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s2.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s2.subject = ljb0.ID) LEFT JOIN Resources ljr1 ON (s2.predicate = ljr1.ID) LEFT JOIN Literals ljl1 ON (s2.predicate = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s2.predicate = ljb1.ID)\nWHERE\n\ts2.object = 16625494614570964497", "select s,p by language-tagged literal" );
}

{
	RDF::Query::Compiler::SQL->add_function( 'time:now', sub {
		my $self	= shift;
		my $parsed_vars	= shift;
		my $expr	= shift;
		my $level	= shift || \do{ my $a = 0 };
		my %queryvars	= map { $_->name => 1 } @$parsed_vars;
		return ({}, [], ['NOW()']);
	} );
	
	my $parsed	= $parser->parse(<<'END');
		PREFIX	rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
		PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
		PREFIX	dcterms: <http://purl.org/dc/terms/>
		PREFIX	geo: <http://www.w3.org/2003/01/geo/wgs84_pos#>
		PREFIX	mygeo: <http://kasei.us/e/ns/geo#>
		PREFIX	xsd: <http://www.w3.org/2001/XMLSchema#>
		PREFIX	time: <time:>
		SELECT	?point
		WHERE	{
					?point a geo:Point .
					FILTER( time:now() > "2006-01-01" )
				}
END
	my $sql;
	lives_ok {
		my $compiler	= RDF::Query::Compiler::SQL->new( $parsed );
		$sql			= $compiler->compile();
	} 'compile: select with function filter';
	is( $sql, qq(SELECT\n\ts6.subject AS point_Node,\n\tljr0.URI AS point_URI,\n\tljl0.Value AS point_Value,\n\tljl0.Language AS point_Language,\n\tljl0.Datatype AS point_Datatype,\n\tljb0.Name AS point_Name\nFROM\n\tStatements s6 LEFT JOIN Resources ljr0 ON (s6.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s6.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s6.subject = ljb0.ID)\nWHERE\n\tNOW() > '2006-01-01' AND\n\ts6.predicate = 2982895206037061277 AND\n\ts6.object = 11045396790191387947), "sql: select with function filter" );
}

{
	RDF::Query::Compiler::SQL->add_function( 'http://kasei.us/e/ns/geo#distance', sub {
		my $self	= shift;
		my $parsed_vars	= shift;
		my $level	= shift || \do{ my $a = 0 };
		my @args	= @_;
		my $vars	= $self->{vars};
		my (@from, @where);
		
		my %queryvars	= map { $_->name => 1 } @$parsed_vars;
		
		++$$level; my $sql_a	= $self->expr2sql( $args[0], $level );
		++$$level; my $sql_b	= $self->expr2sql( $args[1], $level );
		++$$level; my $sql_c	= $self->expr2sql( $args[1], $level );
		push(@where, "distance($sql_a, $sql_b, $sql_c)");
		return ($vars, \@from, \@where);
	} );
	
	my $parsed	= $parser->parse(<<'END');
		PREFIX	rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
		PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
		PREFIX	dcterms: <http://purl.org/dc/terms/>
		PREFIX	geo: <http://www.w3.org/2003/01/geo/wgs84_pos#>
		PREFIX	mygeo: <http://kasei.us/e/ns/geo#>
		PREFIX	xsd: <http://www.w3.org/2001/XMLSchema#>
		SELECT	?image ?point ?lat
		WHERE	{
					?point geo:lat ?lat .
					?image ?pred ?point .
					FILTER( mygeo:distance(?point, +41.849331, -71.392) < "10"^^xsd:integer )
				}
END

	my $sql;
	lives_ok {
		my $compiler	= RDF::Query::Compiler::SQL->new( $parsed );
		$sql			= $compiler->compile();
	} 'compile: select images filterd by distance function comparison';
	is( $sql, qq(SELECT\n\ts10.subject AS image_Node,\n\tljr0.URI AS image_URI,\n\tljl0.Value AS image_Value,\n\tljl0.Language AS image_Language,\n\tljl0.Datatype AS image_Datatype,\n\tljb0.Name AS image_Name,\n\ts9.subject AS point_Node,\n\tljr1.URI AS point_URI,\n\tljl1.Value AS point_Value,\n\tljl1.Language AS point_Language,\n\tljl1.Datatype AS point_Datatype,\n\tljb1.Name AS point_Name,\n\ts9.object AS lat_Node,\n\tljr2.URI AS lat_URI,\n\tljl2.Value AS lat_Value,\n\tljl2.Language AS lat_Language,\n\tljl2.Datatype AS lat_Datatype,\n\tljb2.Name AS lat_Name,\n\tljr3.URI AS pred_URI,\n\tljl3.Value AS pred_Value,\n\tljl3.Language AS pred_Language,\n\tljl3.Datatype AS pred_Datatype,\n\tljb3.Name AS pred_Name\nFROM\n\tStatements s9 LEFT JOIN Resources ljr1 ON (s9.subject = ljr1.ID) LEFT JOIN Literals ljl1 ON (s9.subject = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s9.subject = ljb1.ID) LEFT JOIN Resources ljr2 ON (s9.object = ljr2.ID) LEFT JOIN Literals ljl2 ON (s9.object = ljl2.ID) LEFT JOIN Bnodes ljb2 ON (s9.object = ljb2.ID),\n\tStatements s10 LEFT JOIN Resources ljr0 ON (s10.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s10.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s10.subject = ljb0.ID) LEFT JOIN Resources ljr3 ON (s10.predicate = ljr3.ID) LEFT JOIN Literals ljl3 ON (s10.predicate = ljl3.ID) LEFT JOIN Bnodes ljb3 ON (s10.predicate = ljb3.ID)\nWHERE\n\tdistance(, (0.0 + '+41.849331'), (0.0 + '+41.849331')) < (0 + '10') AND\n\ts9.predicate = 5391429383543785584 AND\n\ts10.object = s9.subject), "sql: select images filterd by distance function comparison" );
}

{
	my $parsed	= $parser->parse(<<'END');
		PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
		SELECT	?name
		WHERE	{
					?p a foaf:Person ; foaf:name ?name .
					FILTER REGEX(?name, "Greg") .
				}
END

	my $compiler	= RDF::Query::Compiler::SQL->new( $parsed );
	my $sql		= $compiler->compile();
	is( $sql, qq(SELECT\n\ts5.object AS name_Node,\n\tljr0.URI AS name_URI,\n\tljl0.Value AS name_Value,\n\tljl0.Language AS name_Language,\n\tljl0.Datatype AS name_Datatype,\n\tljb0.Name AS name_Name,\n\tljr1.URI AS p_URI,\n\tljl1.Value AS p_Value,\n\tljl1.Language AS p_Language,\n\tljl1.Datatype AS p_Datatype,\n\tljb1.Name AS p_Name\nFROM\n\tStatements s4 LEFT JOIN Resources ljr1 ON (s4.subject = ljr1.ID) LEFT JOIN Literals ljl1 ON (s4.subject = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s4.subject = ljb1.ID),\n\tStatements s5 LEFT JOIN Resources ljr0 ON (s5.object = ljr0.ID) LEFT JOIN Literals ljl0 ON (s5.object = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s5.object = ljb0.ID)\nWHERE\n\t(ljl0.Value REGEXP 'Greg' OR ljr0.URI REGEXP 'Greg' OR ljb0.Name REGEXP 'Greg') AND\n\ts4.predicate = 2982895206037061277 AND\n\ts4.object = 3652866608875541952 AND\n\ts5.subject = s4.subject AND\n\ts5.predicate = 14911999128994829034), "select people by regex-filtered name" );
}

{
	my $parsed	= $parser->parse(<<'END');
		PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
		SELECT DISTINCT	?name
		WHERE	{
					?p a foaf:Person ; foaf:name ?name .
					FILTER REGEX(?name, "Greg") .
				}
		LIMIT 1
END

	my $compiler	= RDF::Query::Compiler::SQL->new( $parsed );
	my $sql		= $compiler->compile();
	is( $sql, qq(SELECT DISTINCT\n\ts5.object AS name_Node,\n\tljr0.URI AS name_URI,\n\tljl0.Value AS name_Value,\n\tljl0.Language AS name_Language,\n\tljl0.Datatype AS name_Datatype,\n\tljb0.Name AS name_Name,\n\tljr1.URI AS p_URI,\n\tljl1.Value AS p_Value,\n\tljl1.Language AS p_Language,\n\tljl1.Datatype AS p_Datatype,\n\tljb1.Name AS p_Name\nFROM\n\tStatements s4 LEFT JOIN Resources ljr1 ON (s4.subject = ljr1.ID) LEFT JOIN Literals ljl1 ON (s4.subject = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s4.subject = ljb1.ID),\n\tStatements s5 LEFT JOIN Resources ljr0 ON (s5.object = ljr0.ID) LEFT JOIN Literals ljl0 ON (s5.object = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s5.object = ljb0.ID)\nWHERE\n\t(ljl0.Value REGEXP 'Greg' OR ljr0.URI REGEXP 'Greg' OR ljb0.Name REGEXP 'Greg') AND\n\ts4.predicate = 2982895206037061277 AND\n\ts4.object = 3652866608875541952 AND\n\ts5.subject = s4.subject AND\n\ts5.predicate = 14911999128994829034\nLIMIT 1), "select people by regex-filtered name with DISTINCT and LIMIT" );
}

{
	my $parsed	= $parser->parse(<<'END');
		PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
		SELECT DISTINCT	?name
		WHERE	{
					?p a foaf:Person ; foaf:name ?name .
					FILTER( ?name = "Gregory Todd Williams" )
				}
		LIMIT 1
END

	my $compiler	= RDF::Query::Compiler::SQL->new( $parsed );
	my $sql		= $compiler->compile();
	is( $sql, qq(SELECT DISTINCT\n\ts7.object AS name_Node,\n\tljr0.URI AS name_URI,\n\tljl0.Value AS name_Value,\n\tljl0.Language AS name_Language,\n\tljl0.Datatype AS name_Datatype,\n\tljb0.Name AS name_Name,\n\tljr1.URI AS p_URI,\n\tljl1.Value AS p_Value,\n\tljl1.Language AS p_Language,\n\tljl1.Datatype AS p_Datatype,\n\tljb1.Name AS p_Name\nFROM\n\tStatements s6 LEFT JOIN Resources ljr1 ON (s6.subject = ljr1.ID) LEFT JOIN Literals ljl1 ON (s6.subject = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s6.subject = ljb1.ID),\n\tStatements s7 LEFT JOIN Resources ljr0 ON (s7.object = ljr0.ID) LEFT JOIN Literals ljl0 ON (s7.object = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s7.object = ljb0.ID)\nWHERE\n\t(SELECT value FROM Literals WHERE  = ID LIMIT 1) = 'Gregory Todd Williams' AND\n\ts6.predicate = 2982895206037061277 AND\n\ts6.object = 3652866608875541952 AND\n\ts7.subject = s6.subject AND\n\ts7.predicate = 14911999128994829034\nLIMIT 1), "select people by Literal name with DISTINCT and LIMIT" );
}

{
	my $parsed	= $parser->parse(<<'END');
		PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
		SELECT DISTINCT	?name
		WHERE	{
					?p a foaf:Person ; foaf:name ?name .
					FILTER( ?p = <http://kasei.us/about/foaf.xrdf#greg> )
				}
		LIMIT 1
END

	my $compiler	= RDF::Query::Compiler::SQL->new( $parsed );
	my $sql		= $compiler->compile();
	is( $sql, qq(SELECT DISTINCT\n\ts5.object AS name_Node,\n\tljr0.URI AS name_URI,\n\tljl0.Value AS name_Value,\n\tljl0.Language AS name_Language,\n\tljl0.Datatype AS name_Datatype,\n\tljb0.Name AS name_Name,\n\tljr1.URI AS p_URI,\n\tljl1.Value AS p_Value,\n\tljl1.Language AS p_Language,\n\tljl1.Datatype AS p_Datatype,\n\tljb1.Name AS p_Name\nFROM\n\tStatements s4 LEFT JOIN Resources ljr1 ON (s4.subject = ljr1.ID) LEFT JOIN Literals ljl1 ON (s4.subject = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s4.subject = ljb1.ID),\n\tStatements s5 LEFT JOIN Resources ljr0 ON (s5.object = ljr0.ID) LEFT JOIN Literals ljl0 ON (s5.object = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s5.object = ljb0.ID)\nWHERE\n\t = 2954181085641959508 AND\n\ts4.predicate = 2982895206037061277 AND\n\ts4.object = 3652866608875541952 AND\n\ts5.subject = s4.subject AND\n\ts5.predicate = 14911999128994829034\nLIMIT 1), "select people by URI with DISTINCT and LIMIT" );
	
}

{
	my $parsed	= $parser->parse(<<"END");
		PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
		SELECT	?person ?name
		WHERE	{
					?person foaf:name ?name .
					FILTER BOUND(?name) .
				}
END
	my $compiler	= RDF::Query::Compiler::SQL->new( $parsed );
	isa_ok( $compiler, 'RDF::Query::Compiler::SQL' );
	
	my $sql		= $compiler->compile();
	is( $sql, "SELECT\n\ts4.subject AS person_Node,\n\tljr0.URI AS person_URI,\n\tljl0.Value AS person_Value,\n\tljl0.Language AS person_Language,\n\tljl0.Datatype AS person_Datatype,\n\tljb0.Name AS person_Name,\n\ts4.object AS name_Node,\n\tljr1.URI AS name_URI,\n\tljl1.Value AS name_Value,\n\tljl1.Language AS name_Language,\n\tljl1.Datatype AS name_Datatype,\n\tljb1.Name AS name_Name\nFROM\n\tStatements s4 LEFT JOIN Resources ljr0 ON (s4.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s4.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s4.subject = ljb0.ID) LEFT JOIN Resources ljr1 ON (s4.object = ljr1.ID) LEFT JOIN Literals ljl1 ON (s4.object = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s4.object = ljb1.ID)\nWHERE\n\t IS NOT NULL AND\n\ts4.predicate = 14911999128994829034", "select people and names with filter BOUND()" );
}

{
	my $parsed	= $parser->parse(<<"END");
		PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
		SELECT	?person ?name
		WHERE	{ ?person foaf:name ?name }
		ORDER BY ?name
END
	my $compiler	= RDF::Query::Compiler::SQL->new( $parsed );
	isa_ok( $compiler, 'RDF::Query::Compiler::SQL' );
	
	my $sql		= $compiler->compile();
	is( $sql, "SELECT\n\ts2.subject AS person_Node,\n\tljr0.URI AS person_URI,\n\tljl0.Value AS person_Value,\n\tljl0.Language AS person_Language,\n\tljl0.Datatype AS person_Datatype,\n\tljb0.Name AS person_Name,\n\ts2.object AS name_Node,\n\tljr1.URI AS name_URI,\n\tljl1.Value AS name_Value,\n\tljl1.Language AS name_Language,\n\tljl1.Datatype AS name_Datatype,\n\tljb1.Name AS name_Name\nFROM\n\tStatements s2 LEFT JOIN Resources ljr0 ON (s2.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s2.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s2.subject = ljb0.ID) LEFT JOIN Resources ljr1 ON (s2.object = ljr1.ID) LEFT JOIN Literals ljl1 ON (s2.object = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s2.object = ljb1.ID)\nWHERE\n\ts2.predicate = 14911999128994829034\nORDER BY\n\tname_Value ASC, name_URI ASC, name_Name ASC", "order by names" );
}

{
	my $parsed	= $parser->parse(<<"END");
		PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
		SELECT	?person ?name
		WHERE	{ ?person foaf:name ?name }
		ORDER BY ?name, ?person
END
	my $compiler	= RDF::Query::Compiler::SQL->new( $parsed );
	throws_ok {
		my $sql		= $compiler->compile();
	} 'RDF::Query::Error::CompilationError', 'order by multiple columns throws error';
}

{
	my $parsed	= $parser->parse(<<"END");
		PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
		CONSTRUCT { ?person foaf:name ?name }
		WHERE	{ ?person foaf:name ?name }
END
	my $compiler	= RDF::Query::Compiler::SQL->new( $parsed, 'model' );
	throws_ok {
		my $sql		= $compiler->compile();
	} 'RDF::Query::Error::CompilationError', 'non-select throws error';
}

{
	my $parsed	= $parser->parse(<<"END");
		PREFIX time: <time:>
		PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
		SELECT	?person ?name
		WHERE	{ ?person foaf:name ?name }
		ORDER BY time:now()
END
	my $compiler	= RDF::Query::Compiler::SQL->new( $parsed );
	$compiler->add_function( 'time:now', sub {
		my $self	= shift;
		my $parsed_vars	= shift;
		my $level	= shift || \do{ my $a = 0 };
		return ({}, [], ['NOW()']);
	} );
	isa_ok( $compiler, 'RDF::Query::Compiler::SQL' );
	
	my $sql		= $compiler->compile();
	is( $sql, "SELECT\n\ts2.subject AS person_Node,\n\tljr0.URI AS person_URI,\n\tljl0.Value AS person_Value,\n\tljl0.Language AS person_Language,\n\tljl0.Datatype AS person_Datatype,\n\tljb0.Name AS person_Name,\n\ts2.object AS name_Node,\n\tljr1.URI AS name_URI,\n\tljl1.Value AS name_Value,\n\tljl1.Language AS name_Language,\n\tljl1.Datatype AS name_Datatype,\n\tljb1.Name AS name_Name\nFROM\n\tStatements s2 LEFT JOIN Resources ljr0 ON (s2.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s2.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s2.subject = ljb0.ID) LEFT JOIN Resources ljr1 ON (s2.object = ljr1.ID) LEFT JOIN Literals ljl1 ON (s2.object = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s2.object = ljb1.ID)\nWHERE\n\ts2.predicate = 14911999128994829034\nORDER BY\n\tNOW() ASC", "order by function (NOW())" );
}

{
	my $parsed	= $parser->parse(<<"END");
		PREFIX func: <func:>
		PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
		SELECT	?person ?name
		WHERE	{ ?person foaf:name ?name }
		ORDER BY func:ascii(?name)
END
	my $compiler	= RDF::Query::Compiler::SQL->new( $parsed );
	$compiler->add_function( 'func:ascii', sub {
		my $self	= shift;
		my $parsed_vars	= shift;
		my $level	= shift || \do{ my $a = 0 };
		my $expr	= shift;
		
		my $col		= $self->expr2sql( $expr, $level );
		return ({}, [], ["ASCII($col)"]);
	} );
	isa_ok( $compiler, 'RDF::Query::Compiler::SQL' );
	
	my $sql		= $compiler->compile();
	is( $sql, "SELECT\n\ts2.subject AS person_Node,\n\tljr0.URI AS person_URI,\n\tljl0.Value AS person_Value,\n\tljl0.Language AS person_Language,\n\tljl0.Datatype AS person_Datatype,\n\tljb0.Name AS person_Name,\n\ts2.object AS name_Node,\n\tljr1.URI AS name_URI,\n\tljl1.Value AS name_Value,\n\tljl1.Language AS name_Language,\n\tljl1.Datatype AS name_Datatype,\n\tljb1.Name AS name_Name\nFROM\n\tStatements s2 LEFT JOIN Resources ljr0 ON (s2.subject = ljr0.ID) LEFT JOIN Literals ljl0 ON (s2.subject = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s2.subject = ljb0.ID) LEFT JOIN Resources ljr1 ON (s2.object = ljr1.ID) LEFT JOIN Literals ljl1 ON (s2.object = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s2.object = ljb1.ID)\nWHERE\n\ts2.predicate = 14911999128994829034\nORDER BY\n\tASCII(ljr1.URI) ASC, ASCII(ljl1.Value) ASC, ASCII(ljl1.Language) ASC", "order by function (ASCII(name))" );
}

	














# if ($ENV{RDFQUERY_DEV_MYSQL}) {
# 	my $model_name	= 'model';
# 	SKIP: {
# 		eval "require Kasei::RDF::Common;";
# 		if ($@) {
# 			warn $@;
# 			exit;
# #			skip "Failed to load Kasei::RDF::Common", 1;
# 		}
# 		
# 		my $dsn		= [ Kasei::Common::mysql_dbi_args() ];
# 		
# 		{
# 			my $model	= DBI->connect( @$dsn );
# 			my $sparql	= <<'END';
# 			SELECT	?s ?p ?o
# 			WHERE	{ ?s ?p ?o }
# 			LIMIT 1
# END
# 			lives_ok {
# 				my $query	= new RDF::Query ( $sparql, undef, undef, 'sparql' );
# 				$query->execute( $model, model => $model_name, require_sql => 1 );
# 			} 's-p-o without pre-bound vars';
# 		}
# 		
# 		{
# 			Kasei::RDF::Common->import('mysql_model');
# 			my @myargs	= Kasei::Common::mysql_upd();
# 			my $model	= mysql_model( $model_name, @myargs[ 2, 0, 1 ] );
# 			
# 			{
# 				my $sparql	= <<'END';
# 				SELECT	?s ?p ?o
# 				WHERE	{ ?s ?p ?o }
# 				LIMIT 1
# END
# 				lives_ok {
# 					my $query	= new RDF::Query ( $sparql, undef, undef, 'sparql' );
# 					$query->execute( $model, dsn => $dsn, model => $model_name, require_sql => 1 );
# 				} 's-p-o without pre-bound vars';
# 				
# 				throws_ok {
# 					my $query	= new RDF::Query ( $sparql, undef, undef, 'sparql' );
# 					$query->execute( $model, dsn => $dsn, model => $model_name, require_sql => 1, bind => { p => 1 } );
# 				} 'RDF::Query::Error::CompilationError', 's-p-o without pre-bound vars: forced sql compilation (expected) failure';
# 			}
# 			
# 			{
# 				my $query	= new RDF::Query ( <<"END", undef, undef, 'sparql' );
# 				PREFIX	geo: <http://www.w3.org/2003/01/geo/wgs84_pos#>
# 				SELECT	?image ?point ?lat
# 				WHERE	{
# 							?point geo:lat ?lat .
# 							?image ?pred ?point .
# 							FILTER(	geo:distance(?point) ) .
# 						}
# END
# 				throws_ok {
# 					$query->execute( $model, dsn => $dsn, model => $model_name, require_sql => 1 );
# 				} 'RDF::Query::Error::CompilationError', 'forced sql compilation (expected) failure';
# 			}
# 			
# 			{
# 				print "# FILTER rage test\n";
# 				my $query	= new RDF::Query ( <<"END", undef, undef, 'sparql' );
# 				PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
# 				PREFIX	geo: <http://www.w3.org/2003/01/geo/wgs84_pos#>
# 				SELECT	?image ?point ?lat
# 				WHERE	{
# 							?image a foaf:Image ; ?pred ?point .
# 							?point geo:lat ?lat .
# 							FILTER(
# 								(?pred = <http://purl.org/dc/terms/spatial> || ?pred = <http://xmlns.com/foaf/0.1/based_near>)
# 								&&	?lat > 52
# 								&&	?lat < 53
# 							) .
# 						}
# END
# 				my $stream	= $query->execute( $model, dsn => $dsn, model => $model_name );
# 	
# 				my $count;
# 				while (my $row = $stream->()) {
# 					my ($image, $point, $lat)	= @{ $row };
# 					ok( $image->isa('RDF::Trine::Node::Resource'), 'image is resource');
# 					my $latv	= ($lat) ? $lat->literal_value : undef;
# 					cmp_ok( $latv, '>', 52, 'lat: ' . $latv );
# 					cmp_ok( $latv, '<', 53, 'lat: ' . $latv );
# 					$count++;
# 				}
# 			}
# 			
# 			{
# 				print "# lots of points!\n";
# 				my $query	= new RDF::Query ( <<"END", undef, undef, 'sparql' );
# 					PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
# 					PREFIX	geo: <http://www.w3.org/2003/01/geo/wgs84_pos#>
# 					SELECT	?name
# 					WHERE	{
# 								?point a geo:Point; foaf:name ?name .
# 							}
# END
# 				my $stream	= $query->execute( $model, dsn => $dsn, model => $model_name );
# 				isa_ok( $stream, 'CODE', 'stream' );
# 				my $count;
# 				while (my $row = $stream->()) {
# 					my ($node)	= @{ $row };
# 					my $name	= $node->as_string;
# 					ok( $name, $name );
# 				} continue { last if ++$count >= 100 };
# 			}
# 			
# 			{
# 				print "# foaf:Person ORDER BY name\n";
# 				my $query	= new RDF::Query ( <<"END", undef, undef, 'sparql' );
# 					PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
# 					PREFIX	geo: <http://www.w3.org/2003/01/geo/wgs84_pos#>
# 					SELECT	DISTINCT ?p ?name
# 					WHERE	{
# 								?p a foaf:Person; foaf:name ?name
# 							}
# 					ORDER BY ?name
# END
# 				my $stream	= $query->execute( $model, dsn => $dsn, model => $model_name );
# 				isa_ok( $stream, 'CODE', 'stream' );
# 				my ($count, $last);
# 				while (my $row = $stream->()) {
# 					my ($p, $node)	= @{ $row };
# 					my $name	= $node->as_string;
# 					if (defined($last)) {
# 						cmp_ok( $name, 'ge', $last, "In order: $name (" . $p->as_string . ")" );
# 					} else {
# 						ok( $name, "$name (" . $p->as_string . ")" );
# 					}
# 					$last	= $name;
# 				} continue { last if ++$count >= 200 };
# 			}
# 			
# 			{
# 				print "# geo:Point ORDER BY longitude\n";
# 				my $query	= new RDF::Query ( <<"END", undef, undef, 'sparql' );
# 					PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
# 					PREFIX	geo: <http://www.w3.org/2003/01/geo/wgs84_pos#>
# 					PREFIX	xsd: <http://www.w3.org/2001/XMLSchema#>
# 					SELECT	DISTINCT ?name ?lat ?lon
# 					WHERE	{
# 								?point a geo:Point; foaf:name ?name; geo:lat ?lat; geo:long ?lon
# 							}
# 					ORDER BY DESC( xsd:decimal(?lon) )
# END
# 				my $stream	= $query->execute( $model, dsn => $dsn, model => $model_name );
# 				isa_ok( $stream, 'CODE', 'stream' );
# 				my ($count, $last);
# 				while (my $row = $stream->()) {
# 					my ($node, $lat, $long)	= @{ $row };
# 					my $name	= $node->as_string;
# 					if (defined($last)) {
# 						cmp_ok( $long->as_string, '<=', $last, "In order: $name (" . $long->as_string . ")" );
# 					} else {
# 						ok( $name, "$name (" . $long->as_string . ")" );
# 					}
# 					$last	= $long->as_string;
# 				} continue { last if ++$count >= 200 };
# 			}
# 		}
# 	}
# }



# if ($ENV{RDFQUERY_DEV_POSTGRESQL}) {
# 	eval "require Kasei::RDF::Common;";
# 	$ENV{'POSTGRESQL_MODEL'}	= 'model';
# 	$ENV{'POSTGRESQL_DATABASE'}	= 'greg';
# 	$ENV{'POSTGRESQL_PASSWORD'}	= 'password';
# 	
# 	Kasei::RDF::Common->import('postgresql_model');
# 	my $dbh		= postgresql_model();
# 	warn $dbh;
# 	
# 	my @myargs	= Kasei::Common::postgresql_upd();
# 	my $model	= postgresql_model( 'db1', @myargs[ 2, 0, 1 ] );
# 	my $dsn		= [ Kasei::Common::postgresql_dbi_args() ];
# 	
# 	warn $model;
# 	warn Dumper($dsn);
# 
# 
# }



__END__
{
	my $parsed	= $parser->parse(<<'END');
		PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
		SELECT	?name
		WHERE	{
					?p a foaf:Person .
					?p foaf:name ?name .
					FILTER( ?p = _:r1101876070r10 )
				}
END

	my $compiler	= RDF::Query::Compiler::SQL->new( $parsed );
	my $sql		= $compiler->compile();
	is( $sql, qq(SELECT\n\ts2.object AS name_Node,\n\tljr0.URI AS name_URI,\n\tljl0.Value AS name_Value,\n\tljl0.Language AS name_Language,\n\tljl0.Datatype AS name_Datatype,\n\tljb0.Name AS name_Name,\n\tljr1.URI AS p_URI,\n\tljl1.Value AS p_Value,\n\tljl1.Language AS p_Language,\n\tljl1.Datatype AS p_Datatype,\n\tljb1.Name AS p_Name\nFROM\n\tStatements s2 LEFT JOIN Resources ljr1 ON (s2.subject = ljr1.ID) LEFT JOIN Literals ljl1 ON (s2.subject = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s2.subject = ljb1.ID),\n\tStatements s2 LEFT JOIN Resources ljr0 ON (s2.object = ljr0.ID) LEFT JOIN Literals ljl0 ON (s2.object = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s2.object = ljb0.ID)\nWHERE\n\ts2.predicate = 2982895206037061277 AND\n\ts2.object = 3652866608875541952 AND\n\ts2.subject = s2.subject AND\n\ts2.predicate = 14911999128994829034 AND\n\ts2.subject = 4025741532186680712), "select people by BNode" );
}

{
	my $parsed	= $parser->parse(<<'END');
		PREFIX	foaf: <http://xmlns.com/foaf/0.1/>
		SELECT	?name
		WHERE	{
					?p a foaf:Person ; foaf:name ?name .
					FILTER( ?p = [] )
				}
END

	my $compiler	= RDF::Query::Compiler::SQL->new( $parsed );
	my $sql		= $compiler->compile();
	$sql		=~ s/(s2.subject\s*=\s*)\d+/$1XXX/;
	is( $sql, qq(SELECT\n\ts2.object AS name_Node,\n\tljr0.URI AS name_URI,\n\tljl0.Value AS name_Value,\n\tljl0.Language AS name_Language,\n\tljl0.Datatype AS name_Datatype,\n\tljb0.Name AS name_Name,\n\tljr1.URI AS p_URI,\n\tljl1.Value AS p_Value,\n\tljl1.Language AS p_Language,\n\tljl1.Datatype AS p_Datatype,\n\tljb1.Name AS p_Name\nFROM\n\tStatements s2 LEFT JOIN Resources ljr1 ON (s2.subject = ljr1.ID) LEFT JOIN Literals ljl1 ON (s2.subject = ljl1.ID) LEFT JOIN Bnodes ljb1 ON (s2.subject = ljb1.ID),\n\tStatements s2 LEFT JOIN Resources ljr0 ON (s2.object = ljr0.ID) LEFT JOIN Literals ljl0 ON (s2.object = ljl0.ID) LEFT JOIN Bnodes ljb0 ON (s2.object = ljb0.ID)\nWHERE\n\ts2.predicate = 2982895206037061277 AND\n\ts2.object = 3652866608875541952 AND\n\ts2.subject = s2.subject AND\n\ts2.predicate = 14911999128994829034 AND\n\ts2.subject = XXX), "select people by BNode" );
}