diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fbdcf80 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.komodoproject diff --git a/get_caret_pos.js b/get_caret_pos.js new file mode 100644 index 0000000..b69397a --- /dev/null +++ b/get_caret_pos.js @@ -0,0 +1,104 @@ +/* jshint browser: true */ + +// The properties that we copy into a mirrored div. +// Note that some browsers, such as Firefox, +// do not concatenate properties, i.e. padding-top, bottom etc. -> padding, +// so we have to do every single property specifically. +var properties = [ + 'direction', // RTL support + 'boxSizing', + 'width', // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does + 'height', + 'overflowX', + 'overflowY', // copy the scrollbar for IE + + 'borderTopWidth', + 'borderRightWidth', + 'borderBottomWidth', + 'borderLeftWidth', + 'borderStyle', + + 'paddingTop', + 'paddingRight', + 'paddingBottom', + 'paddingLeft', + + // https://developer.mozilla.org/en-US/docs/Web/CSS/font + 'fontStyle', + 'fontVariant', + 'fontWeight', + 'fontStretch', + 'fontSize', + 'fontSizeAdjust', + 'lineHeight', + 'fontFamily', + + 'textAlign', + 'textTransform', + 'textIndent', + 'textDecoration', // might not make a difference, but better be safe + + 'letterSpacing', + 'wordSpacing', + + 'tabSize', + 'MozTabSize' + +]; + +var isFirefox = window.mozInnerScreenX != null; + +export function getCaretCoordinates(element, position) { + // mirrored div + var div = document.createElement('div'); + div.id = 'input-textarea-caret-position-mirror-div'; + document.body.appendChild(div); + + var style = div.style; + var computed = window.getComputedStyle? getComputedStyle(element) : element.currentStyle; // currentStyle for IE < 9 + + // default textarea styles + style.whiteSpace = 'nowrap'; + if (element.nodeName !== 'INPUT') + style.wordWrap = 'nowrap'; // only for textarea-s + + // position off-screen + style.position = 'absolute'; // required to return coordinates properly + style.visibility = 'hidden'; // not 'display: none' because we want rendering + + // transfer the element's properties to the div + properties.forEach(function (prop) { + style[prop] = computed[prop]; + }); + + if (isFirefox) { + // Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275 + if (element.scrollHeight > parseInt(computed.height)) + style.overflowY = 'scroll'; + } else { + style.overflow = 'hidden'; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll' + } + + div.textContent = element.value.substring(0, position); + // the second special handling for input type="text" vs textarea: spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037 + if (element.nodeName === 'INPUT') + div.textContent = div.textContent.replace(/\s/g, "\u00a0"); + + var span = document.createElement('span'); + // Wrapping must be replicated *exactly*, including when a long word gets + // onto the next line, with whitespace at the end of the line before (#7). + // The *only* reliable way to do that is to copy the *entire* rest of the + // textarea's content into the created at the caret position. + // for inputs, just '.' would be enough, but why bother? + span.textContent = element.value.substring(position) || '.'; // || because a completely empty faux span doesn't render at all + div.appendChild(span); + + var coordinates = { + top: span.offsetTop + parseInt(computed['borderTopWidth']), + left: span.offsetLeft + parseInt(computed['borderLeftWidth']) + }; + + document.body.removeChild(div); + + return coordinates; +} \ No newline at end of file diff --git a/index.html b/index.html index 91baaa8..9934965 100644 --- a/index.html +++ b/index.html @@ -17,16 +17,29 @@ font-size:12pt; text-align:center; } - body { - zoom: 1.5; + body, html { + padding: 0px; + margin: 0px; + } + html { + zoom: 150%; } +
 
