Index: src/docsrc/html/changelog.html
===================================================================
--- src/docsrc/html/changelog.html	(revision 6030)
+++ src/docsrc/html/changelog.html	(working copy)
@@ -34,6 +34,7 @@
     select * from dual join(select x from dual) on 1=1
 </li><li>Issue 598: parser fails on timestamp "24:00:00.1234" - prevent the creation of out-of-range time values.
 </li><li>Allow declaring triggers as source code (like functions). Patch by Sylvain CUAZ.
+</li><li>PostgreSQL compatibility: generate_series (as an alias for system_range).
 </li></ul>
 
 <h2>Version 1.4.185 Beta (2015-01-16)</h2>
Index: src/docsrc/html/roadmap.html
===================================================================
--- src/docsrc/html/roadmap.html	(revision 6030)
+++ src/docsrc/html/roadmap.html	(working copy)
@@ -343,7 +343,6 @@
 </li><li>Batch parameter for INSERT, UPDATE, and DELETE, and commit after each batch. See also MySQL DELETE.
 </li><li>MySQL compatibility: support ALTER TABLE .. MODIFY COLUMN.
 </li><li>Use a lazy and auto-close input stream (open resource when reading, close on eof).
-</li><li>PostgreSQL compatibility: generate_series (as an alias for system_range).
 </li><li>Connection pool: 'reset session' command (delete temp tables, rollback, auto-commit true).
 </li><li>Improve SQL documentation, see http://www.w3schools.com/sql/
 </li><li>MySQL compatibility: DatabaseMetaData.stores*() methods should return the same values. Test with SquirrelSQL.
Index: src/main/org/h2/api/ErrorCode.java
===================================================================
--- src/main/org/h2/api/ErrorCode.java	(revision 6030)
+++ src/main/org/h2/api/ErrorCode.java	(working copy)
@@ -1900,8 +1900,14 @@
      */
     public static final int JAVA_OBJECT_SERIALIZER_CHANGE_WITH_DATA_TABLE = 90141;
 
+    /**
+     * The error with code <code>90142</code> is thrown when
+     * trying to set zero for step size.
+     */
+    public static final int STEP_SIZE_SHOUD_NOT_BE_ZERO = 90142;
 
