-
Notifications
You must be signed in to change notification settings - Fork 10
/
SQLUtilities.vim
1881 lines (1659 loc) · 70.5 KB
/
SQLUtilities.vim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
" SQLUtilities: Variety of tools for writing SQL
" Author: David Fishburn <fishburn@ianywhere.com>
" Date: Nov 23, 2002
" Last Changed: Fri Sep 05 2003 2:05:33 PM
" Version: 1.3.3
" Script: http://www.vim.org/script.php?script_id=492
"
" Dependencies:
" Align.vim - Version 15 (as a minimum)
" - Author: Charles E. Campbell, Jr.
" - http://www.vim.org/script.php?script_id=294
"
" Suggested (Complementary) Plugins:
" db_ext.vim - Author: Peter Bagyinszki and David Fishburn
" - http://www.vim.org/script.php?script_id=356
"
" Functions:
" [range]SQLUFormatter(..list..)
"
" Formats SQL statements into a easily readable form.
" Breaks keywords onto new lines.
" Forces column lists to be split over as many lines as
" necessary to fit the current textwidth of the buffer,
" so that lines do not wrap.
" If parentheses are unbalanced (ie a subselect) it will
" indent everything within the unbalanced paranthesis.
" Works for SELECT, INSERT, UPDATE, DELETE statements.
"
" Examples:
"
" Original:
" SELECT m.MSG_ID, m.PRIORITY_ID, CUST.CUST_NBR, CUST.CUST_NM,
" CUST.CUST_LEGAL_NM, CUST.STORE_ADDR_1, CUST.STORE_ADDR_2,
" CUST.CROSS_STREET, XMLELEMENT( 'Alerts', XMLELEMENT( 'Alert_alert_id',
" alert_id ), XMLELEMENT( 'Alert_agent_id', agent_id ), XMLELEMENT(
" 'Alert_alert_type_id', alert_type_desc), XMLELEMENT(
" 'Alert_alert_date', alert_date), XMLELEMENT(
" 'Alert_url_reference', url_reference), XMLELEMENT(
" 'Alert_read_status', read_status )) CUST.STORE_CITY,
" CUST.STORE_ST, CUST.POST_CODE, CUST.STORE_MGR_NM, FROM MESSAGES m JOIN
" PRIORITY_CD P WHERE m.to_person_id = ? AND p.NAME = 'PRI_EMERGENCY' AND
" p.JOB = 'Plumber' AND m.status_id < ( SELECT s.STATUS_ID FROM
" MSG_STATUS_CD s WHERE s.NAME = 'MSG_READ') ORDER BY m.msg_id desc
"
"
" Formatted:
" SELECT m.MSG_ID, m.PRIORITY_ID, CUST.CUST_NBR, CUST.CUST_NM,
" CUST.CUST_LEGAL_NM, CUST.STORE_ADDR_1, CUST.STORE_ADDR_2,
" CUST.CROSS_STREET,
" XMLELEMENT(
" 'Alerts', XMLELEMENT( 'Alert_alert_id', alert_id ),
" XMLELEMENT( 'Alert_agent_id', agent_id ),
" XMLELEMENT( 'Alert_alert_type_id', alert_type_desc),
" XMLELEMENT( 'Alert_alert_date', alert_date),
" XMLELEMENT(
" 'Alert_url_reference', url_reference
" ), XMLELEMENT( 'Alert_read_status', read_status )
" ) CUST.STORE_CITY, CUST.STORE_ST, CUST.POST_CODE,
" CUST.STORE_MGR_NM
" FROM MESSAGES m
" JOIN PRIORITY_CD P
" WHERE m.to_person_id = ?
" AND p.NAME = 'PRI_EMERGENCY'
" AND p.JOB = 'Plumber'
" AND m.status_id < (
" SELECT s.STATUS_ID
" FROM MSG_STATUS_CD s
" WHERE s.NAME = 'MSG_READ'
" )
" ORDER BY m.msg_id desc
"
"
"
" Original:
" UPDATE "SERVICE_REQUEST" SET "BUILDING_ID" = ?, "UNIT_ID" = ?,
" "REASON_ID" = ?, "PERSON_ID" = ?, "PRIORITY_ID" = ?, "STATUS_ID" = ?,
" "CREATED" = ?, "REQUESTED" = ?, "ARRIVED" = ? WHERE "REQUEST_ID" = ?
"
"
" Formatted:
" UPDATE "SERVICE_REQUEST"
" SET "BUILDING_ID" = ?,
" "UNIT_ID" = ?,
" "REASON_ID" = ?,
" "PERSON_ID" = ?,
" "PRIORITY_ID" = ?,
" "STATUS_ID" = ?,
" "CREATED" = ?,
" "REQUESTED" = ?,
" "ARRIVED" = ?,
" WHERE "REQUEST_ID" = ?
"
"
"
" Original:
" INSERT INTO "MESSAGES" ( "MSG_ID", "TO_PERSON_ID",
" "FROM_PERSON_ID", "REQUEST_ID", "CREATED", "PRIORITY_ID",
" "MSG_TYPE_ID", "STATUS_ID", "READ_WHEN", "TIMEOUT",
" "MSG_TXT", "RESEND_COUNT" ) VALUES ( ?, ?, ?,
" ?, ?, ?, ?, ?, ?, ?, ?, ? )
"
"
" Formatted:
" INSERT INTO "MESSAGES" ( "MSG_ID", "TO_PERSON_ID",
" "FROM_PERSON_ID", "REQUEST_ID", "CREATED",
" "PRIORITY_ID", "MSG_TYPE_ID", "STATUS_ID",
" "READ_WHEN", "TIMEOUT", "MSG_TXT", "RESEND_COUNT" )
" VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
"
"
" Functions:
" SQLUCreateColumnList( optional parameter )
"
" Assumes either the current file, or any other open buffer,
" has a CREATE TABLE statement in a format similar to this:
" CREATE TABLE customer (
" id INT DEFAULT AUTOINCREMENT,
" last_modified TIMESTAMP NULL,
" first_name VARCHAR(30) NOT NULL,
" last_name VARCHAR(60) NOT NULL,
" balance NUMERIC(10,2),
" PRIMARY KEY( id )
" );
" If you place the cursor on the word customer, then the
" unnamed buffer (also displayed by an echo statement) will
" contain:
" id, last_modified, first_name, last_name, balance
"
" Optionally, it will replace the word with the above and place
" the word in the unnamed buffer. Calling the function with
" a parameter enables this feature.
"
" This also uses the g:sqlutil_cmd_terminator to determine when
" the create table statement ends if none of the following terms
" are found before the final );
" primary,reference,unique,check,foreign
" sqlutil_cmd defaults to ";"
"
"
" Functions:
" SQLUGetColumnDef( optional parameter )
" SQLUGetColumnDataType( expand("<cword>"), 1 )
"
" Assumes either the current file, or any other open buffer,
" has a CREATE TABLE statement in a format similar to this:
" CREATE TABLE customer (
" id INT DEFAULT AUTOINCREMENT,
" last_modified TIMESTAMP NULL,
" first_name VARCHAR(30) NOT NULL,
" last_name VARCHAR(60) NOT NULL,
" balance NUMERIC(10,2),
" PRIMARY KEY( id )
" );
" If you place the cursor on the word first_name, then the
" column definition will be placed in the unnamed buffer (and also
" displayed by an echo statement).
" VARCHAR(30) NOT NULL
"
" If the command is called as SQLUGetColumnDef( expand("<cword>"), 1 )
" or using the default mapping \scdt, just the datatype (instead
" of the column definition) will be returned. A separate command
" SQLUGetColumnDataType has been created for this.
" VARCHAR(30)
"
"
" Functions:
" SQLUCreateProcedure()
"
" Assumes either the current file, or any other open buffer,
" has a CREATE TABLE statement in a format similar to this:
" CREATE TABLE customer (
" id INT DEFAULT AUTOINCREMENT,
" last_modified TIMESTAMP NULL,
" first_name VARCHAR(30) NOT NULL,
" last_name VARCHAR(60) NOT NULL,
" balance NUMERIC(10,2),
" PRIMARY KEY( id )
" );
" By calling SQLUCreateProcedure while on the name of a table
" the unnamed buffer will contain the create procedure statement
" for insert, update, delete and select statements.
" Once pasted into the buffer, unneeded functionality can be
" removed.
"
"
"
" Commands:
" [range]SQLUFormatter ..list..
" : Reformats the SQL statements over the specified
" range. Statement will lined up given the
" existing indent of the first word.
" SQLUCreateColumnList: Creates a comma separated list of column names
" for the table name under the cursor, assuming
" the table definition exists in any open
" buffer. The column list is placed in the unnamed
" buffer.
" This also uses the g:sqlutil_cmd_terminator
" This routine can optionally take 2 parameters
" SQLUCreateColumnList T1
" Creates a column list for T1
" SQLUCreateColumnList T1 1
" Creates a column list for T1 but only for
" the primary keys for that table.
" SQLUGetColumnDef : Displays the column definition of the column name
" under the cursor. It assumes the CREATE TABLE
" statement is in an open buffer.
" SQLUGetColumnDataType
" : Displays the column datatype of the column name
" under the cursor. It assumes the CREATE TABLE
" statement is in an open buffer.
" SQLUCreateProcedure : Creates a stored procedure to perform standard
" operations against the table that the cursor
" is currently under.
"
"
"
" Suggested Mappings:
" vmap <silent>sf <Plug>SQLU_Formatter<CR>
" nmap <silent>scl <Plug>SQLU_CreateColumnList<CR>
" nmap <silent>scd <Plug>SQLU_GetColumnDef<CR>
" nmap <silent>scdt <Plug>SQLU_GetColumnDataType<CR>
" nmap <silent>scp <Plug>SQLU_CreateProcedure<CR>
"
" mnemonic explanation
" s - sql
" f - format
" cl - column list
" cd - column definition
" cdt - column datatype
" cp - create procedure
"
" To prevent the default mappings from being created, place the
" following in your _vimrc:
" let g:sqlutil_load_default_maps = 0
"
" Customization:
" By default this script assumes a command is terminated by a ;
" If you are using Microsoft SQL Server a command terminator
" would be "go", or perhaps "\ngo".
" To permenantly override the terminator in your _vimrc file you can add
" let g:sqlutil_cmd_terminator = "\ngo"
"
"
" When building a column list from a script file (ie CREATE TABLE
" statements), you can customize the script to detect when the
" column list finishes by creating the following in your _vimrc:
" let g:sqlutil_col_list_terminators =
" \ 'primary,reference,unique,check,foreign'
" This can be necessary in the following example:
" CREATE TABLE customer (
" id INT DEFAULT AUTOINCREMENT,
" first_name VARCHAR(30) NOT NULL,
" last_name VARCHAR(60) NOT NULL,
" PRIMARY KEY( id )
" );
"
"
" TODO:
" 1. Suggestions welcome
"
"
" History:
" 1.3.3: Sep 05, 2003: NF: Added global variable
" sqlutil_col_list_terminators for
" customization.
" 1.3.2: Aug 24, 2003: NF: Changed all functions to be prefixed by
" SQLU_ for consistency.
" BF: Fixed SQLU_GetColumnDataType and
" SQLU_GetColumnDef to handle tabs.
" 1.3.1: Aug 21, 2003: BF: -@- could be left after incorrect formatting.
" 1.3 : Mar 30, 2003: NF: Support the formatting of FUNCTIONS or
" stored procedures used as derived tables. This
" will nest the function calls on new lines and
" correctly split the paranthesis on new lines if
" the function call is longer than one line. You
" would notice this mainly in the SELECT
" column list.
" NF: Support the formatting of nested CASE
" statements.
" NF: Added the SQLU_GetColumnDataType command.
" NF: Improved primary key determination, it no
" longer requires the PRIMARY KEY statement to
" be part of the CREATE TABLE statement, it can
" be part of an ALTER TABLE statement.
" NF: Improved formatting of SQL keywords.
" INSERT INTO statement, the INTO will no longer
" be split onto a new line.
" NF: Now correctly format the various JOIN keywords:
" NATURAL RIGHT OUTER JOIN will be placed one
" online instead of just the JOIN keyword as
" before.
" BF: Did not properly handle the formatting of
" nested open paranthesis in all cases.
" BF: Using new technique to determine how to change
" the textwidth to utilitize more screen space
" when wrapping long lines.
" 1.2 : Nov 30, 2002: NF: Create procedure uses shiftwidth for indent.
" BF: Save/restore previous search.
" 1.0 : Nov 13, 2002: NF: Initial version.
" ---------------------------------------------------------------------
" Prevent duplicate loading
if exists("g:loaded_sqlutilities") || &cp
finish
endif
let g:loaded_sqlutilities = 1
if !exists('g:sqlutil_cmd_terminator')
let g:sqlutil_cmd_terminator = ';'
endif
if !exists('g:sqlutil_col_list_terminators')
" You can override which keywords will determine
" when a column list finishes:
" CREATE TABLE customer (
" id INT DEFAULT AUTOINCREMENT,
" last_modified TIMESTAMP NULL,
" first_name VARCHAR(30) NOT NULL,
" last_name VARCHAR(60) NOT NULL,
" balance NUMERIC(10,2),
" PRIMARY KEY( id )
" );
" So in the above example, when "primary" is reached, we
" know the column list is complete.
let g:sqlutil_col_list_terminators =
\ 'primary' " PRIMARY KEY
\ ',reference' " foreign keys
\ ',unique' " indicies
\ ',check' " check contraints
\ ',foreign' " foreign keys
endif
" Public Interface:
command! -range -nargs=* SQLUFormatter <line1>,<line2>
\ call s:SQLU_Formatter(<f-args>)
command! -nargs=* SQLUCreateColumnList
\ call SQLU_CreateColumnList(<f-args>)
command! -nargs=* SQLUGetColumnDef
\ call SQLU_GetColumnDef(<f-args>)
command! -nargs=* SQLUGetColumnDataType
\ call SQLU_GetColumnDef(expand("<cword>"), 1)
command! -nargs=* SQLUCreateProcedure
\ call SQLU_CreateProcedure(<f-args>)
if !exists("g:sqlutil_load_default_maps")
let g:sqlutil_load_default_maps = 1
endif
if(g:sqlutil_load_default_maps == 1)
if !hasmapto('<Plug>SQLUFormatter')
vmap <unique> <Leader>sf <Plug>SQLUFormatter
endif
if !hasmapto('<Plug>SQLUCreateColumnList')
map <unique> <Leader>scl <Plug>SQLUCreateColumnList
endif
if !hasmapto('<Plug>SQLUGetColumnDef')
map <unique> <Leader>scd <Plug>SQLUGetColumnDef
endif
if !hasmapto('<Plug>SQLUGetColumnDataType')
map <unique> <Leader>scdt <Plug>SQLUGetColumnDataType
endif
if !hasmapto('<Plug>SQLUCreateProcedure')
map <unique> <Leader>scp <Plug>SQLUCreateProcedure
endif
endif
if exists("g:loaded_sqlutilities_global_maps")
vunmap <unique> <script> <Plug>SQLUFormatter
nunmap <unique> <script> <Plug>SQLUCreateColumnList
nunmap <unique> <script> <Plug>SQLUGetColumnDef
nunmap <unique> <script> <Plug>SQLUGetColumnDataType
nunmap <unique> <script> <Plug>SQLUCreateProcedure
endif
" Global Maps:
vmap <unique> <script> <Plug>SQLUFormatter :SQLUFormatter<CR>
nmap <unique> <script> <Plug>SQLUCreateColumnList :SQLUCreateColumnList<CR>
nmap <unique> <script> <Plug>SQLUGetColumnDef :SQLUGetColumnDef<CR>
nmap <unique> <script> <Plug>SQLUGetColumnDataType :SQLUGetColumnDataType<CR>
nmap <unique> <script> <Plug>SQLUCreateProcedure :SQLUCreateProcedure<CR>
let g:loaded_sqlutilities_global_maps = 1
if has("gui_running") && has("menu")
vnoremenu <script> Plugin.SQLUtil.Format\ Statement :SQLUFormatter<CR>
noremenu <script> Plugin.SQLUtil.Format\ Statement :SQLUFormatter<CR>
noremenu <script> Plugin.SQLUtil.Create\ Procedure :SQLUCreateProcedure<CR>
inoremenu <script> Plugin.SQLUtil.Create\ Procedure
\ <C-O>:SQLUCreateProcedure<CR>
noremenu <script> Plugin.SQLUtil.Create\ Column\ List
\ :SQLUCreateColumnList<CR>
inoremenu <script> Plugin.SQLUtil.Create\ Column\ List
\ <C-O>:SQLUCreateColumnList<CR>
noremenu <script> Plugin.SQLUtil.Column\ Definition
\ :SQLUGetColumnDef<CR>
inoremenu <script> Plugin.SQLUtil.Column\ Definition
\ <C-O>:SQLUGetColumnDef<CR>
endif
" SQLU_Formatter: align selected text based on alignment pattern(s)
function! s:SQLU_Formatter(...) range
" call Decho("SQLU_Formatter() {")
call s:SQLU_WrapperStart( a:firstline, a:lastline )
" Store pervious value of highlight search
let hlsearch = &hlsearch
let &hlsearch = 0
" save previous search string
let saveSearch = @/
" save previous format options and turn off automatic formating
let saveFormatOptions = &formatoptions
silent execute 'setlocal formatoptions-=a'
" Use the mark locations instead of storing the line numbers
" since these values can changes based on the reformatting
" of the lines
let ret = s:SQLU_ReformatStatement()
if ret > -1
let ret = s:SQLU_IndentNestedBlocks()
if ret > -1
let ret = s:SQLU_WrapLongLines()
endif
endif
" Restore default value
" And restore cursor position
let &hlsearch = hlsearch
call s:SQLU_WrapperEnd()
" restore previous format options
let &formatoptions = saveFormatOptions
" restore previous search string
let @/ = saveSearch
endfunction
" This function will return a count of unmatched parenthesis
" ie ( this ( funtion ) - will return 1 in this case
function! s:SQLU_CountUnbalancedParan( line, paran_to_check )
let l = a:line
let lp = substitute(l, '[^(]', '', 'g')
let l = a:line
let rp = substitute(l, '[^)]', '', 'g')
if a:paran_to_check =~ ')'
" echom 'SQLU_CountUnbalancedParan ) returning: '
" \ . (strlen(rp) - strlen(lp))
return (strlen(rp) - strlen(lp))
elseif a:paran_to_check =~ '('
" echom 'SQLU_CountUnbalancedParan ( returning: '
" \ . (strlen(lp) - strlen(rp))
return (strlen(lp) - strlen(rp))
else
" echom 'SQLU_CountUnbalancedParan unknown paran to check: ' .
" \ a:paran_to_check
return 0
endif
endfunction
" WS: wrapper start (internal) Creates guard lines,
" stores marks y and z, and saves search pattern
function! s:SQLU_WrapperStart( beginline, endline )
let b:curline = line(".")
let b:curcol = virtcol(".")
let b:keepsearch = @/
let b:keepline_my = line("'y")
let b:keepcol_my = virtcol("'y")
let b:keepline_mz = line("'z")
let b:keepcol_mz = virtcol("'z")
silent! exec 'norm! '.a:endline."G\<bar>0\<bar>"
" Add a new line to the bottom of the mark to be removed latter
put =''
" silent! exec "norm! mz'<"
silent! exec "ma z"
silent! exec 'norm! '.a:beginline."G\<bar>0\<bar>"
" Add a new line above the mark to be removed latter
put! = ''
" silent! exec "norm! my"
silent! exec "ma y"
let b:ch= &ch
set ch=2
silent! exec "norm! 'zk"
" echom 'SQLU_WrapperStart'
" echom 'y-1l: '.(line("'y")-1).' t: '.getline(line("'y")-1)
" echom 'y: '.line("'y").' t: '.getline(line("'y"))
" echom 'z: '.line("'z").' t: '.getline(line("'z"))
" echom 'z+1: '.(line("'z")+1).' t: '.getline(line("'z")+1)
endfunction
" WE: wrapper end (internal) Removes guard lines,
" restores marks y and z, and restores search pattern
function! s:SQLU_WrapperEnd()
" Delete blanks lines added around the visually selected range
silent! exe "norm! 'yjkdd'zdd"
silent! exe "set ch=".b:ch
unlet b:ch
let @/= b:keepsearch
" if b:keepline_my != 0
" silent! exe 'norm! '.b:keepline_my."G\<bar>".b:keepcol_my."l"
" endif
" if b:keepline_mz != 0
" silent! exe 'norm! '.b:keepline_mz."G\<bar>".b:keepcol_mz."l"
" endif
" silent! exe 'norm! '.b:curline."G\<bar>".b:curcol."l"
silent! exe 'norm! '.b:curline."G\<bar>".b:curcol."l"
unlet b:keepline_my b:keepcol_my
unlet b:keepline_mz b:keepcol_mz
unlet b:curline b:curcol
endfunction
" Reformats the statements
" 1. Keywords (FROM, WHERE, AND, ... ) " are on new lines
" 2. Keywords are right justified
" 3. CASE statements are setup for alignment.
" 4. Operators are lined up
"
function! s:SQLU_ReformatStatement()
" Remove any lines that have comments on them since the comments
" could spill onto new lines and no longer have comment markers
" which would result in syntax errors
" Comments could also contain keywords, which would be split
" on to new lines
silent! 'y+1,'z-1s/.*\zs--.*//e
" Join block of text into 1 line
silent! 'y+1,'z-1j
" Reformat the commas, to remove any spaces before them
silent! 'y+1,'z-1s/\s*,/,/ge
" And add a space following them, this allows the line to be
" split using gqq
silent! 'y+1,'z-1s/,\(\w\)/, \1/ge
" Change more than 1 space with just one except spaces at
" the beginning of the range
" silent! 'y+1,'z-1s/\s\+/ /ge
silent! 'y+1,'z-1s/\(\S\+\)\(\s\+\)/\1 /g
" Go to the start of the block
silent! 'y+1
" Place an UPDATE on a newline, but not if it is preceeded by
" the existing statement. Example:
" INSERT INTO T1 (...)
" ON EXISTING UPDATE
" VALUES (...);
" SELECT ...
" FOR UPDATE
let sql_update_keywords = '' .
\ '\%(\%(\<\%(for\|existing\)\s\+\)\@<!update\)'
" INTO clause can be used in a SELECT statement as well
" as an INSERT statement. We do not want to place INTO
" on a newline if it is preceeded by INSERT
let sql_into_keywords = '' .
\ '\%(\%(\<insert\s\+\)\@<!into\)'
" FROM clause can be used in a DELETE statement as well
" as a SELECT statement. We do not want to place FROM
" on a newline if it is preceeded by DELETE
let sql_from_keywords = '' .
\ '\%(\%(\<delete\s\+\)\@<!from\)'
" Only place order on a newline if followed by "by"
" let sql_order_keywords = '' . \ '\%(\%(\<order\s\+\)\@<!into\)'
" join type syntax from ASA help file
" INNER
" | LEFT [ OUTER ]
" | RIGHT [ OUTER ]
" | FULL [ OUTER ]
" LEFT, RIGHT, FULL can optional be followed by OUTER
" The entire expression is optional
let sql_join_type_keywords = '' .
\ '\%(' .
\ '\%(inner\|' .
\ '\%(\%(\%(left\|right\|full\)\s*\%(outer\)\?\s*\)\?\)' .
\ '\)\?\s*\)\?'
" Decho 'join types: ' . sql_join_type_keywords
" join operator syntax
" [ KEY | NATURAL ] [ join_type ] JOIN
" | CROSS JOIN
let sql_join_operator = '' .
\ '\%(' .
\ '\%(\%(\%(key\|natural\)\?\s*\)\?' .
\ sql_join_type_keywords .
\ 'join\)\|' .
\ '\%(\%(\%(cross\)\?\s*\)\?join\)' .
\ '\)'
" Decho 'join operators: ' . sql_join_operator
" force each keyword onto a newline
let sql_keywords = 'create\|drop\|call\|select\|set\|values\|' .
\ sql_update_keywords . '\|' .
\ sql_into_keywords . '\|' .
\ sql_from_keywords . '\|' .
\ sql_join_operator . '\|' .
\ 'on\|where\|and\|or\|order by\|group by\|' .
\ 'having\|for\|insert\|union\|subscribe\|' .
\ 'intersect\|except\|with\|window'
let cmd = "'y+1,'z-1".'s/\%(^\s*\)\@<!\zs\<\(' .
\ sql_keywords .
\ '\)\>/\r\1/gei'
" Decho cmd
silent! exec cmd
" Ensure keywords at the beginning of a line have a space after them
" This will ensure the Align program lines them up correctly
" silent! 'y+1,'z-1s/^\([a-zA-Z0-9_]*\)(/\1 (/e
" Delete any non empty lines
" Do NOT delete empty lines, since that can affect the marks
" and change which lines get formatted
" 'y+1,'z-1g/^\s*$/d
" Make sure the first word on each line has the special -@- symbol after
" it which is used to align the rest of the SQL statement. This command
" will replace the whitespace following the first word with the special
" symbol
silent! exec "'y+1,'z-1".'s/^\s*\<\w\+\>\zs\s*/-@-'
" Ensure CASE statements also start on new lines
" CASE statements can also be nested, but we want these to align
" with column lists, not keywords, so the -@- is placed BEFORE
" the CASE keywords, not after
let sql_case_keywords = '\(\<end\s\+\)\@<!case'.
\ '\|when\|else\|end\( case\)\?'
" echom 'case: '.sql_case_keywords
" The case keywords must not be proceeded by a -@-
silent! exec "'y+1,'z-1".'s/'.
\ '\%(-@-\)\@<!'.
\ '\<\('.
\ sql_case_keywords.
\ '\)\>/\r-@-\1/gei'
" AlignPush
" Using the Align.vim plugin, reformat the lines
" so that the keywords are RIGHT justified
AlignCtrl default
" Replace the space after the first word on each line with
" -@- to align on this later
" silent! 'y+1,'z-1s/^\(\s*\)\([a-zA-Z0-9_]*\) /\1\2-@-
call s:SQLU_WrapFunctionCalls()
let ret = s:SQLU_SplitUnbalParan()
if ret < 0
" Undo any changes made so far since an error occurred
" silent! exec 'u'
return ret
endif
" Align these based on the special charater
" and the column names are LEFT justified
AlignCtrl Ip0P0rl:
silent! 'y+1,'z-1Align -@-
silent! 'y+1,'z-1s/-@-/ /ge
" Now align the operators
" and the operators are CENTER justified
" AlignCtrl default
" AlignCtrl g [!><=]
" AlignCtrl Wp1P1l
" Change this to only attempt to align the last WHERE clause
" and not the entire SQL statement
" silent! 'y+1,'z-1Align [!><=]=\=
" Reset back to defaults
AlignCtrl default
" Reset the alignment to what it was prior to
" this function
" AlignPop
return 1
endfunction
" Check through the selected text for open ( and
" indent if necessary
function! s:SQLU_IndentNestedBlocks()
let org_textwidth = &textwidth
if &textwidth == 0
" Get the width of the window
let &textwidth = winwidth(winnr())
endif
let sql_keywords = 'select\|set\|\(insert\s*\)\?into\|from\|values'.
\ '\|order\|group\|having\|return\|call'
" Indent nested blocks surrounded by ()s.
let linenum = line("'y+1")
while linenum <= line("'z-1")
let line = getline(linenum)
if line =~ '(\s*$'
let begin_paran = match( line, '(\s*$' )
if begin_paran > -1
let curline = line(".")
let curcol = begin_paran + 1
" echom 'begin_paran: '.begin_paran.
" \ ' line: '.curline.
" \ ' col: '.curcol
silent! exe 'norm! '.linenum."G\<bar>".curcol."l"
" v - visual
" ib - inner block
" k - backup on line
" > - right shift
" . - shift again
" silent! exe 'norm! vibk>.'
silent! exe 'norm! vibk>'
" If the following line begins with a keyword,
" indent one additional time. This is necessary since
" keywords are right justified, so they need an extra
" indent
if getline(linenum+1) =~? '^\s*\('.sql_keywords.'\)'
silent! exe 'norm! .'
endif
" echom 'SQLU_IndentNestedBlocks - from: '.line("'<").' to: ' .
" \ line("'>")
" echom 'SQLU_IndentNestedBlocks - no match: '.getline(linenum)
endif
endif
let linenum = linenum + 1
endwhile
let ret = linenum
" Indent nested CASE blocks
let linenum = line("'y+1")
" Search for the beginning of a CASE statement
let begin_case = '\<\(\<end\s\+\)\@<!case\>'
silent! exe 'norm! '.linenum."G\<bar>0\<bar>"
while( search( begin_case, 'W' ) > 0 )
let curline = line(".")
if( (curline < line("'y+1")) || (curline > line("'z-1" )) )
" echom 'No case statements, leaving loop'
silent! exe 'norm! '.line("'y+1")."G\<bar>0\<bar>"
break
endif
" echom 'begin CASE found at: '.curline
let curline = curline + 1
let end_of_case = s:SQLU_IndentNestedCase( begin_case, curline,
\ line("'z-1") )
let end_of_case = end_of_case + 1
let ret = end_of_case
if( ret < 0 )
break
endif
silent! exe 'norm! '.end_of_case."G\<bar>0\<bar>"
endwhile
let &textwidth = org_textwidth
return ret
endfunction
" Recursively indent nested case statements
function! s:SQLU_IndentNestedCase( begin_case, start_line, end_line )
" Indent nested CASE blocks
let linenum = a:start_line
" Find the matching end case statement
let end_of_prev_case = searchpair(a:begin_case, '',
\ '\<end\( case\)\?\>', 'W', '' )
if( (end_of_prev_case < a:start_line) || (end_of_prev_case > a:end_line) )
call s:SQLU_WarningMsg(
\ 'No matching end case for: ' .
\ getline((linenum-1))
\ )
return -1
" else
" echom 'Matching END found at: '.end_of_prev_case
endif
silent! exe 'norm! '.linenum."G\<bar>0\<bar>"
if( search( a:begin_case, 'W' ) > 0 )
let curline = line(".")
if( (curline > a:start_line) && (curline < end_of_prev_case) )
let curline = curline + 1
let end_of_case = s:SQLU_IndentNestedCase( a:begin_case, curline,
\ line("'z-1") )
" echom 'SQLU_IndentNestedCase from: '.linenum.' to: '.end_of_case
silent! exec (curline-1) . "," . end_of_case . ">>"
" else
" echom 'SQLU_IndentNestedCase No case statements, '.
" \ 'leaving SQLU_IndentNestedCase: '.linenum
endif
endif
return end_of_prev_case
endfunction
" For certain keyword lines (SELECT, ORDER BY, GROUP BY, ...)
" Ensure the lines fit in the textwidth (or default 80), wrap
" the lines where necessary and left justify the column names
function! s:SQLU_WrapFunctionCalls()
" Check if this is a statement that can often by longer than 80 characters
" (select, set and so on), if so, ensure the column list is broken over as
" many lines as necessary and lined up with the other columns
let linenum = line("'y+1")
let org_textwidth = &textwidth
if org_textwidth == 0
" Get the width of the window
let curr_textwidth = winwidth(winnr())
else
let curr_textwidth = org_textwidth
endif
let sql_keywords = 'select\|set\|\(insert\(-@-\)\?\)into\|from\|values'.
\ '\|order\|group\|having\|return'
" Useful in the debugger
" echo linenum.' '.func_call.' '.virtcol(".").'
" '.','.substitute(getline("."), '^ .*\(\%'.(func_call-1).'c...\).*',
" '\1', '' ).', '.getline(linenum)
" call Decho(" Before column splitter 'y+1=".line("'<").
" \ ":".col("'<")." 'z-1=".line("'>").":".col("'>"))
while linenum <= line("'z-1")
let line = getline(linenum)
if strlen(line) < curr_textwidth
let linenum = linenum + 1
continue
endif
let get_func_nm = '[a-zA-Z_.]\+\s*('
" Use a special line textwidth, since if we split function calls
" any text within the parantheses will be indented 2 &shiftwidths
" so when calculating where to split, we must take that into
" account
let keyword_str = matchstr(
\ getline(linenum), '^\s*\('.sql_keywords.'\)' )
let line_textwidth = curr_textwidth - strlen(keyword_str)
let func_call = 0
while( strlen(getline(linenum)) > line_textwidth )
" Find the column # of the start of the function name
let func_call = match( getline(linenum), get_func_nm, func_call )
if func_call < 0
" If no functions found, move on to next line
break
endif
let prev_func_call = 0
" Position cursor at func_call
silent! exe 'norm! '.linenum."G\<bar>".func_call."l"
if search('(', 'W') > linenum
call s:SQLU_WarningMsg(
\ 'SQLU_WrapFunctionCalls - should have found a ('
\ )
let linenum = linenum + 1
break
endif
let end_paran = searchpair( '(', '', ')', '' )
if end_paran < linenum || end_paran > linenum
call s:SQLU_WarningMsg(
\ 'SQLU_WrapFunctionCalls - ' .
\ 'should have found a matching )'
\ )
let linenum = linenum + 1
break
endif
let prev_func_call = func_call
" If the matching ) is past the textwidth
if virtcol(".") > line_textwidth
if (virtcol(".")-func_call) > line_textwidth
" Place the closing brace on a new line only if
" the entire length of the function call and
" parameters is longer than a line
silent! exe "norm! i\r-@-\<esc>"
endif
" If the SQL keyword preceeds the function name dont
" bother placing it on a new line
let preceeded_by_keyword =
\ '^\s*' .
\ '\(' .
\ sql_keywords .
\ '\)' .
\ '\(-@-\)\?' .
\ '\s*' .
\ '\%'.(func_call+1).'c'
" echom 'preceeded_by_keyword: '.preceeded_by_keyword
" echom 'func_call:'.func_call.' Current
" character:"'.getline(linenum)[virtcol(func_call)].'" -
" '.getline(linenum)
if getline(linenum) !~? preceeded_by_keyword
" if line =~? '^\s*\('.sql_keywords.'\)'
" Place the function name on a new line
silent! exe linenum.'s/\%'.(func_call+1).'c/\r-@-'
let linenum = linenum + 1
" These lines will be indented since they are wrapped
" in parantheses. Decrease the line_textwidth by
" that amount to determine where to split nested
" function calls
let line_textwidth = line_textwidth - (2 * &shiftwidth)
let func_call = 0
" Get the new offset of this function from the start
" of the newline it is on
let prev_func_call = match(
\ getline(linenum),get_func_nm,func_call)
endif
endif
" Get the name of the previous function
let prev_func_call_str = matchstr(
\ getline(linenum), get_func_nm, prev_func_call )
" Advance the column by its length to find the next function
let func_call = prev_func_call +
\ strlen(prev_func_call_str)
endwhile
let linenum = linenum + 1
endwhile
let &textwidth = org_textwidth
return linenum
endfunction
" For certain keyword lines (SELECT, ORDER BY, GROUP BY, ...)
" Ensure the lines fit in the textwidth (or default 80), wrap
" the lines where necessary and left justify the column names
function! s:SQLU_WrapLongLines()
" Check if this is a statement that can often by longer than 80 characters
" (select, set and so on), if so, ensure the column list is broken over as
" many lines as necessary and lined up with the other columns
let linenum = line("'y+1")
let org_textwidth = &textwidth
if &textwidth == 0
" Get the width of the window
let &textwidth = winwidth(winnr())
endif
let sql_keywords = 'select\|set\|into\|from\|values'.
\ '\|order\|group\|having\|call'
" call Decho(" Before column splitter 'y+1=".line("'<").
" \ ":".col("'<")." 'z-1=".line("'>").":".col("'>"))
while linenum <= line("'z-1")
let line = getline(linenum)
" if line =~? '^\s*\('.sql_keywords.'\)'
if line =~? '\w'
" Set the textwidth to current value
" minus an adjustment for select and set
" minus any indent value this may have
" echo 'tw: '.&textwidth.' indent: '.indent(line)
" Decho 'line: '.line
" Decho 'tw: '.&textwidth.' match at: '.
" \ matchend(line, sql_keywords )
" let &textwidth = &textwidth - 10 - indent(line)
if line =~? '^\s*\('.sql_keywords.'\)'
let &textwidth = &textwidth - matchend(line, sql_keywords ) - 2
let line_length = strlen(line) - matchend(line, sql_keywords )
else
let line_length = strlen(line)
endif
if( line_length > &textwidth )
" Decho 'linenum: ' . linenum . ' strlen: ' .
" \ strlen(line) . ' textwidth: ' . &textwidth .
" \ ' line: ' . line
" go to the current line
silent! exec linenum
" Mark the start of the wide line
silent! exec "normal mb"
" echom "line b - ".getline("'b")
" Mark the next line
silent! exec "normal jmek"
" echom "line e - ".getline("'e")
" echom "line length- ".strlen(getline(".")).
" \ " tw=".&textwidth
if line =~? '^\s*\('.sql_keywords.'\)'
" Create a special marker for Align.vim
" to line up the columns with
silent! exec linenum . ',' . linenum . 's/\(\w\) /\1-@-'
" If the line begins with SET then force each
" column on a newline, instead of breaking them apart
" this will ensure that the col_name = ... is on the
" same line
if line =~? '^\s*\<set\>'
silent! 'b,'e-1s/,/,\r/ge
endif
else
" Place the special marker that the first non-whitespace
" characeter