<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -9,6 +9,7 @@
 #define ID_PATH rb_intern(&quot;path&quot;)
 #define ID_NEW rb_intern(&quot;new&quot;)
 #define ID_ESCAPE rb_intern(&quot;escape_sql&quot;)
+#define ID_QUERY_VALUES rb_intern(&quot;query_values&quot;)
 
 #define RUBY_STRING(char_ptr) rb_str_new2(char_ptr)
 #define TAINTED_STRING(name, length) rb_tainted_str_new(name, length)
@@ -65,6 +66,12 @@ static VALUE cReader;
 
 static VALUE eSqlite3Error;
 
+static VALUE OPEN_FLAG_READONLY;
+static VALUE OPEN_FLAG_READWRITE;
+static VALUE OPEN_FLAG_CREATE;
+static VALUE OPEN_FLAG_NO_MUTEX;
+static VALUE OPEN_FLAG_FULL_MUTEX;
+
 // Find the greatest common denominator and reduce the provided numerator and denominator.
 // This replaces calles to Rational.reduce! which does the same thing, but really slowly.
 static void reduce( do_int64 *numerator, do_int64 *denominator ) {
@@ -313,6 +320,45 @@ static VALUE typecast(sqlite3_stmt *stmt, int i, VALUE ruby_class) {
   }
 }
 
+#ifdef HAVE_SQLITE3_OPEN_V2
+
+#define FLAG_PRESENT(query_values, flag) !NIL_P(rb_hash_aref(query_values, flag))
+
+static int cConnection_flags_from_uri(VALUE uri) {
+	VALUE query_values = rb_funcall(uri, ID_QUERY_VALUES, 0);
+	
+	int flags = 0;
+	if (!NIL_P(query_values)) {
+		/// scan for flags
+		if (FLAG_PRESENT(query_values, OPEN_FLAG_READONLY)) {
+      flags |= SQLITE_OPEN_READONLY;
+		} 
+		if (FLAG_PRESENT(query_values, OPEN_FLAG_READWRITE)) {
+      flags |= SQLITE_OPEN_READWRITE;
+		} 
+		if (FLAG_PRESENT(query_values, OPEN_FLAG_CREATE)) {
+      flags |= SQLITE_OPEN_CREATE;
+		} 
+		if (FLAG_PRESENT(query_values, OPEN_FLAG_NO_MUTEX)) {
+      flags |= SQLITE_OPEN_NOMUTEX;
+		} 
+		if (FLAG_PRESENT(query_values, OPEN_FLAG_FULL_MUTEX)) {
+      flags |= SQLITE_OPEN_FULLMUTEX;
+		} 
+		
+	} else {
+		flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
+	}
+	
+	return  flags;
+}
+
+static VALUE cConnection_open_v2(VALUE self, VALUE path, VALUE uri, sqlite3 **db) {
+	int flags = cConnection_flags_from_uri(uri);
+	return sqlite3_open_v2(StringValuePtr(path), db, flags, 0);
+}
+
+#endif
 
 /****** Public API ******/
 