-    // next are 90039, 90051, 90056, 90110, 90122, 90142
+
+    // next are 90039, 90051, 90056, 90110, 90122, 90143
 
     private ErrorCode() {
         // utility class
Index: src/main/org/h2/command/Parser.java
===================================================================
--- src/main/org/h2/command/Parser.java	(revision 6030)
+++ src/main/org/h2/command/Parser.java	(working copy)
@@ -1201,12 +1201,18 @@
             }
             if (foundLeftBracket) {
                 Schema mainSchema = database.getSchema(Constants.SCHEMA_MAIN);
-                if (equalsToken(tableName, RangeTable.NAME)) {
+                if (equalsToken(tableName, RangeTable.NAME) || equalsToken(tableName, RangeTable.ALIAS)) {
                     Expression min = readExpression();
                     read(",");
                     Expression max = readExpression();
-                    read(")");
-                    table = new RangeTable(mainSchema, min, max, false);
+                    if(readIf(",")){
+                    	Expression step = readExpression();
+                    	read(")");
+	                    table = new RangeTable(mainSchema, min, max, step, false);
+                    }else{
+	                    read(")");
+	                    table = new RangeTable(mainSchema, min, max, false);
+                    }
                 } else {
                     Expression expr = readFunction(schema, tableName);
                     if (!(expr instanceof FunctionCall)) {
Index: src/main/org/h2/index/RangeCursor.java
===================================================================
--- src/main/org/h2/index/RangeCursor.java	(revision 6030)
+++ src/main/org/h2/index/RangeCursor.java	(working copy)
@@ -19,11 +19,15 @@
     private boolean beforeFirst;
     private long current;
     private Row currentRow;
-    private final long min, max;
+    private final long start, end, step;
 
-    RangeCursor(long min, long max) {
-        this.min = min;
-        this.max = max;
+    RangeCursor(long start, long end) {
+    	this(start, end, 1L);
+    }
+    RangeCursor(long start, long end, long step) {
+        this.start = start;
+        this.end = end;
+        this.step = step;
         beforeFirst = true;
     }
 
@@ -41,12 +45,12 @@
     public boolean next() {
         if (beforeFirst) {
             beforeFirst = false;
-            current = min;
+            current = start;
         } else {
-            current++;
+            current += step;
         }
         currentRow = new Row(new Value[]{ValueLong.get(current)}, 1);
-        return current <= max;
+        return step > 0 ? current <= end : current >= end;
     }
 
     @Override
Index: src/main/org/h2/index/RangeIndex.java
===================================================================
--- src/main/org/h2/index/RangeIndex.java	(revision 6030)
+++ src/main/org/h2/index/RangeIndex.java	(working copy)
@@ -47,6 +47,7 @@
     public Cursor find(Session session, SearchRow first, SearchRow last) {
         long min = rangeTable.getMin(session), start = min;
         long max = rangeTable.getMax(session), end = max;
+        long step = rangeTable.getStep(session);
         try {
             start = Math.max(min, first == null ? min : first.getValue(0).getLong());
         } catch (Exception e) {
@@ -57,7 +58,7 @@
         } catch (Exception e) {
             // error when converting the value - ignore
         }
-        return new RangeCursor(start, end);
+        return new RangeCursor(start, end, step);
     }
 
     @Override
Index: src/main/org/h2/res/_messages_cs.prop
===================================================================
--- src/main/org/h2/res/_messages_cs.prop	(revision 6030)
+++ src/main/org/h2/res/_messages_cs.prop	(working copy)
@@ -163,6 +163,7 @@
 90139=Nenalezena veřejná statická Java metoda: {0}
 90140=Vrácený výsledek je pouze pro čtení. Možná budete muset použít conn.createStatement(..., ResultSet.CONCUR_UPDATABLE).
 90141=#Serializer cannot be changed because there is a data table: {0}
+90142=#Step size should not be zero
 HY000=Obecná chyba: {0}
 HY004=Neznámý datový typ: {0}
 HYC00=Vlastnost není podporována: {0}
Index: src/main/org/h2/res/_messages_de.prop
===================================================================
--- src/main/org/h2/res/_messages_de.prop	(revision 6030)
+++ src/main/org/h2/res/_messages_de.prop	(working copy)
@@ -163,6 +163,7 @@
 90139=Die (public static) Java Funktion wurde nicht gefunden: {0}
 90140=Die Resultat-Zeilen können nicht verändert werden. Mögliche Lösung: conn.createStatement(.., ResultSet.CONCUR_UPDATABLE).
 90141=Serialisierer kann nicht geändert werden wenn eine Daten-Tabelle existiert: {0}
+90142=#Step size should not be zero
 HY000=Allgemeiner Fehler: {0}
 HY004=Unbekannter Datentyp: {0}
 HYC00=Dieses Feature wird nicht unterstützt: {0}
Index: src/main/org/h2/res/_messages_en.prop
===================================================================
--- src/main/org/h2/res/_messages_en.prop	(revision 6030)
+++ src/main/org/h2/res/_messages_en.prop	(working copy)
@@ -163,6 +163,7 @@
 90139=The public static Java method was not found: {0}
 90140=The result set is readonly. You may need to use conn.createStatement(.., ResultSet.CONCUR_UPDATABLE).
 90141=Serializer cannot be changed because there is a data table: {0}
+90142=Step size should not be zero
 HY000=General error: {0}
 HY004=Unknown data type: {0}
 HYC00=Feature not supported: {0}
Index: src/main/org/h2/res/_messages_es.prop
===================================================================
--- src/main/org/h2/res/_messages_es.prop	(revision 6030)
+++ src/main/org/h2/res/_messages_es.prop	(working copy)
@@ -163,6 +163,7 @@
 90139=El metodo Java (publico y estatico) : {0} no fue encontrado
 90140=El conjunto de resultados es de solo lectura. Puede ser necesario usar conn.createStatement(.., ResultSet.CONCUR_UPDATABLE).
 90141=#Serializer cannot be changed because there is a data table: {0}
+90142=#Step size should not be zero
 HY000=Error General : {0}
 HY004=Tipo de dato desconocido : {0}
 HYC00=Caracteristica no soportada: {0}
Index: src/main/org/h2/res/_messages_ja.prop
===================================================================
--- src/main/org/h2/res/_messages_ja.prop	(revision 6030)
+++ src/main/org/h2/res/_messages_ja.prop	(working copy)
@@ -163,6 +163,7 @@
 90139=public staticであるJavaメソッドが見つかりません: {0}
 90140=リザルトセットは読み込み専用です。conn.createStatement(.., ResultSet.CONCUR_UPDATABLE) を使う必要があるかもしれません
 90141=#Serializer cannot be changed because there is a data table: {0}
+90142=stepにゼロはセットできません
 HY000=一般エラー: {0}
 HY004=不明なデータ型: {0}
 HYC00=機能はサポートされていません: {0}
Index: src/main/org/h2/res/_messages_pl.prop
===================================================================
--- src/main/org/h2/res/_messages_pl.prop	(revision 6030)
+++ src/main/org/h2/res/_messages_pl.prop	(working copy)
@@ -163,6 +163,7 @@
 90139=Publiczna, statyczna metoda Java nie znaleziona: {0}
 90140=Wyniki są tylko do odczytu. Być może potrzebujesz użyć conn.createStatement(.., ResultSet.CONCUR_UPDATABLE).
 90141=Serializator nie może być zmieniony ponieważ istnieje tabela z danymi: {0}
+90142=#Step size should not be zero
 HY000=Błąd ogólny: {0}
 HY004=Nieznany typ danych: {0}
 HYC00=Cecha nie jest wspierana: {0}
Index: src/main/org/h2/res/_messages_pt_br.prop
===================================================================
--- src/main/org/h2/res/_messages_pt_br.prop	(revision 6030)
+++ src/main/org/h2/res/_messages_pt_br.prop	(working copy)
@@ -163,6 +163,7 @@
 90139=#The public static Java method was not found: {0}
 90140=#The result set is readonly. You may need to use conn.createStatement(.., ResultSet.CONCUR_UPDATABLE).
 90141=#Serializer cannot be changed because there is a data table: {0}
+90142=#Step size should not be zero
 HY000=Erro geral: {0}
 HY004=Tipo de dados desconhecido: {0}
 HYC00=Recurso não suportado: {0}
Index: src/main/org/h2/res/_messages_ru.prop
===================================================================
--- src/main/org/h2/res/_messages_ru.prop	(revision 6030)
+++ src/main/org/h2/res/_messages_ru.prop	(working copy)
@@ -163,6 +163,7 @@
 90139=public static Java метод не найден: {0}
 90140=Набор записей не является обновляемым. Возможно необходимо использовать conn.createStatement(.., ResultSet.CONCUR_UPDATABLE).
 90141=#Serializer cannot be changed because there is a data table: {0}
+90142=#Step size should not be zero
 HY000=Внутренняя ошибка: {0}
 HY004=Неизвестный тип данных: {0}
 HYC00=Данная функция не поддерживается: {0}
Index: src/main/org/h2/res/_messages_sk.prop
===================================================================
--- src/main/org/h2/res/_messages_sk.prop	(revision 6030)
+++ src/main/org/h2/res/_messages_sk.prop	(working copy)
@@ -163,6 +163,7 @@
 90139=Verejná statická Java metóda nebola nájdená: {0}
 90140=Výsledok (result set) je iba na čítanie. Je potrebné použiť conn.createStatement(.., ResultSet.CONCUR_UPDATABLE).
 90141=#Serializer cannot be changed because there is a data table: {0}
+90142=#Step size should not be zero
 HY000=Všeobecná chyba: {0}
 HY004=Neznámy dátový typ: {0}
 HYC00=Vlastnosť nie je podporovaná: {0}
Index: src/main/org/h2/res/_messages_zh_cn.prop
===================================================================
--- src/main/org/h2/res/_messages_zh_cn.prop	(revision 6030)
+++ src/main/org/h2/res/_messages_zh_cn.prop	(working copy)
@@ -163,6 +163,7 @@
 90139=找不到公用Java静态方法: {0}
 90140=结果集是只读的. 你可以使用 conn.createStatement(.., ResultSet.CONCUR_UPDATABLE).
 90141=#Serializer cannot be changed because there is a data table: {0}
+90142=#Step size should not be zero
 HY000=常规错误: {0}
 HY004=位置数据类型: {0}
 HYC00=不支持的特性: {0}
Index: src/main/org/h2/table/RangeTable.java
===================================================================
--- src/main/org/h2/table/RangeTable.java	(revision 6030)
+++ src/main/org/h2/table/RangeTable.java	(working copy)
@@ -6,6 +6,8 @@
 package org.h2.table;
 
 import java.util.ArrayList;
+
+import org.h2.api.ErrorCode;
 import org.h2.engine.Session;
 import org.h2.expression.Expression;
 import org.h2.index.Index;
@@ -27,7 +29,12 @@
      */
     public static final String NAME = "SYSTEM_RANGE";
 
-    private Expression min, max;
+    /**
+     * The alias for the range table.
+     */
+    public static final String ALIAS = "GENERATE_SERIES";
+
+    private Expression min, max, step;
     private boolean optimized;
 
     /**
@@ -48,7 +55,13 @@
         setColumns(cols);
     }
 
-    @Override
+    public RangeTable(Schema schema, Expression min, Expression max,
+    		Expression step, boolean noColumns) {
+        this(schema, min, max, noColumns);
+        this.step = step;
+	}
+
+	@Override
     public String getDropSQL() {
         return null;
     }
@@ -60,7 +73,11 @@
 
     @Override
     public String getSQL() {
-        return NAME + "(" + min.getSQL() + ", " + max.getSQL() + ")";
+    	String sql = NAME + "(" + min.getSQL() + ", " + max.getSQL();
+    	if(step != null){
+    		sql += ", " + step.getSQL();
+    	}
+        return sql + ")";
     }
 
     @Override
@@ -133,6 +150,10 @@
 
     @Override
     public Index getScanIndex(Session session) {
+    	if(getStep(session) == 0L){
+            throw DbException.get(
+                    ErrorCode.STEP_SIZE_SHOUD_NOT_BE_ZERO);
+    	}
         return new RangeIndex(this, IndexColumn.wrap(columns));
     }
 
@@ -158,10 +179,21 @@
         return max.getValue(session).getLong();
     }
 
+    public long getStep(Session session) {
+        optimize(session);
+        if(step == null){
+        	return 1L;
+        }
+        return step.getValue(session).getLong();
+    }
+
     private void optimize(Session s) {
         if (!optimized) {
             min = min.optimize(s);
             max = max.optimize(s);
+            if(step != null){
+            	step = step.optimize(s);
+            }
             optimized = true;
         }
     }
Index: src/test/org/h2/test/db/TestFunctions.java
===================================================================
--- src/test/org/h2/test/db/TestFunctions.java	(revision 6030)
+++ src/test/org/h2/test/db/TestFunctions.java	(working copy)
@@ -93,7 +93,7 @@
         testToCharFromNumber();
         testToCharFromText();
         testTranslate();
-
+        testGenerateSeries();
         // TODO
         // testCachingOfDeterministicFunctionAlias();
 
@@ -1628,6 +1628,72 @@
         assertResult("abc", stat, "SELECT TO_CHAR('abc') FROM DUAL");
         conn.close();
     }
+    
+
+    private void testGenerateSeries() throws SQLException {
+        Connection conn = getConnection("functions");
+        Statement stat = conn.createStatement();
+        {
+	        ResultSet rs = stat.executeQuery("select * from system_range(1,3)");
+	        rs.next();
+	        assertEquals(1, rs.getInt(1));
+	        rs.next();
+	        assertEquals(2, rs.getInt(1));
+	        rs.next();
+	        assertEquals(3, rs.getInt(1));
+	        rs.close();
+        }
+        {
+	        ResultSet rs = stat.executeQuery("select * from system_range(2,2)");
+	        assertTrue(rs.next());
+	        assertEquals(2, rs.getInt(1));
+	        rs.close();
+        }
+        {
+	        ResultSet rs = stat.executeQuery("select * from system_range(2,1)");
+	        assertFalse(rs.next());
+	        rs.close();
+        }
+        {
+	        ResultSet rs = stat.executeQuery("select * from system_range(1,2,-1)");
+	        assertFalse(rs.next());
+	        rs.close();
+        }
+        {
+            assertThrows(ErrorCode.STEP_SIZE_SHOUD_NOT_BE_ZERO, stat).
+            executeQuery("select * from system_range(1,2,0)");
+        }
+        {
+	        ResultSet rs = stat.executeQuery("select * from system_range(2,1,-1)");
+	        assertTrue(rs.next());
+	        assertEquals(2, rs.getInt(1));
+	        assertTrue(rs.next());
+	        assertEquals(1, rs.getInt(1));
+	        rs.close();
+        }
+        {
+	        ResultSet rs = stat.executeQuery("select * from system_range(1,5,2)");
+	        assertTrue(rs.next());
+	        assertEquals(1, rs.getInt(1));
+	        assertTrue(rs.next());
+	        assertEquals(3, rs.getInt(1));
+	        assertTrue(rs.next());
+	        assertEquals(5, rs.getInt(1));
+	        rs.close();
+        }
+        {
+	        ResultSet rs = stat.executeQuery("select * from system_range(1,6,2)");
+	        assertTrue(rs.next());
+	        assertEquals(1, rs.getInt(1));
+	        assertTrue(rs.next());
+	        assertEquals(3, rs.getInt(1));
+	        assertTrue(rs.next());
+	        assertEquals(5, rs.getInt(1));
+	        rs.close();
+        }
+        conn.close();
+    }
+
 
     private void assertCallResult(String expected, Statement stat, String sql)
             throws SQLException {