- + + @@ -34,7 +47,7 @@ - + @@ -45,10 +58,10 @@ - +
Query: + + + + + + +
Avail Functions
Table:
Group By:
Order Bys:
@@ -69,28 +82,29 @@ - - + + - - + + -
+
Output: -
+
- diff --git a/pql/PQL.js b/pql/PQL.js index 98e1304..a5ef96d 100644 --- a/pql/PQL.js +++ b/pql/PQL.js @@ -9,13 +9,16 @@ export class PQL { PQL._defaultConfig = v; } - static getSQL ({ query, table, group, selects, orderBys }) { - var query_parser = new PARSER(query, table, false, this.defaultConfig); + static getSQL ({ query, table, group, selects, orderBys, variables }) { + var query_parser = new PARSER(query, table, false, this.defaultConfig, [], variables); if (query_parser.hasError()) { throw query_parser.getError(); } - var group_parser = new PARSER(group, table, true, this.defaultConfig); + if (group === undefined) { + group = 'id'; + } + var group_parser = new PARSER(group, table, true, this.defaultConfig, [], variables); if (group_parser.hasError()) { throw group_parser.getError(); } @@ -23,7 +26,7 @@ export class PQL { let select_parsers = new Map(); if (selects instanceof Map) { selects.forEach((v, k) => { - let val = new PARSER(v, table, false, this.defaultConfig); + let val = new PARSER(v, table, false, this.defaultConfig, [], variables); if (val.hasError()) { throw val.getError(); } @@ -32,7 +35,7 @@ export class PQL { } else { for (let k in selects) { if (selects.hasOwnProperty(k)) { - let v = new PARSER(selects[k], table, false, this.defaultConfig); + let v = new PARSER(selects[k], table, false, this.defaultConfig, [], variables); if (v.hasError()) { throw v.getError(); } @@ -41,15 +44,11 @@ export class PQL { } } - if (!select_parsers.size) { - throw "Must have at least 1 select"; - } - let order_by_parsers = new Map(); if (orderBys instanceof Map) { orderBys.forEach((v, k) => { // This one is backwards... be warned that k is the string v is the [desc, asc] - let val = new PARSER(k, table, false, this.defaultConfig); + let val = new PARSER(k, table, false, this.defaultConfig, [], variables); if (val.hasError()) { throw val.getError(); } @@ -58,7 +57,7 @@ export class PQL { } else { for (let k in orderBys) { if (orderBys.hasOwnProperty(k)) { - let v = new PARSER(k, table, false, this.defaultConfig); + let v = new PARSER(k, table, false, this.defaultConfig, [], variables); if (v.hasError()) { throw v.getError(); } diff --git a/pql/config.js b/pql/config.js index 9d56df6..4126b53 100644 --- a/pql/config.js +++ b/pql/config.js @@ -29,124 +29,203 @@ Config.ARG5 = 4; Config.ARG6 = 5; Config.ARG7 = 6; Config.ARG8 = 7; -Config.DB_MAP = { - order: { - name: 'orders', - fields: { - id: { - type: Config.NUMERIC, - }, - order_created: { - type: Config.DATE, - }, - customer_id: { - type: Config.NUMERIC, - }, - order_total: { - type: Config.NUMERIC, - }, - }, - linkTo: { - customer: { - table: 'customer', - psudoql: 'eq(customer_id;,customer.id;);', - }, - }, - linkFrom: {}, - }, - customer: { - name: 'customers', - fields: { - id: { - type: Config.NUMERIC, - }, - name: { - type: Config.STRING, - }, - address1: { - type: Config.STRING, - }, - address2: { - type: Config.STRING, - }, - city: { - type: Config.STRING, - }, - state: { - type: Config.STRING, - }, - postalcode: { - type: Config.STRING, - }, - }, - linkTo: {}, - linkFrom: { - orders: { - table: 'order', - psudoql: 'eq(orders.customer_id;,id;);', - }, - }, - }, -}; +Config.DB_MAP = +{"bill":{"name":"bills","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"invoice_number":{"type":Config.STRING},"purchase_id":{"type":Config.NUMERIC},"invoice_date":{"type":Config.DATE},"notes":{"type":Config.STRING},"payment_status":{"type":Config.NUMERIC},"term_id":{"type":Config.NUMERIC},"due_date":{"type":Config.DATE},"accounting_system_id":{"type":Config.STRING},"vendor_id":{"type":Config.NUMERIC},"effective_date":{"type":Config.DATE},"invoice_amount":{"type":Config.NUMERIC},"shipping_cost":{"type":Config.NUMERIC},"discount_amount":{"type":Config.NUMERIC},"tax_amount":{"type":Config.NUMERIC},"legacy_id":{"type":Config.STRING},"date_closed":{"type":Config.DATE},"currency":{"type":Config.STRING},"type":{"type":Config.STRING}},"linkTo":{"purchase":{"table":"purchase","pql":"eq(purchase_id,purchase.id)"},"term":{"table":"term","pql":"eq(term_id,term.id)"}},"linkFrom":{"shipment":{"table":"shipment","pql":"eq(shipment.bill_id,id)"},"product_items_bills":{"table":"product_items_bills","pql":"eq(product_items_bills.bill_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Bill\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Bill\")"}}},"booking":{"name":"bookings","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"customer_id":{"type":Config.NUMERIC},"ship_attn":{"type":Config.STRING},"ship_name":{"type":Config.STRING},"ship_address1":{"type":Config.STRING},"ship_address2":{"type":Config.STRING},"ship_city":{"type":Config.STRING},"ship_state":{"type":Config.STRING},"ship_zip":{"type":Config.STRING},"ship_country":{"type":Config.STRING},"brand_id":{"type":Config.NUMERIC},"date":{"type":Config.DATE},"customer_po":{"type":Config.STRING},"sales_rep_id":{"type":Config.NUMERIC},"total":{"type":Config.NUMERIC}},"linkTo":{"customer":{"table":"customer","pql":"eq(customer_id,customer.id)"},"brand":{"table":"brand","pql":"eq(brand_id,brand.id)"},"sales_rep":{"table":"sales_rep","pql":"eq(sales_rep_id,sales_rep.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Booking\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Booking\")"}}},"brand":{"name":"brands","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"notes":{"type":Config.STRING},"primary_contact_id":{"type":Config.NUMERIC},"legacy_id":{"type":Config.STRING},"brand_names":{"type":Config.STRING},"parent_id":{"type":Config.NUMERIC},"active":{"type":Config.BOOLEAN},"website":{"type":Config.STRING},"temp_mybooks_id":{"type":Config.NUMERIC},"warehouse_use_vendor_num":{"type":Config.BOOLEAN}},"linkTo":{"primary_contact":{"table":"contact","pql":"eq(primary_contact_id,primary_contact.id)"},"parent":{"table":"brand","pql":"eq(parent_id,parent.id)"}},"linkFrom":{"contacts":{"table":"contact","pql":"eq(contacts.brand_id,id)"},"products":{"table":"product","pql":"eq(products.brand_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Brand\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Brand\")"}}},"carrier":{"name":"carriers","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"scac_code":{"type":Config.STRING}},"linkTo":[],"linkFrom":{"shipment_methods":{"table":"shipment_method","pql":"eq(shipment_methods.carrier_id,id)"}}},"contact":{"name":"contacts","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"last_name":{"type":Config.STRING},"address1":{"type":Config.STRING},"address2":{"type":Config.STRING},"city":{"type":Config.STRING},"state":{"type":Config.STRING},"zip":{"type":Config.STRING},"country":{"type":Config.STRING},"email":{"type":Config.STRING},"phone":{"type":Config.STRING},"timezone_id":{"type":Config.NUMERIC},"contact_hours":{"type":Config.STRING},"notes":{"type":Config.STRING},"fax":{"type":Config.STRING},"alt_phone":{"type":Config.STRING},"fax_optout":{"type":Config.BOOLEAN},"email_optout":{"type":Config.BOOLEAN},"postal_optout":{"type":Config.BOOLEAN},"customer_id":{"type":Config.NUMERIC},"brand_id":{"type":Config.NUMERIC},"vendor_id":{"type":Config.NUMERIC},"parent_contact_id":{"type":Config.NUMERIC},"legacy_id":{"type":Config.STRING},"status":{"type":Config.NUMERIC}},"linkTo":{"timezone":{"table":"timezone","pql":"eq(timezone_id,timezone.id)"},"customer":{"table":"customer","pql":"eq(customer_id,customer.id)"},"brand":{"table":"brand","pql":"eq(brand_id,brand.id)"},"vendor":{"table":"vendor","pql":"eq(vendor_id,vendor.id)"},"parent_contact":{"table":"contact","pql":"eq(parent_contact_id,parent_contact.id)"}},"linkFrom":{"gateway_accounts":{"table":"gateway_account","pql":"eq(gateway_accounts.billing_contact_id,id)"},"customers_shipping":{"table":"customer","pql":"eq(customers_shipping.primary_shipping_contact_id,id)"},"customers_billing":{"table":"customer","pql":"eq(customers_billing.primary_billing_contact_id,id)"},"customers_primary":{"table":"customer","pql":"eq(customers_primary.primary_contact_id,id)"},"users":{"table":"user","pql":"eq(users.contact_id,id)"},"purchases":{"table":"purchase","pql":"eq(purchases.contact_id,id)"},"brands":{"table":"brand","pql":"eq(brands.contact_id,id)"},"vendors":{"table":"vendor","pql":"eq(vendors.primary_contact_id,id)"},"sales_reps":{"table":"sales_rep","pql":"eq(sales_reps.contact_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Contact\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Contact\")"}}},"control_flag":{"name":"control_flags","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"control_key_id":{"type":Config.NUMERIC},"value":{"type":Config.STRING},"module":{"type":Config.STRING},"item_id":{"type":Config.NUMERIC}},"linkTo":{"control_key":{"table":"control_key","pql":"eq(control_key_id,control_key.id)"}},"linkFrom":[]},"control_key":{"name":"control_keys","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"key":{"type":Config.STRING},"module_flags":{"type":Config.NUMERIC}},"linkTo":[],"linkFrom":{"control_flags":{"table":"control_flag","pql":"eq(control_flags.control_key_id,id)"}}},"customer":{"name":"customers","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"primary_rep_id":{"type":Config.NUMERIC},"current_credit":{"type":Config.NUMERIC},"max_credit_amount":{"type":Config.NUMERIC},"primary_contact_id":{"type":Config.NUMERIC},"primary_billing_contact_id":{"type":Config.NUMERIC},"primary_shipping_contact_id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"notes":{"type":Config.STRING},"website":{"type":Config.STRING},"term_id":{"type":Config.NUMERIC},"resale_tax_number":{"type":Config.STRING},"territory_id":{"type":Config.NUMERIC},"federal_id":{"type":Config.STRING},"dunns":{"type":Config.STRING},"tax_code_id":{"type":Config.NUMERIC},"legacy_id":{"type":Config.STRING},"temp_mybooks_id":{"type":Config.STRING},"status":{"type":Config.NUMERIC},"parent_customer_id":{"type":Config.NUMERIC},"unpaid_invoice_amount":{"type":Config.NUMERIC},"primary_shipment_method_id":{"type":Config.NUMERIC},"primary_shipment_account_id":{"type":Config.NUMERIC}},"linkTo":{"rep":{"table":"user","pql":"eq(primary_rep_id,rep.id)"},"primary_contact":{"table":"contact","pql":"eq(primary_contact_id,primary_contact.id)"},"billing_contact":{"table":"contact","pql":"eq(primary_billing_contact_id,billing_contact.id)"},"shipping_contact":{"table":"contact","pql":"eq(primary_shipping_contact_id,shipping_contact.id)"},"term":{"table":"term","pql":"eq(term_id,term.id)"},"territory":{"table":"territory","pql":"eq(territory_id,territory.id)"},"tax_code":{"table":"tax_code","pql":"eq(tax_code_id,tax_code.id)"},"parent_customer":{"table":"customer","pql":"eq(parent_customer_id,parent_customer.id)"},"primary_shipment_method":{"table":"shipment_method","pql":"eq(primary_shipment_method_id,primary_shipment_method.id)"},"primary_shipment_account":{"table":"shipment_account","pql":"eq(primary_shipment_account_id,primary_shipment_account.id)"}},"linkFrom":{"gateway_accounts":{"table":"gateway_account","pql":"eq(gateway_accounts.customer_id,id)"},"payments":{"table":"payment","pql":"eq(payments.customer_id,id)"},"invoices":{"table":"invoice","pql":"eq(invoices.customer_id,id)"},"shipments":{"table":"shipment","pql":"eq(shipments.customer_id,id)"},"orders":{"table":"order","pql":"eq(orders.customer_id,id)"},"child_customers":{"table":"customer","pql":"eq(child_customers.parent_customer_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Customer\")"},"customer_franchises":{"table":"customer_franchises","pql":"eq(customer_franchises.customer_id,id)"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Customer\")"},"shipment_accounts":{"table":"shipment_account","pql":"eq(shipment_accounts.customer_id,id)"},"customer_notifications":{"table":"customers_notifications","pql":"eq(customer_notifications.customer_id,id)"}}},"customer_franchises":{"name":"customer_franchises","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"customer_id":{"type":Config.NUMERIC},"franchise_id":{"type":Config.NUMERIC},"date_created":{"type":Config.DATE},"date_voided":{"type":Config.DATE},"dont_purge":{"type":Config.BOOLEAN}},"linkTo":{"customer":{"table":"customer","pql":"eq(customer_id,customer.id)"},"franchise":{"table":"franchise","pql":"eq(franchise_id,franchise.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Customer_Franchises\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Customer_Franchises\")"}}},"customers_notifications":{"name":"customers_notifications","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"customer_id":{"type":Config.NUMERIC},"type":{"type":Config.STRING},"contact_method":{"type":Config.STRING},"contact_data":{"type":Config.STRING},"contact_name":{"type":Config.STRING}},"linkTo":{"customer":{"table":"customer","pql":"eq(customer_id,customer.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Customers_Notifications\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Customers_Notifications\")"}}},"cycle_count":{"name":"cycle_counts","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"date_created":{"type":Config.DATE},"date_completed":{"type":Config.DATE},"active":{"type":Config.BOOLEAN}},"linkTo":[],"linkFrom":[]},"draft_item":{"name":"draft_items","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"order_draft_id":{"type":Config.NUMERIC},"product_id":{"type":Config.NUMERIC},"status":{"type":Config.NUMERIC},"qty":{"type":Config.NUMERIC},"sell_price":{"type":Config.NUMERIC},"discount":{"type":Config.NUMERIC},"sort_order":{"type":Config.NUMERIC},"starin_promo_id":{"type":Config.NUMERIC},"vendor_promo_id":{"type":Config.NUMERIC},"spiff_id":{"type":Config.NUMERIC},"program_id":{"type":Config.NUMERIC},"order_warehouse_id":{"type":Config.NUMERIC},"order_line_id":{"type":Config.NUMERIC}},"linkTo":{"order_draft":{"table":"order_draft","pql":"eq(order_draft_id,order_draft.id)"},"product":{"table":"product","pql":"eq(product_id,product.id)"},"starin_promo":{"table":"promo","pql":"eq(starin_promo_id,starin_promo.id)"},"vendor_promo":{"table":"promo","pql":"eq(vendor_promo_id,vendor_promo.id)"},"spiff":{"table":"spiff","pql":"eq(spiff_id,spiff.id)"},"program":{"table":"program","pql":"eq(program_id,program.id)"},"order_warehouse":{"table":"office","pql":"eq(order_warehouse_id,order_warehouse.id)"},"order_line":{"table":"order_line","pql":"eq(order_line_id,order_line.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Draft_Item\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Draft_Item\")"}}},"edi_term_code":{"name":"edi_term_codes","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"term_code":{"type":Config.STRING},"description":{"type":Config.STRING}},"linkTo":[],"linkFrom":{"terms":{"table":"term","pql":"eq(terms.edi_term_code_id,id)"}}},"edi_user":{"name":"edi_users","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"contact_id":{"type":Config.NUMERIC},"friendly_name":{"type":Config.STRING},"auth_qualifier":{"type":Config.STRING},"auth_info":{"type":Config.STRING},"security_qualifier":{"type":Config.STRING},"security_info":{"type":Config.STRING},"sender_qualifier":{"type":Config.STRING},"sender_id":{"type":Config.STRING},"receiver_qualifier":{"type":Config.STRING},"receiver_id":{"type":Config.STRING},"control_standard_id":{"type":Config.STRING},"control_version":{"type":Config.STRING},"ack_requested":{"type":Config.STRING},"is_test":{"type":Config.STRING},"sub_element_sep":{"type":Config.STRING},"func_id":{"type":Config.STRING},"application_sender_id":{"type":Config.STRING},"application_receiver_id":{"type":Config.STRING},"responsible_agency_code":{"type":Config.STRING},"version_id":{"type":Config.STRING},"transaction_set_id":{"type":Config.STRING},"element_seperator":{"type":Config.STRING},"tag_seperator":{"type":Config.STRING}},"linkTo":{"contact":{"table":"contact","pql":"eq(contact_id,contact.id)"}},"linkFrom":[]},"employee":{"name":"employees","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"office_id":{"type":Config.NUMERIC},"work_area_location":{"type":Config.STRING},"computer_ip":{"type":Config.STRING},"position":{"type":Config.STRING},"actively_employed":{"type":Config.BOOLEAN},"extension":{"type":Config.STRING},"notes":{"type":Config.STRING}},"linkTo":{"office":{"table":"office","pql":"eq(office_id,office.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Employee\")"},"users":{"table":"user","pql":"eq(users.user_id,id)"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Employee\")"}}},"event":{"name":"events","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"watch_id":{"type":Config.NUMERIC},"transaction_id":{"type":Config.NUMERIC},"message":{"type":Config.STRING},"date":{"type":Config.DATE},"has_read":{"type":Config.BOOLEAN},"module":{"type":Config.STRING}},"linkTo":{"watch":{"table":"watch","pql":"eq(watch_id,watch.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Event\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Event\")"}}},"franchise":{"name":"franchises","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"notes":{"type":Config.STRING},"primary_brand_id":{"type":Config.NUMERIC},"is_exclude":{"type":Config.BOOLEAN},"is_public":{"type":Config.BOOLEAN},"legacy_id":{"type":Config.STRING},"formula":{"type":Config.STRING},"dealer_col":{"type":Config.NUMERIC}},"linkTo":{"brand":{"table":"brand","pql":"eq(primary_brand_id,brand.id)"}},"linkFrom":{"customer_franchises":{"table":"customer_franchises","pql":"eq(customer_franchises.franchise_id,id)"},"franchise_products":{"table":"franchise_products","pql":"eq(franchise_products.franchise_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Franchise\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Franchise\")"}}},"franchise_products":{"name":"franchise_products","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"franchise_id":{"type":Config.NUMERIC},"product_id":{"type":Config.NUMERIC},"date_voided":{"type":Config.DATE},"date_created":{"type":Config.DATE},"rule_id":{"type":Config.NUMERIC},"sell_price":{"type":Config.NUMERIC},"sugar_column":{"type":Config.STRING}},"linkTo":{"franchise":{"table":"franchise","pql":"eq(franchise_id,franchise.id)"},"product":{"table":"product","pql":"eq(product_id,product.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Franchise_Products\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Franchise_Products\")"}}},"gateway_account":{"name":"gateway_accounts","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"payment_gateway_id":{"type":Config.NUMERIC},"customer_id":{"type":Config.NUMERIC},"billing_contact_id":{"type":Config.NUMERIC},"gateway_ref":{"type":Config.STRING},"additional_ref":{"type":Config.STRING},"is_primary":{"type":Config.BOOLEAN},"last_updated":{"type":Config.DATE},"exp_date":{"type":Config.NUMERIC},"type":{"type":Config.STRING}},"linkTo":{"payment_gateway":{"table":"payment_gateway","pql":"eq(payment_gateway_id,payment_gateway.id)"},"billing_contact":{"table":"contact","pql":"eq(billing_contact_id,billing_contact.id)"},"customer":{"table":"customer","pql":"eq(customer_id,customer.id)"}},"linkFrom":{"order_draft":{"table":"order_draft","pql":"eq(order_draft.gateway_account_id,id)"},"orders":{"table":"order","pql":"eq(orders.gateway_account_id,id)"},"remits":{"table":"remit","pql":"eq(remits.gateway_account_id,id)"},"payment_gateway_requests":{"table":"payment_gateway_request","pql":"eq(payment_gateway_requests.gateway_account_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Gateway_Account\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Gateway_Account\")"}}},"groupping_products":{"name":"groupping_products","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"parent_id":{"type":Config.NUMERIC},"child_id":{"type":Config.NUMERIC},"num_of_children_in_parent":{"type":Config.NUMERIC},"discount_percent":{"type":Config.NUMERIC}},"linkTo":{"parent":{"table":"product","pql":"eq(parent_id,parent.id)"},"child":{"table":"product","pql":"eq(child_id,child.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Groupping_Products\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Groupping_Products\")"}}},"invoice":{"name":"invoices_view","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"invoice_date":{"type":Config.DATE},"invoice_status":{"type":Config.NUMERIC},"notes":{"type":Config.STRING},"legacy_id":{"type":Config.STRING},"invoice_sum":{"type":Config.NUMERIC},"amount_due":{"type":Config.NUMERIC},"amount_received":{"type":Config.NUMERIC},"shipment_id":{"type":Config.NUMERIC},"age":{"type":Config.NUMERIC},"override_date":{"type":Config.DATE},"invoice_id":{"type":Config.NUMERIC},"customer_id":{"type":Config.NUMERIC},"retired_date":{"type":Config.DATE}},"linkTo":{"shipment":{"table":"shipment","pql":"eq(shipment_id,shipment.id)"}},"linkFrom":{"product_items":{"table":"product_item","pql":"eq(product_items.invoice_id,id)"},"product_items_returns":{"table":"product_item_return","pql":"eq(product_items_returns.invoice_id,id)"},"product_items_reports":{"table":"product_item_report","pql":"eq(product_items_reports.invoice_id,id)"},"invoices_payments":{"table":"invoices_payments","pql":"eq(invoices_payments.invoice_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Invoice\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Invoice\")"}}},"invoices_payments":{"name":"invoices_payments","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"payment_id":{"type":Config.NUMERIC},"invoice_id":{"type":Config.NUMERIC},"applied_amount":{"type":Config.NUMERIC},"date_applied":{"type":Config.DATE},"date_voided":{"type":Config.DATE}},"linkTo":{"payment":{"table":"payment","pql":"eq(payment_id,payment.id)"},"invoice":{"table":"invoice","pql":"eq(invoice_id,invoice.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Invoices_Payments\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Invoices_Payments\")"}}},"item_exchange":{"name":"item_exchange","fields":{"id":{"type":Config.NUMERIC},"deleted":{"type":Config.BOOLEAN},"product_item_id":{"type":Config.NUMERIC},"from_status":{"type":Config.NUMERIC},"to_status":{"type":Config.NUMERIC},"from_warehouse_id":{"type":Config.NUMERIC},"to_warehouse_id":{"type":Config.NUMERIC},"user_id":{"type":Config.NUMERIC},"notes":{"type":Config.STRING},"date":{"type":Config.DATE},"from_product_id":{"type":Config.NUMERIC},"to_product_id":{"type":Config.NUMERIC},"from_cost":{"type":Config.NUMERIC},"to_cost":{"type":Config.NUMERIC}},"linkTo":{"product_item":{"table":"product_item","pql":"eq(product_item_id,product_item.id)"},"from_warehouse":{"table":"office","pql":"eq(from_warehouse_id,from_warehouse.id)"},"to_warehouse":{"table":"office","pql":"eq(to_warehouse_id,to_warehouse.id)"},"user":{"table":"user","pql":"eq(user_id,user.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Item_Exchange\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Item_Exchange\")"}}},"kit_item":{"name":"kit_items","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"product_id":{"type":Config.NUMERIC},"sort_order":{"type":Config.NUMERIC}},"linkTo":{"product":{"table":"product","pql":"eq(product_id,product.id)"}},"linkFrom":{"product_item_order_kits":{"table":"product_item","pql":"eq(product_item_order_kits.order_kit_id,id)"},"product_item_purchase_kits":{"table":"product_item","pql":"eq(product_item_purchase_kits.purchase_kit_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Kit_Item\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Kit_Item\")"}}},"last_modified":{"name":"last_modifieds","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"date":{"type":Config.DATE},"module":{"type":Config.STRING},"item_id":{"type":Config.NUMERIC},"user_id":{"type":Config.NUMERIC}},"linkTo":{"user":{"table":"user","pql":"eq(user_id,user.id)"}},"linkFrom":[]},"memute":{"name":"memutes","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"module":{"type":Config.STRING},"rule_id":{"type":Config.NUMERIC}},"linkTo":{"rule":{"table":"rule","pql":"eq(rule_id,rule.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Memute\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Memute\")"}}},"note":{"name":"notes","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"user_id":{"type":Config.NUMERIC},"note":{"type":Config.STRING},"date":{"type":Config.DATE},"public":{"type":Config.BOOLEAN},"module_flags":{"type":Config.NUMERIC},"module_priority":{"type":Config.NUMERIC},"category":{"type":Config.STRING},"code":{"type":Config.STRING},"legacy_id":{"type":Config.STRING},"important":{"type":Config.BOOLEAN}},"linkTo":{"user":{"table":"user","pql":"eq(user_id,user.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.note_id,id)"}}},"notes_link":{"name":"notes_link","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"note_id":{"type":Config.NUMERIC},"module":{"type":Config.STRING},"item_id":{"type":Config.NUMERIC},"date":{"type":Config.DATE},"user_id":{"type":Config.NUMERIC},"void":{"type":Config.BOOLEAN}},"linkTo":{"user":{"table":"user","pql":"eq(user_id,user.id)"},"note":{"table":"note","pql":"eq(note_id,note.id)"},"bill":{"table":"$module","pql":"eq(item_id,bill.id)"},"booking":{"table":"$module","pql":"eq(item_id,booking.id)"},"brand":{"table":"$module","pql":"eq(item_id,brand.id)"},"contact":{"table":"$module","pql":"eq(item_id,contact.id)"},"customer":{"table":"$module","pql":"eq(item_id,customer.id)"},"employee":{"table":"$module","pql":"eq(item_id,employee.id)"},"event":{"table":"$module","pql":"eq(item_id,event.id)"},"franchise":{"table":"$module","pql":"eq(item_id,franchise.id)"},"groupping_products":{"table":"$module","pql":"eq(item_id,groupping_products.id)"},"invoice":{"table":"$module","pql":"eq(item_id,invoice.id)"},"invoices_payments":{"table":"$module","pql":"eq(item_id,invoices_payments.id)"},"kit_item":{"table":"$module","pql":"eq(item_id,kit_item.id)"},"memute":{"table":"$module","pql":"eq(item_id,memute.id)"},"message":{"table":"$module","pql":"eq(item_id,message.id)"},"office":{"table":"$module","pql":"eq(item_id,office.id)"},"order":{"table":"$module","pql":"eq(item_id,order.id)"},"order_draft":{"table":"$module","pql":"eq(item_id,order_draft.id)"},"payment":{"table":"$module","pql":"eq(item_id,payment.id)"},"product":{"table":"$module","pql":"eq(item_id,product.id)"},"product_bar_code":{"table":"$module","pql":"eq(item_id,product_bar_code.id)"},"product_item":{"table":"$module","pql":"eq(item_id,product_item.id)"},"product_item_report":{"table":"$module","pql":"eq(item_id,product_item_report.id)"},"product_item_return":{"table":"$module","pql":"eq(item_id,product_item_return.id)"},"product_items_bills":{"table":"$module","pql":"eq(item_id,product_items_bills.id)"},"products_vendors":{"table":"$module","pql":"eq(item_id,products_vendors.id)"},"program":{"table":"$module","pql":"eq(item_id,program.id)"},"programs_customers":{"table":"$module","pql":"eq(item_id,programs_customers.id)"},"promo":{"table":"$module","pql":"eq(item_id,promo.id)"},"promos_franchises":{"table":"$module","pql":"eq(item_id,promos_franchises.id)"},"promos_products":{"table":"$module","pql":"eq(item_id,promos_products.id)"},"purchase":{"table":"$module","pql":"eq(item_id,purchase.id)"},"purchase_tracking":{"table":"$module","pql":"eq(item_id,purchase_tracking.id)"},"purchase_tracking_item":{"table":"$module","pql":"eq(item_id,purchase_tracking_item.id)"},"return_product_due":{"table":"$module","pql":"eq(item_id,return_product_due.id)"},"rule":{"table":"$module","pql":"eq(item_id,rule.id)"},"sales_rep":{"table":"$module","pql":"eq(item_id,sales_rep.id)"},"shipment":{"table":"$module","pql":"eq(item_id,shipment.id)"},"shipment_in":{"table":"$module","pql":"eq(item_id,shipment_in.id)"},"shipment_method":{"table":"$module","pql":"eq(item_id,shipment_method.id)"},"spiff":{"table":"$module","pql":"eq(item_id,spiff.id)"},"spiffs_franchises":{"table":"$module","pql":"eq(item_id,spiffs_franchises.id)"},"spiffs_products":{"table":"$module","pql":"eq(item_id,spiffs_products.id)"},"tax_code":{"table":"$module","pql":"eq(item_id,tax_code.id)"},"term":{"table":"$module","pql":"eq(item_id,term.id)"},"territory":{"table":"$module","pql":"eq(item_id,territory.id)"},"timezone":{"table":"$module","pql":"eq(item_id,timezone.id)"}},"linkFrom":[]},"office":{"name":"offices","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"foreman_id":{"type":Config.NUMERIC},"address1":{"type":Config.STRING},"address2":{"type":Config.STRING},"city":{"type":Config.STRING},"state":{"type":Config.STRING},"zip":{"type":Config.STRING},"country":{"type":Config.STRING},"phone":{"type":Config.STRING},"private_name":{"type":Config.STRING},"is_primary":{"type":Config.BOOLEAN},"open_hours":{"type":Config.STRING},"is_warehouse":{"type":Config.BOOLEAN}},"linkTo":{"foreman":{"table":"user","pql":"eq(foreman_id,foreman.id)"},"user":{"table":"user","pql":"eq(foreman_id,user.id)"}},"linkFrom":{"product_items":{"table":"product_item","pql":"eq(product_items.warehouse_id,id)"},"product_items_reports":{"table":"product_item_report","pql":"eq(product_items_reports.warehouse_id,id)"},"employees":{"table":"employee","pql":"eq(employees.office_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Office\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Office\")"}}},"order":{"name":"orders","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"customer_id":{"type":Config.NUMERIC},"ship_name":{"type":Config.STRING},"ship_address1":{"type":Config.STRING},"ship_address2":{"type":Config.STRING},"ship_city":{"type":Config.STRING},"ship_state":{"type":Config.STRING},"ship_zip":{"type":Config.STRING},"ship_country":{"type":Config.STRING},"lead_user_id":{"type":Config.NUMERIC},"order_step":{"type":Config.NUMERIC},"order_created":{"type":Config.DATE},"order_modified":{"type":Config.DATE},"notes":{"type":Config.STRING},"term_id":{"type":Config.NUMERIC},"po_number":{"type":Config.STRING},"requested_delivery_date":{"type":Config.DATE},"requested_delivery_date_end":{"type":Config.DATE},"ship_on_date":{"type":Config.DATE},"tax_code_id":{"type":Config.NUMERIC},"notes_public":{"type":Config.STRING},"address_error":{"type":Config.BOOLEAN},"legacy_id":{"type":Config.STRING},"shipment_method_id":{"type":Config.NUMERIC},"shipment_addr_hash":{"type":Config.STRING},"ship_phone":{"type":Config.STRING},"ship_email":{"type":Config.STRING},"ship_attn":{"type":Config.STRING},"ship_id":{"type":Config.NUMERIC},"quote_id":{"type":Config.NUMERIC},"currency":{"type":Config.STRING},"billing_id":{"type":Config.NUMERIC},"gateway_account_id":{"type":Config.NUMERIC},"shipment_account_id":{"type":Config.NUMERIC},"ship_quote_amount":{"type":Config.NUMERIC},"ship_flags":{"type":Config.NUMERIC}},"linkTo":{"customer":{"table":"customer","pql":"eq(customer_id,customer.id)"},"lead_user":{"table":"user","pql":"eq(lead_user_id,lead_user.id)"},"term":{"table":"term","pql":"eq(term_id,term.id)"},"tax_code":{"table":"tax_code","pql":"eq(tax_code_id,tax_code.id)"},"shipment_method":{"table":"shipment_method","pql":"eq(shipment_method_id,shipment_method.id)"},"bill_override":{"table":"contact","pql":"eq(billing_id,bill_override.id)"},"gateway_account":{"table":"gateway_account","pql":"eq(gateway_account_id,gateway_account.id)"},"shipment_account":{"table":"shipment_account","pql":"eq(shipment_account_id,shipment_account.id)"},"order_draft":{"table":"order_draft","pql":"eq(quote_id,order_draft.id)"}},"linkFrom":{"invoices":{"table":"invoice","pql":"eq(invoices.order_id,id)"},"shipments":{"table":"shipment","pql":"eq(shipments.order_id,id)"},"product_items":{"table":"product_item","pql":"eq(product_items.order_id,id)"},"purchases":{"table":"purchase","pql":"eq(purchases.order_id,id)"},"product_items_returns":{"table":"product_item_return","pql":"eq(product_items_returns.order_id,id)"},"product_items_reports":{"table":"product_item_report","pql":"eq(product_items_reports.order_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Order\")"},"control_flags":{"table":"control_flag","pql":"eq(control_flags.item_id,id) eq(control_flags.module,\"Order\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Order\")"},"orders_product_items_void":{"table":"orders_product_items_void","pql":"eq(orders_product_items_void.order_id,id)"}}},"order_draft":{"name":"order_drafts","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"customer_id":{"type":Config.NUMERIC},"ship_attn":{"type":Config.STRING},"ship_name":{"type":Config.STRING},"ship_address1":{"type":Config.STRING},"ship_address2":{"type":Config.STRING},"ship_city":{"type":Config.STRING},"ship_state":{"type":Config.STRING},"ship_zip":{"type":Config.STRING},"ship_country":{"type":Config.STRING},"lead_user_id":{"type":Config.NUMERIC},"order_step":{"type":Config.NUMERIC},"draft_created":{"type":Config.DATE},"term_id":{"type":Config.NUMERIC},"po_number":{"type":Config.STRING},"requested_delivery_date":{"type":Config.DATE},"requested_delivery_date_end":{"type":Config.DATE},"ship_on_date":{"type":Config.DATE},"tax_code_id":{"type":Config.NUMERIC},"shipment_method_id":{"type":Config.NUMERIC},"ship_phone":{"type":Config.STRING},"ship_email":{"type":Config.STRING},"ship_id":{"type":Config.NUMERIC},"notes":{"type":Config.STRING},"currency":{"type":Config.STRING},"billing_id":{"type":Config.NUMERIC},"gateway_account_id":{"type":Config.NUMERIC},"ship_flags":{"type":Config.NUMERIC},"shipment_account_id":{"type":Config.NUMERIC},"ship_quote_amount":{"type":Config.NUMERIC},"source":{"type":Config.STRING},"state":{"type":Config.STRING},"reserved_order_id":{"type":Config.NUMERIC},"cart_id":{"type":Config.STRING}},"linkTo":{"gateway_account":{"table":"gateway_account","pql":"eq(gateway_account_id,gateway_account.id)"},"customer":{"table":"customer","pql":"eq(customer_id,customer.id)"},"lead_user":{"table":"user","pql":"eq(lead_user_id,lead_user.id)"},"term":{"table":"term","pql":"eq(term_id,term.id)"},"tax_code":{"table":"tax_code","pql":"eq(tax_code_id,tax_code.id)"},"shipment_method":{"table":"shipment_method","pql":"eq(shipment_method_id,shipment_method.id)"},"shipment_account":{"table":"shipment_account","pql":"eq(shipment_account_id,shipment_account.id)"},"reserved_order":{"table":"order","pql":"eq(reserved_order_id,reserved_order.id)"}},"linkFrom":{"orders":{"table":"order","pql":"eq(orders.quote_id,id)"},"draft_items":{"table":"draft_item","pql":"eq(draft_items.order_draft_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Order_Draft\")"},"control_flags":{"table":"control_flag","pql":"eq(control_flags.item_id,id) eq(control_flags.module,\"Order_Draft\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Order_Draft\")"}}},"order_line":{"name":"order_lines","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"customer_part_num":{"type":Config.STRING},"edi_customer_line_id":{"type":Config.STRING},"edi_entered_price":{"type":Config.NUMERIC},"edi_entered_qty":{"type":Config.NUMERIC}},"linkTo":[],"linkFrom":{"product_items":{"table":"product_item","pql":"eq(product_items.order_line_id,id)"},"product_item_reports":{"table":"product_item_report","pql":"eq(product_item_reports.order_line_id,id)"},"product_item_returns":{"table":"product_item_return","pql":"eq(product_item_returns.order_line_id,id)"}}},"order_return":{"name":"order_returns","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"date_entered":{"type":Config.DATE},"rma_number":{"type":Config.STRING},"notes":{"type":Config.STRING},"date_returned":{"type":Config.DATE},"payment_id":{"type":Config.NUMERIC},"order_id":{"type":Config.NUMERIC},"status":{"type":Config.NUMERIC},"contact_id":{"type":Config.NUMERIC},"type":{"type":Config.STRING},"code":{"type":Config.STRING},"replacement_order_id":{"type":Config.NUMERIC},"vendor_rma_number":{"type":Config.STRING},"office_id":{"type":Config.NUMERIC},"public_note":{"type":Config.STRING}},"linkTo":{"payment":{"table":"payment","pql":"eq(payment_id,payment.id)"},"order":{"table":"order","pql":"eq(order_id,order.id)"},"contact":{"table":"contact","pql":"eq(contact_id,contact.id)"},"replacement_order":{"table":"order","pql":"eq(replacement_order_id,replacement_order.id)"},"office":{"table":"office","pql":"eq(office_id,office.id)"}},"linkFrom":{"shipments_ins":{"table":"shipment_in","pql":"eq(shipments_ins.order_return_id,id)"},"return_product_dues":{"table":"return_product_due","pql":"eq(return_product_dues.order_return_id,id)"},"product_items_returns":{"table":"product_item_return","pql":"eq(product_items_returns.return_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Order_Return\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Order_Return\")"}}},"orders_product_items_void":{"name":"orders_product_items_void","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"order_id":{"type":Config.NUMERIC},"product_item_id":{"type":Config.NUMERIC},"void_sell_price":{"type":Config.NUMERIC},"order_kit_id":{"type":Config.NUMERIC},"date_canceled":{"type":Config.DATE},"moved_from_order_id":{"type":Config.NUMERIC}},"linkTo":{"order":{"table":"order","pql":"eq(order_id,order.id)"},"order_kit":{"table":"kit_item","pql":"eq(order_kit_id,order_kit.id)"},"product_item":{"table":"product_item","pql":"eq(product_item_id,product_item.id)"},"orig_order":{"table":"order","pql":"eq(moved_from_order_id,orig_order.id)"}},"linkFrom":[]},"package":{"name":"packages","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"track_code":{"type":Config.STRING},"weight":{"type":Config.NUMERIC},"height":{"type":Config.NUMERIC},"width":{"type":Config.NUMERIC},"length":{"type":Config.NUMERIC},"shipment_id":{"type":Config.NUMERIC},"last_modified":{"type":Config.DATE}},"linkTo":{"shipment":{"table":"shipment","pql":"eq(shipment_id,shipment.id)"}},"linkFrom":{"product_items":{"table":"product_item","pql":"eq(product_items.package_id,id)"},"product_items_reports":{"table":"product_item_report","pql":"eq(product_items_reports.package_id,id)"},"product_items_returns":{"table":"product_item_return","pql":"eq(product_items_returns.package_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Package\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Package\")"}}},"payment":{"name":"payments","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"reference_number":{"type":Config.STRING},"date_claimed":{"type":Config.DATE},"date_effective":{"type":Config.DATE},"amount_applied":{"type":Config.NUMERIC},"amount_received":{"type":Config.NUMERIC},"notes":{"type":Config.STRING},"legacy_id":{"type":Config.STRING},"customer_id":{"type":Config.NUMERIC},"status":{"type":Config.NUMERIC},"date_created":{"type":Config.DATE},"payment_type":{"type":Config.STRING},"fee":{"type":Config.NUMERIC},"freight_refunded":{"type":Config.NUMERIC},"misc_refunded":{"type":Config.NUMERIC},"tax_refunded":{"type":Config.NUMERIC},"refunded_date":{"type":Config.DATE},"refunded_amount":{"type":Config.STRING},"refunded_method":{"type":Config.STRING},"refunded_notes":{"type":Config.STRING},"currency":{"type":Config.STRING}},"linkTo":{"customer":{"table":"customer","pql":"eq(customer_id,customer.id)"}},"linkFrom":{"payment_gateway_requests":{"table":"payment_gateway_request","pql":"eq(payment_gateway_requests.payment_id,id)"},"remits_payments":{"table":"remits_payments","pql":"eq(remits_payments.payment_id,id)"},"order_returns":{"table":"order_return","pql":"eq(order_returns.payment_id,id)"},"invoices_payments":{"table":"invoices_payments","pql":"eq(invoices_payments.payment_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Payment\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Payment\")"}}},"payment_gateway":{"name":"payment_gateways","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"class_name":{"type":Config.STRING}},"linkTo":[],"linkFrom":{"gateway_accounts":{"table":"gateway_account","pql":"eq(gateway_accounts.payment_gateway_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Payment_Gateway\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Payment_Gateway\")"}}},"payment_gateway_request":{"name":"payment_gateway_requests","fields":{"id":{"type":Config.NUMERIC},"request_type":{"type":Config.STRING},"gateway_account_id":{"type":Config.NUMERIC},"reference":{"type":Config.STRING},"confirmation_num":{"type":Config.STRING},"payment_id":{"type":Config.NUMERIC},"amount":{"type":Config.NUMERIC},"request_date":{"type":Config.DATE},"request_by":{"type":Config.NUMERIC},"approved":{"type":Config.BOOLEAN},"deny_reason":{"type":Config.STRING},"full_request":{"type":Config.STRING},"full_response":{"type":Config.STRING},"deleted":{"type":Config.BOOLEAN}},"linkTo":{"gateway_account":{"table":"gateway_account","pql":"eq(gateway_account_id,gateway_account.id)"},"payment":{"table":"payment","pql":"eq(payment_id,payment.id)"},"request_user":{"table":"user","pql":"eq(request_by,request_user.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Office\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Office\")"}}},"product":{"name":"products","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"date_created":{"type":Config.DATE},"description":{"type":Config.STRING},"item_code":{"type":Config.STRING},"brand_id":{"type":Config.NUMERIC},"category_id":{"type":Config.NUMERIC},"sort_order":{"type":Config.NUMERIC},"weight":{"type":Config.NUMERIC},"dimension_h":{"type":Config.NUMERIC},"dimension_w":{"type":Config.NUMERIC},"dimension_l":{"type":Config.NUMERIC},"list_price":{"type":Config.NUMERIC},"map_price":{"type":Config.NUMERIC},"notes":{"type":Config.STRING},"vendor_part_number":{"type":Config.STRING},"primary_vendor_id":{"type":Config.NUMERIC},"status":{"type":Config.NUMERIC},"taxable":{"type":Config.BOOLEAN},"freight_only":{"type":Config.BOOLEAN},"upc_code":{"type":Config.STRING},"replacement_id":{"type":Config.NUMERIC},"bin_location":{"type":Config.STRING},"has_internal_serial":{"type":Config.BOOLEAN},"has_external_serial":{"type":Config.BOOLEAN},"ht_code":{"type":Config.STRING},"date_discontinued":{"type":Config.DATE},"master_pack_qty":{"type":Config.NUMERIC},"automation_flags":{"type":Config.NUMERIC},"primary_warehouse_id":{"type":Config.NUMERIC},"date_changed":{"type":Config.DATE},"default_stock_type":{"type":Config.NUMERIC}},"linkTo":{"brand":{"table":"brand","pql":"eq(brand_id,brand.id)"},"primary_vendor":{"table":"vendor","pql":"eq(primary_vendor_id,primary_vendor.id)"},"replacement":{"table":"product","pql":"eq(replacement_id,replacement.id)"},"primary_warehouse":{"table":"office","pql":"eq(primary_warehouse_id,primary_warehouse.id)"}},"linkFrom":{"product_items":{"table":"product_item","pql":"eq(product_items.product_id,id)"},"product_item_reports":{"table":"product_item_report","pql":"eq(product_item_reports.product_id,id)"},"children":{"table":"groupping_products","pql":"eq(children.child_id,id)"},"parent":{"table":"groupping_products","pql":"eq(parent.parent_id,id)"},"products_vendors":{"table":"products_vendors","pql":"eq(products_vendors.product_id,id)"},"product_bar_codes":{"table":"product_bar_code","pql":"eq(product_bar_codes.product_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Product\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Product\")"},"franchise_products":{"table":"franchise_products","pql":"eq(franchise_products.product_id,id)"}}},"product_bar_code":{"name":"product_bar_codes","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"bar_code":{"type":Config.STRING},"product_id":{"type":Config.NUMERIC},"type":{"type":Config.STRING}},"linkTo":{"product":{"table":"product","pql":"eq(product_id,product.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Product_Bar_Code\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Product_Bar_Code\")"}}},"product_item":{"name":"product_items","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"serial_number":{"type":Config.STRING},"warehouse_id":{"type":Config.NUMERIC},"warehouse_location":{"type":Config.STRING},"date_received":{"type":Config.DATE},"date_shipped":{"type":Config.DATE},"status":{"type":Config.NUMERIC},"product_id":{"type":Config.NUMERIC},"notes":{"type":Config.STRING},"item_cost":{"type":Config.NUMERIC},"roi_cost":{"type":Config.NUMERIC},"purchase_id":{"type":Config.NUMERIC},"order_id":{"type":Config.NUMERIC},"sell_price":{"type":Config.NUMERIC},"invoice_id":{"type":Config.NUMERIC},"shipment_id":{"type":Config.NUMERIC},"sales_rep_id":{"type":Config.NUMERIC},"sort_order":{"type":Config.NUMERIC},"legacy_id":{"type":Config.STRING},"tax_amount":{"type":Config.NUMERIC},"temp_cust_id":{"type":Config.STRING},"shipment_in_id":{"type":Config.NUMERIC},"vendor_serial":{"type":Config.STRING},"override_date":{"type":Config.DATE},"order_kit_id":{"type":Config.NUMERIC},"purchase_kit_id":{"type":Config.NUMERIC},"discount":{"type":Config.NUMERIC},"starin_promo_id":{"type":Config.NUMERIC},"vendor_promo_id":{"type":Config.NUMERIC},"spiff_id":{"type":Config.NUMERIC},"program_id":{"type":Config.NUMERIC},"package_id":{"type":Config.NUMERIC},"has_changed":{"type":Config.BOOLEAN},"order_warehouse_id":{"type":Config.NUMERIC},"stock_type":{"type":Config.STRING},"order_line_id":{"type":Config.NUMERIC},"purchase_tracking_item_id":{"type":Config.NUMERIC}},"linkTo":{"warehouse":{"table":"office","pql":"eq(warehouse_id,warehouse.id)"},"product":{"table":"product","pql":"eq(product_id,product.id)"},"purchase":{"table":"purchase","pql":"eq(purchase_id,purchase.id)"},"order":{"table":"order","pql":"eq(order_id,order.id)"},"invoice":{"table":"invoice","pql":"eq(invoice_id,invoice.id)"},"shipment":{"table":"shipment","pql":"eq(shipment_id,shipment.id)"},"sales_rep":{"table":"sales_rep","pql":"eq(sales_rep_id,sales_rep.id)"},"shipment_in":{"table":"shipment_in","pql":"eq(shipment_in_id,shipment_in.id)"},"order_kit":{"table":"kit_item","pql":"eq(order_kit_id,order_kit.id)"},"purchase_kit":{"table":"kit_item","pql":"eq(purchase_kit_id,purchase_kit.id)"},"starin_promo":{"table":"promo","pql":"eq(starin_promo_id,starin_promo.id)"},"vendor_promo":{"table":"promo","pql":"eq(vendor_promo_id,vendor_promo.id)"},"spiff":{"table":"spiff","pql":"eq(spiff_id,spiff.id)"},"program":{"table":"program","pql":"eq(program_id,program.id)"},"package":{"table":"package","pql":"eq(package_id,package.id)"},"order_warehouse":{"table":"office","pql":"eq(order_warehouse_id,order_warehouse.id)"},"order_line":{"table":"order_line","pql":"eq(order_line_id,order_line.id)"},"purchase_tracking_item":{"table":"purchase_tracking_item","pql":"eq(purchase_tracking_item_id,purchase_tracking_item.id)"}},"linkFrom":{"product_items_bills":{"table":"product_items_bills","pql":"eq(product_items_bills.product_item_id,id)"},"product_items_returns":{"table":"product_item_return","pql":"eq(product_items_returns.product_item_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Product_Item\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Product_Item\")"}}},"product_item_report":{"name":"product_items_reports","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"serial_number":{"type":Config.STRING},"warehouse_id":{"type":Config.NUMERIC},"warehouse_location":{"type":Config.STRING},"date_received":{"type":Config.DATE},"date_shipped":{"type":Config.DATE},"status":{"type":Config.NUMERIC},"product_id":{"type":Config.NUMERIC},"notes":{"type":Config.STRING},"item_cost":{"type":Config.NUMERIC},"roi_cost":{"type":Config.NUMERIC},"purchase_id":{"type":Config.NUMERIC},"order_id":{"type":Config.NUMERIC},"sell_price":{"type":Config.NUMERIC},"invoice_id":{"type":Config.NUMERIC},"shipment_id":{"type":Config.NUMERIC},"sales_rep_id":{"type":Config.NUMERIC},"sort_order":{"type":Config.NUMERIC},"legacy_id":{"type":Config.STRING},"tax_amount":{"type":Config.NUMERIC},"temp_cust_id":{"type":Config.STRING},"shipment_in_id":{"type":Config.NUMERIC},"vendor_serial":{"type":Config.STRING},"return_id":{"type":Config.NUMERIC},"override_date":{"type":Config.DATE},"order_kit_id":{"type":Config.NUMERIC},"purchase_kit_id":{"type":Config.NUMERIC},"discount":{"type":Config.NUMERIC},"starin_promo_id":{"type":Config.NUMERIC},"vendor_promo_id":{"type":Config.NUMERIC},"spiff_id":{"type":Config.NUMERIC},"program_id":{"type":Config.NUMERIC},"package_id":{"type":Config.NUMERIC},"has_changed":{"type":Config.BOOLEAN},"order_warehouse_id":{"type":Config.NUMERIC},"order_line_id":{"type":Config.NUMERIC},"source":{"type":Config.STRING},"stock_type":{"type":Config.STRING},"purchase_tracking_item_id":{"type":Config.NUMERIC}},"linkTo":{"warehouse":{"table":"office","pql":"eq(warehouse_id,warehouse.id)"},"product":{"table":"product","pql":"eq(product_id,product.id)"},"purchase":{"table":"purchase","pql":"eq(purchase_id,purchase.id)"},"order":{"table":"order","pql":"eq(order_id,order.id)"},"invoice":{"table":"invoice","pql":"eq(invoice_id,invoice.id)"},"shipment":{"table":"shipment","pql":"eq(shipment_id,shipment.id)"},"sales_rep":{"table":"sales_rep","pql":"eq(sales_rep_id,sales_rep.id)"},"shipment_in":{"table":"shipment_in","pql":"eq(shipment_in_id,shipment_in.id)"},"order_kit":{"table":"kit_item","pql":"eq(order_kit_id,order_kit.id)"},"purchase_kit":{"table":"kit_item","pql":"eq(purchase_kit_id,purchase_kit.id)"},"starin_promo":{"table":"promo","pql":"eq(starin_promo_id,starin_promo.id)"},"vendor_promo":{"table":"promo","pql":"eq(vendor_promo_id,vendor_promo.id)"},"spiff":{"table":"spiff","pql":"eq(spiff_id,spiff.id)"},"program":{"table":"program","pql":"eq(program_id,program.id)"},"package":{"table":"package","pql":"eq(package_id,package.id)"},"order_warehouse":{"table":"office","pql":"eq(order_warehouse_id,order_warehouse.id)"},"order_line":{"table":"order_line","pql":"eq(order_line_id,order_line.id)"},"purchase_tracking_item":{"table":"purchase_tracking_item","pql":"eq(purchase_tracking_item_id,purchase_tracking_item.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,product_item_id) eq(notes_links.module,\"Product_Item\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,product_item_id) eq(last_modified.module,\"Product_Item\")"}}},"product_item_return":{"name":"product_items_returns","fields":{"id":{"type":Config.NUMERIC},"deleted":{"type":Config.BOOLEAN},"product_item_id":{"type":Config.NUMERIC},"return_id":{"type":Config.NUMERIC},"return_sell_price":{"type":Config.NUMERIC},"restock_fee":{"type":Config.NUMERIC},"invoice_id":{"type":Config.NUMERIC},"shipment_id":{"type":Config.NUMERIC},"order_id":{"type":Config.NUMERIC},"status":{"type":Config.NUMERIC},"date_shipped":{"type":Config.DATE},"date_return_received":{"type":Config.DATE},"sort_order":{"type":Config.NUMERIC},"sales_rep_id":{"type":Config.NUMERIC},"sell_price":{"type":Config.NUMERIC},"tax_amount":{"type":Config.NUMERIC},"shipment_in_id":{"type":Config.NUMERIC},"override_date":{"type":Config.DATE},"discount":{"type":Config.NUMERIC},"starin_promo_id":{"type":Config.NUMERIC},"vendor_promo_id":{"type":Config.NUMERIC},"spiff_id":{"type":Config.NUMERIC},"program_id":{"type":Config.NUMERIC},"package_id":{"type":Config.NUMERIC},"order_kit_id":{"type":Config.NUMERIC},"order_warehouse_id":{"type":Config.NUMERIC},"order_line_id":{"type":Config.NUMERIC},"stock_type":{"type":Config.STRING}},"linkTo":{"product_item":{"table":"product_item","pql":"eq(product_item_id,product_item.id)"},"order_return":{"table":"order_return","pql":"eq(return_id,order_return.id)"},"invoice":{"table":"invoice","pql":"eq(invoice_id,invoice.id)"},"shipment":{"table":"shipment","pql":"eq(shipment_id,shipment.id)"},"order":{"table":"order","pql":"eq(order_id,order.id)"},"sales_rep":{"table":"user","pql":"eq(sales_rep_id,sales_rep.id)"},"shipment_in":{"table":"shipment_in","pql":"eq(shipment_in_id,shipment_in.id)"},"order_kit":{"table":"kit_item","pql":"eq(order_kit_id,order_kit.id)"},"starin_promo":{"table":"promo","pql":"eq(starin_promo_id,starin_promo.id)"},"vendor_promo":{"table":"promo","pql":"eq(vendor_promo_id,vendor_promo.id)"},"spiff":{"table":"spiff","pql":"eq(spiff_id,spiff.id)"},"program":{"table":"program","pql":"eq(program_id,program.id)"},"package":{"table":"package","pql":"eq(package_id,package.id)"},"order_warehouse":{"table":"office","pql":"eq(order_warehouse_id,order_warehouse.id)"},"order_line":{"table":"order_line","pql":"eq(order_line_id,order_line.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Product_Item_Return\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Product_Item_Return\")"}}},"product_items_bills":{"name":"product_items_bills","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"product_item_id":{"type":Config.NUMERIC},"bill_id":{"type":Config.NUMERIC},"item_cost":{"type":Config.NUMERIC}},"linkTo":{"bill":{"table":"bill","pql":"eq(bill_id,bill.id)"},"product_item":{"table":"product_item","pql":"eq(product_item_id,product_item.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Product_Item_Bills\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Product_Item_Bills\")"}}},"products_vendors":{"name":"products_vendors","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"product_id":{"type":Config.NUMERIC},"vendor_id":{"type":Config.NUMERIC},"cost":{"type":Config.NUMERIC}},"linkTo":{"product":{"table":"product","pql":"eq(product_id,product.id)"},"vendor":{"table":"vendor","pql":"eq(vendor_id,vendor.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Product_Vendors\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Product_Vendors\")"}}},"program":{"name":"programs","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"start_date":{"type":Config.DATE},"end_date":{"type":Config.DATE},"date_created":{"type":Config.DATE},"date_voided":{"type":Config.DATE},"brand_id":{"type":Config.NUMERIC},"rule_id":{"type":Config.NUMERIC}},"linkTo":{"brand":{"table":"brand","pql":"eq(brand_id,brand.id)"},"rule":{"table":"rule","pql":"eq(rule_id,rule.id)"}},"linkFrom":{"product_items":{"table":"product_item","pql":"eq(product_items.program_id,id)"},"product_item_reports":{"table":"product_item_report","pql":"eq(product_item_reports.program_id,id)"},"product_item_returns":{"table":"product_item_returns","pql":"eq(product_item_returns.program_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Program\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Program\")"}}},"programs_customers":{"name":"programs_customers","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"program_id":{"type":Config.STRING},"customer_id":{"type":Config.DATE},"date_created":{"type":Config.DATE},"date_voided":{"type":Config.DATE},"parameters":{"type":Config.STRING}},"linkTo":{"program":{"table":"program","pql":"eq(program_id,program.id)"},"customer":{"table":"customer","pql":"eq(customer_id,customer.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Programs_Customers\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Programs_Customers\")"}}},"promo":{"name":"promos","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"start_date":{"type":Config.DATE},"end_date":{"type":Config.DATE},"date_created":{"type":Config.DATE},"date_voided":{"type":Config.DATE},"type":{"type":Config.STRING},"rule_id":{"type":Config.NUMERIC},"brand_id":{"type":Config.NUMERIC},"notes":{"type":Config.STRING},"promo_code":{"type":Config.STRING},"before_order_rule_id":{"type":Config.NUMERIC},"after_invoice_rule_id":{"type":Config.NUMERIC}},"linkTo":{"rule":{"table":"rule","pql":"eq(rule_id,rule.id)"},"brand_id":{"table":"brand","pql":"eq(brand_id,brand_id.id)"}},"linkFrom":{"product_item_starin_promos":{"table":"product_item","pql":"eq(product_item_starin_promos.starin_promo_id,id)"},"product_item_vendor_promos":{"table":"product_item","pql":"eq(product_item_vendor_promos.vendor_promo_id,id)"},"product_item_return_starin_promos":{"table":"product_item_return","pql":"eq(product_item_return_starin_promos.starin_promo_id,id)"},"product_item_return_vendor_promos":{"table":"product_item_return","pql":"eq(product_item_return_vendor_promos.vendor_promo_id,id)"},"product_item_report_starin_promos":{"table":"product_item_report","pql":"eq(product_item_report_starin_promos.starin_promo_id,id)"},"product_item_report_vendor_promos":{"table":"product_item_report","pql":"eq(product_item_report_vendor_promos.vendor_promo_id,id)"},"promos_franchises":{"table":"promos_franchises","pql":"eq(promos_franchises.promo_id,id)"},"promos_products":{"table":"promos_products","pql":"eq(promos_products.promo_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Promo\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Promo\")"}}},"promos_franchises":{"name":"promos_franchises","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"promo_id":{"type":Config.NUMERIC},"franchise_id":{"type":Config.NUMERIC},"date_created":{"type":Config.DATE},"date_voided":{"type":Config.DATE}},"linkTo":{"promo":{"table":"promo","pql":"eq(promo_id,promo.id)"},"franchise":{"table":"franchise","pql":"eq(franchise_id,franchise.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Promos_Franchises\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Promos_Franchises\")"}}},"promos_products":{"name":"promos_products","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"promo_id":{"type":Config.NUMERIC},"product_id":{"type":Config.NUMERIC},"date_created":{"type":Config.DATE},"date_voided":{"type":Config.DATE},"description":{"type":Config.STRING}},"linkTo":{"promo":{"table":"promo","pql":"eq(promo_id,promo.id)"},"product":{"table":"product","pql":"eq(product_id,product.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Promos_Products\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Promos_Products\")"}}},"purchase":{"name":"purchases","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"vendor_id":{"type":Config.NUMERIC},"date_ordered":{"type":Config.DATE},"notes":{"type":Config.STRING},"status":{"type":Config.NUMERIC},"term_id":{"type":Config.NUMERIC},"shipment_method_id":{"type":Config.NUMERIC},"assigned_user_id":{"type":Config.NUMERIC},"sub_total":{"type":Config.NUMERIC},"tax":{"type":Config.NUMERIC},"freight":{"type":Config.NUMERIC},"misc":{"type":Config.NUMERIC},"total":{"type":Config.NUMERIC},"legacy_id":{"type":Config.STRING},"office_id":{"type":Config.NUMERIC},"order_id":{"type":Config.NUMERIC},"notes_public":{"type":Config.STRING},"ship_account_num":{"type":Config.STRING},"est_received_date":{"type":Config.DATE},"vendor_order_number":{"type":Config.STRING}},"linkTo":{"vendor":{"table":"vendor","pql":"eq(vendor_id,vendor.id)"},"term":{"table":"term","pql":"eq(term_id,term.id)"},"shipment_method":{"table":"shipment_method","pql":"eq(shipment_method_id,shipment_method.id)"},"assigned_user":{"table":"user","pql":"eq(assigned_user_id,assigned_user.id)"},"office":{"table":"office","pql":"eq(office_id,office.id)"},"order":{"table":"order","pql":"eq(order_id,order.id)"}},"linkFrom":{"product_items":{"table":"product_item","pql":"eq(product_items.purchase_id,id)"},"product_items_reports":{"table":"product_item_report","pql":"eq(product_items_reports.purchase_id,id)"},"bills":{"table":"bill","pql":"eq(bills.purchase_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Purchase\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Purchase\")"},"control_flags":{"table":"control_flag","pql":"eq(control_flags.item_id,id) eq(control_flags.module,\"Purchase\")"}}},"purchase_tracking":{"name":"purchase_tracking","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"purchase_id":{"type":Config.NUMERIC},"estimated_receive_date":{"type":Config.DATE},"tracking_number":{"type":Config.STRING},"shipment_method_id":{"type":Config.NUMERIC},"last_updated":{"type":Config.DATE},"variance":{"type":Config.NUMERIC},"note":{"type":Config.STRING},"ref_number":{"type":Config.STRING}},"linkTo":{"purchase":{"table":"purchase","pql":"eq(purchase_id,purchase.id)"},"shipment_method":{"table":"shipment_method","pql":"eq(shipment_method_id,shipment_method.id)"}},"linkFrom":{"purchase_tracking_items":{"table":"purchase_tracking_item","pql":"eq(purchase_tracking_items.purchase_tracking_id,id)"},"control_flags":{"table":"control_flag","pql":"eq(control_flags.item_id,id) eq(control_flags.module,\"PURCHASE_TRACKING\")"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Purchase_Tracking\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Purchase_Tracking\")"}}},"purchase_tracking_item":{"name":"purchase_tracking_items","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"purchase_tracking_id":{"type":Config.NUMERIC},"product_id":{"type":Config.NUMERIC},"stock_type":{"type":Config.STRING},"qty":{"type":Config.NUMERIC},"qty_received":{"type":Config.NUMERIC}},"linkTo":{"purchase_tracking":{"table":"purchase_tracking","pql":"eq(purchase_tracking_id,purchase_tracking.id)"},"product":{"table":"product","pql":"eq(product_id,product.id)"}},"linkFrom":{"product_items":{"table":"product_item","pql":"eq(product_items.purchase_tracking_item_id,id)"},"product_items_reports":{"table":"product_item_report","pql":"eq(product_items_reports.purchase_tracking_item_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Purchase_Tracking_Item\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Purchase_Tracking_Item\")"}}},"remit":{"name":"remits","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"paid":{"type":Config.BOOLEAN},"date_created":{"type":Config.DATE},"date_voided":{"type":Config.DATE},"amount_due":{"type":Config.NUMERIC},"gateway_account_id":{"type":Config.NUMERIC}},"linkTo":{"gateway_account":{"table":"gateway_account","pql":"eq(gateway_account_id,gateway_account.id)"}},"linkFrom":{"shipments":{"table":"shipment","pql":"eq(shipments.remit_id,id)"},"remits_payments":{"table":"remits_payments","pql":"eq(remits_payments.remit_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Remit\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Remit\")"}}},"remits_payments":{"name":"remits_payments","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"remit_id":{"type":Config.NUMERIC},"payment_id":{"type":Config.NUMERIC},"amount":{"type":Config.NUMERIC},"date_applied":{"type":Config.DATE},"date_voided":{"type":Config.DATE}},"linkTo":{"payment":{"table":"payment","pql":"eq(payment_id,payment.id)"},"remit":{"table":"remit","pql":"eq(remit_id,remit.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Remits_Payments\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Remits_Payments\")"}}},"return_product_due":{"name":"return_product_due","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"product_item_id":{"type":Config.NUMERIC},"order_return_id":{"type":Config.NUMERIC}},"linkTo":{"order_return":{"table":"order_return","pql":"eq(order_return_id,order_return.id)"},"product_item":{"table":"product_item","pql":"eq(product_item_id,product_item.id)"}},"linkFrom":[]},"rule":{"name":"rules","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"code":{"type":Config.STRING},"file":{"type":Config.STRING}},"linkTo":[],"linkFrom":[]},"sales_rep":{"name":"sales_reps","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"contact_id":{"type":Config.NUMERIC},"user_id":{"type":Config.NUMERIC},"assigned_user_id":{"type":Config.NUMERIC},"is_distributor":{"type":Config.BOOLEAN},"notes":{"type":Config.STRING},"legacy_id":{"type":Config.STRING}},"linkTo":{"contact":{"table":"contact","pql":"eq(contact_id,contact.id)"},"user":{"table":"user","pql":"eq(user_id,user.id)"},"assigned_user":{"table":"user","pql":"eq(assigned_user_id,assigned_user.id)"}},"linkFrom":{"product_items":{"table":"product_item","pql":"eq(product_items.sales_rep_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Sales_Rep\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Sales_Rep\")"}}},"shipment":{"name":"shipments","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"shipment_method_id":{"type":Config.NUMERIC},"track_code":{"type":Config.STRING},"notes":{"type":Config.STRING},"actual_cost":{"type":Config.NUMERIC},"ship_date":{"type":Config.DATE},"date_due":{"type":Config.DATE},"shipment_fee":{"type":Config.NUMERIC},"handling_fee":{"type":Config.NUMERIC},"flags":{"type":Config.NUMERIC},"shipment_addr_hash":{"type":Config.STRING},"customer_id":{"type":Config.NUMERIC},"bill_id":{"type":Config.NUMERIC},"term_id":{"type":Config.NUMERIC},"backorder_hash":{"type":Config.STRING},"remit_id":{"type":Config.NUMERIC},"discount_amount":{"type":Config.NUMERIC},"shipment_account_id":{"type":Config.NUMERIC},"shipment_flags":{"type":Config.NUMERIC}},"linkTo":{"shipment_method":{"table":"shipment_method","pql":"eq(shipment_method_id,shipment_method.id)"},"customer":{"table":"customer","pql":"eq(customer_id,customer.id)"},"bill":{"table":"bill","pql":"eq(bill_id,bill.id)"},"term":{"table":"term","pql":"eq(term_id,term.id)"},"remit":{"table":"remit","pql":"eq(remit_id,remit.id)"},"shipment_account":{"table":"shipment_account","pql":"eq(shipment_account_id,shipment_account.id)"}},"linkFrom":{"product_items":{"table":"product_item","pql":"eq(product_items.shipment_id,id)"},"product_items_returns":{"table":"product_item_return","pql":"eq(product_items_returns.shipment_id,id)"},"product_items_reports":{"table":"product_item_report","pql":"eq(product_items_reports.shipment_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Shipment\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Shipment\")"},"invoices":{"table":"invoice","pql":"eq(invoices.shipment_id,id)"}}},"shipment_account":{"name":"shipment_accounts","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"customer_id":{"type":Config.NUMERIC},"shipment_method_id":{"type":Config.NUMERIC},"ship_account_num":{"type":Config.STRING},"third_party_contact_id":{"type":Config.NUMERIC}},"linkTo":{"customer":{"table":"customer","pql":"eq(customer_id,customer.id)"},"shipment_method":{"table":"shipment_method","pql":"eq(shipment_method_id,shipment_method.id)"},"third_party_contact":{"table":"contact","pql":"eq(third_party_contact_id,third_party_contact.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Shipment_Account\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Shipment_Account\")"}}},"shipment_in":{"name":"shipments_in","fields":{"id":{"type":Config.NUMERIC},"deleted":{"type":Config.BOOLEAN},"date_entered":{"type":Config.DATE},"purchase_id":{"type":Config.NUMERIC},"order_return_id":{"type":Config.NUMERIC},"notes":{"type":Config.STRING},"track_code":{"type":Config.STRING},"office_id":{"type":Config.NUMERIC},"shipment_method_id":{"type":Config.NUMERIC}},"linkTo":{"purchase":{"table":"purchase","pql":"eq(purchase_id,purchase.id)"},"order_return":{"table":"order_return","pql":"eq(order_return_id,order_return.id)"},"office":{"table":"office","pql":"eq(office_id,office.id)"},"shipment_method":{"table":"shipment_method","pql":"eq(shipment_method_id,shipment_method.id)"}},"linkFrom":{"product_items":{"table":"product_item","pql":"eq(product_items.shipment_in_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Shipment_In\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Shipment_In\")"}}},"shipment_method":{"name":"shipment_methods","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"customer_id":{"type":Config.NUMERIC},"prepaid":{"type":Config.BOOLEAN},"carrier_code":{"type":Config.STRING},"carrier":{"type":Config.STRING},"parent_shipment_method_id":{"type":Config.NUMERIC},"carrier_id":{"type":Config.NUMERIC},"transportaion_method_code":{"type":Config.STRING},"is_freight":{"type":Config.BOOLEAN},"time_in_transit":{"type":Config.NUMERIC},"service_level_code":{"type":Config.STRING}},"linkTo":{"customer":{"table":"customer","pql":"eq(customer_id,customer.id)"},"parent_shipment_method":{"table":"shipment_method","pql":"eq(parent_shipment_method_id,parent_shipment_method.id)"},"scac":{"table":"carrier","pql":"eq(carrier_id,scac.id)"}},"linkFrom":{"purchases":{"table":"purchase","pql":"eq(purchases.shipment_method_id,id)"},"shipments":{"table":"shipment","pql":"eq(shipments.shipment_method_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Shipment_Method\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Shipment_Method\")"},"child_shipment_methods":{"table":"shipment_method","pql":"eq(child_shipment_methods.parent_shipment_method_id,id)"},"shipment_accounts":{"table":"shipment_account","pql":"eq(shipment_accounts.shipment_method_id,id)"}}},"spiff":{"name":"spiffs","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"start_date":{"type":Config.DATE},"end_date":{"type":Config.DATE},"date_created":{"type":Config.DATE},"date_voided":{"type":Config.DATE},"notes":{"type":Config.STRING},"max":{"type":Config.NUMERIC}},"linkTo":[],"linkFrom":{"spiffs_franchises":{"table":"spiffs_franchises","pql":"eq(spiffs_franchises.spiff_id,id)"},"spiffs_products":{"table":"spiffs_products","pql":"eq(spiffs_products.spiff_id,id)"},"product_items":{"table":"product_item","pql":"eq(product_items.spiff_id,id)"},"product_item_returns":{"table":"product_item_return","pql":"eq(product_item_returns.spiff_id,id)"},"product_item_reports":{"table":"product_item_report","pql":"eq(product_item_reports.spiff_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Spiff\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Spiff\")"}}},"spiffs_franchises":{"name":"spiffs_franchises","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"spiff_id":{"type":Config.NUMERIC},"franchise_id":{"type":Config.NUMERIC},"date_created":{"type":Config.DATE},"date_voided":{"type":Config.DATE}},"linkTo":{"spiff":{"table":"spiff","pql":"eq(spiff_id,spiff.id)"},"franchise":{"table":"franchise","pql":"eq(franchise_id,franchise.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Spiff_Franchises\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Spiff_Franchises\")"}}},"spiffs_products":{"name":"spiffs_products","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"spiff_id":{"type":Config.NUMERIC},"product_id":{"type":Config.NUMERIC},"qty":{"type":Config.NUMERIC},"date_created":{"type":Config.DATE},"date_voided":{"type":Config.DATE}},"linkTo":{"spiff":{"table":"spiff","pql":"eq(spiff_id,spiff.id)"},"product":{"table":"product","pql":"eq(product_id,product.id)"}},"linkFrom":{"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Spiffs_Products\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Spiffs_Products\")"}}},"tax_code":{"name":"tax_codes","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"percentage":{"type":Config.NUMERIC},"legacy_id":{"type":Config.STRING},"shipment_tax":{"type":Config.NUMERIC}},"linkTo":[],"linkFrom":{"orders":{"table":"order","pql":"eq(orders.tax_code_id,id)"},"customers":{"table":"customer","pql":"eq(customers.tax_code_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Tax_Code\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Tax_Code\")"}}},"term":{"name":"terms","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"due_days":{"type":Config.NUMERIC},"discount":{"type":Config.NUMERIC},"discount_days":{"type":Config.NUMERIC},"legacy_id":{"type":Config.STRING},"order_override_flags":{"type":Config.NUMERIC},"classification":{"type":Config.STRING},"grace_days":{"type":Config.NUMERIC},"edi_term_code_id":{"type":Config.NUMERIC}},"linkTo":{"edi_term_code":{"table":"edi_term_code","pql":"eq(edi_term_code_id,edi_term_code.id)"}},"linkFrom":{"purchases":{"table":"purchase","pql":"eq(purchases.term_id,id)"},"bills":{"table":"bill","pql":"eq(bills.term_id,id)"},"customers":{"table":"customer","pql":"eq(customers.term_id,id)"},"vendors":{"table":"vendor","pql":"eq(vendors.term_id,id)"},"orders":{"table":"order","pql":"eq(orders.term_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Term\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Term\")"}}},"territory":{"name":"territories","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"notes":{"type":Config.STRING},"legacy_id":{"type":Config.STRING},"country":{"type":Config.STRING},"region":{"type":Config.STRING},"state_province":{"type":Config.STRING}},"linkTo":[],"linkFrom":{"customers":{"table":"customer","pql":"eq(customers.territory_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Territory\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Territory\")"}}},"timezone":{"name":"timezones","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"utc":{"type":Config.STRING},"dst":{"type":Config.STRING}},"linkTo":[],"linkFrom":{"contacts":{"table":"contact","pql":"eq(contacts.timezone_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Timezone\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Timezone\")"}}},"user":{"name":"users_view","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"username":{"type":Config.STRING},"password":{"type":Config.STRING},"can_login":{"type":Config.BOOLEAN},"employee_id":{"type":Config.NUMERIC},"contact_id":{"type":Config.NUMERIC},"legacy_id":{"type":Config.STRING},"wh_scan":{"type":Config.STRING}},"linkTo":{"employee":{"table":"employee","pql":"eq(employee_id,employee.id)"},"contact":{"table":"contact","pql":"eq(contact_id,contact.id)"}},"linkFrom":{"user_historys":{"table":"user_history","pql":"eq(user_historys.user_id,id)"},"voided_gateway_requests":{"table":"payment_gateway_request","pql":"eq(voided_gateway_requests.void_by,id)"},"approved_gateway_requests":{"table":"payment_gateway_request","pql":"eq(approved_gateway_requests.approved_by,id)"},"requested_gateway_requests":{"table":"payment_gateway_request","pql":"eq(requested_gateway_requests.request_by,id)"},"offices":{"table":"office","pql":"eq(offices.foreman_id,id)"},"customers":{"table":"customer","pql":"eq(customers.primary_rep_id,id)"},"purchases":{"table":"purchase","pql":"eq(purchases.assigned_user_id,id)"},"orders":{"table":"order","pql":"eq(orders.assigned_user_id,id)"},"sales_reps":{"table":"sales_rep","pql":"eq(sales_reps.user_id,id)"},"assigned_sales_reps":{"table":"sales_rep","pql":"eq(assigned_sales_reps.assigned_user_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"User\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"User\")"}}},"user_history":{"name":"user_historys","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"user_id":{"type":Config.NUMERIC},"method":{"type":Config.STRING},"args":{"type":Config.STRING},"window_id":{"type":Config.NUMERIC},"date":{"type":Config.DATE}},"linkTo":{"user":{"table":"user","pql":"eq(user_id,user.id)"}},"linkFrom":[]},"vendor":{"name":"vendors","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"name":{"type":Config.STRING},"notes":{"type":Config.STRING},"primary_contact_id":{"type":Config.NUMERIC},"term_id":{"type":Config.NUMERIC},"legacy_id":{"type":Config.STRING},"temp_mybooks_id":{"type":Config.STRING},"our_account_number":{"type":Config.STRING},"default_shipment_method_id":{"type":Config.NUMERIC},"default_office_id":{"type":Config.NUMERIC}},"linkTo":{"term":{"table":"term","pql":"eq(term_id,term.id)"},"primary_contact":{"table":"contact","pql":"eq(primary_contact_id,primary_contact.id)"},"default_shipment_method":{"table":"shipment_method","pql":"eq(default_shipment_method_id,default_shipment_method.id)"},"default_office":{"table":"office","pql":"eq(default_office_id,default_office.id)"}},"linkFrom":{"purchases":{"table":"purchase","pql":"eq(purchases.vendor_id,id)"},"contacts":{"table":"contact","pql":"eq(contacts.vendor_id,id)"},"primary_products":{"table":"product","pql":"eq(primary_products.vendor_id,id)"},"bills":{"table":"bill","pql":"eq(bills.vendor_id,id)"},"products_vendors":{"table":"products_vendors","pql":"eq(products_vendors.vendor_id,id)"},"notes_links":{"table":"notes_link","pql":"eq(notes_links.item_id,id) eq(notes_links.module,\"Vendor\")"},"last_modified":{"table":"last_modified","pql":"eq(last_modified.item_id,id) eq(last_modified.module,\"Vendor\")"}}},"watch":{"name":"watchs","fields":{"deleted":{"type":Config.BOOLEAN},"id":{"type":Config.NUMERIC},"user_id":{"type":Config.NUMERIC},"record_id":{"type":Config.NUMERIC},"created":{"type":Config.DATE},"note":{"type":Config.STRING},"memute_id":{"type":Config.NUMERIC}},"linkTo":{"user":{"table":"user","pql":"eq(user_id,user.id)"},"memute":{"table":"memute","pql":"eq(memute_id,memute.id)"}},"linkFrom":[]}}; + + +import { EQUAL } from './opcodes/comparitors/equal.js'; +import { GREATER_THAN } from './opcodes/comparitors/greater_than.js'; +import { LESS_THAN } from './opcodes/comparitors/less_than.js'; +import { LIKE } from './opcodes/comparitors/like.js'; +import { NO_VALUE } from './opcodes/comparitors/no_value.js'; +import { NOT_EQUAL } from './opcodes/comparitors/not_equal.js'; +import { NOT_LIKE } from './opcodes/comparitors/not_like.js'; + +import { CONSTANTS_ARRAY } from './opcodes/constants_array.js'; +import { NULL } from './opcodes/null.js'; Config.FUNCTION_MAP = { eq: { + description: 'Compares two values', min_args: 2, max_args: 2, return_type: Config.BOOLEAN, - format: [Config.ARG1, ' = ', Config.ARG2], + format: function (args, orig_args, query_obj){ + var pql = this.getPqlObj(); + var obj = new EQUAL(pql); + obj.setLeft(orig_args[0]); + obj.setRight(orig_args[1]); + return obj.getSQL(query_obj); + }, }, gt: { + description: 'Checks if first value is greater than second', min_args: 2, max_args: 2, return_type: Config.BOOLEAN, - format: [Config.ARG1, ' > ', Config.ARG2], + format: function (args, orig_args, query_obj){ + var pql = this.getPqlObj(); + var obj = new GREATER_THAN(pql); + obj.setLeft(orig_args[0]); + obj.setRight(orig_args[1]); + return obj.getSQL(query_obj); + }, }, lt: { + description: 'Checks if first value is less than second', min_args: 2, max_args: 2, return_type: Config.BOOLEAN, - format: [Config.ARG1, ' < ', Config.ARG2], + format: function (args, orig_args, query_obj){ + var pql = this.getPqlObj(); + var obj = new LESS_THAN(pql); + obj.setLeft(orig_args[0]); + obj.setRight(orig_args[1]); + return obj.getSQL(query_obj); + }, }, ne: { + description: 'Checks if values are not equal', min_args: 2, max_args: 2, return_type: Config.BOOLEAN, - format: [Config.ARG1, ' != ', Config.ARG2], + format: function (args, orig_args, query_obj){ + var pql = this.getPqlObj(); + var obj = new NOT_EQUAL(pql); + obj.setLeft(orig_args[0]); + obj.setRight(orig_args[1]); + return obj.getSQL(query_obj); + }, }, like: { + description: 'Checks if first value is like second. % sign is wild card character', min_args: 2, max_args: 2, return_type: Config.BOOLEAN, - format: [Config.ARG1, ' LIKE ', function (args, orig_args){}], + format: function (args, orig_args, query_obj){ + var pql = this.getPqlObj(); + var obj = new LIKE(pql); + obj.setLeft(orig_args[0]); + obj.setRight(orig_args[1]); + return obj.getSQL(query_obj); + }, }, not_like: { + description: 'Checks if first value is not like second. % sign is wild card character', min_args: 2, max_args: 2, return_type: Config.BOOLEAN, - format: [Config.ARG1, ' NOT LIKE ', Config.ARG2], + format: function (args, orig_args, query_obj){ + var pql = this.getPqlObj(); + var obj = new NOT_LIKE(pql); + obj.setLeft(orig_args[0]); + obj.setRight(orig_args[1]); + return obj.getSQL(query_obj); + }, }, 'in': { - min_args: 1, + description: 'Checks if first value is any of the following values', + min_args: 2, max_args: Infinity, return_type: Config.BOOLEAN, - format: ['IN(', Config.ALL_ARGS, ')'], + format: function (args, orig_args, query_obj){ + var pql = this.getPqlObj(); + var out_args = []; + var obj = new EQUAL(pql); + orig_args.splice(1).forEach((v) => { + out_args.push(v); + }); + obj.setLeft(orig_args[0]); + obj.setRight(new CONSTANTS_ARRAY(pql, out_args)); + return obj.getSQL(query_obj); + }, }, not_in: { - min_args: 1, + description: 'Checks if first value is not any of the following values', + min_args: 2, max_args: Infinity, return_type: Config.BOOLEAN, - format: ['NOT IN(', Config.ALL_ARGS, ')'], + format: function (args, orig_args, query_obj){ + var pql = this.getPqlObj(); + var out_args = []; + var obj = new NOT_EQUAL(pql); + orig_args.splice(1).forEach((v) => { + out_args.push(v); + }); + obj.setLeft(orig_args[0]); + obj.setRight(new CONSTANTS_ARRAY(pql, out_args)); + return obj.getSQL(query_obj); + }, }, 'if': { + description: 'If first argument is truethy returns second argument otherwise returns third', min_args: 3, max_args: 3, return_type: Config.ANY, format: ['IF(', Config.ALL_ARGS, ')'], }, + /* Math functions */ + add: { + description: 'Adds values together', + min_args: 2, + max_args: Infinity, + return_type: Config.NUMBER, + format: function (args, orig_args, query_obj){ + return args.join(' + '); + }, + }, + sub: { + description: 'Subtracts values from eachother', + min_args: 2, + max_args: Infinity, + return_type: Config.NUMBER, + format: function (args, orig_args, query_obj){ + return args.join(' - '); + }, + }, + mul: { + description: 'Multiplies values together', + min_args: 2, + max_args: Infinity, + return_type: Config.NUMBER, + format: function (args, orig_args, query_obj){ + return args.join(' * '); + }, + }, + div: { + description: 'Divide values together', + min_args: 2, + max_args: Infinity, + return_type: Config.NUMBER, + format: function (args, orig_args, query_obj){ + return args.join(' / '); + }, + }, + mod: { + description: 'Modulus values together', + min_args: 2, + max_args: 2, + return_type: Config.NUMBER, + format: ['MOD(', Config.ARG1, ', ', Config.ARG2, ')'], + }, + pow: { + description: 'Powers values together', + min_args: 2, + max_args: 2, + return_type: Config.NUMBER, + format: ['POW(', Config.ARG1, ', ', Config.ARG2, ')'], + }, + sqrt: { + description: 'Square roots a value', + min_args: 1, + max_args: 1, + return_type: Config.NUMBER, + format: ['SQRT(', Config.ARG1, ')'], + }, /* Standard SQL functions */ abs: { + description: 'Gets absolute value', min_args: 1, max_args: 1, arg_types: [ @@ -156,6 +235,7 @@ Config.FUNCTION_MAP = { format: ['ABS(', Config.ARG1, ')'], }, 'char': { + description: 'Gets character from numerical ascii character', min_args: 1, max_args: Infinity, arg_types: [ @@ -165,18 +245,21 @@ Config.FUNCTION_MAP = { format: ['CHAR(', Config.ALL_ARGS, ')'], }, coalesce: { + description: 'Returns first non-null value from arguments', min_args: 1, max_args: Infinity, return_type: Config.ANY_TYPE, format: ['COALESCE(', Config.ALL_ARGS, ')'], }, ifnull: { + description: 'Returns first argument if not null otherwise returns second argument', min_args: 2, max_args: 2, return_type: Config.ANY_TYPE, format: ['IFNULL(', Config.ARG1, ', ', Config.ARG2, ')'], }, instr: { + description: 'Gets character position of argument two in argument one\'s string', min_args: 2, max_args: 2, return_type: Config.NUMERIC, @@ -187,12 +270,14 @@ Config.FUNCTION_MAP = { format: ['INSTR(', Config.ARG1, ', ', Config.ARG2, ')'], }, hex: { + description: 'Returns hex value of argument', min_args: 1, max_args: 1, return_type: Config.STRING, format: ['HEX(', Config.ARG1, ')'], }, length: { + description: 'Returns the string length', min_args: 1, max_args: 1, arg_types: [ @@ -202,6 +287,7 @@ Config.FUNCTION_MAP = { format: ['LENGTH(', Config.ARG1, ')'], }, lower: { + description: 'Returns string converted to lower case', min_args: 1, max_args: 1, arg_types: [ @@ -211,6 +297,7 @@ Config.FUNCTION_MAP = { format: ['LOWER(', Config.ARG1, ')'], }, ltrim: { + description: 'Returns left-trimmed string', min_args: 1, max_args: 1, arg_types: [ @@ -220,18 +307,21 @@ Config.FUNCTION_MAP = { format: ['LTRIM(', Config.ARG1, ')'], }, nullif: { + description: 'Returns null if argument one equals argument two', min_args: 2, max_args: 2, return_type: Config.ANY_TYPE, format: ['NULLIF(', Config.ARG1, ', ', Config.ARG2, ')'], }, random: { + description: 'Returns random number (platform dependent)', min_args: 0, max_args: 0, return_type: Config.NUMERIC, format: ['RANDOM()'], }, replace: { + description: 'Searches for argument two and replaces matches with argument three in argument one', min_args: 3, max_args: 3, arg_types: [ @@ -243,6 +333,7 @@ Config.FUNCTION_MAP = { format: ['REPLACE(', Config.ARG1, ', ', Config.ARG2, ', ', Config.ARG3, ')'], }, round: { + description: 'Returns rounded value of argument one with decimal percision of argument two', min_args: 1, max_args: 2, arg_types: [ @@ -253,6 +344,7 @@ Config.FUNCTION_MAP = { format: ['ROUND(', Config.ALL_ARGS, ')'], }, rtrim: { + description: 'Returns right-trimmed value', min_args: 1, max_args: 1, arg_types: [ @@ -262,6 +354,7 @@ Config.FUNCTION_MAP = { format: ['RTRIM(', Config.ARG1, ')'], }, substr: { + description: 'Returns part of argument one string from argument two position with length of argument three', min_args: 2, max_args: 3, arg_types: [ @@ -273,6 +366,7 @@ Config.FUNCTION_MAP = { format: ['SUBSTR(', Config.ALL_ARGS, ')'], }, trim: { + description: 'Returns full trimmed string', min_args: 1, max_args: 1, arg_types: [ @@ -282,15 +376,7 @@ Config.FUNCTION_MAP = { format: ['TRIM(', Config.ARG1, ')'], }, upper: { - min_args: 1, - max_args: 1, - arg_types: [ - Config.STRING, - ], - return_type: Config.STRING, - format: ['UPPER(', Config.ARG1, ')'], - }, - upper: { + description: 'Returns value converted to upper case', min_args: 1, max_args: 1, arg_types: [ @@ -300,6 +386,7 @@ Config.FUNCTION_MAP = { format: ['UPPER(', Config.ARG1, ')'], }, concat: { + description: 'Returns multiple strings joined together', min_args: 1, max_args: Infinity, arg_types: [ @@ -311,6 +398,7 @@ Config.FUNCTION_MAP = { /* Date Functions */ date_format: { + description: 'Returns date converted converted using format of argument two', min_args: 2, max_args: 2, arg_types: [ @@ -321,6 +409,7 @@ Config.FUNCTION_MAP = { format: ['DATE_FORMAT(', Config.ARG1, ', ', Config.ARG2,')'], }, date: { + description: 'Returns date porition of argument one', min_args: 1, max_args: 1, arg_types: [ @@ -330,6 +419,7 @@ Config.FUNCTION_MAP = { format: ['DATE(', Config.ARG1, ')'], }, day: { + description: 'Returns day number of date', min_args: 1, max_args: 1, arg_types: [ @@ -339,6 +429,7 @@ Config.FUNCTION_MAP = { format: ['DAY(', Config.ARG1, ')'], }, from_unixtime: { + description: 'Returns date type from unix timestamp', min_args: 1, max_args: 1, arg_types: [ @@ -348,15 +439,7 @@ Config.FUNCTION_MAP = { format: ['FROM_UNIXTIME(', Config.ARG1, ')'], }, hour: { - min_args: 1, - max_args: 1, - arg_types: [ - Config.DATE, - ], - return_type: Config.NUMERIC, - format: ['HOUR(', Config.ARG1, ')'], - }, - hour: { + description: 'Returns hour portion of date', min_args: 1, max_args: 1, arg_types: [ @@ -366,6 +449,7 @@ Config.FUNCTION_MAP = { format: ['HOUR(', Config.ARG1, ')'], }, minute: { + description: 'Returns minute porition of date', min_args: 1, max_args: 1, arg_types: [ @@ -375,6 +459,7 @@ Config.FUNCTION_MAP = { format: ['MINUTE(', Config.ARG1, ')'], }, month: { + description: 'Returns month number porition of date', min_args: 1, max_args: 1, arg_types: [ @@ -384,12 +469,14 @@ Config.FUNCTION_MAP = { format: ['MONTH(', Config.ARG1, ')'], }, now: { + description: 'Returns current time/date', min_args: 0, max_args: 0, return_type: Config.DATE, format: ['NOW()'], }, second: { + description: 'Returns second porition of date', min_args: 1, max_args: 1, arg_types: [ @@ -399,6 +486,7 @@ Config.FUNCTION_MAP = { format: ['SECOND(', Config.ARG1, ')'], }, time: { + description: 'Returns time porition of date', min_args: 1, max_args: 1, arg_types: [ @@ -408,6 +496,7 @@ Config.FUNCTION_MAP = { format: ['TIME(', Config.ARG1, ')'], }, unix_timestamp: { + description: 'Returns unix timestamp from date', min_args: 0, max_args: 1, arg_types: [ @@ -417,6 +506,7 @@ Config.FUNCTION_MAP = { format: ['UNIX_TIMESTAMP(', Config.ARG1, ')'], }, year: { + description: 'Returns year porition from date', min_args: 1, max_args: 1, arg_types: [ @@ -428,6 +518,7 @@ Config.FUNCTION_MAP = { /* Aggregate functions */ avg: { + description: 'Returns average value', min_args: 1, max_args: 2, arg_types: [ @@ -448,6 +539,7 @@ Config.FUNCTION_MAP = { }, }, count: { + description: 'Returns count of items', min_args: 1, max_args: 2, arg_types: [ @@ -469,6 +561,7 @@ Config.FUNCTION_MAP = { }, // group_concat(expr[,distinct[,seperator[,order_by_expression,order_by_direction ...]]]); group_concat: { + description: 'Returns the group concatinated string', min_args: 1, max_args: Infinity, arg_types: [ @@ -482,12 +575,14 @@ Config.FUNCTION_MAP = { is_group_function: true, format: (args, orig_args) => { let distinct = (orig_args.length >= 2) && Boolean(Number(orig_args[1].getValue())) ? 'DISTINCT ' : ''; - let seperator; + let seperator = ''; if (orig_args.length >= 3) { - if (!orig_args[2].isConstant()) { + if (!orig_args[2].isConstant() && !(orig_args[2].isInstanceOf(NULL))) { throw `Third argument of "group_concat" function must be a constant type`; } - seperator = ` SEPARATOR ${ args[2] }`; + if (!(orig_args[2].isInstanceOf(NULL))) { + seperator = ` SEPARATOR ${ args[2] }`; + } } let order_bys = new Set; if (orig_args.length > 3) { @@ -497,13 +592,16 @@ Config.FUNCTION_MAP = { } // Inc in order of 2's for (let i = 3; i < args.length; i=i+2) { - let dir = orig_args[i+1].getValue().toString().toLowerCase() == 'desc' ? 'DESC' : 'ASC'; + let dir = 'ASC'; + if (orig_args[i+1].isConstant()) { + dir = orig_args[i+1].getValue().toString().toLowerCase() == 'desc' ? 'DESC' : 'ASC'; + } order_bys.add(`${ args[i] } ${ dir }`); } } let order_by; if (order_bys.size) { - order_by = ` ORDER BY ${ order_bys.values().join(', ') }` + order_by = ` ORDER BY ${ Array.from(order_bys).join(',') }` } else { order_by = ''; } @@ -511,6 +609,7 @@ Config.FUNCTION_MAP = { }, }, max: { + description: 'Returns maximum value', min_args: 1, max_args: 2, arg_types: [ @@ -531,6 +630,7 @@ Config.FUNCTION_MAP = { }, }, min: { + description: 'Returns minimum value', min_args: 1, max_args: 2, arg_types: [ @@ -551,6 +651,7 @@ Config.FUNCTION_MAP = { }, }, sum: { + description: 'Returns sum of values', min_args: 1, max_args: 2, arg_types: [ @@ -571,6 +672,7 @@ Config.FUNCTION_MAP = { }, }, having: { + description: 'Forces item into having statment', min_args: 1, max_args: 1, return_type: Config.ANY, @@ -579,23 +681,12 @@ Config.FUNCTION_MAP = { } }; -import { EQUAL } from './opcodes/comparitors/equal.js'; -import { GREATER_THAN } from './opcodes/comparitors/greater_than.js'; -import { IN } from './opcodes/comparitors/in.js'; -import { LESS_THAN } from './opcodes/comparitors/less_than.js'; -import { LIKE } from './opcodes/comparitors/like.js'; -import { NO_VALUE } from './opcodes/comparitors/no_value.js'; -import { NOT_EQUAL } from './opcodes/comparitors/not_equal.js'; -import { NOT_IN } from './opcodes/comparitors/not_in.js'; -import { NOT_LIKE } from './opcodes/comparitors/not_like.js'; Config.COMPARITORS = new Set([ EQUAL, GREATER_THAN, - IN, LESS_THAN, LIKE, NO_VALUE, NOT_EQUAL, - NOT_IN, NOT_LIKE, ]); \ No newline at end of file diff --git a/pql/opcodes/comparitors/equal.js b/pql/opcodes/comparitors/equal.js index 51e0141..566dd6f 100644 --- a/pql/opcodes/comparitors/equal.js +++ b/pql/opcodes/comparitors/equal.js @@ -1,5 +1,6 @@ import { COMPARITOR } from './comparitor.js'; import { NULL } from './../null.js'; +import { CONSTANTS_ARRAY } from './../constants_array.js'; export class EQUAL extends COMPARITOR { static set comparitors (v) {} @@ -15,8 +16,15 @@ export class EQUAL extends COMPARITOR { if (this.right.getType) { right_type = this.right.getType(); } - if (this.right instanceof NULL) { + if (this.right.isInstanceOf(NULL)) { return this.left.getSQL(query_object, right_type) + ' IS NULL'; + } else if (this.right.isInstanceOf(CONSTANTS_ARRAY)) { + let right_values = this.right.getValue(); + let sql_safe_values = []; + right_values.forEach((v) => { + sql_safe_values.push(v.getSQL(query_object, left_type)); + }); + return this.left.getSQL(query_object, right_type) + ' IN (' + sql_safe_values.join(', ') + ')'; } else { return this.left.getSQL(query_object, right_type) + ' = ' + this.right.getSQL(query_object, left_type); } diff --git a/pql/opcodes/comparitors/greater_than.js b/pql/opcodes/comparitors/greater_than.js index ff400a2..a47e8e9 100644 --- a/pql/opcodes/comparitors/greater_than.js +++ b/pql/opcodes/comparitors/greater_than.js @@ -1,4 +1,5 @@ import { COMPARITOR } from './comparitor.js'; +import { CONSTANTS_ARRAY } from './../constants_array.js'; export class GREATER_THAN extends COMPARITOR { static set comparitors (v) {} @@ -14,6 +15,17 @@ export class GREATER_THAN extends COMPARITOR { if (this.right.getType) { right_type = this.right.getType(); } - return this.left.getSQL(query_object, right_type) + ' > ' + this.right.getSQL(query_object, left_type); + if (this.right.isInstanceOf(CONSTANTS_ARRAY)) { + let right_values = this.right.getValue(); + let sql_safe_values = []; + let left_sql = this.left.getSQL(query_object, right_type); + right_values.forEach((v) => { + sql_safe_values.push(left_sql + ' > ' + v.getSQL(query_object, left_type)); + }); + + return `(${ sql_safe_values.join(' AND ') })`; + } else { + return this.left.getSQL(query_object, right_type) + ' > ' + this.right.getSQL(query_object, left_type); + } } } diff --git a/pql/opcodes/comparitors/in.js b/pql/opcodes/comparitors/in.js deleted file mode 100644 index fa3cb37..0000000 --- a/pql/opcodes/comparitors/in.js +++ /dev/null @@ -1,12 +0,0 @@ -import { COMPARITOR } from './comparitor.js'; - -export class IN extends COMPARITOR { - static set comparitors (v) {} - static get comparitors () { - return ['|']; - } - getSQL (query_object) { - //TODO: FIX! - return this.left.getSQL(query_object) + ' > ' + this.right.getSQL(query_object); - } -} diff --git a/pql/opcodes/comparitors/less_than.js b/pql/opcodes/comparitors/less_than.js index a0e19cc..4465e8d 100644 --- a/pql/opcodes/comparitors/less_than.js +++ b/pql/opcodes/comparitors/less_than.js @@ -1,4 +1,5 @@ import { COMPARITOR } from './comparitor.js'; +import { CONSTANTS_ARRAY } from './../constants_array.js'; export class LESS_THAN extends COMPARITOR { static set comparitors (v) {} @@ -14,6 +15,17 @@ export class LESS_THAN extends COMPARITOR { if (this.right.getType) { right_type = this.right.getType(); } - return this.left.getSQL(query_object, right_type) + ' < ' + this.right.getSQL(query_object, left_type); + if (this.right.isInstanceOf(CONSTANTS_ARRAY)) { + let right_values = this.right.getValue(); + let sql_safe_values = []; + let left_sql = this.left.getSQL(query_object, right_type); + right_values.forEach((v) => { + sql_safe_values.push(left_sql + ' < ' + v.getSQL(query_object, left_type)); + }); + + return `(${ sql_safe_values.join(' AND ') })`; + } else { + return this.left.getSQL(query_object, right_type) + ' < ' + this.right.getSQL(query_object, left_type); + } } } diff --git a/pql/opcodes/comparitors/like.js b/pql/opcodes/comparitors/like.js index ecfedec..fc93a62 100644 --- a/pql/opcodes/comparitors/like.js +++ b/pql/opcodes/comparitors/like.js @@ -1,21 +1,50 @@ import { COMPARITOR } from './comparitor.js'; +import { NULL } from './../null.js'; import { CONSTANT } from './../constant.js'; +import { CONSTANTS_ARRAY } from './../constants_array.js'; export class LIKE extends COMPARITOR { static set comparitors (v) {} static get comparitors () { return ['~']; } - get right () { - return this._right; - } - set right (v) { - if (v instanceof CONSTANT) { - v.setValue(v.getValue() + '%'); + static _getSQLHelper (query_object, left, right) { + var val; + var left_sql; + if (typeof left === 'string') { + left_sql = left; + } else { + left_sql = left.getSQL(query_object); + } + // Hack used to add the wild card char + if (right.isInstanceOf(CONSTANT)) { + val = right.getValue(); + right.setValue(val + '%'); + } + var ret_val; + if (right.isInstanceOf(NULL)) { + ret_val = left_sql + ' IS NULL'; + } else { + ret_val = left_sql + ' LIKE ' + right.getSQL(query_object); } - return this._right = v; + // Continue hack from above hack + if (right.isInstanceOf(CONSTANT)) { + right.setValue(val); + } + return ret_val; } getSQL (query_object) { - return this.left.getSQL(query_object) + ' LIKE ' + this.right.getSQL(query_object); + if (this.right.isInstanceOf(CONSTANTS_ARRAY)) { + let right_values = this.right.getValue(); + let sql_safe_values = []; + let left_sql = this.left.getSQL(query_object); + right_values.forEach((v) => { + sql_safe_values.push(this.constructor._getSQLHelper(query_object, left_sql, v)); + }); + + return `(${ sql_safe_values.join(' OR ') })`; + } else { + return this.constructor._getSQLHelper(query_object, this.left, this.right); + } } } diff --git a/pql/opcodes/comparitors/not_equal.js b/pql/opcodes/comparitors/not_equal.js index 3819c33..e8edebc 100644 --- a/pql/opcodes/comparitors/not_equal.js +++ b/pql/opcodes/comparitors/not_equal.js @@ -1,5 +1,6 @@ import { COMPARITOR } from './comparitor.js'; import { NULL } from './../null.js'; +import { CONSTANTS_ARRAY } from './../constants_array.js'; export class NOT_EQUAL extends COMPARITOR { static set comparitors (v) {} @@ -16,8 +17,15 @@ export class NOT_EQUAL extends COMPARITOR { right_type = this.right.getType(); } - if (this.right instanceof NULL) { - return this.left.getSQL(query_object, right_type) + ' IS NULL'; + if (this.right.isInstanceOf(NULL)) { + return this.left.getSQL(query_object, right_type) + ' IS NOT NULL'; + } else if (this.right.isInstanceOf(CONSTANTS_ARRAY)) { + let right_values = this.right.getValue(); + let sql_safe_values = []; + right_values.forEach((v) => { + sql_safe_values.push(v.getSQL(query_object, left_type)); + }); + return this.left.getSQL(query_object, right_type) + ' NOT IN (' + sql_safe_values.join(', ') + ')'; } else { return this.left.getSQL(query_object, right_type) + ' != ' + this.right.getSQL(query_object, left_type); } diff --git a/pql/opcodes/comparitors/not_in.js b/pql/opcodes/comparitors/not_in.js deleted file mode 100644 index f3de1de..0000000 --- a/pql/opcodes/comparitors/not_in.js +++ /dev/null @@ -1,12 +0,0 @@ -import { COMPARITOR } from './comparitor.js'; - -export class NOT_IN extends COMPARITOR { - static set comparitors (v) {} - static get comparitors () { - return ['!|']; - } - getSQL (query_object) { - // TODO: FIX THIS - return this.left.getSQL(query_object) + ' NOT IN ' + this.right.getSQL(query_object); - } -} diff --git a/pql/opcodes/comparitors/not_like.js b/pql/opcodes/comparitors/not_like.js index 820d60a..6f48559 100644 --- a/pql/opcodes/comparitors/not_like.js +++ b/pql/opcodes/comparitors/not_like.js @@ -1,11 +1,50 @@ import { COMPARITOR } from './comparitor.js'; +import { NULL } from './../null.js'; +import { CONSTANT } from './../constant.js'; +import { CONSTANTS_ARRAY } from './../constants_array.js'; export class NOT_LIKE extends COMPARITOR { static set comparitors (v) {} static get comparitors () { return ['!~', '!']; } + static _getSQLHelper (query_object, left, right) { + var val; + var left_sql; + if (typeof left === 'string') { + left_sql = left; + } else { + left_sql = left.getSQL(query_object); + } + // Hack used to add the wild card char + if (right.isInstanceOf(CONSTANT)) { + val = right.getValue(); + right.setValue(val + '%'); + } + var ret_val; + if (right.isInstanceOf(NULL)) { + ret_val = left_sql + ' IS NOT NULL'; + } else { + ret_val = left_sql + ' NOT LIKE ' + right.getSQL(query_object); + } + // Continue hack from above hack + if (right.isInstanceOf(CONSTANT)) { + right.setValue(val); + } + return ret_val; + } getSQL (query_object) { - return this.left.getSQL(query_object) + ' NOT LIKE ' + this.right.getSQL(query_object); + if (this.right.isInstanceOf(CONSTANTS_ARRAY)) { + let right_values = this.right.getValue(); + let sql_safe_values = []; + let left_sql = this.left.getSQL(query_object); + right_values.forEach((v) => { + sql_safe_values.push(this.constructor._getSQLHelper(query_object, left_sql, v)); + }); + + return `(${ sql_safe_values.join(' AND ') })`; + } else { + return this.constructor._getSQLHelper(query_object, this.left, this.right); + } } } diff --git a/pql/opcodes/constant.js b/pql/opcodes/constant.js index 18fff4e..e8de395 100644 --- a/pql/opcodes/constant.js +++ b/pql/opcodes/constant.js @@ -20,9 +20,9 @@ export class CONSTANT extends OPCODE { getSQL (query_object, type_ref) { let value = this.getValue(); if ((this._force_numeric || (type_ref && type_ref.is_numeric)) && this.constructor.canBeNumeric(value)) { - return value.replace(/[^0-9.\-]+/, ''); + return value.toString().replace(/[^0-9.\-]+/g, ''); } else { - return "'" + query_object.constructor.escapeDBString(value) + "'"; + return query_object.constructor.escapeDBString(value.toString(), true); } } isConstant () { diff --git a/pql/opcodes/constants_array.js b/pql/opcodes/constants_array.js new file mode 100644 index 0000000..638c0a6 --- /dev/null +++ b/pql/opcodes/constants_array.js @@ -0,0 +1,7 @@ +import { CONSTANT } from './constant.js'; + +export class CONSTANTS_ARRAY extends CONSTANT { + getSQL (query_object, type_ref) { + throw 'Cannot call getSQL of CONSTANTS_ARRAY'; + } +} diff --git a/pql/opcodes/function.js b/pql/opcodes/function.js index 2d9d8e2..8f0aa1d 100644 --- a/pql/opcodes/function.js +++ b/pql/opcodes/function.js @@ -42,7 +42,7 @@ export class FUNCTION extends OPCODE { this._arguments.forEach((v) => { args.push(v.getSQL(query_obj)); }); - return FUNCTION.buildFromFormat(this.getFormat(), args, this._arguments); + return this.buildFromFormat(this.getFormat(), args, this._arguments, query_obj); } needsGroup () { if (this._needs_group_cache !== null) { @@ -58,12 +58,12 @@ export class FUNCTION extends OPCODE { } return this._needs_group_cache = false; } - static buildFromFormat (format_arg, args, orig_args) { + buildFromFormat (format_arg, args, orig_args, query_obj) { let out = []; switch(typeof format_arg){ case 'function': - return format_arg(args, orig_args); + return format_arg.call(this, args, orig_args, query_obj); break; case 'string': // Break does not need to go here because it needs to continue to 'object' section @@ -72,7 +72,7 @@ export class FUNCTION extends OPCODE { // Check if is not array type if (!(format_arg instanceof Array)) { if (format_arg[args.length] !== undefined) { - return this.buildFromFormat(format_arg[args.length], args, orig_args); + return this.buildFromFormat(format_arg[args.length], args, orig_args, query_obj); } else { throw `Could not find ${ format_arg[args.length] } in format config for function`; } @@ -91,11 +91,11 @@ export class FUNCTION extends OPCODE { } break; case 'function': - out.push(format(args, orig_args)); + out.push(format.call(this, args, orig_args, query_obj)); break; case 'object': if (format[args.length]) { - return this.buildFromFormat(format[args.length], args); + return this.buildFromFormat(format[args.length], args, orig_args, query_obj); } else { throw `Format '${JSON.stringify(format)}' does not have key of '${args.length}'`; } diff --git a/pql/opcodes/opcode.js b/pql/opcodes/opcode.js index 35c1a26..36dbc94 100644 --- a/pql/opcodes/opcode.js +++ b/pql/opcodes/opcode.js @@ -21,4 +21,7 @@ export class OPCODE { needsGroup () { return false; } + isInstanceOf (chk_class) { + return this instanceof chk_class; + } } diff --git a/pql/opcodes/variable.js b/pql/opcodes/variable.js new file mode 100644 index 0000000..3ed328c --- /dev/null +++ b/pql/opcodes/variable.js @@ -0,0 +1,52 @@ +import { CONSTANT } from './constant.js'; +import { NULL } from './null.js'; + +export class VARIABLE extends CONSTANT { + constructor (pql_obj, var_name) { + super(pql_obj, null); + + pql_obj.addVariable(var_name, this); + + this._force_numeric = false; + this.setVarName(var_name); + } + setVarName (var_name) { + this._var_name = var_name; + } + getVarName () { + return this._var_name; + } + setValue (val) { + this._value = val; + return this; + } + getValue () { + return this._value; + } + getSQL (query_object, type_ref) { + let value = this.getValue(); + if (value === null || value === undefined) { + return (new NULL(query_object)).getSQL(query_object); + } else if ((this._force_numeric || (type_ref && type_ref.is_numeric)) && this.constructor.canBeNumeric(value)) { + return value.toString().replace(/[^0-9.\-]+/g, ''); + } else { + return query_object.constructor.escapeDBString(value.toString(), true); + } + } + isInstanceOf (class_obj) { + let value = this.getValue(); + if (value === null || value === undefined) { + return class_obj === NULL; + } + return this instanceof class_obj; + } + isConstant () { + return true; + } + static canBeNumeric (value) { + if (value === null || value === undefined) { + return false; + } + return /^-?(?:[0-9]+(?:\.[0-9]+)?|\.[0-9]+)$/.test(value); + } +} diff --git a/pql/parser.js b/pql/parser.js index 4f7cae5..5463964 100644 --- a/pql/parser.js +++ b/pql/parser.js @@ -9,15 +9,18 @@ import { OR } from './opcodes/or.js'; import { SEPERATOR } from './opcodes/seperator.js'; import { SEPERATORS } from './opcodes/seperators.js'; import { NO_VALUE } from './opcodes/comparitors/no_value.js'; +import { VARIABLE } from './opcodes/variable.js'; +import { CONSTANTS_ARRAY } from './opcodes/constants_array.js'; import { TABLE_REF } from './parser/table_ref.js'; export class PARSER { - constructor (query, ref_table, allow_seperator = false, config = null, table_refs = []) { + constructor (query, ref_table, allow_seperator = false, config = null, table_refs = [], variables = {}) { this._hasError = false; this._error = null; this._codes = []; this._comparitors = new COMPARITORS(config.COMPARITORS); this._table_refs = table_refs; + this._variables = {}; // Defaults to false allow_seperator = !!allow_seperator; @@ -30,17 +33,21 @@ export class PARSER { this._ref_table = ref_table; - if (!query.length) { + if (query === null || query === undefined || !query.length) { let group = new GROUP(this, []); group.setNeedWrap(false); this.setCodes(group); + this.assignVariables(variables); } else { try { let general = this.T_GENERAL(query, allow_seperator); - if (general !== false && general[0] != query.length) { + if (general === false) { + throw ["Unknown character", query.length]; + } + if (general[0] != query.length) { throw ["Unknown character", query.length - general[0]]; } - if (general[1] instanceof GROUP) { + if (general[1].isInstanceOf(GROUP)) { general[1].setNeedWrap(false) } this.setCodes(general[1]); @@ -50,41 +57,71 @@ export class PARSER { } else { this.setError(e.message || e, true); } + } finally { + this.assignVariables(variables); + } + } + } + assignVariables (var_list_obj) { + for (let var_name in var_list_obj) { + if (var_list_obj.hasOwnProperty(var_name)) { + this.assignVariable(var_name, var_list_obj[var_name]); + } + } + } + assignVariable (var_name, val) { + if (this._variables[var_name]) { + for (let obj of this._variables[var_name]) { + obj.setValue(val); } } } + addVariable (var_name, obj) { + if (this._variables[var_name] === undefined) { + this._variables[var_name] = []; + } + this._variables[var_name].push(obj); + } getRefTable () { return this._ref_table; } _getCodesOfGroup (need_group) { let codes = this._codes; - if (codes instanceof GROUP) { + if (codes && (codes.isInstanceOf(GROUP))) { let group_codes = codes.getOpCodes(); let new_codes = []; + let has_or = false; + let has_needs_group = false; for (let code of group_codes) { // If it has an OR just return all of them if in where - if (code instanceof OR) { + if (code.isInstanceOf(OR)) { + has_or = true; + } + if (code.needsGroup()) { + has_needs_group = true; + } + if (has_or && has_needs_group) { if (need_group) { return codes; } else { return (new GROUP(this, [])).setNeedWrap(false); } } - if (!!need_group == code.needsGroup()) { + if (!!need_group === code.needsGroup()) { new_codes.push(code); } } // Trims off any comparitors (ANDs and ORs) from beginning and end of codes while (new_codes.length) { - if (new_codes[0] instanceof SEPERATORS) { + if (new_codes[0] && (new_codes[0].isInstanceOf(SEPERATORS))) { new_codes.shift(); } else { break; } } while (new_codes.length) { - if (new_codes[new_codes.length - 1] instanceof SEPERATORS) { + if (new_codes[new_codes.length - 1] && (new_codes[new_codes.length - 1].isInstanceOf(SEPERATORS))) { new_codes.pop(); } else { break; @@ -92,7 +129,7 @@ export class PARSER { } return (new GROUP(this, new_codes)).setNeedWrap(false); } else { - if (need_group != codes.needsGroup()) { + if (need_group !== codes.needsGroup()) { return (new GROUP(this, [])).setNeedWrap(false); } else { return codes; @@ -170,7 +207,7 @@ export class PARSER { * When a table reference is specified. */ T_TABLE_REF (str, table_ref) { - let match = str.match(/^([a-zA-Z0-9_]+)\./); + let match = str.match(/^\s*([a-zA-Z_][a-zA-Z0-9_]*)\./); if (!match) { return false; } @@ -209,63 +246,64 @@ export class PARSER { * Is the value that comes after a comparitor. */ T_COMPARE_VALUE (str) { - var next_char; - var tries = 0; - var next_char; - // Ignore spaces - while ((next_char = str.substr(tries, 1)) && (next_char === ' ' || next_char === "\r" || next_char === "\n" || next_char === "\t")) { - tries++; + let ret_const = this.T_CONSTANT(str); + if (ret_const) { + return ret_const; } - // NULL is unique and is a solid hyphen - if (next_char == '-' && /^-(?:[^a-zA-Z0-9_]|$)/.test(str.substr(tries))) { + let match = str.match(/^\s*([a-zA-Z0-9_\-.]+)/); + if (match) { return [ - tries + 1, - new NULL() + match[0].length, + new CONSTANT(this, match[1]) ]; } - - let match; - let data; - switch (next_char) { - case '"': - match = str.match(/^\s*"((?:[^"\\]?(?:\\[\x00-\xFF])?)*)"/); - if (!match) { - return false; - } - match[1] = match[1].replace(/((?:^|[^\\])(\\\.)*)\\n/, "$1\n"); - match[1] = match[1].replace(/((?:^|[^\\])(\\\\)*)\\r/, "$1\r"); - match[1] = match[1].replace(/((?:^|[^\\])(\\\\)*)\\t/, "$1\t"); - data = match[1].replace(/\\([\x00-\xFF])/, '$1'); - break; - case "'": - match = str.match(/^\s*'((?:[^'\\]?(?:\\[\x00-\xFF])?)*)'/); - if (!match) { - return false; + let ret_array = this.T_ARRAY_VALUE(str); + if (ret_array) { + return ret_array; + } + return false; + } + T_ARRAY_VALUE (str) { + let tries = 0; + let next_char; + // Ignore spaces + while ((next_char = str.substr(tries, 1)) && (next_char === ' ' || next_char === "\r" || next_char === "\n" || next_char === "\t")) { + tries++; + } + if (next_char === '[') { + let array_values = []; + let s = str.substr(tries + 1); + do { + let c = this.T_COMPARE_VALUE(s); + if (!c) { + throw ['Expected T_COMPARE_VALUE', s.length]; } - match[1] = match[1].replace(/((?:^|[^\\])(\\\.)*)\\n/, "$1\n"); - match[1] = match[1].replace(/((?:^|[^\\])(\\\\)*)\\r/, "$1\r"); - match[1] = match[1].replace(/((?:^|[^\\])(\\\\)*)\\t/, "$1\t"); - data = match[1].replace(/\\([\x00-\xFF])/, '$1'); - break; - default: - match = str.match(/^\s*([a-zA-Z0-9_\-.]+)/); - if (!match) { - return false; + s = s.substr(c[0]); + array_values.push(c[1]); + let sep = this.T_SEPERATOR(s); + if (!sep) { + let term = s.match(/^\s*\]/); + if (term) { + return [ + str.length - s.length + term[0].length, + new CONSTANTS_ARRAY(this, array_values) + ]; + } else { + throw ['Expected T_CONSTANT, T_SEPERATOR or closing array "]" character', s.length]; + } + } else { + s = s.substr(sep[0]); } - data = match[1]; - break; + } while (true); } - return [ - match[0].length, - new CONSTANT(this, data) - ]; + return false; } /* * Is a field in a table */ T_FIELD (str, table_ref) { - let match = str.match(/^[a-zA-Z0-9_]+/); + let match = str.match(/^\s*([a-zA-Z_][a-zA-Z0-9_]*)/); if (!match) { return false; } @@ -280,7 +318,7 @@ export class PARSER { } try { - var field = new FIELD(this, match[0], table_ref); + var field = new FIELD(this, match[1], table_ref); } catch (e) { throw [e, str.length, [FIELD]]; } @@ -294,7 +332,7 @@ export class PARSER { ]; //throw ["Expected T_COMPARITOR", str.length]; } - if (comparitor[1] instanceof NO_VALUE) { + if (comparitor[1].isInstanceOf(NO_VALUE)) { return [ match[0].length + comparitor[0], field @@ -320,7 +358,7 @@ export class PARSER { * Is a function in sql */ T_FUNCTION (str) { - var match = str.match(/^([a-zA-Z0-9_]+)\(\s*/); + var match = str.match(/^\s*([a-zA-Z0-9_]+)\(/); if (!match) { return false; } @@ -337,17 +375,17 @@ export class PARSER { str = str.substring(match[0].length); for (let i = 0; true; i++) { if (found_args.length >= max_args) { - throw ["Function '" + func.getFuncName() + "' cannot have more than " + max_args.toString() + " args", str.length, []]; + throw [`Function '${ func.getFuncName() }' cannot have more than ${ max_args.toString() } args`, str.length, []]; } let general = this.T_GENERAL(str); if (!general && found_args.length < min_args) { - throw ["Function '" + func.getFuncName() + "' expected " + min_args.toString() + " but got " + found_args.length.toString() + " args", str.length, []]; + throw [`Function '${ func.getFuncName() }' expected ${ min_args.toString() } but got ${ found_args.length.toString() } args`, str.length, []]; } if (!general) { break; } sum_length += general[0]; - if (general instanceof GROUP) { + if (general[1].isInstanceOf(GROUP)) { general.setNeedWrap(false); } found_args.push(general[1]); @@ -360,7 +398,7 @@ export class PARSER { str = str.substring(seperator[0]); sum_length += seperator[0]; } - let closer = this.T_GROUP_CLOSER(str); + let closer = this.T_FUNCTION_CLOSER(str); if (!closer) { throw ["Open function group tag without close tag", str.length]; } @@ -375,7 +413,7 @@ export class PARSER { ]; //throw ["Expected T_COMPARITOR", str.length]; } - if (comparitor[1] instanceof NO_VALUE) { + if (comparitor[1].isInstanceOf(NO_VALUE)) { return [ match[0].length + sum_length + closer[0] + comparitor[0], func @@ -383,60 +421,67 @@ export class PARSER { } str = str.substring(comparitor[0]); let value = this.T_COMPARE_VALUE(str); - if (!value) { - throw ["Expected T_COMPARE_VALUE", str.length]; - } comparitor[1].setLeft(func); comparitor[1].setRight(value[1]); - + return [ match[0].length + sum_length + closer[0] + comparitor[0] + value[0], comparitor[1] ]; } - /* - * Is a null literal. "-" character. - */ - T_NULL (str) { - if (str.substr(0, 1) == '-') { - return [1, new NULL(this)]; - } - return false; - } - /* * Is a string constant. The matching string will be exactly sent as is. Also good for binary data. */ T_CONSTANT (str) { - let chr = str.substr(0, 1); + let tries = 0; + let next_char; + // Ignore spaces + while ((next_char = str.substr(tries, 1)) && (next_char === ' ' || next_char === "\r" || next_char === "\n" || next_char === "\t")) { + tries++; + } let match; - switch (chr) { + switch (next_char) { case '"': - match = str.match(/^"((?:[^"\\]?(?:\\[\x00-\xFF])?)*)"/); + match = str.match(/^\s*"((?:[^"\\]?(?:\\[\x00-\xFF])?)*)"/); // Make sure we have data if (match && match.length && match[1].length) { - match[1] = match[1].replace(/((?:^|[^\\])(\\\.)*)\\n/, "$1\n"); - match[1] = match[1].replace(/((?:^|[^\\])(\\\\)*)\\r/, "$1\r"); - match[1] = match[1].replace(/((?:^|[^\\])(\\\\)*)\\t/, "$1\t"); - match[1] = match[1].replace(/\\([\x00-\xFF])/, '$1'); + match[1] = match[1].replace(/((?:^|[^\\])(\\\.)*)\\n/g, "$1\n"); + match[1] = match[1].replace(/((?:^|[^\\])(\\\\)*)\\r/g, "$1\r"); + match[1] = match[1].replace(/((?:^|[^\\])(\\\\)*)\\t/g, "$1\t"); + match[1] = match[1].replace(/\\([\x00-\xFF])/g, '$1'); } break; case "'": - match = str.match(/^'((?:[^'\\]?(?:\\[\x00-\xFF])?)*)'/); + match = str.match(/^\s*'((?:[^'\\]?(?:\\[\x00-\xFF])?)*)'/); // Make sure we have data if (match && match.length && match[1].length) { - match[1] = match[1].replace(/((?:^|[^\\])(\\\.)*)\\n/, "$1\n"); - match[1] = match[1].replace(/((?:^|[^\\])(\\\\)*)\\r/, "$1\r"); - match[1] = match[1].replace(/((?:^|[^\\])(\\\\)*)\\t/, "$1\t"); - match[1] = match[1].replace(/\\([\x00-\xFF])/, '$1'); + match[1] = match[1].replace(/((?:^|[^\\])(\\\.)*)\\n/g, "$1\n"); + match[1] = match[1].replace(/((?:^|[^\\])(\\\\)*)\\r/g, "$1\r"); + match[1] = match[1].replace(/((?:^|[^\\])(\\\\)*)\\t/g, "$1\t"); + match[1] = match[1].replace(/\\([\x00-\xFF])/g, '$1'); + } + break; + case '@': + match = str.match(/\s*@([a-zA-Z0-9\-_]+)/); + if (match) { + return [ + match[0].length, + new VARIABLE(this, match[1]) + ]; } break; case '-': - return [1, new NULL(this)]; + if (!/[0-9]/.test(str.substr(tries + 1, 1))) { + return [1 + tries, new NULL(this)]; + } } if (!match) { - return false; + // see if it's a constant numeric + match = str.match(/^\s*(-?[0-9]+(\.[0-9]+)?)/); + if (!match) { + return false; + } } return [ match[0].length, @@ -447,9 +492,9 @@ export class PARSER { let op_order = [ this.T_TABLE_REF, this.T_FUNCTION, + this.T_CONSTANT, this.T_FIELD, this.T_GROUP_OPENER, - this.T_CONSTANT, /*this.T_SUBQUERY_OPENER*/ ]; let op_len = op_order.length; @@ -468,7 +513,7 @@ export class PARSER { let sep; str = str.substring(found[0]); used_str_len += found[0]; - if (op_codes.length && !(op_codes[op_codes.length - 1] instanceof SEPERATORS)) { + if (op_codes.length && !(op_codes[op_codes.length - 1].isInstanceOf(SEPERATORS))) { throw [`Expected space, "|"${ (allow_seperator) ? ' or ","': '' }`, str.length + found[0]]; } op_codes.push(found[1]); @@ -490,18 +535,18 @@ export class PARSER { op_codes.push(sep[1]); } } - } while (found && str.length != 0); + } while (found && str.length !== 0); // Removes any trailing ANDs while (op_codes.length) { - if (op_codes[op_codes.length - 1] instanceof AND) { + if (op_codes[op_codes.length - 1] && (op_codes[op_codes.length - 1].isInstanceOf(AND))) { op_codes.pop(); } else { break; } } - if (op_codes.length && op_codes[op_codes.length - 1] instanceof SEPERATORS) { + if (op_codes.length && (op_codes[op_codes.length - 1].isInstanceOf(SEPERATORS))) { throw ["Cannot terminate this section with a seperator", str.length]; } @@ -542,7 +587,16 @@ export class PARSER { general[1] ]; } - + T_FUNCTION_CLOSER (str) { + let match = str.match(/^\s*\)/); + if (!match) { + return false; + } + return [ + match[0].length, + true + ]; + } /* * Represents a closed prenthesis ")" */ @@ -589,7 +643,7 @@ export class PARSER { * Is the pipe "|" character */ T_OR (str) { - let match = str.match(/^\s*\|\s*/); + let match = str.match(/^\s*\|/); if (!match) { return false; } @@ -603,7 +657,7 @@ export class PARSER { * Is the comma "," character */ T_SEPERATOR (str) { - let match = str.match(/^\s*,\s*/); + let match = str.match(/^\s*,/); if (!match) { return false; } diff --git a/pql/parser/sql_builder.js b/pql/parser/sql_builder.js index 02f63f5..7e79ddc 100644 --- a/pql/parser/sql_builder.js +++ b/pql/parser/sql_builder.js @@ -56,7 +56,7 @@ export class SQL_BUILDER { this.getOrderBys().forEach((v, k) => { let code = k.getCodes().getSQL(this); if (code) { - orders.push(code + (v.toLowerCase() === 'desc' ? ' DESC' : ' ASC' )); + orders.push(code + ((v !== null && v !== undefined && v.toLowerCase() === 'desc') ? ' DESC' : ' ASC' )); } }); let order_by_str = ''; @@ -110,7 +110,7 @@ export class SQL_BUILDER { } // Pop last item off the array and clone it so it doesn't count itself - let parser = new PARSER(link_obj.psudoql, this.getTable(), false, this.getQuery().getConfig(), table_ary.slice(0, table_ary.length - 1)); + let parser = new PARSER(link_obj.pql, this.getTable(), false, this.getQuery().getConfig(), table_ary.slice(0, table_ary.length - 1)); // This needs to happen here to ensure the tables get added in the proper order parser.getCodes().getSQL(this); @@ -126,9 +126,9 @@ export class SQL_BUILDER { let cur_obj = db_map[this.getTable()]; let cur_link_obj; table_ary.forEach((v) => { - if (cur_obj.linkTo.hasOwnProperty(v)) { + if (cur_obj.linkTo && cur_obj.linkTo.hasOwnProperty(v)) { cur_link_obj = cur_obj.linkTo[v]; - } else if (cur_obj.linkFrom.hasOwnProperty(v)) { + } else if (cur_obj.linkFrom && cur_obj.linkFrom.hasOwnProperty(v)) { cur_link_obj = cur_obj.linkFrom[v]; } else { throw `No table link from "${ cur_obj.name }" to table "${ v }"`; diff --git a/pql/parser/table_ref.js b/pql/parser/table_ref.js index 1820ee3..19eba00 100644 --- a/pql/parser/table_ref.js +++ b/pql/parser/table_ref.js @@ -12,9 +12,9 @@ export class TABLE_REF { if (!this._cur_table_obj) { this._cur_table_obj = db_map[this._pql_obj.getRefTable()]; } - if (this._cur_table_obj.linkTo[table]) { + if (this._cur_table_obj.linkTo && this._cur_table_obj.linkTo[table]) { this._cur_table_obj = db_map[this._cur_table_obj.linkTo[table].table]; - } else if (this._cur_table_obj.linkFrom[table]) { + } else if (this._cur_table_obj.linkFrom && this._cur_table_obj.linkFrom[table]) { this._cur_table_obj = db_map[this._cur_table_obj.linkFrom[table].table]; } else { throw `Link "${ table }" does not exist in table "${ this._cur_table_obj.name }"`; diff --git a/psudoql.komodoproject b/psudoql.komodoproject deleted file mode 100644 index 8e97858..0000000 --- a/psudoql.komodoproject +++ /dev/null @@ -1,15 +0,0 @@ - - - - - 0 - - None - None - None - 0 - 1 - None - None - - diff --git a/unit_test/index.html b/unit_test/index.html new file mode 100644 index 0000000..02002ee --- /dev/null +++ b/unit_test/index.html @@ -0,0 +1,37 @@ + + + + + QUnit Example + + + + + +
+
+ + + + + \ No newline at end of file diff --git a/unit_test/test_config.js b/unit_test/test_config.js new file mode 100644 index 0000000..4ea4c50 --- /dev/null +++ b/unit_test/test_config.js @@ -0,0 +1,754 @@ +export class Config { + static ALL_ARGS (args) { + return args.join(', '); + }; +} +/* Database types */ +Config.NUMERIC = { + is_numeric: true, +}; +Config.DATE = { + is_numeric: false, +}; +Config.STRING = { + is_numeric: false, +}; +Config.BOOLEAN = { + is_numeric: true, +}; +Config.ANY_TYPE = { + is_numeric: false, +}; + +/* These are to make it easier to read the code for the function definitions */ +Config.ARG1 = 0; +Config.ARG2 = 1; +Config.ARG3 = 2; +Config.ARG4 = 3; +Config.ARG5 = 4; +Config.ARG6 = 5; +Config.ARG7 = 6; +Config.ARG8 = 7; +Config.DB_MAP = { + table1: { + name: 'TablE1', + fields: { + id: { + type: Config.NUMERIC, + }, + name: { + type: Config.STRING, + }, + linkField1: { + type: Config.NUMERIC, + }, + }, + linkTo: { + link1: { + table: 'linkTo1', + pql: 'eq(linkField1,link1.id)', + }, + }, + linkFrom: { + link2: { + table: 'linkFrom2', + pql: 'eq(id, link2.linkId)', + }, + }, + }, + linkTo1: { + name: 'linkTB1', + fields: { + id: { + type: Config.NUMERIC, + }, + name: { + type: Config.STRING, + }, + }, + linkFrom: { + linkTable1: { + table: 'table1', + pql: 'eq(id, linkTable1.linkField1)', + } + }, + }, + linkFrom2: { + name: 'linkTB2', + fields: { + id: { + type: Config.NUMERIC, + }, + linkId: { + type: Config.NUMERIC, + }, + name: { + type: Config.STRING, + }, + }, + linkTo: { + linkTable1: { + table: 'table1', + pql: 'eq(linkId, linkTable1.id)', + } + }, + }, +}; + +import { EQUAL } from './../pql/opcodes/comparitors/equal.js'; +import { GREATER_THAN } from './../pql/opcodes/comparitors/greater_than.js'; +import { LESS_THAN } from './../pql/opcodes/comparitors/less_than.js'; +import { LIKE } from './../pql/opcodes/comparitors/like.js'; +import { NO_VALUE } from './../pql/opcodes/comparitors/no_value.js'; +import { NOT_EQUAL } from './../pql/opcodes/comparitors/not_equal.js'; +import { NOT_LIKE } from './../pql/opcodes/comparitors/not_like.js'; + +import { CONSTANTS_ARRAY } from './../pql/opcodes/constants_array.js'; +import { NULL } from './../pql/opcodes/null.js'; + +Config.FUNCTION_MAP = { + eq: { + description: 'Compares two values', + min_args: 2, + max_args: 2, + return_type: Config.BOOLEAN, + format: function (args, orig_args, query_obj){ + var pql = this.getPqlObj(); + var obj = new EQUAL(pql); + obj.setLeft(orig_args[0]); + obj.setRight(orig_args[1]); + return obj.getSQL(query_obj); + }, + }, + gt: { + description: 'Checks if first value is greater than second', + min_args: 2, + max_args: 2, + return_type: Config.BOOLEAN, + format: function (args, orig_args, query_obj){ + var pql = this.getPqlObj(); + var obj = new GREATER_THAN(pql); + obj.setLeft(orig_args[0]); + obj.setRight(orig_args[1]); + return obj.getSQL(query_obj); + }, + }, + lt: { + description: 'Checks if first value is less than second', + min_args: 2, + max_args: 2, + return_type: Config.BOOLEAN, + format: function (args, orig_args, query_obj){ + var pql = this.getPqlObj(); + var obj = new LESS_THAN(pql); + obj.setLeft(orig_args[0]); + obj.setRight(orig_args[1]); + return obj.getSQL(query_obj); + }, + }, + ne: { + description: 'Checks if values are not equal', + min_args: 2, + max_args: 2, + return_type: Config.BOOLEAN, + format: function (args, orig_args, query_obj){ + var pql = this.getPqlObj(); + var obj = new NOT_EQUAL(pql); + obj.setLeft(orig_args[0]); + obj.setRight(orig_args[1]); + return obj.getSQL(query_obj); + }, + }, + like: { + description: 'Checks if first value is like second. % sign is wild card character', + min_args: 2, + max_args: 2, + return_type: Config.BOOLEAN, + format: function (args, orig_args, query_obj){ + var pql = this.getPqlObj(); + var obj = new LIKE(pql); + obj.setLeft(orig_args[0]); + obj.setRight(orig_args[1]); + return obj.getSQL(query_obj); + }, + }, + not_like: { + description: 'Checks if first value is not like second. % sign is wild card character', + min_args: 2, + max_args: 2, + return_type: Config.BOOLEAN, + format: function (args, orig_args, query_obj){ + var pql = this.getPqlObj(); + var obj = new NOT_LIKE(pql); + obj.setLeft(orig_args[0]); + obj.setRight(orig_args[1]); + return obj.getSQL(query_obj); + }, + }, + 'in': { + description: 'Checks if first value is any of the following values', + min_args: 2, + max_args: Infinity, + return_type: Config.BOOLEAN, + format: function (args, orig_args, query_obj){ + var pql = this.getPqlObj(); + var out_args = []; + var obj = new EQUAL(pql); + orig_args.splice(1).forEach((v) => { + out_args.push(v); + }); + obj.setLeft(orig_args[0]); + obj.setRight(new CONSTANTS_ARRAY(pql, out_args)); + return obj.getSQL(query_obj); + }, + }, + not_in: { + description: 'Checks if first value is not any of the following values', + min_args: 2, + max_args: Infinity, + return_type: Config.BOOLEAN, + format: function (args, orig_args, query_obj){ + var pql = this.getPqlObj(); + var out_args = []; + var obj = new NOT_EQUAL(pql); + orig_args.splice(1).forEach((v) => { + out_args.push(v); + }); + obj.setLeft(orig_args[0]); + obj.setRight(new CONSTANTS_ARRAY(pql, out_args)); + return obj.getSQL(query_obj); + }, + }, + 'if': { + description: 'If first argument is truethy returns second argument otherwise returns third', + min_args: 3, + max_args: 3, + return_type: Config.ANY, + format: ['IF(', Config.ALL_ARGS, ')'], + }, + /* Math functions */ + add: { + description: 'Adds values together', + min_args: 2, + max_args: Infinity, + return_type: Config.NUMBER, + format: function (args, orig_args, query_obj){ + return args.join(' + '); + }, + }, + sub: { + description: 'Subtracts values from eachother', + min_args: 2, + max_args: Infinity, + return_type: Config.NUMBER, + format: function (args, orig_args, query_obj){ + return args.join(' - '); + }, + }, + mul: { + description: 'Multiplies values together', + min_args: 2, + max_args: Infinity, + return_type: Config.NUMBER, + format: function (args, orig_args, query_obj){ + return args.join(' * '); + }, + }, + div: { + description: 'Divide values together', + min_args: 2, + max_args: Infinity, + return_type: Config.NUMBER, + format: function (args, orig_args, query_obj){ + return args.join(' / '); + }, + }, + mod: { + description: 'Modulus values together', + min_args: 2, + max_args: 2, + return_type: Config.NUMBER, + format: ['MOD(', Config.ARG1, ', ', Config.ARG2, ')'], + }, + pow: { + description: 'Powers values together', + min_args: 2, + max_args: 2, + return_type: Config.NUMBER, + format: ['POW(', Config.ARG1, ', ', Config.ARG2, ')'], + }, + sqrt: { + description: 'Square roots a value', + min_args: 1, + max_args: 1, + return_type: Config.NUMBER, + format: ['SQRT(', Config.ARG1, ')'], + }, + + /* Standard SQL functions */ + abs: { + description: 'Gets absolute value', + min_args: 1, + max_args: 1, + arg_types: [ + Config.NUMERIC, + ], + return_type: Config.NUMERIC, + format: ['ABS(', Config.ARG1, ')'], + }, + 'char': { + description: 'Gets character from numerical ascii character', + min_args: 1, + max_args: Infinity, + arg_types: [ + Config.NUMERIC, + ], + return_type: Config.STRING, + format: ['CHAR(', Config.ALL_ARGS, ')'], + }, + coalesce: { + description: 'Returns first non-null value from arguments', + min_args: 1, + max_args: Infinity, + return_type: Config.ANY_TYPE, + format: ['COALESCE(', Config.ALL_ARGS, ')'], + }, + ifnull: { + description: 'Returns first argument if not null otherwise returns second argument', + min_args: 2, + max_args: 2, + return_type: Config.ANY_TYPE, + format: ['IFNULL(', Config.ARG1, ', ', Config.ARG2, ')'], + }, + instr: { + description: 'Gets character position of argument two in argument one\'s string', + min_args: 2, + max_args: 2, + return_type: Config.NUMERIC, + arg_types: [ + Config.STRING, + Config.STRING, + ], + format: ['INSTR(', Config.ARG1, ', ', Config.ARG2, ')'], + }, + hex: { + description: 'Returns hex value of argument', + min_args: 1, + max_args: 1, + return_type: Config.STRING, + format: ['HEX(', Config.ARG1, ')'], + }, + length: { + description: 'Returns the string length', + min_args: 1, + max_args: 1, + arg_types: [ + Config.STRING, + ], + return_type: Config.NUMERIC, + format: ['LENGTH(', Config.ARG1, ')'], + }, + lower: { + description: 'Returns string converted to lower case', + min_args: 1, + max_args: 1, + arg_types: [ + Config.STRING, + ], + return_type: Config.STRING, + format: ['LOWER(', Config.ARG1, ')'], + }, + ltrim: { + description: 'Returns left-trimmed string', + min_args: 1, + max_args: 1, + arg_types: [ + Config.STRING, + ], + return_type: Config.STRING, + format: ['LTRIM(', Config.ARG1, ')'], + }, + nullif: { + description: 'Returns null if argument one equals argument two', + min_args: 2, + max_args: 2, + return_type: Config.ANY_TYPE, + format: ['NULLIF(', Config.ARG1, ', ', Config.ARG2, ')'], + }, + random: { + description: 'Returns random number (platform dependent)', + min_args: 0, + max_args: 0, + return_type: Config.NUMERIC, + format: ['RANDOM()'], + }, + replace: { + description: 'Searches for argument two and replaces matches with argument three in argument one', + min_args: 3, + max_args: 3, + arg_types: [ + Config.STRING, + Config.STRING, + Config.STRING, + ], + return_type: Config.STRING, + format: ['REPLACE(', Config.ARG1, ', ', Config.ARG2, ', ', Config.ARG3, ')'], + }, + round: { + description: 'Returns rounded value of argument one with decimal percision of argument two', + min_args: 1, + max_args: 2, + arg_types: [ + Config.NUMERIC, + Config.NUMERIC, + ], + return_type: Config.NUMERIC, + format: ['ROUND(', Config.ALL_ARGS, ')'], + }, + rtrim: { + description: 'Returns right-trimmed value', + min_args: 1, + max_args: 1, + arg_types: [ + Config.STRING, + ], + return_type: Config.STRING, + format: ['RTRIM(', Config.ARG1, ')'], + }, + substr: { + description: 'Returns part of argument one string from argument two position with length of argument three', + min_args: 2, + max_args: 3, + arg_types: [ + Config.STRING, + Config.NUMERIC, + Config.NUMERIC, + ], + return_type: Config.STRING, + format: ['SUBSTR(', Config.ALL_ARGS, ')'], + }, + trim: { + description: 'Returns full trimmed string', + min_args: 1, + max_args: 1, + arg_types: [ + Config.STRING, + ], + return_type: Config.STRING, + format: ['TRIM(', Config.ARG1, ')'], + }, + upper: { + description: 'Returns value converted to upper case', + min_args: 1, + max_args: 1, + arg_types: [ + Config.STRING, + ], + return_type: Config.STRING, + format: ['UPPER(', Config.ARG1, ')'], + }, + concat: { + description: 'Returns multiple strings joined together', + min_args: 1, + max_args: Infinity, + arg_types: [ + Config.STRING, + ], + return_type: Config.STRING, + format: ['CONCAT(', Config.ALL_ARGS, ')'], + }, + + /* Date Functions */ + date_format: { + description: 'Returns date converted converted using format of argument two', + min_args: 2, + max_args: 2, + arg_types: [ + Config.DATE, + Config.STRING, + ], + return_type: Config.STRING, + format: ['DATE_FORMAT(', Config.ARG1, ', ', Config.ARG2,')'], + }, + date: { + description: 'Returns date porition of argument one', + min_args: 1, + max_args: 1, + arg_types: [ + Config.DATE, + ], + return_type: Config.DATE, + format: ['DATE(', Config.ARG1, ')'], + }, + day: { + description: 'Returns day number of date', + min_args: 1, + max_args: 1, + arg_types: [ + Config.DATE, + ], + return_type: Config.NUMERIC, + format: ['DAY(', Config.ARG1, ')'], + }, + from_unixtime: { + description: 'Returns date type from unix timestamp', + min_args: 1, + max_args: 1, + arg_types: [ + Config.NUMERIC, + ], + return_type: Config.DATE, + format: ['FROM_UNIXTIME(', Config.ARG1, ')'], + }, + hour: { + description: 'Returns hour portion of date', + min_args: 1, + max_args: 1, + arg_types: [ + Config.DATE, + ], + return_type: Config.NUMERIC, + format: ['HOUR(', Config.ARG1, ')'], + }, + minute: { + description: 'Returns minute porition of date', + min_args: 1, + max_args: 1, + arg_types: [ + Config.DATE, + ], + return_type: Config.NUMERIC, + format: ['MINUTE(', Config.ARG1, ')'], + }, + month: { + description: 'Returns month number porition of date', + min_args: 1, + max_args: 1, + arg_types: [ + Config.DATE, + ], + return_type: Config.NUMERIC, + format: ['MONTH(', Config.ARG1, ')'], + }, + now: { + description: 'Returns current time/date', + min_args: 0, + max_args: 0, + return_type: Config.DATE, + format: ['NOW()'], + }, + second: { + description: 'Returns second porition of date', + min_args: 1, + max_args: 1, + arg_types: [ + Config.DATE, + ], + return_type: Config.NUMERIC, + format: ['SECOND(', Config.ARG1, ')'], + }, + time: { + description: 'Returns time porition of date', + min_args: 1, + max_args: 1, + arg_types: [ + Config.DATE, + ], + return_type: Config.STRING, + format: ['TIME(', Config.ARG1, ')'], + }, + unix_timestamp: { + description: 'Returns unix timestamp from date', + min_args: 0, + max_args: 1, + arg_types: [ + Config.DATE, + ], + return_type: Config.NUMERIC, + format: ['UNIX_TIMESTAMP(', Config.ARG1, ')'], + }, + year: { + description: 'Returns year porition from date', + min_args: 1, + max_args: 1, + arg_types: [ + Config.DATE, + ], + return_type: Config.NUMERIC, + format: ['YEAR(', Config.ARG1, ')'], + }, + + /* Aggregate functions */ + avg: { + description: 'Returns average value', + min_args: 1, + max_args: 2, + arg_types: [ + Config.NUMERIC, + Config.BOOLEAN, + ], + return_type: Config.NUMERIC, + is_group_function: true, + format: { + 1: ['AVG(', Config.ARG1, ')'], + 2: [(args, orig_args) => { + let distinct = Boolean(Number(orig_args[1].getValue())); + if (distinct) { + return `AVG(DISTINCT ${ args[0] }`; + } + return `AVG(${ args[0] }`; + }], + }, + }, + count: { + description: 'Returns count of items', + min_args: 1, + max_args: 2, + arg_types: [ + Config.NUMERIC, + Config.BOOLEAN, + ], + return_type: Config.NUMERIC, + is_group_function: true, + format: { + 1: ['COUNT(', Config.ARG1, ')'], + 2: [(args, orig_args) => { + let distinct = Boolean(Number(orig_args[1].getValue())); + if (distinct) { + return `COUNT(DISTINCT ${ args[0] }`; + } + return `COUNT(${ args[0] }`; + }], + }, + }, + // group_concat(expr[,distinct[,seperator[,order_by_expression,order_by_direction ...]]]); + group_concat: { + description: 'Returns the group concatinated string', + min_args: 1, + max_args: Infinity, + arg_types: [ + Config.STRING, + Config.BOOLEAN, + Config.STRING, + Config.STRING, + Config.STRING, + ], + return_type: Config.STRING, + is_group_function: true, + format: (args, orig_args) => { + let distinct = (orig_args.length >= 2) && Boolean(Number(orig_args[1].getValue())) ? 'DISTINCT ' : ''; + let seperator = ''; + if (orig_args.length >= 3) { + if (!orig_args[2].isConstant() && !(orig_args[2].isInstanceOf(NULL))) { + throw `Third argument of "group_concat" function must be a constant type`; + } + if (!(orig_args[2].isInstanceOf(NULL))) { + seperator = ` SEPARATOR ${ args[2] }`; + } + } + let order_bys = new Set; + if (orig_args.length > 3) { + // Check to make sure there are always pairs of order_by_expression and order_by_direction + if (orig_args.length % 2 == 0) { + throw `GROUP_CONCAT function must have order_by_expression and order_by_direction in pairs` + } + // Inc in order of 2's + for (let i = 3; i < args.length; i=i+2) { + let dir = 'ASC'; + if (orig_args[i+1].isConstant()) { + dir = orig_args[i+1].getValue().toString().toLowerCase() == 'desc' ? 'DESC' : 'ASC'; + } + order_bys.add(`${ args[i] } ${ dir }`); + } + } + let order_by; + if (order_bys.size) { + order_by = ` ORDER BY ${ Array.from(order_bys).join(',') }` + } else { + order_by = ''; + } + return `GROUP_CONCAT(${ distinct }${ args[0] }${ order_by }${ seperator })`; + }, + }, + max: { + description: 'Returns maximum value', + min_args: 1, + max_args: 2, + arg_types: [ + Config.NUMERIC, + Config.BOOLEAN, + ], + return_type: Config.NUMERIC, + is_group_function: true, + format: { + 1: ['MAX(', Config.ARG1, ')'], + 2: [(args, orig_args) => { + let distinct = Boolean(Number(orig_args[1].getValue())); + if (distinct) { + return `MAX(DISTINCT ${ args[0] }`; + } + return `MAX(${ args[0] }`; + }], + }, + }, + min: { + description: 'Returns minimum value', + min_args: 1, + max_args: 2, + arg_types: [ + Config.NUMERIC, + Config.BOOLEAN, + ], + return_type: Config.NUMERIC, + is_group_function: true, + format: { + 1: ['MIN(', Config.ARG1, ')'], + 2: [(args, orig_args) => { + let distinct = Boolean(Number(orig_args[1].getValue())); + if (distinct) { + return `MIN(DISTINCT ${ args[0] }`; + } + return `MIN(${ args[0] }`; + }], + }, + }, + sum: { + description: 'Returns sum of values', + min_args: 1, + max_args: 2, + arg_types: [ + Config.NUMERIC, + Config.BOOLEAN, + ], + return_type: Config.NUMERIC, + is_group_function: true, + format: { + 1: ['SUM(', Config.ARG1, ')'], + 2: [(args, orig_args) => { + let distinct = Boolean(Number(orig_args[1].getValue())); + if (distinct) { + return `SUM(DISTINCT ${ args[0] }`; + } + return `SUM(${ args[0] }`; + }], + }, + }, + having: { + description: 'Forces item into having statment', + min_args: 1, + max_args: 1, + return_type: Config.ANY, + is_group_function: true, + format: [Config.ARG1], + } +}; + +Config.COMPARITORS = new Set([ + EQUAL, + GREATER_THAN, + LESS_THAN, + LIKE, + NO_VALUE, + NOT_EQUAL, + NOT_LIKE, +]); \ No newline at end of file diff --git a/unit_test/tests/compare.js b/unit_test/tests/compare.js new file mode 100644 index 0000000..5fbd664 --- /dev/null +++ b/unit_test/tests/compare.js @@ -0,0 +1,208 @@ +import { PQL } from './../../pql/PQL.js'; +import { Config } from './../test_config.js'; + +window.QUnit.test('Compare Equal - Number', function (assert) { + var query = PQL.getSQL({ + query: 'id:5', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" = 5 GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare Equal - String', function (assert) { + var query = PQL.getSQL({ + query: 'id:hello', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" = \'hello\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare Equal - Caps String', function (assert) { + var query = PQL.getSQL({ + query: 'id:hEll0e', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" = \'hEll0e\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare Equal - Double Quote', function (assert) { + var query = PQL.getSQL({ + query: 'id:"H\\"el\'l\\"-\x00"', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" = \'H"el\\\'l"-\\0\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare Equal - Single Quote', function (assert) { + var query = PQL.getSQL({ + query: " id : 'J0\\\'h\"n Smith' ", + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" = \'J0\\\'h"n Smith\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare Not Equal - Number', function (assert) { + var query = PQL.getSQL({ + query: 'id!:5', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" != 5 GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare Not Equal - String', function (assert) { + var query = PQL.getSQL({ + query: 'id!:hello', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" != \'hello\' GROUP BY "TablE1"."id"', "Passed!"); +}); + +window.QUnit.test('Compare Like - Number', function (assert) { + var query = PQL.getSQL({ + query: 'id~5', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" LIKE \'5%\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare Like - String', function (assert) { + var query = PQL.getSQL({ + query: 'id~Hello45.4', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" LIKE \'Hello45.4%\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare NOT Like - Number', function (assert) { + var query = PQL.getSQL({ + query: 'id !~ -983.493', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" NOT LIKE \'-983.493%\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare NOT Like - String', function (assert) { + var query = PQL.getSQL({ + query: 'id !~ JOOOOe', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" NOT LIKE \'JOOOOe%\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare Greater Than - Number Negative w/ Decimal', function (assert) { + var query = PQL.getSQL({ + query: 'id > -493.44', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" > -493.44 GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare Greater Than - String', function (assert) { + var query = PQL.getSQL({ + query: 'id > "HOSH\\".5"', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" > \'HOSH".5\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare Less Than - String', function (assert) { + var query = PQL.getSQL({ + query: 'id < "HOSH\\".35"', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" < \'HOSH".35\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare Less Than - Number', function (assert) { + var query = PQL.getSQL({ + query: 'id < 3', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" < 3 GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare No Value - Basic', function (assert) { + var query = PQL.getSQL({ + query: 'id', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare No Value - Spacer', function (assert) { + var query = PQL.getSQL({ + query: ' id ', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare Equal - NULL', function (assert) { + var query = PQL.getSQL({ + query: 'id:-', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" IS NULL GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare Not Equal - NULL', function (assert) { + var query = PQL.getSQL({ + query: 'id!:-', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" IS NOT NULL GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare Not Like - NULL', function (assert) { + var query = PQL.getSQL({ + query: 'id!~-', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" IS NOT NULL GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare Like - NULL', function (assert) { + var query = PQL.getSQL({ + query: 'id~-', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" IS NULL GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare Like - NULL', function (assert) { + var query = PQL.getSQL({ + query: 'id~-', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" IS NULL GROUP BY "TablE1"."id"', "Passed!"); +}); + +window.QUnit.test('Compare In List - Numbers', function (assert) { + var query = PQL.getSQL({ + query: 'id:[4,3,43,33]', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" IN (4, 3, 43, 33) GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare Not In List - Numbers', function (assert) { + var query = PQL.getSQL({ + query: 'id!:[14,3,43,33]', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" NOT IN (14, 3, 43, 33) GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare In List - Numbers w/ Strings', function (assert) { + var query = PQL.getSQL({ + query: 'id:[5,3, 4, "Hi\\"o", \'33\']', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" IN (5, 3, 4, \'Hi"o\', 33) GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare In List - Negative Numbers w/ Decimal', function (assert) { + var query = PQL.getSQL({ + query: 'id:[-5,3 , -44.33]', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" IN (-5, 3, -44.33) GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare Like In List - Strings', function (assert) { + var query = PQL.getSQL({ + query: 'id~[hI,l0w]', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE ("TablE1"."id" LIKE \'hI%\' OR "TablE1"."id" LIKE \'l0w%\') GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare NOT Like In List - Strings', function (assert) { + var query = PQL.getSQL({ + query: 'id!~[hI,l0w]', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE ("TablE1"."id" NOT LIKE \'hI%\' AND "TablE1"."id" NOT LIKE \'l0w%\') GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Compare Like In List - Numbers/Strings/Negative', function (assert) { + var query = PQL.getSQL({ + query: ' id ~ [ foo, -1, 1.5, .3 , bar] ', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE ("TablE1"."id" LIKE \'foo%\' OR "TablE1"."id" LIKE \'-1%\' OR "TablE1"."id" LIKE \'1.5%\' OR "TablE1"."id" LIKE \'.3%\' OR "TablE1"."id" LIKE \'bar%\') GROUP BY "TablE1"."id"', "Passed!"); +}); \ No newline at end of file diff --git a/unit_test/tests/constants.js b/unit_test/tests/constants.js new file mode 100644 index 0000000..e761ea5 --- /dev/null +++ b/unit_test/tests/constants.js @@ -0,0 +1,45 @@ +import { PQL } from './../../pql/PQL.js'; +import { Config } from './../test_config.js'; + +window.QUnit.test('Constants 1 - Strings', function (assert) { + var query = PQL.getSQL({ + query: '"id"', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE \'id\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Constants 2 - Strings', function (assert) { + var query = PQL.getSQL({ + query: '"id" \'id\'', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE \'id\' AND \'id\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Constants 3 - Numbers', function (assert) { + var query = PQL.getSQL({ + query: '54', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE \'54\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Constants 4 - Negative Numbers', function (assert) { + var query = PQL.getSQL({ + query: '-54', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE \'-54\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Constants 5 - Negative Numbers Decimal', function (assert) { + var query = PQL.getSQL({ + query: '-54.1234', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE \'-54.1234\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Constants 6 - Positive Numbers Decimal', function (assert) { + var query = PQL.getSQL({ + query: '154.1234', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE \'154.1234\' GROUP BY "TablE1"."id"', "Passed!"); +}); \ No newline at end of file diff --git a/unit_test/tests/cross_table.js b/unit_test/tests/cross_table.js new file mode 100644 index 0000000..02e5eff --- /dev/null +++ b/unit_test/tests/cross_table.js @@ -0,0 +1,32 @@ +import { PQL } from './../../pql/PQL.js'; +import { Config } from './../test_config.js'; + +window.QUnit.test('Cross Table - Compare', function (assert) { + var query = PQL.getSQL({ + query: 'link1.id:5', + table: 'table1', + }); + + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" LEFT JOIN "linkTB1" AS "a0" ON "TablE1"."linkField1" = "a0"."id" WHERE "a0"."id" = 5 GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Cross Table2 - Compare', function (assert) { + var query = PQL.getSQL({ + query: 'link2.id:5', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" LEFT JOIN "linkTB2" AS "a0" ON "TablE1"."id" = "a0"."linkId" WHERE "a0"."id" = 5 GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Cross Table - Multi Tables 1', function (assert) { + var query = PQL.getSQL({ + query: 'link1.linkTable1.link2.name~"foobar"', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" LEFT JOIN "linkTB1" AS "a0" ON "TablE1"."linkField1" = "a0"."id" LEFT JOIN "TablE1" AS "a1" ON "a0"."id" = "a1"."linkField1" LEFT JOIN "linkTB2" AS "a2" ON "a1"."id" = "a2"."linkId" WHERE "a2"."name" LIKE \'foobar%\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Cross Table - Multi Tables 2', function (assert) { + var query = PQL.getSQL({ + query: ' link2.linkTable1.link1.name ~ "foobar"', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" LEFT JOIN "linkTB2" AS "a0" ON "TablE1"."id" = "a0"."linkId" LEFT JOIN "TablE1" AS "a1" ON "a0"."linkId" = "a1"."id" LEFT JOIN "linkTB1" AS "a2" ON "a1"."linkField1" = "a2"."id" WHERE "a2"."name" LIKE \'foobar%\' GROUP BY "TablE1"."id"', "Passed!"); +}); \ No newline at end of file diff --git a/unit_test/tests/functions.js b/unit_test/tests/functions.js new file mode 100644 index 0000000..7162796 --- /dev/null +++ b/unit_test/tests/functions.js @@ -0,0 +1,241 @@ +import { PQL } from './../../pql/PQL.js'; +import { Config } from './../test_config.js'; + +window.QUnit.test('Functions - eq', function (assert) { + var query = PQL.getSQL({ + query: 'eq(id,name)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" = "TablE1"."name" GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Functions - ne', function (assert) { + var query = PQL.getSQL({ + query: 'ne(id, name)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" != "TablE1"."name" GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Functions - lt', function (assert) { + var query = PQL.getSQL({ + query: 'lt(5, 3)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE \'5\' < \'3\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Functions - gt', function (assert) { + var query = PQL.getSQL({ + query: 'gt(-5.11, 31)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE \'-5.11\' > \'31\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Functions - like', function (assert) { + var query = PQL.getSQL({ + query: 'LIKE(id, "foodbar")', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" LIKE \'foodbar%\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Functions - not_like', function (assert) { + var query = PQL.getSQL({ + query: 'NoT_LikE(id, "%foodbar")', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" NOT LIKE \'%foodbar%\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Functions - in', function (assert) { + var query = PQL.getSQL({ + query: 'in(id, name, 5, 3, 4, 1)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" IN ("TablE1"."name", 5, 3, 4, 1) GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Functions - not_in', function (assert) { + var query = PQL.getSQL({ + query: 'not_in(name, concat("Foo", "BAR + ", id), 5, 3, 4, 1)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."name" NOT IN (CONCAT(\'Foo\', \'BAR + \', "TablE1"."id"), \'5\', \'3\', \'4\', \'1\') GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Functions - if', function (assert) { + var query = PQL.getSQL({ + query: 'IF(LIKE(CONCAT(id, "-", name), CONCAT(link1.id, "-", link2.id, "%")), -, 1)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" LEFT JOIN "linkTB1" AS "a0" ON "TablE1"."linkField1" = "a0"."id" LEFT JOIN "linkTB2" AS "a1" ON "TablE1"."id" = "a1"."linkId" WHERE IF(CONCAT("TablE1"."id", \'-\', "TablE1"."name") LIKE CONCAT("a0"."id", \'-\', "a1"."id", \'%\'), NULL, \'1\') GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Functions - add', function (assert) { + var query = PQL.getSQL({ + query: 'add(4, id, 2)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE \'4\' + "TablE1"."id" + \'2\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Functions - sub', function (assert) { + var query = PQL.getSQL({ + query: 'sub(4, 2)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE \'4\' - \'2\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Functions - mul', function (assert) { + var query = PQL.getSQL({ + query: 'MuL(4, 2)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE \'4\' * \'2\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Functions - div', function (assert) { + var query = PQL.getSQL({ + query: 'DIV(4, 2)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE \'4\' / \'2\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Functions - mod', function (assert) { + var query = PQL.getSQL({ + query: 'MOD(4, 2)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE MOD(\'4\', \'2\') GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Functions - pow', function (assert) { + var query = PQL.getSQL({ + query: 'pow(4, 2)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE POW(\'4\', \'2\') GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Functions - sqrt', function (assert) { + var query = PQL.getSQL({ + query: 'sqrt(4)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE SQRT(\'4\') GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Functions - abs', function (assert) { + var query = PQL.getSQL({ + query: 'abs(4)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE ABS(\'4\') GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Functions - char', function (assert) { + var query = PQL.getSQL({ + query: 'char(4):1', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE CHAR(\'4\') = \'1\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Functions - coalesce', function (assert) { + var query = PQL.getSQL({ + query: 'coalesce(4,-):1', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE COALESCE(\'4\', NULL) = \'1\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Functions - ifnull', function (assert) { + var query = PQL.getSQL({ + query: 'ifnull(id, "foobar")', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE IFNULL("TablE1"."id", \'foobar\') GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Functions - avg', function (assert) { + var query = PQL.getSQL({ + query: 'avg(id)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" HAVING AVG("TablE1"."id")', "Passed!"); +}); +window.QUnit.test('Functions - count', function (assert) { + var query = PQL.getSQL({ + query: 'count(id) > 21', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" HAVING COUNT("TablE1"."id") > \'21\'', "Passed!"); +}); +window.QUnit.test('Functions - group_concat 1', function (assert) { + var query = PQL.getSQL({ + query: 'group_concat(name)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" HAVING GROUP_CONCAT("TablE1"."name")', "Passed!"); +}); +window.QUnit.test('Functions - group_concat distinct', function (assert) { + var query = PQL.getSQL({ + query: 'group_concat(name, 1)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" HAVING GROUP_CONCAT(DISTINCT "TablE1"."name")', "Passed!"); +}); +window.QUnit.test('Functions - group_concat non distinct 1', function (assert) { + var query = PQL.getSQL({ + query: 'group_concat(name, 0)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" HAVING GROUP_CONCAT("TablE1"."name")', "Passed!"); +}); +window.QUnit.test('Functions - group_concat non distinct 2', function (assert) { + var query = PQL.getSQL({ + query: 'group_concat(name, -)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" HAVING GROUP_CONCAT("TablE1"."name")', "Passed!"); +}); +window.QUnit.test('Functions - group_concat distinct separator', function (assert) { + var query = PQL.getSQL({ + query: 'group_concat(name, 1, "-")', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" HAVING GROUP_CONCAT(DISTINCT "TablE1"."name" SEPARATOR \'-\')', "Passed!"); +}); +window.QUnit.test('Functions - group_concat distinct null separator', function (assert) { + var query = PQL.getSQL({ + query: 'group_concat(name, 1, -)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" HAVING GROUP_CONCAT(DISTINCT "TablE1"."name")', "Passed!"); +}); +window.QUnit.test('Functions - group_concat distinct separator w/ sort desc', function (assert) { + var query = PQL.getSQL({ + query: 'group_concat(name, 1, "!", id, "desc")', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" HAVING GROUP_CONCAT(DISTINCT "TablE1"."name" ORDER BY "TablE1"."id" DESC SEPARATOR \'!\')', "Passed!"); +}); +window.QUnit.test('Functions - group_concat distinct separator w/ sort asc', function (assert) { + var query = PQL.getSQL({ + query: 'group_concat(name, 1, "!", id, "asc")', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" HAVING GROUP_CONCAT(DISTINCT "TablE1"."name" ORDER BY "TablE1"."id" ASC SEPARATOR \'!\')', "Passed!"); +}); +window.QUnit.test('Functions - group_concat distinct separator w/ sort null', function (assert) { + var query = PQL.getSQL({ + query: 'group_concat(name, 1, "!", id, -)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" HAVING GROUP_CONCAT(DISTINCT "TablE1"."name" ORDER BY "TablE1"."id" ASC SEPARATOR \'!\')', "Passed!"); +}); +window.QUnit.test('Functions - max', function (assert) { + var query = PQL.getSQL({ + query: 'max(id)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" HAVING MAX("TablE1"."id")', "Passed!"); +}); +window.QUnit.test('Functions - min', function (assert) { + var query = PQL.getSQL({ + query: 'MIN(id)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" HAVING MIN("TablE1"."id")', "Passed!"); +}); +window.QUnit.test('Functions - min', function (assert) { + var query = PQL.getSQL({ + query: 'having(id) > 5', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" HAVING "TablE1"."id" > \'5\'', "Passed!"); +}); \ No newline at end of file diff --git a/unit_test/tests/group_by.js b/unit_test/tests/group_by.js new file mode 100644 index 0000000..b9d78a7 --- /dev/null +++ b/unit_test/tests/group_by.js @@ -0,0 +1,29 @@ +import { PQL } from './../../pql/PQL.js'; +import { Config } from './../test_config.js'; + +window.QUnit.test('Group By 1', function (assert) { + var query = PQL.getSQL({ + query: '(link1.id:5 | link2.name~foo)', + group: 'id,name,4,5', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" LEFT JOIN "linkTB1" AS "a0" ON "TablE1"."linkField1" = "a0"."id" LEFT JOIN "linkTB2" AS "a1" ON "TablE1"."id" = "a1"."linkId" WHERE "a0"."id" = 5 OR "a1"."name" LIKE \'foo%\' GROUP BY "TablE1"."id" , "TablE1"."name" , \'4\' , \'5\'', "Passed!"); +}); +window.QUnit.test('Group By 2', function (assert) { + var query = PQL.getSQL({ + group: '@testVar,@testVarStr,id', + table: 'table1', + variables: { + testVar: 1, + testVarStr: "hi" + } + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY \'1\' , \'hi\' , "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Group By 3', function (assert) { + var query = PQL.getSQL({ + group: '"h0Ei"', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY \'h0Ei\'', "Passed!"); +}); \ No newline at end of file diff --git a/unit_test/tests/groups.js b/unit_test/tests/groups.js new file mode 100644 index 0000000..84b223f --- /dev/null +++ b/unit_test/tests/groups.js @@ -0,0 +1,32 @@ +import { PQL } from './../../pql/PQL.js'; +import { Config } from './../test_config.js'; + +window.QUnit.test('Group OR', function (assert) { + var query = PQL.getSQL({ + query: '(link1.id:5 | link2.name~foo)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" LEFT JOIN "linkTB1" AS "a0" ON "TablE1"."linkField1" = "a0"."id" LEFT JOIN "linkTB2" AS "a1" ON "TablE1"."id" = "a1"."linkId" WHERE "a0"."id" = 5 OR "a1"."name" LIKE \'foo%\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Group Multi', function (assert) { + var query = PQL.getSQL({ + query: '(link1.id:5 | link2.name~foo) id:4', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" LEFT JOIN "linkTB1" AS "a0" ON "TablE1"."linkField1" = "a0"."id" LEFT JOIN "linkTB2" AS "a1" ON "TablE1"."id" = "a1"."linkId" WHERE ( "a0"."id" = 5 OR "a1"."name" LIKE \'foo%\' ) AND "TablE1"."id" = 4 GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Group Multi-Multi 1', function (assert) { + var query = PQL.getSQL({ + query: '(link1.id:5 | link2.name~foo) (id:4)', + table: 'table1', + }); + // If only 1 item in group it does not wrap it + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" LEFT JOIN "linkTB1" AS "a0" ON "TablE1"."linkField1" = "a0"."id" LEFT JOIN "linkTB2" AS "a1" ON "TablE1"."id" = "a1"."linkId" WHERE ( "a0"."id" = 5 OR "a1"."name" LIKE \'foo%\' ) AND "TablE1"."id" = 4 GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Group Multi-Multi 2', function (assert) { + var query = PQL.getSQL({ + query: '(link1.id:5 | link2.name~foo) (id:4 name!:3)', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" LEFT JOIN "linkTB1" AS "a0" ON "TablE1"."linkField1" = "a0"."id" LEFT JOIN "linkTB2" AS "a1" ON "TablE1"."id" = "a1"."linkId" WHERE ( "a0"."id" = 5 OR "a1"."name" LIKE \'foo%\' ) AND ( "TablE1"."id" = 4 AND "TablE1"."name" != \'3\' ) GROUP BY "TablE1"."id"', "Passed!"); +}); \ No newline at end of file diff --git a/unit_test/tests/order_by.js b/unit_test/tests/order_by.js new file mode 100644 index 0000000..df27bf7 --- /dev/null +++ b/unit_test/tests/order_by.js @@ -0,0 +1,59 @@ +import { PQL } from './../../pql/PQL.js'; +import { Config } from './../test_config.js'; + +window.QUnit.test('Order By DESC w/ GroupFn', function (assert) { + var query = PQL.getSQL({ + orderBys: { + 'count(id)': 'desc', + }, + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" ORDER BY COUNT("TablE1"."id") DESC', "Passed!"); +}); +window.QUnit.test('Order By ASC w/ GroupFn', function (assert) { + var query = PQL.getSQL({ + orderBys: { + 'count(id)': 'asc', + }, + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" ORDER BY COUNT("TablE1"."id") ASC', "Passed!"); +}); +window.QUnit.test('Order By ASC (unknown by) w/ GroupFn', function (assert) { + var query = PQL.getSQL({ + orderBys: { + 'count(id)': '', + }, + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" ORDER BY COUNT("TablE1"."id") ASC', "Passed!"); +}); +window.QUnit.test('Order By ASC (null by) w/ GroupFn', function (assert) { + var query = PQL.getSQL({ + orderBys: { + 'count(id)': null, + }, + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" ORDER BY COUNT("TablE1"."id") ASC', "Passed!"); +}); +window.QUnit.test('Order By ASC (undefined by) w/ GroupFn', function (assert) { + var query = PQL.getSQL({ + orderBys: { + 'count(id)': undefined, + }, + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" ORDER BY COUNT("TablE1"."id") ASC', "Passed!"); +}); +window.QUnit.test('Order By Multi', function (assert) { + var query = PQL.getSQL({ + orderBys: { + 'count(id)': undefined, + id: 'asc', + name: 'desc', + }, + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" ORDER BY COUNT("TablE1"."id") ASC,"TablE1"."id" ASC,"TablE1"."name" DESC', "Passed!"); +}); \ No newline at end of file diff --git a/unit_test/tests/selects.js b/unit_test/tests/selects.js new file mode 100644 index 0000000..3138041 --- /dev/null +++ b/unit_test/tests/selects.js @@ -0,0 +1,57 @@ +import { PQL } from './../../pql/PQL.js'; +import { Config } from './../test_config.js'; + +window.QUnit.test('Select 1', function (assert) { + var query = PQL.getSQL({ + query: 'id:@id', + table: 'table1', + selects: { + jo: 'count(id)' + }, + variables: { + id: 3, + }, + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT COUNT("TablE1"."id") AS "jo" FROM "TablE1" WHERE "TablE1"."id" = 3 GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Select 2', function (assert) { + var query = PQL.getSQL({ + query: 'id:@id', + table: 'table1', + selects: { + jo: 'count(id)', + jo: 'count(name)', + }, + variables: { + id: 3, + }, + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT COUNT("TablE1"."name") AS "jo" FROM "TablE1" WHERE "TablE1"."id" = 3 GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Select 3', function (assert) { + var query = PQL.getSQL({ + query: 'id:@id', + table: 'table1', + selects: { + '"test"': 'count(@id)', + }, + variables: { + id: 3, + }, + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT COUNT(\'3\') AS """test"" FROM "TablE1" WHERE "TablE1"."id" = 3 GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Select 4', function (assert) { + var query = PQL.getSQL({ + query: 'id:@id', + table: 'table1', + selects: { + '': 'count(@id)', + '*': '"blah"', + }, + variables: { + id: 3, + }, + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT COUNT(\'3\'), \'blah\' AS "*" FROM "TablE1" WHERE "TablE1"."id" = 3 GROUP BY "TablE1"."id"', "Passed!"); +}); \ No newline at end of file diff --git a/unit_test/tests/seperators.js b/unit_test/tests/seperators.js new file mode 100644 index 0000000..fe1d466 --- /dev/null +++ b/unit_test/tests/seperators.js @@ -0,0 +1,47 @@ +import { PQL } from './../../pql/PQL.js'; +import { Config } from './../test_config.js'; + +window.QUnit.test('Seperators - AND 1', function (assert) { + var query = PQL.getSQL({ + query: 'link1.id:5 link1.id~"FOO BAR"', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" LEFT JOIN "linkTB1" AS "a0" ON "TablE1"."linkField1" = "a0"."id" WHERE "a0"."id" = 5 AND "a0"."id" LIKE \'FOO BAR%\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Seperators - AND 2', function (assert) { + var query = PQL.getSQL({ + query: ' link1.id: -504.1 link2.id~"FOO BAR" id!:44', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" LEFT JOIN "linkTB1" AS "a0" ON "TablE1"."linkField1" = "a0"."id" LEFT JOIN "linkTB2" AS "a1" ON "TablE1"."id" = "a1"."linkId" WHERE "a0"."id" = -504.1 AND "a1"."id" LIKE \'FOO BAR%\' AND "TablE1"."id" != 44 GROUP BY "TablE1"."id"', "Passed!"); +}); + +window.QUnit.test('Seperators - AND 3', function (assert) { + var query = PQL.getSQL({ + query: 'id: yo id!:-', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" = \'yo\' AND "TablE1"."id" IS NOT NULL GROUP BY "TablE1"."id"', "Passed!"); +}); + +window.QUnit.test('Seperators - OR 1', function (assert) { + var query = PQL.getSQL({ + query: 'id | name!"FOO"', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" OR "TablE1"."name" NOT LIKE \'FOO%\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Seperators - OR 2', function (assert) { + var query = PQL.getSQL({ + query: 'id | name!"FOO" | link1.id~"FOO BAR"', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" LEFT JOIN "linkTB1" AS "a0" ON "TablE1"."linkField1" = "a0"."id" WHERE "TablE1"."id" OR "TablE1"."name" NOT LIKE \'FOO%\' OR "a0"."id" LIKE \'FOO BAR%\' GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Seperators - Mix', function (assert) { + var query = PQL.getSQL({ + query: 'id | name!"FOO" link1.id~"FOO BAR"', + table: 'table1', + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" LEFT JOIN "linkTB1" AS "a0" ON "TablE1"."linkField1" = "a0"."id" WHERE "TablE1"."id" OR "TablE1"."name" NOT LIKE \'FOO%\' AND "a0"."id" LIKE \'FOO BAR%\' GROUP BY "TablE1"."id"', "Passed!"); +}); \ No newline at end of file diff --git a/unit_test/tests/variable.js b/unit_test/tests/variable.js new file mode 100644 index 0000000..bfd6a7b --- /dev/null +++ b/unit_test/tests/variable.js @@ -0,0 +1,86 @@ +import { PQL } from './../../pql/PQL.js'; +import { Config } from './../test_config.js'; + +window.QUnit.test('Variable 1', function (assert) { + var query = PQL.getSQL({ + query: 'id:@id', + table: 'table1', + variables: { + id: 3, + }, + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" = 3 GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Variable 2', function (assert) { + var query = PQL.getSQL({ + query: 'id:@no_value', + table: 'table1', + variables: { + id: 3, + }, + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" IS NULL GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Variable 3', function (assert) { + var query = PQL.getSQL({ + query: 'id:@no_value|id:@id', + table: 'table1', + variables: { + id: -3, + }, + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" IS NULL OR "TablE1"."id" = -3 GROUP BY "TablE1"."id"', "Passed!"); +}); +window.QUnit.test('Variable 4', function (assert) { + var query = PQL.getSQL({ + query: 'id:@no_value|count(ifnull(id,@val))>@amt', + table: 'table1', + variables: { + val: "1", + amt: 3, + }, + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" HAVING "TablE1"."id" IS NULL OR COUNT(IFNULL("TablE1"."id", \'1\')) > \'3\'', "Passed!"); +}); +window.QUnit.test('Variable 5', function (assert) { + var query = PQL.getSQL({ + query: '(id:@no_value|count(ifnull(id,@val))>@amt) if(count(@id) > -009, @amt, - )', + table: 'table1', + variables: { + id: 4, + val: "1", + amt: 3, + }, + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" HAVING ( "TablE1"."id" IS NULL OR COUNT(IFNULL("TablE1"."id", \'1\')) > \'3\' ) IF(COUNT(\'4\') > \'-009\', \'3\', NULL)', "Passed!"); +}); +window.QUnit.test('Variable 6', function (assert) { + var query = PQL.getSQL({ + query: '(id:3 eq(7,count(@id)))', + table: 'table1', + variables: { + id: 0, + }, + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" = 3 GROUP BY "TablE1"."id" HAVING \'7\' = COUNT(\'0\')', "Passed!"); +}); +window.QUnit.test('Variable 7', function (assert) { + var query = PQL.getSQL({ + query: '(id:3|eq(7,ifnull(count(@id),1)))', + table: 'table1', + variables: { + id: 0, + }, + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" GROUP BY "TablE1"."id" HAVING "TablE1"."id" = 3 OR \'7\' = IFNULL(COUNT(\'0\'), \'1\')', "Passed!"); +}); +window.QUnit.test('Variable 8', function (assert) { + var query = PQL.getSQL({ + query: '(id:3 | eq(7,ifnull(count(@id),1))) id < -', + table: 'table1', + variables: { + id: 0, + }, + }); + assert.ok(query.replace (/\s+/g, ' ') === 'SELECT * FROM "TablE1" WHERE "TablE1"."id" < NULL GROUP BY "TablE1"."id" HAVING ( "TablE1"."id" = 3 OR \'7\' = IFNULL(COUNT(\'0\'), \'1\') )', "Passed!"); +}); \ No newline at end of file