Sybase iAnywhere SQL AAnywhere Mobile and Embedded Database

Rows and Columns


SQL Anywhere perspective on technology

header image

rewrite_query

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
// Take a provided query and rewrite the syntax in SA format
function rewrite_query($query) {          
  global $wpdb;	
 
  //Replace the column delimiter " with "
  $query = preg_replace( "/"/", '"', $query );
 
  //Rewrite INSERT/UPDATE to INSERT/UPDATE INTO tbl ON EXISTING SKIP  
  $pattern = "/INSERT\s*IGNORE\s*INTO\s*/i";
  $pattern2 = "/\)\s*VALUES\s*\(/i";
  $pattern3 = "/UPDATE\s*IGNORE\s*/i";
  if( preg_match( $pattern, $query, $insmatches ) ) {
      $query = preg_replace( $pattern, 'INSERT INTO ', $query );
      $query = preg_replace( $pattern2, ') ON EXISTING SKIP VALUES (', $query );
      //Replace all empty strings with DEFAULT
	    $query = preg_replace( "/''/", "DEFAULT", $query );
  }else if( preg_match( $pattern3, $query, $insmatches ) ) {
      $query = preg_replace( $pattern3, 'UPDATE ', $query );
  }elseif( preg_match( "/SHOW TABLES\s*;/i", $query) > 0 ) {
      //Replace SHOW TABLES with SELECT table_name FROM SYSTABLE
      $query = "SELECT table_name FROM SYSTABLE;";
  } elseif( preg_match( "/FOUND_ROWS\s*\(\s*\)/i", $query) > 0 ) {
       //Replace FOUND_ROWS() with @@rowcount  - it is an imperfect solution, but shouldn't break anything
       $query = "SELECT @@rowcount FROM DUMMY;";
  } elseif( preg_match( "/DESCRIBE\s*(\w+)\s*;/i", $query, $tablename ) > 0 ) {
       //Replace DESCRIBE <table> with SELECT statement    
       $query = "SELECT sc.column_name as \"Field\",  
               IF( sd.domain_name = 'char' OR  sd.domain_name = 'varchar' ) THEN 
                   sd.domain_name ||'(' || cast( sc.width as char(5) ) || ')' 
               ELSE sd.domain_name 
               ENDIF as \"Type\", 
               IF( sc.nulls = 'N' ) THEN 'NO' ELSE 'YES' ENDIF as \"Null\",
               IF( sc.pkey = 'Y' ) THEN 'PRI' ELSE 'NO' ENDIF as \"Key\",
               sc.\"default\" as \"Default\"
             FROM SYSCOLUMN sc, SYSDOMAIN sd, SYSTABLE st 
             WHERE sc.domain_id = sd.domain_id
               AND st.table_id = sc.table_id
               AND st.table_name = '".$tablename[1]."';";		
    } elseif( preg_match( "/SHOW\s*INDEX\s*FROM\s*(\w+)\s*;/i", $query, $tablename ) > 0 ) {
       //Replace SHOW INDEX FROM <table> with SELECT statement    
       $query = "SELECT st.table_name as \"Table\",
               IF( si.\"unique\" = 'Y' ) THEN 0 ELSE 1 ENDIF as \"Non-unique\",
               si.index_name as \"key_name\",
               sic.\"sequence\"+1 as \"Seq_in_index\",
               sc.column_name as \"Column_name\",
               IF( sic.\"order\" = 'A' ) THEN 'A' ELSE NULL ENDIF as \"Collation\",
               0 as \"Cardinality\",
               NULL as \"Sub_part\",
               NULL as \"Packed\",
               IF( si.\"unique\" = 'U' ) THEN 'YES' ELSE '' ENDIF as \"NULL\",
               '' as \"Index_type\",
               si.\"remarks\" as \"Comment\"
             FROM SYSINDEX si, SYSTABLE  st, SYSIXCOL sic, SYSCOLUMN sc 
             WHERE 
               si.table_id = st.\"table_id\"
               AND sic.index_id = si.\"index_id\" 
               AND sic.table_id = si.\"table_id\"
               AND sc.table_id = sic.\"table_id\"
               AND sc.column_id = sic.\"column_id\"
               AND st.\"table_name\" = '".$tablename[1]."';";				
   } elseif( preg_match( "/^ALTER\s*TABLE\s*(\w+)\s*/i", $query ) > 0 ) {
       //Currently, this has only been tested on an upgrade from 1.5 to 2.3
        if( preg_match( "/ALTER\s*TABLE\s*(\w+)\s*ADD\s*COLUMN\s*/i", $query, $alter ) > 0 ) {
           //Remove COLUMN keyword from ALTER TABLE <table> ADD COLUMN
           $query = preg_replace( '/\s*COLUMN\s*/i', " ", $query);		
       }elseif( preg_match( "/ALTER\s*TABLE\s*(\w+)\s*CHANGE\s*COLUMN\s*/i", $query, $alter ) > 0 ) {
           //Remove COLUMN keyword from ALTER TABLE CHANGE COLUMN and change CHANGE to MODIFY
           $query = preg_replace( '/\s*CHANGE\s*COLUMN\s*\w+\s*/i', " MODIFY ", $query);
       }elseif( preg_match( "/ALTER\s*TABLE\s*(\w+)\s*ALTER\s*COLUMN\s*(\w+)\s*SET\s*/i", $query, $alter ) > 0 ) {
           //Remove COLUMN keyword from ALTER TABLE CHANGE COLUMN and change CHANGE to MODIFY
           $query = preg_replace( '/\s*ALTER\s*COLUMN\s*\w+\s*SET\s*/i', " MODIFY ".$alter[2]." ", $query);
       }
 
       //Replace default date with corrected date
      	$query = preg_replace( "/0000-00-00 00:00:00/", "1970-01-01 00:00:00", $query );
 
       //Remove size specs for bigint and int columns
       $query = preg_replace( "/int\(\d+\)\s*/i", "int ", $query );		
 
       //Replace enum( a, b, c ) with check constraint check( @col IN( a, b, c ) )
      	if( preg_match( "/\s*enum\s*\(/i", $query, $alter ) > 0 ) {
           $query = preg_replace( "/\)/i", " ))", $query );
           $query = preg_replace( "/\s*enum\s*\(/i", " varchar(128) CHECK( @col IN (", $query );
       }
 
       //Replace longtext/text with long varchar
       $query = preg_replace( "/\s*longtext\s*/i", " long varchar ", $query );		
       $query = preg_replace( "/\s*text\s*/i", " long varchar ", $query );		
 
       //Replace auto_increment with default autoincrement
       $query = preg_replace( "/\s*auto_increment\s*/i", " default autoincrement ", $query );
 
       //Fix poor preg_replace in upgrade.php for DEFAULT CHECK constraints
       //if( preg_match( "/\s*MODIFY\s*\w+\s*DEFAULT\s*'\w+'\s*CHECK\(/i", $query, $alter ) > 0 ) {
       //    $query = $query.') )';
       //}
 
       //Replace bigint unsigned with unsigned bigint
       $query = preg_replace( "/\s*bigint\s*unsigned\s*/i", " unsigned bigint ", $query );
 
   } elseif( preg_match( "/^ALTER\s*TABLE\s*(\w+)\s*ADD\s*KEY\s*(\w+)/i", $query, $alter ) > 0 ) {
       //REPLACE ALTER TABLE ADD KEY with CREATE INDEX
      $query = preg_replace( "/ALTER\s*TABLE\s*\w+\s*ADD\s*KEY\s*\w+/i", "CREATE INDEX ".$alter[2]." ON ".$alter[1], $query );
  } else {
       //Find LIMIT clause integer arguments and replace with TOP N
       //  START AT m
      //MySQL syntax for LIMIT is:
       //  LIMIT {[offset,] row_count | row_count OFFSET offset}]
       //Note that the LIMIT clause is at the end of the query
 
       // LIMIT followed by an integer, possibly followed by a comma and another integer
       $pattern = "/LIMIT\s*(\d+)((\s*,?\s*)(\d+)*)/i";  
       $matched = preg_match( $pattern, $query, $limitmatches );
    	if( $matched == 1 ) {
           //Remove the LIMIT statement, replace offset 0 with 1
           $query = preg_replace( $pattern, '', $query);
           if( count( $limitmatches ) == 5 ) {
               if( $limitmatches[1] == '0' ) {
                   $limitmatches[1] = '1';
               }
               $replacement = 'SELECT TOP '.$limitmatches[4].' START AT '.$limitmatches[1].' ';
          } else {
       	      $replacement = 'SELECT TOP '.$limitmatches[1].' ';
          }	
       	  $query = preg_replace( '/^\s*SELECT\s*/i', $replacement, $query );
   		}
 
       //Replace date_add with DATEADD with translated INTERVAL arguments
       //MySQL date_add syntax: 
    	//  DATE_ADD(date,INTERVAL expr unit)
    	//SQL Anywhere syntax:
    	//  DATEADD( unit, expr, date )
    	//Match a date: (19|20)\d\d([- /.])(0[1-9]|1[012])\2(0[1-9]|[12][0-9]|3[01])  - \2 doesn't work as a back reference to the date separator	    
    	//This will match a date string in the first parameter of the date_add function	    
       //    $pattern = "/(DATE_ADD\s*\(\s*')((19|20)\d\d([- \/.])(0[1-9]|1[012])([- \/.])(0[1-9]|[12][0-9]|3[01]))('\s*,\s*INTERVAL\s*)(\d+)(\:*)(\d*)(\s*HOUR_MINUTE\s*|\s*MINUTE\s*|\s*SECOND\s*|\s*DAY\s*|\s*HOUR\s*)(\s*\))/i";
 
       //This will match a variable in the first parameter of the date_add function
    	$pattern = "/(DATE_ADD\s*\(\s*)(\w+)(\s*,\s*INTERVAL\s*)(\d+)(\:*)(\d*)(\s*HOUR_MINUTE\s*|\s*MINUTE\s*|\s*SECOND\s*|\s*DAY\s*|\s*HOUR\s*)(\s*\))/i";
           	$matched = preg_match( $pattern, $query, $dateaddmatches );
    	if( $matched == 1 ) {
           if( preg_match('/\s*hour_minute\s*/i', $dateaddmatches[7]) > 0 ) {
               $addminutes = ($dateaddmatches[4]*60) + $dateaddmatches[6];
               $replacement = 'DATEADD( minute, '.$addminutes.', \''.$dateaddmatches[2].'\' )';
           } else {
             $replacement = 'DATEADD( '.$dateaddmatches[7].', '.$dateaddmatches[4].', '.$dateaddmatches[2].' )';
           }
       	  $query = preg_replace( $pattern, $replacement, $query );
    	}
 
       //Replace SQL_CALC_FOUND_ROWS with nothing - it is an imperfect solution, but shouldn't break anything
      $query = preg_replace( "/\s*SQL_CALC_FOUND_ROWS\s*/i", ' ', $query);
 
    	//fix some GROUP BY issues with queries that MySQL allows
    	$pattern = "/SELECT\s*DISTINCT\s*YEAR\(post_date\)/i";
    	if( preg_match( $pattern, $query, $foo ) ) {
           $pattern = "/GROUP\s*BY\s*YEAR\s*\(\s*post_date\s*\), MONTH\(post_date\), DAYOFMONTH\(post_date\)/i";
           $pattern2 = "/GROUP\s*BY\s*YEAR\s*\(\s*post_date\s*\), MONTH\(post_date\)/i";
           $pattern3 = "/GROUP\s*BY\s*YEAR\s*\(\s*post_date\s*\)/i";
           $orderbypattern = "/ORDER\s*BY\s*post_date\s*/i";
           if( preg_match( $orderbypattern, $query ) ) {
               if( preg_match( $pattern, $query ) ) {
                   $query = preg_replace( $orderbypattern, "ORDER BY YEAR(post_date) DESC, MONTH(post_date) DESC, DAYOFMONTH(post_date)", $query );
               }else if( preg_match( $pattern2, $query ) ) {
                   $query = preg_replace( $orderbypattern, "ORDER BY YEAR(post_date) DESC, MONTH(post_date)", $query );
               }else if( preg_match( $pattern3, $query ) ) {
                   $query = preg_replace( $orderbypattern, "ORDER BY YEAR(post_date)", $query );
               } else {
                   $query = preg_replace( $orderbypattern, "ORDER BY YEAR(post_date)", $query );
               }
           }		   
      }
       // Remove GROUP BY wpdb->posts.id because ID is a pkey, so grouping by it makes no sense
       $pattern = "/\s*GROUP\s*BY\s*".$wpdb->posts.".ID\s*/i";
       $query = preg_replace( $pattern, " ", $query );
 
    	//Rewrite a specific IF( <condition>, t, f ) to IF <condition> THEN t ELSE f ENDIF
    	$pattern = "/SELECT\s*\*\s*,\s*IF\s*\(\s*(DATEADD\s*\(\s*MINUTE\s*,\s*\d+\s*,\s*\w+\s*\)\s*\>=\s*NOW\(\))\s*,\s*(\w+)\s*,\s*(\w+)\)/i"; 
       if( preg_match( $pattern, $query, $ifmatches ) ) {
           $replacement = 'SELECT *, IF '.$ifmatches[1].' THEN '.$ifmatches[2].' ELSE '.$ifmatches[3].' ENDIF';
       	  $query = preg_replace( $pattern, $replacement, $query );
    	}
 
       //Remove order by meta_id from select...meta_key...order by meta_id 
       // to avoid syntax error (can;t sort by a column not in the select list...
       $pattern = '/ORDER\s*BY\s*meta_id\s*DESC\s*/i';
       $query = preg_replace( $pattern, ' ', $query);
   }	
   return $query; 
}
Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • DZone
  • LinkedIn
  • NewsVine
  • Print this article!
  • Reddit
  • Slashdot
  • StumbleUpon
  • Technorati
  • TwitThis
  • Yahoo! Buzz

2 Comments

2 responses so far ↓

Leave a Comment

Note that all comments are currently being moderated until I have a better handle on spam, so your comment may not appear for a couple of hours

Sybase Privacy policy