class ScriptReader {
    private final Reader reader;
    private char[] buffer;
    private int bufferPos;
    private int bufferStart = -1;
    private int bufferEnd;
    private boolean endOfFile;
    private boolean insideRemark;
    private boolean blockRemark;
    private boolean skipRemarks;
    private int remarkStart;

    /**
     * Create a new SQL script reader from the given reader
     *
     * @param reader the reader
     */
    public ScriptReader(Reader reader) {
        this.reader = reader;
        buffer = new char[Constants.IO_BUFFER_SIZE * 2];
    }

    /**
     * Close the underlying reader.
     */
    public void close() {
        try {
            reader.close();
        } catch (IOException e) {
            throw DbException.convertIOException(e, null);
        }
    }

    /**
     * Read a statement from the reader. This method returns null if the end has
     * been reached.
     *
     * @return the SQL statement or null
     */
    public String readStatement() {
        if (endOfFile) {
            return null;
        }
        try {
            return readStatementLoop();
        } catch (IOException e) {
            throw DbException.convertIOException(e, null);
        }
    }

    private String readStatementLoop() throws IOException {<FILL_FUNCTION_BODY>}

    private void startRemark(boolean block) {
        blockRemark = block;
        remarkStart = bufferPos - 2;
        insideRemark = true;
    }

    private void endRemark() {
        clearRemark();
        insideRemark = false;
    }

    private void clearRemark() {
        if (skipRemarks) {
            Arrays.fill(buffer, remarkStart, bufferPos, ' ');
        }
    }

    private int read() throws IOException {
        if (bufferPos >= bufferEnd) {
            return readBuffer();
        }
        return buffer[bufferPos++];
    }

    private int readBuffer() throws IOException {
        if (endOfFile) {
            return -1;
        }
        int keep = bufferPos - bufferStart;
        if (keep > 0) {
            char[] src = buffer;
            if (keep + Constants.IO_BUFFER_SIZE > src.length) {
                buffer = new char[src.length * 2];
            }
            System.arraycopy(src, bufferStart, buffer, 0, keep);
        }
        remarkStart -= bufferStart;
        bufferStart = 0;
        bufferPos = keep;
        int len = reader.read(buffer, keep, Constants.IO_BUFFER_SIZE);
        if (len == -1) {
            // ensure bufferPos > bufferEnd
            bufferEnd = -1024;
            endOfFile = true;
            // ensure the right number of characters are read
            // in case the input buffer is still used
            bufferPos++;
            return -1;
        }
        bufferEnd = keep + len;
        return buffer[bufferPos++];
    }

    /**
     * Check if this is the last statement, and if the single line or block
     * comment is not finished yet.
     *
     * @return true if the current position is inside a remark
     */
    public boolean isInsideRemark() {
        return insideRemark;
    }

    /**
     * If currently inside a remark, this method tells if it is a block comment
     * (true) or single line comment (false)
     *
     * @return true if inside a block comment
     */
    public boolean isBlockRemark() {
        return blockRemark;
    }

    /**
     * If comments should be skipped completely by this reader.
     *
     * @param skipRemarks true if comments should be skipped
     */
    public void setSkipRemarks(boolean skipRemarks) {
        this.skipRemarks = skipRemarks;
    }

}

        bufferStart = bufferPos;
        int c = read();
        while (true) {
            if (c < 0) {
                endOfFile = true;
                if (bufferPos - 1 == bufferStart) {
                    return null;
                }
                break;
            } else if (c == ';') {
                break;
            }
            switch (c) {
            case '$': {
                c = read();
                if (c == '$' && (bufferPos - bufferStart < 3 || buffer[bufferPos - 3] <= ' ')) {
                    // dollar quoted string
                    while (true) {
                        c = read();
                        if (c < 0) {
                            break;
                        }
                        if (c == '$') {
                            c = read();
                            if (c < 0) {
                                break;
                            }
                            if (c == '$') {
                                break;
                            }
                        }
                    }
                    c = read();
                }
                break;
            }
            case '\'':
                while (true) {
                    c = read();
                    if (c < 0) {
                        break;
                    }
                    if (c == '\'') {
                        break;
                    }
                }
                c = read();
                break;
            case '"':
                while (true) {
                    c = read();
                    if (c < 0) {
                        break;
                    }
                    if (c == '\"') {
                        break;
                    }
                }
                c = read();
                break;
            case '/': {
                c = read();
                if (c == '*') {
                    // block comment
                    startRemark(false);
                    while (true) {
                        c = read();
                        if (c < 0) {
                            break;
                        }
                        if (c == '*') {
                            c = read();
                            if (c < 0) {
                                clearRemark();
                                break;
                            }
                            if (c == '/') {
                                endRemark();
                                break;
                            }
                        }
                    }
                    c = read();
                } else if (c == '/') {
                    // single line comment
                    startRemark(false);
                    while (true) {
                        c = read();
                        if (c < 0) {
                            clearRemark();
                            break;
                        }
                        if (c == '\r' || c == '\n') {
                            endRemark();
                            break;
                        }
                    }
                    c = read();
                }
                break;
            }
            case '-': {
                c = read();
                if (c == '-') {
                    // single line comment
                    startRemark(false);
                    while (true) {
                        c = read();
                        if (c < 0) {
                            clearRemark();
                            break;
                        }
                        if (c == '\r' || c == '\n') {
                            endRemark();
                            break;
                        }
                    }
                    c = read();
                }
                break;
            }
            default: {
                c = read();
            }
            }
        }
        return new String(buffer, bufferStart, bufferPos - 1 - bufferStart);
    
<no_super_class>