aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Interfaces/English.lproj/DBView.xib212
-rw-r--r--Source/CMMCPConnection.h2
-rw-r--r--Source/CMMCPConnection.m22
-rw-r--r--Source/CustomQuery.h9
-rw-r--r--Source/CustomQuery.m505
-rw-r--r--Source/SPSQLParser.h18
-rw-r--r--Source/SPSQLParser.m116
-rw-r--r--Source/SPStringAdditions.h1
-rw-r--r--Source/SPStringAdditions.m45
-rw-r--r--Source/SPTableData.m52
-rw-r--r--Source/TableDump.m5
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"]) {