@@ -320,9 +366,14 @@ static VALUE cConnection_initialize(VALUE self, VALUE uri) {
   int ret;
   VALUE path;
   sqlite3 *db;
-
+  
   path = rb_funcall(uri, ID_PATH, 0);
+
+#ifdef HAVE_SQLITE3_OPEN_V2
+  ret = cConnection_open_v2(self, path, uri, &amp;db);
+#else
   ret = sqlite3_open(StringValuePtr(path), &amp;db);
+#endif
 
   if ( ret != SQLITE_OK ) {
     rb_raise(eSqlite3Error, sqlite3_errmsg(db));
@@ -530,6 +581,10 @@ static VALUE cReader_row_count(VALUE self) {
   return rb_iv_get(self, &quot;@row_count&quot;);
 }
 
+static VALUE mSqlite3_sqlite_version(VALUE self) {
+  return rb_str_new2(sqlite3_libversion());
+}
+
 void Init_do_sqlite3_ext() {
   rb_require(&quot;bigdecimal&quot;);
   rb_require(&quot;date&quot;);
@@ -562,7 +617,9 @@ void Init_do_sqlite3_ext() {
 
   // Initialize the DataObjects::Sqlite3 module, and define its classes
   mSqlite3 = rb_define_module_under(mDO, &quot;Sqlite3&quot;);
-
+  puts(&quot;hi&quot;);
+  rb_define_singleton_method(mSqlite3, &quot;sqlite3_version&quot;, mSqlite3_sqlite_version, 0);
+  
   eSqlite3Error = rb_define_class(&quot;Sqlite3Error&quot;, rb_eStandardError);
 
   cConnection = SQLITE3_CLASS(&quot;Connection&quot;, cDO_Connection);
@@ -587,4 +644,10 @@ void Init_do_sqlite3_ext() {
   rb_define_method(cReader, &quot;field_count&quot;, cReader_field_count, 0);
   rb_define_method(cReader, &quot;row_count&quot;, cReader_row_count, 0);
 
+  OPEN_FLAG_READONLY = rb_str_new2(&quot;read_only&quot;);
+  OPEN_FLAG_READWRITE = rb_str_new2(&quot;read_write&quot;);
+  OPEN_FLAG_CREATE = rb_str_new2(&quot;create&quot;);
+  OPEN_FLAG_NO_MUTEX = rb_str_new2(&quot;no_mutex&quot;);
+  OPEN_FLAG_FULL_MUTEX = rb_str_new2(&quot;full_mutex&quot;);
+
 }</diff>
      <filename>do_sqlite3/ext/do_sqlite3_ext/do_sqlite3_ext.c</filename>
    </modified>
    <modified>
      <diff>@@ -36,5 +36,10 @@ end
 # create_makefile(extension_name)
 if have_header( &quot;sqlite3.h&quot; ) &amp;&amp; have_library( &quot;sqlite3&quot;, &quot;sqlite3_open&quot; )
   have_func(&quot;sqlite3_prepare_v2&quot;)
+  
+  if (have_func(&quot;sqlite3_open_v2&quot;))
+    puts &quot;Using sqlite3_open_v2&quot;
+  end
+  
   create_makefile(extension_name)
 end</diff>
      <filename>do_sqlite3/ext/do_sqlite3_ext/extconf.rb</filename>
    </modified>
    <modified>
      <diff>@@ -17,6 +17,57 @@ describe &quot;DataObjects::Sqlite3&quot; do
     end
   end
 
+  unless JRUBY
+    it &quot;should provide the sqlite3 version&quot; do
+      lambda {DataObjects::Sqlite3.sqlite3_version}.should_not raise_error
+      
+      DataObjects::Sqlite3.sqlite3_version.should match(/^3\.\d+(\.\d+)?/)
+    end
+  end
+end
+
+describe &quot;DataObjects::Sqlite3, open flags&quot; do
+  include Sqlite3SpecHelpers
+  
+  url = &quot;sqlite3://#{File.expand_path(File.dirname(__FILE__))}/ro_test.db&quot;
+  ro_url = url + &quot;?read_only=true&quot;
+
+  
+  minor_version = DataObjects::Sqlite3.sqlite3_version.split(/\./)[1].to_i
+  
+  
+  unless JRUBY 
+    before(:all) do
+      @connection = DataObjects::Connection.new(url)
+
+      command = @connection.create_command(&quot;DROP TABLE users&quot;)
+      command.execute_non_query rescue nil
+
+      command = @connection.create_command(&quot;CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)&quot;)
+      command.execute_non_query
+
+      insert(&quot;INSERT INTO users VALUES (1, 'A dummy user')&quot;)
+      @connection.close
+    end
+    
+    unless minor_version &lt; 5
+    
+      it &quot;should open a database in readonly mode if sqlite_open_v2 is available&quot; do
+        @connection = DataObjects::Connection.new(ro_url)
+        
+        lambda {insert(&quot;insert into users (2, 'not allowed dummy')&quot;)}.should raise_error
+        
+        @connection.close
+      end
+    else
+      it &quot;should ignore the ...open_v2 flags if sqlite version is not sufficient&quot; do
+        @connection = DataObjects::Connection.new(ro_url)
+
+        lambda {insert(&quot;INSERT INTO users VALUES (2, 'Allowed to')&quot;)}.should_not raise_error
+        @connection.close
+      end
+    end      
+  end
 end
 
 NOW = DateTime.now</diff>
      <filename>do_sqlite3/spec/integration/do_sqlite3_spec.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>faabb9ddd9a92ef8c8cc2ddb13024b7556fd6840</id>
    </parent>
  </parents>
  <author>
    <name>Ognen Ivanovski</name>
    <email>oivanovski@leto.lan</email>
  </author>
  <url>http://github.com/sam/do/commit/e23bb7aeb70c95f86102846148e71e1886520fc1</url>
  <id>e23bb7aeb70c95f86102846148e71e1886520fc1</id>
  <committed-date>2009-01-25T11:19:44-08:00</committed-date>
  <authored-date>2009-01-24T16:57:51-08:00</authored-date>
  <message>- do_sqlite3 now uses sqlite3_open_v2() if it is compiled against sqlite &gt;= 3.5.0
- in such case the following query_options will be honoured and passed to sqlite_open_v2
   - read_only =&gt; SQLITE_OPEN_READ_ONLY
   - read_write =&gt; SQLITE_OPEN_READ_WRITE
   - create =&gt; SQLITE_OPEN_CREATE
   - no_mutex =&gt; SQLITE_OPEN_NOMUTEX
   - full_mutex =&gt; SQLITE_OPEN_FULLMUTEX
 - see http://www.sqlite.org/c3ref/open.html for more details
 - example: sqlite3://path/to/file.db?read_only=true will open that connection in read only mode
 - in case sqlite is &lt; 3.5.0 these flags are silently ignored
 - added specs to cover this
 - added DataObjects::Sqlite3.sqlite3_version method that returns the sqlite3 version in use</message>
  <tree>459b9f9a12e2200abcf128b47ff2e4dc30af6498</tree>
  <committer>
    <name>Dirkjan Bussink</name>
    <email>d.bussink@gmail.com</email>
  </committer>
</commit>
