Changeset 550 for trunk/grails-app


Ignore:
Timestamp:
May 28, 2010, 1:29:19 AM (15 years ago)
Author:
gav
Message:

Improvements to stockTakeByLocation report, includes heap memory protection improvements.
Tested against MySQL with 767 InventoryLocations, 3770 InventoryItems and 270MiB of images in database.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/grails-app/services/InventoryReportService.groovy

    r547 r550  
    1313    def g = new org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib()
    1414
     15    // Protect java heap memory.
     16    // Most likely want to set paramsMax and inClauseMax to the same values.
    1517    def paramsMax = 250
     18
     19    // At least with Oracle and MSSQL db limits are 1000 (in list) and 2000 (nodes) respectively.
     20    // But 255 has also been mentioned on the internet as a possible limit for some databases.
     21    def inClauseMax = 250
    1622
    1723    /**
     
    5258        def result = [:]
    5359
     60        result.inventoryItemList = []
     61        result.inventoryItemCount = 0
     62        result.locationCount = 0
     63        result.errorMessage = null
    5464        result.summaryOfCalculationMethod = 'This report should be used in conjunction with the `Stock Take (Overview)` Report.'
    5565
    56         // Sanitise the locations string and convert to a list.
     66        def fail = { Map m ->
     67            result.error = [ code: m.code, args: m.args ]
     68            result.errorMessage = g.message(result.error)
     69            result.locations = ''
     70            return result
     71        }
     72
     73        def paginateParams = [:]
     74        paginateParams.max = Math.min(params?.max?.toInteger() ?: paramsMax, paramsMax)
     75
     76        def namedParams = [:]
     77        namedParams.locationList = []
     78
     79        // Sanitise the user supplied locations string and convert to a list.
    5780        result.locations = params.locationString.trim()
    5881        if(result.locations.startsWith('e.g:'))
     
    6184        result.locations = result.locations.collect {it.trim()}
    6285
    63         def paginateParams = [:]
    64         paginateParams.max = Math.min(params?.max?.toInteger() ?: paramsMax, paramsMax)
    65 
    66         def namedParams = [:]
    67         namedParams.locationList = [null] // null protects against HQL unexpected end of subtree exception with an empty list.
    68 
    69         // Fill namedParams.locationList
    70         result.locations.each() {
    71             InventoryLocation.findAllByNameIlike(it).each() {
    72                 namedParams.locationList << it
     86        // Fill namedParams.locationList.
     87        result.locations.each() { location ->
     88            if(namedParams.locationList.size() < paramsMax) {
     89                // paramsMax+1 to ensure the too many locations check bellow is triggered.
     90                namedParams.locationList += InventoryLocation.findAllByNameIlike(location, [max: paramsMax+1])
    7391            }
     92            namedParams.locationList.unique()
    7493        }
    7594
    76         // Return the actual locations as a string.
    77         if(namedParams.locationList.size() > 1)
    78             result.locations = namedParams.locationList[1..-1].toString()[1..-2]
     95        // Return the actual locations as a string, along with a count.
     96        result.locationCount = namedParams.locationList.size()
     97        if(result.locationCount > 0) {
     98            result.locations = namedParams.locationList.toString()[1..-2]
     99        }
    79100        else
    80101            result.locations = g.message(code: 'default.none.text')
     102
     103        // Exit if empty location list.
     104        // Protects against HQL unexpected end of subtree exception with an empty list.
     105        if(namedParams.locationList.isEmpty())
     106            return fail(code:'report.error.no.locations.found')
     107
     108        // Exit if IN clause list too big.
     109        if(namedParams.locationList.size() > inClauseMax)
     110            return fail(code:'report.error.too.many.locations', args: [inClauseMax])
    81111
    82112        // Inventory List.
     
    91121
    92122        // Exit if too many results.
    93         result.countWarning = null
    94         if(result.inventoryItemCount > paramsMax) {
    95             result.countWarning = g.message(code: 'report.too.many.results.warning',
    96                                                                     args: [paramsMax],
    97                                                                     default: "Warning over ${paramsMax} results, please run report again!")
    98             result.inventoryItemList = []
    99             return result
    100         }
     123        if(result.inventoryItemCount > paramsMax)
     124            return fail(code:'report.error.too.many.results', args: [paramsMax])
    101125
    102126        result.inventoryListQuery = "select distinct inventoryItem " + result.inventoryListQuery
     
    107131        namedParams.inventoryList = inventoryList
    108132
    109         // Note: HQL docs advise not using fetch aliases in where clause (or any other clause).
     133        // Exit if empty inventory list.
     134        // Protects against HQL unexpected end of subtree exception with an empty list.
     135        if(namedParams.inventoryList.isEmpty())
     136            return fail(code:'report.error.no.inventory.items.found')
     137
     138        // Exit if inventory list too big.
     139        if(namedParams.inventoryList.size() > inClauseMax)
     140            return fail(code:'report.error.too.many.inventory.items', args: [inClauseMax])
     141
     142        // Note: HQL docs advise 'not using fetch aliases in where clause (or any other clause)'.
    110143        // Access is via the parent object, however that does not work for the order by clause in this case.
    111144        result.query = "from InventoryItem as inventoryItem \
     
    119152                                        order by inventoryStore.name, inventoryLocation.name"
    120153
    121         result.query = "select inventoryItem " + result.query
     154        result.query = "select  distinct inventoryItem " + result.query
    122155        result.inventoryItemList = InventoryItem.executeQuery(result.query, namedParams, paginateParams)
    123156
Note: See TracChangeset for help on using the changeset viewer.