diff options
author | rowanbeentje <rowan@beent.je> | 2009-03-19 23:44:06 +0000 |
---|---|---|
committer | rowanbeentje <rowan@beent.je> | 2009-03-19 23:44:06 +0000 |
commit | ddf7d62d20614111acdd420075ef762d6deaa8d7 (patch) | |
tree | cf5ce70dde9c25d1b21da1cded5cee9f3e562704 | |
parent | 4a8d0f731bbb03b9ace041d421800e5c6470326a (diff) | |
download | sequelpro-ddf7d62d20614111acdd420075ef762d6deaa8d7.tar.gz sequelpro-ddf7d62d20614111acdd420075ef762d6deaa8d7.tar.bz2 sequelpro-ddf7d62d20614111acdd420075ef762d6deaa8d7.zip |
SPSQLParser changes:
- Use method caches for oft-called functions, and support caching of chunks of the underlying string for string walking, resulting in an overall 1.3x-1.4x parsing speedup.
- Improve handling of multi-character comment starts (eg / or -) at the very end of strings
- When running splitString... methods return even empty strings for consistency.
- Update TableDump and TableData to match new usage
SPStringAddition changes:
- Add a formatter for time intervals.
CMMCPConnection changes:
- Add support for timing queries
CustomQuery and nib changes:
- Change the "Run Queries" button to "Run All".
- Add a "Run Current" button, which runs the query the text caret is currently positioned inside; if text is actually selected, this changes to "Run Selection". This addresses Issue #43.
- Amend the "rows affected" string to better reflect the actual number of rows altered by several queries, show the query count if > 1, and display the overall execution time of the queries. This addresses Issue #142.
- No longer execute blank strings as part of the custom query, preventing errors.
-rw-r--r-- | Interfaces/English.lproj/DBView.xib | 212 | ||||
-rw-r--r-- | Source/CMMCPConnection.h | 2 | ||||
-rw-r--r-- | Source/CMMCPConnection.m | 22 | ||||
-rw-r--r-- | Source/CustomQuery.h | 9 | ||||
-rw-r--r-- | Source/CustomQuery.m | 505 | ||||
-rw-r--r-- | Source/SPSQLParser.h | 18 | ||||
-rw-r--r-- | Source/SPSQLParser.m | 116 | ||||
-rw-r--r-- | Source/SPStringAdditions.h | 1 | ||||
-rw-r--r-- | Source/SPStringAdditions.m | 45 | ||||
-rw-r--r-- | Source/SPTableData.m | 52 | ||||
-rw-r--r-- | Source/TableDump.m | 5 |
11 files changed, 748 insertions, 239 deletions
diff --git a/Interfaces/English.lproj/DBView.xib b/Interfaces/English.lproj/DBView.xib index 74755ef7..f30440be 100644 --- a/Interfaces/English.lproj/DBView.xib +++ b/Interfaces/English.lproj/DBView.xib @@ -44,7 +44,7 @@ <string key="NSWindowContentMaxSize">{3.40282e+38, 3.40282e+38}</string> <string key="NSWindowContentMinSize">{780, 480}</string> <object class="NSView" key="NSWindowView" id="579726586"> - <nil key="NSNextResponder"/> + <reference key="NSNextResponder"/> <int key="NSvFlags">256</int> <object class="NSMutableArray" key="NSSubviews"> <bool key="EncodedWithXMLCoder">YES</bool> @@ -78,6 +78,7 @@ <int key="NSvFlags">4352</int> <string key="NSFrameSize">{194, 393}</string> <reference key="NSSuperview" ref="73685676"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="_NSCornerView" key="NSCornerView"> <nil key="NSNextResponder"/> @@ -174,6 +175,8 @@ </object> <string key="NSFrame">{{1, 1}, {194, 393}}</string> <reference key="NSSuperview" ref="233472824"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="251040077"/> <reference key="NSDocView" ref="251040077"/> <object class="NSColor" key="NSBGColor" id="1024678221"> <int key="NSColorSpace">6</int> @@ -188,6 +191,7 @@ <int key="NSvFlags">-2147483392</int> <string key="NSFrame">{{175, 1}, {15, 481}}</string> <reference key="NSSuperview" ref="233472824"/> + <reference key="NSWindow"/> <reference key="NSTarget" ref="233472824"/> <string key="NSAction">_doScroller:</string> <double key="NSPercent">9.979253e-01</double> @@ -197,6 +201,7 @@ <int key="NSvFlags">256</int> <string key="NSFrame">{{-100, -100}, {141, 11}}</string> <reference key="NSSuperview" ref="233472824"/> + <reference key="NSWindow"/> <int key="NSsFlags">257</int> <reference key="NSTarget" ref="233472824"/> <string key="NSAction">_doScroller:</string> @@ -205,6 +210,8 @@ </object> <string key="NSFrameSize">{196, 395}</string> <reference key="NSSuperview" ref="355288374"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="73685676"/> <int key="NSsFlags">530</int> <reference key="NSVScroller" ref="693168867"/> <reference key="NSHScroller" ref="656188692"/> @@ -226,6 +233,7 @@ <int key="NSvFlags">4352</int> <string key="NSFrameSize">{194, 123}</string> <reference key="NSSuperview" ref="685057119"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="_NSCornerView" key="NSCornerView"> <nil key="NSNextResponder"/> @@ -289,6 +297,8 @@ </object> <string key="NSFrame">{{1, 1}, {194, 123}}</string> <reference key="NSSuperview" ref="298226231"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="347093764"/> <reference key="NSDocView" ref="347093764"/> <reference key="NSBGColor" ref="1024678221"/> <int key="NScvFlags">4</int> @@ -298,6 +308,7 @@ <int key="NSvFlags">-2147483392</int> <string key="NSFrame">{{175, 1}, {15, 481}}</string> <reference key="NSSuperview" ref="298226231"/> + <reference key="NSWindow"/> <reference key="NSTarget" ref="298226231"/> <string key="NSAction">_doScroller:</string> <double key="NSPercent">9.979253e-01</double> @@ -307,6 +318,7 @@ <int key="NSvFlags">256</int> <string key="NSFrame">{{-100, -100}, {141, 11}}</string> <reference key="NSSuperview" ref="298226231"/> + <reference key="NSWindow"/> <int key="NSsFlags">257</int> <reference key="NSTarget" ref="298226231"/> <string key="NSAction">_doScroller:</string> @@ -315,6 +327,8 @@ </object> <string key="NSFrame">{{0, 404}, {196, 125}}</string> <reference key="NSSuperview" ref="355288374"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="685057119"/> <int key="NSsFlags">530</int> <reference key="NSVScroller" ref="245346414"/> <reference key="NSHScroller" ref="353686052"/> @@ -324,12 +338,14 @@ </object> <string key="NSFrame">{{0, 22}, {196, 529}}</string> <reference key="NSSuperview" ref="372294785"/> + <reference key="NSWindow"/> </object> <object class="NSButton" id="644515521"> <reference key="NSNextResponder" ref="372294785"/> <int key="NSvFlags">292</int> <string key="NSFrame">{{0, -1}, {32, 25}}</string> <reference key="NSSuperview" ref="372294785"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="492393561"> <int key="NSCellFlags">-2080244224</int> @@ -358,6 +374,7 @@ <int key="NSvFlags">292</int> <string key="NSFrame">{{20, 0}, {46, 25}}</string> <reference key="NSSuperview" ref="372294785"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSPopUpButtonCell" key="NSCell" id="753352469"> <int key="NSCellFlags">-2076049856</int> @@ -366,7 +383,7 @@ <reference key="NSControlView" ref="1029554648"/> <int key="NSButtonFlags">-2042609409</int> <int key="NSButtonFlags2">35</int> - <object class="NSCustomResource" key="NSNormalImage" id="831599241"> + <object class="NSCustomResource" key="NSNormalImage"> <string key="NSClassName">NSImage</string> <string key="NSResourceName">button_action</string> </object> @@ -382,7 +399,10 @@ <int key="NSKeyEquivModMask">1048576</int> <int key="NSMnemonicLoc">2147483647</int> <int key="NSState">1</int> - <reference key="NSImage" ref="831599241"/> + <object class="NSCustomResource" key="NSImage"> + <string key="NSClassName">NSImage</string> + <string key="NSResourceName">button_action</string> + </object> <string key="NSAction">_popUpItemAction:</string> <reference key="NSTarget" ref="753352469"/> </object> @@ -468,6 +488,7 @@ </object> <string key="NSFrame">{{179, 0}, {15, 23}}</string> <reference key="NSSuperview" ref="372294785"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSImageCell" key="NSCell" id="875296521"> <int key="NSCellFlags">130560</int> @@ -500,6 +521,7 @@ </object> <string key="NSFrame">{{60, 0}, {119, 23}}</string> <reference key="NSSuperview" ref="372294785"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSImageCell" key="NSCell" id="761703901"> <int key="NSCellFlags">130560</int> @@ -518,6 +540,7 @@ </object> <string key="NSFrameSize">{194, 550}</string> <reference key="NSSuperview" ref="937377983"/> + <reference key="NSWindow"/> <string key="NSClassName">NSView</string> </object> <object class="NSCustomView" id="604818293"> @@ -530,6 +553,7 @@ <int key="NSvFlags">274</int> <string key="NSFrame">{{-7, -10}, {672, 564}}</string> <reference key="NSSuperview" ref="604818293"/> + <reference key="NSWindow"/> <object class="NSMutableArray" key="NSTabViewItems"> <bool key="EncodedWithXMLCoder">YES</bool> <object class="NSTabViewItem" id="831053945"> @@ -566,6 +590,7 @@ </object> <string key="NSFrame">{{608, 6}, {10, 13}}</string> <reference key="NSSuperview" ref="220777809"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSImageCell" key="NSCell" id="545156725"> <int key="NSCellFlags">130560</int> @@ -586,6 +611,7 @@ <int key="NSvFlags">257</int> <string key="NSFrame">{{400, 8}, {55, 11}}</string> <reference key="NSSuperview" ref="220777809"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="42997882"> <int key="NSCellFlags">67239424</int> @@ -606,6 +632,7 @@ <int key="NSvFlags">257</int> <string key="NSFrame">{{456, 6}, {135, 15}}</string> <reference key="NSSuperview" ref="220777809"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSPopUpButtonCell" key="NSCell" id="126755904"> <int key="NSCellFlags">-1539178944</int> @@ -714,12 +741,14 @@ <int key="NSvFlags">4352</int> <string key="NSFrameSize">{625, 282}</string> <reference key="NSSuperview" ref="16936123"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSTableHeaderView" key="NSHeaderView" id="926883367"> <reference key="NSNextResponder" ref="639957061"/> <int key="NSvFlags">256</int> <string key="NSFrameSize">{625, 17}</string> <reference key="NSSuperview" ref="639957061"/> + <reference key="NSWindow"/> <reference key="NSTableView" ref="715508012"/> </object> <object class="_NSCornerView" key="NSCornerView" id="868771861"> @@ -727,6 +756,7 @@ <int key="NSvFlags">-2147483392</int> <string key="NSFrame">{{-26, 0}, {16, 17}}</string> <reference key="NSSuperview" ref="22340145"/> + <reference key="NSWindow"/> </object> <object class="NSMutableArray" key="NSTableColumns"> <bool key="EncodedWithXMLCoder">YES</bool> @@ -1259,6 +1289,8 @@ </object> <string key="NSFrame">{{1, 17}, {625, 282}}</string> <reference key="NSSuperview" ref="22340145"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="715508012"/> <reference key="NSDocView" ref="715508012"/> <reference key="NSBGColor" ref="1024678221"/> <int key="NScvFlags">4</int> @@ -1268,6 +1300,7 @@ <int key="NSvFlags">-2147483392</int> <string key="NSFrame">{{636, 17}, {15, 265}}</string> <reference key="NSSuperview" ref="22340145"/> + <reference key="NSWindow"/> <reference key="NSTarget" ref="22340145"/> <string key="NSAction">_doScroller:</string> <double key="NSPercent">8.170732e-01</double> @@ -1277,6 +1310,7 @@ <int key="NSvFlags">-2147483392</int> <string key="NSFrame">{{1, 282}, {635, 15}}</string> <reference key="NSSuperview" ref="22340145"/> + <reference key="NSWindow"/> <int key="NSsFlags">1</int> <reference key="NSTarget" ref="22340145"/> <string key="NSAction">_doScroller:</string> @@ -1291,6 +1325,8 @@ </object> <string key="NSFrame">{{1, 0}, {625, 17}}</string> <reference key="NSSuperview" ref="22340145"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="926883367"/> <reference key="NSDocView" ref="926883367"/> <reference key="NSBGColor" ref="1024678221"/> <int key="NScvFlags">4</int> @@ -1299,6 +1335,8 @@ </object> <string key="NSFrame">{{-1, 24}, {627, 300}}</string> <reference key="NSSuperview" ref="220777809"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="16936123"/> <int key="NSsFlags">562</int> <reference key="NSVScroller" ref="943144555"/> <reference key="NSHScroller" ref="456666876"/> @@ -1312,6 +1350,7 @@ <int key="NSvFlags">290</int> <string key="NSFrame">{{107, 0}, {519, 26}}</string> <reference key="NSSuperview" ref="220777809"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="449911070"> <int key="NSCellFlags">-2080244224</int> @@ -1332,6 +1371,7 @@ <int key="NSvFlags">260</int> <string key="NSFrame">{{-1, 0}, {28, 26}}</string> <reference key="NSSuperview" ref="220777809"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="615057790"> <int key="NSCellFlags">604110336</int> @@ -1356,6 +1396,7 @@ <int key="NSvFlags">260</int> <string key="NSFrame">{{26, 0}, {28, 26}}</string> <reference key="NSSuperview" ref="220777809"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="551584499"> <int key="NSCellFlags">604110336</int> @@ -1380,6 +1421,7 @@ <int key="NSvFlags">260</int> <string key="NSFrame">{{53, 0}, {28, 26}}</string> <reference key="NSSuperview" ref="220777809"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="183531828"> <int key="NSCellFlags">604110336</int> @@ -1404,6 +1446,7 @@ <int key="NSvFlags">260</int> <string key="NSFrame">{{80, 0}, {28, 26}}</string> <reference key="NSSuperview" ref="220777809"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="230889234"> <int key="NSCellFlags">67239424</int> @@ -1426,6 +1469,7 @@ </object> <string key="NSFrameSize">{626, 324}</string> <reference key="NSSuperview" ref="628830973"/> + <reference key="NSWindow"/> <string key="NSClassName">NSView</string> </object> <object class="NSCustomView" id="1063281455"> @@ -1438,6 +1482,7 @@ <int key="NSvFlags">264</int> <string key="NSFrame">{{7, 184}, {46, 14}}</string> <reference key="NSSuperview" ref="1063281455"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="749773740"> <int key="NSCellFlags">67239424</int> @@ -1464,12 +1509,14 @@ <int key="NSvFlags">4352</int> <string key="NSFrameSize">{625, 138}</string> <reference key="NSSuperview" ref="794929378"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSTableHeaderView" key="NSHeaderView" id="459548655"> <reference key="NSNextResponder" ref="1038672854"/> <int key="NSvFlags">256</int> <string key="NSFrameSize">{625, 17}</string> <reference key="NSSuperview" ref="1038672854"/> + <reference key="NSWindow"/> <reference key="NSTableView" ref="584834515"/> </object> <object class="_NSCornerView" key="NSCornerView" id="476444025"> @@ -1477,6 +1524,7 @@ <int key="NSvFlags">-2147483392</int> <string key="NSFrame">{{-26, 0}, {16, 17}}</string> <reference key="NSSuperview" ref="376224367"/> + <reference key="NSWindow"/> </object> <object class="NSMutableArray" key="NSTableColumns"> <bool key="EncodedWithXMLCoder">YES</bool> @@ -1729,6 +1777,8 @@ </object> <string key="NSFrame">{{1, 17}, {625, 138}}</string> <reference key="NSSuperview" ref="376224367"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="584834515"/> <reference key="NSDocView" ref="584834515"/> <reference key="NSBGColor" ref="1024678221"/> <int key="NScvFlags">4</int> @@ -1738,6 +1788,7 @@ <int key="NSvFlags">-2147483392</int> <string key="NSFrame">{{84, 17}, {15, 67}}</string> <reference key="NSSuperview" ref="376224367"/> + <reference key="NSWindow"/> <reference key="NSTarget" ref="376224367"/> <string key="NSAction">_doScroller:</string> <double key="NSPercent">8.170732e-01</double> @@ -1747,6 +1798,7 @@ <int key="NSvFlags">-2147483392</int> <string key="NSFrame">{{1, 123}, {612, 15}}</string> <reference key="NSSuperview" ref="376224367"/> + <reference key="NSWindow"/> <int key="NSsFlags">1</int> <reference key="NSTarget" ref="376224367"/> <string key="NSAction">_doScroller:</string> @@ -1761,6 +1813,8 @@ </object> <string key="NSFrame">{{1, 0}, {625, 17}}</string> <reference key="NSSuperview" ref="376224367"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="459548655"/> <reference key="NSDocView" ref="459548655"/> <reference key="NSBGColor" ref="1024678221"/> <int key="NScvFlags">4</int> @@ -1769,6 +1823,8 @@ </object> <string key="NSFrame">{{-1, 22}, {627, 156}}</string> <reference key="NSSuperview" ref="1063281455"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="794929378"/> <int key="NSsFlags">562</int> <reference key="NSVScroller" ref="1019209947"/> <reference key="NSHScroller" ref="328951385"/> @@ -1782,6 +1838,7 @@ <int key="NSvFlags">258</int> <string key="NSFrame">{{80, -2}, {546, 26}}</string> <reference key="NSSuperview" ref="1063281455"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="617707076"> <int key="NSCellFlags">-2080244224</int> @@ -1802,6 +1859,7 @@ <int key="NSvFlags">260</int> <string key="NSFrame">{{-1, -2}, {28, 26}}</string> <reference key="NSSuperview" ref="1063281455"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="304948035"> <int key="NSCellFlags">604110336</int> @@ -1826,6 +1884,7 @@ <int key="NSvFlags">260</int> <string key="NSFrame">{{26, -2}, {28, 26}}</string> <reference key="NSSuperview" ref="1063281455"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="515502959"> <int key="NSCellFlags">604110336</int> @@ -1850,6 +1909,7 @@ <int key="NSvFlags">260</int> <string key="NSFrame">{{53, -2}, {28, 26}}</string> <reference key="NSSuperview" ref="1063281455"/> + <reference key="NSWindow"/> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="425551900"> <int key="NSCellFlags">67239424</int> @@ -1869,15 +1929,18 @@ </object> <string key="NSFrame">{{0, 333}, {626, 198}}</string> <reference key="NSSuperview" ref="628830973"/> + <reference key="NSWindow"/> <string key="NSClassName">NSView</string> </object> </object> <string key="NSFrame">{{7, 10}, {626, 531}}</string> <reference key="NSSuperview" ref="461236772"/> + <reference key="NSWindow"/> </object> </object> <string key="NSFrame">{{10, 7}, {637, 544}}</string> <reference key="NSSuperview" ref="714795046"/> + <reference key="NSWindow"/> </object> <string key="NSLabel">Structure</string> <reference key="NSColor" ref="62854682"/> @@ -2532,7 +2595,7 @@ <object class="NSTextView" id="1055190999"> <reference key="NSNextResponder" ref="1072692119"/> <int key="NSvFlags">6418</int> - <string key="NSFrameSize">{626, 14}</string> + <string key="NSFrameSize">{625, 14}</string> <reference key="NSSuperview" ref="1072692119"/> <object class="NSTextContainer" key="NSTextContainer" id="326170846"> <object class="NSLayoutManager" key="NSLayoutManager"> @@ -2550,7 +2613,7 @@ <nil key="NSDelegate"/> </object> <reference key="NSTextView" ref="1055190999"/> - <double key="NSWidth">6.260000e+02</double> + <double key="NSWidth">6.250000e+02</double> <int key="NSTCFlags">1</int> </object> <object class="NSTextViewSharedData" key="NSSharedData"> @@ -2590,7 +2653,7 @@ <nil key="NSDelegate"/> </object> </object> - <string key="NSFrame">{{1, 1}, {626, 155}}</string> + <string key="NSFrame">{{1, 1}, {625, 155}}</string> <reference key="NSSuperview" ref="71560786"/> <reference key="NSNextKeyView" ref="1055190999"/> <reference key="NSDocView" ref="1055190999"/> @@ -2622,7 +2685,7 @@ <double key="NSPercent">9.456522e-01</double> </object> </object> - <string key="NSFrameSize">{628, 157}</string> + <string key="NSFrameSize">{627, 157}</string> <reference key="NSSuperview" ref="873437769"/> <reference key="NSNextKeyView" ref="1072692119"/> <int key="NSsFlags">530</int> @@ -2631,7 +2694,7 @@ <reference key="NSContentView" ref="1072692119"/> </object> </object> - <string key="NSFrameSize">{628, 156}</string> + <string key="NSFrameSize">{627, 156}</string> <reference key="NSSuperview" ref="894339536"/> <string key="NSClassName">NSView</string> </object> @@ -2653,13 +2716,13 @@ <object class="NSTableView" id="581095761"> <reference key="NSNextResponder" ref="90844306"/> <int key="NSvFlags">4352</int> - <string key="NSFrameSize">{626, 226}</string> + <string key="NSFrameSize">{625, 226}</string> <reference key="NSSuperview" ref="90844306"/> <bool key="NSEnabled">YES</bool> <object class="NSTableHeaderView" key="NSHeaderView" id="1038415606"> <reference key="NSNextResponder" ref="533922066"/> <int key="NSvFlags">256</int> - <string key="NSFrameSize">{626, 17}</string> + <string key="NSFrameSize">{625, 17}</string> <reference key="NSSuperview" ref="533922066"/> <reference key="NSTableView" ref="581095761"/> </object> @@ -2672,7 +2735,7 @@ <object class="NSMutableArray" key="NSTableColumns"> <bool key="EncodedWithXMLCoder">YES</bool> <object class="NSTableColumn" id="544095042"> - <double key="NSWidth">6.230000e+02</double> + <double key="NSWidth">6.220000e+02</double> <double key="NSMinWidth">4.000000e+01</double> <double key="NSMaxWidth">1.000000e+03</double> <object class="NSTableHeaderCell" key="NSHeaderCell"> @@ -2709,7 +2772,7 @@ <bool key="NSAllowsTypeSelect">YES</bool> </object> </object> - <string key="NSFrame">{{1, 17}, {626, 226}}</string> + <string key="NSFrame">{{1, 17}, {625, 226}}</string> <reference key="NSSuperview" ref="678281118"/> <reference key="NSNextKeyView" ref="581095761"/> <reference key="NSDocView" ref="581095761"/> @@ -2742,7 +2805,7 @@ <bool key="EncodedWithXMLCoder">YES</bool> <reference ref="1038415606"/> </object> - <string key="NSFrame">{{1, 0}, {626, 17}}</string> + <string key="NSFrame">{{1, 0}, {625, 17}}</string> <reference key="NSSuperview" ref="678281118"/> <reference key="NSNextKeyView" ref="1038415606"/> <reference key="NSDocView" ref="1038415606"/> @@ -2751,7 +2814,7 @@ </object> <reference ref="151074124"/> </object> - <string key="NSFrameSize">{628, 244}</string> + <string key="NSFrameSize">{627, 244}</string> <reference key="NSSuperview" ref="345834048"/> <reference key="NSNextKeyView" ref="90844306"/> <int key="NSsFlags">562</int> @@ -2765,13 +2828,13 @@ <object class="NSButton" id="811536132"> <reference key="NSNextResponder" ref="345834048"/> <int key="NSvFlags">265</int> - <string key="NSFrame">{{523, 245}, {90, 28}}</string> + <string key="NSFrame">{{522, 245}, {90, 28}}</string> <reference key="NSSuperview" ref="345834048"/> <bool key="NSEnabled">YES</bool> <object class="NSButtonCell" key="NSCell" id="177866566"> <int key="NSCellFlags">-2080244224</int> <int key="NSCellFlags2">134348800</int> - <string key="NSContents">Run Query</string> + <string key="NSContents">Run All</string> <reference key="NSSupport" ref="26"/> <reference key="NSControlView" ref="811536132"/> <int key="NSButtonFlags">-2034876161</int> @@ -2785,8 +2848,8 @@ </object> <object class="NSPopUpButton" id="86760255"> <reference key="NSNextResponder" ref="345834048"/> - <int key="NSvFlags">266</int> - <string key="NSFrame">{{264, 248}, {259, 22}}</string> + <int key="NSvFlags">268</int> + <string key="NSFrame">{{214, 248}, {186, 22}}</string> <reference key="NSSuperview" ref="345834048"/> <bool key="NSEnabled">YES</bool> <object class="NSPopUpButtonCell" key="NSCell" id="830957297"> @@ -2835,7 +2898,7 @@ <object class="NSPopUpButton" id="872178320"> <reference key="NSNextResponder" ref="345834048"/> <int key="NSvFlags">264</int> - <string key="NSFrame">{{17, 248}, {245, 22}}</string> + <string key="NSFrame">{{17, 248}, {195, 22}}</string> <reference key="NSSuperview" ref="345834048"/> <bool key="NSEnabled">YES</bool> <object class="NSPopUpButtonCell" key="NSCell" id="416049103"> @@ -2916,8 +2979,28 @@ <int key="NSArrowPosition">1</int> </object> </object> + <object class="NSButton" id="1009499029"> + <reference key="NSNextResponder" ref="345834048"/> + <int key="NSvFlags">265</int> + <string key="NSFrame">{{414, 245}, {110, 28}}</string> + <reference key="NSSuperview" ref="345834048"/> + <bool key="NSEnabled">YES</bool> + <object class="NSButtonCell" key="NSCell" id="588489390"> + <int key="NSCellFlags">604110336</int> + <int key="NSCellFlags2">134348800</int> + <string key="NSContents">Run Current</string> + <reference key="NSSupport" ref="26"/> + <reference key="NSControlView" ref="1009499029"/> + <int key="NSButtonFlags">-2038284033</int> + <int key="NSButtonFlags2">129</int> + <string key="NSAlternateContents"/> + <string key="NSKeyEquivalent"/> + <int key="NSPeriodicDelay">200</int> + <int key="NSPeriodicInterval">25</int> + </object> + </object> </object> - <string key="NSFrame">{{0, 165}, {628, 269}}</string> + <string key="NSFrame">{{0, 165}, {627, 269}}</string> <reference key="NSSuperview" ref="894339536"/> <string key="NSClassName">NSView</string> </object> @@ -2929,7 +3012,7 @@ <object class="NSTextField" id="910005271"> <reference key="NSNextResponder" ref="875002707"/> <int key="NSvFlags">266</int> - <string key="NSFrame">{{242, 67}, {369, 14}}</string> + <string key="NSFrame">{{242, 67}, {368, 14}}</string> <reference key="NSSuperview" ref="875002707"/> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="401235649"> @@ -2965,7 +3048,7 @@ <object class="NSTextField" id="547765795"> <reference key="NSNextResponder" ref="875002707"/> <int key="NSvFlags">274</int> - <string key="NSFrame">{{17, 20}, {594, 43}}</string> + <string key="NSFrame">{{17, 20}, {593, 43}}</string> <reference key="NSSuperview" ref="875002707"/> <bool key="NSEnabled">YES</bool> <object class="NSTextFieldCell" key="NSCell" id="948571736"> @@ -2979,16 +3062,16 @@ </object> </object> </object> - <string key="NSFrame">{{0, 443}, {628, 87}}</string> + <string key="NSFrame">{{0, 443}, {627, 87}}</string> <reference key="NSSuperview" ref="894339536"/> <string key="NSClassName">NSView</string> </object> </object> - <string key="NSFrame">{{6, 10}, {628, 530}}</string> + <string key="NSFrame">{{6, 10}, {627, 530}}</string> <reference key="NSSuperview" ref="746504912"/> </object> </object> - <string key="NSFrame">{{10, 7}, {638, 544}}</string> + <string key="NSFrame">{{10, 7}, {637, 544}}</string> </object> <string key="NSLabel">Custom Query</string> <reference key="NSColor" ref="62854682"/> @@ -3374,16 +3457,20 @@ </object> <string key="NSFrame">{{203, 0}, {660, 550}}</string> <reference key="NSSuperview" ref="937377983"/> + <reference key="NSWindow"/> <string key="NSClassName">NSView</string> </object> </object> <string key="NSFrameSize">{863, 550}</string> <reference key="NSSuperview" ref="579726586"/> + <reference key="NSWindow"/> <bool key="NSIsVertical">YES</bool> <string key="NSAutosaveName">DBViewSplitter</string> </object> </object> <string key="NSFrameSize">{863, 550}</string> + <reference key="NSSuperview"/> + <reference key="NSWindow"/> </object> <string key="NSScreenRect">{{0, 0}, {1440, 878}}</string> <string key="NSMinSize">{780, 502}</string> @@ -9614,14 +9701,6 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8</string> <int key="connectionID">214</int> </object> <object class="IBConnectionRecord"> - <object class="IBActionConnection" key="connection"> - <string key="label">performQuery:</string> - <reference key="source" ref="601471102"/> - <reference key="destination" ref="811536132"/> - </object> - <int key="connectionID">215</int> - </object> - <object class="IBConnectionRecord"> <object class="IBOutletConnection" key="connection"> <string key="label">textView</string> <reference key="source" ref="601471102"/> @@ -11729,6 +11808,38 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8</string> </object> <int key="connectionID">4822</int> </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">runSelectedQueries:</string> + <reference key="source" ref="601471102"/> + <reference key="destination" ref="1009499029"/> + </object> + <int key="connectionID">5125</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBOutletConnection" key="connection"> + <string key="label">runAllButton</string> + <reference key="source" ref="601471102"/> + <reference key="destination" ref="811536132"/> + </object> + <int key="connectionID">5126</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBOutletConnection" key="connection"> + <string key="label">runSelectionButton</string> + <reference key="source" ref="601471102"/> + <reference key="destination" ref="1009499029"/> + </object> + <int key="connectionID">5127</int> + </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">runAllQueries:</string> + <reference key="source" ref="601471102"/> + <reference key="destination" ref="811536132"/> + </object> + <int key="connectionID">5128</int> + </object> </object> <object class="IBMutableOrderedSet" key="objectRecords"> <object class="NSArray" key="orderedObjects"> @@ -16141,8 +16252,9 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8</string> <bool key="EncodedWithXMLCoder">YES</bool> <reference ref="678281118"/> <reference ref="872178320"/> - <reference ref="86760255"/> <reference ref="811536132"/> + <reference ref="86760255"/> + <reference ref="1009499029"/> </object> <reference key="parent" ref="894339536"/> </object> @@ -16608,6 +16720,20 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8</string> <reference key="object" ref="437382929"/> <reference key="parent" ref="808590596"/> </object> + <object class="IBObjectRecord"> + <int key="objectID">5123</int> + <reference key="object" ref="1009499029"/> + <object class="NSMutableArray" key="children"> + <bool key="EncodedWithXMLCoder">YES</bool> + <reference ref="588489390"/> + </object> + <reference key="parent" ref="345834048"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">5124</int> + <reference key="object" ref="588489390"/> + <reference key="parent" ref="1009499029"/> + </object> </object> </object> <object class="NSMutableDictionary" key="flattenedProperties"> @@ -17607,6 +17733,8 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8</string> <string>51.ImportedFromIB2</string> <string>512.IBPluginDependency</string> <string>512.ImportedFromIB2</string> + <string>5123.IBPluginDependency</string> + <string>5124.IBPluginDependency</string> <string>513.IBPluginDependency</string> <string>513.ImportedFromIB2</string> <string>514.IBPluginDependency</string> @@ -19026,8 +19154,8 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> - <string>{{318, 360}, {863, 550}}</string> - <string>{{318, 360}, {863, 550}}</string> + <string>{{55, 306}, {863, 550}}</string> + <string>{{55, 306}, {863, 550}}</string> <reference ref="9"/> <reference ref="9"/> <string>{{62, 352}, {845, 504}}</string> @@ -19051,6 +19179,8 @@ IGRvIHlvdSB3YW50IHRvIGFkZCBmb3IgdGhpcyBmaWVsZD8</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <reference ref="9"/> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <reference ref="9"/> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <reference ref="9"/> @@ -19401,7 +19531,7 @@ Y2hhbmdlIHRoZSBvcmRlcg</string> </object> </object> <nil key="sourceID"/> - <int key="maxID">5122</int> + <int key="maxID">5128</int> </object> <object class="IBClassDescriber" key="IBDocument.Classes"> <object class="NSMutableArray" key="referencedPartialClassDescriptions"> @@ -19475,8 +19605,9 @@ Y2hhbmdlIHRoZSBvcmRlcg</string> <string>closeQueryFavoritesSheet:</string> <string>closeSheet:</string> <string>copyQueryFavorite:</string> - <string>performQuery:</string> <string>removeQueryFavorite:</string> + <string>runAllQueries:</string> + <string>runSelectedQueries:</string> </object> <object class="NSMutableArray" key="dict.values"> <bool key="EncodedWithXMLCoder">YES</bool> @@ -19488,6 +19619,7 @@ Y2hhbmdlIHRoZSBvcmRlcg</string> <string>id</string> <string>id</string> <string>id</string> + <string>id</string> </object> </object> <object class="NSMutableDictionary" key="outlets"> @@ -19503,6 +19635,8 @@ Y2hhbmdlIHRoZSBvcmRlcg</string> <string>queryFavoritesView</string> <string>queryHistoryButton</string> <string>removeQueryFavoriteButton</string> + <string>runAllButton</string> + <string>runSelectionButton</string> <string>tableWindow</string> <string>textView</string> <string>valueSheet</string> @@ -19523,6 +19657,8 @@ Y2hhbmdlIHRoZSBvcmRlcg</string> <string>id</string> <string>id</string> <string>id</string> + <string>id</string> + <string>id</string> </object> </object> <object class="IBClassDescriptionSource" key="sourceIdentifier"> diff --git a/Source/CMMCPConnection.h b/Source/CMMCPConnection.h index e6479eb1..b26319ee 100644 --- a/Source/CMMCPConnection.h +++ b/Source/CMMCPConnection.h @@ -49,6 +49,7 @@ NSString *connectionHost; int connectionPort; NSString *connectionSocket; + float lastQueryExecutionTime; NSTimer *keepAliveTimer; NSDate *lastKeepAliveSuccess; @@ -66,6 +67,7 @@ - (void) setParentWindow:(NSWindow *)theWindow; - (BOOL) selectDB:(NSString *) dbName; - (CMMCPResult *) queryString:(NSString *) query; +- (float) lastQueryExecutionTime; - (MCPResult *) listDBsLike:(NSString *) dbsName; - (BOOL) checkConnection; - (void) setDelegate:(id)object; diff --git a/Source/CMMCPConnection.m b/Source/CMMCPConnection.m index 109ab280..64315ff6 100644 --- a/Source/CMMCPConnection.m +++ b/Source/CMMCPConnection.m @@ -68,6 +68,7 @@ static void forcePingTimeout(int signalNumber); connectionSocket = nil; keepAliveTimer = nil; lastKeepAliveSuccess = nil; + lastQueryExecutionTime = 0; if (![NSBundle loadNibNamed:@"ConnectionErrorDialog" owner:self]) { NSLog(@"Connection error dialog could not be loaded; connection failure handling will not function correctly."); } @@ -345,6 +346,7 @@ static void forcePingTimeout(int signalNumber); CMMCPResult *theResult; const char *theCQuery = [self cStringFromString:query]; int theQueryCode; + NSDate *queryStartDate; // If no connection is present, return nil. if (!mConnected) return nil; @@ -360,10 +362,16 @@ static void forcePingTimeout(int signalNumber); [delegate willQueryString:query]; } - if (0 == (theQueryCode = mysql_query(mConnection, theCQuery))) { + // Run the query, storing run time (note this will include some network and overhead) + queryStartDate = [NSDate date]; + theQueryCode = mysql_query(mConnection, theCQuery); + lastQueryExecutionTime = [[NSDate date] timeIntervalSinceDate:queryStartDate]; + + // Retrieve the result or error appropriately. + if (0 == theQueryCode) { if (mysql_field_count(mConnection) != 0) { - // Use CMMCPResult instad of MCPResult + // Use CMMCPResult instead of MCPResult theResult = [[CMMCPResult alloc] initWithMySQLPtr:mConnection encoding:mEncoding timeZone:mTimeZone]; } else { return nil; @@ -385,6 +393,16 @@ static void forcePingTimeout(int signalNumber); /* + * Return the time taken to execute the last query. This should be close to the time it took + * the server to run the query, but will include network lag and some client library overhead. + */ +- (float) lastQueryExecutionTime +{ + return lastQueryExecutionTime; +} + + +/* * Modified version of selectDB to be used in Sequel Pro. * Checks the connection exists, and handles keepalive, otherwise calling the parent implementation. */ diff --git a/Source/CustomQuery.h b/Source/CustomQuery.h index 8e81c7e5..c3a61b56 100644 --- a/Source/CustomQuery.h +++ b/Source/CustomQuery.h @@ -43,6 +43,8 @@ IBOutlet id queryFavoritesView; IBOutlet id removeQueryFavoriteButton; IBOutlet id copyQueryFavoriteButton; + IBOutlet id runSelectionButton; + IBOutlet id runAllButton; NSArray *queryResult; NSUserDefaults *prefs; @@ -52,7 +54,8 @@ } // IBAction methods -- (IBAction)performQuery:(id)sender; +- (IBAction)runAllQueries:(id)sender; +- (IBAction)runSelectedQueries:(id)sender; - (IBAction)chooseQueryFavorite:(id)sender; - (IBAction)chooseQueryHistory:(id)sender; - (IBAction)closeSheet:(id)sender; @@ -63,6 +66,10 @@ - (IBAction)copyQueryFavorite:(id)sender; - (IBAction)closeQueryFavoritesSheet:(id)sender; +// Query actions +- (void)performQueries:(NSArray *)queries; +- (NSString *)queryAtPosition:(long)position; + // Accessors - (NSArray *)currentResult; diff --git a/Source/CustomQuery.m b/Source/CustomQuery.m index e917e984..94ec6cba 100644 --- a/Source/CustomQuery.m +++ b/Source/CustomQuery.m @@ -25,177 +25,77 @@ #import "CustomQuery.h" #import "SPSQLParser.h" #import "SPGrowlController.h" +#import "SPStringAdditions.h" + @implementation CustomQuery -//IBAction methods -- (IBAction)performQuery:(id)sender; + + +#pragma mark IBAction methods + + /* -performs the mysql-query given by the user -sets the tableView columns corresponding to the mysql-result -*/ -{ + * Split all the queries in the text view, split them into individual queries, + * and run sequentially. + */ +- (IBAction)runAllQueries:(id)sender +{ + SPSQLParser *queryParser; + NSArray *queries; + // Fixes bug in key equivalents. - if ([[NSApp currentEvent] type] == NSKeyUp) - { + if ([[NSApp currentEvent] type] == NSKeyUp) { return; } - - NSArray *theColumns; - NSTableColumn *theCol; - CMMCPResult *theResult = nil; - NSArray *queries; - NSMutableArray *menuItems = [NSMutableArray array]; - NSMutableArray *tempResult = [NSMutableArray array]; - NSMutableString *errors = [NSMutableString string]; - SPSQLParser *queryParser; - int i; - - // Notify listeners that a query has started - [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:self]; // Retrieve the custom query string and split it into separate SQL queries queryParser = [[SPSQLParser alloc] initWithString:[textView string]]; queries = [queryParser splitStringByCharacter:';']; [queryParser release]; - // Perform the queries in series - for ( i = 0 ; i < [queries count] ; i++ ) { - theResult = [mySQLConnection queryString:[queries objectAtIndex:i]]; - if ( ![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { - - // If the query errored, append error to the error log for display at the end - if ( [queries count] > 1 ) { - [errors appendString:[NSString stringWithFormat:NSLocalizedString(@"[ERROR in query %d] %@\n", @"error text when multiple custom query failed"), - i+1, - [mySQLConnection getLastErrorMessage]]]; - } else { - [errors setString:[mySQLConnection getLastErrorMessage]]; - } - } - } - - //perform empty query if no query is given - if ( [queries count] == 0 ) { - theResult = [mySQLConnection queryString:@""]; - [errors setString:[mySQLConnection getLastErrorMessage]]; - } - -//put result in array - [queryResult release]; - queryResult = nil; - if ( nil != theResult ) - { - int r = [theResult numOfRows]; - if (r) [theResult dataSeek:0]; - for ( i = 0 ; i < r ; i++ ) { - [tempResult addObject:[theResult fetchRowAsArray]]; - } - queryResult = [[NSArray arrayWithArray:tempResult] retain]; - } - -//add query to history - [queryHistoryButton insertItemWithTitle:[textView string] atIndex:1]; - while ( [queryHistoryButton numberOfItems] > 21 ) { - [queryHistoryButton removeItemAtIndex:[queryHistoryButton numberOfItems]-1]; - } - for ( i = 1 ; i < [queryHistoryButton numberOfItems] ; i++ ) - { - [menuItems addObject:[queryHistoryButton itemTitleAtIndex:i]]; - } - [prefs setObject:menuItems forKey:@"queryHistory"]; + [self performQueries:queries]; -//select the text of the query textView and set standard font + // Select the text of the query textView for re-editing and set standard font [textView selectAll:self]; - if ( [errors length] ) { - [errorText setStringValue:errors]; - } else { - [errorText setStringValue:NSLocalizedString(@"There were no errors.", @"text shown when query was successfull")]; - } - if ( [mySQLConnection affectedRows] != -1 ) { - [affectedRowsText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"%@ row(s) affected", @"text showing how many rows have been affected"), - [[NSNumber numberWithLongLong:[mySQLConnection affectedRows]] stringValue]]]; - } else { - [affectedRowsText setStringValue:@""]; - } if ( [prefs boolForKey:@"useMonospacedFonts"] ) { [textView setFont:[NSFont fontWithName:@"Monaco" size:[NSFont smallSystemFontSize]]]; } else { [textView setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; } +} - if ( !theResult || ![theResult numOfRows] ) { -//no rows in result - //free tableView - theColumns = [customQueryView tableColumns]; - while ([theColumns count]) { - [customQueryView removeTableColumn:[theColumns objectAtIndex:0]]; - } -// theCol = [[NSTableColumn alloc] initWithIdentifier:@""]; -// [[theCol headerCell] setStringValue:@""]; -// [customQueryView addTableColumn:theCol]; -// [customQueryView sizeLastColumnToFit]; - [customQueryView reloadData]; -// [theCol release]; - - //query finished - [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:self]; - - // Query finished Growl notification - [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Query Finished" - description:[NSString stringWithFormat:NSLocalizedString(@"%@",@"description for query finished growl notification"), [errorText stringValue]] - notificationName:@"Query Finished"]; - - return; - } - -//set columns -//remove all columns - theColumns = [customQueryView tableColumns]; -// i=0; - while ([theColumns count]) { - [customQueryView removeTableColumn:[theColumns objectAtIndex:0]]; -// i++; - } +/* + * Depending on selection, run either the query containing the selection caret (if the caret is + * at a single point within the text view), or run the selected text (if a text range is selected). + */ +- (IBAction)runSelectedQueries:(id)sender +{ + NSArray *queries; + NSString *query; + NSRange selectedRange = [textView selectedRange]; + SPSQLParser *queryParser; -//add columns, corresponding to the query result - theColumns = [theResult fetchFieldNames]; - for ( i = 0 ; i < [theResult numOfFields] ; i++) { - theCol = [[NSTableColumn alloc] initWithIdentifier:[NSNumber numberWithInt:i]]; - [theCol setResizingMask:NSTableColumnUserResizingMask]; - NSTextFieldCell *dataCell = [[[NSTextFieldCell alloc] initTextCell:@""] autorelease]; - [dataCell setEditable:NO]; - if ( [prefs boolForKey:@"useMonospacedFonts"] ) { - [dataCell setFont:[NSFont fontWithName:@"Monaco" size:10]]; - } else { - [dataCell setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; + // If the current selection is a single caret position, run the current query. + if (selectedRange.length == 0) { + query = [self queryAtPosition:selectedRange.location]; + if (!query) { + NSBeep(); + return; } - [dataCell setLineBreakMode:NSLineBreakByTruncatingTail]; - [theCol setDataCell:dataCell]; - [[theCol headerCell] setStringValue:[theColumns objectAtIndex:i]]; + queries = [NSArray arrayWithObject:query]; - [customQueryView addTableColumn:theCol]; - [theCol release]; + // Otherwise, run the selected text. + } else { + queryParser = [[SPSQLParser alloc] initWithString:[[textView string] substringWithRange:selectedRange]]; + queries = [queryParser splitStringByCharacter:';']; + [queryParser release]; } - [customQueryView sizeLastColumnToFit]; - //tries to fix problem with last row (otherwise to small) - //sets last column to width of the first if smaller than 30 - //problem not fixed for resizing window - if ( [[customQueryView tableColumnWithIdentifier:[NSNumber numberWithInt:[theColumns count]-1]] width] < 30 ) - [[customQueryView tableColumnWithIdentifier:[NSNumber numberWithInt:[theColumns count]-1]] - setWidth:[[customQueryView tableColumnWithIdentifier:[NSNumber numberWithInt:0]] width]]; - [customQueryView reloadData]; - - //query finished - [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:self]; - - // Query finished Growl notification - [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Query Finished" - description:[NSString stringWithFormat:NSLocalizedString(@"%@",@"description for query finished growl notification"), [errorText stringValue]] - notificationName:@"Query Finished"]; + [self performQueries:queries]; } + - (IBAction)chooseQueryFavorite:(id)sender /* insert the choosen favorite query in the query textView or save query to favorites or opens window to edit favorites @@ -255,7 +155,10 @@ closes the sheet } -//queryFavoritesSheet methods +#pragma mark - +#pragma mark queryFavoritesSheet methods + + - (IBAction)addQueryFavorite:(id)sender /* adds a query favorite @@ -352,7 +255,229 @@ closes queryFavoritesSheet and saves favorites to preferences } -//getter methods +#pragma mark - +#pragma mark Query actions + + +- (void)performQueries:(NSArray *)queries; +/* +performs the mysql-query given by the user +sets the tableView columns corresponding to the mysql-result +*/ +{ + + NSArray *theColumns; + NSTableColumn *theCol; + CMMCPResult *theResult = nil; + NSMutableArray *menuItems = [NSMutableArray array]; + NSMutableArray *tempResult = [NSMutableArray array]; + NSMutableString *errors = [NSMutableString string]; + int i, totalQueriesRun = 0, totalAffectedRows = 0; + float executionTime = 0; + + // Notify listeners that a query has started + [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:self]; + + // Perform the supplied queries in series + for ( i = 0 ; i < [queries count] ; i++ ) { + + // Don't run blank queries, or queries which only contain whitespace. + if ([[[queries objectAtIndex:i] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0) + continue; + + // Run the query, timing execution (note this also includes network and overhead) + theResult = [mySQLConnection queryString:[queries objectAtIndex:i]]; + executionTime += [mySQLConnection lastQueryExecutionTime]; + totalQueriesRun++; + + // Record any affected rows + if ( [mySQLConnection affectedRows] != -1 ) + totalAffectedRows += [mySQLConnection affectedRows]; + + // Store any error messages + if ( ![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { + + // If the query errored, append error to the error log for display at the end + if ( [queries count] > 1 ) { + [errors appendString:[NSString stringWithFormat:NSLocalizedString(@"[ERROR in query %d] %@\n", @"error text when multiple custom query failed"), + i+1, + [mySQLConnection getLastErrorMessage]]]; + } else { + [errors setString:[mySQLConnection getLastErrorMessage]]; + } + } + } + + //perform empty query if no query is given + if ( [queries count] == 0 ) { + theResult = [mySQLConnection queryString:@""]; + [errors setString:[mySQLConnection getLastErrorMessage]]; + } + +//put result in array + [queryResult release]; + queryResult = nil; + if ( nil != theResult ) + { + int r = [theResult numOfRows]; + if (r) [theResult dataSeek:0]; + for ( i = 0 ; i < r ; i++ ) { + [tempResult addObject:[theResult fetchRowAsArray]]; + } + queryResult = [[NSArray arrayWithArray:tempResult] retain]; + } + +//add query to history + [queryHistoryButton insertItemWithTitle:[queries componentsJoinedByString:@"; "] atIndex:1]; + while ( [queryHistoryButton numberOfItems] > 21 ) { + [queryHistoryButton removeItemAtIndex:[queryHistoryButton numberOfItems]-1]; + } + for ( i = 1 ; i < [queryHistoryButton numberOfItems] ; i++ ) + { + [menuItems addObject:[queryHistoryButton itemTitleAtIndex:i]]; + } + [prefs setObject:menuItems forKey:@"queryHistory"]; + + if ( [errors length] ) { + [errorText setStringValue:errors]; + } else { + [errorText setStringValue:NSLocalizedString(@"There were no errors.", @"text shown when query was successfull")]; + } + + // Set up the status string + if ( totalQueriesRun > 1 ) { + [affectedRowsText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"%i total row(s) affected, by %i queries taking %@", @"text showing how many rows have been affected by multiple queries"), + totalAffectedRows, + totalQueriesRun, + [NSString stringForTimeInterval:executionTime] + ]]; + } else { + [affectedRowsText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"%i row(s) affected, taking %@", @"text showing how many rows have been affected by a single query"), + totalAffectedRows, + [NSString stringForTimeInterval:executionTime] + ]]; + } + + if ( !theResult || ![theResult numOfRows] ) { +//no rows in result + //free tableView + theColumns = [customQueryView tableColumns]; + while ([theColumns count]) { + [customQueryView removeTableColumn:[theColumns objectAtIndex:0]]; + } +// theCol = [[NSTableColumn alloc] initWithIdentifier:@""]; +// [[theCol headerCell] setStringValue:@""]; +// [customQueryView addTableColumn:theCol]; +// [customQueryView sizeLastColumnToFit]; + [customQueryView reloadData]; +// [theCol release]; + + //query finished + [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:self]; + + // Query finished Growl notification + [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Query Finished" + description:[NSString stringWithFormat:NSLocalizedString(@"%@",@"description for query finished growl notification"), [errorText stringValue]] + notificationName:@"Query Finished"]; + + return; + } + +//set columns +//remove all columns + theColumns = [customQueryView tableColumns]; +// i=0; + while ([theColumns count]) { + [customQueryView removeTableColumn:[theColumns objectAtIndex:0]]; +// i++; + } + +//add columns, corresponding to the query result + theColumns = [theResult fetchFieldNames]; + for ( i = 0 ; i < [theResult numOfFields] ; i++) { + theCol = [[NSTableColumn alloc] initWithIdentifier:[NSNumber numberWithInt:i]]; + [theCol setResizingMask:NSTableColumnUserResizingMask]; + NSTextFieldCell *dataCell = [[[NSTextFieldCell alloc] initTextCell:@""] autorelease]; + [dataCell setEditable:NO]; + if ( [prefs boolForKey:@"useMonospacedFonts"] ) { + [dataCell setFont:[NSFont fontWithName:@"Monaco" size:10]]; + } else { + [dataCell setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; + } + [dataCell setLineBreakMode:NSLineBreakByTruncatingTail]; + [theCol setDataCell:dataCell]; + [[theCol headerCell] setStringValue:[theColumns objectAtIndex:i]]; + + [customQueryView addTableColumn:theCol]; + [theCol release]; + } + + [customQueryView sizeLastColumnToFit]; + //tries to fix problem with last row (otherwise to small) + //sets last column to width of the first if smaller than 30 + //problem not fixed for resizing window + if ( [[customQueryView tableColumnWithIdentifier:[NSNumber numberWithInt:[theColumns count]-1]] width] < 30 ) + [[customQueryView tableColumnWithIdentifier:[NSNumber numberWithInt:[theColumns count]-1]] + setWidth:[[customQueryView tableColumnWithIdentifier:[NSNumber numberWithInt:0]] width]]; + [customQueryView reloadData]; + + //query finished + [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:self]; + + // Query finished Growl notification + [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Query Finished" + description:[NSString stringWithFormat:NSLocalizedString(@"%@",@"description for query finished growl notification"), [errorText stringValue]] + notificationName:@"Query Finished"]; +} + +/* + * Retrieve the query at a position specified within the custom query + * text view. This will return nil if the position specified is beyond + * the available string or if an empty query would be returned. + */ +- (NSString *)queryAtPosition:(long)position +{ + SPSQLParser *customQueryParser; + NSArray *queries; + NSString *query = nil; + int i, queryPosition = 0; + + // If the supplied position is negative or beyond the end of the string, return nil. + if (position < 0 || position > [[textView string] length]) + return nil; + + // Split the current text into queries + customQueryParser = [[SPSQLParser alloc] initWithString:[textView string]]; + queries = [[NSArray alloc] initWithArray:[customQueryParser splitStringByCharacter:';']]; + [customQueryParser release]; + + // Walk along the array of queries to identify the current query - taking into account + // the extra semicolon at the end of each query + for (i = 0; i < [queries count]; i++ ) { + queryPosition += [[queries objectAtIndex:i] length]; + if (queryPosition >= position) { + query = [NSString stringWithString:[queries objectAtIndex:i]]; + break; + } + queryPosition++; + } + + [queries release]; + + // Ensure the string isn't empty. + // (We could also strip comments for this check, but that prevents use of conditional comments) + if ([[query stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0) + return nil; + + // Return the located string. + return query; +} + + +#pragma mark - +#pragma mark Accessors + + - (NSArray *)currentResult /* returns the current result (as shown in custom result view) as array, the first object containing the field names as array, the following objects containing the rows as array @@ -384,7 +509,10 @@ returns the current result (as shown in custom result view) as array, the first } -//additional methods +#pragma mark - +#pragma mark Additional methods + + - (void)setConnection:(CMMCPConnection *)theConnection /* sets the connection (received from TableDocument) and makes things that have to be done only once @@ -447,11 +575,14 @@ inserts the query in the textView and performs query */ { [textView setString:query]; - [self performQuery:self]; + [self runAllQueries:self]; } -//tableView datasource methods +#pragma mark - +#pragma mark TableView datasource methods + + - (int)numberOfRowsInTableView:(NSTableView *)aTableView { if ( aTableView == customQueryView ) { @@ -668,7 +799,10 @@ opens sheet with value when double clicking on a field } -//splitView delegate methods +#pragma mark - +#pragma mark SplitView delegate methods + + - (BOOL)splitView:(NSSplitView *)sender canCollapseSubview:(NSView *)subview /* tells the splitView that it can collapse views @@ -702,7 +836,10 @@ defines min position of splitView } -//textView delegate methods +#pragma mark - +#pragma mark TextView delegate methods + + - (BOOL)textView:(NSTextView *)aTextView doCommandBySelector:(SEL)aSelector /* traps enter key and @@ -714,7 +851,7 @@ traps enter key and if ( [aTextView methodForSelector:aSelector] == [aTextView methodForSelector:@selector(insertNewline:)] && [[[NSApp currentEvent] characters] isEqualToString:@"\003"] ) { - [self performQuery:self]; + [self runAllQueries:self]; return YES; } else { return NO; @@ -732,6 +869,68 @@ traps enter key and } /* + * A notification posted when the selection changes within the text view; + * used to control the run-currentrun-selection button state and action. + */ +- (void)textViewDidChangeSelection:(NSNotification *)aNotification +{ + + // Ensure that the notification is from the custom query text view + if ( [aNotification object] != textView ) return; + + // If no text is selected, disable the button. + if ( [textView selectedRange].location == NSNotFound ) { + [runSelectionButton setEnabled:NO]; + return; + } + + // If the current selection is a single caret position, update the button based on + // whether the caret is inside a valid query. + if ([textView selectedRange].length == 0) { + int selectionPosition = [textView selectedRange].location; + int movedRangeStart, movedRangeLength; + NSRange oldSelection; + + // Retrieve the old selection position + [[[aNotification userInfo] objectForKey:@"NSOldSelectedCharacterRange"] getValue:&oldSelection]; + + // Only process the query text if the selection previously had length, or moved more than 100 characters, + // or the intervening space contained a semicolon, or typing has been performed with no current query. + // This adds more checks to every keypress, but ensures the majority of the actions don't incur a + // parsing overhead - which is cheap on small text strings but heavy of large queries. + movedRangeStart = (selectionPosition < oldSelection.location)?selectionPosition:oldSelection.location; + movedRangeLength = abs(selectionPosition - oldSelection.location); + if (oldSelection.length > 0 + || movedRangeLength > 100 + || oldSelection.location > [[textView string] length] + || [[textView string] rangeOfString:@";" options:0 range:NSMakeRange(movedRangeStart, movedRangeLength)].location != NSNotFound + || (![runSelectionButton isEnabled] && selectionPosition > oldSelection.location + && [[[[textView string] substringWithRange:NSMakeRange(movedRangeStart, movedRangeLength)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length]) + ) { + + [runSelectionButton setTitle:NSLocalizedString(@"Run Current", @"Title of button to run current query in custom query view")]; + + // If a valid query is present at the cursor position, enable the button + if ([self queryAtPosition:selectionPosition]) { + [runSelectionButton setEnabled:YES]; + } else { + [runSelectionButton setEnabled:NO]; + } + } + + // For selection ranges, enable the button. + } else { + [runSelectionButton setTitle:NSLocalizedString(@"Run Selection", @"Title of button to run selected text in custom query view")]; + [runSelectionButton setEnabled:YES]; + } +} + + +#pragma mark - +#pragma mark TableView notifications + + +/* * Updates various interface elements based on the current table view selection. */ - (void)tableViewSelectionDidChange:(NSNotification *)notification @@ -744,6 +943,10 @@ traps enter key and } } + +#pragma mark - + + // Last but not least - (id)init; { diff --git a/Source/SPSQLParser.h b/Source/SPSQLParser.h index 14ac6f9d..3e68501c 100644 --- a/Source/SPSQLParser.h +++ b/Source/SPSQLParser.h @@ -23,6 +23,12 @@ #import <Cocoa/Cocoa.h> +/* + * Define the length of the character cache to use when parsing instead of accessing + * via characterAtIndex:. There is a balance here between updating the cache very + * often and access penalties; 1500 appears a reasonable compromise. + */ +#define CHARACTER_CACHE_LENGTH 1500 /* * This class provides a string class intended for SQL parsing. It extends NSMutableString, @@ -53,6 +59,9 @@ @interface SPSQLParser : NSMutableString { id string; + unichar *stringCharCache; + long charCacheStart; + long charCacheEnd; } @@ -210,6 +219,15 @@ typedef enum _SPCommentTypes { - (long) endIndexOfStringQuotedByCharacter:(unichar)quoteCharacter startingAtIndex:(long)index; - (long) endIndexOfCommentOfType:(SPCommentType)commentType startingAtIndex:(long)index; +/* + * Cacheing methods to enable a faster alternative to characterAtIndex: when walking strings, and overrides to update. + */ +- (unichar) charAtIndex:(long)index; +- (void) clearCharCache; +- (void) deleteCharactersInRange:(NSRange)aRange; +- (void) insertString:(NSString *)aString atIndex:(NSUInteger)anIndex; + + /* Required and primitive methods to allow subclassing class cluster */ #pragma mark - diff --git a/Source/SPSQLParser.m b/Source/SPSQLParser.m index 9828f529..e5c490da 100644 --- a/Source/SPSQLParser.m +++ b/Source/SPSQLParser.m @@ -60,11 +60,11 @@ case '-': if (stringLength < currentStringIndex + 2) break; if ([string characterAtIndex:currentStringIndex+1] != '-') break; - if (![[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:[string characterAtIndex:currentStringIndex+2]]) break; + if (![[NSCharacterSet whitespaceCharacterSet] characterIsMember:[string characterAtIndex:currentStringIndex+2]]) break; commentEndIndex = [self endIndexOfCommentOfType:SPDoubleDashComment startingAtIndex:currentStringIndex]; // Remove the comment - [string deleteCharactersInRange:NSMakeRange(currentStringIndex, commentEndIndex - currentStringIndex + 1)]; + [self deleteCharactersInRange:NSMakeRange(currentStringIndex, commentEndIndex - currentStringIndex + 1)]; stringLength -= commentEndIndex - currentStringIndex + 1; currentStringIndex--; break; @@ -73,7 +73,7 @@ commentEndIndex = [self endIndexOfCommentOfType:SPHashComment startingAtIndex:currentStringIndex]; // Remove the comment - [string deleteCharactersInRange:NSMakeRange(currentStringIndex, commentEndIndex - currentStringIndex + 1)]; + [self deleteCharactersInRange:NSMakeRange(currentStringIndex, commentEndIndex - currentStringIndex + 1)]; stringLength -= commentEndIndex - currentStringIndex + 1; currentStringIndex--; break; @@ -85,7 +85,7 @@ commentEndIndex = [self endIndexOfCommentOfType:SPCStyleComment startingAtIndex:currentStringIndex]; // Remove the comment - [string deleteCharactersInRange:NSMakeRange(currentStringIndex, commentEndIndex - currentStringIndex + 1)]; + [self deleteCharactersInRange:NSMakeRange(currentStringIndex, commentEndIndex - currentStringIndex + 1)]; stringLength -= commentEndIndex - currentStringIndex + 1; currentStringIndex--; break; @@ -158,7 +158,7 @@ if (stringIndex == NSNotFound) return NO; // If it has been found, trim the string appropriately and return YES - [string deleteCharactersInRange:NSMakeRange(0, stringIndex + (inclusive?1:0))]; + [self deleteCharactersInRange:NSMakeRange(0, stringIndex + (inclusive?1:0))]; return YES; } @@ -213,7 +213,7 @@ // Select the appropriate string range, truncate the current string, and return the selected string resultString = [NSString stringWithString:[string substringWithRange:NSMakeRange(0, stringIndex + (inclusiveReturn?1:0))]]; - [string deleteCharactersInRange:NSMakeRange(0, stringIndex + (inclusiveTrim?1:0))]; + [self deleteCharactersInRange:NSMakeRange(0, stringIndex + (inclusiveTrim?1:0))]; return resultString; } @@ -255,7 +255,7 @@ - (NSString *) stringFromCharacter:(unichar)fromCharacter toCharacter:(unichar)toCharacter inclusively:(BOOL)inclusive skippingBrackets:(BOOL)skipBrackets ignoringQuotedStrings:(BOOL)ignoreQuotedStrings { long fromCharacterIndex, toCharacterIndex; - + // Look for the first occurrence of the from: character fromCharacterIndex = [self firstOccurrenceOfCharacter:fromCharacter afterIndex:-1 skippingBrackets:skipBrackets ignoringQuotedStrings:ignoreQuotedStrings]; if (fromCharacterIndex == NSNotFound) return nil; @@ -318,7 +318,7 @@ // Select the correct part of the string, truncate the current string, and return the selected string. resultString = [string substringWithRange:NSMakeRange(fromCharacterIndex + (inclusiveReturn?0:1), toCharacterIndex + (inclusiveReturn?1:-1) - fromCharacterIndex)]; - [string deleteCharactersInRange:NSMakeRange(fromCharacterIndex + (inclusiveTrim?0:1), toCharacterIndex + (inclusiveTrim?1:-1) - fromCharacterIndex)]; + [self deleteCharactersInRange:NSMakeRange(fromCharacterIndex + (inclusiveTrim?0:1), toCharacterIndex + (inclusiveTrim?1:-1) - fromCharacterIndex)]; return resultString; } @@ -358,16 +358,14 @@ NSMutableArray *resultsArray = [NSMutableArray array]; long stringIndex = -1, nextIndex = 0; - // Walk through the string finding the character to split by, and add non-zero length strings. + // Walk through the string finding the character to split by, and add all strings to the array. while (1) { nextIndex = [self firstOccurrenceOfCharacter:character afterIndex:stringIndex skippingBrackets:skipBrackets ignoringQuotedStrings:ignoreQuotedStrings]; if (nextIndex == NSNotFound) { break; } - if (nextIndex - stringIndex - 1 > 0) { - [resultsArray addObject:[string substringWithRange:NSMakeRange(stringIndex+1, nextIndex - stringIndex - 1)]]; - } + [resultsArray addObject:[string substringWithRange:NSMakeRange(stringIndex+1, nextIndex - stringIndex - 1)]]; stringIndex = nextIndex; } @@ -408,12 +406,16 @@ long stringLength = [string length]; int bracketingLevel = 0; + // Cache frequently used selectors, avoiding dynamic binding overhead + IMP charAtIndex = [self methodForSelector:@selector(charAtIndex:)]; + IMP endIndex = [self methodForSelector:@selector(endIndexOfStringQuotedByCharacter:startingAtIndex:)]; + // Sanity check inputs if (startIndex < -1) startIndex = -1; // Walk along the string, processing characters for (currentStringIndex = startIndex + 1; currentStringIndex < stringLength; currentStringIndex++) { - currentCharacter = [string characterAtIndex:currentStringIndex]; + currentCharacter = (unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex); // Check for the ending character, and if it has been found and quoting/brackets is valid, return. if (currentCharacter == character) { @@ -430,7 +432,7 @@ case '"': case '`': if (!ignoreQuotedStrings) break; - quotedStringEndIndex = [self endIndexOfStringQuotedByCharacter:currentCharacter startingAtIndex:currentStringIndex+1]; + quotedStringEndIndex = (long)(*endIndex)(self, @selector(endIndexOfStringQuotedByCharacter:startingAtIndex:), currentCharacter, currentStringIndex+1); if (quotedStringEndIndex == NSNotFound) { return NSNotFound; } @@ -449,8 +451,8 @@ // For comments starting "--[\s]", ensure the start syntax is valid before proceeding. case '-': if (stringLength < currentStringIndex + 2) break; - if ([string characterAtIndex:currentStringIndex+1] != '-') break; - if (![[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:[string characterAtIndex:currentStringIndex+2]]) break; + if ((unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex+1) != '-') break; + if (![[NSCharacterSet whitespaceCharacterSet] characterIsMember:(unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex+2)]) break; currentStringIndex = [self endIndexOfCommentOfType:SPDoubleDashComment startingAtIndex:currentStringIndex]; break; @@ -461,7 +463,7 @@ // For comments starting "/*", ensure the start syntax is valid before proceeding. case '/': if (stringLength < currentStringIndex + 1) break; - if ([string characterAtIndex:currentStringIndex+1] != '*') break; + if ((unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex+1) != '*') break; currentStringIndex = [self endIndexOfCommentOfType:SPCStyleComment startingAtIndex:currentStringIndex]; break; } @@ -480,17 +482,20 @@ BOOL characterIsEscaped; unichar currentCharacter; + // Cache the charAtIndex selector, avoiding dynamic binding overhead + IMP charAtIndex = [self methodForSelector:@selector(charAtIndex:)]; + stringLength = [string length]; // Walk the string looking for the string end for ( currentStringIndex = index; currentStringIndex < stringLength; currentStringIndex++) { - currentCharacter = [string characterAtIndex:currentStringIndex]; + currentCharacter = (unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex); // If the string end is a backtick and one has been encountered, treat it as end of string if (quoteCharacter == '`' && currentCharacter == '`') { // ...as long as the next character isn't also a backtick, in which case it's being quoted. Skip both. - if ((currentStringIndex + 1) < stringLength && [string characterAtIndex:currentStringIndex+1] == '`') { + if ((currentStringIndex + 1) < stringLength && (unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex+1) == '`') { currentStringIndex++; continue; } @@ -504,7 +509,7 @@ characterIsEscaped = NO; i = 1; quotedStringLength = currentStringIndex - 1; - while ((quotedStringLength - i) > 0 && [string characterAtIndex:currentStringIndex - i] == '\\') { + while ((quotedStringLength - i) > 0 && (unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex - i) == '\\') { characterIsEscaped = !characterIsEscaped; i++; } @@ -512,7 +517,7 @@ // If an even number have been found, it may be the end of the string - as long as the subsequent character // isn't also the same character, in which case it's another form of escaping. if (!characterIsEscaped) { - if ((currentStringIndex + 1) < stringLength && [string characterAtIndex:currentStringIndex+1] == quoteCharacter) { + if ((currentStringIndex + 1) < stringLength && (unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), currentStringIndex+1) == quoteCharacter) { currentStringIndex++; continue; } @@ -534,6 +539,9 @@ long stringLength = [string length]; unichar currentCharacter; + // Cache the charAtIndex selector, avoiding dynamic binding overhead + IMP charAtIndex = [self methodForSelector:@selector(charAtIndex:)]; + switch (commentType) { // For comments of type "--[\s]", start the comment processing two characters in to match the start syntax, @@ -545,7 +553,7 @@ case SPHashComment: index++; for ( ; index < stringLength; index++ ) { - currentCharacter = [string characterAtIndex:index]; + currentCharacter = (unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), index); if (currentCharacter == '\r' || currentCharacter == '\n') { return index-1; } @@ -557,8 +565,8 @@ case SPCStyleComment: index = index+2; for ( ; index < stringLength; index++ ) { - if ([string characterAtIndex:index] == '*') { - if ((stringLength > index + 1) && [string characterAtIndex:index+1] == '/') { + if ((unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), index) == '*') { + if ((stringLength > index + 1) && (unichar)(long)(*charAtIndex)(self, @selector(charAtIndex:), index+1) == '/') { return (index+1); } } @@ -569,6 +577,52 @@ return (stringLength-1); } +/* + * Provide a method to retrieve a character from the local cache. + * Does no bounds checking on the underlying string, and so is kept + * separate for characterAtIndex:. + */ +- (unichar) charAtIndex:(long)index +{ + + // If the current cache doesn't include the current character, update it. + if (index > charCacheEnd || index < charCacheStart) { + if (charCacheEnd > -1) { + free(stringCharCache); + } + unsigned int remainingStringLength = [string length] - index; + unsigned int newcachelength = (CHARACTER_CACHE_LENGTH < remainingStringLength)?CHARACTER_CACHE_LENGTH:remainingStringLength; + stringCharCache = (unichar *)calloc(newcachelength, sizeof(unichar)); + [string getCharacters:stringCharCache range:NSMakeRange(index, newcachelength)]; + charCacheEnd = index + newcachelength - 1; + charCacheStart = index; + } + return stringCharCache[index - charCacheStart]; +} + +/* + * Provide a method to cleat the cache, and use it when updating the string. + */ +- (void) clearCharCache +{ + if (charCacheEnd > -1) { + free(stringCharCache); + } + charCacheEnd = -1; + charCacheStart = 0; +} +- (void) deleteCharactersInRange:(NSRange)aRange +{ + [super deleteCharactersInRange:aRange]; + [self clearCharCache]; +} +- (void) insertString:(NSString *)aString atIndex:(NSUInteger)anIndex +{ + [super insertString:aString atIndex:anIndex]; + [self clearCharCache]; +} + + /* Required and primitive methods to allow subclassing class cluster */ #pragma mark - @@ -576,45 +630,53 @@ if (self = [super init]) { string = [[NSMutableString string] retain]; } + charCacheEnd = -1; return self; } - (id) initWithBytes:(const void *)bytes length:(unsigned int)length encoding:(NSStringEncoding)encoding { if (self = [super init]) { string = [[NSMutableString alloc] initWithBytes:bytes length:length encoding:encoding]; } + charCacheEnd = -1; return self; } - (id) initWithBytesNoCopy:(void *)bytes length:(unsigned int)length encoding:(NSStringEncoding)encoding freeWhenDone:(BOOL)flag { if (self = [super init]) { string = [[NSMutableString alloc] initWithBytesNoCopy:bytes length:length encoding:encoding freeWhenDone:flag]; } + charCacheEnd = -1; return self; } - (id) initWithCapacity:(unsigned int)capacity { if (self = [super init]) { string = [[NSMutableString stringWithCapacity:capacity] retain]; } + charCacheEnd = -1; return self; } - (id) initWithCharactersNoCopy:(unichar *)characters length:(unsigned int)length freeWhenDone:(BOOL)flag { if (self = [super init]) { string = [[NSMutableString alloc] initWithCharactersNoCopy:characters length:length freeWhenDone:flag]; } + charCacheEnd = -1; return self; } - (id) initWithContentsOfFile:(id)path { + charCacheEnd = -1; return [self initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL]; } - (id) initWithContentsOfFile:(NSString *)path encoding:(NSStringEncoding)encoding error:(NSError **)error { if (self = [super init]) { string = [[NSMutableString alloc] initWithContentsOfFile:path encoding:encoding error:error]; } + charCacheEnd = -1; return self; } - (id) initWithCString:(const char *)nullTerminatedCString encoding:(NSStringEncoding)encoding { if (self = [super init]) { string = [[NSMutableString alloc] initWithCString:nullTerminatedCString encoding:encoding]; } + charCacheEnd = -1; return self; } - (id) initWithFormat:(NSString *)format, ... { @@ -622,12 +684,14 @@ va_start(argList, format); id str = [self initWithFormat:format arguments:argList]; va_end(argList); + charCacheEnd = -1; return str; } - (id) initWithFormat:(NSString *)format arguments:(va_list)argList { if (self = [super init]) { string = [[NSMutableString alloc] initWithFormat:format arguments:argList]; } + charCacheEnd = -1; return self; } - (unsigned int) length { @@ -641,15 +705,19 @@ } - (unsigned int) replaceOccurrencesOfString:(NSString *)target withString:(NSString *)replacement options:(unsigned)options range:(NSRange)searchRange { return [string replaceOccurrencesOfString:target withString:replacement options:options range:searchRange]; + [self clearCharCache]; } - (void) setString:(NSString *)aString { [string setString:aString]; + [self clearCharCache]; } - (void) replaceCharactersInRange:(NSRange)range withString:(NSString *)aString { [string replaceCharactersInRange:range withString:aString]; + [self clearCharCache]; } - (void) dealloc { [string release]; + if (charCacheEnd != -1) free(stringCharCache); [super dealloc]; } @end
\ No newline at end of file diff --git a/Source/SPStringAdditions.h b/Source/SPStringAdditions.h index 4acd748c..0a323cf3 100644 --- a/Source/SPStringAdditions.h +++ b/Source/SPStringAdditions.h @@ -25,6 +25,7 @@ @interface NSString (SPStringAdditions) + (NSString *)stringForByteSize:(int)byteSize; ++ (NSString *)stringForTimeInterval:(float)timeInterval; #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 - (NSArray *)componentsSeparatedByCharactersInSet:(NSCharacterSet *)set; diff --git a/Source/SPStringAdditions.m b/Source/SPStringAdditions.m index 2916611d..ad6972ce 100644 --- a/Source/SPStringAdditions.m +++ b/Source/SPStringAdditions.m @@ -66,6 +66,51 @@ return [numberFormatter stringFromNumber:[NSNumber numberWithFloat:size]]; } + +// ------------------------------------------------------------------------------- +// stringForTimeInterval: +// +// Returns a human readable version string of the supplied time interval. +// ------------------------------------------------------------------------------- ++ (NSString *)stringForTimeInterval:(float)timeInterval +{ + NSNumberFormatter *numberFormatter = [[[NSNumberFormatter alloc] init] autorelease]; + + [numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle]; + + if (timeInterval < 1) { + timeInterval = (timeInterval * 1000); + [numberFormatter setFormat:@"#,##0 ms"]; + + return [numberFormatter stringFromNumber:[NSNumber numberWithFloat:timeInterval]]; + } + + if (timeInterval < 100) { + [numberFormatter setFormat:@"#,##0.0 s"]; + + return [numberFormatter stringFromNumber:[NSNumber numberWithFloat:timeInterval]]; + } + + if (timeInterval < 300) { + [numberFormatter setFormat:@"#,##0 s"]; + + return [numberFormatter stringFromNumber:[NSNumber numberWithFloat:timeInterval]]; + } + + if (timeInterval < 3600) { + timeInterval = (timeInterval / 60); + [numberFormatter setFormat:@"#,##0 min"]; + + return [numberFormatter stringFromNumber:[NSNumber numberWithFloat:timeInterval]]; + } + + timeInterval = (timeInterval / 3600); + [numberFormatter setFormat:@"#,##0 hours"]; + + return [numberFormatter stringFromNumber:[NSNumber numberWithFloat:timeInterval]]; +} + + #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 // ------------------------------------------------------------------------------- // componentsSeparatedByCharactersInSet: diff --git a/Source/SPTableData.m b/Source/SPTableData.m index 905a8ec5..1391e355 100644 --- a/Source/SPTableData.m +++ b/Source/SPTableData.m @@ -508,12 +508,18 @@ NSMutableDictionary *fieldDetails = [[NSMutableDictionary alloc] init]; NSMutableArray *detailParts; NSString *detailString; - int i, partsArrayLength; + int i, definitionPartsIndex = 0, partsArrayLength; if (![definitionParts count]) return [NSDictionary dictionary]; + // Skip blank items within the definition parts + while (definitionPartsIndex < [definitionParts count] + && ![[[definitionParts objectAtIndex:definitionPartsIndex] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length]) + definitionPartsIndex++; + // The first item is always the data type. - [fieldParser setString:[definitionParts objectAtIndex:0]]; + [fieldParser setString:[definitionParts objectAtIndex:definitionPartsIndex]]; + definitionPartsIndex++; // If no field length definition is present, store only the type if ([fieldParser firstOccurrenceOfCharacter:'(' ignoringQuotedStrings:YES] == NSNotFound) { @@ -595,8 +601,8 @@ // Walk through the remaining column definition parts storing recognised details partsArrayLength = [definitionParts count]; - for (i = 1; i < partsArrayLength; i++) { - detailString = [[NSString alloc] initWithString:[[definitionParts objectAtIndex:i] uppercaseString]]; + for ( ; definitionPartsIndex < partsArrayLength; definitionPartsIndex++) { + detailString = [[NSString alloc] initWithString:[[definitionParts objectAtIndex:definitionPartsIndex] uppercaseString]]; // Whether numeric fields are unsigned if ([detailString isEqualToString:@"UNSIGNED"]) { @@ -611,30 +617,30 @@ [fieldDetails setValue:[NSNumber numberWithBool:YES] forKey:@"binary"]; // Whether text types have a different encoding to the table - } else if ([detailString isEqualToString:@"CHARSET"] && (i + 1 < partsArrayLength)) { - if (![[[definitionParts objectAtIndex:i+1] uppercaseString] isEqualToString:@"DEFAULT"]) { - [fieldDetails setValue:[definitionParts objectAtIndex:i+1] forKey:@"encoding"]; + } else if ([detailString isEqualToString:@"CHARSET"] && (definitionPartsIndex + 1 < partsArrayLength)) { + if (![[[definitionParts objectAtIndex:definitionPartsIndex+1] uppercaseString] isEqualToString:@"DEFAULT"]) { + [fieldDetails setValue:[definitionParts objectAtIndex:definitionPartsIndex+1] forKey:@"encoding"]; } - i++; - } else if ([detailString isEqualToString:@"CHARACTER"] && (i + 2 < partsArrayLength) - && [[[definitionParts objectAtIndex:i+1] uppercaseString] isEqualToString:@"SET"]) { - if (![[[definitionParts objectAtIndex:i+2] uppercaseString] isEqualToString:@"DEFAULT"]) {; - [fieldDetails setValue:[definitionParts objectAtIndex:i+2] forKey:@"encoding"]; + definitionPartsIndex++; + } else if ([detailString isEqualToString:@"CHARACTER"] && (definitionPartsIndex + 2 < partsArrayLength) + && [[[definitionParts objectAtIndex:definitionPartsIndex+1] uppercaseString] isEqualToString:@"SET"]) { + if (![[[definitionParts objectAtIndex:definitionPartsIndex+2] uppercaseString] isEqualToString:@"DEFAULT"]) {; + [fieldDetails setValue:[definitionParts objectAtIndex:definitionPartsIndex+2] forKey:@"encoding"]; } - i = i + 2; + definitionPartsIndex += 2; // Whether text types have a different collation to the table - } else if ([detailString isEqualToString:@"COLLATE"] && (i + 1 < partsArrayLength)) { - if (![[[definitionParts objectAtIndex:i+1] uppercaseString] isEqualToString:@"DEFAULT"]) { - [fieldDetails setValue:[definitionParts objectAtIndex:i+1] forKey:@"collation"]; + } else if ([detailString isEqualToString:@"COLLATE"] && (definitionPartsIndex + 1 < partsArrayLength)) { + if (![[[definitionParts objectAtIndex:definitionPartsIndex+1] uppercaseString] isEqualToString:@"DEFAULT"]) { + [fieldDetails setValue:[definitionParts objectAtIndex:definitionPartsIndex+1] forKey:@"collation"]; } - i++; + definitionPartsIndex++; // Whether fields are NOT NULL - } else if ([detailString isEqualToString:@"NOT"] && (i + 1 < partsArrayLength) - && [[[definitionParts objectAtIndex:i+1] uppercaseString] isEqualToString:@"NULL"]) { + } else if ([detailString isEqualToString:@"NOT"] && (definitionPartsIndex + 1 < partsArrayLength) + && [[[definitionParts objectAtIndex:definitionPartsIndex+1] uppercaseString] isEqualToString:@"NULL"]) { [fieldDetails setValue:[NSNumber numberWithBool:NO] forKey:@"null"]; - i++; + definitionPartsIndex++; // Whether fields are NULL } else if ([detailString isEqualToString:@"NULL"]) { @@ -645,11 +651,11 @@ [fieldDetails setValue:[NSNumber numberWithBool:YES] forKey:@"autoincrement"]; // Field defaults - } else if ([detailString isEqualToString:@"DEFAULT"] && (i + 1 < partsArrayLength)) { - detailParser = [[SPSQLParser alloc] initWithString:[definitionParts objectAtIndex:i+1]]; + } else if ([detailString isEqualToString:@"DEFAULT"] && (definitionPartsIndex + 1 < partsArrayLength)) { + detailParser = [[SPSQLParser alloc] initWithString:[definitionParts objectAtIndex:definitionPartsIndex+1]]; [fieldDetails setValue:[detailParser unquotedString] forKey:@"default"]; [detailParser release]; - i++; + definitionPartsIndex++; } // TODO: Currently unhandled: [UNIQUE | PRIMARY] KEY | COMMENT 'foo' | COLUMN_FORMAT bar | STORAGE q | REFERENCES... diff --git a/Source/TableDump.m b/Source/TableDump.m index fb0504a8..a59aad73 100644 --- a/Source/TableDump.m +++ b/Source/TableDump.m @@ -428,6 +428,11 @@ for ( i = 0 ; i < [queries count] ; i++ ) { [singleProgressBar setDoubleValue:((i+1)*100/[queries count])]; [singleProgressBar displayIfNeeded]; + + // Skip blank or whitespace-only queries to avoid errors + if ([[[queries objectAtIndex:i] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0) + continue; + [mySQLConnection queryString:[queries objectAtIndex:i]]; if (![[mySQLConnection getLastErrorMessage] isEqualToString:@""] && ![[mySQLConnection getLastErrorMessage] isEqualToString:@"Query was empty"]) { |