Index: trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SQLStore2_Queries.php |
— | — | @@ -175,6 +175,8 @@ |
176 | 176 | * step-wise execution of the query might lead to better performance, since |
177 | 177 | * it exploits the tree-structure of the joins, which is important for fast |
178 | 178 | * processing -- not all DBMS might be able in seeing this by themselves. |
| 179 | + * |
| 180 | + * @param SMWQuery $query |
179 | 181 | */ |
180 | 182 | public function getQueryResult( SMWQuery $query ) { |
181 | 183 | global $smwgIgnoreQueryErrors, $smwgQSortingSupport; |
— | — | @@ -254,7 +256,7 @@ |
255 | 257 | * the proper debug output for the given query. |
256 | 258 | * |
257 | 259 | * @param SMWQuery $query |
258 | | - * @param $rootid |
| 260 | + * @param integer $rootid |
259 | 261 | */ |
260 | 262 | protected function getDebugQueryResult( SMWQuery $query, $rootid ) { |
261 | 263 | $qobj = $this->m_queries[$rootid]; |
— | — | @@ -307,8 +309,11 @@ |
308 | 310 | /** |
309 | 311 | * Using a preprocessed internal query description referenced by $rootid, compute |
310 | 312 | * the proper counting output for the given query. |
| 313 | + * |
| 314 | + * @param SMWQuery $query |
| 315 | + * @param integer $rootid |
311 | 316 | */ |
312 | | - protected function getCountQueryResult( $query, $rootid ) { |
| 317 | + protected function getCountQueryResult( SMWQuery $query, $rootid ) { |
313 | 318 | wfProfileIn( 'SMWSQLStore2Queries::getCountQueryResult (SMW)' ); |
314 | 319 | $qobj = $this->m_queries[$rootid]; |
315 | 320 | if ( $qobj->joinfield === '' ) { // empty result, no query needed |
— | — | @@ -338,7 +343,7 @@ |
339 | 344 | * we want here. It would be nice if we could eliminate the bug in POSTGRES as well. |
340 | 345 | * |
341 | 346 | * @param SMWQuery $query |
342 | | - * @param $rootid |
| 347 | + * @param integer $rootid |
343 | 348 | * |
344 | 349 | * @return SMWQueryResult |
345 | 350 | */ |
— | — | @@ -766,24 +771,31 @@ |
767 | 772 | * Process stored queries and change store accordingly. The query obj is modified |
768 | 773 | * so that it contains non-recursive description of a select to execute for getting |
769 | 774 | * the actual result. |
| 775 | + * |
| 776 | + * @param SMWSQLStore2Query $query |
770 | 777 | */ |
771 | 778 | protected function executeQueries( SMWSQLStore2Query &$query ) { |
772 | 779 | global $wgDBtype; |
| 780 | + |
773 | 781 | switch ( $query->type ) { |
774 | | - case SMW_SQL2_TABLE: // normal query with conjunctive subcondition |
| 782 | + case SMW_SQL2_TABLE: // Normal query with conjunctive subcondition. |
775 | 783 | foreach ( $query->components as $qid => $joinfield ) { |
776 | 784 | $subquery = $this->m_queries[$qid]; |
777 | 785 | $this->executeQueries( $subquery ); |
778 | | - if ( $subquery->jointable != '' ) { // join with jointable.joinfield |
| 786 | + |
| 787 | + if ( $subquery->jointable != '' ) { // Join with jointable.joinfield |
779 | 788 | $query->from .= ' INNER JOIN ' . $this->m_dbs->tableName( $subquery->jointable ) . " AS $subquery->alias ON $joinfield=" . $subquery->joinfield; |
780 | | - } elseif ( $subquery->joinfield !== '' ) { // require joinfield as "value" via WHERE |
| 789 | + } elseif ( $subquery->joinfield !== '' ) { // Require joinfield as "value" via WHERE. |
781 | 790 | $condition = ''; |
| 791 | + |
782 | 792 | foreach ( $subquery->joinfield as $value ) { |
783 | 793 | $condition .= ( $condition ? ' OR ':'' ) . "$joinfield=" . $this->m_dbs->addQuotes( $value ); |
784 | 794 | } |
| 795 | + |
785 | 796 | if ( count( $subquery->joinfield ) > 1 ) { |
786 | 797 | $condition = "($condition)"; |
787 | 798 | } |
| 799 | + |
788 | 800 | $query->where .= ( ( $query->where == '' ) ? '':' AND ' ) . $condition; |
789 | 801 | } else { // interpret empty joinfields as impossible condition (empty result) |
790 | 802 | $query->joinfield = ''; // make whole query false |
— | — | @@ -792,36 +804,47 @@ |
793 | 805 | $query->from = ''; |
794 | 806 | break; |
795 | 807 | } |
| 808 | + |
796 | 809 | if ( $subquery->where != '' ) { |
797 | 810 | $query->where .= ( ( $query->where == '' ) ? '':' AND ' ) . '(' . $subquery->where . ')'; |
798 | 811 | } |
| 812 | + |
799 | 813 | $query->from .= $subquery->from; |
800 | 814 | } |
| 815 | + |
801 | 816 | $query->components = array(); |
802 | 817 | break; |
803 | 818 | case SMW_SQL2_CONJUNCTION: |
804 | 819 | // pick one subquery with jointable as anchor point ... |
805 | 820 | reset( $query->components ); |
806 | 821 | $key = false; |
| 822 | + |
807 | 823 | foreach ( $query->components as $qkey => $qid ) { |
808 | 824 | if ( $this->m_queries[$qkey]->jointable != '' ) { |
809 | 825 | $key = $qkey; |
810 | 826 | break; |
811 | 827 | } |
812 | 828 | } |
| 829 | + |
813 | 830 | if ( $key !== false ) { |
814 | 831 | $result = $this->m_queries[$key]; |
815 | 832 | unset( $query->components[$key] ); |
816 | | - $this->executeQueries( $result ); // execute it first (may change jointable and joinfield, e.g. when making temporary tables) |
817 | | - // ... and append to this query the remaining queries |
| 833 | + |
| 834 | + // Execute it first (may change jointable and joinfield, e.g. when making temporary tables) |
| 835 | + $this->executeQueries( $result ); |
| 836 | + |
| 837 | + // ... and append to this query the remaining queries. |
818 | 838 | foreach ( $query->components as $qid => $joinfield ) { |
819 | 839 | $result->components[$qid] = $result->joinfield; |
820 | 840 | } |
821 | | - $this->executeQueries( $result ); // second execute, now incorporating remaining conditions |
822 | | - } else { // only fixed values in conjunction, make a new value without joining |
| 841 | + |
| 842 | + // Second execute, now incorporating remaining conditions. |
| 843 | + $this->executeQueries( $result ); |
| 844 | + } else { // Only fixed values in conjunction, make a new value without joining. |
823 | 845 | $key = $qkey; |
824 | 846 | $result = $this->m_queries[$key]; |
825 | 847 | unset( $query->components[$key] ); |
| 848 | + |
826 | 849 | foreach ( $query->components as $qid => $joinfield ) { |
827 | 850 | if ( $result->joinfield != $this->m_queries[$qid]->joinfield ) { |
828 | 851 | $result->joinfield = ''; // all other values should already be '' |
— | — | @@ -835,11 +858,14 @@ |
836 | 859 | if ( $this->m_qmode !== SMWQuery::MODE_DEBUG ) { |
837 | 860 | $this->m_dbs->query( $this->getCreateTempIDTableSQL( $this->m_dbs->tableName( $query->alias ) ), 'SMW::executeQueries' ); |
838 | 861 | } |
| 862 | + |
839 | 863 | $this->m_querylog[$query->alias] = array(); |
| 864 | + |
840 | 865 | foreach ( $query->components as $qid => $joinfield ) { |
841 | 866 | $subquery = $this->m_queries[$qid]; |
842 | 867 | $this->executeQueries( $subquery ); |
843 | 868 | $sql = ''; |
| 869 | + |
844 | 870 | if ( $subquery->jointable != '' ) { |
845 | 871 | $sql = 'INSERT ' . ( ( $wgDBtype == 'postgres' ) ? '':'IGNORE ' ) . 'INTO ' . |
846 | 872 | $this->m_dbs->tableName( $query->alias ) . |
— | — | @@ -849,21 +875,25 @@ |
850 | 876 | // NOTE: this works only for single "unconditional" values without further |
851 | 877 | // WHERE or FROM. The execution must take care of not creating any others. |
852 | 878 | $values = ''; |
| 879 | + |
853 | 880 | foreach ( $subquery->joinfield as $value ) { |
854 | 881 | $values .= ( $values ? ',':'' ) . '(' . $this->m_dbs->addQuotes( $value ) . ')'; |
855 | 882 | } |
| 883 | + |
856 | 884 | $sql = 'INSERT ' . ( ( $wgDBtype == 'postgres' ) ? '':'IGNORE ' ) . 'INTO ' . $this->m_dbs->tableName( $query->alias ) . " (id) VALUES $values"; |
857 | 885 | } // else: // interpret empty joinfields as impossible condition (empty result), ignore |
858 | 886 | if ( $sql ) { |
859 | 887 | $this->m_querylog[$query->alias][] = $sql; |
| 888 | + |
860 | 889 | if ( $this->m_qmode !== SMWQuery::MODE_DEBUG ) { |
861 | 890 | $this->m_dbs->query( $sql , 'SMW::executeQueries' ); |
862 | 891 | } |
863 | 892 | } |
864 | 893 | } |
| 894 | + |
865 | 895 | $query->jointable = $query->alias; |
866 | 896 | $query->joinfield = "$query->alias.id"; |
867 | | - $query->sortfields = array(); // make sure we got no sortfields |
| 897 | + $query->sortfields = array(); // Make sure we got no sortfields. |
868 | 898 | // TODO: currently this eliminates sortkeys, possibly keep them (needs different temp table format though, maybe not such a good thing to do) |
869 | 899 | break; |
870 | 900 | case SMW_SQL2_PROP_HIERARCHY: case SMW_SQL2_CLASS_HIERARCHY: // make a saturated hierarchy |
— | — | @@ -876,45 +906,58 @@ |
877 | 907 | /** |
878 | 908 | * Find subproperties or subcategories. This may require iterative computation, |
879 | 909 | * and temporary tables are used in many cases. |
| 910 | + * |
| 911 | + * @param SMWSQLStore2Query $query |
880 | 912 | */ |
881 | | - protected function executeHierarchyQuery( &$query ) { |
| 913 | + protected function executeHierarchyQuery( SMWSQLStore2Query &$query ) { |
882 | 914 | global $wgDBtype; |
| 915 | + global $smwgQSubpropertyDepth, $smwgQSubcategoryDepth; |
| 916 | + |
883 | 917 | $fname = "SMWSQLStore2Queries::executeQueries-hierarchy-$query->type (SMW)"; |
884 | 918 | wfProfileIn( $fname ); |
885 | | - global $smwgQSubpropertyDepth, $smwgQSubcategoryDepth; |
886 | | - $depth = ( $query->type == SMW_SQL2_PROP_HIERARCHY ) ? $smwgQSubpropertyDepth:$smwgQSubcategoryDepth; |
| 919 | + |
| 920 | + $depth = ( $query->type == SMW_SQL2_PROP_HIERARCHY ) ? $smwgQSubpropertyDepth : $smwgQSubcategoryDepth; |
| 921 | + |
887 | 922 | if ( $depth <= 0 ) { // treat as value, no recursion |
888 | 923 | $query->type = SMW_SQL2_VALUE; |
889 | 924 | wfProfileOut( $fname ); |
890 | 925 | return; |
891 | 926 | } |
| 927 | + |
892 | 928 | $values = ''; |
893 | 929 | $valuecond = ''; |
| 930 | + |
894 | 931 | foreach ( $query->joinfield as $value ) { |
895 | 932 | $values .= ( $values ? ',':'' ) . '(' . $this->m_dbs->addQuotes( $value ) . ')'; |
896 | 933 | $valuecond .= ( $valuecond ? ' OR ':'' ) . 'o_id=' . $this->m_dbs->addQuotes( $value ); |
897 | 934 | } |
| 935 | + |
898 | 936 | $smwtable = $this->m_dbs->tableName( ( $query->type == SMW_SQL2_PROP_HIERARCHY ) ? 'smw_subp2':'smw_subs2' ); |
899 | | - // try to safe time (SELECT is cheaper than creating/dropping 3 temp tables): |
| 937 | + |
| 938 | + // Try to safe time (SELECT is cheaper than creating/dropping 3 temp tables): |
900 | 939 | $res = $this->m_dbs->select( $smwtable, 's_id', $valuecond, array( 'LIMIT' => 1 ) ); |
| 940 | + |
901 | 941 | if ( !$this->m_dbs->fetchObject( $res ) ) { // no subobjects, we are done! |
902 | 942 | $this->m_dbs->freeResult( $res ); |
903 | 943 | $query->type = SMW_SQL2_VALUE; |
904 | 944 | wfProfileOut( $fname ); |
905 | 945 | return; |
906 | 946 | } |
| 947 | + |
907 | 948 | $this->m_dbs->freeResult( $res ); |
908 | 949 | $tablename = $this->m_dbs->tableName( $query->alias ); |
909 | 950 | $this->m_querylog[$query->alias] = array( "Recursively computed hierarchy for element(s) $values." ); |
910 | 951 | $query->jointable = $query->alias; |
911 | 952 | $query->joinfield = "$query->alias.id"; |
| 953 | + |
912 | 954 | if ( $this->m_qmode == SMWQuery::MODE_DEBUG ) { |
913 | 955 | wfProfileOut( $fname ); |
914 | | - return; // no real queries in debug mode |
| 956 | + return; // No real queries in debug mode. |
915 | 957 | } |
| 958 | + |
916 | 959 | $this->m_dbs->query( $this->getCreateTempIDTableSQL( $tablename ), 'SMW::executeHierarchyQuery' ); |
917 | 960 | |
918 | | - if ( array_key_exists( $values, $this->m_hierarchies ) ) { // just copy known result |
| 961 | + if ( array_key_exists( $values, $this->m_hierarchies ) ) { // Just copy known result. |
919 | 962 | $this->m_dbs->query( "INSERT INTO $tablename (id) SELECT id" . |
920 | 963 | ' FROM ' . $this->m_hierarchies[$values], |
921 | 964 | 'SMW::executeHierarchyQuery' ); |
— | — | @@ -930,28 +973,33 @@ |
931 | 974 | $tmpres = 'smw_res'; |
932 | 975 | $this->m_dbs->query( $this->getCreateTempIDTableSQL( $tmpnew ), 'SMW::executeQueries' ); |
933 | 976 | $this->m_dbs->query( $this->getCreateTempIDTableSQL( $tmpres ), 'SMW::executeQueries' ); |
934 | | - $this->m_dbs->query( "INSERT " . ( ( $wgDBtype == 'postgres' ) ? "":"IGNORE" ) . " INTO $tablename (id) VALUES $values", 'SMW::executeHierarchyQuery' ); |
935 | | - $this->m_dbs->query( "INSERT " . ( ( $wgDBtype == 'postgres' ) ? "":"IGNORE" ) . " INTO $tmpnew (id) VALUES $values", 'SMW::executeHierarchyQuery' ); |
| 977 | + $this->m_dbs->query( "INSERT " . ( ( $wgDBtype == 'postgres' ) ? "" : "IGNORE" ) . " INTO $tablename (id) VALUES $values", 'SMW::executeHierarchyQuery' ); |
| 978 | + $this->m_dbs->query( "INSERT " . ( ( $wgDBtype == 'postgres' ) ? "" : "IGNORE" ) . " INTO $tmpnew (id) VALUES $values", 'SMW::executeHierarchyQuery' ); |
936 | 979 | |
937 | 980 | for ( $i = 0; $i < $depth; $i++ ) { |
938 | | - $this->m_dbs->query( "INSERT " . ( ( $wgDBtype == 'postgres' ) ? '':'IGNORE ' ) . "INTO $tmpres (id) SELECT s_id" . ( $wgDBtype == 'postgres' ? '::integer':'' ) . " FROM $smwtable, $tmpnew WHERE o_id=id", |
| 981 | + $this->m_dbs->query( "INSERT " . ( ( $wgDBtype == 'postgres' ) ? '' : 'IGNORE ' ) . "INTO $tmpres (id) SELECT s_id" . ( $wgDBtype == 'postgres' ? '::integer':'' ) . " FROM $smwtable, $tmpnew WHERE o_id=id", |
939 | 982 | 'SMW::executeHierarchyQuery' ); |
940 | 983 | if ( $this->m_dbs->affectedRows() == 0 ) { // no change, exit loop |
941 | 984 | break; |
942 | 985 | } |
943 | | - $this->m_dbs->query( "INSERT " . ( ( $wgDBtype == 'postgres' ) ? '':'IGNORE ' ) . "INTO $tablename (id) SELECT $tmpres.id FROM $tmpres", |
| 986 | + |
| 987 | + $this->m_dbs->query( "INSERT " . ( ( $wgDBtype == 'postgres' ) ? '' : 'IGNORE ' ) . "INTO $tablename (id) SELECT $tmpres.id FROM $tmpres", |
944 | 988 | 'SMW::executeHierarchyQuery' ); |
| 989 | + |
945 | 990 | if ( $this->m_dbs->affectedRows() == 0 ) { // no change, exit loop |
946 | 991 | break; |
947 | 992 | } |
| 993 | + |
948 | 994 | $this->m_dbs->query( 'TRUNCATE TABLE ' . $tmpnew, 'SMW::executeHierarchyQuery' ); // empty "new" table |
949 | 995 | $tmpname = $tmpnew; |
950 | 996 | $tmpnew = $tmpres; |
951 | 997 | $tmpres = $tmpname; |
952 | 998 | } |
| 999 | + |
953 | 1000 | $this->m_hierarchies[$values] = $tablename; |
954 | | - $this->m_dbs->query( ( ( $wgDBtype == 'postgres' ) ? 'DROP TABLE IF EXISTS smw_new':'DROP TEMPORARY TABLE smw_new' ), 'SMW::executeHierarchyQuery' ); |
955 | | - $this->m_dbs->query( ( ( $wgDBtype == 'postgres' ) ? 'DROP TABLE IF EXISTS smw_res':'DROP TEMPORARY TABLE smw_res' ), 'SMW::executeHierarchyQuery' ); |
| 1001 | + $this->m_dbs->query( ( ( $wgDBtype == 'postgres' ) ? 'DROP TABLE IF EXISTS smw_new' : 'DROP TEMPORARY TABLE smw_new' ), 'SMW::executeHierarchyQuery' ); |
| 1002 | + $this->m_dbs->query( ( ( $wgDBtype == 'postgres' ) ? 'DROP TABLE IF EXISTS smw_res' : 'DROP TEMPORARY TABLE smw_res' ), 'SMW::executeHierarchyQuery' ); |
| 1003 | + |
956 | 1004 | wfProfileOut( $fname ); |
957 | 1005 | } |
958 | 1006 | |
— | — | @@ -960,7 +1008,7 @@ |
961 | 1009 | * in the SMWQuery $query. It is always required that $qid is the id of a query that joins with |
962 | 1010 | * smw_ids so that the field alias.smw_title is $available for default sorting. |
963 | 1011 | * |
964 | | - * @param $qid |
| 1012 | + * @param integer $qid |
965 | 1013 | */ |
966 | 1014 | protected function applyOrderConditions( $qid ) { |
967 | 1015 | $qobj = $this->m_queries[$qid]; |
— | — | @@ -999,6 +1047,9 @@ |
1000 | 1048 | |
1001 | 1049 | /** |
1002 | 1050 | * Get a SQL option array for the given query and preprocessed query object at given id. |
| 1051 | + * |
| 1052 | + * @param SMWQuery $query |
| 1053 | + * @param integer $rootid |
1003 | 1054 | */ |
1004 | 1055 | protected function getSQLOptions( SMWQuery $query, $rootid ) { |
1005 | 1056 | global $smwgQSortingSupport, $smwgQRandSortingSupport; |
— | — | @@ -1040,6 +1091,8 @@ |
1041 | 1092 | * a table exists, so the code is ready to reuse existing tables if the code was modified to |
1042 | 1093 | * keep them after query answering. Also, PostgreSQL tables will use a RULE to achieve built-in |
1043 | 1094 | * duplicate elimination. The latter is done using INSERT IGNORE in MySQL. |
| 1095 | + * |
| 1096 | + * @param string $tablename |
1044 | 1097 | */ |
1045 | 1098 | protected function getCreateTempIDTableSQL( $tablename ) { |
1046 | 1099 | global $wgDBtype; |
— | — | @@ -1063,4 +1116,4 @@ |
1064 | 1117 | } |
1065 | 1118 | } |
1066 | 1119 | |
1067 | | -} |
| 1120 | +} |
\ No newline at end of file |