Mercurial > core / lisp/lib/obj/query.lisp
changeset 575: |
efb4a19ff530 |
parent: |
9e7d4393eac6
|
child: |
60c7b1c83c47 |
author: |
Richard Westhaver <ellis@rwest.io> |
date: |
Sun, 04 Aug 2024 00:18:52 -0400 |
permissions: |
-rw-r--r-- |
description: |
color palettes, obj/query upgrades and q/sql parsing - successfully parsing SQL-SELECT |
1 ;;; obj/query/pkg.lisp --- Query Objects 3 ;; Lisp primitive Query objects for DIY query engines. 7 ;; This package provides the base set of classes and methods for implementing 10 ;; The intention is to use these objects in several high-level packages where 11 ;; we need the ability to ask complex questions about some arbitrary data 14 ;; The type of high-level packages can loosely be categorized as: 16 ;; - Frontends :: The interface exposed to the user - SQL, Prolog, etc. 18 ;; - Middleware :: interfaces which are used internally and exposed publicly - 19 ;; query planners/optimizers/ast 21 ;; - Backends :: The interface exposed to the underlying data sources - 22 ;; RocksDB, SQLite, etc. 26 ;; https://gist.github.com/twitu/221c8349887cec0a83b395e4cbb492a7 28 ;; https://www1.columbia.edu/sec/acis/db2/db2d0/db2d0103.htm 30 ;; https://howqueryengineswork.com/ 33 (in-package :obj/query) 37 (defvar *literal-value-types* '(boolean fixnum signed-byte unsigned-byte float double-float string))) 39 (deftype literal-value-type () `(or ,@*literal-value-types*)) 43 (name (symbol-name (gensym "#")) :type simple-string) 44 (type t :type (or symbol list))) 46 (defmethod make-load-form ((self field) &optional env) 47 (declare (ignore env)) 48 `(make-field :name ,(field-name self) :type ,(field-type self))) 51 (deftype field-vector () '(vector field)) 53 ;; convenience interface for FIELD-VECTOR 54 (defclass column-vector () ((data :type simple-vector :accessor column-data))) 56 (defclass literal-value-vector (column-vector) 57 ((type :type literal-value-type :initarg :type :accessor column-type) 58 (data :initarg :data :accessor column-data) 59 (size :type fixnum :initarg :size :accessor column-size))) 61 (defgeneric column-literal-value (self) 62 (:method ((self literal-value-vector)) 65 (defgeneric column-type (self) 66 (:method ((self column-vector)) 67 (array-element-type (column-data self)))) 69 (defgeneric column-value (self i) 70 (:method ((self column-vector) (i fixnum)) 71 (aref (column-data self) i)) 72 (:method ((self literal-value-vector) (i fixnum)) 73 (if (or (< i 0) (>= i (column-size self))) 74 (error 'simple-error :format-control "index out of bounds: ~A" :format-arguments i) 75 (column-literal-value self)))) 79 ((fields :type field-vector :initarg :fields :accessor fields))) 81 (defun make-schema (&rest fields) 82 (make-instance 'schema :fields (coerce fields 'field-vector))) 84 (defgeneric load-schema (self &optional schema)) 86 (defmethod make-load-form ((self schema) &optional env) 87 (declare (ignore env)) 88 `(make-instance ,(class-of self) :fields ,(fields self))) 90 (defclass schema-metadata () 91 ((metadata :initarg :metadata :accessor schema-metadata))) 93 (defmethod make-load-form ((self schema-metadata) &optional env) 94 (declare (ignore env)) 95 `(make-instance ,(class-of self) :metadata ,(schema-metadata self))) 97 (defgeneric column-size (self) 98 (:method ((self column-vector)) 99 (length (column-data self)))) 102 (defstruct record-batch 103 (schema (make-schema) :type schema) 104 (fields #() :type field-vector)) 106 (defmethod make-load-form ((self record-batch) &optional env) 107 (declare (ignore env)) 108 `(make-record-batch :schema ,(record-batch-schema self) :fields ,(record-batch-fields self))) 111 (defgeneric field (self n) 112 (:method ((self record-batch) (n fixnum)) 113 (aref (record-batch-fields self) n))) 115 (defgeneric fields (self) 116 (:method ((self record-batch)) 117 (record-batch-fields self))) 119 (defgeneric schema (self) 120 (:method ((self record-batch)) 121 (record-batch-schema self))) 123 (defgeneric derive-schema (self)) 125 (defgeneric select (self names) 126 (:method ((self schema) (names list)) 127 (let* ((fields (fields self)) 128 (ret (make-array (length fields) :element-type 'field :fill-pointer 0 129 :initial-element (make-field)))) 130 (make-instance 'schema 131 :fields (dolist (n names ret) 132 (if-let ((found (find n fields :test 'equal :key 'field-name))) 133 (vector-push found ret) 134 (error 'invalid-argument :item n :reason "Invalid column name")))))) 135 (:method ((self schema) (names vector)) 136 (let* ((fields (fields self)) 137 (ret (make-array (length fields) :element-type 'field :fill-pointer 0 138 :initial-element (make-field)))) 139 (make-instance 'schema 140 :fields (loop for n across names 141 do (if-let ((found (find n fields :test 'equal :key 'field-name))) 142 (vector-push found ret) 143 (error 'invalid-argument :item n :reason "Invalid column name")) 144 finally (return ret)))))) 146 (defgeneric project (self indices) 147 (:method ((self schema) (indices list)) 148 (make-instance 'schema 149 :fields (coerce (mapcar (lambda (i) (aref (fields self) i)) indices) 'field-vector))) 150 (:method ((self schema) (indices vector)) 151 (make-instance 'schema 153 (loop for i across indices 154 collect (aref (fields self) i)) 157 (defgeneric row-count (self) 158 (:method ((self record-batch)) 159 (sequence:length (aref (record-batch-fields self) 0)))) 161 (defgeneric column-count (self) 162 (:method ((self record-batch)) 163 (length (record-batch-fields self)))) 165 ;;; Execution Context 166 (defclass execution-context () ()) 168 (defclass data-source () 169 ((schema :type schema :accessor schema))) 171 (defgeneric scan-data-source (self projection) 172 (:documentation "Scan the data source, selecting the specified columns.")) 175 ;; minimal data-frame abstraction. methods are prefixed with 'DF-'. 176 (defclass data-frame () 177 ((fields :initform #() :initarg :fields :accessor df-fields) 178 (data :initform #() :initarg :data :accessor df-data))) 180 (defgeneric df-col (self)) 182 (defgeneric df-project (&rest expr &key &allow-other-keys)) 183 (defgeneric df-filter (expr)) 184 (defgeneric df-aggregate (group-by agg-expr)) 187 (defclass query-expression () ()) 189 (defclass query-plan () 190 ((schema :type schema :accessor schema :initarg :schema) 191 (children :type (vector query-plan)))) 193 (defclass logical-plan (query-plan) 194 ((children :type (vector logical-plan) :accessor children :initarg :children))) 196 (defclass physical-plan (query-plan) 197 ((children :type (vector physical-plan)))) 199 ;;; Logical Expressions 200 (defclass logical-expression (query-expression) ()) 202 (defgeneric to-field (self input) 203 (:method ((self string) (input logical-plan)) 204 (declare (ignore input)) 205 (make-field :name self :type 'string)) 206 (:method ((self number) (input logical-plan)) 207 (declare (ignore input)) 208 (make-field :name (princ-to-string self) :type 'number))) 210 (defclass column-expression (logical-expression) 211 ((name :type string :initarg :name :accessor column-name))) 213 (defmethod to-field ((self column-expression) (input logical-plan)) 214 (or (find (column-name self) (fields (schema input)) :test 'equal :key 'field-name) 215 (error 'invalid-argument :item (column-name self) :reason "Invalid column name"))) 217 (defmethod df-col ((self string)) 218 (make-instance 'column-expression :name self)) 220 (defclass literal-expression (logical-expression) ()) 223 (defclass alias-expression (logical-expression) 224 ((expr :type logical-expression :initarg :expr) 225 (alias :type string :initarg :alias))) 228 (defclass unary-expression (logical-expression) 229 ((expr :type logical-expression))) 232 (defclass binary-expression (logical-expression) 233 ((lhs :type logical-expression :initarg :lhs :accessor lhs) 234 (rhs :type logical-expression :initarg :rhs :accessor rhs))) 236 (defgeneric binary-expression-name (self)) 237 (defgeneric binary-expression-op (self)) 239 (defclass boolean-binary-expression (binary-expression) 240 ((name :initarg :name :type string :accessor binary-expression-name) 241 (op :initarg :op :type symbol :accessor binary-expression-op))) 243 (defmethod to-field ((self boolean-binary-expression) (input logical-plan)) 244 (declare (ignore input)) 245 (make-field :name (binary-expression-name self) :type 'boolean)) 248 (defclass eq-expression (boolean-binary-expression) () 253 (defclass neq-expression (boolean-binary-expression) () 258 (defclass gt-expression (boolean-binary-expression) () 263 (defclass lt-expression (boolean-binary-expression) () 268 (defclass gteq-expression (boolean-binary-expression) () 273 (defclass lteq-expression (boolean-binary-expression) () 279 (defclass and-expression (boolean-binary-expression) () 284 (defclass or-expression (boolean-binary-expression) () 290 (defclass math-expression (binary-expression) 291 ((name :initarg :name :type string :accessor binary-expression-name) 292 (op :initarg :op :type symbol :accessor binary-expression-op))) 294 ;; TODO 2024-08-03: ??? 295 (defmethod to-field ((self math-expression) (input logical-plan)) 296 (declare (ignorable input)) 297 (make-field :name "mult" :type (field-type (to-field (lhs self) input)))) 299 (defclass add-expression (math-expression) () 304 (defclass sub-expression (math-expression) () 309 (defclass mult-expression (math-expression) () 314 (defclass div-expression (math-expression) () 319 (defclass mod-expression (math-expression) () 325 (deftype aggregate-function () `(function ((input logical-expression)) query-expression)) 327 (deftype aggregate-function-designator () `(or aggregate-function symbol)) 329 (defclass aggregate-expression (logical-expression) 331 (expr :type logical-expression))) 333 (defmethod to-field ((self aggregate-expression) (input logical-plan)) 334 (declare (ignorable input)) 335 (make-field :name (slot-value self 'name) :type (field-type (to-field (slot-value self 'expr) input)))) 337 (defclass sum-expression (aggregate-expression) () 341 (defclass min-expression (aggregate-expression) () 345 (defclass max-expression (aggregate-expression) () 349 (defclass avg-expression (aggregate-expression) () 353 (defclass count-expression (aggregate-expression) () 357 (defmethod to-field ((self count-expression) (input logical-plan)) 358 (declare (ignore input)) 359 (make-field :name "COUNT" :type 'number)) 364 (defclass scan-data (logical-plan) 365 ((path :type string :initarg :path) 366 (data-source :type data-source :initarg :data-source) 367 (projection :type (vector string) :initarg :projection))) 369 (defmethod derive-schema ((self scan-data)) 370 (let ((proj (slot-value self 'projection))) 371 (if (= 0 (length proj)) 372 (slot-value self 'schema) 373 (select (slot-value self 'schema) proj)))) 375 (defmethod schema ((self scan-data)) 376 (derive-schema self)) 379 (defclass projection (logical-plan) 380 ((input :type logical-plan :initarg :input) 381 (expr :type (vector logical-expression) :initarg :expr))) 383 (defmethod schema ((self projection)) 384 (schema (slot-value self 'input))) 387 (defclass selection (logical-plan) 388 ((input :type logical-plan :initarg :input) 389 (expr :type logical-expression :initarg :expr))) 391 (defmethod schema ((self selection)) 392 (schema (slot-value self 'input))) 395 (defclass aggregate (logical-plan) 396 ((input :type logical-plan :initarg :input) 397 (group-expr :type (vector logical-expression) :initarg :group-expr) 398 (agg-expr :type (vector aggregate-expression) :initarg :agg-expr))) 400 (defmethod schema ((self aggregate)) 401 (let ((input (slot-value self 'input)) 403 (loop for g across (slot-value self 'group-expr) 404 do (push (to-field g input) ret)) 405 (loop for a across (slot-value self 'agg-expr) 406 do (push (to-field a input) ret)) 407 (make-schema :fields (coerce ret 'field-vector)))) 409 ;;; Physical Expression 410 (defclass physical-expression (query-expression) ()) 412 (defclass literal-physical-expression (physical-expression) ()) 414 (defgeneric evaluate (self input) 415 (:documentation "Evaluate the expression SELF with INPUT and return a COLUMN-VECTOR result.") 416 (:method ((self string) (input record-batch)) 417 (make-instance 'literal-value-vector 418 :size (row-count input) 420 :data (sb-ext:string-to-octets self))) 421 (:method ((self number) (input record-batch)) 422 (make-instance 'literal-value-vector :size (row-count input) :type 'number :data self))) 424 (defclass column-physical-expression (physical-expression) 425 ((val :type array-index :initarg :val))) 427 (defmethod evaluate ((self column-physical-expression) (input record-batch)) 428 (field input (slot-value self 'val))) 430 (defclass binary-physical-expression (physical-expression) 431 ((lhs :type physical-expression :accessor lhs :initarg :lhs) 432 (rhs :type physical-expression :accessor rhs :initarg :rhs))) 434 (defgeneric evaluate2 (self lhs rhs)) 436 (defmethod evaluate ((self binary-physical-expression) (input record-batch)) 437 (let ((ll (evaluate (lhs self) input)) 438 (rr (evaluate (rhs self) input))) 439 (assert (= (length ll) (length rr))) 440 (if (eql (column-type ll) (column-type rr)) 441 (evaluate2 self ll rr) 442 (error "invalid state! lhs != rhs")))) 444 (defclass eq-physical-expression (binary-physical-expression) ()) 446 (defmethod evaluate2 ((self eq-physical-expression) lhs rhs) 447 (declare (ignore self)) 450 (defclass neq-physical-expression (binary-physical-expression) ()) 452 (defmethod evaluate2 ((self neq-physical-expression) lhs rhs) 453 (declare (ignore self)) 456 (defclass lt-physical-expression (binary-physical-expression) ()) 458 (defclass gt-physical-expression (binary-physical-expression) ()) 460 (defclass lteq-physical-expression (binary-physical-expression) ()) 462 (defclass gteq-physical-expression (binary-physical-expression) ()) 464 (defclass and-physical-expression (binary-physical-expression) ()) 466 (defclass or-physical-expression (binary-physical-expression) ()) 468 (defclass math-physical-expression (binary-physical-expression) ()) 470 (defmethod evaluate2 ((self math-physical-expression) (lhs column-vector) (rhs column-vector)) 471 (coerce (loop for i below (column-size lhs) 472 collect (evaluate2 self (column-value lhs i) (column-value rhs i))) 475 (defclass add-physical-expresion (math-expression) ()) 477 (defmethod evaluate2 ((self add-physical-expresion) lhs rhs) 478 (declare (ignore self)) 481 (defclass sub-physical-expression (math-expression) ()) 483 (defmethod evaluate2 ((self sub-physical-expression) lhs rhs) 484 (declare (ignore self)) 487 (defclass mult-physical-expression (math-expression) ()) 489 (defmethod evaluate2 ((self mult-physical-expression) lhs rhs) 490 (declare (ignore self)) 493 (defclass div-physical-expression (math-expression) ()) 495 (defmethod evaluate2 ((self div-physical-expression) lhs rhs) 496 (declare (ignore self)) 499 (defclass accumulator () 500 ((value :initarg :value :accessor accumulator-value))) 502 (defgeneric accumulate (self val) 503 (:method ((self accumulator) val) 505 (setf (accumulator-value self) (+ val (accumulator-value self)))))) 507 (defgeneric make-accumulator (self)) 510 (defclass max-accumulator (accumulator) ()) 512 (defmethod accumulate ((self max-accumulator) (val number)) 513 (when (> val (accumulator-value self)) 514 (setf (accumulator-value self) val))) 516 (defclass aggregate-physical-expression (physical-expression) 517 ((input :type physical-expression))) 519 (defclass max-physical-expression (aggregate-physical-expression) ()) 521 (defmethod make-accumulator ((self max-physical-expression)) 522 (make-instance 'max-accumulator)) 525 (defgeneric execute (self)) 527 (defclass scan-exec (physical-plan) 528 ((data-source :type data-source :initarg :data-source) 529 (projection :type (vector string) :initarg :projection))) 531 (defmethod schema ((self scan-exec)) 532 (select (schema (slot-value self 'data-source)) (slot-value self 'projection))) 534 (defmethod execute ((self scan-exec)) 535 (scan-data-source (slot-value self 'data-source) (slot-value self 'projection))) 537 (defclass projection-exec (physical-plan) 538 ((input :type physical-plan :initarg :input) 539 (expr :type (vector physical-expression) :initarg :expr))) 541 (defmethod execute ((self projection-exec)) 543 (loop for batch across (fields (execute (slot-value self 'input))) 544 collect (make-record-batch :schema (slot-value self 'schema) 546 (loop for e across (slot-value self 'expr) 547 collect (evaluate e batch)) 549 '(vector record-batch))) 552 (defclass selection-exec (physical-plan) 553 ((input :type physical-plan :initarg :input) 554 (expr :type physical-expression :initarg :expr))) 556 (defmethod schema ((self selection-exec)) 557 (schema (slot-value self 'input))) 559 (defmethod execute ((self selection-exec)) 561 (loop for batch across (execute (slot-value self 'input)) 562 with res = (coerce (evaluate (slot-value self 'expr) batch) 'bit-vector) 563 with schema = (schema batch) 564 with count = (column-count (fields (schema batch))) 565 with filtered = (loop for i from 0 below count 566 collect (filter self (field batch i) res)) 567 collect (make-record-batch :schema schema :fields (coerce filtered 'field-vector))) 568 '(vector record-batch))) 570 (defgeneric filter (self columns selection) 571 (:method ((self selection-exec) (columns column-vector) (selection simple-bit-vector)) 573 (loop for i from 0 below (length selection) 574 unless (zerop (bit selection i)) 575 collect (column-value columns i)) 578 (defclass hash-aggregate-exec (physical-plan) 579 ((input :type physical-plan :initarg :input) 580 (group-expr :type (vector physical-plan) :initarg :group-expr) 581 (agg-expr :type (vector aggregate-physical-expression) :initarg :agg-expr))) 583 (defmethod execute ((self hash-aggregate-exec)) 585 (loop for batch across (execute (slot-value self 'input)) 586 with map = (make-hash-table :test 'equal) 587 with groupkeys = (map 'vector (lambda (x) (evaluate x batch)) (slot-value self 'group-expr)) 588 with aggr-inputs = (map 'vector (lambda (x) (evaluate (slot-value x 'input) batch)) 589 (slot-value self 'agg-expr)) 590 do (loop for row-idx from 0 below (row-count batch) 591 with row-key = (map 'vector 593 (when-let ((val (column-value x row-idx))) 595 (octet-vector (sb-ext:octets-to-string val)) 598 with accs = (if-let ((val (gethash row-key map))) 601 (gethash row-key map) 604 (slot-value self 'agg-expr)))) 605 ;; start accumulating 606 do (loop for i from 0 below (length accs) 607 for accum across accs 608 with val = (column-value (aref aggr-inputs i) row-idx) 609 return (accumulate accum val)) 610 ;; collect results in array 611 with ret = (make-record-batch :schema (slot-value self 'schema) 612 :fields (make-array (hash-table-size map) 614 :initial-element (make-field))) 615 do (loop for row-idx from 0 below (hash-table-size map) 616 for gkey being the hash-keys of map 617 using (hash-value accums) 618 with glen = (length (slot-value self 'group-expr)) 619 do (loop for i from 0 below glen 620 do (setf (aref (aref (fields ret) i) row-idx) 622 do (loop for i from 0 below (length (slot-value self 'agg-expr)) 623 do (setf (aref (aref (fields ret) (+ i glen)) row-idx) 624 (accumulator-value (aref accums i))))) 626 '(vector record-batch))) 630 ;; The Query Planner is effectively a compiler which translates logical 631 ;; expressions and plans into their physical counterparts. 633 (defclass query-planner () ()) 635 (defgeneric make-physical-expression (expr input) 636 (:documentation "Translate logical expression EXPR and logical plan INPUT 637 into a physical expression.") 638 (:method ((expr string) (input logical-plan)) 639 (declare (ignore input)) 641 (:method ((expr number) (input logical-plan)) 642 (declare (ignore input)) 644 (:method ((expr column-expression) (input logical-plan)) 645 (let ((i (position (column-name expr) (fields (schema input)) :key 'field-name :test 'equal))) 646 (make-instance 'column-physical-expression :val i))) 647 (:method ((expr binary-expression) (input logical-plan)) 648 (let ((l (make-physical-expression (lhs expr) input)) 649 (r (make-physical-expression (rhs expr) input))) 651 (eq-expression (make-instance 'eq-physical-expression :lhs l :rhs r)) 652 (neq-expression (make-instance 'neq-physical-expression :lhs l :rhs r)) 653 (gt-expression (make-instance 'gt-physical-expression :lhs l :rhs r)) 654 (gteq-expression (make-instance 'gteq-physical-expression :lhs l :rhs r)) 655 (lt-expression (make-instance 'lt-physical-expression :lhs l :rhs r)) 656 (lteq-expression (make-instance 'lteq-physical-expression :lhs l :rhs r)) 657 (and-expression (make-instance 'and-physical-expression :lhs l :rhs r)) 658 (or-expression (make-instance 'or-physical-expression :lhs l :rhs r)) 659 (add-expression (make-instance 'add-physical-expresion :lhs l :rhs r)) 660 (sub-expression (make-instance 'sub-physical-expression :lhs l :rhs r)) 661 (mult-expression (make-instance 'mult-physical-expression :lhs l :rhs r)) 662 (div-expression (make-instance 'div-physical-expression :lhs l :rhs r)))))) 664 (defgeneric make-physical-plan (plan) 665 (:documentation "Create a physical plan from logical PLAN.") 666 (:method ((plan logical-plan)) 668 (scan-data (make-instance 'scan-exec 669 :data-source (slot-value plan 'data-source) 670 :projection (slot-value plan 'projection))) 671 (projection (make-instance 'projection-exec 672 :schema (make-instance 'schema 675 (lambda (x) (to-field x (slot-value plan 'input))) 676 (slot-value plan 'expr))) 677 :input (make-physical-plan (slot-value plan 'input)) 678 :expr (map 'vector (lambda (x) (make-physical-expression x (slot-value plan 'input))) 679 (slot-value plan 'expr)))) 680 (selection (make-instance 'selection-exec 681 :input (make-physical-plan (slot-value plan 'input)) 682 :expr (make-physical-expression (slot-value plan 'expr) (slot-value plan 'input)))) 683 (aggregate (make-instance 'hash-aggregate-exec 684 :input (make-physical-plan (slot-value plan 'input)) 685 :group-expr (make-physical-expression (slot-value plan 'group-expr) (slot-value plan 'input)) 686 :agg-expr (make-physical-expression (slot-value plan 'agg-expr) (slot-value plan 'input))))))) 694 ;; outer-join left-outer-join right-outer-join 708 ;; correlated-subquery 710 ;; SELECT id, name, (SELECT count(*) FROM orders WHERE customer_id = customer.id) AS num_orders FROM customers 712 ;; uncorrelated-subquery 716 ;; SELECT * FROM orders WHERE total > (SELECT avg(total) FROM sales WHERE customer_state = 'CA') 718 ;; NOTE 2024-08-02: EXISTS, IN, NOT EXISTS, and NOT IN are also subqueries 722 ;; The Query Optimizer is responsible for walking a QUERY-PLAN and returning a 723 ;; modified version of the same object. Usually we want to run optimization on 724 ;; LOGICAL-PLANs but we also support specializing on PHYSICAL-PLAN. 726 ;; Rule-based Optimizers: projection/predicate push-down, sub-expr elim 728 ;; TBD: Cost-based optimizers 730 (defclass query-optimizer () ()) 732 (defstruct (query-vop (:constructor make-query-vop (info))) 735 (defgeneric optimize-query (self plan)) 737 ;; Projection Pushdown 738 (defun extract-columns (expr input &optional accum) 740 (array-index (accumulate accum (field (fields (schema input)) expr))) 741 (column-expression (accumulate accum (column-name expr))) 743 (extract-columns (lhs expr) input accum) 744 (extract-columns (rhs expr) input accum)) 745 (alias-expression (extract-columns (slot-value expr 'expr) input accum)) 747 (literal-expression nil))) 749 (defun extract-columns* (exprs input &optional accum) 750 (mapcar (lambda (x) (extract-columns x input accum)) exprs)) 752 (defclass projection-pushdown-optimizer (query-optimizer) ()) 754 (defun %pushdown (plan &optional column-names) 755 (declare (logical-plan plan)) 758 (extract-columns (slot-value plan 'expr) column-names) 759 (let ((input (%pushdown (slot-value plan 'input) column-names))) 760 (make-instance 'projection :input input :expr (slot-value plan 'expr)))) 762 (extract-columns (slot-value plan 'expr) column-names) 763 (let ((input (%pushdown (slot-value plan 'input) column-names))) 764 (make-instance 'selection :input input :expr (slot-value plan 'expr)))) 766 (extract-columns (slot-value plan 'group-expr) column-names) 768 (loop for x across (slot-value plan 'agg-expr) collect (slot-value x 'input)) 770 (let ((input (%pushdown (slot-value plan 'input) column-names))) 771 (make-instance 'aggregate 773 :group-expr (slot-value plan 'group-expr) 774 :agg-expr (slot-value plan 'agg-expr)))) 775 (scan-data (make-instance 'scan-data 776 :path (slot-value plan 'name) 777 :data-source (slot-value plan 'data-source) 778 :projection column-names)))) ;; maybe sort here? 780 (defmethod optimize-query ((self projection-pushdown-optimizer) (plan logical-plan)) 784 (defclass query () ()) 786 (defgeneric make-query (self &rest initargs &key &allow-other-keys) 787 (:method ((self t) &rest initargs) 788 (declare (ignore initargs)) 789 (make-instance 'query)))