1 module dpq.querybuilder; 2 3 import dpq.value; 4 import dpq.query; 5 import dpq.connection; 6 import dpq.attributes; 7 import dpq.column; 8 9 import std.typecons; 10 import std..string; 11 12 import std.algorithm : map; 13 14 version (unittest) import std.stdio; 15 16 17 enum Order : string 18 { 19 asc = "ASC", 20 desc = "DESC" 21 }; 22 23 private enum QueryType 24 { 25 select = "SELECT", 26 update = "UPDATE", 27 insert = "INSERT", 28 delete_ = "DELETE" 29 } 30 31 struct QueryBuilder 32 { 33 private 34 { 35 string[] _columns; 36 string _table; 37 string _filter; 38 39 string[] _orderBy; 40 Order[] _orders; 41 42 int _limit = -1; 43 44 Nullable!int _offset; 45 Value[string] _params; 46 Value[] _indexParams; 47 48 string[] _returning; 49 50 // UPDATE's SET 51 string[] _set; 52 53 int _paramIndex = 0; 54 QueryType _type; 55 Connection* _connection; 56 } 57 58 this(ref Connection connection) 59 { 60 _connection = &connection; 61 } 62 63 void opIndexAssign(T)(T val, string key) 64 { 65 _params[key] = val; 66 } 67 68 unittest 69 { 70 writeln(" * QueryBuilder"); 71 writeln("\t * opIndexAssign"); 72 73 QueryBuilder qb; 74 qb["foo"] = 123; 75 qb["bar"] = "456"; 76 77 assert(qb._params["foo"] == Value(123)); 78 assert(qb._params["bar"] == Value("456")); 79 } 80 81 // SELECT methods 82 ref QueryBuilder select(string[] cols...) 83 { 84 _columns = cols; 85 _type = QueryType.select; 86 return this; 87 } 88 89 90 ref QueryBuilder select(Column[] cols...) 91 { 92 _type = QueryType.select; 93 _columns = []; 94 foreach(col; cols) 95 { 96 if (col.column != col.asName) 97 _columns ~= "%s AS %s".format(col.column, col.asName); 98 else 99 _columns ~= col.column; 100 } 101 102 return this; 103 } 104 105 unittest 106 { 107 writeln("\t * select"); 108 109 QueryBuilder qb; 110 qb.select("foo", "bar", "baz"); 111 assert(qb._columns == ["foo", "bar", "baz"]); 112 113 Column[] cs = [Column("foo", "foo_test"), Column("bar")]; 114 qb.select(cs); 115 assert(qb._columns == ["foo AS foo_test", "bar"]); 116 } 117 118 ref QueryBuilder from(string from) 119 { 120 assert( 121 _type == QueryType.select || _type == QueryType.delete_, 122 "QueryBuilder.from() can only be used for SELECT or DELETE queries."); 123 124 _table = from; 125 return this; 126 } 127 128 ref QueryBuilder from(T)() 129 { 130 return from(relationName!T); 131 } 132 133 unittest 134 { 135 writeln("\t\t * from"); 136 QueryBuilder qb; 137 138 qb.from("sometable"); 139 assert(qb._table == "sometable"); 140 141 struct Test {} 142 qb.from!Test; 143 assert(qb._table == "test"); 144 } 145 146 ref QueryBuilder where(string filter) 147 { 148 _filter = filter; 149 return this; 150 } 151 152 ref QueryBuilder where(T)(string col, T val) 153 { 154 _params["__where_filt"] = Value(val); 155 _filter = "%s = {__where_filt}".format(col); 156 157 return this; 158 } 159 160 unittest 161 { 162 writeln("\t\t * where"); 163 164 string str = "a = $1 AND b = $2"; 165 QueryBuilder qb; 166 qb.where(str); 167 assert(qb._filter == str); 168 169 qb.where("some_field", 1); 170 assert(qb._filter == "some_field = {__where_filt}"); 171 assert(qb._params["__where_filt"] == Value(1)); 172 } 173 174 ref QueryBuilder order(string col, Order order) 175 { 176 assert(_type == QueryType.select, "QueryBuilder.order() can only be used for SELECT queries."); 177 _orderBy ~= col; 178 _orders ~= order; 179 return this; 180 } 181 182 unittest 183 { 184 writeln("\t\t * order"); 185 186 QueryBuilder qb; 187 188 qb.order("some_col", Order.asc); 189 190 assert(qb._orderBy[0] == "some_col"); 191 assert(qb._orders[0] == Order.asc); 192 193 qb.order("some_other_col", Order.desc); 194 195 assert(qb._orderBy[1] == "some_other_col"); 196 assert(qb._orders[1] == Order.desc); 197 } 198 199 ref QueryBuilder limit(int limit) 200 { 201 assert(_type == QueryType.select, "QueryBuilder.limit() can only be used for SELECT queries."); 202 203 _limit = limit; 204 return this; 205 } 206 207 unittest 208 { 209 writeln("\t\t * limit"); 210 211 QueryBuilder qb; 212 qb.limit(1); 213 assert(qb._limit == 1); 214 } 215 216 ref QueryBuilder offset(int offset) 217 { 218 assert(_type == QueryType.select, "QueryBuilder.offset() can only be used for SELECT queries."); 219 _offset = offset; 220 return this; 221 } 222 223 unittest 224 { 225 writeln("\t\t * offset"); 226 227 QueryBuilder qb; 228 qb.offset(1); 229 assert(qb._offset == 1); 230 } 231 232 // UPDATE methods 233 ref QueryBuilder update(string table) 234 { 235 _table = table; 236 _type = QueryType.update; 237 return this; 238 } 239 240 ref QueryBuilder update(T)() 241 { 242 return update(relationName!T); 243 } 244 245 unittest 246 { 247 QueryBuilder qb; 248 qb.update("sometable"); 249 250 assert(qb._table == "sometable"); 251 assert(qb._type == QueryType.update); 252 253 struct Test {} 254 255 qb.update!Test; 256 assert(qb._type == QueryType.update); 257 assert(qb._table == relationName!Test); 258 } 259 260 ref QueryBuilder set(Value[string] params) 261 { 262 foreach (col, val; params) 263 set(col, val); 264 265 return this; 266 } 267 268 ref QueryBuilder set(T)(string col, T val) 269 { 270 assert(_type == QueryType.update, "QueryBuilder.set() can only be used on UPDATE queries"); 271 272 _params[col] = val; 273 _set ~= "\"%s\" = {%s}".format(col, col); 274 275 return this; 276 } 277 278 ref QueryBuilder set(string set) 279 { 280 _set ~= set; 281 282 return this; 283 } 284 285 unittest 286 { 287 writeln("\t * set"); 288 289 QueryBuilder qb; 290 qb.update("foo") 291 .set("some_col", 1); 292 293 assert(qb._params["some_col"] == Value(1)); 294 assert(qb._set.length == 1); 295 assert(qb._set[0] == "\"some_col\" = {some_col}"); 296 297 qb.set([ 298 "col1": Value(1), 299 "col2": Value(2)]); 300 301 assert(qb._params.length == 3); 302 assert(qb._set.length == 3); 303 assert(qb._set[1] == "\"col1\" = {col1}"); 304 assert(qb._set[2] == "\"col2\" = {col2}"); 305 306 string str = "asd = $1"; 307 qb.set(str); 308 assert(qb._params.length == 3); 309 assert(qb._set.length == 4); 310 assert(qb._set[3] == str); 311 } 312 313 // INSERT methods 314 ref QueryBuilder insert(string table, string[] cols...) 315 { 316 _table = table; 317 _columns = cols; 318 _type = QueryType.insert; 319 return this; 320 } 321 322 323 ref QueryBuilder insert(string table, Column[] cols...) 324 { 325 import std.array; 326 return insert(table, array(cols.map!(c => c.column))); 327 } 328 329 unittest 330 { 331 writeln("\t * insert"); 332 333 QueryBuilder qb; 334 qb.insert("table", "col1", "col2"); 335 336 assert(qb._type == QueryType.insert); 337 assert(qb._table == "table"); 338 assert(qb._columns == ["col1", "col2"]); 339 340 Column[] cs = [ 341 Column("some_col", "stupid_as_name"), 342 Column("qwe")]; 343 344 qb.insert("table2", cs); 345 assert(qb._table == "table2"); 346 assert(qb._columns.length == 2); 347 assert(qb._columns == ["some_col", "qwe"]); 348 } 349 350 ref QueryBuilder values(T...)(T vals) 351 { 352 assert(_type == QueryType.insert, "QueryBuilder.values() can only be used on INSERT queries"); 353 354 foreach (val; vals) 355 addValue(val); 356 357 return this; 358 } 359 360 ref QueryBuilder values(Value[] vals) 361 { 362 assert(_type == QueryType.insert, "QueryBuilder.values() can only be used on INSERT queries"); 363 364 foreach (val; vals) 365 addValue(val); 366 367 return this; 368 } 369 370 unittest 371 { 372 writeln("\t * values"); 373 374 QueryBuilder qb; 375 qb.insert("table", "col") 376 .values(1, 2, 3); 377 378 assert(qb._type == QueryType.insert); 379 assert(qb._indexParams.length == 3); 380 assert(qb._indexParams == [Value(1), Value(2), Value(3)]); 381 382 qb.values([Value(4), Value(5)]); 383 assert(qb._indexParams.length == 5); 384 assert(qb._indexParams == [Value(1), Value(2), Value(3), Value(4), Value(5)]); 385 } 386 387 ref QueryBuilder remove() 388 { 389 _type = QueryType.delete_; 390 return this; 391 } 392 393 ref QueryBuilder remove(string table) 394 { 395 from(table); 396 return remove(); 397 } 398 399 ref QueryBuilder remove(T)() 400 { 401 return remove(relationName!T); 402 } 403 404 ref QueryBuilder returning(string[] ret...) 405 { 406 foreach (r; ret) 407 _returning ~= r; 408 409 return this; 410 } 411 412 unittest 413 { 414 writeln("\t * remove"); 415 416 struct Test {} 417 QueryBuilder qb; 418 qb.remove!Test; 419 420 assert(qb._type == QueryType.delete_); 421 assert(qb._table == relationName!Test); 422 } 423 424 ref QueryBuilder addValue(T)(T val) 425 { 426 _indexParams ~= Value(val); 427 return this; 428 } 429 430 // Other stuff 431 432 private string replaceParams(string str) 433 { 434 int index = _paramIndex; 435 436 foreach (param, val; _params) 437 str = str.replace("{" ~ param ~ "}", "$%d".format(++index)); 438 439 return str; 440 } 441 442 unittest 443 { 444 writeln("\t * replaceParams"); 445 QueryBuilder qb; 446 string str = "SELECT {foo} FROM table WHERE id = {bar} AND name = '{baz}'"; 447 qb["foo"] = "a"; 448 qb["bar"] = "b"; 449 450 str = qb.replaceParams(str); 451 452 // No idea what the order might be 453 assert( 454 str == "SELECT $1 FROM table WHERE id = $2 AND name = '{baz}'" || 455 str == "SELECT $2 FROM table WHERE id = $1 AND name = '{baz}'"); 456 } 457 458 private string selectCommand() 459 { 460 string cols; 461 if (_columns.length == 0) 462 cols = "*"; 463 else 464 cols = _columns.join(", "); 465 466 string str = "SELECT %s FROM \"%s\"".format(cols, _table); 467 468 if (_filter.length > 0) 469 str ~= " WHERE " ~ _filter; 470 471 if (_orderBy.length > 0) 472 { 473 str ~= " ORDER BY "; 474 for (int i = 0; i < _orderBy.length; ++i) 475 str ~= "\"" ~ _orderBy[i] ~ "\" " ~ _orders[i] ~ ", "; 476 str = str[0 .. $ - 2]; 477 } 478 479 if (_limit != -1) 480 str ~= " LIMIT %d".format(_limit); 481 482 if (!_offset.isNull) 483 str ~= " OFFSET %d".format(_offset); 484 485 return replaceParams(str); 486 } 487 488 unittest 489 { 490 writeln("\t * selectCommand"); 491 492 QueryBuilder qb; 493 qb.select("col") 494 .from("table") 495 .where("id", 1) 496 .limit(1) 497 .offset(1); 498 499 string str = qb.command(); 500 assert(str == `SELECT col FROM "table" WHERE id = $1 LIMIT 1 OFFSET 1`, str); 501 } 502 503 private string insertCommand() 504 { 505 int index = 0; 506 string str = "INSERT INTO \"%s\" (%s) VALUES (%s)".format( 507 _table, 508 _columns.join(","), 509 _indexParams.map!(v => "$%d".format(++index)).join(", ") 510 ); 511 512 if (_returning.length > 0) 513 { 514 str ~= " RETURNING "; 515 str ~= _returning.join(", "); 516 } 517 518 return str; 519 } 520 521 unittest 522 { 523 writeln("\t * insertCommand"); 524 525 QueryBuilder qb; 526 qb.insert("table", "col") 527 .values(1) 528 .returning("id"); 529 530 string str = qb.command(); 531 assert(str == `INSERT INTO "table" (col) VALUES ($1) RETURNING id`); 532 } 533 534 private string updateCommand() 535 { 536 string str = "UPDATE \"%s\" SET %s".format( 537 _table, 538 _set.join(", ")); 539 540 if (_filter.length > 0) 541 str ~= " WHERE " ~ _filter; 542 543 if (_returning.length > 0) 544 { 545 str ~= " RETURNING "; 546 str ~= _returning.join(", "); 547 } 548 549 return replaceParams(str); 550 } 551 552 unittest 553 { 554 writeln("\t * updateCommand"); 555 556 QueryBuilder qb; 557 qb.update("table") 558 .set("col", 1) 559 .where("foo", 2) 560 .returning("id"); 561 562 string str = qb.command(); 563 assert( 564 str == `UPDATE "table" SET "col" = $1 WHERE foo = $2 RETURNING id` || 565 str == `UPDATE "table" SET "col" = $2 WHERE foo = $1 RETURNING id`); 566 } 567 568 private string deleteCommand() 569 { 570 string str = "DELETE FROM \"%s\"".format(_table); 571 572 if (_filter.length > 0) 573 str ~= " WHERE " ~ _filter; 574 575 if (_returning.length > 0) 576 { 577 str ~= " RETURNING "; 578 str ~= _returning.join(", "); 579 } 580 581 return replaceParams(str); 582 } 583 584 unittest 585 { 586 writeln("\t * deleteCommand"); 587 588 QueryBuilder qb; 589 qb.remove("table") 590 .where("id", 1) 591 .returning("id"); 592 593 string str = qb.command(); 594 assert(str == `DELETE FROM "table" WHERE id = $1 RETURNING id`, str); 595 } 596 597 @property string command() 598 { 599 final switch (_type) 600 { 601 case QueryType.select: 602 return selectCommand(); 603 case QueryType.update: 604 return updateCommand(); 605 case QueryType.insert: 606 return insertCommand(); 607 case QueryType.delete_: 608 return deleteCommand(); 609 } 610 } 611 612 @property private Value[] paramsArr() 613 { 614 Value[] res = _indexParams; 615 //foreach (param; _indexParams) 616 // res ~= param; 617 618 foreach (param, val; _params) 619 res ~= val; 620 621 return res; 622 } 623 624 unittest 625 { 626 writeln("\t * paramsArr"); 627 628 QueryBuilder qb; 629 qb.addParams("1", "2", "3"); 630 qb["foo"] = 1; 631 qb["bar"] = 2; 632 633 auto ps = qb.paramsArr(); 634 assert( 635 ps == [Value("1"), Value("2"), Value("3"), Value(1), Value(2)] || 636 ps == [Value("1"), Value("2"), Value("3"), Value(2), Value(1)]); 637 } 638 639 void addParam(T)(T val) 640 { 641 _indexParams ~= Value(val); 642 ++_paramIndex; 643 } 644 645 unittest 646 { 647 writeln("\t * addParam"); 648 649 QueryBuilder qb; 650 651 assert(qb._paramIndex == 0); 652 653 qb.addParam(1); 654 assert(qb._paramIndex == 1); 655 assert(qb._indexParams.length == 1); 656 assert(qb._indexParams[0] == Value(1)); 657 658 qb.addParam(2); 659 assert(qb._paramIndex == 2); 660 assert(qb._indexParams.length == 2); 661 assert(qb._indexParams[1] == Value(2)); 662 } 663 664 ref QueryBuilder addParams(T...)(T vals) 665 { 666 foreach (val; vals) 667 addParam(val); 668 669 return this; 670 } 671 672 unittest 673 { 674 writeln("\t * addParams"); 675 676 QueryBuilder qb; 677 qb.addParams(1, 2, 3); 678 679 assert(qb._indexParams.length == 3); 680 assert(qb._paramIndex == 3); 681 } 682 683 ref QueryBuilder opBinary(string op, T)(T val) 684 if (op == "<<") 685 { 686 return addParam(val); 687 } 688 689 690 Query query() 691 { 692 if (_connection != null) 693 return Query(*_connection, command, paramsArr); 694 695 return Query(command, paramsArr); 696 } 697 698 Query query(ref Connection conn) 699 { 700 return Query(conn, command, paramsArr); 701 } 702 703 } 704 705 void param(alias P)(QueryBuilder b) 706 { 707 b[P.stringof] = P; 708 }