root/HTTP/DB.lua

Revision 1443 (checked in by rsz, 2 months ago)

cleanup

Line 
1 --------------------------------------------------------------------------------
2 -- Title:               DB.lua
3 -- Description:         Like a square peg in a round hole
4 -- Author:              Raphaël Szwarc http://alt.textdrive.com/lua/
5 -- Creation Date:       January 30, 2007
6 -- Legal:               Copyright (C) 2007 Raphaël Szwarc
7 --                      Under the terms of the MIT License
8 --                      http://www.opensource.org/licenses/mit-license.html
9 --------------------------------------------------------------------------------
10
11 -- import dependencies
12 local assert = assert
13 local getmetatable = getmetatable
14 local ipairs = ipairs
15 local module = module
16 local require = require
17 local select = select
18 local setmetatable = setmetatable
19 local tostring = tostring
20 local type = type
21 local unpack = unpack
22
23 --------------------------------------------------------------------------------
24 -- DB
25 --------------------------------------------------------------------------------
26
27 module( 'DB' )
28 _VERSION = '1.0'
29
30 local self = setmetatable( _M, {} )
31 local meta = getmetatable( self )
32
33 local function NewEnvironment( aURL, ... )
34     local aType = assert( aURL.scheme, 'missing scheme in ' .. aURL )
35     local aModule = require( 'luasql.' .. aType )
36     local LuaSQL = require( 'luasql' )
37     local aFunction = assert( LuaSQL[ aType ], 'missing ' .. aType .. ' initialization function in luasql'  )
38     local anEnvironment = assert( aFunction( ... ) )
39    
40     return anEnvironment
41 end
42
43 local function Source( aPath )
44     aPath.absolute = false
45    
46     return tostring( aPath )
47 end
48
49 local function NewConnection( anEnvironment, aURL )
50     local aSource = Source( aURL.path )
51     local aUser = aURL.user
52     local aPassword = aURL.password
53     local aHost = aURL.host
54     local aPort = aURL.port
55     local aConnection = assert( anEnvironment:connect( aSource, aUser, aPassword, aHost, aPort ) )
56    
57     return aConnection
58 end
59
60 function meta:__call( aURL, ... )
61     local URL = require( 'URL' )
62     local aURL = URL( aURL )
63     local anEnvironment = NewEnvironment( aURL, ... )
64     local aConnection = NewConnection( anEnvironment, aURL )
65     local aDB = { url = aURL, environment = anEnvironment, connection = aConnection }
66
67     setmetatable( aDB, self )
68    
69     return aDB
70 end
71
72 function self:__call( aStatement, ... )
73     return require( 'DBCursor' )( self, aStatement, ... )
74 end
75
76 function self:__newindex( aKey, aValue )
77     if aKey == 'autocommit' then
78         self.connection:setautocommit( aValue )
79     elseif aKey == 'commit' and aValue then
80         assert( self.connection:commit() )
81     elseif aKey == 'commit' and not aValue then
82         assert( self.connection:rollback() )
83     elseif aKey == 'close' and aValue then
84         self.connection:close()
85         self.environment:close()
86     end
87 end
88
89 function self:__concat( aValue )
90     return tostring( self ) .. tostring( aValue )
91 end
92
93 function self:__eq( aValue )
94     return tostring( self ) == tostring( aValue )
95 end
96
97 function self:__lt( aValue )
98     return tostring( self ) < tostring( aValue )
99 end
100
101 function self:__tostring()
102     return tostring( self.url )
103 end
104
105 --------------------------------------------------------------------------------
106 -- DBCursor
107 --------------------------------------------------------------------------------
108
109 module( 'DBCursor' )
110 _VERSION = '1.0'
111
112 local self = setmetatable( _M, {} )
113 local meta = getmetatable( self )
114
115 local function Escape( aValue )
116     return aValue:gsub( '\'', '\'\'' )
117 end
118
119 local function Encoder( aConnection )
120     if type( aConnection.escape ) == 'function' then
121         return function( aValue )
122             return aConnection:escape( aValue )
123         end
124     end
125
126     return Escape
127 end
128
129 local function BindValue( anEncoder, aValue )
130     local aType = type( aValue )
131    
132     if aType == 'string' then
133         aValue = ( '\'%s\'' ):format( anEncoder( aValue ) )
134     elseif aType == 'nil' then
135         aValue = 'null'
136     else
137         aValue = tostring( aValue )
138     end
139    
140     return aValue
141 end
142
143 local function BindStatement( aConnection, aStatement, ... )
144     local aStatement = tostring( aStatement )
145     local aCount =  select( '#', ... )
146    
147     if aCount > 0 then
148         local anEncoder = Encoder( aConnection )
149         local someBindings = {}
150        
151         for anIndex = 1, aCount do
152             local aValue = select( anIndex, ... )
153            
154             someBindings[ anIndex ] = BindValue( anEncoder, aValue )
155         end
156        
157         aStatement = aStatement:format( unpack( someBindings ) )
158     end
159
160     return aStatement
161 end
162
163 local function Normalize( someValues )
164     for anIndex, aValue in ipairs( someValues ) do
165         someValues[ anIndex ] = aValue:lower()
166     end
167    
168     return someValues
169 end
170
171 function meta:__call( aDB, aStatement, ... )
172     local aConnection = aDB.connection
173     local aStatement = BindStatement( aConnection, aStatement, ... )
174     local aResult = assert( aConnection:execute( aStatement ) )
175    
176     if type( aResult ) ~= 'number' then
177         local someNames = Normalize( aResult:getcolnames() )
178         local someTypes = Normalize( aResult:getcoltypes() )
179         local aCursor = { db = aDB, cursor = aResult, statement = aStatement, names = someNames, types = someTypes, row = {} }
180        
181         if aResult[ 'numrows' ] then
182             aCursor[ 'count' ] = aResult:numrows()
183         end
184        
185         setmetatable( aCursor, self )
186        
187         return aCursor
188     end
189    
190     return aResult
191 end
192
193 function self:__call()
194     local aCursor = self.cursor
195     local aRow = aCursor:fetch( self.row, 'n' )
196    
197     if aRow then
198         local someNames = self.names
199         local aResult = {}
200        
201         for anIndex, aValue in ipairs( aRow ) do
202             local aName = someNames[ anIndex ]
203            
204             aResult[ aName ] = aValue
205         end
206        
207         return aResult
208     end
209    
210     aCursor:close()
211 end
212
213 function self:__newindex( aKey, aValue )
214     if aKey == 'close' and aValue then
215         self.cursor:close()
216     end
217 end
218
219 function self:__concat( aValue )
220     return tostring( self ) .. tostring( aValue )
221 end
222
223 function self:__eq( aValue )
224     return tostring( self ) == tostring( aValue )
225 end
226
227 function self:__lt( aValue )
228     return tostring( self ) < tostring( aValue )
229 end
230
231 function self:__tostring()
232     return tostring( self.statement )
233 end
Note: See TracBrowser for help on using the browser.