This file is indexed.

/usr/lib/ruby/vendor_ruby/sequel/dataset/sql.rb is in ruby-sequel 3.33.0-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

   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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
module Sequel
  class Dataset
    # ---------------------
    # :section: 3 - User Methods relating to SQL Creation
    # These are methods you can call to see what SQL will be generated by the dataset.
    # ---------------------
    
    # Returns a DELETE SQL query string.  See +delete+.
    # 
    #   dataset.filter{|o| o.price >= 100}.delete_sql
    #   # => "DELETE FROM items WHERE (price >= 100)"
    def delete_sql
      return static_sql(opts[:sql]) if opts[:sql]
      check_modification_allowed!
      clause_sql(:delete)
    end
    
    # Returns an EXISTS clause for the dataset as a +LiteralString+.
    #
    #   DB.select(1).where(DB[:items].exists)
    #   # SELECT 1 WHERE (EXISTS (SELECT * FROM items))
    def exists
      SQL::PlaceholderLiteralString.new("EXISTS ?", [self], true)
    end
    
    # Returns an INSERT SQL query string.  See +insert+.
    #
    #   DB[:items].insert_sql(:a=>1)
    #   # => "INSERT INTO items (a) VALUES (1)"
    def insert_sql(*values)
      return static_sql(@opts[:sql]) if @opts[:sql]

      check_modification_allowed!

      columns = []

      case values.size
      when 0
        return insert_sql({})
      when 1
        case vals = values.at(0)
        when Hash
          vals = @opts[:defaults].merge(vals) if @opts[:defaults]
          vals = vals.merge(@opts[:overrides]) if @opts[:overrides]
          values = []
          vals.each do |k,v| 
            columns << k
            values << v
          end
        when Dataset, Array, LiteralString
          values = vals
        else
          if vals.respond_to?(:values) && (v = vals.values).is_a?(Hash)
            return insert_sql(v) 
          end
        end
      when 2
        if (v0 = values.at(0)).is_a?(Array) && ((v1 = values.at(1)).is_a?(Array) || v1.is_a?(Dataset) || v1.is_a?(LiteralString))
          columns, values = v0, v1
          raise(Error, "Different number of values and columns given to insert_sql") if values.is_a?(Array) and columns.length != values.length
        end
      end

      if values.is_a?(Array) && values.empty? && !insert_supports_empty_values? 
        columns = [columns().last]
        values = ['DEFAULT'.lit]
      end
      clone(:columns=>columns, :values=>values)._insert_sql
    end
    
    # Returns a literal representation of a value to be used as part
    # of an SQL expression. 
    # 
    #   DB[:items].literal("abc'def\\") #=> "'abc''def\\\\'"
    #   DB[:items].literal(:items__id) #=> "items.id"
    #   DB[:items].literal([1, 2, 3]) => "(1, 2, 3)"
    #   DB[:items].literal(DB[:items]) => "(SELECT * FROM items)"
    #   DB[:items].literal(:x + 1 > :y) => "((x + 1) > y)"
    #
    # If an unsupported object is given, an +Error+ is raised.
    def literal_append(sql, v)
      case v
      when Symbol
        literal_symbol_append(sql, v)
      when String
        case v
        when LiteralString
          sql << v
        when SQL::Blob
          literal_blob_append(sql, v)
        else
          literal_string_append(sql, v)
        end
      when Integer
        sql << literal_integer(v)
      when Hash
        literal_hash_append(sql, v)
      when SQL::Expression
        literal_expression_append(sql, v)
      when Float
        sql << literal_float(v)
      when BigDecimal
        sql << literal_big_decimal(v)
      when NilClass
        sql << literal_nil
      when TrueClass
        sql << literal_true
      when FalseClass
        sql << literal_false
      when Array
        literal_array_append(sql, v)
      when Time
        sql << (v.is_a?(SQLTime) ? literal_sqltime(v) : literal_time(v))
      when DateTime
        sql << literal_datetime(v)
      when Date
        sql << literal_date(v)
      when Dataset
        literal_dataset_append(sql, v)
      else
        literal_other_append(sql, v)
      end
    end
    
    # Returns an array of insert statements for inserting multiple records.
    # This method is used by +multi_insert+ to format insert statements and
    # expects a keys array and and an array of value arrays.
    #
    # This method should be overridden by descendants if the support
    # inserting multiple records in a single SQL statement.
    def multi_insert_sql(columns, values)
      values.map{|r| insert_sql(columns, r)}
    end
    
    # Returns a SELECT SQL query string.
    #
    #   dataset.select_sql # => "SELECT * FROM items"
    def select_sql
      return static_sql(@opts[:sql]) if @opts[:sql]
      clause_sql(:select)
    end

    # Same as +select_sql+, not aliased directly to make subclassing simpler.
    def sql
      select_sql
    end
    
    # Returns a TRUNCATE SQL query string.  See +truncate+
    #
    #   DB[:items].truncate_sql # => 'TRUNCATE items'
    def truncate_sql
      if opts[:sql]
        static_sql(opts[:sql])
      else
        check_modification_allowed!
        raise(InvalidOperation, "Can't truncate filtered datasets") if opts[:where]
        _truncate_sql(source_list(opts[:from]))
      end
    end

    # Formats an UPDATE statement using the given values.  See +update+.
    #
    #   DB[:items].update_sql(:price => 100, :category => 'software')
    #   # => "UPDATE items SET price = 100, category = 'software'
    #
    # Raises an +Error+ if the dataset is grouped or includes more
    # than one table.
    def update_sql(values = {})
      return static_sql(opts[:sql]) if opts[:sql]
      check_modification_allowed!
      clone(:values=>values)._update_sql
    end
    
    # ---------------------
    # :section: 9 - Internal Methods relating to SQL Creation
    # These methods, while public, are not designed to be used directly by the end user.
    # ---------------------
    
    # Given a type (e.g. select) and an array of clauses,
    # return an array of methods to call to build the SQL string.
    def self.clause_methods(type, clauses)
      clauses.map{|clause| :"#{type}_#{clause}_sql"}.freeze
    end

    ALL = ' ALL'.freeze
    AND_SEPARATOR = " AND ".freeze
    APOS = "'".freeze
    APOS_RE = /'/.freeze
    ARRAY_EMPTY = '(NULL)'.freeze
    AS = ' AS '.freeze
    ASC = ' ASC'.freeze
    BACKSLASH_RE = /\\/.freeze
    BOOL_FALSE = "'f'".freeze
    BOOL_TRUE = "'t'".freeze
    BRACKET_CLOSE =  ']'.freeze
    BRACKET_OPEN = '['.freeze
    CASE_ELSE = " ELSE ".freeze
    CASE_END = " END)".freeze
    CASE_OPEN = '(CASE'.freeze
    CASE_THEN = " THEN ".freeze
    CASE_WHEN = " WHEN ".freeze
    CAST_OPEN = 'CAST('.freeze
    COLUMN_ALL = '.*'.freeze
    COLUMN_REF_RE1 = /\A((?:(?!__).)+)__((?:(?!___).)+)___(.+)\z/.freeze
    COLUMN_REF_RE2 = /\A((?:(?!___).)+)___(.+)\z/.freeze
    COLUMN_REF_RE3 = /\A((?:(?!__).)+)__(.+)\z/.freeze
    COMMA = ', '.freeze
    COMMA_SEPARATOR = COMMA
    CONDITION_FALSE = '(1 = 0)'.freeze
    CONDITION_TRUE = '(1 = 1)'.freeze
    COUNT_FROM_SELF_OPTS = [:distinct, :group, :sql, :limit, :compounds]
    COUNT_OF_ALL_AS_COUNT = SQL::Function.new(:count, LiteralString.new('*'.freeze)).as(:count)
    DATASET_ALIAS_BASE_NAME = 't'.freeze
    DEFAULT_VALUES = " DEFAULT VALUES".freeze
    DELETE = 'DELETE'.freeze
    DELETE_CLAUSE_METHODS = clause_methods(:delete, %w'delete from where')
    DESC = ' DESC'.freeze
    DISTINCT = " DISTINCT".freeze
    DOT = '.'.freeze
    DOUBLE_APOS = "''".freeze
    DOUBLE_QUOTE = '""'.freeze
    EQUAL = ' = '.freeze
    EXTRACT = 'extract('.freeze
    FOR_UPDATE = ' FOR UPDATE'.freeze
    FORMAT_DATE = "'%Y-%m-%d'".freeze
    FORMAT_DATE_STANDARD = "DATE '%Y-%m-%d'".freeze
    FORMAT_OFFSET = "%+03i%02i".freeze
    FORMAT_TIMESTAMP_RE = /%[Nz]/.freeze
    FORMAT_TIMESTAMP_USEC = ".%06d".freeze
    FORMAT_USEC = '%N'.freeze
    FRAME_ALL = "ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING".freeze
    FRAME_ROWS = "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW".freeze
    FROM = ' FROM '.freeze
    FUNCTION_EMPTY = '()'.freeze
    GROUP_BY = " GROUP BY ".freeze
    HAVING = " HAVING ".freeze
    INSERT = "INSERT".freeze
    INSERT_CLAUSE_METHODS = clause_methods(:insert, %w'insert into columns values')
    INTO = " INTO ".freeze
    IS_LITERALS = {nil=>'NULL'.freeze, true=>'TRUE'.freeze, false=>'FALSE'.freeze}.freeze
    IS_OPERATORS = ::Sequel::SQL::ComplexExpression::IS_OPERATORS
    LIMIT = " LIMIT ".freeze
    N_ARITY_OPERATORS = ::Sequel::SQL::ComplexExpression::N_ARITY_OPERATORS
    NOT_SPACE = 'NOT '.freeze
    NULL = "NULL".freeze
    NULLS_FIRST = " NULLS FIRST".freeze
    NULLS_LAST = " NULLS LAST".freeze
    OFFSET = " OFFSET ".freeze
    ON = ' ON '.freeze
    ON_PAREN = " ON (".freeze
    ORDER_BY = " ORDER BY ".freeze
    ORDER_BY_NS = "ORDER BY ".freeze
    OVER = ' OVER '.freeze
    PAREN_CLOSE = ')'.freeze
    PAREN_OPEN = '('.freeze
    PAREN_SPACE_OPEN = ' ('.freeze
    PARTITION_BY = "PARTITION BY ".freeze
    QUAD_BACKSLASH = "\\\\\\\\".freeze
    QUALIFY_KEYS = [:select, :where, :having, :order, :group]
    QUESTION_MARK = '?'.freeze
    QUESTION_MARK_RE = /\?/.freeze
    QUOTE = '"'.freeze
    QUOTE_RE = /"/.freeze
    RETURNING = " RETURNING ".freeze
    SELECT = 'SELECT'.freeze
    SELECT_CLAUSE_METHODS = clause_methods(:select, %w'with select distinct columns from join where group having compounds order limit lock')
    SET = ' SET '.freeze
    SPACE = ' '.freeze
    SQL_WITH = "WITH ".freeze
    TILDE = '~'.freeze
    TIMESTAMP_FORMAT = "'%Y-%m-%d %H:%M:%S%N%z'".freeze
    STANDARD_TIMESTAMP_FORMAT = "TIMESTAMP #{TIMESTAMP_FORMAT}".freeze
    TWO_ARITY_OPERATORS = ::Sequel::SQL::ComplexExpression::TWO_ARITY_OPERATORS
    UNDERSCORE = '_'.freeze
    UPDATE = 'UPDATE'.freeze
    UPDATE_CLAUSE_METHODS = clause_methods(:update, %w'update table set where')
    USING = ' USING ('.freeze
    VALUES = " VALUES ".freeze
    WHERE = " WHERE ".freeze
    WILDCARD = LiteralString.new('*').freeze

    PUBLIC_APPEND_METHODS = (<<-END).split.map{|x| x.to_sym}
      literal
      aliased_expression_sql
      array_sql
      boolean_constant_sql
      case_expression_sql
      cast_sql
      column_all_sql
      complex_expression_sql
      constant_sql
      function_sql
      join_clause_sql
      join_on_clause_sql
      join_using_clause_sql
      negative_boolean_constant_sql
      ordered_expression_sql
      placeholder_literal_string_sql
      qualified_identifier_sql
      quote_identifier
      quote_schema_table
      quoted_identifier
      subscript_sql
      window_sql
      window_function_sql
    END
    PRIVATE_APPEND_METHODS = (<<-END).split.map{|x| x.to_sym}
      argument_list
      as_sql
      column_list
      compound_dataset_sql
      expression_list
      literal_array
      literal_blob
      literal_dataset
      literal_expression
      literal_hash
      literal_other
      literal_string
      literal_symbol
      source_list
      subselect_sql
      table_ref
    END
    def self.def_append_methods(meths)
      meths.each do |meth|
        class_eval(<<-END, __FILE__, __LINE__ + 1)
          def #{meth}(*args, &block)
            s = ''
            #{meth}_append(s, *args, &block)
            s
          end
        END
      end
    end
    def_append_methods(PUBLIC_APPEND_METHODS + PRIVATE_APPEND_METHODS)
    private *PRIVATE_APPEND_METHODS

    # SQL fragment for AliasedExpression
    def aliased_expression_sql_append(sql, ae)
      literal_append(sql, ae.expression)
      as_sql_append(sql, ae.aliaz)
    end

    # SQL fragment for Array
    def array_sql_append(sql, a)
      if a.empty?
        sql << ARRAY_EMPTY
      else
        sql << PAREN_OPEN
        expression_list_append(sql, a)
        sql << PAREN_CLOSE
      end
    end

    # SQL fragment for BooleanConstants
    def boolean_constant_sql_append(sql, constant)
      if (constant == true || constant == false) && !supports_where_true?
        sql << (constant == true ? CONDITION_TRUE : CONDITION_FALSE)
      else
        literal_append(sql, constant)
      end
    end

    # SQL fragment for CaseExpression
    def case_expression_sql_append(sql, ce)
      sql << CASE_OPEN
      if ce.expression?
        sql << SPACE
        literal_append(sql, ce.expression)
      end
      w = CASE_WHEN
      t = CASE_THEN
      ce.conditions.each do |c,r|
        sql << w
        literal_append(sql, c)
        sql << t
        literal_append(sql, r)
      end
      sql << CASE_ELSE
      literal_append(sql, ce.default)
      sql << CASE_END
    end

    # SQL fragment for the SQL CAST expression
    def cast_sql_append(sql, expr, type)
      sql << CAST_OPEN
      literal_append(sql, expr)
      sql << AS << db.cast_type_literal(type).to_s
      sql << PAREN_CLOSE
    end

    # SQL fragment for specifying all columns in a given table
    def column_all_sql_append(sql, ca)
      quote_schema_table_append(sql, ca.table)
      sql << COLUMN_ALL
    end

    def complex_expression_sql_append(sql, op, args)
      case op
      when *IS_OPERATORS
        r = args.at(1)
        if r.nil? || supports_is_true?
          raise(InvalidOperation, 'Invalid argument used for IS operator') unless v = IS_LITERALS[r]
          sql << PAREN_OPEN
          literal_append(sql, args.at(0))
          sql << SPACE << op.to_s << SPACE
          sql << v << PAREN_CLOSE
        elsif op == :IS
          complex_expression_sql_append(sql, :"=", args)
        else
          complex_expression_sql_append(sql, :OR, [SQL::BooleanExpression.new(:"!=", *args), SQL::BooleanExpression.new(:IS, args.at(0), nil)])
        end
      when :IN, :"NOT IN"
        cols = args.at(0)
        vals = args.at(1)
        col_array = true if cols.is_a?(Array)
        if vals.is_a?(Array)
          val_array = true
          empty_val_array = vals == []
        end
        if empty_val_array
          literal_append(sql, empty_array_value(op, cols))
        elsif col_array
          if !supports_multiple_column_in?
            if val_array
              expr = SQL::BooleanExpression.new(:OR, *vals.to_a.map{|vs| SQL::BooleanExpression.from_value_pairs(cols.to_a.zip(vs).map{|c, v| [c, v]})})
              literal_append(sql, op == :IN ? expr : ~expr)
            else
              old_vals = vals
              vals = vals.naked if vals.is_a?(Sequel::Dataset)
              vals = vals.to_a
              val_cols = old_vals.columns
              complex_expression_sql_append(sql, op, [cols, vals.map!{|x| x.values_at(*val_cols)}])
            end
          else
            # If the columns and values are both arrays, use array_sql instead of
            # literal so that if values is an array of two element arrays, it
            # will be treated as a value list instead of a condition specifier.
            sql << PAREN_OPEN
            literal_append(sql, cols)
            sql << SPACE << op.to_s << SPACE
            if val_array
              array_sql_append(sql, vals)
            else
              literal_append(sql, vals)
            end
            sql << PAREN_CLOSE
          end
        else
          sql << PAREN_OPEN
          literal_append(sql, cols)
          sql << SPACE << op.to_s << SPACE
          literal_append(sql, vals)
          sql << PAREN_CLOSE
        end
      when *TWO_ARITY_OPERATORS
        sql << PAREN_OPEN
        literal_append(sql, args.at(0))
        sql << SPACE << op.to_s << SPACE
        literal_append(sql, args.at(1))
        sql << PAREN_CLOSE
      when *N_ARITY_OPERATORS
        sql << PAREN_OPEN
        c = false
        op_str = " #{op} "
        args.each do |a|
          sql << op_str if c
          literal_append(sql, a)
          c ||= true
        end
        sql << PAREN_CLOSE
      when :NOT
        sql << NOT_SPACE
        literal_append(sql, args.at(0))
      when :NOOP
        literal_append(sql, args.at(0))
      when :'B~'
        sql << TILDE
        literal_append(sql, args.at(0))
      when :extract
        sql << EXTRACT << args.at(0).to_s << FROM
        literal_append(sql, args.at(1))
        sql << PAREN_CLOSE
      else
        raise(InvalidOperation, "invalid operator #{op}")
      end
    end
    
    # SQL fragment for constants
    def constant_sql_append(sql, constant)
      sql << constant.to_s
    end

    # SQL fragment specifying an SQL function call
    def function_sql_append(sql, f)
      sql << f.f.to_s
      args = f.args
      if args.empty?
        sql << FUNCTION_EMPTY
      else
        literal_append(sql, args)
      end
    end

    # SQL fragment specifying a JOIN clause without ON or USING.
    def join_clause_sql_append(sql, jc)
      table = jc.table
      table_alias = jc.table_alias
      table_alias = nil if table == table_alias
      sql << SPACE << join_type_sql(jc.join_type) << SPACE
      table_ref_append(sql, table)
      as_sql_append(sql, table_alias) if table_alias
    end

    # SQL fragment specifying a JOIN clause with ON.
    def join_on_clause_sql_append(sql, jc)
      join_clause_sql_append(sql, jc)
      sql << ON
      literal_append(sql, filter_expr(jc.on))
    end

    # SQL fragment specifying a JOIN clause with USING.
    def join_using_clause_sql_append(sql, jc)
      join_clause_sql_append(sql, jc)
      sql << USING
      column_list_append(sql, jc.using)
      sql << PAREN_CLOSE
    end
    
    # SQL fragment for NegativeBooleanConstants
    def negative_boolean_constant_sql_append(sql, constant)
      sql << NOT_SPACE
      boolean_constant_sql_append(sql, constant)
    end

    # SQL fragment for the ordered expression, used in the ORDER BY
    # clause.
    def ordered_expression_sql_append(sql, oe)
      literal_append(sql, oe.expression)
      sql << (oe.descending ? DESC : ASC)
      case oe.nulls
      when :first
        sql << NULLS_FIRST
      when :last
        sql << NULLS_LAST
      end
    end

    # SQL fragment for a literal string with placeholders
    def placeholder_literal_string_sql_append(sql, pls)
      args = pls.args
      sql << PAREN_OPEN if pls.parens
      if args.is_a?(Hash)
        re = /:(#{args.keys.map{|k| Regexp.escape(k.to_s)}.join('|')})\b/
        if RUBY_VERSION >= '1.8.7'
          str = pls.str
          loop do
            previous, q, str = str.partition(re)
            sql << previous
            literal_append(sql, args[($1||q[1..-1].to_s).to_sym]) unless q.empty?
            break if str.empty?
          end
        else
          sql << pls.str.gsub(re){literal(args[$1.to_sym])}
        end
      else
        i = -1
        if RUBY_VERSION >= '1.8.7'
          str = pls.str
          loop do
            previous, q, str = str.partition(QUESTION_MARK)
            sql << previous
            literal_append(sql, args.at(i+=1)) unless q.empty?
            break if str.empty?
          end
        else
          sql << pls.str.gsub(QUESTION_MARK_RE){literal(args.at(i+=1))}
        end
      end
      sql << PAREN_CLOSE if pls.parens
    end

    # SQL fragment for the qualifed identifier, specifying
    # a table and a column (or schema and table).
    def qualified_identifier_sql_append(sql, qcr)
      case t = qcr.table
      when Symbol, SQL::QualifiedIdentifier, SQL::Identifier
        literal_append(sql, t) 
      else
        quote_identifier_append(sql, t)
      end
      sql << DOT
      case c = qcr.column
      when Symbol, SQL::QualifiedIdentifier, SQL::Identifier
        literal_append(sql, c) 
      else
        quote_identifier_append(sql, c)
      end
    end

    # Adds quoting to identifiers (columns and tables). If identifiers are not
    # being quoted, returns name as a string.  If identifiers are being quoted
    # quote the name with quoted_identifier.
    def quote_identifier_append(sql, name)
      if name.is_a?(LiteralString)
        sql << name
      else
        name = name.value if name.is_a?(SQL::Identifier)
        name = input_identifier(name)
        if quote_identifiers?
          quoted_identifier_append(sql, name)
        else
          sql << name
        end
      end
    end

    # Separates the schema from the table and returns a string with them
    # quoted (if quoting identifiers)
    def quote_schema_table_append(sql, table)
      schema, table = schema_and_table(table)
      if schema
        quote_identifier_append(sql, schema)
        sql << DOT
      end
      quote_identifier_append(sql, table)
    end

    # This method quotes the given name with the SQL standard double quote. 
    # should be overridden by subclasses to provide quoting not matching the
    # SQL standard, such as backtick (used by MySQL and SQLite).
    def quoted_identifier_append(sql, name)
      sql << QUOTE << name.to_s.gsub(QUOTE_RE, DOUBLE_QUOTE) << QUOTE
    end

    # Split the schema information from the table
    def schema_and_table(table_name)
      sch = db.default_schema if db
      case table_name
      when Symbol
        s, t, a = split_symbol(table_name)
        [s||sch, t]
      when SQL::QualifiedIdentifier
        [table_name.table, table_name.column]
      when SQL::Identifier
        [sch, table_name.value]
      when String
        [sch, table_name]
      else
        raise Error, 'table_name should be a Symbol, SQL::QualifiedIdentifier, SQL::Identifier, or String'
      end
    end

    # SQL fragment for specifying subscripts (SQL array accesses)
    def subscript_sql_append(sql, s)
      literal_append(sql, s.f)
      sql << BRACKET_OPEN
      expression_list_append(sql, s.sub)
      sql << BRACKET_CLOSE
    end

    # The SQL fragment for the given window's options.
    def window_sql_append(sql, opts)
      raise(Error, 'This dataset does not support window functions') unless supports_window_functions?
      sql << PAREN_OPEN
      window, part, order, frame = opts.values_at(:window, :partition, :order, :frame)
      space = false
      space_s = SPACE
      if window
        literal_append(sql, window)
        space = true
      end
      if part
        sql << space_s if space
        sql << PARTITION_BY
        expression_list_append(sql, Array(part))
        space = true
      end
      if order
        sql << space_s if space
        sql << ORDER_BY_NS
        expression_list_append(sql, Array(order))
        space = true
      end
      case frame
        when nil
          # nothing
        when :all
          sql << space_s if space
          sql << FRAME_ALL
        when :rows
          sql << space_s if space
          sql << FRAME_ROWS
        when String
          sql << space_s if space
          sql << frame
        else
          raise Error, "invalid window frame clause, should be :all, :rows, a string, or nil"
      end
      sql << PAREN_CLOSE
    end

    # The SQL fragment for the given window function's function and window.
    def window_function_sql_append(sql, function, window)
      literal_append(sql, function)
      sql << OVER
      literal_append(sql, window)
    end

    protected

    # Formats in INSERT statement using the stored columns and values.
    def _insert_sql
      clause_sql(:insert)
    end

    # Formats an UPDATE statement using the stored values.
    def _update_sql
      clause_sql(:update)
    end

    # Return a from_self dataset if an order or limit is specified, so it works as expected
    # with UNION, EXCEPT, and INTERSECT clauses.
    def compound_from_self
      (@opts[:limit] || @opts[:order]) ? from_self : self
    end
    
    private

    # Formats the truncate statement.  Assumes the table given has already been
    # literalized.
    def _truncate_sql(table)
      "TRUNCATE TABLE #{table}"
    end

    # Returns an appropriate symbol for the alias represented by s.
    def alias_alias_symbol(s)
      case s
      when Symbol
        s
      when String
        s.to_sym
      when SQL::Identifier
        s.value.to_s.to_sym
      else
        raise Error, "Invalid alias for alias_alias_symbol: #{s.inspect}"
      end
    end

    # Returns an appropriate alias symbol for the given object, which can be
    # a Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier, or
    # SQL::AliasedExpression.
    def alias_symbol(sym)
      case sym
      when Symbol
        s, t, a = split_symbol(sym)
        a || s ? (a || t).to_sym : sym
      when String
        sym.to_sym
      when SQL::Identifier
        sym.value.to_s.to_sym
      when SQL::QualifiedIdentifier
        alias_symbol(sym.column)
      when SQL::AliasedExpression
        alias_alias_symbol(sym.aliaz)
      else
        raise Error, "Invalid alias for alias_symbol: #{sym.inspect}"
      end
    end

    # Clone of this dataset usable in aggregate operations.  Does
    # a from_self if dataset contains any parameters that would
    # affect normal aggregation, or just removes an existing
    # order if not.
    def aggregate_dataset
      options_overlap(COUNT_FROM_SELF_OPTS) ? from_self : unordered
    end

    def argument_list_append(sql, args)
      c = false
      comma = COMMA
      args.each do |a|
        sql << comma if c
        sql << a.to_s
        c ||= true
      end
    end

    # SQL fragment for specifying an alias.  expression should already be literalized.
    def as_sql_append(sql, aliaz)
      sql << AS
      quote_identifier_append(sql, aliaz)
    end
    
    # Raise an InvalidOperation exception if deletion is not allowed
    # for this dataset
    def check_modification_allowed!
      raise(InvalidOperation, "Grouped datasets cannot be modified") if opts[:group]
      raise(InvalidOperation, "Joined datasets cannot be modified") if !supports_modifying_joins? && joined_dataset?
    end

    # Prepare an SQL statement by calling all clause methods for the given statement type.
    def clause_sql(type)
      sql = @opts[:append_sql] || ''
      send("#{type}_clause_methods").each{|x| send(x, sql)}
      sql
    end

    # Converts an array of column names into a comma seperated string of 
    # column names. If the array is empty, a wildcard (*) is returned.
    def column_list_append(sql, columns)
      if (columns.nil? || columns.empty?)
        sql << WILDCARD
      else
        expression_list_append(sql, columns)
      end
    end

    # Yield each two pair of arguments to the block, which should
    # return a string representing the SQL code for those
    # two arguments.  If more than 2 arguments are provided, all
    # calls to the block # after the first will have a LiteralString
    # as the first argument, representing the application of the block to
    # the previous arguments.
    def complex_expression_arg_pairs(args)
      case args.length
      when 1
        literal(args.at(0))
      when 2
        yield args.at(0), args.at(1)
      else
        args.inject{|m, a| LiteralString.new(yield(m, a))}
      end
    end

    # The SQL to use for the dataset used in a UNION/INTERSECT/EXCEPT clause. 
    def compound_dataset_sql_append(sql, ds)
      subselect_sql_append(sql, ds)
    end

    # The alias to use for datasets, takes a number to make sure the name is unique.
    def dataset_alias(number)
      :"#{DATASET_ALIAS_BASE_NAME}#{number}"
    end
    
    # The strftime format to use when literalizing the time.
    def default_timestamp_format
      requires_sql_standard_datetimes? ? STANDARD_TIMESTAMP_FORMAT : TIMESTAMP_FORMAT
    end

    # The order of methods to call to build the DELETE SQL statement
    def delete_clause_methods
      DELETE_CLAUSE_METHODS
    end

    def delete_delete_sql(sql)
      sql << DELETE
    end

    # Converts an array of expressions into a comma separated string of
    # expressions.
    def expression_list_append(sql, columns)
      c = false
      co = COMMA
      columns.each do |col|
        sql << co if c
        literal_append(sql, col)
        c ||= true
      end
    end
    
    def empty_array_value(op, cols)
      if Sequel.empty_array_handle_nulls
        c = Array(cols)
        SQL::BooleanExpression.from_value_pairs(c.zip(c), :AND, op == :IN)
      else
        {1 => ((op == :IN) ? 0 : 1)}
      end
    end
    
    # Format the timestamp based on the default_timestamp_format, with a couple
    # of modifiers.  First, allow %N to be used for fractions seconds (if the
    # database supports them), and override %z to always use a numeric offset
    # of hours and minutes.
    def format_timestamp(v)
      v2 = db.from_application_timestamp(v)
      fmt = default_timestamp_format.gsub(FORMAT_TIMESTAMP_RE) do |m|
        if m == FORMAT_USEC
          format_timestamp_usec(v.is_a?(DateTime) ? v.sec_fraction*(RUBY_VERSION < '1.9.0' ? 86400000000 : 1000000) : v.usec) if supports_timestamp_usecs?
        else
          if supports_timestamp_timezones?
            # Would like to just use %z format, but it doesn't appear to work on Windows
            # Instead, the offset fragment is constructed manually
            minutes = (v2.is_a?(DateTime) ? v2.offset * 1440 : v2.utc_offset/60).to_i
            format_timestamp_offset(*minutes.divmod(60))
          end
        end
      end
      v2.strftime(fmt)
    end
    
    # Return the SQL timestamp fragment to use for the timezone offset.
    def format_timestamp_offset(hour, minute)
      sprintf(FORMAT_OFFSET, hour, minute)
    end

    # Return the SQL timestamp fragment to use for the fractional time part.
    # Should start with the decimal point.  Uses 6 decimal places by default.
    def format_timestamp_usec(usec)
      sprintf(FORMAT_TIMESTAMP_USEC, usec)
    end

    # Modify the identifier returned from the database based on the
    # identifier_output_method.
    def input_identifier(v)
      (i = identifier_input_method) ? v.to_s.send(i) : v.to_s
    end

    # SQL fragment specifying the table to insert INTO
    def insert_into_sql(sql)
      sql << INTO
      source_list_append(sql, @opts[:from])
    end

    # The order of methods to call to build the INSERT SQL statement
    def insert_clause_methods
      INSERT_CLAUSE_METHODS
    end

    # SQL fragment specifying the columns to insert into
    def insert_columns_sql(sql)
      columns = opts[:columns]
      if columns && !columns.empty?
        sql << PAREN_SPACE_OPEN
        c = false
        co = COMMA
        columns.each do |col|
          sql << co if c
          if col.is_a?(String) && !col.is_a?(LiteralString)
            quote_identifier_append(sql, col)
          else
            literal_append(sql, col)
          end
          c ||= true
        end
        sql << PAREN_CLOSE
      end 
    end

    def insert_insert_sql(sql)
      sql << INSERT
    end

    # SQL fragment specifying the values to insert.
    def insert_values_sql(sql)
      case values = opts[:values]
      when Array
        if values.empty?
          sql << DEFAULT_VALUES
        else
          sql << VALUES
          literal_append(sql, values)
        end
      when Dataset
        sql << SPACE
        subselect_sql_append(sql, values)
      when LiteralString
        sql << SPACE << values
      else
        raise Error, "Unsupported INSERT values type, should be an Array or Dataset: #{values.inspect}"
      end
    end

    # SQL fragment specifying the values to return.
    def insert_returning_sql(sql)
      if opts.has_key?(:returning)
        sql << RETURNING
        column_list_append(sql, Array(opts[:returning]))
      end
    end
    alias delete_returning_sql insert_returning_sql
    alias update_returning_sql insert_returning_sql

    # SQL fragment specifying a JOIN type, converts underscores to
    # spaces and upcases.
    def join_type_sql(join_type)
      "#{join_type.to_s.gsub(UNDERSCORE, SPACE).upcase} JOIN"
    end

    # Whether this dataset is a joined dataset
    def joined_dataset?
     (opts[:from].is_a?(Array) && opts[:from].size > 1) || opts[:join]
    end

    # SQL fragment for Array.  Treats as an expression if an array of all two pairs, or as a SQL array otherwise.
    def literal_array_append(sql, v)
      if Sequel.condition_specifier?(v)
        literal_expression_append(sql, SQL::BooleanExpression.from_value_pairs(v))
      else
        array_sql_append(sql, v)
      end
    end

    # SQL fragment for BigDecimal
    def literal_big_decimal(v)
      d = v.to_s("F")
      v.nan? || v.infinite? ?  "'#{d}'" : d
    end

    # SQL fragment for SQL::Blob
    def literal_blob_append(sql, v)
      literal_string_append(sql, v)
    end

    # SQL fragment for Dataset.  Does a subselect inside parantheses.
    def literal_dataset_append(sql, v)
      sql << PAREN_OPEN
      subselect_sql_append(sql, v)
      sql << PAREN_CLOSE
    end

    # SQL fragment for Date, using the ISO8601 format.
    def literal_date(v)
      if requires_sql_standard_datetimes?
        v.strftime(FORMAT_DATE_STANDARD)
      else
        v.strftime(FORMAT_DATE)
      end
    end

    # SQL fragment for DateTime
    def literal_datetime(v)
      format_timestamp(v)
    end

    # SQL fragment for SQL::Expression, result depends on the specific type of expression.
    def literal_expression_append(sql, v)
      v.to_s_append(self, sql)
    end

    # SQL fragment for false
    def literal_false
      BOOL_FALSE
    end

    # SQL fragment for Float
    def literal_float(v)
      v.to_s
    end

    # SQL fragment for Hash, treated as an expression
    def literal_hash_append(sql, v)
      literal_expression_append(sql, SQL::BooleanExpression.from_value_pairs(v))
    end

    # SQL fragment for Integer
    def literal_integer(v)
      v.to_s
    end
    
    # SQL fragment for nil
    def literal_nil
      NULL
    end

    # SQL fragment for a type of object not handled by Dataset#literal.
    # Calls +sql_literal+ if object responds to it, otherwise raises an error.
    # Classes implementing +sql_literal+ should call a class-specific method on the dataset
    # provided and should add that method to Sequel::Dataset, allowing for adapters
    # to provide customized literalizations.
    # If a database specific type is allowed, this should be overriden in a subclass.
    def literal_other_append(sql, v)
      if v.respond_to?(:sql_literal_append)
        v.sql_literal_append(self, sql)
      elsif v.respond_to?(:sql_literal)
        sql << v.sql_literal(self)
      else
        raise Error, "can't express #{v.inspect} as a SQL literal"
      end
    end

    # SQL fragment for Sequel::SQLTime, containing just the time part
    def literal_sqltime(v)
      v.strftime("'%H:%M:%S#{format_timestamp_usec(v.usec) if supports_timestamp_usecs?}'")
    end

    # SQL fragment for String.  Doubles \ and ' by default.
    def literal_string_append(sql, v)
      sql << APOS << v.gsub(BACKSLASH_RE, QUAD_BACKSLASH).gsub(APOS_RE, DOUBLE_APOS) << APOS
    end

    # Converts a symbol into a column name. This method supports underscore
    # notation in order to express qualified (two underscores) and aliased
    # (three underscores) columns:
    #
    #   dataset.literal(:abc) #=> "abc"
    #   dataset.literal(:abc___a) #=> "abc AS a"
    #   dataset.literal(:items__abc) #=> "items.abc"
    #   dataset.literal(:items__abc___a) #=> "items.abc AS a"
    def literal_symbol_append(sql, v)
      c_table, column, c_alias = split_symbol(v)
      if c_table
        quote_identifier_append(sql, c_table)
        sql << DOT
      end
      quote_identifier_append(sql, column)
      as_sql_append(sql, c_alias) if c_alias
    end

    # SQL fragment for Time
    def literal_time(v)
      format_timestamp(v)
    end

    # SQL fragment for true
    def literal_true
      BOOL_TRUE
    end

    # Returns a qualified column name (including a table name) if the column
    # name isn't already qualified.
    def qualified_column_name(column, table)
      if Symbol === column 
        c_table, column, c_alias = split_symbol(column)
        unless c_table
          case table
          when Symbol
            schema, table, t_alias = split_symbol(table)
            t_alias ||= Sequel::SQL::QualifiedIdentifier.new(schema, table) if schema
          when Sequel::SQL::AliasedExpression
            t_alias = table.aliaz
          end
          c_table = t_alias || table
        end
        ::Sequel::SQL::QualifiedIdentifier.new(c_table, column)
      else
        column
      end
    end
    
    # Qualify the given expression e to the given table.
    def qualified_expression(e, table)
      Qualifier.new(self, table).transform(e)
    end

    # The order of methods to call to build the SELECT SQL statement
    def select_clause_methods
      SELECT_CLAUSE_METHODS
    end

    # Modify the sql to add the columns selected
    def select_columns_sql(sql)
      sql << SPACE
      column_list_append(sql, @opts[:select])
    end

    # Modify the sql to add the DISTINCT modifier
    def select_distinct_sql(sql)
      if distinct = @opts[:distinct]
        sql << DISTINCT
        unless distinct.empty?
          sql << ON_PAREN
          expression_list_append(sql, distinct)
          sql << PAREN_CLOSE
        end
      end
    end

    # Modify the sql to add a dataset to the via an EXCEPT, INTERSECT, or UNION clause.
    # This uses a subselect for the compound datasets used, because using parantheses doesn't
    # work on all databases.  I consider this an ugly hack, but can't I think of a better default.
    def select_compounds_sql(sql)
      return unless c = @opts[:compounds]
      c.each do |type, dataset, all|
        sql << SPACE << type.to_s.upcase
        sql << ALL if all
        sql << SPACE
        compound_dataset_sql_append(sql, dataset)
      end
    end

    # Modify the sql to add the list of tables to select FROM
    def select_from_sql(sql)
      if f = @opts[:from]
        sql << FROM
        source_list_append(sql, f)
      end
    end
    alias delete_from_sql select_from_sql

    # Modify the sql to add the expressions to GROUP BY
    def select_group_sql(sql)
      if group = @opts[:group]
        sql << GROUP_BY
        if go = @opts[:group_options]
          sql << go.to_s.upcase
          sql << PAREN_OPEN
        end
        expression_list_append(sql, group)
        sql << PAREN_CLOSE if go
      end
    end

    # Modify the sql to add the filter criteria in the HAVING clause
    def select_having_sql(sql)
      if having = @opts[:having]
        sql << HAVING
        literal_append(sql, having)
      end
    end

    # Modify the sql to add the list of tables to JOIN to
    def select_join_sql(sql)
      if js = @opts[:join]
        js.each{|j| literal_append(sql, j)}
      end
    end

    # Modify the sql to limit the number of rows returned and offset
    def select_limit_sql(sql)
      if l = @opts[:limit]
        sql << LIMIT
        literal_append(sql, l)
      end
      if o = @opts[:offset]
        sql << OFFSET
        literal_append(sql, o)
      end
    end
  
    # Modify the sql to support the different types of locking modes.
    def select_lock_sql(sql)
      case l = @opts[:lock]
      when :update
        sql << FOR_UPDATE
      when String
        sql << SPACE << l
      end
    end

    # Modify the sql to add the expressions to ORDER BY
    def select_order_sql(sql)
      if o = @opts[:order]
        sql << ORDER_BY
        expression_list_append(sql, o)
      end
    end
    alias delete_order_sql select_order_sql
    alias update_order_sql select_order_sql

    def select_select_sql(sql)
      sql << SELECT
    end

    # Modify the sql to add the filter criteria in the WHERE clause
    def select_where_sql(sql)
      if w = @opts[:where]
        sql << WHERE
        literal_append(sql, w)
      end
    end
    alias delete_where_sql select_where_sql
    alias update_where_sql select_where_sql
    
    # SQL Fragment specifying the WITH clause
    def select_with_sql(sql)
      ws = opts[:with]
      return if !ws || ws.empty?
      sql << select_with_sql_base
      c = false
      comma = COMMA
      ws.each do |w|
        sql << comma if c
        quote_identifier_append(sql, w[:name])
        if args = w[:args]
         sql << PAREN_OPEN
         argument_list_append(sql, args)
         sql << PAREN_CLOSE
        end
        sql << AS
        literal_dataset_append(sql, w[:dataset])
        c ||= true
      end
      sql << SPACE
    end
    alias delete_with_sql select_with_sql
    alias insert_with_sql select_with_sql
    alias update_with_sql select_with_sql
    
    # The base keyword to use for the SQL WITH clause
    def select_with_sql_base
      SQL_WITH
    end

    # Converts an array of source names into into a comma separated list.
    def source_list_append(sql, sources)
      raise(Error, 'No source specified for query') if sources.nil? || sources == []
      c = false
      co = COMMA
      sources.each do |s|
        sql << co if c
        table_ref_append(sql, s)
        c ||= true
      end
    end
    
    # Splits the symbol into three parts.  Each part will
    # either be a string or nil.
    #
    # For columns, these parts are the table, column, and alias.
    # For tables, these parts are the schema, table, and alias.
    def split_symbol(sym)
      case s = sym.to_s
      when COLUMN_REF_RE1
        [$1, $2, $3]
      when COLUMN_REF_RE2
        [nil, $1, $2]
      when COLUMN_REF_RE3
        [$1, $2, nil]
      else
        [nil, s, nil]
      end
    end

    # SQL to use if this dataset uses static SQL.  Since static SQL
    # can be a PlaceholderLiteralString in addition to a String,
    # we literalize nonstrings.
    def static_sql(sql)
      if append_sql = @opts[:append_sql]
        if sql.is_a?(String)
          append_sql << sql
        else
          literal_append(append_sql, sql)
        end
      else
        if sql.is_a?(String)
          sql
        else
          literal(sql)
        end
      end
    end

    # SQL fragment for a subselect using the given database's SQL.
    def subselect_sql_append(sql, ds)
      ds.clone(:append_sql=>sql).sql
    end

    # SQL fragment specifying a table name.
    def table_ref_append(sql, t)
      if t.is_a?(String)
        quote_identifier_append(sql, t)
      else
        literal_append(sql, t)
      end
    end
    alias identifier_append table_ref_append
    
    # The order of methods to call to build the UPDATE SQL statement
    def update_clause_methods
      UPDATE_CLAUSE_METHODS
    end

    # SQL fragment specifying the tables from with to delete.
    # Includes join table if modifying joins is allowed.
    def update_table_sql(sql)
      sql << SPACE
      source_list_append(sql, @opts[:from])
      select_join_sql(sql) if supports_modifying_joins?
    end

    # The SQL fragment specifying the columns and values to SET.
    def update_set_sql(sql)
      values = opts[:values]
      sql << SET
      if values.is_a?(Hash)
        values = opts[:defaults].merge(values) if opts[:defaults]
        values = values.merge(opts[:overrides]) if opts[:overrides]
        c = false
        eq = EQUAL
        values.each do |k, v|
          sql << COMMA if c
          if k.is_a?(String) && !k.is_a?(LiteralString)
            quote_identifier_append(sql, k)
          else
            literal_append(sql, k)
          end
          sql << eq
          literal_append(sql, v)
          c ||= true
        end
      else
        sql << values
      end
    end

    def update_update_sql(sql)
      sql << UPDATE
    end
  end
end